diff --git a/.codeqlmanifest.json b/.codeqlmanifest.json index 4efe12f6d2b..c4d4260a2cc 100644 --- a/.codeqlmanifest.json +++ b/.codeqlmanifest.json @@ -1,8 +1,11 @@ -{ "provide": [ "*/ql/src/qlpack.yml", +{ "provide": [ "ruby/.codeqlmanifest.json", + "*/ql/src/qlpack.yml", "*/ql/lib/qlpack.yml", "*/ql/test/qlpack.yml", "cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml", "*/ql/examples/qlpack.yml", "*/upgrades/qlpack.yml", + "javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml", + "javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml", "misc/legacy-support/*/qlpack.yml", "misc/suite-helpers/qlpack.yml" ] } diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 8f9ffdde4de..ff73bcb4e7b 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,9 +1,14 @@ { "extensions": [ + "rust-lang.rust", + "bungcip.better-toml", "github.vscode-codeql", "slevesque.vscode-zipexplorer" ], "settings": { + "files.watcherExclude": { + "**/target/**": true + }, "codeQL.runningQueries.memory": 2048 } } diff --git a/.github/actions/fetch-codeql/action.yml b/.github/actions/fetch-codeql/action.yml new file mode 100644 index 00000000000..de6d50dc12f --- /dev/null +++ b/.github/actions/fetch-codeql/action.yml @@ -0,0 +1,14 @@ +name: Fetch CodeQL +description: Fetches the latest version of CodeQL +runs: + using: composite + steps: + - name: Fetch CodeQL + shell: bash + run: | + LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | grep -v beta | sort --version-sort | tail -1) + gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip "$LATEST" + unzip -q codeql-linux64.zip + echo "${{ github.workspace }}/codeql" >> $GITHUB_PATH + env: + GITHUB_TOKEN: ${{ github.token }} diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..613f9287146 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,18 @@ +version: 2 +updates: + - package-ecosystem: "cargo" + directory: "ruby/node-types" + schedule: + interval: "daily" + - package-ecosystem: "cargo" + directory: "ruby/generator" + schedule: + interval: "daily" + - package-ecosystem: "cargo" + directory: "ruby/extractor" + schedule: + interval: "daily" + - package-ecosystem: "cargo" + directory: "ruby/autobuilder" + schedule: + interval: "daily" diff --git a/.github/labeler.yml b/.github/labeler.yml index 17fac6dc765..87b97436ad9 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -18,6 +18,10 @@ Python: - python/**/* - change-notes/**/*python* +Ruby: + - ruby/**/* + - change-notes/**/*ruby* + documentation: - "**/*.qhelp" - "**/*.md" diff --git a/.github/workflows/csv-coverage-pr-artifacts.yml b/.github/workflows/csv-coverage-pr-artifacts.yml index 201eea5c073..8b89b9b22c1 100644 --- a/.github/workflows/csv-coverage-pr-artifacts.yml +++ b/.github/workflows/csv-coverage-pr-artifacts.yml @@ -6,6 +6,8 @@ on: - '.github/workflows/csv-coverage-pr-comment.yml' - '*/ql/src/**/*.ql' - '*/ql/src/**/*.qll' + - '*/ql/lib/**/*.ql' + - '*/ql/lib/**/*.qll' - 'misc/scripts/library-coverage/*.py' # input data files - '*/documentation/library-coverage/cwe-sink.csv' diff --git a/.github/workflows/qhelp-pr-preview.yml b/.github/workflows/qhelp-pr-preview.yml new file mode 100644 index 00000000000..7e2ea6a10f8 --- /dev/null +++ b/.github/workflows/qhelp-pr-preview.yml @@ -0,0 +1,39 @@ +name: Query help preview + +on: + pull_request: + branches: + - main + - 'rc/*' + paths: + - "ruby/**/*.qhelp" + +jobs: + qhelp: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 2 + - name: Determine changed files + id: changes + run: | + echo -n "::set-output name=qhelp_files::" + (git diff --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep .qhelp$ | grep -v .inc.qhelp; + git diff --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep .inc.qhelp$ | xargs -d '\n' -rn1 basename | xargs -d '\n' -rn1 git grep -l) | + sort -u | xargs -d '\n' -n1 printf "'%s' " + + - uses: ./.github/actions/fetch-codeql + + - name: QHelp preview + if: ${{ steps.changes.outputs.qhelp_files }} + run: | + ( echo "QHelp previews:"; + for path in ${{ steps.changes.outputs.qhelp_files }} ; do + echo "
${path}" + echo + codeql generate query-help --format=markdown ${path} + echo "
" + done) | gh pr comment "${{ github.event.pull_request.number }}" -F - + env: + GITHUB_TOKEN: ${{ github.token }} diff --git a/.github/workflows/ruby-build.yml b/.github/workflows/ruby-build.yml new file mode 100644 index 00000000000..4361ce5a1cc --- /dev/null +++ b/.github/workflows/ruby-build.yml @@ -0,0 +1,232 @@ +name: "Ruby: Build" + +on: + push: + paths: + - 'ruby/**' + branches: + - main + - 'rc/*' + pull_request: + paths: + - 'ruby/**' + branches: + - main + - 'rc/*' + workflow_dispatch: + inputs: + tag: + description: "Version tag to create" + required: false + +env: + CARGO_TERM_COLOR: always + +defaults: + run: + working-directory: ruby + +jobs: + build: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v2 + - name: Install GNU tar + if: runner.os == 'macOS' + run: | + brew install gnu-tar + echo "/usr/local/opt/gnu-tar/libexec/gnubin" >> $GITHUB_PATH + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + ruby/target + key: ${{ runner.os }}-rust-cargo-${{ hashFiles('**/Cargo.lock') }} + - name: Check formatting + run: cargo fmt --all -- --check + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose + - name: Release build + run: cargo build --release + - name: Generate dbscheme + if: ${{ matrix.os == 'ubuntu-latest' }} + run: target/release/ruby-generator --dbscheme ql/lib/ruby.dbscheme --library ql/lib/codeql/ruby/ast/internal/TreeSitter.qll + - uses: actions/upload-artifact@v2 + if: ${{ matrix.os == 'ubuntu-latest' }} + with: + name: ruby.dbscheme + path: ruby/ql/lib/ruby.dbscheme + - uses: actions/upload-artifact@v2 + if: ${{ matrix.os == 'ubuntu-latest' }} + with: + name: TreeSitter.qll + path: ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll + - uses: actions/upload-artifact@v2 + with: + name: extractor-${{ matrix.os }} + path: | + ruby/target/release/ruby-autobuilder + ruby/target/release/ruby-autobuilder.exe + ruby/target/release/ruby-extractor + ruby/target/release/ruby-extractor.exe + retention-days: 1 + compile-queries: + runs-on: ubuntu-latest + env: + CODEQL_THREADS: 4 # TODO: remove this once it's set by the CLI + steps: + - uses: actions/checkout@v2 + - name: Fetch CodeQL + run: | + LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | grep -v beta | sort --version-sort | tail -1) + gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip "$LATEST" + unzip -q codeql-linux64.zip + env: + GITHUB_TOKEN: ${{ github.token }} + - name: Build Query Pack + run: | + codeql/codeql pack create ql/lib --output target/packs + codeql/codeql pack install ql/src + codeql/codeql pack create ql/src --output target/packs + PACK_FOLDER=$(readlink -f target/packs/codeql/ruby-queries/*) + codeql/codeql generate query-help --format=sarifv2.1.0 --output="${PACK_FOLDER}/rules.sarif" ql/src + (cd ql/src; find queries \( -name '*.qhelp' -o -name '*.rb' -o -name '*.erb' \) -exec bash -c 'mkdir -p "'"${PACK_FOLDER}"'/$(dirname "{}")"' \; -exec cp "{}" "${PACK_FOLDER}/{}" \;) + - name: Compile with previous CodeQL versions + run: | + for version in $(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | sort --version-sort | tail -3 | head -2); do + rm -f codeql-linux64.zip + gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip "$version" + rm -rf codeql; unzip -q codeql-linux64.zip + codeql/codeql query compile target/packs/* + done + env: + GITHUB_TOKEN: ${{ github.token }} + - uses: actions/upload-artifact@v2 + with: + name: codeql-ruby-queries + path: | + ruby/target/packs/* + retention-days: 1 + + package: + runs-on: ubuntu-latest + needs: [build, compile-queries] + steps: + - uses: actions/checkout@v2 + - uses: actions/download-artifact@v2 + with: + name: ruby.dbscheme + path: ruby/ruby + - uses: actions/download-artifact@v2 + with: + name: extractor-ubuntu-latest + path: ruby/linux64 + - uses: actions/download-artifact@v2 + with: + name: extractor-windows-latest + path: ruby/win64 + - uses: actions/download-artifact@v2 + with: + name: extractor-macos-latest + path: ruby/osx64 + - run: | + mkdir -p ruby + cp -r codeql-extractor.yml tools ql/lib/ruby.dbscheme.stats ruby/ + mkdir -p ruby/tools/{linux64,osx64,win64} + cp linux64/ruby-autobuilder ruby/tools/linux64/autobuilder + cp osx64/ruby-autobuilder ruby/tools/osx64/autobuilder + cp win64/ruby-autobuilder.exe ruby/tools/win64/autobuilder.exe + cp linux64/ruby-extractor ruby/tools/linux64/extractor + cp osx64/ruby-extractor ruby/tools/osx64/extractor + cp win64/ruby-extractor.exe ruby/tools/win64/extractor.exe + chmod +x ruby/tools/{linux64,osx64}/{autobuilder,extractor} + zip -rq codeql-ruby.zip ruby + - uses: actions/upload-artifact@v2 + with: + name: codeql-ruby-pack + path: ruby/codeql-ruby.zip + retention-days: 1 + - uses: actions/download-artifact@v2 + with: + name: codeql-ruby-queries + path: ruby/qlpacks + - run: | + echo '{ + "provide": [ + "ruby/codeql-extractor.yml", + "qlpacks/*/*/*/qlpack.yml" + ] + }' > .codeqlmanifest.json + zip -rq codeql-ruby-bundle.zip .codeqlmanifest.json ruby qlpacks + - uses: actions/upload-artifact@v2 + with: + name: codeql-ruby-bundle + path: ruby/codeql-ruby-bundle.zip + retention-days: 1 + + test: + defaults: + run: + working-directory: ${{ github.workspace }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + + runs-on: ${{ matrix.os }} + needs: [package] + steps: + - uses: actions/checkout@v2 + with: + repository: Shopify/example-ruby-app + ref: 67a0decc5eb550f3a9228eda53925c3afd40dfe9 + - name: Fetch CodeQL + shell: bash + run: | + LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | grep -v beta | sort --version-sort | tail -1) + gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql.zip "$LATEST" + unzip -q codeql.zip + env: + GITHUB_TOKEN: ${{ github.token }} + working-directory: ${{ runner.temp }} + - name: Download Ruby bundle + uses: actions/download-artifact@v2 + with: + name: codeql-ruby-bundle + path: ${{ runner.temp }} + - name: Unzip Ruby bundle + shell: bash + run: unzip -q -d "${{ runner.temp }}/ruby-bundle" "${{ runner.temp }}/codeql-ruby-bundle.zip" + - name: Prepare test files + shell: bash + run: | + echo "import ruby select count(File f)" > "test.ql" + echo "| 4 |" > "test.expected" + echo 'name: sample-tests + version: 0.0.0 + dependencies: + codeql/ruby-all: 0.0.1 + extractor: ruby + tests: . + ' > qlpack.yml + - name: Run QL test + shell: bash + run: | + "${{ runner.temp }}/codeql/codeql" test run --search-path "${{ runner.temp }}/ruby-bundle" --additional-packs "${{ runner.temp }}/ruby-bundle" . + - name: Create database + shell: bash + run: | + "${{ runner.temp }}/codeql/codeql" database create --search-path "${{ runner.temp }}/ruby-bundle" --language ruby --source-root . ../database + - name: Analyze database + shell: bash + run: | + "${{ runner.temp }}/codeql/codeql" database analyze --search-path "${{ runner.temp }}/ruby-bundle" --format=sarifv2.1.0 --output=out.sarif ../database ruby-code-scanning.qls diff --git a/.github/workflows/ruby-dataset-measure.yml b/.github/workflows/ruby-dataset-measure.yml new file mode 100644 index 00000000000..39e5a8486d6 --- /dev/null +++ b/.github/workflows/ruby-dataset-measure.yml @@ -0,0 +1,71 @@ +name: "Ruby: Collect database stats" + +on: + push: + branches: + - main + - 'rc/*' + paths: + - ruby/ql/lib/ruby.dbscheme + pull_request: + branches: + - main + - 'rc/*' + paths: + - ruby/ql/lib/ruby.dbscheme + workflow_dispatch: + +jobs: + measure: + env: + CODEQL_THREADS: 4 # TODO: remove this once it's set by the CLI + strategy: + fail-fast: false + matrix: + repo: [rails/rails, discourse/discourse, spree/spree] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - uses: ./.github/actions/fetch-codeql + + - uses: ./ruby/actions/create-extractor-pack + + - name: Checkout ${{ matrix.repo }} + uses: actions/checkout@v2 + with: + repository: ${{ matrix.repo }} + path: ${{ github.workspace }}/repo + - name: Create database + run: | + codeql database create \ + --search-path "${{ github.workspace }}/ruby" \ + --threads 4 \ + --language ruby --source-root "${{ github.workspace }}/repo" \ + "${{ runner.temp }}/database" + - name: Measure database + run: | + mkdir -p "stats/${{ matrix.repo }}" + codeql dataset measure --threads 4 --output "stats/${{ matrix.repo }}/stats.xml" "${{ runner.temp }}/database/db-ruby" + - uses: actions/upload-artifact@v2 + with: + name: measurements + path: stats + retention-days: 1 + + merge: + runs-on: ubuntu-latest + needs: measure + steps: + - uses: actions/checkout@v2 + - uses: actions/download-artifact@v2 + with: + name: measurements + path: stats + - run: | + python -m pip install --user lxml + find stats -name 'stats.xml' | sort | xargs python ruby/scripts/merge_stats.py --output ruby/ql/lib/ruby.dbscheme.stats --normalise ruby_tokeninfo + - uses: actions/upload-artifact@v2 + with: + name: ruby.dbscheme.stats + path: ruby/ql/lib/ruby.dbscheme.stats diff --git a/.github/workflows/ruby-qltest.yml b/.github/workflows/ruby-qltest.yml new file mode 100644 index 00000000000..fad38bf689e --- /dev/null +++ b/.github/workflows/ruby-qltest.yml @@ -0,0 +1,48 @@ +name: "Ruby: Run QL Tests" + +on: + push: + paths: + - 'ruby/**' + branches: + - main + - 'rc/*' + pull_request: + paths: + - 'ruby/**' + branches: + - main + - 'rc/*' + +env: + CARGO_TERM_COLOR: always + +defaults: + run: + working-directory: ruby + +jobs: + qltest: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: ./.github/actions/fetch-codeql + - uses: ./ruby/actions/create-extractor-pack + - name: Run QL tests + run: | + codeql test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path "${{ github.workspace }}/ruby" --additional-packs "${{ github.workspace }}" --consistency-queries ql/consistency-queries ql/test + env: + GITHUB_TOKEN: ${{ github.token }} + - name: Check QL formatting + run: find ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only + - name: Check QL compilation + run: | + codeql query compile --check-only --threads=4 --warnings=error --search-path "${{ github.workspace }}/ruby" --additional-packs "${{ github.workspace }}" "ql/src" "ql/examples" + env: + GITHUB_TOKEN: ${{ github.token }} + - name: Check DB upgrade scripts + run: | + echo >empty.trap + codeql dataset import -S ql/lib/upgrades/initial/ruby.dbscheme testdb empty.trap + codeql dataset upgrade testdb --additional-packs ql/lib/upgrades + diff -q testdb/ruby.dbscheme ql/lib/ruby.dbscheme diff --git a/.github/workflows/sync-files.yml b/.github/workflows/sync-files.yml new file mode 100644 index 00000000000..e41f4b75dc6 --- /dev/null +++ b/.github/workflows/sync-files.yml @@ -0,0 +1,20 @@ +name: Check synchronized files + +on: + push: + branches: + - main + - 'rc/*' + pull_request: + branches: + - main + - 'rc/*' + +jobs: + sync: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Check synchronized files + run: python config/sync-files.py + diff --git a/.gitignore b/.gitignore index 0951496d45c..bf37ce08333 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,6 @@ /codeql/ csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json + +# Avoid committing cached package components +.codeql diff --git a/CODEOWNERS b/CODEOWNERS index 89529f95924..d5f3362a7ca 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -3,6 +3,7 @@ /java/ @github/codeql-java /javascript/ @github/codeql-javascript /python/ @github/codeql-python +/ruby/ @github/codeql-ruby # Make @xcorail (GitHub Security Lab) a code owner for experimental queries so he gets pinged when we promote a query out of experimental /cpp/**/experimental/**/* @github/codeql-c-analysis @xcorail @@ -10,6 +11,7 @@ /java/**/experimental/**/* @github/codeql-java @xcorail /javascript/**/experimental/**/* @github/codeql-javascript @xcorail /python/**/experimental/**/* @github/codeql-python @xcorail +/ruby/**/experimental/**/* @github/codeql-ruby @xcorail # Notify members of codeql-go about PRs to the shared data-flow library files /java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @github/codeql-java @github/codeql-go @@ -22,4 +24,4 @@ /docs/codeql-cli/ @github/codeql-cli-reviewers /docs/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers /docs/ql-language-reference/ @github/codeql-frontend-reviewers -/docs/query-*-style-guide.md @github/codeql-analysis-reviewers \ No newline at end of file +/docs/query-*-style-guide.md @github/codeql-analysis-reviewers diff --git a/config/identical-files.json b/config/identical-files.json index 74ef7b82323..a60bd919028 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -24,14 +24,17 @@ "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll", "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll", "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll", - "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll" + "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll", + "ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll", + "ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll" ], "DataFlow Java/C++/C#/Python Common": [ "java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll", "cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll", "cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll", "csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll", - "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll" + "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll", + "ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll" ], "TaintTracking::Configuration Java/C++/C#/Python": [ "cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll", @@ -49,18 +52,21 @@ "python/ql/lib/semmle/python/dataflow/new/internal/tainttracking1/TaintTrackingImpl.qll", "python/ql/lib/semmle/python/dataflow/new/internal/tainttracking2/TaintTrackingImpl.qll", "python/ql/lib/semmle/python/dataflow/new/internal/tainttracking3/TaintTrackingImpl.qll", - "python/ql/lib/semmle/python/dataflow/new/internal/tainttracking4/TaintTrackingImpl.qll" + "python/ql/lib/semmle/python/dataflow/new/internal/tainttracking4/TaintTrackingImpl.qll", + "ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll" ], "DataFlow Java/C++/C#/Python Consistency checks": [ "java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll", "cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll", "cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll", "csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll", - "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll" + "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll", + "ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll" ], "DataFlow Java/C# Flow Summaries": [ "java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll", - "csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll" + "csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll", + "ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll" ], "SsaReadPosition Java/C#": [ "java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll", @@ -367,8 +373,10 @@ ], "Inline Test Expectations": [ "cpp/ql/test/TestUtilities/InlineExpectationsTest.qll", + "csharp/ql/test/TestUtilities/InlineExpectationsTest.qll", "java/ql/test/TestUtilities/InlineExpectationsTest.qll", - "python/ql/test/TestUtilities/InlineExpectationsTest.qll" + "python/ql/test/TestUtilities/InlineExpectationsTest.qll", + "ruby/ql/test/TestUtilities/InlineExpectationsTest.qll" ], "C++ ExternalAPIs": [ "cpp/ql/src/Security/CWE/CWE-020/ExternalAPIs.qll", @@ -440,7 +448,8 @@ "csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll", "csharp/ql/lib/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll", "csharp/ql/lib/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll", - "csharp/ql/lib/semmle/code/cil/internal/SsaImplCommon.qll" + "csharp/ql/lib/semmle/code/cil/internal/SsaImplCommon.qll", + "ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll" ], "CryptoAlgorithms Python/JS": [ "javascript/ql/lib/semmle/javascript/security/CryptoAlgorithms.qll", @@ -460,6 +469,23 @@ ], "ReDoS Polynomial Python/JS": [ "javascript/ql/lib/semmle/javascript/security/performance/SuperlinearBackTracking.qll", - "python/ql/lib/semmle/python/security/performance/SuperlinearBackTracking.qll" + "python/ql/lib/semmle/python/security/performance/SuperlinearBackTracking.qll", + "ruby/ql/lib/codeql/ruby/regexp/SuperlinearBackTracking.qll" + ], + "CFG": [ + "csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll", + "ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll" + ], + "TypeTracker": [ + "python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll", + "ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll" + ], + "CodeQL Tutorial": [ + "cpp/ql/lib/tutorial.qll", + "csharp/ql/lib/tutorial.qll", + "java/ql/lib/tutorial.qll", + "javascript/ql/lib/tutorial.qll", + "python/ql/lib/tutorial.qll", + "ruby/ql/lib/tutorial.qll" ] -} \ No newline at end of file +} diff --git a/cpp/change-notes/2021-06-10-cleartext-transmission.md b/cpp/change-notes/2021-06-10-cleartext-transmission.md new file mode 100644 index 00000000000..ce6debf1407 --- /dev/null +++ b/cpp/change-notes/2021-06-10-cleartext-transmission.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* A new query (`cpp/cleartext-transmission`) has been added. This is similar to the `cpp/cleartext-storage-file`, `cpp/cleartext-storage-buffer` and `cpp/cleartext-storage-database` queries but looks for cases where sensitive information is most likely transmitted over a network. diff --git a/cpp/change-notes/2021-09-27-command-line-injection.md b/cpp/change-notes/2021-09-27-command-line-injection.md new file mode 100644 index 00000000000..53ce1fd1dbe --- /dev/null +++ b/cpp/change-notes/2021-09-27-command-line-injection.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The "Uncontrolled data used in OS command" (`cpp/command-line-injection`) query has been enhanced to reduce false positive results and its `@precision` increased to `high` \ No newline at end of file diff --git a/cpp/change-notes/2021-09-27-overflow-static.md b/cpp/change-notes/2021-09-27-overflow-static.md new file mode 100644 index 00000000000..e28ba4970ce --- /dev/null +++ b/cpp/change-notes/2021-09-27-overflow-static.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* Increase precision to high for the "Static buffer overflow" query + (`cpp/static-buffer-overflow`). This means the query is run and displayed by default on Code Scanning and LGTM. diff --git a/cpp/change-notes/2021-10-01-improper-null-termination.md b/cpp/change-notes/2021-10-01-improper-null-termination.md new file mode 100644 index 00000000000..fc569913ccf --- /dev/null +++ b/cpp/change-notes/2021-10-01-improper-null-termination.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Several improvements made to the `NullTermination.qll` library and the 'Potential improper null termination' (cpp/improper-null-termination). These changes reduce the number of false positive results for this query and related query 'User-controlled data may not be null terminated' (cpp/user-controlled-null-termination-tainted). diff --git a/cpp/change-notes/2021-10-07-extraction-errors.md b/cpp/change-notes/2021-10-07-extraction-errors.md new file mode 100644 index 00000000000..d4d00afb77a --- /dev/null +++ b/cpp/change-notes/2021-10-07-extraction-errors.md @@ -0,0 +1,3 @@ +codescanning +* Problems with extraction that in most cases won't break the analysis in a significant way are now reported as warnings rather than errors. +* The failed extractor invocations query now has severity `error`. diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/security/PrivateCleartextWrite.qll b/cpp/ql/lib/experimental/semmle/code/cpp/security/PrivateCleartextWrite.qll index 922dadaa20e..5438722fd08 100644 --- a/cpp/ql/lib/experimental/semmle/code/cpp/security/PrivateCleartextWrite.qll +++ b/cpp/ql/lib/experimental/semmle/code/cpp/security/PrivateCleartextWrite.qll @@ -52,11 +52,8 @@ module PrivateCleartextWrite { class WriteSink extends Sink { WriteSink() { - exists(FileWrite f, BufferWrite b | - this.asExpr() = f.getASource() - or - this.asExpr() = b.getAChild() - ) + this.asExpr() = any(FileWrite f).getASource() or + this.asExpr() = any(BufferWrite b).getAChild() } } } diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/security/PrivateData.qll b/cpp/ql/lib/experimental/semmle/code/cpp/security/PrivateData.qll index 621e8aad707..ec37e8ce86c 100644 --- a/cpp/ql/lib/experimental/semmle/code/cpp/security/PrivateData.qll +++ b/cpp/ql/lib/experimental/semmle/code/cpp/security/PrivateData.qll @@ -13,26 +13,25 @@ import cpp /** A string for `match` that identifies strings that look like they represent private data. */ private string privateNames() { - // Inspired by the list on https://cwe.mitre.org/data/definitions/359.html - // Government identifiers, such as Social Security Numbers - result = "%social%security%number%" or - // Contact information, such as home addresses and telephone numbers - result = "%postcode%" or - result = "%zipcode%" or - // result = "%telephone%" or - // Geographic location - where the user is (or was) - result = "%latitude%" or - result = "%longitude%" or - // Financial data - such as credit card numbers, salary, bank accounts, and debts - result = "%creditcard%" or - result = "%salary%" or - result = "%bankaccount%" or - // Communications - e-mail addresses, private e-mail messages, SMS text messages, chat logs, etc. - // result = "%email%" or - // result = "%mobile%" or - result = "%employer%" or - // Health - medical conditions, insurance status, prescription records - result = "%medical%" + result = + [ + // Inspired by the list on https://cwe.mitre.org/data/definitions/359.html + // Government identifiers, such as Social Security Numbers + "%social%security%number%", + // Contact information, such as home addresses and telephone numbers + "%postcode%", "%zipcode%", + // result = "%telephone%" or + // Geographic location - where the user is (or was) + "%latitude%", "%longitude%", + // Financial data - such as credit card numbers, salary, bank accounts, and debts + "%creditcard%", "%salary%", "%bankaccount%", + // Communications - e-mail addresses, private e-mail messages, SMS text messages, chat logs, etc. + // result = "%email%" or + // result = "%mobile%" or + "%employer%", + // Health - medical conditions, insurance status, prescription records + "%medical%" + ] } /** An expression that might contain private data. */ diff --git a/cpp/ql/lib/external/ExternalArtifact.qll b/cpp/ql/lib/external/ExternalArtifact.qll index abbc96a7b47..1034f1c9ecc 100644 --- a/cpp/ql/lib/external/ExternalArtifact.qll +++ b/cpp/ql/lib/external/ExternalArtifact.qll @@ -15,7 +15,7 @@ class ExternalData extends @externalDataElement { * Gets the path of the file this data was loaded from, with its * extension replaced by `.ql`. */ - string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") } + string getQueryPath() { result = this.getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") } /** Gets the number of fields in this data item. */ int getNumFields() { result = 1 + max(int i | externalData(this, _, i, _) | i) } @@ -24,22 +24,22 @@ class ExternalData extends @externalDataElement { string getField(int i) { externalData(this, _, i, result) } /** Gets the integer value of the `i`th field of this data item. */ - int getFieldAsInt(int i) { result = getField(i).toInt() } + int getFieldAsInt(int i) { result = this.getField(i).toInt() } /** Gets the floating-point value of the `i`th field of this data item. */ - float getFieldAsFloat(int i) { result = getField(i).toFloat() } + float getFieldAsFloat(int i) { result = this.getField(i).toFloat() } /** Gets the value of the `i`th field of this data item, interpreted as a date. */ - date getFieldAsDate(int i) { result = getField(i).toDate() } + date getFieldAsDate(int i) { result = this.getField(i).toDate() } /** Gets a textual representation of this data item. */ - string toString() { result = getQueryPath() + ": " + buildTupleString(0) } + string toString() { result = this.getQueryPath() + ": " + this.buildTupleString(0) } /** Gets a textual representation of this data item, starting with the `n`th field. */ private string buildTupleString(int n) { - n = getNumFields() - 1 and result = getField(n) + n = this.getNumFields() - 1 and result = this.getField(n) or - n < getNumFields() - 1 and result = getField(n) + "," + buildTupleString(n + 1) + n < this.getNumFields() - 1 and result = this.getField(n) + "," + this.buildTupleString(n + 1) } } @@ -53,8 +53,8 @@ class DefectExternalData extends ExternalData { } /** Gets the URL associated with this data item. */ - string getURL() { result = getField(0) } + string getURL() { result = this.getField(0) } /** Gets the message associated with this data item. */ - string getMessage() { result = getField(1) } + string getMessage() { result = this.getField(1) } } diff --git a/cpp/ql/lib/semmle/code/cpp/Class.qll b/cpp/ql/lib/semmle/code/cpp/Class.qll index 987ec7ffa3d..c4fdbfb40f6 100644 --- a/cpp/ql/lib/semmle/code/cpp/Class.qll +++ b/cpp/ql/lib/semmle/code/cpp/Class.qll @@ -269,13 +269,13 @@ class Class extends UserType { * DEPRECATED: name changed to `hasImplicitCopyConstructor` to reflect that * `= default` members are no longer included. */ - deprecated predicate hasGeneratedCopyConstructor() { hasImplicitCopyConstructor() } + deprecated predicate hasGeneratedCopyConstructor() { this.hasImplicitCopyConstructor() } /** * DEPRECATED: name changed to `hasImplicitCopyAssignmentOperator` to * reflect that `= default` members are no longer included. */ - deprecated predicate hasGeneratedCopyAssignmentOperator() { hasImplicitCopyConstructor() } + deprecated predicate hasGeneratedCopyAssignmentOperator() { this.hasImplicitCopyConstructor() } /** * Holds if this class, struct or union has an implicitly-declared copy @@ -487,7 +487,7 @@ class Class extends UserType { exists(ClassDerivation cd | // Add the offset of the direct base class and the offset of `baseClass` // within that direct base class. - cd = getADerivation() and + cd = this.getADerivation() and result = cd.getBaseClass().getANonVirtualBaseClassByteOffset(baseClass) + cd.getByteOffset() ) } @@ -502,12 +502,12 @@ class Class extends UserType { */ int getABaseClassByteOffset(Class baseClass) { // Handle the non-virtual case. - result = getANonVirtualBaseClassByteOffset(baseClass) + result = this.getANonVirtualBaseClassByteOffset(baseClass) or exists(Class virtualBaseClass, int virtualBaseOffset, int offsetFromVirtualBase | // Look for the base class as a non-virtual base of a direct or indirect // virtual base, adding the two offsets. - getVirtualBaseClassByteOffset(virtualBaseClass) = virtualBaseOffset and + this.getVirtualBaseClassByteOffset(virtualBaseClass) = virtualBaseOffset and offsetFromVirtualBase = virtualBaseClass.getANonVirtualBaseClassByteOffset(baseClass) and result = virtualBaseOffset + offsetFromVirtualBase ) @@ -623,11 +623,11 @@ class Class extends UserType { * inherits one). */ predicate isPolymorphic() { - exists(MemberFunction f | f.getDeclaringType() = getABaseClass*() and f.isVirtual()) + exists(MemberFunction f | f.getDeclaringType() = this.getABaseClass*() and f.isVirtual()) } override predicate involvesTemplateParameter() { - getATemplateArgument().(Type).involvesTemplateParameter() + this.getATemplateArgument().(Type).involvesTemplateParameter() } /** Holds if this class, struct or union was declared 'final'. */ @@ -765,7 +765,7 @@ class ClassDerivation extends Locatable, @derivation { * }; * ``` */ - Class getBaseClass() { result = getBaseType().getUnderlyingType() } + Class getBaseClass() { result = this.getBaseType().getUnderlyingType() } override string getAPrimaryQlClass() { result = "ClassDerivation" } @@ -818,7 +818,7 @@ class ClassDerivation extends Locatable, @derivation { predicate hasSpecifier(string s) { this.getASpecifier().hasName(s) } /** Holds if the derivation is for a virtual base class. */ - predicate isVirtual() { hasSpecifier("virtual") } + predicate isVirtual() { this.hasSpecifier("virtual") } /** Gets the location of the derivation. */ override Location getLocation() { derivations(underlyingElement(this), _, _, _, result) } @@ -846,7 +846,7 @@ class ClassDerivation extends Locatable, @derivation { * ``` */ class LocalClass extends Class { - LocalClass() { isLocal() } + LocalClass() { this.isLocal() } override string getAPrimaryQlClass() { not this instanceof LocalStruct and result = "LocalClass" } @@ -989,9 +989,9 @@ class ClassTemplateSpecialization extends Class { TemplateClass getPrimaryTemplate() { // Ignoring template arguments, the primary template has the same name // as each of its specializations. - result.getSimpleName() = getSimpleName() and + result.getSimpleName() = this.getSimpleName() and // It is in the same namespace as its specializations. - result.getNamespace() = getNamespace() and + result.getNamespace() = this.getNamespace() and // It is distinguished by the fact that each of its template arguments // is a distinct template parameter. count(TemplateParameter tp | tp = result.getATemplateArgument()) = @@ -1108,7 +1108,7 @@ deprecated class Interface extends Class { * ``` */ class VirtualClassDerivation extends ClassDerivation { - VirtualClassDerivation() { hasSpecifier("virtual") } + VirtualClassDerivation() { this.hasSpecifier("virtual") } override string getAPrimaryQlClass() { result = "VirtualClassDerivation" } } @@ -1136,7 +1136,7 @@ class VirtualBaseClass extends Class { VirtualClassDerivation getAVirtualDerivation() { result.getBaseClass() = this } /** A class/struct that is derived from this one using virtual inheritance. */ - Class getAVirtuallyDerivedClass() { result = getAVirtualDerivation().getDerivedClass() } + Class getAVirtuallyDerivedClass() { result = this.getAVirtualDerivation().getDerivedClass() } } /** @@ -1155,7 +1155,7 @@ class ProxyClass extends UserType { override string getAPrimaryQlClass() { result = "ProxyClass" } /** Gets the location of the proxy class. */ - override Location getLocation() { result = getTemplateParameter().getDefinitionLocation() } + override Location getLocation() { result = this.getTemplateParameter().getDefinitionLocation() } /** Gets the template parameter for which this is the proxy class. */ TemplateParameter getTemplateParameter() { diff --git a/cpp/ql/lib/semmle/code/cpp/Declaration.qll b/cpp/ql/lib/semmle/code/cpp/Declaration.qll index 7ac79fd99c1..0433be1f120 100644 --- a/cpp/ql/lib/semmle/code/cpp/Declaration.qll +++ b/cpp/ql/lib/semmle/code/cpp/Declaration.qll @@ -184,7 +184,7 @@ class Declaration extends Locatable, @declaration { predicate hasDefinition() { exists(this.getDefinition()) } /** DEPRECATED: Use `hasDefinition` instead. */ - predicate isDefined() { hasDefinition() } + predicate isDefined() { this.hasDefinition() } /** Gets the preferred location of this declaration, if any. */ override Location getLocation() { none() } @@ -209,7 +209,7 @@ class Declaration extends Locatable, @declaration { predicate isStatic() { this.hasSpecifier("static") } /** Holds if this declaration is a member of a class/struct/union. */ - predicate isMember() { hasDeclaringType() } + predicate isMember() { this.hasDeclaringType() } /** Holds if this declaration is a member of a class/struct/union. */ predicate hasDeclaringType() { exists(this.getDeclaringType()) } @@ -226,14 +226,14 @@ class Declaration extends Locatable, @declaration { * When called on a template, this will return a template parameter type for * both typed and non-typed parameters. */ - final Locatable getATemplateArgument() { result = getTemplateArgument(_) } + final Locatable getATemplateArgument() { result = this.getTemplateArgument(_) } /** * Gets a template argument used to instantiate this declaration from a template. * When called on a template, this will return a non-typed template * parameter value. */ - final Locatable getATemplateArgumentKind() { result = getTemplateArgumentKind(_) } + final Locatable getATemplateArgumentKind() { result = this.getTemplateArgumentKind(_) } /** * Gets the `i`th template argument used to instantiate this declaration from a @@ -252,9 +252,9 @@ class Declaration extends Locatable, @declaration { * `getTemplateArgument(1)` return `1`. */ final Locatable getTemplateArgument(int index) { - if exists(getTemplateArgumentValue(index)) - then result = getTemplateArgumentValue(index) - else result = getTemplateArgumentType(index) + if exists(this.getTemplateArgumentValue(index)) + then result = this.getTemplateArgumentValue(index) + else result = this.getTemplateArgumentType(index) } /** @@ -275,14 +275,13 @@ class Declaration extends Locatable, @declaration { * `getTemplateArgumentKind(0)`. */ final Locatable getTemplateArgumentKind(int index) { - if exists(getTemplateArgumentValue(index)) - then result = getTemplateArgumentType(index) - else none() + exists(this.getTemplateArgumentValue(index)) and + result = this.getTemplateArgumentType(index) } /** Gets the number of template arguments for this declaration. */ final int getNumberOfTemplateArguments() { - result = count(int i | exists(getTemplateArgument(i))) + result = count(int i | exists(this.getTemplateArgument(i))) } private Type getTemplateArgumentType(int index) { @@ -328,9 +327,9 @@ class DeclarationEntry extends Locatable, TDeclarationEntry { * available), or the name declared by this entry otherwise. */ string getCanonicalName() { - if getDeclaration().hasDefinition() - then result = getDeclaration().getDefinition().getName() - else result = getName() + if this.getDeclaration().hasDefinition() + then result = this.getDeclaration().getDefinition().getName() + else result = this.getName() } /** @@ -371,18 +370,18 @@ class DeclarationEntry extends Locatable, TDeclarationEntry { /** * Holds if this declaration entry has a specifier with the given name. */ - predicate hasSpecifier(string specifier) { getASpecifier() = specifier } + predicate hasSpecifier(string specifier) { this.getASpecifier() = specifier } /** Holds if this declaration entry is a definition. */ predicate isDefinition() { none() } // overridden in subclasses override string toString() { - if isDefinition() - then result = "definition of " + getName() + if this.isDefinition() + then result = "definition of " + this.getName() else - if getName() = getCanonicalName() - then result = "declaration of " + getName() - else result = "declaration of " + getCanonicalName() + " as " + getName() + if this.getName() = this.getCanonicalName() + then result = "declaration of " + this.getName() + else result = "declaration of " + this.getCanonicalName() + " as " + this.getName() } } @@ -581,7 +580,7 @@ private class DirectAccessHolder extends Element { // transitive closure with a restricted base case. this.thisCanAccessClassStep(base, derived) or - exists(Class between | thisCanAccessClassTrans(base, between) | + exists(Class between | this.thisCanAccessClassTrans(base, between) | isDirectPublicBaseOf(between, derived) or this.thisCanAccessClassStep(between, derived) ) diff --git a/cpp/ql/lib/semmle/code/cpp/Element.qll b/cpp/ql/lib/semmle/code/cpp/Element.qll index 1f547adccaa..9273d1b31bf 100644 --- a/cpp/ql/lib/semmle/code/cpp/Element.qll +++ b/cpp/ql/lib/semmle/code/cpp/Element.qll @@ -61,7 +61,7 @@ class ElementBase extends @element { /** * Gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs. */ - final string getPrimaryQlClasses() { result = concat(getAPrimaryQlClass(), ",") } + final string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") } /** * Gets the name of a primary CodeQL class to which this element belongs. @@ -206,9 +206,9 @@ class Element extends ElementBase { /** Gets the closest `Element` enclosing this one. */ cached Element getEnclosingElement() { - result = getEnclosingElementPref() + result = this.getEnclosingElementPref() or - not exists(getEnclosingElementPref()) and + not exists(this.getEnclosingElementPref()) and ( this = result.(Class).getAMember() or @@ -281,7 +281,7 @@ private predicate isFromUninstantiatedTemplateRec(Element e, Element template) { * ``` */ class StaticAssert extends Locatable, @static_assert { - override string toString() { result = "static_assert(..., \"" + getMessage() + "\")" } + override string toString() { result = "static_assert(..., \"" + this.getMessage() + "\")" } /** * Gets the expression which this static assertion ensures is true. diff --git a/cpp/ql/lib/semmle/code/cpp/Enum.qll b/cpp/ql/lib/semmle/code/cpp/Enum.qll index 9cddeb78f9b..38263dacf7a 100644 --- a/cpp/ql/lib/semmle/code/cpp/Enum.qll +++ b/cpp/ql/lib/semmle/code/cpp/Enum.qll @@ -85,7 +85,7 @@ class Enum extends UserType, IntegralOrEnumType { * ``` */ class LocalEnum extends Enum { - LocalEnum() { isLocal() } + LocalEnum() { this.isLocal() } override string getAPrimaryQlClass() { result = "LocalEnum" } } diff --git a/cpp/ql/lib/semmle/code/cpp/File.qll b/cpp/ql/lib/semmle/code/cpp/File.qll index 853b34ecfd9..3b72533b4f4 100644 --- a/cpp/ql/lib/semmle/code/cpp/File.qll +++ b/cpp/ql/lib/semmle/code/cpp/File.qll @@ -38,7 +38,7 @@ class Container extends Locatable, @container { * DEPRECATED: Use `getLocation` instead. * Gets a URL representing the location of this container. * - * For more information see [Providing URLs](https://help.semmle.com/QL/learn-ql/ql/locations.html#providing-urls). + * For more information see [Providing URLs](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/#providing-urls). */ deprecated string getURL() { none() } // overridden by subclasses @@ -52,7 +52,7 @@ class Container extends Locatable, @container { */ string getRelativePath() { exists(string absPath, string pref | - absPath = getAbsolutePath() and sourceLocationPrefix(pref) + absPath = this.getAbsolutePath() and sourceLocationPrefix(pref) | absPath = pref and result = "" or @@ -79,7 +79,7 @@ class Container extends Locatable, @container { * */ string getBaseName() { - result = getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1) + result = this.getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1) } /** @@ -105,7 +105,9 @@ class Container extends Locatable, @container { * "/tmp/x.tar.gz""gz" * */ - string getExtension() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) } + string getExtension() { + result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) + } /** * Gets the stem of this container, that is, the prefix of its base name up to @@ -124,7 +126,9 @@ class Container extends Locatable, @container { * "/tmp/x.tar.gz""x.tar" * */ - string getStem() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) } + string getStem() { + result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) + } /** Gets the parent container of this file or folder, if any. */ Container getParentContainer() { @@ -135,20 +139,20 @@ class Container extends Locatable, @container { Container getAChildContainer() { this = result.getParentContainer() } /** Gets a file in this container. */ - File getAFile() { result = getAChildContainer() } + File getAFile() { result = this.getAChildContainer() } /** Gets the file in this container that has the given `baseName`, if any. */ File getFile(string baseName) { - result = getAFile() and + result = this.getAFile() and result.getBaseName() = baseName } /** Gets a sub-folder in this container. */ - Folder getAFolder() { result = getAChildContainer() } + Folder getAFolder() { result = this.getAChildContainer() } /** Gets the sub-folder in this container that has the given `baseName`, if any. */ Folder getFolder(string baseName) { - result = getAFolder() and + result = this.getAFolder() and result.getBaseName() = baseName } @@ -157,7 +161,7 @@ class Container extends Locatable, @container { * * This is the absolute path of the container. */ - override string toString() { result = getAbsolutePath() } + override string toString() { result = this.getAbsolutePath() } } /** diff --git a/cpp/ql/lib/semmle/code/cpp/Function.qll b/cpp/ql/lib/semmle/code/cpp/Function.qll index 6cae134645f..0f1de2b512c 100644 --- a/cpp/ql/lib/semmle/code/cpp/Function.qll +++ b/cpp/ql/lib/semmle/code/cpp/Function.qll @@ -43,26 +43,26 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function { */ string getFullSignature() { exists(string name, string templateArgs, string args | - result = name + templateArgs + args + " -> " + getType().toString() and - name = getQualifiedName() and + result = name + templateArgs + args + " -> " + this.getType().toString() and + name = this.getQualifiedName() and ( - if exists(getATemplateArgument()) + if exists(this.getATemplateArgument()) then templateArgs = "<" + concat(int i | - exists(getTemplateArgument(i)) + exists(this.getTemplateArgument(i)) | - getTemplateArgument(i).toString(), ", " order by i + this.getTemplateArgument(i).toString(), ", " order by i ) + ">" else templateArgs = "" ) and args = "(" + concat(int i | - exists(getParameter(i)) + exists(this.getParameter(i)) | - getParameter(i).getType().toString(), ", " order by i + this.getParameter(i).getType().toString(), ", " order by i ) + ")" ) } @@ -70,7 +70,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function { /** Gets a specifier of this function. */ override Specifier getASpecifier() { funspecifiers(underlyingElement(this), unresolveElement(result)) or - result.hasName(getADeclarationEntry().getASpecifier()) + result.hasName(this.getADeclarationEntry().getASpecifier()) } /** Gets an attribute of this function. */ @@ -149,7 +149,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function { * Holds if this function is declared with `__attribute__((naked))` or * `__declspec(naked)`. */ - predicate isNaked() { getAnAttribute().hasName("naked") } + predicate isNaked() { this.getAnAttribute().hasName("naked") } /** * Holds if this function has a trailing return type. @@ -172,7 +172,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function { * Gets the return type of this function after specifiers have been deeply * stripped and typedefs have been resolved. */ - Type getUnspecifiedType() { result = getType().getUnspecifiedType() } + Type getUnspecifiedType() { result = this.getType().getUnspecifiedType() } /** * Gets the nth parameter of this function. There is no result for the @@ -206,7 +206,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function { int getEffectiveNumberOfParameters() { // This method is overridden in `MemberFunction`, where the result is // adjusted to account for the implicit `this` parameter. - result = getNumberOfParameters() + result = this.getNumberOfParameters() } /** @@ -216,7 +216,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function { * return `int p1, int p2`. */ string getParameterString() { - result = concat(int i | | min(getParameter(i).getTypedName()), ", " order by i) + result = concat(int i | | min(this.getParameter(i).getTypedName()), ", " order by i) } /** Gets a call to this function. */ @@ -229,7 +229,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function { */ override FunctionDeclarationEntry getADeclarationEntry() { if fun_decls(_, underlyingElement(this), _, _, _) - then declEntry(result) + then this.declEntry(result) else exists(Function f | this.isConstructedFrom(f) and @@ -250,7 +250,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function { * Gets the location of a `FunctionDeclarationEntry` corresponding to this * declaration. */ - override Location getADeclarationLocation() { result = getADeclarationEntry().getLocation() } + override Location getADeclarationLocation() { result = this.getADeclarationEntry().getLocation() } /** Holds if this Function is a Template specialization. */ predicate isSpecialization() { @@ -265,14 +265,14 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function { * definition, if any. */ override FunctionDeclarationEntry getDefinition() { - result = getADeclarationEntry() and + result = this.getADeclarationEntry() and result.isDefinition() } /** Gets the location of the definition, if any. */ override Location getDefinitionLocation() { - if exists(getDefinition()) - then result = getDefinition().getLocation() + if exists(this.getDefinition()) + then result = this.getDefinition().getLocation() else exists(Function f | this.isConstructedFrom(f) and result = f.getDefinition().getLocation()) } @@ -281,7 +281,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function { * definition, if possible.) */ override Location getLocation() { - if exists(getDefinition()) + if exists(this.getDefinition()) then result = this.getDefinitionLocation() else result = this.getADeclarationLocation() } @@ -299,7 +299,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function { BlockStmt getBlock() { result.getParentScope() = this } /** Holds if this function has an entry point. */ - predicate hasEntryPoint() { exists(getEntryPoint()) } + predicate hasEntryPoint() { exists(this.getEntryPoint()) } /** * Gets the first node in this function's control flow graph. @@ -392,7 +392,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function { * Holds if this function has C linkage, as specified by one of its * declaration entries. For example: `extern "C" void foo();`. */ - predicate hasCLinkage() { getADeclarationEntry().hasCLinkage() } + predicate hasCLinkage() { this.getADeclarationEntry().hasCLinkage() } /** * Holds if this function is constructed from `f` as a result @@ -409,27 +409,27 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function { * several functions that are not linked together have been compiled. An * example would be a project with many 'main' functions. */ - predicate isMultiplyDefined() { strictcount(getFile()) > 1 } + predicate isMultiplyDefined() { strictcount(this.getFile()) > 1 } /** Holds if this function is a varargs function. */ - predicate isVarargs() { hasSpecifier("varargs") } + predicate isVarargs() { this.hasSpecifier("varargs") } /** Gets a type that is specified to be thrown by the function. */ - Type getAThrownType() { result = getADeclarationEntry().getAThrownType() } + Type getAThrownType() { result = this.getADeclarationEntry().getAThrownType() } /** * Gets the `i`th type specified to be thrown by the function. */ - Type getThrownType(int i) { result = getADeclarationEntry().getThrownType(i) } + Type getThrownType(int i) { result = this.getADeclarationEntry().getThrownType(i) } /** Holds if the function has an exception specification. */ - predicate hasExceptionSpecification() { getADeclarationEntry().hasExceptionSpecification() } + predicate hasExceptionSpecification() { this.getADeclarationEntry().hasExceptionSpecification() } /** Holds if this function has a `throw()` exception specification. */ - predicate isNoThrow() { getADeclarationEntry().isNoThrow() } + predicate isNoThrow() { this.getADeclarationEntry().isNoThrow() } /** Holds if this function has a `noexcept` exception specification. */ - predicate isNoExcept() { getADeclarationEntry().isNoExcept() } + predicate isNoExcept() { this.getADeclarationEntry().isNoExcept() } /** * Gets a function that overloads this one. @@ -539,7 +539,7 @@ private predicate candGetAnOverloadNonMember(string name, Namespace namespace, F */ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl { /** Gets the function which is being declared or defined. */ - override Function getDeclaration() { result = getFunction() } + override Function getDeclaration() { result = this.getFunction() } override string getAPrimaryQlClass() { result = "FunctionDeclarationEntry" } @@ -586,7 +586,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl { * case, catch) plus the number of branching expressions (`?`, `&&`, * `||`) plus one. */ - int getCyclomaticComplexity() { result = 1 + cyclomaticComplexityBranches(getBlock()) } + int getCyclomaticComplexity() { result = 1 + cyclomaticComplexityBranches(this.getBlock()) } /** * If this is a function definition, get the block containing the @@ -594,7 +594,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl { */ BlockStmt getBlock() { this.isDefinition() and - result = getFunction().getBlock() and + result = this.getFunction().getBlock() and result.getFile() = this.getFile() } @@ -604,7 +604,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl { */ pragma[noopt] int getNumberOfLines() { - exists(BlockStmt b, Location l, int start, int end, int diff | b = getBlock() | + exists(BlockStmt b, Location l, int start, int end, int diff | b = this.getBlock() | l = b.getLocation() and start = l.getStartLine() and end = l.getEndLine() and @@ -618,7 +618,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl { * declaration. */ ParameterDeclarationEntry getAParameterDeclarationEntry() { - result = getParameterDeclarationEntry(_) + result = this.getParameterDeclarationEntry(_) } /** @@ -639,7 +639,8 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl { * return 'int p1, int p2'. */ string getParameterString() { - result = concat(int i | | min(getParameterDeclarationEntry(i).getTypedName()), ", " order by i) + result = + concat(int i | | min(this.getParameterDeclarationEntry(i).getTypedName()), ", " order by i) } /** @@ -647,10 +648,10 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl { * * `extern "C" void foo();` */ - predicate hasCLinkage() { getASpecifier() = "c_linkage" } + predicate hasCLinkage() { this.getASpecifier() = "c_linkage" } /** Holds if this declaration entry has a void parameter list. */ - predicate hasVoidParamList() { getASpecifier() = "void_param_list" } + predicate hasVoidParamList() { this.getASpecifier() = "void_param_list" } /** Holds if this declaration is also a definition of its function. */ override predicate isDefinition() { fun_def(underlyingElement(this)) } @@ -665,7 +666,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl { predicate isImplicit() { fun_implicit(underlyingElement(this)) } /** Gets a type that is specified to be thrown by the declared function. */ - Type getAThrownType() { result = getThrownType(_) } + Type getAThrownType() { result = this.getThrownType(_) } /** * Gets the `i`th type specified to be thrown by the declared function @@ -690,8 +691,8 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl { predicate hasExceptionSpecification() { fun_decl_throws(underlyingElement(this), _, _) or fun_decl_noexcept(underlyingElement(this), _) or - isNoThrow() or - isNoExcept() + this.isNoThrow() or + this.isNoExcept() } /** @@ -763,7 +764,7 @@ class Operator extends Function { */ class TemplateFunction extends Function { TemplateFunction() { - is_function_template(underlyingElement(this)) and exists(getATemplateArgument()) + is_function_template(underlyingElement(this)) and exists(this.getATemplateArgument()) } override string getAPrimaryQlClass() { result = "TemplateFunction" } diff --git a/cpp/ql/lib/semmle/code/cpp/Include.qll b/cpp/ql/lib/semmle/code/cpp/Include.qll index f21edb2651d..9a120b1013c 100644 --- a/cpp/ql/lib/semmle/code/cpp/Include.qll +++ b/cpp/ql/lib/semmle/code/cpp/Include.qll @@ -23,7 +23,7 @@ class Include extends PreprocessorDirective, @ppd_include { * Gets the token which occurs after `#include`, for example `"filename"` * or ``. */ - string getIncludeText() { result = getHead() } + string getIncludeText() { result = this.getHead() } /** Gets the file directly included by this `#include`. */ File getIncludedFile() { includes(underlyingElement(this), unresolveElement(result)) } @@ -53,7 +53,7 @@ class Include extends PreprocessorDirective, @ppd_include { * ``` */ class IncludeNext extends Include, @ppd_include_next { - override string toString() { result = "#include_next " + getIncludeText() } + override string toString() { result = "#include_next " + this.getIncludeText() } } /** @@ -65,5 +65,5 @@ class IncludeNext extends Include, @ppd_include_next { * ``` */ class Import extends Include, @ppd_objc_import { - override string toString() { result = "#import " + getIncludeText() } + override string toString() { result = "#import " + this.getIncludeText() } } diff --git a/cpp/ql/lib/semmle/code/cpp/Initializer.qll b/cpp/ql/lib/semmle/code/cpp/Initializer.qll index 64607af3393..62af72c1803 100644 --- a/cpp/ql/lib/semmle/code/cpp/Initializer.qll +++ b/cpp/ql/lib/semmle/code/cpp/Initializer.qll @@ -34,8 +34,8 @@ class Initializer extends ControlFlowNode, @initialiser { override predicate fromSource() { not this.getLocation() instanceof UnknownLocation } override string toString() { - if exists(getDeclaration()) - then result = "initializer for " + max(getDeclaration().getName()) + if exists(this.getDeclaration()) + then result = "initializer for " + max(this.getDeclaration().getName()) else result = "initializer" } diff --git a/cpp/ql/lib/semmle/code/cpp/Location.qll b/cpp/ql/lib/semmle/code/cpp/Location.qll index 2a730ea5768..92b358d474c 100644 --- a/cpp/ql/lib/semmle/code/cpp/Location.qll +++ b/cpp/ql/lib/semmle/code/cpp/Location.qll @@ -61,7 +61,7 @@ class Location extends @location { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -79,8 +79,8 @@ class Location extends @location { /** Holds if location `l` is completely contained within this one. */ predicate subsumes(Location l) { - exists(File f | f = getFile() | - exists(int thisStart, int thisEnd | charLoc(f, thisStart, thisEnd) | + exists(File f | f = this.getFile() | + exists(int thisStart, int thisEnd | this.charLoc(f, thisStart, thisEnd) | exists(int lStart, int lEnd | l.charLoc(f, lStart, lEnd) | thisStart <= lStart and lEnd <= thisEnd ) @@ -97,10 +97,10 @@ class Location extends @location { * see `subsumes`. */ predicate charLoc(File f, int start, int end) { - f = getFile() and + f = this.getFile() and exists(int maxCols | maxCols = maxCols(f) | - start = getStartLine() * maxCols + getStartColumn() and - end = getEndLine() * maxCols + getEndColumn() + start = this.getStartLine() * maxCols + this.getStartColumn() and + end = this.getEndLine() * maxCols + this.getEndColumn() ) } } @@ -144,7 +144,7 @@ class Locatable extends Element { } * expressions, one for statements and one for other program elements. */ class UnknownLocation extends Location { - UnknownLocation() { getFile().getAbsolutePath() = "" } + UnknownLocation() { this.getFile().getAbsolutePath() = "" } } /** diff --git a/cpp/ql/lib/semmle/code/cpp/Macro.qll b/cpp/ql/lib/semmle/code/cpp/Macro.qll index aa4b8d41999..6d61ae7be7c 100644 --- a/cpp/ql/lib/semmle/code/cpp/Macro.qll +++ b/cpp/ql/lib/semmle/code/cpp/Macro.qll @@ -44,10 +44,10 @@ class Macro extends PreprocessorDirective, @ppd_define { * Gets the name of the macro. For example, `MAX` in * `#define MAX(x,y) (((x)>(y))?(x):(y))`. */ - string getName() { result = getHead().splitAt("(", 0) } + string getName() { result = this.getHead().splitAt("(", 0) } /** Holds if the macro has name `name`. */ - predicate hasName(string name) { getName() = name } + predicate hasName(string name) { this.getName() = name } } /** @@ -130,7 +130,7 @@ class MacroAccess extends Locatable, @macroinvocation { override string toString() { result = this.getMacro().getHead() } /** Gets the name of the accessed macro. */ - string getMacroName() { result = getMacro().getName() } + string getMacroName() { result = this.getMacro().getName() } } /** @@ -197,8 +197,8 @@ class MacroInvocation extends MacroAccess { * expression. In other cases, it may have multiple results or no results. */ Expr getExpr() { - result = getAnExpandedElement() and - not result.getParent() = getAnExpandedElement() and + result = this.getAnExpandedElement() and + not result.getParent() = this.getAnExpandedElement() and not result instanceof Conversion } @@ -208,8 +208,8 @@ class MacroInvocation extends MacroAccess { * element is not a statement (for example if it is an expression). */ Stmt getStmt() { - result = getAnExpandedElement() and - not result.getParent() = getAnExpandedElement() + result = this.getAnExpandedElement() and + not result.getParent() = this.getAnExpandedElement() } /** @@ -278,7 +278,7 @@ deprecated class MacroInvocationExpr extends Expr { MacroInvocation getInvocation() { result.getExpr() = this } /** Gets the name of the invoked macro. */ - string getMacroName() { result = getInvocation().getMacroName() } + string getMacroName() { result = this.getInvocation().getMacroName() } } /** @@ -298,7 +298,7 @@ deprecated class MacroInvocationStmt extends Stmt { MacroInvocation getInvocation() { result.getStmt() = this } /** Gets the name of the invoked macro. */ - string getMacroName() { result = getInvocation().getMacroName() } + string getMacroName() { result = this.getInvocation().getMacroName() } } /** Holds if `l` is the location of a macro. */ diff --git a/cpp/ql/lib/semmle/code/cpp/MemberFunction.qll b/cpp/ql/lib/semmle/code/cpp/MemberFunction.qll index 63c1406d8a5..03b1704549f 100644 --- a/cpp/ql/lib/semmle/code/cpp/MemberFunction.qll +++ b/cpp/ql/lib/semmle/code/cpp/MemberFunction.qll @@ -36,7 +36,9 @@ class MemberFunction extends Function { * `this` parameter. */ override int getEffectiveNumberOfParameters() { - if isStatic() then result = getNumberOfParameters() else result = getNumberOfParameters() + 1 + if this.isStatic() + then result = this.getNumberOfParameters() + else result = this.getNumberOfParameters() + 1 } /** Holds if this member is private. */ @@ -49,13 +51,13 @@ class MemberFunction extends Function { predicate isPublic() { this.hasSpecifier("public") } /** Holds if this declaration has the lvalue ref-qualifier */ - predicate isLValueRefQualified() { hasSpecifier("&") } + predicate isLValueRefQualified() { this.hasSpecifier("&") } /** Holds if this declaration has the rvalue ref-qualifier */ - predicate isRValueRefQualified() { hasSpecifier("&&") } + predicate isRValueRefQualified() { this.hasSpecifier("&&") } /** Holds if this declaration has a ref-qualifier */ - predicate isRefQualified() { isLValueRefQualified() or isRValueRefQualified() } + predicate isRefQualified() { this.isLValueRefQualified() or this.isRValueRefQualified() } /** Holds if this function overrides that function. */ predicate overrides(MemberFunction that) { @@ -73,10 +75,10 @@ class MemberFunction extends Function { * class body. */ FunctionDeclarationEntry getClassBodyDeclarationEntry() { - if strictcount(getADeclarationEntry()) = 1 - then result = getDefinition() + if strictcount(this.getADeclarationEntry()) = 1 + then result = this.getDefinition() else ( - result = getADeclarationEntry() and result != getDefinition() + result = this.getADeclarationEntry() and result != this.getDefinition() ) } @@ -198,7 +200,7 @@ class Constructor extends MemberFunction { * compiler-generated action which initializes a base class or member * variable. */ - ConstructorInit getAnInitializer() { result = getInitializer(_) } + ConstructorInit getAnInitializer() { result = this.getInitializer(_) } /** * Gets an entry in the constructor's initializer list, or a @@ -220,8 +222,8 @@ class ImplicitConversionFunction extends MemberFunction { functions(underlyingElement(this), _, 4) or // ConversionConstructor (deprecated) - strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and - not hasSpecifier("explicit") + strictcount(Parameter p | p = this.getAParameter() and not p.hasInitializer()) = 1 and + not this.hasSpecifier("explicit") } /** Gets the type this `ImplicitConversionFunction` takes as input. */ @@ -248,8 +250,8 @@ class ImplicitConversionFunction extends MemberFunction { */ deprecated class ConversionConstructor extends Constructor, ImplicitConversionFunction { ConversionConstructor() { - strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and - not hasSpecifier("explicit") + strictcount(Parameter p | p = this.getAParameter() and not p.hasInitializer()) = 1 and + not this.hasSpecifier("explicit") } override string getAPrimaryQlClass() { @@ -301,15 +303,15 @@ class CopyConstructor extends Constructor { hasCopySignature(this) and ( // The rest of the parameters all have default values - forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer()) + forall(int i | i > 0 and exists(this.getParameter(i)) | this.getParameter(i).hasInitializer()) or // or this is a template class, in which case the default values have // not been extracted even if they exist. In that case, we assume that // there are default values present since that is the most common case // in real-world code. - getDeclaringType() instanceof TemplateClass + this.getDeclaringType() instanceof TemplateClass ) and - not exists(getATemplateArgument()) + not exists(this.getATemplateArgument()) } override string getAPrimaryQlClass() { result = "CopyConstructor" } @@ -325,8 +327,8 @@ class CopyConstructor extends Constructor { // type-checked for each template instantiation; if an argument in an // instantiation fails to type-check then the corresponding parameter has // no default argument in the instantiation. - getDeclaringType() instanceof TemplateClass and - getNumberOfParameters() > 1 + this.getDeclaringType() instanceof TemplateClass and + this.getNumberOfParameters() > 1 } } @@ -358,15 +360,15 @@ class MoveConstructor extends Constructor { hasMoveSignature(this) and ( // The rest of the parameters all have default values - forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer()) + forall(int i | i > 0 and exists(this.getParameter(i)) | this.getParameter(i).hasInitializer()) or // or this is a template class, in which case the default values have // not been extracted even if they exist. In that case, we assume that // there are default values present since that is the most common case // in real-world code. - getDeclaringType() instanceof TemplateClass + this.getDeclaringType() instanceof TemplateClass ) and - not exists(getATemplateArgument()) + not exists(this.getATemplateArgument()) } override string getAPrimaryQlClass() { result = "MoveConstructor" } @@ -382,8 +384,8 @@ class MoveConstructor extends Constructor { // type-checked for each template instantiation; if an argument in an // instantiation fails to type-check then the corresponding parameter has // no default argument in the instantiation. - getDeclaringType() instanceof TemplateClass and - getNumberOfParameters() > 1 + this.getDeclaringType() instanceof TemplateClass and + this.getNumberOfParameters() > 1 } } @@ -426,7 +428,7 @@ class Destructor extends MemberFunction { * Gets a compiler-generated action which destructs a base class or member * variable. */ - DestructorDestruction getADestruction() { result = getDestruction(_) } + DestructorDestruction getADestruction() { result = this.getDestruction(_) } /** * Gets a compiler-generated action which destructs a base class or member @@ -475,16 +477,16 @@ class ConversionOperator extends MemberFunction, ImplicitConversionFunction { */ class CopyAssignmentOperator extends Operator { CopyAssignmentOperator() { - hasName("operator=") and + this.hasName("operator=") and ( hasCopySignature(this) or // Unlike CopyConstructor, this member allows a non-reference // parameter. - getParameter(0).getUnspecifiedType() = getDeclaringType() + this.getParameter(0).getUnspecifiedType() = this.getDeclaringType() ) and not exists(this.getParameter(1)) and - not exists(getATemplateArgument()) + not exists(this.getATemplateArgument()) } override string getAPrimaryQlClass() { result = "CopyAssignmentOperator" } @@ -507,10 +509,10 @@ class CopyAssignmentOperator extends Operator { */ class MoveAssignmentOperator extends Operator { MoveAssignmentOperator() { - hasName("operator=") and + this.hasName("operator=") and hasMoveSignature(this) and not exists(this.getParameter(1)) and - not exists(getATemplateArgument()) + not exists(this.getATemplateArgument()) } override string getAPrimaryQlClass() { result = "MoveAssignmentOperator" } diff --git a/cpp/ql/lib/semmle/code/cpp/Namespace.qll b/cpp/ql/lib/semmle/code/cpp/Namespace.qll index d46abc6b4db..47ebc9d35c5 100644 --- a/cpp/ql/lib/semmle/code/cpp/Namespace.qll +++ b/cpp/ql/lib/semmle/code/cpp/Namespace.qll @@ -38,8 +38,8 @@ class Namespace extends NameQualifyingElement, @namespace { * unless the namespace has exactly one declaration entry. */ override Location getLocation() { - if strictcount(getADeclarationEntry()) = 1 - then result = getADeclarationEntry().getLocation() + if strictcount(this.getADeclarationEntry()) = 1 + then result = this.getADeclarationEntry().getLocation() else result instanceof UnknownDefaultLocation } @@ -50,7 +50,7 @@ class Namespace extends NameQualifyingElement, @namespace { predicate hasName(string name) { name = this.getName() } /** Holds if this namespace is anonymous. */ - predicate isAnonymous() { hasName("(unnamed namespace)") } + predicate isAnonymous() { this.hasName("(unnamed namespace)") } /** Gets the name of the parent namespace, if it exists. */ private string getParentName() { @@ -60,9 +60,9 @@ class Namespace extends NameQualifyingElement, @namespace { /** Gets the qualified name of this namespace. For example: `a::b`. */ string getQualifiedName() { - if exists(getParentName()) - then result = getParentNamespace().getQualifiedName() + "::" + getName() - else result = getName() + if exists(this.getParentName()) + then result = this.getParentNamespace().getQualifiedName() + "::" + this.getName() + else result = this.getName() } /** Gets the parent namespace, if any. */ @@ -99,7 +99,7 @@ class Namespace extends NameQualifyingElement, @namespace { /** Gets a version of the `QualifiedName` that is more suitable for display purposes. */ string getFriendlyName() { result = this.getQualifiedName() } - final override string toString() { result = getFriendlyName() } + final override string toString() { result = this.getFriendlyName() } /** Gets a declaration of (part of) this namespace. */ NamespaceDeclarationEntry getADeclarationEntry() { result.getNamespace() = this } diff --git a/cpp/ql/lib/semmle/code/cpp/Parameter.qll b/cpp/ql/lib/semmle/code/cpp/Parameter.qll index b87bfe6a4c7..47b77b542c1 100644 --- a/cpp/ql/lib/semmle/code/cpp/Parameter.qll +++ b/cpp/ql/lib/semmle/code/cpp/Parameter.qll @@ -40,12 +40,12 @@ class Parameter extends LocalScopeVariable, @parameter { */ override string getName() { exists(VariableDeclarationEntry vde | - vde = getANamedDeclarationEntry() and result = vde.getName() + vde = this.getANamedDeclarationEntry() and result = vde.getName() | - vde.isDefinition() or not getANamedDeclarationEntry().isDefinition() + vde.isDefinition() or not this.getANamedDeclarationEntry().isDefinition() ) or - not exists(getANamedDeclarationEntry()) and + not exists(this.getANamedDeclarationEntry()) and result = "(unnamed parameter " + this.getIndex().toString() + ")" } @@ -58,8 +58,12 @@ class Parameter extends LocalScopeVariable, @parameter { */ string getTypedName() { exists(string typeString, string nameString | - (if exists(getType().getName()) then typeString = getType().getName() else typeString = "") and - (if exists(getName()) then nameString = getName() else nameString = "") and + ( + if exists(this.getType().getName()) + then typeString = this.getType().getName() + else typeString = "" + ) and + (if exists(this.getName()) then nameString = this.getName() else nameString = "") and ( if typeString != "" and nameString != "" then result = typeString + " " + nameString @@ -69,7 +73,7 @@ class Parameter extends LocalScopeVariable, @parameter { } private VariableDeclarationEntry getANamedDeclarationEntry() { - result = getAnEffectiveDeclarationEntry() and result.getName() != "" + result = this.getAnEffectiveDeclarationEntry() and result.getName() != "" } /** @@ -82,13 +86,13 @@ class Parameter extends LocalScopeVariable, @parameter { * own). */ private VariableDeclarationEntry getAnEffectiveDeclarationEntry() { - if getFunction().isConstructedFrom(_) + if this.getFunction().isConstructedFrom(_) then exists(Function prototypeInstantiation | - prototypeInstantiation.getParameter(getIndex()) = result.getVariable() and - getFunction().isConstructedFrom(prototypeInstantiation) + prototypeInstantiation.getParameter(this.getIndex()) = result.getVariable() and + this.getFunction().isConstructedFrom(prototypeInstantiation) ) - else result = getADeclarationEntry() + else result = this.getADeclarationEntry() } /** @@ -114,7 +118,7 @@ class Parameter extends LocalScopeVariable, @parameter { * `getName()` is not "(unnamed parameter i)" (where `i` is the index * of the parameter). */ - predicate isNamed() { exists(getANamedDeclarationEntry()) } + predicate isNamed() { exists(this.getANamedDeclarationEntry()) } /** * Gets the function to which this parameter belongs, if it is a function @@ -157,9 +161,9 @@ class Parameter extends LocalScopeVariable, @parameter { */ override Location getLocation() { exists(VariableDeclarationEntry vde | - vde = getAnEffectiveDeclarationEntry() and result = vde.getLocation() + vde = this.getAnEffectiveDeclarationEntry() and result = vde.getLocation() | - vde.isDefinition() or not getAnEffectiveDeclarationEntry().isDefinition() + vde.isDefinition() or not this.getAnEffectiveDeclarationEntry().isDefinition() ) } } diff --git a/cpp/ql/lib/semmle/code/cpp/Preprocessor.qll b/cpp/ql/lib/semmle/code/cpp/Preprocessor.qll index 2389db07f2a..91b7aa1aab9 100644 --- a/cpp/ql/lib/semmle/code/cpp/Preprocessor.qll +++ b/cpp/ql/lib/semmle/code/cpp/Preprocessor.qll @@ -29,8 +29,8 @@ class PreprocessorDirective extends Locatable, @preprocdirect { PreprocessorBranch getAGuard() { exists(PreprocessorEndif e, int line | result.getEndIf() = e and - e.getFile() = getFile() and - result.getFile() = getFile() and + e.getFile() = this.getFile() and + result.getFile() = this.getFile() and line = this.getLocation().getStartLine() and result.getLocation().getStartLine() < line and line < e.getLocation().getEndLine() @@ -69,7 +69,9 @@ class PreprocessorBranchDirective extends PreprocessorDirective, TPreprocessorBr * directives in different translation units, then there can be more than * one result. */ - PreprocessorEndif getEndIf() { preprocpair(unresolveElement(getIf()), unresolveElement(result)) } + PreprocessorEndif getEndIf() { + preprocpair(unresolveElement(this.getIf()), unresolveElement(result)) + } /** * Gets the next `#elif`, `#else` or `#endif` matching this branching @@ -137,7 +139,7 @@ class PreprocessorBranch extends PreprocessorBranchDirective, @ppd_branch { * which evaluated it, or was not taken by any translation unit which * evaluated it. */ - predicate wasPredictable() { not (wasTaken() and wasNotTaken()) } + predicate wasPredictable() { not (this.wasTaken() and this.wasNotTaken()) } } /** @@ -268,7 +270,7 @@ class PreprocessorUndef extends PreprocessorDirective, @ppd_undef { /** * Gets the name of the macro that is undefined. */ - string getName() { result = getHead() } + string getName() { result = this.getHead() } } /** diff --git a/cpp/ql/lib/semmle/code/cpp/Print.qll b/cpp/ql/lib/semmle/code/cpp/Print.qll index f8d30f55a88..64ae5b960d1 100644 --- a/cpp/ql/lib/semmle/code/cpp/Print.qll +++ b/cpp/ql/lib/semmle/code/cpp/Print.qll @@ -105,8 +105,8 @@ private class DumpType extends Type { // for a `SpecifiedType`, insert the qualifiers after // `getDeclaratorSuffixBeforeQualifiers()`. result = - getTypeSpecifier() + getDeclaratorPrefix() + getDeclaratorSuffixBeforeQualifiers() + - getDeclaratorSuffix() + this.getTypeSpecifier() + this.getDeclaratorPrefix() + + this.getDeclaratorSuffixBeforeQualifiers() + this.getDeclaratorSuffix() } /** @@ -147,29 +147,35 @@ private class DumpType extends Type { } private class BuiltInDumpType extends DumpType, BuiltInType { - override string getTypeSpecifier() { result = toString() } + override string getTypeSpecifier() { result = this.toString() } } private class IntegralDumpType extends BuiltInDumpType, IntegralType { - override string getTypeSpecifier() { result = getCanonicalArithmeticType().toString() } + override string getTypeSpecifier() { result = this.getCanonicalArithmeticType().toString() } } private class DerivedDumpType extends DumpType, DerivedType { - override string getTypeSpecifier() { result = getBaseType().(DumpType).getTypeSpecifier() } + override string getTypeSpecifier() { result = this.getBaseType().(DumpType).getTypeSpecifier() } override string getDeclaratorSuffixBeforeQualifiers() { - result = getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() + result = this.getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() } - override string getDeclaratorSuffix() { result = getBaseType().(DumpType).getDeclaratorSuffix() } + override string getDeclaratorSuffix() { + result = this.getBaseType().(DumpType).getDeclaratorSuffix() + } } private class DecltypeDumpType extends DumpType, Decltype { - override string getTypeSpecifier() { result = getBaseType().(DumpType).getTypeSpecifier() } + override string getTypeSpecifier() { result = this.getBaseType().(DumpType).getTypeSpecifier() } - override string getDeclaratorPrefix() { result = getBaseType().(DumpType).getDeclaratorPrefix() } + override string getDeclaratorPrefix() { + result = this.getBaseType().(DumpType).getDeclaratorPrefix() + } - override string getDeclaratorSuffix() { result = getBaseType().(DumpType).getDeclaratorSuffix() } + override string getDeclaratorSuffix() { + result = this.getBaseType().(DumpType).getDeclaratorSuffix() + } } private class PointerIshDumpType extends DerivedDumpType { @@ -180,10 +186,10 @@ private class PointerIshDumpType extends DerivedDumpType { override string getDeclaratorPrefix() { exists(string declarator | - result = getBaseType().(DumpType).getDeclaratorPrefix() + declarator and - if getBaseType().getUnspecifiedType() instanceof ArrayType - then declarator = "(" + getDeclaratorToken() + ")" - else declarator = getDeclaratorToken() + result = this.getBaseType().(DumpType).getDeclaratorPrefix() + declarator and + if this.getBaseType().getUnspecifiedType() instanceof ArrayType + then declarator = "(" + this.getDeclaratorToken() + ")" + else declarator = this.getDeclaratorToken() ) } @@ -206,13 +212,13 @@ private class RValueReferenceDumpType extends PointerIshDumpType, RValueReferenc } private class PointerToMemberDumpType extends DumpType, PointerToMemberType { - override string getTypeSpecifier() { result = getBaseType().(DumpType).getTypeSpecifier() } + override string getTypeSpecifier() { result = this.getBaseType().(DumpType).getTypeSpecifier() } override string getDeclaratorPrefix() { exists(string declarator, string parenDeclarator, Type baseType | - declarator = getClass().(DumpType).getTypeIdentityString() + "::*" and - result = getBaseType().(DumpType).getDeclaratorPrefix() + " " + parenDeclarator and - baseType = getBaseType().getUnspecifiedType() and + declarator = this.getClass().(DumpType).getTypeIdentityString() + "::*" and + result = this.getBaseType().(DumpType).getDeclaratorPrefix() + " " + parenDeclarator and + baseType = this.getBaseType().getUnspecifiedType() and if baseType instanceof ArrayType or baseType instanceof RoutineType then parenDeclarator = "(" + declarator else parenDeclarator = declarator @@ -221,38 +227,44 @@ private class PointerToMemberDumpType extends DumpType, PointerToMemberType { override string getDeclaratorSuffixBeforeQualifiers() { exists(Type baseType | - baseType = getBaseType().getUnspecifiedType() and + baseType = this.getBaseType().getUnspecifiedType() and if baseType instanceof ArrayType or baseType instanceof RoutineType - then result = ")" + getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() - else result = getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() + then result = ")" + this.getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() + else result = this.getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() ) } - override string getDeclaratorSuffix() { result = getBaseType().(DumpType).getDeclaratorSuffix() } + override string getDeclaratorSuffix() { + result = this.getBaseType().(DumpType).getDeclaratorSuffix() + } } private class ArrayDumpType extends DerivedDumpType, ArrayType { - override string getDeclaratorPrefix() { result = getBaseType().(DumpType).getDeclaratorPrefix() } + override string getDeclaratorPrefix() { + result = this.getBaseType().(DumpType).getDeclaratorPrefix() + } override string getDeclaratorSuffixBeforeQualifiers() { - if exists(getArraySize()) + if exists(this.getArraySize()) then result = - "[" + getArraySize().toString() + "]" + - getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() - else result = "[]" + getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() + "[" + this.getArraySize().toString() + "]" + + this.getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() + else result = "[]" + this.getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() } } private class FunctionPointerIshDumpType extends DerivedDumpType, FunctionPointerIshType { override string getDeclaratorSuffixBeforeQualifiers() { - result = ")" + getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() + result = ")" + this.getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() } - override string getDeclaratorSuffix() { result = getBaseType().(DumpType).getDeclaratorSuffix() } + override string getDeclaratorSuffix() { + result = this.getBaseType().(DumpType).getDeclaratorSuffix() + } override string getDeclaratorPrefix() { - result = getBaseType().(DumpType).getDeclaratorPrefix() + "(" + getDeclaratorToken() + result = this.getBaseType().(DumpType).getDeclaratorPrefix() + "(" + this.getDeclaratorToken() } /** @@ -274,10 +286,10 @@ private class BlockDumpType extends FunctionPointerIshDumpType, BlockType { } private class RoutineDumpType extends DumpType, RoutineType { - override string getTypeSpecifier() { result = getReturnType().(DumpType).getTypeSpecifier() } + override string getTypeSpecifier() { result = this.getReturnType().(DumpType).getTypeSpecifier() } override string getDeclaratorPrefix() { - result = getReturnType().(DumpType).getDeclaratorPrefix() + result = this.getReturnType().(DumpType).getDeclaratorPrefix() } language[monotonicAggregates] @@ -285,39 +297,41 @@ private class RoutineDumpType extends DumpType, RoutineType { result = "(" + concat(int i | - exists(getParameterType(i)) + exists(this.getParameterType(i)) | - getParameterTypeString(getParameterType(i)), ", " order by i + getParameterTypeString(this.getParameterType(i)), ", " order by i ) + ")" } override string getDeclaratorSuffix() { result = - getReturnType().(DumpType).getDeclaratorSuffixBeforeQualifiers() + - getReturnType().(DumpType).getDeclaratorSuffix() + this.getReturnType().(DumpType).getDeclaratorSuffixBeforeQualifiers() + + this.getReturnType().(DumpType).getDeclaratorSuffix() } } private class SpecifiedDumpType extends DerivedDumpType, SpecifiedType { override string getDeclaratorPrefix() { exists(string basePrefix | - basePrefix = getBaseType().(DumpType).getDeclaratorPrefix() and - if getBaseType().getUnspecifiedType() instanceof RoutineType + basePrefix = this.getBaseType().(DumpType).getDeclaratorPrefix() and + if this.getBaseType().getUnspecifiedType() instanceof RoutineType then result = basePrefix - else result = basePrefix + " " + getSpecifierString() + else result = basePrefix + " " + this.getSpecifierString() ) } override string getDeclaratorSuffixBeforeQualifiers() { exists(string baseSuffix | - baseSuffix = getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() and - if getBaseType().getUnspecifiedType() instanceof RoutineType - then result = baseSuffix + " " + getSpecifierString() + baseSuffix = this.getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() and + if this.getBaseType().getUnspecifiedType() instanceof RoutineType + then result = baseSuffix + " " + this.getSpecifierString() else result = baseSuffix ) } - override string getDeclaratorSuffix() { result = getBaseType().(DumpType).getDeclaratorSuffix() } + override string getDeclaratorSuffix() { + result = this.getBaseType().(DumpType).getDeclaratorSuffix() + } } private class UserDumpType extends DumpType, DumpDeclaration, UserType { @@ -330,18 +344,18 @@ private class UserDumpType extends DumpType, DumpDeclaration, UserType { // "lambda [] type at line 12, col. 40" // Use `min(getSimpleName())` to work around an extractor bug where a lambda can have different names // from different compilation units. - simpleName = "(" + min(getSimpleName()) + ")" - else simpleName = getSimpleName() + simpleName = "(" + min(this.getSimpleName()) + ")" + else simpleName = this.getSimpleName() ) and - result = getScopePrefix(this) + simpleName + getTemplateArgumentsString() + result = getScopePrefix(this) + simpleName + this.getTemplateArgumentsString() ) } - override string getTypeSpecifier() { result = getIdentityString() } + override string getTypeSpecifier() { result = this.getIdentityString() } } private class DumpProxyClass extends UserDumpType, ProxyClass { - override string getIdentityString() { result = getName() } + override string getIdentityString() { result = this.getName() } } private class DumpVariable extends DumpDeclaration, Variable { @@ -360,9 +374,9 @@ private class DumpVariable extends DumpDeclaration, Variable { private class DumpFunction extends DumpDeclaration, Function { override string getIdentityString() { result = - getType().(DumpType).getTypeSpecifier() + getType().(DumpType).getDeclaratorPrefix() + " " + - getScopePrefix(this) + getName() + getTemplateArgumentsString() + - getDeclaratorSuffixBeforeQualifiers() + getDeclaratorSuffix() + this.getType().(DumpType).getTypeSpecifier() + this.getType().(DumpType).getDeclaratorPrefix() + + " " + getScopePrefix(this) + this.getName() + this.getTemplateArgumentsString() + + this.getDeclaratorSuffixBeforeQualifiers() + this.getDeclaratorSuffix() } language[monotonicAggregates] @@ -370,28 +384,29 @@ private class DumpFunction extends DumpDeclaration, Function { result = "(" + concat(int i | - exists(getParameter(i).getType()) + exists(this.getParameter(i).getType()) | - getParameterTypeString(getParameter(i).getType()), ", " order by i - ) + ")" + getQualifierString() + getParameterTypeString(this.getParameter(i).getType()), ", " order by i + ) + ")" + this.getQualifierString() } private string getQualifierString() { - if exists(getACVQualifier()) + if exists(this.getACVQualifier()) then - result = " " + strictconcat(string qualifier | qualifier = getACVQualifier() | qualifier, " ") + result = + " " + strictconcat(string qualifier | qualifier = this.getACVQualifier() | qualifier, " ") else result = "" } private string getACVQualifier() { - result = getASpecifier().getName() and + result = this.getASpecifier().getName() and result = ["const", "volatile"] } private string getDeclaratorSuffix() { result = - getType().(DumpType).getDeclaratorSuffixBeforeQualifiers() + - getType().(DumpType).getDeclaratorSuffix() + this.getType().(DumpType).getDeclaratorSuffixBeforeQualifiers() + + this.getType().(DumpType).getDeclaratorSuffix() } } diff --git a/cpp/ql/lib/semmle/code/cpp/Specifier.qll b/cpp/ql/lib/semmle/code/cpp/Specifier.qll index 4a425b690f4..fe2919c3ed6 100644 --- a/cpp/ql/lib/semmle/code/cpp/Specifier.qll +++ b/cpp/ql/lib/semmle/code/cpp/Specifier.qll @@ -31,11 +31,7 @@ class Specifier extends Element, @specifier { * A C/C++ function specifier: `inline`, `virtual`, or `explicit`. */ class FunctionSpecifier extends Specifier { - FunctionSpecifier() { - this.hasName("inline") or - this.hasName("virtual") or - this.hasName("explicit") - } + FunctionSpecifier() { this.hasName(["inline", "virtual", "explicit"]) } override string getAPrimaryQlClass() { result = "FunctionSpecifier" } } @@ -45,13 +41,7 @@ class FunctionSpecifier extends Specifier { * or `mutable". */ class StorageClassSpecifier extends Specifier { - StorageClassSpecifier() { - this.hasName("auto") or - this.hasName("register") or - this.hasName("static") or - this.hasName("extern") or - this.hasName("mutable") - } + StorageClassSpecifier() { this.hasName(["auto", "register", "static", "extern", "mutable"]) } override string getAPrimaryQlClass() { result = "StorageClassSpecifier" } } @@ -60,11 +50,7 @@ class StorageClassSpecifier extends Specifier { * A C++ access specifier: `public`, `protected`, or `private`. */ class AccessSpecifier extends Specifier { - AccessSpecifier() { - this.hasName("public") or - this.hasName("protected") or - this.hasName("private") - } + AccessSpecifier() { this.hasName(["public", "protected", "private"]) } /** * Gets the visibility of a field with access specifier `this` if it is @@ -140,7 +126,7 @@ class Attribute extends Element, @attribute { AttributeArgument getArgument(int i) { result.getAttribute() = this and result.getIndex() = i } /** Gets an argument of the attribute. */ - AttributeArgument getAnArgument() { result = getArgument(_) } + AttributeArgument getAnArgument() { result = this.getArgument(_) } } /** @@ -166,7 +152,7 @@ class StdAttribute extends Attribute, @stdattribute { * Holds if this attribute has the given namespace and name. */ predicate hasQualifiedName(string namespace, string name) { - namespace = getNamespace() and hasName(name) + namespace = this.getNamespace() and this.hasName(name) } } @@ -184,7 +170,7 @@ class Declspec extends Attribute, @declspec { } */ class MicrosoftAttribute extends Attribute, @msattribute { AttributeArgument getNamedArgument(string name) { - result = getAnArgument() and result.getName() = name + result = this.getAnArgument() and result.getName() = name } } @@ -212,13 +198,13 @@ class AlignAs extends Attribute, @alignas { * ``` */ class FormatAttribute extends GnuAttribute { - FormatAttribute() { getName() = "format" } + FormatAttribute() { this.getName() = "format" } /** * Gets the archetype of this format attribute, for example * `"printf"`. */ - string getArchetype() { result = getArgument(0).getValueText() } + string getArchetype() { result = this.getArgument(0).getValueText() } /** * Gets the index in (1-based) format attribute notation associated @@ -236,7 +222,7 @@ class FormatAttribute extends GnuAttribute { * Gets the (0-based) index of the format string, * according to this attribute. */ - int getFormatIndex() { result = getArgument(1).getValueInt() - firstArgumentNumber() } + int getFormatIndex() { result = this.getArgument(1).getValueInt() - this.firstArgumentNumber() } /** * Gets the (0-based) index of the first format argument (if any), @@ -244,8 +230,8 @@ class FormatAttribute extends GnuAttribute { */ int getFirstFormatArgIndex() { exists(int val | - val = getArgument(2).getValueInt() and - result = val - firstArgumentNumber() and + val = this.getArgument(2).getValueInt() and + result = val - this.firstArgumentNumber() and not val = 0 // indicates a `vprintf` style format function with arguments not directly available. ) } @@ -277,7 +263,7 @@ class AttributeArgument extends Element, @attribute_arg { /** * Gets the value of this argument, if its value is integral. */ - int getValueInt() { result = getValueText().toInt() } + int getValueInt() { result = this.getValueText().toInt() } /** * Gets the value of this argument, if its value is a type. @@ -304,11 +290,11 @@ class AttributeArgument extends Element, @attribute_arg { then result = "empty argument" else exists(string prefix, string tail | - (if exists(getName()) then prefix = getName() + "=" else prefix = "") and + (if exists(this.getName()) then prefix = this.getName() + "=" else prefix = "") and ( if exists(@attribute_arg_type self | self = underlyingElement(this)) - then tail = getValueType().getName() - else tail = getValueText() + then tail = this.getValueType().getName() + else tail = this.getValueText() ) and result = prefix + tail ) diff --git a/cpp/ql/lib/semmle/code/cpp/Struct.qll b/cpp/ql/lib/semmle/code/cpp/Struct.qll index 50a208894b4..5465472374f 100644 --- a/cpp/ql/lib/semmle/code/cpp/Struct.qll +++ b/cpp/ql/lib/semmle/code/cpp/Struct.qll @@ -41,7 +41,7 @@ class Struct extends Class { * ``` */ class LocalStruct extends Struct { - LocalStruct() { isLocal() } + LocalStruct() { this.isLocal() } override string getAPrimaryQlClass() { not this instanceof LocalUnion and result = "LocalStruct" } } diff --git a/cpp/ql/lib/semmle/code/cpp/TestFile.qll b/cpp/ql/lib/semmle/code/cpp/TestFile.qll index b9e3fe3a614..a2e496ab019 100644 --- a/cpp/ql/lib/semmle/code/cpp/TestFile.qll +++ b/cpp/ql/lib/semmle/code/cpp/TestFile.qll @@ -10,8 +10,8 @@ import semmle.code.cpp.File */ private class GoogleTestHeader extends File { GoogleTestHeader() { - getBaseName() = "gtest.h" and - getParentContainer().getBaseName() = "gtest" + this.getBaseName() = "gtest.h" and + this.getParentContainer().getBaseName() = "gtest" } } @@ -30,8 +30,8 @@ private class GoogleTest extends MacroInvocation { */ private class BoostTestFolder extends Folder { BoostTestFolder() { - getBaseName() = "test" and - getParentContainer().getBaseName() = "boost" + this.getBaseName() = "test" and + this.getParentContainer().getBaseName() = "boost" } } @@ -49,7 +49,7 @@ private class BoostTest extends MacroInvocation { * The `cppunit` directory. */ private class CppUnitFolder extends Folder { - CppUnitFolder() { getBaseName() = "cppunit" } + CppUnitFolder() { this.getBaseName() = "cppunit" } } /** @@ -57,8 +57,8 @@ private class CppUnitFolder extends Folder { */ private class CppUnitClass extends Class { CppUnitClass() { - getFile().getParentContainer+() instanceof CppUnitFolder and - getNamespace().getParentNamespace*().getName() = "CppUnit" + this.getFile().getParentContainer+() instanceof CppUnitFolder and + this.getNamespace().getParentNamespace*().getName() = "CppUnit" } } diff --git a/cpp/ql/lib/semmle/code/cpp/Type.qll b/cpp/ql/lib/semmle/code/cpp/Type.qll index bf3defd4f40..f8552144ea8 100644 --- a/cpp/ql/lib/semmle/code/cpp/Type.qll +++ b/cpp/ql/lib/semmle/code/cpp/Type.qll @@ -81,7 +81,7 @@ class Type extends Locatable, @type { * Holds if this type refers to type `t` (by default, * a type always refers to itself). */ - predicate refersTo(Type t) { refersToDirectly*(t) } + predicate refersTo(Type t) { this.refersToDirectly*(t) } /** * Holds if this type refers to type `t` directly. @@ -1080,11 +1080,11 @@ class DerivedType extends Type, @derivedtype { override predicate refersToDirectly(Type t) { t = this.getBaseType() } - override predicate involvesReference() { getBaseType().involvesReference() } + override predicate involvesReference() { this.getBaseType().involvesReference() } - override predicate involvesTemplateParameter() { getBaseType().involvesTemplateParameter() } + override predicate involvesTemplateParameter() { this.getBaseType().involvesTemplateParameter() } - override Type stripType() { result = getBaseType().stripType() } + override Type stripType() { result = this.getBaseType().stripType() } /** * Holds if this type has the `__autoreleasing` specifier or if it points to @@ -1165,33 +1165,35 @@ class Decltype extends Type, @decltype { */ predicate parenthesesWouldChangeMeaning() { decltypes(underlyingElement(this), _, _, true) } - override Type getUnderlyingType() { result = getBaseType().getUnderlyingType() } + override Type getUnderlyingType() { result = this.getBaseType().getUnderlyingType() } - override Type stripTopLevelSpecifiers() { result = getBaseType().stripTopLevelSpecifiers() } + override Type stripTopLevelSpecifiers() { result = this.getBaseType().stripTopLevelSpecifiers() } - override Type stripType() { result = getBaseType().stripType() } + override Type stripType() { result = this.getBaseType().stripType() } - override Type resolveTypedefs() { result = getBaseType().resolveTypedefs() } + override Type resolveTypedefs() { result = this.getBaseType().resolveTypedefs() } - override Location getLocation() { result = getExpr().getLocation() } + override Location getLocation() { result = this.getExpr().getLocation() } override string toString() { result = "decltype(...)" } override string getName() { none() } - override int getSize() { result = getBaseType().getSize() } + override int getSize() { result = this.getBaseType().getSize() } - override int getAlignment() { result = getBaseType().getAlignment() } + override int getAlignment() { result = this.getBaseType().getAlignment() } - override int getPointerIndirectionLevel() { result = getBaseType().getPointerIndirectionLevel() } + override int getPointerIndirectionLevel() { + result = this.getBaseType().getPointerIndirectionLevel() + } override string explain() { result = "decltype resulting in {" + this.getBaseType().explain() + "}" } - override predicate involvesReference() { getBaseType().involvesReference() } + override predicate involvesReference() { this.getBaseType().involvesReference() } - override predicate involvesTemplateParameter() { getBaseType().involvesTemplateParameter() } + override predicate involvesTemplateParameter() { this.getBaseType().involvesTemplateParameter() } override predicate isDeeplyConst() { this.getBaseType().isDeeplyConst() } @@ -1223,7 +1225,7 @@ class PointerType extends DerivedType { override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConst() } override Type resolveTypedefs() { - result.(PointerType).getBaseType() = getBaseType().resolveTypedefs() + result.(PointerType).getBaseType() = this.getBaseType().resolveTypedefs() } } @@ -1240,7 +1242,9 @@ class ReferenceType extends DerivedType { override string getAPrimaryQlClass() { result = "ReferenceType" } - override int getPointerIndirectionLevel() { result = getBaseType().getPointerIndirectionLevel() } + override int getPointerIndirectionLevel() { + result = this.getBaseType().getPointerIndirectionLevel() + } override string explain() { result = "reference to {" + this.getBaseType().explain() + "}" } @@ -1251,7 +1255,7 @@ class ReferenceType extends DerivedType { override predicate involvesReference() { any() } override Type resolveTypedefs() { - result.(ReferenceType).getBaseType() = getBaseType().resolveTypedefs() + result.(ReferenceType).getBaseType() = this.getBaseType().resolveTypedefs() } } @@ -1330,11 +1334,11 @@ class SpecifiedType extends DerivedType { } override Type resolveTypedefs() { - result.(SpecifiedType).getBaseType() = getBaseType().resolveTypedefs() and - result.getASpecifier() = getASpecifier() + result.(SpecifiedType).getBaseType() = this.getBaseType().resolveTypedefs() and + result.getASpecifier() = this.getASpecifier() } - override Type stripTopLevelSpecifiers() { result = getBaseType().stripTopLevelSpecifiers() } + override Type stripTopLevelSpecifiers() { result = this.getBaseType().stripTopLevelSpecifiers() } } /** @@ -1433,7 +1437,8 @@ class GNUVectorType extends DerivedType { override int getAlignment() { arraysizes(underlyingElement(this), _, _, result) } override string explain() { - result = "GNU " + getNumElements() + " element vector of {" + this.getBaseType().explain() + "}" + result = + "GNU " + this.getNumElements() + " element vector of {" + this.getBaseType().explain() + "}" } override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConst() } @@ -1468,7 +1473,9 @@ class FunctionReferenceType extends FunctionPointerIshType { override string getAPrimaryQlClass() { result = "FunctionReferenceType" } - override int getPointerIndirectionLevel() { result = getBaseType().getPointerIndirectionLevel() } + override int getPointerIndirectionLevel() { + result = this.getBaseType().getPointerIndirectionLevel() + } override string explain() { result = "reference to {" + this.getBaseType().(RoutineType).explain() + "}" @@ -1535,8 +1542,8 @@ class FunctionPointerIshType extends DerivedType { int getNumberOfParameters() { result = count(int i | exists(this.getParameterType(i))) } override predicate involvesTemplateParameter() { - getReturnType().involvesTemplateParameter() or - getAParameterType().involvesTemplateParameter() + this.getReturnType().involvesTemplateParameter() or + this.getAParameterType().involvesTemplateParameter() } override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConst() } @@ -1581,7 +1588,7 @@ class PointerToMemberType extends Type, @ptrtomember { this.getBaseType().explain() + "}" } - override predicate involvesTemplateParameter() { getBaseType().involvesTemplateParameter() } + override predicate involvesTemplateParameter() { this.getBaseType().involvesTemplateParameter() } override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConst() } } @@ -1650,7 +1657,6 @@ class RoutineType extends Type, @routinetype { i = 0 and result = "" and not exists(this.getAParameterType()) or ( - exists(this.getParameterType(i)) and if i < max(int j | exists(this.getParameterType(j))) then // Not the last one @@ -1671,8 +1677,8 @@ class RoutineType extends Type, @routinetype { override predicate isDeeplyConstBelow() { none() } // Current limitation: no such thing as a const routine type override predicate involvesTemplateParameter() { - getReturnType().involvesTemplateParameter() or - getAParameterType().involvesTemplateParameter() + this.getReturnType().involvesTemplateParameter() or + this.getAParameterType().involvesTemplateParameter() } } diff --git a/cpp/ql/lib/semmle/code/cpp/TypedefType.qll b/cpp/ql/lib/semmle/code/cpp/TypedefType.qll index aaf452ce4bb..51bcf6f6127 100644 --- a/cpp/ql/lib/semmle/code/cpp/TypedefType.qll +++ b/cpp/ql/lib/semmle/code/cpp/TypedefType.qll @@ -25,7 +25,7 @@ class TypedefType extends UserType { override Type getUnderlyingType() { result = this.getBaseType().getUnderlyingType() } - override Type stripTopLevelSpecifiers() { result = getBaseType().stripTopLevelSpecifiers() } + override Type stripTopLevelSpecifiers() { result = this.getBaseType().stripTopLevelSpecifiers() } override int getSize() { result = this.getBaseType().getSize() } @@ -43,11 +43,11 @@ class TypedefType extends UserType { result = this.getBaseType().getASpecifier() } - override predicate involvesReference() { getBaseType().involvesReference() } + override predicate involvesReference() { this.getBaseType().involvesReference() } - override Type resolveTypedefs() { result = getBaseType().resolveTypedefs() } + override Type resolveTypedefs() { result = this.getBaseType().resolveTypedefs() } - override Type stripType() { result = getBaseType().stripType() } + override Type stripType() { result = this.getBaseType().stripType() } } /** @@ -90,7 +90,7 @@ class UsingAliasTypedefType extends TypedefType { * ``` */ class LocalTypedefType extends TypedefType { - LocalTypedefType() { isLocal() } + LocalTypedefType() { this.isLocal() } override string getAPrimaryQlClass() { result = "LocalTypedefType" } } diff --git a/cpp/ql/lib/semmle/code/cpp/Union.qll b/cpp/ql/lib/semmle/code/cpp/Union.qll index 6dcb2f0796c..2f1b5b07b25 100644 --- a/cpp/ql/lib/semmle/code/cpp/Union.qll +++ b/cpp/ql/lib/semmle/code/cpp/Union.qll @@ -37,7 +37,7 @@ class Union extends Struct { * ``` */ class LocalUnion extends Union { - LocalUnion() { isLocal() } + LocalUnion() { this.isLocal() } override string getAPrimaryQlClass() { result = "LocalUnion" } } diff --git a/cpp/ql/lib/semmle/code/cpp/UserType.qll b/cpp/ql/lib/semmle/code/cpp/UserType.qll index 2ab0603f06c..13697722190 100644 --- a/cpp/ql/lib/semmle/code/cpp/UserType.qll +++ b/cpp/ql/lib/semmle/code/cpp/UserType.qll @@ -30,19 +30,19 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @ * Gets the simple name of this type, without any template parameters. For example * if the name of the type is `"myType"`, the simple name is just `"myType"`. */ - string getSimpleName() { result = getName().regexpReplaceAll("<.*", "") } + string getSimpleName() { result = this.getName().regexpReplaceAll("<.*", "") } override predicate hasName(string name) { usertypes(underlyingElement(this), name, _) } /** Holds if this type is anonymous. */ - predicate isAnonymous() { getName().matches("(unnamed%") } + predicate isAnonymous() { this.getName().matches("(unnamed%") } override predicate hasSpecifier(string s) { Type.super.hasSpecifier(s) } override Specifier getASpecifier() { result = Type.super.getASpecifier() } override Location getLocation() { - if hasDefinition() + if this.hasDefinition() then result = this.getDefinitionLocation() else result = this.getADeclarationLocation() } @@ -53,16 +53,16 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @ else exists(Class t | this.(Class).isConstructedFrom(t) and result = t.getADeclarationEntry()) } - override Location getADeclarationLocation() { result = getADeclarationEntry().getLocation() } + override Location getADeclarationLocation() { result = this.getADeclarationEntry().getLocation() } override TypeDeclarationEntry getDefinition() { - result = getADeclarationEntry() and + result = this.getADeclarationEntry() and result.isDefinition() } override Location getDefinitionLocation() { - if exists(getDefinition()) - then result = getDefinition().getLocation() + if exists(this.getDefinition()) + then result = this.getDefinition().getLocation() else exists(Class t | this.(Class).isConstructedFrom(t) and result = t.getDefinition().getLocation() @@ -80,7 +80,7 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @ * Holds if this is a local type (that is, a type that has a directly-enclosing * function). */ - predicate isLocal() { exists(getEnclosingFunction()) } + predicate isLocal() { exists(this.getEnclosingFunction()) } /* * Dummy implementations of inherited methods. This class must not be @@ -107,9 +107,9 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @ * ``` */ class TypeDeclarationEntry extends DeclarationEntry, @type_decl { - override UserType getDeclaration() { result = getType() } + override UserType getDeclaration() { result = this.getType() } - override string getName() { result = getType().getName() } + override string getName() { result = this.getType().getName() } override string getAPrimaryQlClass() { result = "TypeDeclarationEntry" } diff --git a/cpp/ql/lib/semmle/code/cpp/Variable.qll b/cpp/ql/lib/semmle/code/cpp/Variable.qll index 12e25f33afe..b0c9bac7f66 100644 --- a/cpp/ql/lib/semmle/code/cpp/Variable.qll +++ b/cpp/ql/lib/semmle/code/cpp/Variable.qll @@ -104,17 +104,17 @@ class Variable extends Declaration, @variable { override VariableDeclarationEntry getADeclarationEntry() { result.getDeclaration() = this } - override Location getADeclarationLocation() { result = getADeclarationEntry().getLocation() } + override Location getADeclarationLocation() { result = this.getADeclarationEntry().getLocation() } override VariableDeclarationEntry getDefinition() { - result = getADeclarationEntry() and + result = this.getADeclarationEntry() and result.isDefinition() } - override Location getDefinitionLocation() { result = getDefinition().getLocation() } + override Location getDefinitionLocation() { result = this.getDefinition().getLocation() } override Location getLocation() { - if exists(getDefinition()) + if exists(this.getDefinition()) then result = this.getDefinitionLocation() else result = this.getADeclarationLocation() } @@ -199,7 +199,7 @@ class Variable extends Declaration, @variable { * ``` */ class VariableDeclarationEntry extends DeclarationEntry, @var_decl { - override Variable getDeclaration() { result = getVariable() } + override Variable getDeclaration() { result = this.getVariable() } override string getAPrimaryQlClass() { result = "VariableDeclarationEntry" } @@ -276,32 +276,33 @@ class ParameterDeclarationEntry extends VariableDeclarationEntry { int getIndex() { param_decl_bind(underlyingElement(this), result, _) } private string getAnonymousParameterDescription() { - not exists(getName()) and + not exists(this.getName()) and exists(string idx | idx = - ((getIndex() + 1).toString() + "th") + ((this.getIndex() + 1).toString() + "th") .replaceAll("1th", "1st") .replaceAll("2th", "2nd") .replaceAll("3th", "3rd") .replaceAll("11st", "11th") .replaceAll("12nd", "12th") .replaceAll("13rd", "13th") and - if exists(getCanonicalName()) - then result = "declaration of " + getCanonicalName() + " as anonymous " + idx + " parameter" + if exists(this.getCanonicalName()) + then + result = "declaration of " + this.getCanonicalName() + " as anonymous " + idx + " parameter" else result = "declaration of " + idx + " parameter" ) } override string toString() { - isDefinition() and - result = "definition of " + getName() + this.isDefinition() and + result = "definition of " + this.getName() or - not isDefinition() and - if getName() = getCanonicalName() - then result = "declaration of " + getName() - else result = "declaration of " + getCanonicalName() + " as " + getName() + not this.isDefinition() and + if this.getName() = this.getCanonicalName() + then result = "declaration of " + this.getName() + else result = "declaration of " + this.getCanonicalName() + " as " + this.getName() or - result = getAnonymousParameterDescription() + result = this.getAnonymousParameterDescription() } /** @@ -311,8 +312,12 @@ class ParameterDeclarationEntry extends VariableDeclarationEntry { */ string getTypedName() { exists(string typeString, string nameString | - (if exists(getType().getName()) then typeString = getType().getName() else typeString = "") and - (if exists(getName()) then nameString = getName() else nameString = "") and + ( + if exists(this.getType().getName()) + then typeString = this.getType().getName() + else typeString = "" + ) and + (if exists(this.getName()) then nameString = this.getName() else nameString = "") and if typeString != "" and nameString != "" then result = typeString + " " + nameString else result = typeString + nameString @@ -540,7 +545,7 @@ class MemberVariable extends Variable, @membervariable { } /** Holds if this member is mutable. */ - predicate isMutable() { getADeclarationEntry().hasSpecifier("mutable") } + predicate isMutable() { this.getADeclarationEntry().hasSpecifier("mutable") } private Type getAType() { membervariables(underlyingElement(this), unresolveElement(result), _) } } diff --git a/cpp/ql/lib/semmle/code/cpp/XML.qll b/cpp/ql/lib/semmle/code/cpp/XML.qll index 5871fed0ddd..76f3b3cb022 100755 --- a/cpp/ql/lib/semmle/code/cpp/XML.qll +++ b/cpp/ql/lib/semmle/code/cpp/XML.qll @@ -24,7 +24,7 @@ class XMLLocatable extends @xmllocatable, TXMLLocatable { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -108,7 +108,7 @@ class XMLParent extends @xmlparent { } /** Gets the text value contained in this XML parent. */ - string getTextValue() { result = allCharactersString() } + string getTextValue() { result = this.allCharactersString() } /** Gets a printable representation of this XML parent. */ string toString() { result = this.getName() } @@ -119,7 +119,7 @@ class XMLFile extends XMLParent, File { XMLFile() { xmlEncoding(this, _) } /** Gets a printable representation of this XML file. */ - override string toString() { result = getName() } + override string toString() { result = this.getName() } /** Gets the name of this XML file. */ override string getName() { result = File.super.getAbsolutePath() } @@ -129,14 +129,14 @@ class XMLFile extends XMLParent, File { * * Gets the path of this XML file. */ - deprecated string getPath() { result = getAbsolutePath() } + deprecated string getPath() { result = this.getAbsolutePath() } /** * DEPRECATED: Use `getParentContainer().getAbsolutePath()` instead. * * Gets the path of the folder that contains this XML file. */ - deprecated string getFolder() { result = getParentContainer().getAbsolutePath() } + deprecated string getFolder() { result = this.getParentContainer().getAbsolutePath() } /** Gets the encoding of this XML file. */ string getEncoding() { xmlEncoding(this, result) } @@ -200,7 +200,7 @@ class XMLDTD extends XMLLocatable, @xmldtd { */ class XMLElement extends @xmlelement, XMLParent, XMLLocatable { /** Holds if this XML element has the given `name`. */ - predicate hasName(string name) { name = getName() } + predicate hasName(string name) { name = this.getName() } /** Gets the name of this XML element. */ override string getName() { xmlElements(this, result, _, _, _) } @@ -239,7 +239,7 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable { string getAttributeValue(string name) { result = this.getAttribute(name).getValue() } /** Gets a printable representation of this XML element. */ - override string toString() { result = getName() } + override string toString() { result = this.getName() } } /** diff --git a/cpp/ql/lib/semmle/code/cpp/commons/Buffer.qll b/cpp/ql/lib/semmle/code/cpp/commons/Buffer.qll index 5778ee7dfb1..2328476d525 100644 --- a/cpp/ql/lib/semmle/code/cpp/commons/Buffer.qll +++ b/cpp/ql/lib/semmle/code/cpp/commons/Buffer.qll @@ -10,44 +10,11 @@ import semmle.code.cpp.dataflow.DataFlow * char data[1]; // v * }; * ``` - * This requires that `v` is an array of size 0 or 1, and `v` is the last member of `c`. - * In addition, if the size of the structure is taken, there must be at least one instance - * where a `c` pointer is allocated with additional space. - * For example, holds for `c` if it occurs as - * ``` - * malloc(sizeof(c) + 100 * sizeof(char)) - * ``` - * but not if it only ever occurs as - * ``` - * malloc(sizeof(c)) - * ``` + * This requires that `v` is an array of size 0 or 1. */ predicate memberMayBeVarSize(Class c, MemberVariable v) { - exists(int i | - // `v` is the last field in `c` - i = max(int j | c.getCanonicalMember(j) instanceof Field | j) and - v = c.getCanonicalMember(i) and - // v is an array of size at most 1 - v.getUnspecifiedType().(ArrayType).getArraySize() <= 1 and - not c instanceof Union - ) and - // If the size is taken, then arithmetic is performed on the result at least once - ( - // `sizeof(c)` is not taken - not exists(SizeofOperator so | - so.(SizeofTypeOperator).getTypeOperand().getUnspecifiedType() = c or - so.(SizeofExprOperator).getExprOperand().getUnspecifiedType() = c - ) - or - // or `sizeof(c)` is taken - exists(SizeofOperator so | - so.(SizeofTypeOperator).getTypeOperand().getUnspecifiedType() = c or - so.(SizeofExprOperator).getExprOperand().getUnspecifiedType() = c - | - // and arithmetic is performed on the result - so.getParent*() instanceof AddExpr - ) - ) + c = v.getDeclaringType() and + v.getUnspecifiedType().(ArrayType).getArraySize() <= 1 } /** @@ -60,10 +27,6 @@ int getBufferSize(Expr bufferExpr, Element why) { result = bufferVar.getUnspecifiedType().(ArrayType).getSize() and why = bufferVar and not memberMayBeVarSize(_, bufferVar) and - not exists(Union bufferType | - bufferType.getAMemberVariable() = why and - bufferVar.getUnspecifiedType().(ArrayType).getSize() <= 1 - ) and not result = 0 // zero sized arrays are likely to have special usage, for example or // behaving a bit like a 'union' overlapping other fields. @@ -85,13 +48,6 @@ int getBufferSize(Expr bufferExpr, Element why) { parentPtr.getTarget().getUnspecifiedType().(PointerType).getBaseType() = parentClass and result = getBufferSize(parentPtr, _) + bufferVar.getType().getSize() - parentClass.getSize() ) - or - exists(Union bufferType | - bufferType.getAMemberVariable() = why and - why = bufferVar and - bufferVar.getUnspecifiedType().(ArrayType).getSize() <= 1 and - result = bufferType.getSize() - ) ) or // buffer is a fixed size dynamic allocation diff --git a/cpp/ql/lib/semmle/code/cpp/commons/CommonType.qll b/cpp/ql/lib/semmle/code/cpp/commons/CommonType.qll index 5d6c64630a6..26e60538ec6 100644 --- a/cpp/ql/lib/semmle/code/cpp/commons/CommonType.qll +++ b/cpp/ql/lib/semmle/code/cpp/commons/CommonType.qll @@ -375,8 +375,8 @@ class Wchar_t extends Type { class MicrosoftInt8Type extends IntegralType { MicrosoftInt8Type() { this instanceof CharType and - not isExplicitlyUnsigned() and - not isExplicitlySigned() + not this.isExplicitlyUnsigned() and + not this.isExplicitlySigned() } } @@ -391,8 +391,8 @@ class MicrosoftInt8Type extends IntegralType { class MicrosoftInt16Type extends IntegralType { MicrosoftInt16Type() { this instanceof ShortType and - not isExplicitlyUnsigned() and - not isExplicitlySigned() + not this.isExplicitlyUnsigned() and + not this.isExplicitlySigned() } } @@ -407,8 +407,8 @@ class MicrosoftInt16Type extends IntegralType { class MicrosoftInt32Type extends IntegralType { MicrosoftInt32Type() { this instanceof IntType and - not isExplicitlyUnsigned() and - not isExplicitlySigned() + not this.isExplicitlyUnsigned() and + not this.isExplicitlySigned() } } @@ -423,8 +423,8 @@ class MicrosoftInt32Type extends IntegralType { class MicrosoftInt64Type extends IntegralType { MicrosoftInt64Type() { this instanceof LongLongType and - not isExplicitlyUnsigned() and - not isExplicitlySigned() + not this.isExplicitlyUnsigned() and + not this.isExplicitlySigned() } } diff --git a/cpp/ql/lib/semmle/code/cpp/commons/Dependency.qll b/cpp/ql/lib/semmle/code/cpp/commons/Dependency.qll index 1b885fb8f5f..59776d5b87a 100644 --- a/cpp/ql/lib/semmle/code/cpp/commons/Dependency.qll +++ b/cpp/ql/lib/semmle/code/cpp/commons/Dependency.qll @@ -33,7 +33,7 @@ DependencyOptions getDependencyOptions() { any() } class DependsSource extends Element { DependsSource() { // not inside a template instantiation - not exists(Element other | isFromTemplateInstantiation(other)) or + not exists(Element other | this.isFromTemplateInstantiation(other)) or // allow DeclarationEntrys of template specializations this.(DeclarationEntry).getDeclaration().(Function).isConstructedFrom(_) or this.(DeclarationEntry).getDeclaration().(Class).isConstructedFrom(_) diff --git a/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll b/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll index ee072fb0ed3..2f811ab83a0 100644 --- a/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll +++ b/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll @@ -1,6 +1,7 @@ import cpp private import semmle.code.cpp.models.interfaces.ArrayFunction private import semmle.code.cpp.models.implementations.Strcat +import semmle.code.cpp.dataflow.DataFlow private predicate mayAddNullTerminatorHelper(Expr e, VariableAccess va, Expr e0) { exists(StackVariable v0, Expr val | @@ -45,22 +46,28 @@ predicate mayAddNullTerminator(Expr e, VariableAccess va) { ae.getRValue().getAChild*() = va ) or - // Function call: library function, varargs function, function - // containing assembler code, or function where the relevant - // parameter is potentially added a null terminator. + // Function calls... exists(Call c, Function f, int i | e = c and f = c.getTarget() and not functionArgumentMustBeNullTerminated(f, i) and c.getAnArgumentSubExpr(i) = va | - not f.hasEntryPoint() and not functionArgumentMustBeNullTerminated(f, i) + // library function + not f.hasEntryPoint() or + // function where the relevant parameter is potentially added a null terminator mayAddNullTerminator(_, f.getParameter(i).getAnAccess()) or + // varargs function f.isVarargs() and i >= f.getNumberOfParameters() or + // function containing assembler code exists(AsmStmt s | s.getEnclosingFunction() = f) + or + // function where the relevant parameter is returned (leaking it to be potentially null terminated elsewhere) + DataFlow::localFlow(DataFlow::parameterNode(f.getParameter(i)), + DataFlow::exprNode(any(ReturnStmt rs).getExpr())) ) or // Call without target (e.g., function pointer call) diff --git a/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll b/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll index ef5f5511f6d..a7c72a5db8d 100644 --- a/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll +++ b/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll @@ -8,7 +8,7 @@ import semmle.code.cpp.commons.StringAnalysis import semmle.code.cpp.models.interfaces.FormattingFunction class PrintfFormatAttribute extends FormatAttribute { - PrintfFormatAttribute() { getArchetype() = ["printf", "__printf__"] } + PrintfFormatAttribute() { this.getArchetype() = ["printf", "__printf__"] } } /** @@ -20,13 +20,13 @@ class AttributeFormattingFunction extends FormattingFunction { AttributeFormattingFunction() { exists(PrintfFormatAttribute printf_attrib | - printf_attrib = getAnAttribute() and + printf_attrib = this.getAnAttribute() and exists(printf_attrib.getFirstFormatArgIndex()) // exclude `vprintf` style format functions ) } override int getFormatParameterIndex() { - forex(PrintfFormatAttribute printf_attrib | printf_attrib = getAnAttribute() | + forex(PrintfFormatAttribute printf_attrib | printf_attrib = this.getAnAttribute() | result = printf_attrib.getFormatIndex() ) } @@ -132,7 +132,7 @@ deprecated predicate variadicFormatter(Function f, int formatParamIndex) { class UserDefinedFormattingFunction extends FormattingFunction { override string getAPrimaryQlClass() { result = "UserDefinedFormattingFunction" } - UserDefinedFormattingFunction() { isVarargs() and callsVariadicFormatter(this, _, _, _) } + UserDefinedFormattingFunction() { this.isVarargs() and callsVariadicFormatter(this, _, _, _) } override int getFormatParameterIndex() { callsVariadicFormatter(this, _, result, _) } @@ -191,7 +191,7 @@ class FormattingFunctionCall extends Expr { exists(int i | result = this.getArgument(i) and n >= 0 and - n = i - getTarget().(FormattingFunction).getFirstFormatArgumentIndex() + n = i - this.getTarget().(FormattingFunction).getFirstFormatArgumentIndex() ) } @@ -251,7 +251,22 @@ class FormattingFunctionCall extends Expr { int getNumFormatArgument() { result = count(this.getFormatArgument(_)) and // format arguments must be known - exists(getTarget().(FormattingFunction).getFirstFormatArgumentIndex()) + exists(this.getTarget().(FormattingFunction).getFirstFormatArgumentIndex()) + } + + /** + * Gets the argument, if any, to which the output is written. If `isStream` is + * `true`, the output argument is a stream (that is, this call behaves like + * `fprintf`). If `isStream` is `false`, the output argument is a buffer (that + * is, this call behaves like `sprintf`) + */ + Expr getOutputArgument(boolean isStream) { + result = + this.(Call) + .getArgument(this.(Call) + .getTarget() + .(FormattingFunction) + .getOutputParameterIndex(isStream)) } } @@ -275,7 +290,7 @@ class FormatLiteral extends Literal { * DEPRECATED: Use getDefaultCharType() instead. */ deprecated predicate isWideCharDefault() { - getUse().getTarget().(FormattingFunction).isWideCharDefault() + this.getUse().getTarget().(FormattingFunction).isWideCharDefault() } /** @@ -283,7 +298,7 @@ class FormatLiteral extends Literal { * `char` or `wchar_t`. */ Type getDefaultCharType() { - result = getUse().getTarget().(FormattingFunction).getDefaultCharType() + result = this.getUse().getTarget().(FormattingFunction).getDefaultCharType() } /** @@ -292,7 +307,7 @@ class FormatLiteral extends Literal { * which is correct for a particular function. */ Type getNonDefaultCharType() { - result = getUse().getTarget().(FormattingFunction).getNonDefaultCharType() + result = this.getUse().getTarget().(FormattingFunction).getNonDefaultCharType() } /** @@ -300,7 +315,9 @@ class FormatLiteral extends Literal { * snapshots there may be multiple results where we can't tell which is correct for a * particular function. */ - Type getWideCharType() { result = getUse().getTarget().(FormattingFunction).getWideCharType() } + Type getWideCharType() { + result = this.getUse().getTarget().(FormattingFunction).getWideCharType() + } /** * Holds if this `FormatLiteral` is in a context that supports @@ -338,7 +355,7 @@ class FormatLiteral extends Literal { } private string getFlagRegexp() { - if isMicrosoft() then result = "[-+ #0']*" else result = "[-+ #0'I]*" + if this.isMicrosoft() then result = "[-+ #0']*" else result = "[-+ #0'I]*" } private string getFieldWidthRegexp() { result = "(?:[1-9][0-9]*|\\*|\\*[0-9]+\\$)?" } @@ -346,13 +363,13 @@ class FormatLiteral extends Literal { private string getPrecRegexp() { result = "(?:\\.(?:[0-9]*|\\*|\\*[0-9]+\\$))?" } private string getLengthRegexp() { - if isMicrosoft() + if this.isMicrosoft() then result = "(?:hh?|ll?|L|q|j|z|t|w|I32|I64|I)?" else result = "(?:hh?|ll?|L|q|j|z|Z|t)?" } private string getConvCharRegexp() { - if isMicrosoft() + if this.isMicrosoft() then result = "[aAcCdeEfFgGimnopsSuxXZ@]" else result = "[aAcCdeEfFgGimnopsSuxX@]" } @@ -732,16 +749,16 @@ class FormatLiteral extends Literal { * Gets the argument type required by the nth conversion specifier. */ Type getConversionType(int n) { - result = getConversionType1(n) or - result = getConversionType1b(n) or - result = getConversionType2(n) or - result = getConversionType3(n) or - result = getConversionType4(n) or - result = getConversionType6(n) or - result = getConversionType7(n) or - result = getConversionType8(n) or - result = getConversionType9(n) or - result = getConversionType10(n) + result = this.getConversionType1(n) or + result = this.getConversionType1b(n) or + result = this.getConversionType2(n) or + result = this.getConversionType3(n) or + result = this.getConversionType4(n) or + result = this.getConversionType6(n) or + result = this.getConversionType7(n) or + result = this.getConversionType8(n) or + result = this.getConversionType9(n) or + result = this.getConversionType10(n) } private Type getConversionType1(int n) { @@ -771,15 +788,15 @@ class FormatLiteral extends Literal { or conv = ["c", "C"] and len = ["l", "w"] and - result = getWideCharType() + result = this.getWideCharType() or conv = "c" and (len != "l" and len != "w" and len != "h") and - result = getDefaultCharType() + result = this.getDefaultCharType() or conv = "C" and (len != "l" and len != "w" and len != "h") and - result = getNonDefaultCharType() + result = this.getNonDefaultCharType() ) ) } @@ -816,15 +833,15 @@ class FormatLiteral extends Literal { or conv = ["s", "S"] and len = ["l", "w"] and - result.(PointerType).getBaseType() = getWideCharType() + result.(PointerType).getBaseType() = this.getWideCharType() or conv = "s" and (len != "l" and len != "w" and len != "h") and - result.(PointerType).getBaseType() = getDefaultCharType() + result.(PointerType).getBaseType() = this.getDefaultCharType() or conv = "S" and (len != "l" and len != "w" and len != "h") and - result.(PointerType).getBaseType() = getNonDefaultCharType() + result.(PointerType).getBaseType() = this.getNonDefaultCharType() ) ) } @@ -879,19 +896,19 @@ class FormatLiteral extends Literal { exists(string len, string conv | this.parseConvSpec(n, _, _, _, _, _, len, conv) and (len != "l" and len != "w" and len != "h") and - getUse().getTarget().(FormattingFunction).getFormatCharType().getSize() > 1 and // wide function + this.getUse().getTarget().(FormattingFunction).getFormatCharType().getSize() > 1 and // wide function ( conv = "c" and - result = getNonDefaultCharType() + result = this.getNonDefaultCharType() or conv = "C" and - result = getDefaultCharType() + result = this.getDefaultCharType() or conv = "s" and - result.(PointerType).getBaseType() = getNonDefaultCharType() + result.(PointerType).getBaseType() = this.getNonDefaultCharType() or conv = "S" and - result.(PointerType).getBaseType() = getDefaultCharType() + result.(PointerType).getBaseType() = this.getDefaultCharType() ) ) } @@ -924,9 +941,13 @@ class FormatLiteral extends Literal { * not account for positional arguments (`$`). */ int getFormatArgumentIndexFor(int n, int mode) { - hasFormatArgumentIndexFor(n, mode) and + this.hasFormatArgumentIndexFor(n, mode) and (3 * n) + mode = - rank[result + 1](int n2, int mode2 | hasFormatArgumentIndexFor(n2, mode2) | (3 * n2) + mode2) + rank[result + 1](int n2, int mode2 | + this.hasFormatArgumentIndexFor(n2, mode2) + | + (3 * n2) + mode2 + ) } /** @@ -936,7 +957,7 @@ class FormatLiteral extends Literal { int getNumArgNeeded(int n) { exists(this.getConvSpecOffset(n)) and exists(this.getConversionChar(n)) and - result = count(int mode | hasFormatArgumentIndexFor(n, mode)) + result = count(int mode | this.hasFormatArgumentIndexFor(n, mode)) } /** @@ -948,7 +969,7 @@ class FormatLiteral extends Literal { // At least one conversion specifier has a parameter field, in which case, // they all should have. result = max(string s | this.getParameterField(_) = s + "$" | s.toInt()) - else result = count(int n, int mode | hasFormatArgumentIndexFor(n, mode)) + else result = count(int n, int mode | this.hasFormatArgumentIndexFor(n, mode)) } /** @@ -1038,10 +1059,10 @@ class FormatLiteral extends Literal { exists(int sizeBits | sizeBits = min(int bits | - bits = getIntegralDisplayType(n).getSize() * 8 + bits = this.getIntegralDisplayType(n).getSize() * 8 or exists(IntegralType t | - t = getUse().getConversionArgument(n).getType().getUnderlyingType() + t = this.getUse().getConversionArgument(n).getType().getUnderlyingType() | t.isSigned() and bits = t.getSize() * 8 ) @@ -1056,10 +1077,10 @@ class FormatLiteral extends Literal { exists(int sizeBits | sizeBits = min(int bits | - bits = getIntegralDisplayType(n).getSize() * 8 + bits = this.getIntegralDisplayType(n).getSize() * 8 or exists(IntegralType t | - t = getUse().getConversionArgument(n).getType().getUnderlyingType() + t = this.getUse().getConversionArgument(n).getType().getUnderlyingType() | t.isUnsigned() and bits = t.getSize() * 8 ) @@ -1074,26 +1095,26 @@ class FormatLiteral extends Literal { exists(int sizeBytes, int baseLen | sizeBytes = min(int bytes | - bytes = getIntegralDisplayType(n).getSize() + bytes = this.getIntegralDisplayType(n).getSize() or exists(IntegralType t | - t = getUse().getConversionArgument(n).getType().getUnderlyingType() + t = this.getUse().getConversionArgument(n).getType().getUnderlyingType() | t.isUnsigned() and bytes = t.getSize() ) ) and baseLen = sizeBytes * 2 and ( - if hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x" + if this.hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x" ) ) or this.getConversionChar(n).toLowerCase() = "p" and exists(PointerType ptrType, int baseLen | - ptrType = getFullyConverted().getType() and + ptrType = this.getFullyConverted().getType() and baseLen = max(ptrType.getSize() * 2) and // e.g. "0x1234567812345678"; exact format is platform dependent ( - if hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x" + if this.hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x" ) ) or @@ -1102,17 +1123,17 @@ class FormatLiteral extends Literal { exists(int sizeBits, int baseLen | sizeBits = min(int bits | - bits = getIntegralDisplayType(n).getSize() * 8 + bits = this.getIntegralDisplayType(n).getSize() * 8 or exists(IntegralType t | - t = getUse().getConversionArgument(n).getType().getUnderlyingType() + t = this.getUse().getConversionArgument(n).getType().getUnderlyingType() | t.isUnsigned() and bits = t.getSize() * 8 ) ) and baseLen = (sizeBits / 3.0).ceil() and ( - if hasAlternateFlag(n) then len = 1 + baseLen else len = baseLen // "0" + if this.hasAlternateFlag(n) then len = 1 + baseLen else len = baseLen // "0" ) ) or @@ -1135,8 +1156,8 @@ class FormatLiteral extends Literal { */ int getMaxConvertedLengthLimited(int n) { if this.getConversionChar(n).toLowerCase() = "f" - then result = getMaxConvertedLength(n).minimum(8) - else result = getMaxConvertedLength(n) + then result = this.getMaxConvertedLength(n).minimum(8) + else result = this.getMaxConvertedLength(n) } /** diff --git a/cpp/ql/lib/semmle/code/cpp/commons/Scanf.qll b/cpp/ql/lib/semmle/code/cpp/commons/Scanf.qll index 461030f389d..58d980318d9 100644 --- a/cpp/ql/lib/semmle/code/cpp/commons/Scanf.qll +++ b/cpp/ql/lib/semmle/code/cpp/commons/Scanf.qll @@ -24,7 +24,7 @@ abstract class ScanfFunction extends Function { * Holds if the default meaning of `%s` is a `wchar_t*` string * (rather than a `char*`). */ - predicate isWideCharDefault() { exists(getName().indexOf("wscanf")) } + predicate isWideCharDefault() { exists(this.getName().indexOf("wscanf")) } } /** @@ -34,10 +34,10 @@ class Scanf extends ScanfFunction { Scanf() { this instanceof TopLevelFunction and ( - hasGlobalOrStdOrBslName("scanf") or // scanf(format, args...) - hasGlobalOrStdOrBslName("wscanf") or // wscanf(format, args...) - hasGlobalName("_scanf_l") or // _scanf_l(format, locale, args...) - hasGlobalName("_wscanf_l") // _wscanf_l(format, locale, args...) + this.hasGlobalOrStdOrBslName("scanf") or // scanf(format, args...) + this.hasGlobalOrStdOrBslName("wscanf") or // wscanf(format, args...) + this.hasGlobalName("_scanf_l") or // _scanf_l(format, locale, args...) + this.hasGlobalName("_wscanf_l") // _wscanf_l(format, locale, args...) ) } @@ -53,10 +53,10 @@ class Fscanf extends ScanfFunction { Fscanf() { this instanceof TopLevelFunction and ( - hasGlobalOrStdOrBslName("fscanf") or // fscanf(src_stream, format, args...) - hasGlobalOrStdOrBslName("fwscanf") or // fwscanf(src_stream, format, args...) - hasGlobalName("_fscanf_l") or // _fscanf_l(src_stream, format, locale, args...) - hasGlobalName("_fwscanf_l") // _fwscanf_l(src_stream, format, locale, args...) + this.hasGlobalOrStdOrBslName("fscanf") or // fscanf(src_stream, format, args...) + this.hasGlobalOrStdOrBslName("fwscanf") or // fwscanf(src_stream, format, args...) + this.hasGlobalName("_fscanf_l") or // _fscanf_l(src_stream, format, locale, args...) + this.hasGlobalName("_fwscanf_l") // _fwscanf_l(src_stream, format, locale, args...) ) } @@ -72,10 +72,10 @@ class Sscanf extends ScanfFunction { Sscanf() { this instanceof TopLevelFunction and ( - hasGlobalOrStdOrBslName("sscanf") or // sscanf(src_stream, format, args...) - hasGlobalOrStdOrBslName("swscanf") or // swscanf(src, format, args...) - hasGlobalName("_sscanf_l") or // _sscanf_l(src, format, locale, args...) - hasGlobalName("_swscanf_l") // _swscanf_l(src, format, locale, args...) + this.hasGlobalOrStdOrBslName("sscanf") or // sscanf(src_stream, format, args...) + this.hasGlobalOrStdOrBslName("swscanf") or // swscanf(src, format, args...) + this.hasGlobalName("_sscanf_l") or // _sscanf_l(src, format, locale, args...) + this.hasGlobalName("_swscanf_l") // _swscanf_l(src, format, locale, args...) ) } @@ -91,10 +91,10 @@ class Snscanf extends ScanfFunction { Snscanf() { this instanceof TopLevelFunction and ( - hasGlobalName("_snscanf") or // _snscanf(src, max_amount, format, args...) - hasGlobalName("_snwscanf") or // _snwscanf(src, max_amount, format, args...) - hasGlobalName("_snscanf_l") or // _snscanf_l(src, max_amount, format, locale, args...) - hasGlobalName("_snwscanf_l") // _snwscanf_l(src, max_amount, format, locale, args...) + this.hasGlobalName("_snscanf") or // _snscanf(src, max_amount, format, args...) + this.hasGlobalName("_snwscanf") or // _snwscanf(src, max_amount, format, args...) + this.hasGlobalName("_snscanf_l") or // _snscanf_l(src, max_amount, format, locale, args...) + this.hasGlobalName("_snwscanf_l") // _snwscanf_l(src, max_amount, format, locale, args...) // note that the max_amount is not a limit on the output length, it's an input length // limit used with non null-terminated strings. ) @@ -120,18 +120,18 @@ class ScanfFunctionCall extends FunctionCall { /** * Gets the `scanf`-like function that is called. */ - ScanfFunction getScanfFunction() { result = getTarget() } + ScanfFunction getScanfFunction() { result = this.getTarget() } /** * Gets the position at which the input string or stream parameter occurs, * if this function call does not read from standard input. */ - int getInputParameterIndex() { result = getScanfFunction().getInputParameterIndex() } + int getInputParameterIndex() { result = this.getScanfFunction().getInputParameterIndex() } /** * Gets the position at which the format parameter occurs. */ - int getFormatParameterIndex() { result = getScanfFunction().getFormatParameterIndex() } + int getFormatParameterIndex() { result = this.getScanfFunction().getFormatParameterIndex() } /** * Gets the format expression used in this call. @@ -142,7 +142,7 @@ class ScanfFunctionCall extends FunctionCall { * Holds if the default meaning of `%s` is a `wchar_t*` string * (rather than a `char*`). */ - predicate isWideCharDefault() { getScanfFunction().isWideCharDefault() } + predicate isWideCharDefault() { this.getScanfFunction().isWideCharDefault() } } /** @@ -158,7 +158,7 @@ class ScanfFormatLiteral extends Expr { ScanfFunctionCall getUse() { result.getFormat() = this } /** Holds if the default meaning of `%s` is a `wchar_t*` (rather than a `char*`). */ - predicate isWideCharDefault() { getUse().getTarget().(ScanfFunction).isWideCharDefault() } + predicate isWideCharDefault() { this.getUse().getTarget().(ScanfFunction).isWideCharDefault() } /** * Gets the format string itself, transformed as follows: diff --git a/cpp/ql/lib/semmle/code/cpp/commons/Synchronization.qll b/cpp/ql/lib/semmle/code/cpp/commons/Synchronization.qll index 92955ae3580..f1b9cf80d64 100644 --- a/cpp/ql/lib/semmle/code/cpp/commons/Synchronization.qll +++ b/cpp/ql/lib/semmle/code/cpp/commons/Synchronization.qll @@ -40,8 +40,8 @@ abstract class MutexType extends Type { * Gets a call that locks or tries to lock any mutex of this type. */ FunctionCall getLockAccess() { - result = getMustlockAccess() or - result = getTrylockAccess() + result = this.getMustlockAccess() or + result = this.getTrylockAccess() } /** @@ -63,22 +63,22 @@ abstract class MutexType extends Type { /** * DEPRECATED: use mustlockAccess(fc, arg) instead. */ - deprecated Function getMustlockFunction() { result = getMustlockAccess().getTarget() } + deprecated Function getMustlockFunction() { result = this.getMustlockAccess().getTarget() } /** * DEPRECATED: use trylockAccess(fc, arg) instead. */ - deprecated Function getTrylockFunction() { result = getTrylockAccess().getTarget() } + deprecated Function getTrylockFunction() { result = this.getTrylockAccess().getTarget() } /** * DEPRECATED: use lockAccess(fc, arg) instead. */ - deprecated Function getLockFunction() { result = getLockAccess().getTarget() } + deprecated Function getLockFunction() { result = this.getLockAccess().getTarget() } /** * DEPRECATED: use unlockAccess(fc, arg) instead. */ - deprecated Function getUnlockFunction() { result = getUnlockAccess().getTarget() } + deprecated Function getUnlockFunction() { result = this.getUnlockAccess().getTarget() } } /** @@ -155,17 +155,17 @@ class DefaultMutexType extends MutexType { override predicate mustlockAccess(FunctionCall fc, Expr arg) { fc.getTarget() = mustlockCandidate() and - lockArgType(fc, arg) + this.lockArgType(fc, arg) } override predicate trylockAccess(FunctionCall fc, Expr arg) { fc.getTarget() = trylockCandidate() and - lockArgType(fc, arg) + this.lockArgType(fc, arg) } override predicate unlockAccess(FunctionCall fc, Expr arg) { fc.getTarget() = unlockCandidate() and - lockArgType(fc, arg) + this.lockArgType(fc, arg) } } diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/BasicBlocks.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/BasicBlocks.qll index 16947019f54..34373d943af 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/BasicBlocks.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/BasicBlocks.qll @@ -194,14 +194,14 @@ class BasicBlock extends ControlFlowNodeBase { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). * * Yields no result if this basic block spans multiple source files. */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { - hasLocationInfoInternal(filepath, startline, startcolumn, filepath, endline, endcolumn) + this.hasLocationInfoInternal(filepath, startline, startcolumn, filepath, endline, endcolumn) } pragma[noinline] @@ -276,7 +276,7 @@ class EntryBasicBlock extends BasicBlock { */ class ExitBasicBlock extends BasicBlock { ExitBasicBlock() { - getEnd() instanceof Function or - aborting(getEnd()) + this.getEnd() instanceof Function or + aborting(this.getEnd()) } } diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/ControlFlowGraph.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/ControlFlowGraph.qll index bac051f6474..c7b3d1dc16f 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/ControlFlowGraph.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/ControlFlowGraph.qll @@ -66,7 +66,7 @@ class ControlFlowNode extends Locatable, ControlFlowNodeBase { */ ControlFlowNode getATrueSuccessor() { qlCFGTrueSuccessor(this, result) and - result = getASuccessor() + result = this.getASuccessor() } /** @@ -75,7 +75,7 @@ class ControlFlowNode extends Locatable, ControlFlowNodeBase { */ ControlFlowNode getAFalseSuccessor() { qlCFGFalseSuccessor(this, result) and - result = getASuccessor() + result = this.getASuccessor() } /** Gets the `BasicBlock` containing this control-flow node. */ diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll index d96fc34259c..9aee2556c1d 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll @@ -121,7 +121,7 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition { override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) { exists(boolean testIsTrue | - comparesLt(left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue) + this.comparesLt(left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue) ) } @@ -135,7 +135,7 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition { override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) { exists(boolean testIsTrue | - comparesEq(left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue) + this.comparesEq(left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue) ) } } @@ -147,27 +147,29 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition { private class GuardConditionFromShortCircuitNot extends GuardCondition, NotExpr { GuardConditionFromShortCircuitNot() { not exists(Instruction inst | this.getFullyConverted() = inst.getAST()) and - exists(IRGuardCondition ir | getOperand() = ir.getAST()) + exists(IRGuardCondition ir | this.getOperand() = ir.getAST()) } override predicate controls(BasicBlock controlled, boolean testIsTrue) { - getOperand().(GuardCondition).controls(controlled, testIsTrue.booleanNot()) + this.getOperand().(GuardCondition).controls(controlled, testIsTrue.booleanNot()) } override predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) { - getOperand().(GuardCondition).comparesLt(left, right, k, isLessThan, testIsTrue.booleanNot()) + this.getOperand() + .(GuardCondition) + .comparesLt(left, right, k, isLessThan, testIsTrue.booleanNot()) } override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) { - getOperand().(GuardCondition).ensuresLt(left, right, k, block, isLessThan.booleanNot()) + this.getOperand().(GuardCondition).ensuresLt(left, right, k, block, isLessThan.booleanNot()) } override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) { - getOperand().(GuardCondition).comparesEq(left, right, k, areEqual, testIsTrue.booleanNot()) + this.getOperand().(GuardCondition).comparesEq(left, right, k, areEqual, testIsTrue.booleanNot()) } override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) { - getOperand().(GuardCondition).ensuresEq(left, right, k, block, areEqual.booleanNot()) + this.getOperand().(GuardCondition).ensuresEq(left, right, k, block, areEqual.booleanNot()) } } @@ -303,9 +305,9 @@ class IRGuardCondition extends Instruction { cached predicate controlsEdge(IRBlock pred, IRBlock succ, boolean testIsTrue) { pred.getASuccessor() = succ and - controls(pred, testIsTrue) + this.controls(pred, testIsTrue) or - succ = getBranchSuccessor(testIsTrue) and + succ = this.getBranchSuccessor(testIsTrue) and branch.getCondition() = this and branch.getBlock() = pred } diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/LocalScopeVariableReachability.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/LocalScopeVariableReachability.qll index 32857146029..f6685865830 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/LocalScopeVariableReachability.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/LocalScopeVariableReachability.qll @@ -73,19 +73,19 @@ abstract deprecated class LocalScopeVariableReachability extends string { */ exists(BasicBlock bb, int i | - isSource(source, v) and + this.isSource(source, v) and bb.getNode(i) = source and not bb.isUnreachable() | exists(int j | j > i and sink = bb.getNode(j) and - isSink(sink, v) and - not exists(int k | isBarrier(bb.getNode(k), v) | k in [i + 1 .. j - 1]) + this.isSink(sink, v) and + not exists(int k | this.isBarrier(bb.getNode(k), v) | k in [i + 1 .. j - 1]) ) or - not exists(int k | isBarrier(bb.getNode(k), v) | k > i) and - bbSuccessorEntryReaches(bb, v, sink, _) + not exists(int k | this.isBarrier(bb.getNode(k), v) | k > i) and + this.bbSuccessorEntryReaches(bb, v, sink, _) ) } @@ -97,11 +97,11 @@ abstract deprecated class LocalScopeVariableReachability extends string { bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry, succSkipsFirstLoopAlwaysTrueUponEntry) | - bbEntryReachesLocally(succ, v, node) and + this.bbEntryReachesLocally(succ, v, node) and succSkipsFirstLoopAlwaysTrueUponEntry = false or - not isBarrier(succ.getNode(_), v) and - bbSuccessorEntryReaches(succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry) + not this.isBarrier(succ.getNode(_), v) and + this.bbSuccessorEntryReaches(succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry) ) } @@ -110,7 +110,7 @@ abstract deprecated class LocalScopeVariableReachability extends string { ) { exists(int n | node = bb.getNode(n) and - isSink(node, v) + this.isSink(node, v) | not exists(this.firstBarrierIndexIn(bb, v)) or @@ -119,7 +119,7 @@ abstract deprecated class LocalScopeVariableReachability extends string { } private int firstBarrierIndexIn(BasicBlock bb, SemanticStackVariable v) { - result = min(int m | isBarrier(bb.getNode(m), v)) + result = min(int m | this.isBarrier(bb.getNode(m), v)) } } @@ -271,7 +271,7 @@ abstract deprecated class LocalScopeVariableReachabilityWithReassignment extends * accounts for loops where the condition is provably true upon entry. */ override predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) { - reachesTo(source, v, sink, _) + this.reachesTo(source, v, sink, _) } /** @@ -281,21 +281,21 @@ abstract deprecated class LocalScopeVariableReachabilityWithReassignment extends ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink, SemanticStackVariable v0 ) { exists(ControlFlowNode def | - actualSourceReaches(source, v, def, v0) and + this.actualSourceReaches(source, v, def, v0) and LocalScopeVariableReachability.super.reaches(def, v0, sink) and - isSinkActual(sink, v0) + this.isSinkActual(sink, v0) ) } private predicate actualSourceReaches( ControlFlowNode source, SemanticStackVariable v, ControlFlowNode def, SemanticStackVariable v0 ) { - isSourceActual(source, v) and def = source and v0 = v + this.isSourceActual(source, v) and def = source and v0 = v or exists(ControlFlowNode source1, SemanticStackVariable v1 | - actualSourceReaches(source, v, source1, v1) + this.actualSourceReaches(source, v, source1, v1) | - reassignment(source1, v1, def, v0) + this.reassignment(source1, v1, def, v0) ) } @@ -307,14 +307,14 @@ abstract deprecated class LocalScopeVariableReachabilityWithReassignment extends } final override predicate isSource(ControlFlowNode node, LocalScopeVariable v) { - isSourceActual(node, v) + this.isSourceActual(node, v) or // Reassignment generates a new (non-actual) source - reassignment(_, _, node, v) + this.reassignment(_, _, node, v) } final override predicate isSink(ControlFlowNode node, LocalScopeVariable v) { - isSinkActual(node, v) + this.isSinkActual(node, v) or // Reassignment generates a new (non-actual) sink exprDefinition(_, node, v.getAnAccess()) @@ -347,21 +347,21 @@ abstract deprecated class LocalScopeVariableReachabilityExt extends string { /** See `LocalScopeVariableReachability.reaches`. */ predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) { exists(BasicBlock bb, int i | - isSource(source, v) and + this.isSource(source, v) and bb.getNode(i) = source and not bb.isUnreachable() | exists(int j | j > i and sink = bb.getNode(j) and - isSink(sink, v) and - not exists(int k | isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) | + this.isSink(sink, v) and + not exists(int k | this.isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) | k in [i .. j - 1] ) ) or - not exists(int k | isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) | k >= i) and - bbSuccessorEntryReaches(source, bb, v, sink, _) + not exists(int k | this.isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) | k >= i) and + this.bbSuccessorEntryReaches(source, bb, v, sink, _) ) } @@ -372,22 +372,22 @@ abstract deprecated class LocalScopeVariableReachabilityExt extends string { exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry | bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry, succSkipsFirstLoopAlwaysTrueUponEntry) and - not isBarrier(source, bb.getEnd(), succ.getStart(), v) + not this.isBarrier(source, bb.getEnd(), succ.getStart(), v) | - bbEntryReachesLocally(source, succ, v, node) and + this.bbEntryReachesLocally(source, succ, v, node) and succSkipsFirstLoopAlwaysTrueUponEntry = false or - not exists(int k | isBarrier(source, succ.getNode(k), succ.getNode(k + 1), v)) and - bbSuccessorEntryReaches(source, succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry) + not exists(int k | this.isBarrier(source, succ.getNode(k), succ.getNode(k + 1), v)) and + this.bbSuccessorEntryReaches(source, succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry) ) } private predicate bbEntryReachesLocally( ControlFlowNode source, BasicBlock bb, SemanticStackVariable v, ControlFlowNode node ) { - isSource(source, v) and - exists(int n | node = bb.getNode(n) and isSink(node, v) | - not exists(int m | m < n | isBarrier(source, bb.getNode(m), bb.getNode(m + 1), v)) + this.isSource(source, v) and + exists(int n | node = bb.getNode(n) and this.isSink(node, v) | + not exists(int m | m < n | this.isBarrier(source, bb.getNode(m), bb.getNode(m + 1), v)) ) } } diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll index 5c0f6b3ac14..49805b9fb3c 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/SSA.qll @@ -59,7 +59,7 @@ class SsaDefinition extends ControlFlowNodeBase { ControlFlowNode getDefinition() { result = this } /** Gets the `BasicBlock` containing this definition. */ - BasicBlock getBasicBlock() { result.contains(getDefinition()) } + BasicBlock getBasicBlock() { result.contains(this.getDefinition()) } /** Holds if this definition is a phi node for variable `v`. */ predicate isPhiNode(StackVariable v) { exists(StandardSSA x | x.phi_node(v, this.(BasicBlock))) } diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/StackVariableReachability.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/StackVariableReachability.qll index 6c50d254faa..7b5fcd504b1 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/StackVariableReachability.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/StackVariableReachability.qll @@ -72,19 +72,19 @@ abstract class StackVariableReachability extends string { */ exists(BasicBlock bb, int i | - isSource(source, v) and + this.isSource(source, v) and bb.getNode(i) = source and not bb.isUnreachable() | exists(int j | j > i and sink = bb.getNode(j) and - isSink(sink, v) and - not exists(int k | isBarrier(bb.getNode(k), v) | k in [i + 1 .. j - 1]) + this.isSink(sink, v) and + not exists(int k | this.isBarrier(bb.getNode(k), v) | k in [i + 1 .. j - 1]) ) or - not exists(int k | isBarrier(bb.getNode(k), v) | k > i) and - bbSuccessorEntryReaches(bb, v, sink, _) + not exists(int k | this.isBarrier(bb.getNode(k), v) | k > i) and + this.bbSuccessorEntryReaches(bb, v, sink, _) ) } @@ -96,11 +96,11 @@ abstract class StackVariableReachability extends string { bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry, succSkipsFirstLoopAlwaysTrueUponEntry) | - bbEntryReachesLocally(succ, v, node) and + this.bbEntryReachesLocally(succ, v, node) and succSkipsFirstLoopAlwaysTrueUponEntry = false or - not isBarrier(succ.getNode(_), v) and - bbSuccessorEntryReaches(succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry) + not this.isBarrier(succ.getNode(_), v) and + this.bbSuccessorEntryReaches(succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry) ) } @@ -109,7 +109,7 @@ abstract class StackVariableReachability extends string { ) { exists(int n | node = bb.getNode(n) and - isSink(node, v) + this.isSink(node, v) | not exists(this.firstBarrierIndexIn(bb, v)) or @@ -118,7 +118,7 @@ abstract class StackVariableReachability extends string { } private int firstBarrierIndexIn(BasicBlock bb, SemanticStackVariable v) { - result = min(int m | isBarrier(bb.getNode(m), v)) + result = min(int m | this.isBarrier(bb.getNode(m), v)) } } @@ -268,7 +268,7 @@ abstract class StackVariableReachabilityWithReassignment extends StackVariableRe * accounts for loops where the condition is provably true upon entry. */ override predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) { - reachesTo(source, v, sink, _) + this.reachesTo(source, v, sink, _) } /** @@ -278,21 +278,21 @@ abstract class StackVariableReachabilityWithReassignment extends StackVariableRe ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink, SemanticStackVariable v0 ) { exists(ControlFlowNode def | - actualSourceReaches(source, v, def, v0) and + this.actualSourceReaches(source, v, def, v0) and StackVariableReachability.super.reaches(def, v0, sink) and - isSinkActual(sink, v0) + this.isSinkActual(sink, v0) ) } private predicate actualSourceReaches( ControlFlowNode source, SemanticStackVariable v, ControlFlowNode def, SemanticStackVariable v0 ) { - isSourceActual(source, v) and def = source and v0 = v + this.isSourceActual(source, v) and def = source and v0 = v or exists(ControlFlowNode source1, SemanticStackVariable v1 | - actualSourceReaches(source, v, source1, v1) + this.actualSourceReaches(source, v, source1, v1) | - reassignment(source1, v1, def, v0) + this.reassignment(source1, v1, def, v0) ) } @@ -304,14 +304,14 @@ abstract class StackVariableReachabilityWithReassignment extends StackVariableRe } final override predicate isSource(ControlFlowNode node, StackVariable v) { - isSourceActual(node, v) + this.isSourceActual(node, v) or // Reassignment generates a new (non-actual) source - reassignment(_, _, node, v) + this.reassignment(_, _, node, v) } final override predicate isSink(ControlFlowNode node, StackVariable v) { - isSinkActual(node, v) + this.isSinkActual(node, v) or // Reassignment generates a new (non-actual) sink exprDefinition(_, node, v.getAnAccess()) @@ -342,21 +342,21 @@ abstract class StackVariableReachabilityExt extends string { /** See `StackVariableReachability.reaches`. */ predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) { exists(BasicBlock bb, int i | - isSource(source, v) and + this.isSource(source, v) and bb.getNode(i) = source and not bb.isUnreachable() | exists(int j | j > i and sink = bb.getNode(j) and - isSink(sink, v) and - not exists(int k | isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) | + this.isSink(sink, v) and + not exists(int k | this.isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) | k in [i .. j - 1] ) ) or - not exists(int k | isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) | k >= i) and - bbSuccessorEntryReaches(source, bb, v, sink, _) + not exists(int k | this.isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) | k >= i) and + this.bbSuccessorEntryReaches(source, bb, v, sink, _) ) } @@ -367,22 +367,22 @@ abstract class StackVariableReachabilityExt extends string { exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry | bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry, succSkipsFirstLoopAlwaysTrueUponEntry) and - not isBarrier(source, bb.getEnd(), succ.getStart(), v) + not this.isBarrier(source, bb.getEnd(), succ.getStart(), v) | - bbEntryReachesLocally(source, succ, v, node) and + this.bbEntryReachesLocally(source, succ, v, node) and succSkipsFirstLoopAlwaysTrueUponEntry = false or - not exists(int k | isBarrier(source, succ.getNode(k), succ.getNode(k + 1), v)) and - bbSuccessorEntryReaches(source, succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry) + not exists(int k | this.isBarrier(source, succ.getNode(k), succ.getNode(k + 1), v)) and + this.bbSuccessorEntryReaches(source, succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry) ) } private predicate bbEntryReachesLocally( ControlFlowNode source, BasicBlock bb, SemanticStackVariable v, ControlFlowNode node ) { - isSource(source, v) and - exists(int n | node = bb.getNode(n) and isSink(node, v) | - not exists(int m | m < n | isBarrier(source, bb.getNode(m), bb.getNode(m + 1), v)) + this.isSource(source, v) and + exists(int n | node = bb.getNode(n) and this.isSink(node, v) | + not exists(int m | m < n | this.isBarrier(source, bb.getNode(m), bb.getNode(m + 1), v)) ) } } diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/SubBasicBlocks.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/SubBasicBlocks.qll index fa9d2e94081..4fbea43b805 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/SubBasicBlocks.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/SubBasicBlocks.qll @@ -80,7 +80,7 @@ class SubBasicBlock extends ControlFlowNodeBase { * returns a 0-based position, while `getRankInBasicBlock` returns a 1-based * position. */ - deprecated int getPosInBasicBlock(BasicBlock bb) { result = getRankInBasicBlock(bb) - 1 } + deprecated int getPosInBasicBlock(BasicBlock bb) { result = this.getRankInBasicBlock(bb) - 1 } pragma[noinline] private int getIndexInBasicBlock(BasicBlock bb) { this = bb.getNode(result) } @@ -102,7 +102,7 @@ class SubBasicBlock extends ControlFlowNodeBase { exists(BasicBlock bb | exists(int outerIndex | result = bb.getNode(outerIndex) and - index = outerToInnerIndex(bb, outerIndex) + index = this.outerToInnerIndex(bb, outerIndex) ) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/internal/ConstantExprs.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/internal/ConstantExprs.qll index 476f626e874..da5204bb063 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/internal/ConstantExprs.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/internal/ConstantExprs.qll @@ -344,14 +344,13 @@ private int convertIntToType(int val, IntegralType t) { then if val = 0 then result = 0 else result = 1 else if t.isUnsigned() - then if val >= 0 and val.bitShiftRight(t.getSize() * 8) = 0 then result = val else none() + then val >= 0 and val.bitShiftRight(t.getSize() * 8) = 0 and result = val else if val >= 0 and val.bitShiftRight(t.getSize() * 8 - 1) = 0 then result = val - else - if (-(val + 1)).bitShiftRight(t.getSize() * 8 - 1) = 0 - then result = val - else none() + else ( + (-(val + 1)).bitShiftRight(t.getSize() * 8 - 1) = 0 and result = val + ) } /** @@ -386,7 +385,7 @@ library class ExprEvaluator extends int { abstract predicate interesting(Expr e); /** Gets the value of (interesting) expression `e`, if any. */ - int getValue(Expr e) { result = getValueInternal(e, e) } + int getValue(Expr e) { result = this.getValueInternal(e, e) } /** * When evaluating a syntactic subexpression of `e`, we may @@ -426,9 +425,9 @@ library class ExprEvaluator extends int { * calculates the values bottom-up. */ predicate interestingInternal(Expr e, Expr req, boolean sub) { - interesting(e) and req = e and sub = true + this.interesting(e) and req = e and sub = true or - exists(Expr mid | interestingInternal(e, mid, sub) | + exists(Expr mid | this.interestingInternal(e, mid, sub) | req = mid.(NotExpr).getOperand() or req = mid.(BinaryLogicalOperation).getAnOperand() or req = mid.(RelationalOperation).getAnOperand() or @@ -443,36 +442,36 @@ library class ExprEvaluator extends int { ) or exists(VariableAccess va, Variable v, boolean sub1 | - interestingVariableAccess(e, va, v, sub1) and + this.interestingVariableAccess(e, va, v, sub1) and req = v.getAnAssignedValue() and - (sub1 = true implies not ignoreVariableAssignment(e, v, req)) and + (sub1 = true implies not this.ignoreVariableAssignment(e, v, req)) and sub = false ) or exists(Function f | - interestingFunction(e, f) and + this.interestingFunction(e, f) and returnStmt(f, req) and sub = false ) } private predicate interestingVariableAccess(Expr e, VariableAccess va, Variable v, boolean sub) { - interestingInternal(e, va, sub) and + this.interestingInternal(e, va, sub) and v = getVariableTarget(va) and ( v.hasInitializer() or - sub = true and allowVariableWithoutInitializer(e, v) + sub = true and this.allowVariableWithoutInitializer(e, v) ) and tractableVariable(v) and forall(StmtParent def | nonAnalyzableVariableDefinition(v, def) | sub = true and - ignoreNonAnalyzableVariableDefinition(e, v, def) + this.ignoreNonAnalyzableVariableDefinition(e, v, def) ) } private predicate interestingFunction(Expr e, Function f) { - exists(FunctionCall fc | interestingInternal(e, fc, _) | + exists(FunctionCall fc | this.interestingInternal(e, fc, _) | f = fc.getTarget() and not obviouslyNonConstant(f) and not f.getUnspecifiedType() instanceof VoidType @@ -482,10 +481,10 @@ library class ExprEvaluator extends int { /** Gets the value of subexpressions `req` for expression `e`, if any. */ private int getValueInternal(Expr e, Expr req) { ( - interestingInternal(e, req, true) and + this.interestingInternal(e, req, true) and ( result = req.(CompileTimeConstantInt).getIntValue() or - result = getCompoundValue(e, req.(CompileTimeVariableExpr)) + result = this.getCompoundValue(e, req.(CompileTimeVariableExpr)) ) and ( req.getUnderlyingType().(IntegralType).isSigned() or @@ -496,109 +495,126 @@ library class ExprEvaluator extends int { /** Gets the value of compound subexpressions `val` for expression `e`, if any. */ private int getCompoundValue(Expr e, CompileTimeVariableExpr val) { - interestingInternal(e, val, true) and + this.interestingInternal(e, val, true) and ( exists(NotExpr req | req = val | - result = 1 and getValueInternal(e, req.getOperand()) = 0 + result = 1 and this.getValueInternal(e, req.getOperand()) = 0 or - result = 0 and getValueInternal(e, req.getOperand()) != 0 + result = 0 and this.getValueInternal(e, req.getOperand()) != 0 ) or exists(LogicalAndExpr req | req = val | result = 1 and - getValueInternal(e, req.getLeftOperand()) != 0 and - getValueInternal(e, req.getRightOperand()) != 0 + this.getValueInternal(e, req.getLeftOperand()) != 0 and + this.getValueInternal(e, req.getRightOperand()) != 0 or - result = 0 and getValueInternal(e, req.getAnOperand()) = 0 + result = 0 and this.getValueInternal(e, req.getAnOperand()) = 0 ) or exists(LogicalOrExpr req | req = val | - result = 1 and getValueInternal(e, req.getAnOperand()) != 0 + result = 1 and this.getValueInternal(e, req.getAnOperand()) != 0 or result = 0 and - getValueInternal(e, req.getLeftOperand()) = 0 and - getValueInternal(e, req.getRightOperand()) = 0 + this.getValueInternal(e, req.getLeftOperand()) = 0 and + this.getValueInternal(e, req.getRightOperand()) = 0 ) or exists(LTExpr req | req = val | result = 1 and - getValueInternal(e, req.getLeftOperand()) < getValueInternal(e, req.getRightOperand()) + this.getValueInternal(e, req.getLeftOperand()) < + this.getValueInternal(e, req.getRightOperand()) or result = 0 and - getValueInternal(e, req.getLeftOperand()) >= getValueInternal(e, req.getRightOperand()) + this.getValueInternal(e, req.getLeftOperand()) >= + this.getValueInternal(e, req.getRightOperand()) ) or exists(GTExpr req | req = val | result = 1 and - getValueInternal(e, req.getLeftOperand()) > getValueInternal(e, req.getRightOperand()) + this.getValueInternal(e, req.getLeftOperand()) > + this.getValueInternal(e, req.getRightOperand()) or result = 0 and - getValueInternal(e, req.getLeftOperand()) <= getValueInternal(e, req.getRightOperand()) + this.getValueInternal(e, req.getLeftOperand()) <= + this.getValueInternal(e, req.getRightOperand()) ) or exists(LEExpr req | req = val | result = 1 and - getValueInternal(e, req.getLeftOperand()) <= getValueInternal(e, req.getRightOperand()) + this.getValueInternal(e, req.getLeftOperand()) <= + this.getValueInternal(e, req.getRightOperand()) or result = 0 and - getValueInternal(e, req.getLeftOperand()) > getValueInternal(e, req.getRightOperand()) + this.getValueInternal(e, req.getLeftOperand()) > + this.getValueInternal(e, req.getRightOperand()) ) or exists(GEExpr req | req = val | result = 1 and - getValueInternal(e, req.getLeftOperand()) >= getValueInternal(e, req.getRightOperand()) + this.getValueInternal(e, req.getLeftOperand()) >= + this.getValueInternal(e, req.getRightOperand()) or result = 0 and - getValueInternal(e, req.getLeftOperand()) < getValueInternal(e, req.getRightOperand()) + this.getValueInternal(e, req.getLeftOperand()) < + this.getValueInternal(e, req.getRightOperand()) ) or exists(EQExpr req | req = val | result = 1 and - getValueInternal(e, req.getLeftOperand()) = getValueInternal(e, req.getRightOperand()) + this.getValueInternal(e, req.getLeftOperand()) = + this.getValueInternal(e, req.getRightOperand()) or result = 0 and - getValueInternal(e, req.getLeftOperand()) != getValueInternal(e, req.getRightOperand()) + this.getValueInternal(e, req.getLeftOperand()) != + this.getValueInternal(e, req.getRightOperand()) ) or exists(NEExpr req | req = val | result = 0 and - getValueInternal(e, req.getLeftOperand()) = getValueInternal(e, req.getRightOperand()) + this.getValueInternal(e, req.getLeftOperand()) = + this.getValueInternal(e, req.getRightOperand()) or result = 1 and - getValueInternal(e, req.getLeftOperand()) != getValueInternal(e, req.getRightOperand()) + this.getValueInternal(e, req.getLeftOperand()) != + this.getValueInternal(e, req.getRightOperand()) ) or exists(AddExpr req | req = val | result = - getValueInternal(e, req.getLeftOperand()) + getValueInternal(e, req.getRightOperand()) + this.getValueInternal(e, req.getLeftOperand()) + + this.getValueInternal(e, req.getRightOperand()) ) or exists(SubExpr req | req = val | result = - getValueInternal(e, req.getLeftOperand()) - getValueInternal(e, req.getRightOperand()) + this.getValueInternal(e, req.getLeftOperand()) - + this.getValueInternal(e, req.getRightOperand()) ) or exists(MulExpr req | req = val | result = - getValueInternal(e, req.getLeftOperand()) * getValueInternal(e, req.getRightOperand()) + this.getValueInternal(e, req.getLeftOperand()) * + this.getValueInternal(e, req.getRightOperand()) ) or exists(RemExpr req | req = val | result = - getValueInternal(e, req.getLeftOperand()) % getValueInternal(e, req.getRightOperand()) + this.getValueInternal(e, req.getLeftOperand()) % + this.getValueInternal(e, req.getRightOperand()) ) or exists(DivExpr req | req = val | result = - getValueInternal(e, req.getLeftOperand()) / getValueInternal(e, req.getRightOperand()) + this.getValueInternal(e, req.getLeftOperand()) / + this.getValueInternal(e, req.getRightOperand()) ) or - exists(AssignExpr req | req = val | result = getValueInternal(e, req.getRValue())) + exists(AssignExpr req | req = val | result = this.getValueInternal(e, req.getRValue())) or - result = getVariableValue(e, val.(VariableAccess)) + result = this.getVariableValue(e, val.(VariableAccess)) or exists(FunctionCall call | call = val and not callWithMultipleTargets(call) | - result = getFunctionValue(call.getTarget()) + result = this.getFunctionValue(call.getTarget()) ) ) } @@ -606,7 +622,7 @@ library class ExprEvaluator extends int { language[monotonicAggregates] private int getVariableValue(Expr e, VariableAccess va) { exists(Variable v | - interestingVariableAccess(e, va, v, true) and + this.interestingVariableAccess(e, va, v, true) and // All assignments must have the same int value result = unique(Expr value | @@ -620,14 +636,16 @@ library class ExprEvaluator extends int { /** Holds if the function `f` is considered by the analysis and may return `ret`. */ pragma[noinline] private predicate interestingReturnValue(Function f, Expr ret) { - interestingFunction(_, f) and + this.interestingFunction(_, f) and returnStmt(f, ret) } private int getFunctionValue(Function f) { // All returns must have the same int value // And it must have at least one return - forex(Expr ret | interestingReturnValue(f, ret) | result = getValueInternalNonSubExpr(ret)) + forex(Expr ret | this.interestingReturnValue(f, ret) | + result = this.getValueInternalNonSubExpr(ret) + ) } /** @@ -642,10 +660,10 @@ library class ExprEvaluator extends int { * omitted). */ private int getValueInternalNonSubExpr(Expr req) { - interestingInternal(_, req, false) and + this.interestingInternal(_, req, false) and ( result = req.(CompileTimeConstantInt).getIntValue() or - result = getCompoundValueNonSubExpr(req.(CompileTimeVariableExpr)) + result = this.getCompoundValueNonSubExpr(req.(CompileTimeVariableExpr)) ) and ( req.getUnderlyingType().(IntegralType).isSigned() or @@ -656,131 +674,131 @@ library class ExprEvaluator extends int { private int getCompoundValueNonSubExpr(CompileTimeVariableExpr val) { ( exists(NotExpr req | req = val | - result = 1 and getValueInternalNonSubExpr(req.getOperand()) = 0 + result = 1 and this.getValueInternalNonSubExpr(req.getOperand()) = 0 or - result = 0 and getValueInternalNonSubExpr(req.getOperand()) != 0 + result = 0 and this.getValueInternalNonSubExpr(req.getOperand()) != 0 ) or exists(LogicalAndExpr req | req = val | result = 1 and - getValueInternalNonSubExpr(req.getLeftOperand()) != 0 and - getValueInternalNonSubExpr(req.getRightOperand()) != 0 + this.getValueInternalNonSubExpr(req.getLeftOperand()) != 0 and + this.getValueInternalNonSubExpr(req.getRightOperand()) != 0 or - result = 0 and getValueInternalNonSubExpr(req.getAnOperand()) = 0 + result = 0 and this.getValueInternalNonSubExpr(req.getAnOperand()) = 0 ) or exists(LogicalOrExpr req | req = val | - result = 1 and getValueInternalNonSubExpr(req.getAnOperand()) != 0 + result = 1 and this.getValueInternalNonSubExpr(req.getAnOperand()) != 0 or result = 0 and - getValueInternalNonSubExpr(req.getLeftOperand()) = 0 and - getValueInternalNonSubExpr(req.getRightOperand()) = 0 + this.getValueInternalNonSubExpr(req.getLeftOperand()) = 0 and + this.getValueInternalNonSubExpr(req.getRightOperand()) = 0 ) or exists(LTExpr req | req = val | result = 1 and - getValueInternalNonSubExpr(req.getLeftOperand()) < - getValueInternalNonSubExpr(req.getRightOperand()) + this.getValueInternalNonSubExpr(req.getLeftOperand()) < + this.getValueInternalNonSubExpr(req.getRightOperand()) or result = 0 and - getValueInternalNonSubExpr(req.getLeftOperand()) >= - getValueInternalNonSubExpr(req.getRightOperand()) + this.getValueInternalNonSubExpr(req.getLeftOperand()) >= + this.getValueInternalNonSubExpr(req.getRightOperand()) ) or exists(GTExpr req | req = val | result = 1 and - getValueInternalNonSubExpr(req.getLeftOperand()) > - getValueInternalNonSubExpr(req.getRightOperand()) + this.getValueInternalNonSubExpr(req.getLeftOperand()) > + this.getValueInternalNonSubExpr(req.getRightOperand()) or result = 0 and - getValueInternalNonSubExpr(req.getLeftOperand()) <= - getValueInternalNonSubExpr(req.getRightOperand()) + this.getValueInternalNonSubExpr(req.getLeftOperand()) <= + this.getValueInternalNonSubExpr(req.getRightOperand()) ) or exists(LEExpr req | req = val | result = 1 and - getValueInternalNonSubExpr(req.getLeftOperand()) <= - getValueInternalNonSubExpr(req.getRightOperand()) + this.getValueInternalNonSubExpr(req.getLeftOperand()) <= + this.getValueInternalNonSubExpr(req.getRightOperand()) or result = 0 and - getValueInternalNonSubExpr(req.getLeftOperand()) > - getValueInternalNonSubExpr(req.getRightOperand()) + this.getValueInternalNonSubExpr(req.getLeftOperand()) > + this.getValueInternalNonSubExpr(req.getRightOperand()) ) or exists(GEExpr req | req = val | result = 1 and - getValueInternalNonSubExpr(req.getLeftOperand()) >= - getValueInternalNonSubExpr(req.getRightOperand()) + this.getValueInternalNonSubExpr(req.getLeftOperand()) >= + this.getValueInternalNonSubExpr(req.getRightOperand()) or result = 0 and - getValueInternalNonSubExpr(req.getLeftOperand()) < - getValueInternalNonSubExpr(req.getRightOperand()) + this.getValueInternalNonSubExpr(req.getLeftOperand()) < + this.getValueInternalNonSubExpr(req.getRightOperand()) ) or exists(EQExpr req | req = val | result = 1 and - getValueInternalNonSubExpr(req.getLeftOperand()) = - getValueInternalNonSubExpr(req.getRightOperand()) + this.getValueInternalNonSubExpr(req.getLeftOperand()) = + this.getValueInternalNonSubExpr(req.getRightOperand()) or result = 0 and - getValueInternalNonSubExpr(req.getLeftOperand()) != - getValueInternalNonSubExpr(req.getRightOperand()) + this.getValueInternalNonSubExpr(req.getLeftOperand()) != + this.getValueInternalNonSubExpr(req.getRightOperand()) ) or exists(NEExpr req | req = val | result = 0 and - getValueInternalNonSubExpr(req.getLeftOperand()) = - getValueInternalNonSubExpr(req.getRightOperand()) + this.getValueInternalNonSubExpr(req.getLeftOperand()) = + this.getValueInternalNonSubExpr(req.getRightOperand()) or result = 1 and - getValueInternalNonSubExpr(req.getLeftOperand()) != - getValueInternalNonSubExpr(req.getRightOperand()) + this.getValueInternalNonSubExpr(req.getLeftOperand()) != + this.getValueInternalNonSubExpr(req.getRightOperand()) ) or exists(AddExpr req | req = val | result = - getValueInternalNonSubExpr(req.getLeftOperand()) + - getValueInternalNonSubExpr(req.getRightOperand()) + this.getValueInternalNonSubExpr(req.getLeftOperand()) + + this.getValueInternalNonSubExpr(req.getRightOperand()) ) or exists(SubExpr req | req = val | result = - getValueInternalNonSubExpr(req.getLeftOperand()) - - getValueInternalNonSubExpr(req.getRightOperand()) + this.getValueInternalNonSubExpr(req.getLeftOperand()) - + this.getValueInternalNonSubExpr(req.getRightOperand()) ) or exists(MulExpr req | req = val | result = - getValueInternalNonSubExpr(req.getLeftOperand()) * - getValueInternalNonSubExpr(req.getRightOperand()) + this.getValueInternalNonSubExpr(req.getLeftOperand()) * + this.getValueInternalNonSubExpr(req.getRightOperand()) ) or exists(RemExpr req | req = val | result = - getValueInternalNonSubExpr(req.getLeftOperand()) % - getValueInternalNonSubExpr(req.getRightOperand()) + this.getValueInternalNonSubExpr(req.getLeftOperand()) % + this.getValueInternalNonSubExpr(req.getRightOperand()) ) or exists(DivExpr req | req = val | result = - getValueInternalNonSubExpr(req.getLeftOperand()) / - getValueInternalNonSubExpr(req.getRightOperand()) + this.getValueInternalNonSubExpr(req.getLeftOperand()) / + this.getValueInternalNonSubExpr(req.getRightOperand()) ) or - exists(AssignExpr req | req = val | result = getValueInternalNonSubExpr(req.getRValue())) + exists(AssignExpr req | req = val | result = this.getValueInternalNonSubExpr(req.getRValue())) or - result = getVariableValueNonSubExpr(val.(VariableAccess)) + result = this.getVariableValueNonSubExpr(val.(VariableAccess)) or exists(FunctionCall call | call = val and not callWithMultipleTargets(call) | - result = getFunctionValue(call.getTarget()) + result = this.getFunctionValue(call.getTarget()) ) ) } private int getVariableValueNonSubExpr(VariableAccess va) { // All assignments must have the same int value - result = getMinVariableValueNonSubExpr(va) and - result = getMaxVariableValueNonSubExpr(va) + result = this.getMinVariableValueNonSubExpr(va) and + result = this.getMaxVariableValueNonSubExpr(va) } /** @@ -791,8 +809,9 @@ library class ExprEvaluator extends int { pragma[noopt] private int getMinVariableValueNonSubExpr(VariableAccess va) { exists(Variable v | - interestingVariableAccess(_, va, v, false) and - result = min(Expr value | value = v.getAnAssignedValue() | getValueInternalNonSubExpr(value)) + this.interestingVariableAccess(_, va, v, false) and + result = + min(Expr value | value = v.getAnAssignedValue() | this.getValueInternalNonSubExpr(value)) ) } @@ -804,8 +823,9 @@ library class ExprEvaluator extends int { pragma[noopt] private int getMaxVariableValueNonSubExpr(VariableAccess va) { exists(Variable v | - interestingVariableAccess(_, va, v, false) and - result = max(Expr value | value = v.getAnAssignedValue() | getValueInternalNonSubExpr(value)) + this.interestingVariableAccess(_, va, v, false) and + result = + max(Expr value | value = v.getAnAssignedValue() | this.getValueInternalNonSubExpr(value)) ) } } @@ -968,9 +988,9 @@ library class LoopEntryConditionEvaluator extends ExprEvaluator { abstract predicate isLoopBody(Expr e, StmtParent s); private predicate isLoopBodyDescendant(Expr e, StmtParent s) { - isLoopBody(e, s) + this.isLoopBody(e, s) or - exists(StmtParent mid | isLoopBodyDescendant(e, mid) | + exists(StmtParent mid | this.isLoopBodyDescendant(e, mid) | s = mid.(Stmt).getAChild() or s = mid.(Expr).getAChild() ) @@ -978,13 +998,13 @@ library class LoopEntryConditionEvaluator extends ExprEvaluator { // Same as `interestingInternal(e, sub, true)` but avoids negative recursion private predicate interestingSubExpr(Expr e, Expr sub) { - interesting(e) and e = sub + this.interesting(e) and e = sub or - exists(Expr mid | interestingSubExpr(e, mid) and sub = mid.getAChild()) + exists(Expr mid | this.interestingSubExpr(e, mid) and sub = mid.getAChild()) } private predicate maybeInterestingVariable(Expr e, Variable v) { - exists(VariableAccess va | interestingSubExpr(e, va) | va.getTarget() = v) + exists(VariableAccess va | this.interestingSubExpr(e, va) | va.getTarget() = v) } /** @@ -996,9 +1016,9 @@ library class LoopEntryConditionEvaluator extends ExprEvaluator { * definition of `v`. */ private predicate reachesLoopEntryFromLoopBody(Expr e, Variable v, StmtParent valueOrDef) { - maybeInterestingVariable(e, v) and + this.maybeInterestingVariable(e, v) and (valueOrDef = v.getAnAssignedValue() or nonAnalyzableVariableDefinition(v, valueOrDef)) and - isLoopBodyDescendant(e, valueOrDef) and + this.isLoopBodyDescendant(e, valueOrDef) and /* * Use primitive basic blocks in reachability analysis for better performance. * This is similar to the pattern used in e.g. `DefinitionsAndUses` and @@ -1008,16 +1028,16 @@ library class LoopEntryConditionEvaluator extends ExprEvaluator { exists(PrimitiveBasicBlock bb1, int pos1 | bb1.getNode(pos1) = valueOrDef | // Reaches in same basic block exists(int pos2 | - loopEntryAt(bb1, pos2, e) and + this.loopEntryAt(bb1, pos2, e) and pos2 > pos1 and - not exists(int k | assignmentAt(bb1, k, v) | k in [pos1 + 1 .. pos2 - 1]) + not exists(int k | this.assignmentAt(bb1, k, v) | k in [pos1 + 1 .. pos2 - 1]) ) or // Reaches in a successor block exists(PrimitiveBasicBlock bb2 | bb2 = bb1.getASuccessor() and - not exists(int pos3 | assignmentAt(bb1, pos3, v) and pos3 > pos1) and - bbReachesLoopEntry(bb2, e, v) + not exists(int pos3 | this.assignmentAt(bb1, pos3, v) and pos3 > pos1) and + this.bbReachesLoopEntry(bb2, e, v) ) ) } @@ -1025,12 +1045,12 @@ library class LoopEntryConditionEvaluator extends ExprEvaluator { private predicate loopEntryAt(PrimitiveBasicBlock bb, int pos, Expr e) { exists(Node cfn | bb.getNode(pos) = cfn and - isLoopEntry(e, cfn) + this.isLoopEntry(e, cfn) ) } private predicate assignmentAt(PrimitiveBasicBlock bb, int pos, Variable v) { - maybeInterestingVariable(_, v) and + this.maybeInterestingVariable(_, v) and bb.getNode(pos) = v.getAnAssignedValue() } @@ -1039,19 +1059,19 @@ library class LoopEntryConditionEvaluator extends ExprEvaluator { * the loop belonging to `e` without crossing an assignment to `v`. */ private predicate bbReachesLoopEntry(PrimitiveBasicBlock bb, Expr e, Variable v) { - bbReachesLoopEntryLocally(bb, e, v) + this.bbReachesLoopEntryLocally(bb, e, v) or exists(PrimitiveBasicBlock succ | succ = bb.getASuccessor() | - bbReachesLoopEntry(succ, e, v) and - not assignmentAt(bb, _, v) + this.bbReachesLoopEntry(succ, e, v) and + not this.assignmentAt(bb, _, v) ) } private predicate bbReachesLoopEntryLocally(PrimitiveBasicBlock bb, Expr e, Variable v) { exists(int pos | - loopEntryAt(bb, pos, e) and - maybeInterestingVariable(e, v) and - not exists(int pos1 | assignmentAt(bb, pos1, v) | pos1 < pos) + this.loopEntryAt(bb, pos, e) and + this.maybeInterestingVariable(e, v) and + not exists(int pos1 | this.assignmentAt(bb, pos1, v) | pos1 < pos) ) } @@ -1085,10 +1105,10 @@ library class LoopEntryConditionEvaluator extends ExprEvaluator { * ``` */ override predicate ignoreNonAnalyzableVariableDefinition(Expr e, Variable v, StmtParent def) { - maybeInterestingVariable(e, v) and + this.maybeInterestingVariable(e, v) and nonAnalyzableVariableDefinition(v, def) and - isLoopBodyDescendant(e, def) and - not reachesLoopEntryFromLoopBody(e, v, def) + this.isLoopBodyDescendant(e, def) and + not this.reachesLoopEntryFromLoopBody(e, v, def) } /** @@ -1121,10 +1141,10 @@ library class LoopEntryConditionEvaluator extends ExprEvaluator { * ``` */ override predicate ignoreVariableAssignment(Expr e, Variable v, Expr value) { - maybeInterestingVariable(e, v) and + this.maybeInterestingVariable(e, v) and value = v.getAnAssignedValue() and - isLoopBodyDescendant(e, value) and - not reachesLoopEntryFromLoopBody(e, v, value) + this.isLoopBodyDescendant(e, value) and + not this.reachesLoopEntryFromLoopBody(e, v, value) } } diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll index f588a25a176..e11244c42b0 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -801,6 +801,9 @@ private module Cached { exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) } + cached + predicate allowParameterReturnInSelfCached(ParamNode p) { allowParameterReturnInSelf(p) } + cached newtype TCallContext = TAnyCallContext() or @@ -937,7 +940,7 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall { } override predicate relevantFor(DataFlowCallable callable) { - recordDataFlowCallSite(getCall(), callable) + recordDataFlowCallSite(this.getCall(), callable) } override predicate matchesCall(DataFlowCall call) { call = this.getCall() } @@ -1236,6 +1239,13 @@ class TypedContent extends MkTypedContent { /** Gets a textual representation of this content. */ string toString() { result = c.toString() } + + /** + * Holds if access paths with this `TypedContent` at their head always should + * be tracked at high precision. This disables adaptive access path precision + * for such access paths. + */ + predicate forceHighPrecision() { forceHighPrecision(c) } } /** @@ -1250,7 +1260,7 @@ abstract class AccessPathFront extends TAccessPathFront { TypedContent getHead() { this = TFrontHead(result) } - predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) } + predicate isClearedAt(Node n) { clearsContentCached(n, this.getHead().getContent()) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll index a55e65a81f6..dd64fc70039 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll @@ -175,6 +175,7 @@ module Consistency { query predicate postWithInFlow(Node n, string msg) { isPostUpdateNode(n) and + not clearsContent(n, _) and simpleLocalFlowStep(_, n) and msg = "PostUpdateNode should not be the target of local flow." } diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll index e64f8277528..0eecf7a6040 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll @@ -219,15 +219,13 @@ class DataFlowExpr = Expr; class DataFlowType = Type; /** A function call relevant for data flow. */ -class DataFlowCall extends Expr { - DataFlowCall() { this instanceof Call } - +class DataFlowCall extends Expr instanceof Call { /** * Gets the nth argument for this call. * * The range of `n` is from `0` to `getNumberOfArguments() - 1`. */ - Expr getArgument(int n) { result = this.(Call).getArgument(n) } + Expr getArgument(int n) { result = super.getArgument(n) } /** Gets the data flow node corresponding to this call. */ ExprNode getNode() { result.getExpr() = this } @@ -240,6 +238,12 @@ predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub impl int accessPathLimit() { result = 5 } +/** + * Holds if access paths with `c` at their head always should be tracked at high + * precision. This disables adaptive access path precision for such access paths. + */ +predicate forceHighPrecision(Content c) { none() } + /** The unit type. */ private newtype TUnit = TMkUnit() @@ -283,3 +287,12 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { no /** Extra data-flow steps needed for lambda flow analysis. */ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() } + +/** + * Holds if flow is allowed to pass from parameter `p` and back to itself as a + * side-effect, resulting in a summary from `p` to itself. + * + * One example would be to allow flow like `p.foo = p.bar;`, which is disallowed + * by default as a heuristic. + */ +predicate allowParameterReturnInSelf(ParameterNode p) { none() } diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll index 01338eaeff4..c67374c3db9 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll @@ -101,18 +101,18 @@ class Node extends TNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { - getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) } /** * Gets an upper bound on the type of this node. */ - Type getTypeBound() { result = getType() } + Type getTypeBound() { result = this.getType() } } /** @@ -293,11 +293,11 @@ abstract class PostUpdateNode extends Node { */ abstract Node getPreUpdateNode(); - override Function getFunction() { result = getPreUpdateNode().getFunction() } + override Function getFunction() { result = this.getPreUpdateNode().getFunction() } - override Type getType() { result = getPreUpdateNode().getType() } + override Type getType() { result = this.getPreUpdateNode().getType() } - override Location getLocation() { result = getPreUpdateNode().getLocation() } + override Location getLocation() { result = this.getPreUpdateNode().getLocation() } } abstract private class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNode { @@ -309,7 +309,7 @@ abstract private class PartialDefinitionNode extends PostUpdateNode, TPartialDef PartialDefinition getPartialDefinition() { result = pd } - override string toString() { result = getPreUpdateNode().toString() + " [post update]" } + override string toString() { result = this.getPreUpdateNode().toString() + " [post update]" } } private class VariablePartialDefinitionNode extends PartialDefinitionNode { @@ -380,13 +380,13 @@ private class ObjectInitializerNode extends PostUpdateNode, TExprNode { class PreObjectInitializerNode extends Node, TPreObjectInitializerNode { Expr getExpr() { this = TPreObjectInitializerNode(result) } - override Function getFunction() { result = getExpr().getEnclosingFunction() } + override Function getFunction() { result = this.getExpr().getEnclosingFunction() } - override Type getType() { result = getExpr().getType() } + override Type getType() { result = this.getExpr().getType() } - override Location getLocation() { result = getExpr().getLocation() } + override Location getLocation() { result = this.getExpr().getLocation() } - override string toString() { result = getExpr().toString() + " [pre init]" } + override string toString() { result = this.getExpr().toString() + " [pre init]" } } /** @@ -401,7 +401,7 @@ private class PostConstructorInitThis extends PostUpdateNode, TPostConstructorIn } override string toString() { - result = getPreUpdateNode().getConstructorFieldInit().toString() + " [post-this]" + result = this.getPreUpdateNode().getConstructorFieldInit().toString() + " [post-this]" } } @@ -416,15 +416,17 @@ private class PostConstructorInitThis extends PostUpdateNode, TPostConstructorIn class PreConstructorInitThis extends Node, TPreConstructorInitThis { ConstructorFieldInit getConstructorFieldInit() { this = TPreConstructorInitThis(result) } - override Constructor getFunction() { result = getConstructorFieldInit().getEnclosingFunction() } - - override PointerType getType() { - result.getBaseType() = getConstructorFieldInit().getEnclosingFunction().getDeclaringType() + override Constructor getFunction() { + result = this.getConstructorFieldInit().getEnclosingFunction() } - override Location getLocation() { result = getConstructorFieldInit().getLocation() } + override PointerType getType() { + result.getBaseType() = this.getConstructorFieldInit().getEnclosingFunction().getDeclaringType() + } - override string toString() { result = getConstructorFieldInit().toString() + " [pre-this]" } + override Location getLocation() { result = this.getConstructorFieldInit().getLocation() } + + override string toString() { result = this.getConstructorFieldInit().toString() + " [pre-this]" } } /** diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowVar.qll index e3f8b6f68fb..c01edf0429a 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowVar.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowVar.qll @@ -354,7 +354,7 @@ module FlowVar_internal { result = def.getAUse(v) or exists(SsaDefinition descendentDef | - getASuccessorSsaVar+() = TSsaVar(descendentDef, _) and + this.getASuccessorSsaVar+() = TSsaVar(descendentDef, _) and result = descendentDef.getAUse(v) ) ) @@ -515,7 +515,7 @@ module FlowVar_internal { this.bbInLoopCondition(bbInside) and not this.bbInLoop(bbOutside) and bbOutside = bbInside.getASuccessor() and - not reachesWithoutAssignment(bbInside, v) + not this.reachesWithoutAssignment(bbInside, v) } /** @@ -546,7 +546,7 @@ module FlowVar_internal { private predicate bbInLoop(BasicBlock bb) { bbDominates(this.(Loop).getStmt(), bb) or - bbInLoopCondition(bb) + this.bbInLoopCondition(bb) } /** Holds if `sbb` is inside this loop. */ @@ -563,7 +563,7 @@ module FlowVar_internal { bb = this.(Loop).getStmt() and v = this.getARelevantVariable() or - reachesWithoutAssignment(bb.getAPredecessor(), v) and + this.reachesWithoutAssignment(bb.getAPredecessor(), v) and this.bbInLoop(bb) ) and not assignsToVar(bb, v) diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll index fa9d2e94081..4fbea43b805 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll @@ -80,7 +80,7 @@ class SubBasicBlock extends ControlFlowNodeBase { * returns a 0-based position, while `getRankInBasicBlock` returns a 1-based * position. */ - deprecated int getPosInBasicBlock(BasicBlock bb) { result = getRankInBasicBlock(bb) - 1 } + deprecated int getPosInBasicBlock(BasicBlock bb) { result = this.getRankInBasicBlock(bb) - 1 } pragma[noinline] private int getIndexInBasicBlock(BasicBlock bb) { this = bb.getNode(result) } @@ -102,7 +102,7 @@ class SubBasicBlock extends ControlFlowNodeBase { exists(BasicBlock bb | exists(int outerIndex | result = bb.getNode(outerIndex) and - index = outerToInnerIndex(bb, outerIndex) + index = this.outerToInnerIndex(bb, outerIndex) ) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll index f4f73b8247c..acb029c23d9 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration { predicate isSanitizer(DataFlow::Node node) { none() } final override predicate isBarrier(DataFlow::Node node) { - isSanitizer(node) or + this.isSanitizer(node) or defaultTaintSanitizer(node) } /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } - final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } + final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) } /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } - final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } + final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } - final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } + final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { + this.isSanitizerGuard(guard) + } /** * Holds if the additional taint propagation step from `node1` to `node2` @@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration { predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() } final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - isAdditionalTaintStep(node1, node2) or + this.isAdditionalTaintStep(node1, node2) or defaultAdditionalTaintStep(node1, node2) } diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll index f4f73b8247c..acb029c23d9 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration { predicate isSanitizer(DataFlow::Node node) { none() } final override predicate isBarrier(DataFlow::Node node) { - isSanitizer(node) or + this.isSanitizer(node) or defaultTaintSanitizer(node) } /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } - final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } + final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) } /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } - final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } + final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } - final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } + final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { + this.isSanitizerGuard(guard) + } /** * Holds if the additional taint propagation step from `node1` to `node2` @@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration { predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() } final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - isAdditionalTaintStep(node1, node2) or + this.isAdditionalTaintStep(node1, node2) or defaultAdditionalTaintStep(node1, node2) } diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/Access.qll b/cpp/ql/lib/semmle/code/cpp/exprs/Access.qll index e18c0c78dc6..35cf1974127 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/Access.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/Access.qll @@ -203,7 +203,7 @@ class PointerFieldAccess extends FieldAccess { PointerFieldAccess() { exists(PointerType t | - t = getQualifier().getFullyConverted().getUnspecifiedType() and + t = this.getQualifier().getFullyConverted().getUnspecifiedType() and t.getBaseType() instanceof Class ) } @@ -218,7 +218,9 @@ class PointerFieldAccess extends FieldAccess { class DotFieldAccess extends FieldAccess { override string getAPrimaryQlClass() { result = "DotFieldAccess" } - DotFieldAccess() { exists(Class c | c = getQualifier().getFullyConverted().getUnspecifiedType()) } + DotFieldAccess() { + exists(Class c | c = this.getQualifier().getFullyConverted().getUnspecifiedType()) + } } /** diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/ArithmeticOperation.qll b/cpp/ql/lib/semmle/code/cpp/exprs/ArithmeticOperation.qll index b94c9cee724..615192c86a7 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/ArithmeticOperation.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/ArithmeticOperation.qll @@ -148,7 +148,7 @@ class PostfixIncrExpr extends IncrementOperation, PostfixCrementOperation, @post override int getPrecedence() { result = 17 } - override string toString() { result = "... " + getOperator() } + override string toString() { result = "... " + this.getOperator() } } /** @@ -166,7 +166,7 @@ class PostfixDecrExpr extends DecrementOperation, PostfixCrementOperation, @post override int getPrecedence() { result = 17 } - override string toString() { result = "... " + getOperator() } + override string toString() { result = "... " + this.getOperator() } } /** diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll b/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll index b1f97f18802..dcbedde4475 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll @@ -35,12 +35,12 @@ class BuiltInVarArgsStart extends VarArgsExpr, @vastartexpr { /** * Gets the `va_list` argument. */ - final Expr getVAList() { result = getChild(0) } + final Expr getVAList() { result = this.getChild(0) } /** * Gets the argument that specifies the last named parameter before the ellipsis. */ - final VariableAccess getLastNamedParameter() { result = getChild(1) } + final VariableAccess getLastNamedParameter() { result = this.getChild(1) } } /** @@ -60,7 +60,7 @@ class BuiltInVarArgsEnd extends VarArgsExpr, @vaendexpr { /** * Gets the `va_list` argument. */ - final Expr getVAList() { result = getChild(0) } + final Expr getVAList() { result = this.getChild(0) } } /** @@ -78,7 +78,7 @@ class BuiltInVarArg extends VarArgsExpr, @vaargexpr { /** * Gets the `va_list` argument. */ - final Expr getVAList() { result = getChild(0) } + final Expr getVAList() { result = this.getChild(0) } } /** @@ -98,12 +98,12 @@ class BuiltInVarArgCopy extends VarArgsExpr, @vacopyexpr { /** * Gets the destination `va_list` argument. */ - final Expr getDestinationVAList() { result = getChild(0) } + final Expr getDestinationVAList() { result = this.getChild(0) } /** * Gets the the source `va_list` argument. */ - final Expr getSourceVAList() { result = getChild(1) } + final Expr getSourceVAList() { result = this.getChild(1) } } /** diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/Call.qll b/cpp/ql/lib/semmle/code/cpp/exprs/Call.qll index 6f6f710ac4b..b4761dffe9a 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/Call.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/Call.qll @@ -71,10 +71,10 @@ class Call extends Expr, NameQualifiableElement, TCall { * at index 2, respectively. */ Expr getAnArgumentSubExpr(int index) { - result = getArgument(index) + result = this.getArgument(index) or exists(Expr mid | - mid = getAnArgumentSubExpr(index) and + mid = this.getAnArgumentSubExpr(index) and not mid instanceof Call and not mid instanceof SizeofOperator and result = mid.getAChild() @@ -167,27 +167,27 @@ class FunctionCall extends Call, @funbindexpr { override string getAPrimaryQlClass() { result = "FunctionCall" } /** Gets an explicit template argument for this call. */ - Locatable getAnExplicitTemplateArgument() { result = getExplicitTemplateArgument(_) } + Locatable getAnExplicitTemplateArgument() { result = this.getExplicitTemplateArgument(_) } /** Gets an explicit template argument value for this call. */ - Locatable getAnExplicitTemplateArgumentKind() { result = getExplicitTemplateArgumentKind(_) } + Locatable getAnExplicitTemplateArgumentKind() { result = this.getExplicitTemplateArgumentKind(_) } /** Gets a template argument for this call. */ - Locatable getATemplateArgument() { result = getTarget().getATemplateArgument() } + Locatable getATemplateArgument() { result = this.getTarget().getATemplateArgument() } /** Gets a template argument value for this call. */ - Locatable getATemplateArgumentKind() { result = getTarget().getATemplateArgumentKind() } + Locatable getATemplateArgumentKind() { result = this.getTarget().getATemplateArgumentKind() } /** Gets the nth explicit template argument for this call. */ Locatable getExplicitTemplateArgument(int n) { - n < getNumberOfExplicitTemplateArguments() and - result = getTemplateArgument(n) + n < this.getNumberOfExplicitTemplateArguments() and + result = this.getTemplateArgument(n) } /** Gets the nth explicit template argument value for this call. */ Locatable getExplicitTemplateArgumentKind(int n) { - n < getNumberOfExplicitTemplateArguments() and - result = getTemplateArgumentKind(n) + n < this.getNumberOfExplicitTemplateArguments() and + result = this.getTemplateArgumentKind(n) } /** Gets the number of explicit template arguments for this call. */ @@ -198,19 +198,19 @@ class FunctionCall extends Call, @funbindexpr { } /** Gets the number of template arguments for this call. */ - int getNumberOfTemplateArguments() { result = count(int i | exists(getTemplateArgument(i))) } + int getNumberOfTemplateArguments() { result = count(int i | exists(this.getTemplateArgument(i))) } /** Gets the nth template argument for this call (indexed from 0). */ - Locatable getTemplateArgument(int n) { result = getTarget().getTemplateArgument(n) } + Locatable getTemplateArgument(int n) { result = this.getTarget().getTemplateArgument(n) } /** Gets the nth template argument value for this call (indexed from 0). */ - Locatable getTemplateArgumentKind(int n) { result = getTarget().getTemplateArgumentKind(n) } + Locatable getTemplateArgumentKind(int n) { result = this.getTarget().getTemplateArgumentKind(n) } /** Holds if any template arguments for this call are implicit / deduced. */ predicate hasImplicitTemplateArguments() { exists(int i | - exists(getTemplateArgument(i)) and - not exists(getExplicitTemplateArgument(i)) + exists(this.getTemplateArgument(i)) and + not exists(this.getExplicitTemplateArgument(i)) ) } @@ -233,9 +233,9 @@ class FunctionCall extends Call, @funbindexpr { * visible at the call site. */ Type getExpectedReturnType() { - if getTargetType() instanceof RoutineType - then result = getTargetType().(RoutineType).getReturnType() - else result = getTarget().getType() + if this.getTargetType() instanceof RoutineType + then result = this.getTargetType().(RoutineType).getReturnType() + else result = this.getTarget().getType() } /** @@ -247,9 +247,9 @@ class FunctionCall extends Call, @funbindexpr { * was visible at the call site. */ Type getExpectedParameterType(int n) { - if getTargetType() instanceof RoutineType - then result = getTargetType().(RoutineType).getParameterType(n) - else result = getTarget().getParameter(n).getType() + if this.getTargetType() instanceof RoutineType + then result = this.getTargetType().(RoutineType).getParameterType(n) + else result = this.getTarget().getParameter(n).getType() } /** @@ -263,7 +263,7 @@ class FunctionCall extends Call, @funbindexpr { /** * Gets the type of this expression, that is, the return type of the function being called. */ - override Type getType() { result = getExpectedReturnType() } + override Type getType() { result = this.getExpectedReturnType() } /** * Holds if this is a call to a virtual function. @@ -280,7 +280,7 @@ class FunctionCall extends Call, @funbindexpr { /** Gets a textual representation of this function call. */ override string toString() { - if exists(getTarget()) + if exists(this.getTarget()) then result = "call to " + this.getTarget().getName() else result = "call to unknown function" } @@ -288,15 +288,15 @@ class FunctionCall extends Call, @funbindexpr { override predicate mayBeImpure() { this.getChild(_).mayBeImpure() or this.getTarget().mayHaveSideEffects() or - isVirtual() or - getTarget().getAnAttribute().getName() = "weak" + this.isVirtual() or + this.getTarget().getAnAttribute().getName() = "weak" } override predicate mayBeGloballyImpure() { this.getChild(_).mayBeGloballyImpure() or this.getTarget().mayHaveSideEffects() or - isVirtual() or - getTarget().getAnAttribute().getName() = "weak" + this.isVirtual() or + this.getTarget().getAnAttribute().getName() = "weak" } } @@ -367,7 +367,7 @@ class OverloadedPointerDereferenceExpr extends FunctionCall { * ``` */ class OverloadedArrayExpr extends FunctionCall { - OverloadedArrayExpr() { getTarget().hasName("operator[]") } + OverloadedArrayExpr() { this.getTarget().hasName("operator[]") } override string getAPrimaryQlClass() { result = "OverloadedArrayExpr" } @@ -585,7 +585,7 @@ class ConstructorFieldInit extends ConstructorInit, @ctorfieldinit { */ Expr getExpr() { result = this.getChild(0) } - override string toString() { result = "constructor init of field " + getTarget().getName() } + override string toString() { result = "constructor init of field " + this.getTarget().getName() } override predicate mayBeImpure() { this.getExpr().mayBeImpure() } diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/Cast.qll b/cpp/ql/lib/semmle/code/cpp/exprs/Cast.qll index ebe88ddf71c..273023a8229 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/Cast.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/Cast.qll @@ -188,8 +188,8 @@ private predicate isPointerToMemberOrNullPointer(Type type) { class ArithmeticConversion extends Cast { ArithmeticConversion() { conversionkinds(underlyingElement(this), 0) and - isArithmeticOrEnum(getUnspecifiedType()) and - isArithmeticOrEnum(getExpr().getUnspecifiedType()) + isArithmeticOrEnum(this.getUnspecifiedType()) and + isArithmeticOrEnum(this.getExpr().getUnspecifiedType()) } override string getSemanticConversionString() { result = "arithmetic conversion" } @@ -204,8 +204,8 @@ class ArithmeticConversion extends Cast { */ class IntegralConversion extends ArithmeticConversion { IntegralConversion() { - isIntegralOrEnum(getUnspecifiedType()) and - isIntegralOrEnum(getExpr().getUnspecifiedType()) + isIntegralOrEnum(this.getUnspecifiedType()) and + isIntegralOrEnum(this.getExpr().getUnspecifiedType()) } override string getAPrimaryQlClass() { @@ -224,8 +224,8 @@ class IntegralConversion extends ArithmeticConversion { */ class FloatingPointConversion extends ArithmeticConversion { FloatingPointConversion() { - getUnspecifiedType() instanceof FloatingPointType and - getExpr().getUnspecifiedType() instanceof FloatingPointType + this.getUnspecifiedType() instanceof FloatingPointType and + this.getExpr().getUnspecifiedType() instanceof FloatingPointType } override string getAPrimaryQlClass() { @@ -244,8 +244,8 @@ class FloatingPointConversion extends ArithmeticConversion { */ class FloatingPointToIntegralConversion extends ArithmeticConversion { FloatingPointToIntegralConversion() { - isIntegralOrEnum(getUnspecifiedType()) and - getExpr().getUnspecifiedType() instanceof FloatingPointType + isIntegralOrEnum(this.getUnspecifiedType()) and + this.getExpr().getUnspecifiedType() instanceof FloatingPointType } override string getAPrimaryQlClass() { @@ -264,8 +264,8 @@ class FloatingPointToIntegralConversion extends ArithmeticConversion { */ class IntegralToFloatingPointConversion extends ArithmeticConversion { IntegralToFloatingPointConversion() { - getUnspecifiedType() instanceof FloatingPointType and - isIntegralOrEnum(getExpr().getUnspecifiedType()) + this.getUnspecifiedType() instanceof FloatingPointType and + isIntegralOrEnum(this.getExpr().getUnspecifiedType()) } override string getAPrimaryQlClass() { @@ -290,8 +290,8 @@ class IntegralToFloatingPointConversion extends ArithmeticConversion { class PointerConversion extends Cast { PointerConversion() { conversionkinds(underlyingElement(this), 0) and - isPointerOrNullPointer(getUnspecifiedType()) and - isPointerOrNullPointer(getExpr().getUnspecifiedType()) + isPointerOrNullPointer(this.getUnspecifiedType()) and + isPointerOrNullPointer(this.getExpr().getUnspecifiedType()) } override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "PointerConversion" } @@ -315,8 +315,8 @@ class PointerToMemberConversion extends Cast { PointerToMemberConversion() { conversionkinds(underlyingElement(this), 0) and exists(Type fromType, Type toType | - fromType = getExpr().getUnspecifiedType() and - toType = getUnspecifiedType() and + fromType = this.getExpr().getUnspecifiedType() and + toType = this.getUnspecifiedType() and isPointerToMemberOrNullPointer(fromType) and isPointerToMemberOrNullPointer(toType) and // A conversion from nullptr to nullptr is a `PointerConversion`, not a @@ -345,8 +345,8 @@ class PointerToMemberConversion extends Cast { class PointerToIntegralConversion extends Cast { PointerToIntegralConversion() { conversionkinds(underlyingElement(this), 0) and - isIntegralOrEnum(getUnspecifiedType()) and - isPointerOrNullPointer(getExpr().getUnspecifiedType()) + isIntegralOrEnum(this.getUnspecifiedType()) and + isPointerOrNullPointer(this.getExpr().getUnspecifiedType()) } override string getAPrimaryQlClass() { @@ -366,8 +366,8 @@ class PointerToIntegralConversion extends Cast { class IntegralToPointerConversion extends Cast { IntegralToPointerConversion() { conversionkinds(underlyingElement(this), 0) and - isPointerOrNullPointer(getUnspecifiedType()) and - isIntegralOrEnum(getExpr().getUnspecifiedType()) + isPointerOrNullPointer(this.getUnspecifiedType()) and + isIntegralOrEnum(this.getExpr().getUnspecifiedType()) } override string getAPrimaryQlClass() { @@ -403,7 +403,7 @@ class BoolConversion extends Cast { class VoidConversion extends Cast { VoidConversion() { conversionkinds(underlyingElement(this), 0) and - getUnspecifiedType() instanceof VoidType + this.getUnspecifiedType() instanceof VoidType } override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "VoidConversion" } @@ -434,8 +434,8 @@ class InheritanceConversion extends Cast { * conversion is to an indirect virtual base class. */ final ClassDerivation getDerivation() { - result.getBaseClass() = getBaseClass() and - result.getDerivedClass() = getDerivedClass() + result.getBaseClass() = this.getBaseClass() and + result.getDerivedClass() = this.getDerivedClass() } /** @@ -490,12 +490,12 @@ class BaseClassConversion extends InheritanceConversion { override Class getBaseClass() { result = getConversionClass(this) } - override Class getDerivedClass() { result = getConversionClass(getExpr()) } + override Class getDerivedClass() { result = getConversionClass(this.getExpr()) } /** * Holds if this conversion is to a virtual base class. */ - predicate isVirtual() { getDerivation().isVirtual() or not exists(getDerivation()) } + predicate isVirtual() { this.getDerivation().isVirtual() or not exists(this.getDerivation()) } } /** @@ -515,7 +515,7 @@ class DerivedClassConversion extends InheritanceConversion { override string getSemanticConversionString() { result = "derived class conversion" } - override Class getBaseClass() { result = getConversionClass(getExpr()) } + override Class getBaseClass() { result = getConversionClass(this.getExpr()) } override Class getDerivedClass() { result = getConversionClass(this) } } @@ -637,8 +637,8 @@ class DynamicCast extends Cast, @dynamic_cast { */ class UuidofOperator extends Expr, @uuidof { override string toString() { - if exists(getTypeOperand()) - then result = "__uuidof(" + getTypeOperand().getName() + ")" + if exists(this.getTypeOperand()) + then result = "__uuidof(" + this.getTypeOperand().getName() + ")" else result = "__uuidof(0)" } diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll b/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll index f77518c2f56..2811d1703aa 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll @@ -26,7 +26,7 @@ class Expr extends StmtParent, @expr { Function getEnclosingFunction() { result = exprEnclosingElement(this) } /** Gets the nearest enclosing set of curly braces around this expression in the source, if any. */ - BlockStmt getEnclosingBlock() { result = getEnclosingStmt().getEnclosingBlock() } + BlockStmt getEnclosingBlock() { result = this.getEnclosingStmt().getEnclosingBlock() } override Stmt getEnclosingStmt() { result = this.getParent().(Expr).getEnclosingStmt() @@ -219,13 +219,13 @@ class Expr extends StmtParent, @expr { * Holds if this expression is a _glvalue_. A _glvalue_ is either an _lvalue_ or an * _xvalue_. */ - predicate isGLValueCategory() { isLValueCategory() or isXValueCategory() } + predicate isGLValueCategory() { this.isLValueCategory() or this.isXValueCategory() } /** * Holds if this expression is an _rvalue_. An _rvalue_ is either a _prvalue_ or an * _xvalue_. */ - predicate isRValueCategory() { isPRValueCategory() or isXValueCategory() } + predicate isRValueCategory() { this.isPRValueCategory() or this.isXValueCategory() } /** * Gets a string representation of the value category of the expression. @@ -240,15 +240,15 @@ class Expr extends StmtParent, @expr { * `hasLValueToRvalueConversion()` holds. */ string getValueCategoryString() { - isLValueCategory() and + this.isLValueCategory() and result = "lvalue" or - isXValueCategory() and + this.isXValueCategory() and result = "xvalue" or ( - isPRValueCategory() and - if hasLValueToRValueConversion() then result = "prvalue(load)" else result = "prvalue" + this.isPRValueCategory() and + if this.hasLValueToRValueConversion() then result = "prvalue(load)" else result = "prvalue" ) } @@ -263,7 +263,7 @@ class Expr extends StmtParent, @expr { * such as an expression inside a sizeof. */ predicate isUnevaluated() { - exists(Element e | e = getParentWithConversions+() | + exists(Element e | e = this.getParentWithConversions+() | e instanceof SizeofOperator or exists(Expr e2 | @@ -279,7 +279,7 @@ class Expr extends StmtParent, @expr { e instanceof AlignofOperator ) or - exists(Decltype d | d.getExpr() = getParentWithConversions*()) + exists(Decltype d | d.getExpr() = this.getParentWithConversions*()) } /** @@ -725,7 +725,7 @@ class PointerDereferenceExpr extends UnaryOperation, @indirect { * * Gets the expression that is being dereferenced. */ - deprecated Expr getExpr() { result = getOperand() } + deprecated Expr getExpr() { result = this.getOperand() } override string getOperator() { result = "*" } @@ -780,15 +780,15 @@ class NewOrNewArrayExpr extends Expr, @any_new_expr { * Gets the alignment argument passed to the allocation function, if any. */ Expr getAlignmentArgument() { - hasAlignedAllocation() and + this.hasAlignedAllocation() and ( // If we have an allocator call, the alignment is the second argument to // that call. - result = getAllocatorCall().getArgument(1) + result = this.getAllocatorCall().getArgument(1) or // Otherwise, the alignment winds up as child number 3 of the `new` // itself. - result = getChild(3) + result = this.getChild(3) ) } @@ -916,7 +916,7 @@ class NewArrayExpr extends NewOrNewArrayExpr, @new_array_expr { * Gets the element type of the array being allocated. */ Type getAllocatedElementType() { - result = getType().getUnderlyingType().(PointerType).getBaseType() + result = this.getType().getUnderlyingType().(PointerType).getBaseType() } /** @@ -946,7 +946,12 @@ class DeleteExpr extends Expr, @delete_expr { */ Type getDeletedObjectType() { result = - getExpr().getFullyConverted().getType().stripTopLevelSpecifiers().(PointerType).getBaseType() + this.getExpr() + .getFullyConverted() + .getType() + .stripTopLevelSpecifiers() + .(PointerType) + .getBaseType() } /** @@ -957,7 +962,7 @@ class DeleteExpr extends Expr, @delete_expr { /** * Gets the destructor to be called to destroy the object, if any. */ - Destructor getDestructor() { result = getDestructorCall().getTarget() } + Destructor getDestructor() { result = this.getDestructorCall().getTarget() } /** * Gets the `operator delete` that deallocates storage. Does not hold @@ -1020,7 +1025,12 @@ class DeleteArrayExpr extends Expr, @delete_array_expr { */ Type getDeletedElementType() { result = - getExpr().getFullyConverted().getType().stripTopLevelSpecifiers().(PointerType).getBaseType() + this.getExpr() + .getFullyConverted() + .getType() + .stripTopLevelSpecifiers() + .(PointerType) + .getBaseType() } /** @@ -1034,7 +1044,7 @@ class DeleteArrayExpr extends Expr, @delete_array_expr { /** * Gets the destructor to be called to destroy each element in the array, if any. */ - Destructor getDestructor() { result = getDestructorCall().getTarget() } + Destructor getDestructor() { result = this.getDestructorCall().getTarget() } /** * Gets the `operator delete[]` that deallocates storage. @@ -1101,7 +1111,7 @@ class StmtExpr extends Expr, @expr_stmt { * x = ({ dosomething(); a+b; }); * ``` */ - Expr getResultExpr() { result = getStmtResultExpr(getStmt()) } + Expr getResultExpr() { result = getStmtResultExpr(this.getStmt()) } } /** Get the result expression of a statement. (Helper function for StmtExpr.) */ @@ -1230,20 +1240,20 @@ class FoldExpr extends Expr, @foldexpr { predicate isRightFold() { fold(underlyingElement(this), _, false) } /** Holds if this is a unary fold expression. */ - predicate isUnaryFold() { getNumChild() = 1 } + predicate isUnaryFold() { this.getNumChild() = 1 } /** Holds if this is a binary fold expression. */ - predicate isBinaryFold() { getNumChild() = 2 } + predicate isBinaryFold() { this.getNumChild() = 2 } /** * Gets the child expression containing the unexpanded parameter pack. */ Expr getPackExpr() { this.isUnaryFold() and - result = getChild(0) + result = this.getChild(0) or this.isBinaryFold() and - if this.isRightFold() then result = getChild(0) else result = getChild(1) + if this.isRightFold() then result = this.getChild(0) else result = this.getChild(1) } /** @@ -1251,7 +1261,7 @@ class FoldExpr extends Expr, @foldexpr { */ Expr getInitExpr() { this.isBinaryFold() and - if this.isRightFold() then result = getChild(1) else result = getChild(0) + if this.isRightFold() then result = this.getChild(1) else result = this.getChild(0) } } diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/Lambda.qll b/cpp/ql/lib/semmle/code/cpp/exprs/Lambda.qll index 8a51001f4d5..c885831c444 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/Lambda.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/Lambda.qll @@ -24,7 +24,7 @@ class LambdaExpression extends Expr, @lambdaexpr { /** * Gets an implicitly or explicitly captured value of this lambda expression. */ - LambdaCapture getACapture() { result = getCapture(_) } + LambdaCapture getACapture() { result = this.getCapture(_) } /** * Gets the nth implicitly or explicitly captured value of this lambda expression. @@ -58,13 +58,13 @@ class LambdaExpression extends Expr, @lambdaexpr { * - The return type. * - The statements comprising the lambda body. */ - Operator getLambdaFunction() { result = getType().(Closure).getLambdaFunction() } + Operator getLambdaFunction() { result = this.getType().(Closure).getLambdaFunction() } /** * Gets the initializer that initializes the captured variables in the closure, if any. * A lambda that does not capture any variables will not have an initializer. */ - ClassAggregateLiteral getInitializer() { result = getChild(0) } + ClassAggregateLiteral getInitializer() { result = this.getChild(0) } } /** @@ -103,7 +103,7 @@ class Closure extends Class { * ``` */ class LambdaCapture extends Locatable, @lambdacapture { - override string toString() { result = getField().getName() } + override string toString() { result = this.getField().getName() } override string getAPrimaryQlClass() { result = "LambdaCapture" } diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/Literal.qll b/cpp/ql/lib/semmle/code/cpp/exprs/Literal.qll index 31790f85bfb..18ca03740ac 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/Literal.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/Literal.qll @@ -60,12 +60,12 @@ class TextLiteral extends Literal { /** Gets a hex escape sequence that appears in the character or string literal (see [lex.ccon] in the C++ Standard). */ string getAHexEscapeSequence(int occurrence, int offset) { - result = getValueText().regexpFind("(?= 0 and - elementIndex < getArraySize() + elementIndex < this.getArraySize() } /** @@ -298,8 +298,8 @@ class ArrayOrVectorAggregateLiteral extends AggregateLiteral { */ bindingset[elementIndex] predicate isValueInitialized(int elementIndex) { - isInitialized(elementIndex) and - not exists(getElementExpr(elementIndex)) + this.isInitialized(elementIndex) and + not exists(this.getElementExpr(elementIndex)) } } diff --git a/cpp/ql/lib/semmle/code/cpp/internal/QualifiedName.qll b/cpp/ql/lib/semmle/code/cpp/internal/QualifiedName.qll index 692ce1fee19..7cf0c647142 100644 --- a/cpp/ql/lib/semmle/code/cpp/internal/QualifiedName.qll +++ b/cpp/ql/lib/semmle/code/cpp/internal/QualifiedName.qll @@ -173,7 +173,7 @@ class LocalVariable extends LocalScopeVariable, @localvariable { } class VariableDeclarationEntry extends @var_decl { string toString() { result = "QualifiedName DeclarationEntry" } - Variable getDeclaration() { result = getVariable() } + Variable getDeclaration() { result = this.getVariable() } /** * Gets the variable which is being declared or defined. diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll index b77bc6cebf0..ece55d181bf 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll @@ -4,7 +4,7 @@ private import semmle.code.cpp.ir.dataflow.DataFlow private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil private import semmle.code.cpp.ir.dataflow.DataFlow3 private import semmle.code.cpp.ir.IR -private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as Dispatch +private import semmle.code.cpp.ir.dataflow.ResolveCall private import semmle.code.cpp.controlflow.IRGuards private import semmle.code.cpp.models.interfaces.Taint private import semmle.code.cpp.models.interfaces.DataFlow @@ -355,20 +355,6 @@ predicate taintedIncludingGlobalVars(Expr source, Element tainted, string global */ GlobalOrNamespaceVariable globalVarFromId(string id) { id = result.getQualifiedName() } -/** - * Resolve potential target function(s) for `call`. - * - * If `call` is a call through a function pointer (`ExprCall`) or - * targets a virtual method, simple data flow analysis is performed - * in order to identify target(s). - */ -Function resolveCall(Call call) { - exists(CallInstruction callInstruction | - callInstruction.getAST() = call and - result = Dispatch::viableCallable(callInstruction) - ) -} - /** * Provides definitions for augmenting source/sink pairs with data-flow paths * between them. From a `@kind path-problem` query, import this module in the @@ -479,7 +465,7 @@ module TaintedWithPath { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/ResolveCall.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/ResolveCall.qll new file mode 100644 index 00000000000..f25386d3ba8 --- /dev/null +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/ResolveCall.qll @@ -0,0 +1,23 @@ +/** + * Provides a predicate for non-contextual virtual dispatch and function + * pointer resolution. + */ + +import cpp +private import semmle.code.cpp.ir.ValueNumbering +private import internal.DataFlowDispatch +private import semmle.code.cpp.ir.IR + +/** + * Resolve potential target function(s) for `call`. + * + * If `call` is a call through a function pointer (`ExprCall`) or its target is + * a virtual member function, simple data flow analysis is performed in order + * to identify the possible target(s). + */ +Function resolveCall(Call call) { + exists(CallInstruction callInstruction | + callInstruction.getAST() = call and + result = viableCallable(callInstruction) + ) +} diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll index f588a25a176..e11244c42b0 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll @@ -801,6 +801,9 @@ private module Cached { exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) } + cached + predicate allowParameterReturnInSelfCached(ParamNode p) { allowParameterReturnInSelf(p) } + cached newtype TCallContext = TAnyCallContext() or @@ -937,7 +940,7 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall { } override predicate relevantFor(DataFlowCallable callable) { - recordDataFlowCallSite(getCall(), callable) + recordDataFlowCallSite(this.getCall(), callable) } override predicate matchesCall(DataFlowCall call) { call = this.getCall() } @@ -1236,6 +1239,13 @@ class TypedContent extends MkTypedContent { /** Gets a textual representation of this content. */ string toString() { result = c.toString() } + + /** + * Holds if access paths with this `TypedContent` at their head always should + * be tracked at high precision. This disables adaptive access path precision + * for such access paths. + */ + predicate forceHighPrecision() { forceHighPrecision(c) } } /** @@ -1250,7 +1260,7 @@ abstract class AccessPathFront extends TAccessPathFront { TypedContent getHead() { this = TFrontHead(result) } - predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) } + predicate isClearedAt(Node n) { clearsContentCached(n, this.getHead().getContent()) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll index a55e65a81f6..dd64fc70039 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll @@ -175,6 +175,7 @@ module Consistency { query predicate postWithInFlow(Node n, string msg) { isPostUpdateNode(n) and + not clearsContent(n, _) and simpleLocalFlowStep(_, n) and msg = "PostUpdateNode should not be the target of local flow." } 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 73bf72a3643..3f215883df3 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 @@ -466,6 +466,12 @@ predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub impl int accessPathLimit() { result = 5 } +/** + * Holds if access paths with `c` at their head always should be tracked at high + * precision. This disables adaptive access path precision for such access paths. + */ +predicate forceHighPrecision(Content c) { none() } + /** The unit type. */ private newtype TUnit = TMkUnit() @@ -501,3 +507,12 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { no /** Extra data-flow steps needed for lambda flow analysis. */ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() } + +/** + * Holds if flow is allowed to pass from parameter `p` and back to itself as a + * side-effect, resulting in a summary from `p` to itself. + * + * One example would be to allow flow like `p.foo = p.bar;`, which is disallowed + * by default as a heuristic. + */ +predicate allowParameterReturnInSelf(ParameterNode p) { none() } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll index bf21249d4ca..bdcaa856daa 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll @@ -110,7 +110,7 @@ class Node extends TIRDataFlowNode { /** * Gets an upper bound on the type of this node. */ - IRType getTypeBound() { result = getType() } + IRType getTypeBound() { result = this.getType() } /** Gets the location of this element. */ Location getLocation() { none() } // overridden by subclasses @@ -120,7 +120,7 @@ class Node extends TIRDataFlowNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -831,7 +831,7 @@ class FieldContent extends Content, TFieldContent { FieldContent() { this = TFieldContent(c, startBit, endBit) } // Ensure that there's just 1 result for `toString`. - override string toString() { result = min(Field f | f = getAField() | f.toString()) } + override string toString() { result = min(Field f | f = this.getAField() | f.toString()) } predicate hasOffset(Class cl, int start, int end) { cl = c and start = startBit and end = endBit } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll index 2f4037d4ec8..c7e61ea2e33 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll @@ -38,5 +38,8 @@ Instruction callOutput(CallInstruction call, FunctionOutput output) { effect.getPrimaryInstruction() = call and output.isParameterDerefOrQualifierObject(effect.getIndex()) ) - // TODO: return value dereference + or + // TODO: modify this when we get return value dereferences + result = call and + output.isReturnValueDeref() } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll index f4f73b8247c..acb029c23d9 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration { predicate isSanitizer(DataFlow::Node node) { none() } final override predicate isBarrier(DataFlow::Node node) { - isSanitizer(node) or + this.isSanitizer(node) or defaultTaintSanitizer(node) } /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } - final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } + final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) } /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } - final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } + final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } - final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } + final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { + this.isSanitizerGuard(guard) + } /** * Holds if the additional taint propagation step from `node1` to `node2` @@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration { predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() } final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - isAdditionalTaintStep(node1, node2) or + this.isAdditionalTaintStep(node1, node2) or defaultAdditionalTaintStep(node1, node2) } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll index f4f73b8247c..acb029c23d9 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration { predicate isSanitizer(DataFlow::Node node) { none() } final override predicate isBarrier(DataFlow::Node node) { - isSanitizer(node) or + this.isSanitizer(node) or defaultTaintSanitizer(node) } /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } - final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } + final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) } /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } - final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } + final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } - final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } + final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { + this.isSanitizerGuard(guard) + } /** * Holds if the additional taint propagation step from `node1` to `node2` @@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration { predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() } final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - isAdditionalTaintStep(node1, node2) or + this.isAdditionalTaintStep(node1, node2) or defaultAdditionalTaintStep(node1, node2) } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll index f4f73b8247c..acb029c23d9 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll @@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration { predicate isSanitizer(DataFlow::Node node) { none() } final override predicate isBarrier(DataFlow::Node node) { - isSanitizer(node) or + this.isSanitizer(node) or defaultTaintSanitizer(node) } /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } - final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } + final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) } /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } - final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } + final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } - final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } + final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { + this.isSanitizerGuard(guard) + } /** * Holds if the additional taint propagation step from `node1` to `node2` @@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration { predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() } final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - isAdditionalTaintStep(node1, node2) or + this.isAdditionalTaintStep(node1, node2) or defaultAdditionalTaintStep(node1, node2) } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll index 4b86f9a7cec..bb8630a5e0c 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll @@ -24,7 +24,7 @@ class IRBlockBase extends TIRBlock { final string toString() { result = getFirstInstruction(this).toString() } /** Gets the source location of the first non-`Phi` instruction in this block. */ - final Language::Location getLocation() { result = getFirstInstruction().getLocation() } + final Language::Location getLocation() { result = this.getFirstInstruction().getLocation() } /** * INTERNAL: Do not use. @@ -39,7 +39,7 @@ class IRBlockBase extends TIRBlock { ) and this = rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 | - funcBlock.getEnclosingFunction() = getEnclosingFunction() and + funcBlock.getEnclosingFunction() = this.getEnclosingFunction() and funcBlock.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and // Ensure that the block containing `EnterFunction` always comes first. if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction @@ -59,15 +59,15 @@ class IRBlockBase extends TIRBlock { * Get the `Phi` instructions that appear at the start of this block. */ final PhiInstruction getAPhiInstruction() { - Construction::getPhiInstructionBlockStart(result) = getFirstInstruction() + Construction::getPhiInstructionBlockStart(result) = this.getFirstInstruction() } /** * Gets an instruction in this block. This includes `Phi` instructions. */ final Instruction getAnInstruction() { - result = getInstruction(_) or - result = getAPhiInstruction() + result = this.getInstruction(_) or + result = this.getAPhiInstruction() } /** @@ -78,7 +78,9 @@ class IRBlockBase extends TIRBlock { /** * Gets the last instruction in this block. */ - final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) } + final Instruction getLastInstruction() { + result = this.getInstruction(this.getInstructionCount() - 1) + } /** * Gets the number of non-`Phi` instructions in this block. @@ -149,7 +151,7 @@ class IRBlock extends IRBlockBase { * Block `A` dominates block `B` if any control flow path from the entry block of the function to * block `B` must pass through block `A`. A block always dominates itself. */ - final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } + final predicate dominates(IRBlock block) { this.strictlyDominates(block) or this = block } /** * Gets a block on the dominance frontier of this block. @@ -159,8 +161,8 @@ class IRBlock extends IRBlockBase { */ pragma[noinline] final IRBlock dominanceFrontier() { - dominates(result.getAPredecessor()) and - not strictlyDominates(result) + this.dominates(result.getAPredecessor()) and + not this.strictlyDominates(result) } /** @@ -189,7 +191,7 @@ class IRBlock extends IRBlockBase { * Block `A` post-dominates block `B` if any control flow path from `B` to the exit block of the * function must pass through block `A`. A block always post-dominates itself. */ - final predicate postDominates(IRBlock block) { strictlyPostDominates(block) or this = block } + final predicate postDominates(IRBlock block) { this.strictlyPostDominates(block) or this = block } /** * Gets a block on the post-dominance frontier of this block. @@ -199,16 +201,16 @@ class IRBlock extends IRBlockBase { */ pragma[noinline] final IRBlock postPominanceFrontier() { - postDominates(result.getASuccessor()) and - not strictlyPostDominates(result) + this.postDominates(result.getASuccessor()) and + not this.strictlyPostDominates(result) } /** * Holds if this block is reachable from the entry block of its function. */ final predicate isReachableFromFunctionEntry() { - this = getEnclosingIRFunction().getEntryBlock() or - getAPredecessor().isReachableFromFunctionEntry() + this = this.getEnclosingIRFunction().getEntryBlock() or + this.getAPredecessor().isReachableFromFunctionEntry() } } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index 2fb3edad602..88a973fc5a8 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -41,7 +41,7 @@ class Instruction extends Construction::TStageInstruction { } /** Gets a textual representation of this element. */ - final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } + final string toString() { result = this.getOpcode().toString() + ": " + this.getAST().toString() } /** * Gets a string showing the result, opcode, and operands of the instruction, equivalent to what @@ -50,7 +50,8 @@ class Instruction extends Construction::TStageInstruction { * `mu0_28(int) = Store r0_26, r0_27` */ final string getDumpString() { - result = getResultString() + " = " + getOperationString() + " " + getOperandsString() + result = + this.getResultString() + " = " + this.getOperationString() + " " + this.getOperandsString() } private predicate shouldGenerateDumpStrings() { @@ -66,10 +67,13 @@ class Instruction extends Construction::TStageInstruction { * VariableAddress[x] */ final string getOperationString() { - shouldGenerateDumpStrings() and - if exists(getImmediateString()) - then result = getOperationPrefix() + getOpcode().toString() + "[" + getImmediateString() + "]" - else result = getOperationPrefix() + getOpcode().toString() + this.shouldGenerateDumpStrings() and + if exists(this.getImmediateString()) + then + result = + this.getOperationPrefix() + this.getOpcode().toString() + "[" + this.getImmediateString() + + "]" + else result = this.getOperationPrefix() + this.getOpcode().toString() } /** @@ -78,17 +82,17 @@ class Instruction extends Construction::TStageInstruction { string getImmediateString() { none() } private string getOperationPrefix() { - shouldGenerateDumpStrings() and + this.shouldGenerateDumpStrings() and if this instanceof SideEffectInstruction then result = "^" else result = "" } private string getResultPrefix() { - shouldGenerateDumpStrings() and - if getResultIRType() instanceof IRVoidType + this.shouldGenerateDumpStrings() and + if this.getResultIRType() instanceof IRVoidType then result = "v" else - if hasMemoryResult() - then if isResultModeled() then result = "m" else result = "mu" + if this.hasMemoryResult() + then if this.isResultModeled() then result = "m" else result = "mu" else result = "r" } @@ -97,7 +101,7 @@ class Instruction extends Construction::TStageInstruction { * used by debugging and printing code only. */ int getDisplayIndexInBlock() { - shouldGenerateDumpStrings() and + this.shouldGenerateDumpStrings() and exists(IRBlock block | this = block.getInstruction(result) or @@ -111,12 +115,12 @@ class Instruction extends Construction::TStageInstruction { } private int getLineRank() { - shouldGenerateDumpStrings() and + this.shouldGenerateDumpStrings() and this = rank[result](Instruction instr | instr = - getAnInstructionAtLine(getEnclosingIRFunction(), getLocation().getFile(), - getLocation().getStartLine()) + getAnInstructionAtLine(this.getEnclosingIRFunction(), this.getLocation().getFile(), + this.getLocation().getStartLine()) | instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock() ) @@ -130,8 +134,9 @@ class Instruction extends Construction::TStageInstruction { * Example: `r1_1` */ string getResultId() { - shouldGenerateDumpStrings() and - result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank() + this.shouldGenerateDumpStrings() and + result = + this.getResultPrefix() + this.getAST().getLocation().getStartLine() + "_" + this.getLineRank() } /** @@ -142,8 +147,8 @@ class Instruction extends Construction::TStageInstruction { * Example: `r1_1(int*)` */ final string getResultString() { - shouldGenerateDumpStrings() and - result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")" + this.shouldGenerateDumpStrings() and + result = this.getResultId() + "(" + this.getResultLanguageType().getDumpString() + ")" } /** @@ -153,10 +158,10 @@ class Instruction extends Construction::TStageInstruction { * Example: `func:r3_4, this:r3_5` */ string getOperandsString() { - shouldGenerateDumpStrings() and + this.shouldGenerateDumpStrings() and result = concat(Operand operand | - operand = getAnOperand() + operand = this.getAnOperand() | operand.getDumpString(), ", " order by operand.getDumpSortOrder() ) @@ -190,7 +195,7 @@ class Instruction extends Construction::TStageInstruction { * Gets the function that contains this instruction. */ final Language::Function getEnclosingFunction() { - result = getEnclosingIRFunction().getFunction() + result = this.getEnclosingIRFunction().getFunction() } /** @@ -208,7 +213,7 @@ class Instruction extends Construction::TStageInstruction { /** * Gets the location of the source code for this instruction. */ - final Language::Location getLocation() { result = getAST().getLocation() } + final Language::Location getLocation() { result = this.getAST().getLocation() } /** * Gets the `Expr` whose result is computed by this instruction, if any. The `Expr` may be a @@ -243,7 +248,7 @@ class Instruction extends Construction::TStageInstruction { * a result, its result type will be `IRVoidType`. */ cached - final IRType getResultIRType() { result = getResultLanguageType().getIRType() } + final IRType getResultIRType() { result = this.getResultLanguageType().getIRType() } /** * Gets the type of the result produced by this instruction. If the @@ -254,7 +259,7 @@ class Instruction extends Construction::TStageInstruction { */ final Language::Type getResultType() { exists(Language::LanguageType resultType | - resultType = getResultLanguageType() and + resultType = this.getResultLanguageType() and ( resultType.hasUnspecifiedType(result, _) or @@ -283,7 +288,7 @@ class Instruction extends Construction::TStageInstruction { * result of the `Load` instruction is a prvalue of type `int`, representing * the integer value loaded from variable `x`. */ - final predicate isGLValue() { getResultLanguageType().hasType(_, true) } + final predicate isGLValue() { this.getResultLanguageType().hasType(_, true) } /** * Gets the size of the result produced by this instruction, in bytes. If the @@ -292,7 +297,7 @@ class Instruction extends Construction::TStageInstruction { * If `this.isGLValue()` holds for this instruction, the value of * `getResultSize()` will always be the size of a pointer. */ - final int getResultSize() { result = getResultLanguageType().getByteSize() } + final int getResultSize() { result = this.getResultLanguageType().getByteSize() } /** * Gets the opcode that specifies the operation performed by this instruction. @@ -314,14 +319,16 @@ class Instruction extends Construction::TStageInstruction { /** * Holds if this instruction produces a memory result. */ - final predicate hasMemoryResult() { exists(getResultMemoryAccess()) } + final predicate hasMemoryResult() { exists(this.getResultMemoryAccess()) } /** * Gets the kind of memory access performed by this instruction's result. * Holds only for instructions with a memory result. */ pragma[inline] - final MemoryAccessKind getResultMemoryAccess() { result = getOpcode().getWriteMemoryAccess() } + final MemoryAccessKind getResultMemoryAccess() { + result = this.getOpcode().getWriteMemoryAccess() + } /** * Holds if the memory access performed by this instruction's result will not always write to @@ -332,7 +339,7 @@ class Instruction extends Construction::TStageInstruction { * (for example, the global side effects of a function call). */ pragma[inline] - final predicate hasResultMayMemoryAccess() { getOpcode().hasMayWriteMemoryAccess() } + final predicate hasResultMayMemoryAccess() { this.getOpcode().hasMayWriteMemoryAccess() } /** * Gets the operand that holds the memory address to which this instruction stores its @@ -340,7 +347,7 @@ class Instruction extends Construction::TStageInstruction { * is `r1`. */ final AddressOperand getResultAddressOperand() { - getResultMemoryAccess().usesAddressOperand() and + this.getResultMemoryAccess().usesAddressOperand() and result.getUse() = this } @@ -349,7 +356,7 @@ class Instruction extends Construction::TStageInstruction { * result, if any. For example, in `m3 = Store r1, r2`, the result of `getResultAddressOperand()` * is the instruction that defines `r1`. */ - final Instruction getResultAddress() { result = getResultAddressOperand().getDef() } + final Instruction getResultAddress() { result = this.getResultAddressOperand().getDef() } /** * Holds if the result of this instruction is precisely modeled in SSA. Always @@ -368,7 +375,7 @@ class Instruction extends Construction::TStageInstruction { */ final predicate isResultModeled() { // Register results are always in SSA form. - not hasMemoryResult() or + not this.hasMemoryResult() or Construction::hasModeledMemoryResult(this) } @@ -412,7 +419,7 @@ class Instruction extends Construction::TStageInstruction { /** * Gets all direct successors of this instruction. */ - final Instruction getASuccessor() { result = getSuccessor(_) } + final Instruction getASuccessor() { result = this.getSuccessor(_) } /** * Gets a predecessor of this instruction such that the predecessor reaches @@ -423,7 +430,7 @@ class Instruction extends Construction::TStageInstruction { /** * Gets all direct predecessors of this instruction. */ - final Instruction getAPredecessor() { result = getPredecessor(_) } + final Instruction getAPredecessor() { result = this.getPredecessor(_) } } /** @@ -543,7 +550,7 @@ class IndexedInstruction extends Instruction { * at this instruction. This instruction has no predecessors. */ class EnterFunctionInstruction extends Instruction { - EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction } + EnterFunctionInstruction() { this.getOpcode() instanceof Opcode::EnterFunction } } /** @@ -554,7 +561,7 @@ class EnterFunctionInstruction extends Instruction { * struct, or union, see `FieldAddressInstruction`. */ class VariableAddressInstruction extends VariableInstruction { - VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } + VariableAddressInstruction() { this.getOpcode() instanceof Opcode::VariableAddress } } /** @@ -566,7 +573,7 @@ class VariableAddressInstruction extends VariableInstruction { * The result has an `IRFunctionAddress` type. */ class FunctionAddressInstruction extends FunctionInstruction { - FunctionAddressInstruction() { getOpcode() instanceof Opcode::FunctionAddress } + FunctionAddressInstruction() { this.getOpcode() instanceof Opcode::FunctionAddress } } /** @@ -577,7 +584,7 @@ class FunctionAddressInstruction extends FunctionInstruction { * initializes that parameter. */ class InitializeParameterInstruction extends VariableInstruction { - InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter } + InitializeParameterInstruction() { this.getOpcode() instanceof Opcode::InitializeParameter } /** * Gets the parameter initialized by this instruction. @@ -603,7 +610,7 @@ class InitializeParameterInstruction extends VariableInstruction { * initialized elsewhere, would not otherwise have a definition in this function. */ class InitializeNonLocalInstruction extends Instruction { - InitializeNonLocalInstruction() { getOpcode() instanceof Opcode::InitializeNonLocal } + InitializeNonLocalInstruction() { this.getOpcode() instanceof Opcode::InitializeNonLocal } } /** @@ -611,7 +618,7 @@ class InitializeNonLocalInstruction extends Instruction { * with the value of that memory on entry to the function. */ class InitializeIndirectionInstruction extends VariableInstruction { - InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection } + InitializeIndirectionInstruction() { this.getOpcode() instanceof Opcode::InitializeIndirection } /** * Gets the parameter initialized by this instruction. @@ -635,24 +642,24 @@ class InitializeIndirectionInstruction extends VariableInstruction { * An instruction that initializes the `this` pointer parameter of the enclosing function. */ class InitializeThisInstruction extends Instruction { - InitializeThisInstruction() { getOpcode() instanceof Opcode::InitializeThis } + InitializeThisInstruction() { this.getOpcode() instanceof Opcode::InitializeThis } } /** * An instruction that computes the address of a non-static field of an object. */ class FieldAddressInstruction extends FieldInstruction { - FieldAddressInstruction() { getOpcode() instanceof Opcode::FieldAddress } + FieldAddressInstruction() { this.getOpcode() instanceof Opcode::FieldAddress } /** * Gets the operand that provides the address of the object containing the field. */ - final UnaryOperand getObjectAddressOperand() { result = getAnOperand() } + final UnaryOperand getObjectAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the object containing the field. */ - final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } + final Instruction getObjectAddress() { result = this.getObjectAddressOperand().getDef() } } /** @@ -661,17 +668,19 @@ class FieldAddressInstruction extends FieldInstruction { * This instruction is used for element access to C# arrays. */ class ElementsAddressInstruction extends UnaryInstruction { - ElementsAddressInstruction() { getOpcode() instanceof Opcode::ElementsAddress } + ElementsAddressInstruction() { this.getOpcode() instanceof Opcode::ElementsAddress } /** * Gets the operand that provides the address of the array object. */ - final UnaryOperand getArrayObjectAddressOperand() { result = getAnOperand() } + final UnaryOperand getArrayObjectAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the array object. */ - final Instruction getArrayObjectAddress() { result = getArrayObjectAddressOperand().getDef() } + final Instruction getArrayObjectAddress() { + result = this.getArrayObjectAddressOperand().getDef() + } } /** @@ -685,7 +694,7 @@ class ElementsAddressInstruction extends UnaryInstruction { * taken may want to ignore any function that contains an `ErrorInstruction`. */ class ErrorInstruction extends Instruction { - ErrorInstruction() { getOpcode() instanceof Opcode::Error } + ErrorInstruction() { this.getOpcode() instanceof Opcode::Error } } /** @@ -695,7 +704,7 @@ class ErrorInstruction extends Instruction { * an initializer, or whose initializer only partially initializes the variable. */ class UninitializedInstruction extends VariableInstruction { - UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized } + UninitializedInstruction() { this.getOpcode() instanceof Opcode::Uninitialized } /** * Gets the variable that is uninitialized. @@ -710,7 +719,7 @@ class UninitializedInstruction extends VariableInstruction { * least one instruction, even when the AST has no semantic effect. */ class NoOpInstruction extends Instruction { - NoOpInstruction() { getOpcode() instanceof Opcode::NoOp } + NoOpInstruction() { this.getOpcode() instanceof Opcode::NoOp } } /** @@ -732,32 +741,32 @@ class NoOpInstruction extends Instruction { * `void`-returning function. */ class ReturnInstruction extends Instruction { - ReturnInstruction() { getOpcode() instanceof ReturnOpcode } + ReturnInstruction() { this.getOpcode() instanceof ReturnOpcode } } /** * An instruction that returns control to the caller of the function, without returning a value. */ class ReturnVoidInstruction extends ReturnInstruction { - ReturnVoidInstruction() { getOpcode() instanceof Opcode::ReturnVoid } + ReturnVoidInstruction() { this.getOpcode() instanceof Opcode::ReturnVoid } } /** * An instruction that returns control to the caller of the function, including a return value. */ class ReturnValueInstruction extends ReturnInstruction { - ReturnValueInstruction() { getOpcode() instanceof Opcode::ReturnValue } + ReturnValueInstruction() { this.getOpcode() instanceof Opcode::ReturnValue } /** * Gets the operand that provides the value being returned by the function. */ - final LoadOperand getReturnValueOperand() { result = getAnOperand() } + final LoadOperand getReturnValueOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the value being returned by the function, if an * exact definition is available. */ - final Instruction getReturnValue() { result = getReturnValueOperand().getDef() } + final Instruction getReturnValue() { result = this.getReturnValueOperand().getDef() } } /** @@ -770,28 +779,28 @@ class ReturnValueInstruction extends ReturnInstruction { * that the caller initialized the memory pointed to by the parameter before the call. */ class ReturnIndirectionInstruction extends VariableInstruction { - ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } + ReturnIndirectionInstruction() { this.getOpcode() instanceof Opcode::ReturnIndirection } /** * Gets the operand that provides the value of the pointed-to memory. */ - final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + final SideEffectOperand getSideEffectOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the value of the pointed-to memory, if an exact * definition is available. */ - final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + final Instruction getSideEffect() { result = this.getSideEffectOperand().getDef() } /** * Gets the operand that provides the address of the pointed-to memory. */ - final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + final AddressOperand getSourceAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the pointed-to memory. */ - final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } + final Instruction getSourceAddress() { result = this.getSourceAddressOperand().getDef() } /** * Gets the parameter for which this instruction reads the final pointed-to value within the @@ -821,12 +830,12 @@ class ReturnIndirectionInstruction extends VariableInstruction { * * There are several different copy instructions, depending on the source and destination of the * copy operation: - * - `CopyInstruction` - Copies a register operand to a register result. + * - `CopyValueInstruction` - Copies a register operand to a register result. * - `LoadInstruction` - Copies a memory operand to a register result. * - `StoreInstruction` - Copies a register operand to a memory result. */ class CopyInstruction extends Instruction { - CopyInstruction() { getOpcode() instanceof CopyOpcode } + CopyInstruction() { this.getOpcode() instanceof CopyOpcode } /** * Gets the operand that provides the input value of the copy. @@ -837,16 +846,16 @@ class CopyInstruction extends Instruction { * Gets the instruction whose result provides the input value of the copy, if an exact definition * is available. */ - final Instruction getSourceValue() { result = getSourceValueOperand().getDef() } + final Instruction getSourceValue() { result = this.getSourceValueOperand().getDef() } } /** * An instruction that returns a register result containing a copy of its register operand. */ class CopyValueInstruction extends CopyInstruction, UnaryInstruction { - CopyValueInstruction() { getOpcode() instanceof Opcode::CopyValue } + CopyValueInstruction() { this.getOpcode() instanceof Opcode::CopyValue } - final override UnaryOperand getSourceValueOperand() { result = getAnOperand() } + final override UnaryOperand getSourceValueOperand() { result = this.getAnOperand() } } /** @@ -863,47 +872,49 @@ private string getAddressOperandDescription(AddressOperand operand) { * An instruction that returns a register result containing a copy of its memory operand. */ class LoadInstruction extends CopyInstruction { - LoadInstruction() { getOpcode() instanceof Opcode::Load } + LoadInstruction() { this.getOpcode() instanceof Opcode::Load } final override string getImmediateString() { - result = getAddressOperandDescription(getSourceAddressOperand()) + result = getAddressOperandDescription(this.getSourceAddressOperand()) } /** * Gets the operand that provides the address of the value being loaded. */ - final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + final AddressOperand getSourceAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the value being loaded. */ - final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } + final Instruction getSourceAddress() { result = this.getSourceAddressOperand().getDef() } - final override LoadOperand getSourceValueOperand() { result = getAnOperand() } + final override LoadOperand getSourceValueOperand() { result = this.getAnOperand() } } /** * An instruction that returns a memory result containing a copy of its register operand. */ class StoreInstruction extends CopyInstruction { - StoreInstruction() { getOpcode() instanceof Opcode::Store } + StoreInstruction() { this.getOpcode() instanceof Opcode::Store } final override string getImmediateString() { - result = getAddressOperandDescription(getDestinationAddressOperand()) + result = getAddressOperandDescription(this.getDestinationAddressOperand()) } /** * Gets the operand that provides the address of the location to which the value will be stored. */ - final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + final AddressOperand getDestinationAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the location to which the value will * be stored, if an exact definition is available. */ - final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } + final Instruction getDestinationAddress() { + result = this.getDestinationAddressOperand().getDef() + } - final override StoreValueOperand getSourceValueOperand() { result = getAnOperand() } + final override StoreValueOperand getSourceValueOperand() { result = this.getAnOperand() } } /** @@ -911,27 +922,27 @@ class StoreInstruction extends CopyInstruction { * operand. */ class ConditionalBranchInstruction extends Instruction { - ConditionalBranchInstruction() { getOpcode() instanceof Opcode::ConditionalBranch } + ConditionalBranchInstruction() { this.getOpcode() instanceof Opcode::ConditionalBranch } /** * Gets the operand that provides the Boolean condition controlling the branch. */ - final ConditionOperand getConditionOperand() { result = getAnOperand() } + final ConditionOperand getConditionOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the Boolean condition controlling the branch. */ - final Instruction getCondition() { result = getConditionOperand().getDef() } + final Instruction getCondition() { result = this.getConditionOperand().getDef() } /** * Gets the instruction to which control will flow if the condition is true. */ - final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) } + final Instruction getTrueSuccessor() { result = this.getSuccessor(EdgeKind::trueEdge()) } /** * Gets the instruction to which control will flow if the condition is false. */ - final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) } + final Instruction getFalseSuccessor() { result = this.getSuccessor(EdgeKind::falseEdge()) } } /** @@ -943,14 +954,14 @@ class ConditionalBranchInstruction extends Instruction { * successors. */ class ExitFunctionInstruction extends Instruction { - ExitFunctionInstruction() { getOpcode() instanceof Opcode::ExitFunction } + ExitFunctionInstruction() { this.getOpcode() instanceof Opcode::ExitFunction } } /** * An instruction whose result is a constant value. */ class ConstantInstruction extends ConstantValueInstruction { - ConstantInstruction() { getOpcode() instanceof Opcode::Constant } + ConstantInstruction() { this.getOpcode() instanceof Opcode::Constant } } /** @@ -959,7 +970,7 @@ class ConstantInstruction extends ConstantValueInstruction { class IntegerConstantInstruction extends ConstantInstruction { IntegerConstantInstruction() { exists(IRType resultType | - resultType = getResultIRType() and + resultType = this.getResultIRType() and (resultType instanceof IRIntegerType or resultType instanceof IRBooleanType) ) } @@ -969,7 +980,7 @@ class IntegerConstantInstruction extends ConstantInstruction { * An instruction whose result is a constant value of floating-point type. */ class FloatConstantInstruction extends ConstantInstruction { - FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType } + FloatConstantInstruction() { this.getResultIRType() instanceof IRFloatingPointType } } /** @@ -978,7 +989,9 @@ class FloatConstantInstruction extends ConstantInstruction { class StringConstantInstruction extends VariableInstruction { override IRStringLiteral var; - final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) } + final override string getImmediateString() { + result = Language::getStringLiteralText(this.getValue()) + } /** * Gets the string literal whose address is returned by this instruction. @@ -990,37 +1003,37 @@ class StringConstantInstruction extends VariableInstruction { * An instruction whose result is computed from two operands. */ class BinaryInstruction extends Instruction { - BinaryInstruction() { getOpcode() instanceof BinaryOpcode } + BinaryInstruction() { this.getOpcode() instanceof BinaryOpcode } /** * Gets the left operand of this binary instruction. */ - final LeftOperand getLeftOperand() { result = getAnOperand() } + final LeftOperand getLeftOperand() { result = this.getAnOperand() } /** * Gets the right operand of this binary instruction. */ - final RightOperand getRightOperand() { result = getAnOperand() } + final RightOperand getRightOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the value of the left operand of this binary * instruction. */ - final Instruction getLeft() { result = getLeftOperand().getDef() } + final Instruction getLeft() { result = this.getLeftOperand().getDef() } /** * Gets the instruction whose result provides the value of the right operand of this binary * instruction. */ - final Instruction getRight() { result = getRightOperand().getDef() } + final Instruction getRight() { result = this.getRightOperand().getDef() } /** * Holds if this instruction's operands are `op1` and `op2`, in either order. */ final predicate hasOperands(Operand op1, Operand op2) { - op1 = getLeftOperand() and op2 = getRightOperand() + op1 = this.getLeftOperand() and op2 = this.getRightOperand() or - op1 = getRightOperand() and op2 = getLeftOperand() + op1 = this.getRightOperand() and op2 = this.getLeftOperand() } } @@ -1028,7 +1041,7 @@ class BinaryInstruction extends Instruction { * An instruction that computes the result of an arithmetic operation. */ class ArithmeticInstruction extends Instruction { - ArithmeticInstruction() { getOpcode() instanceof ArithmeticOpcode } + ArithmeticInstruction() { this.getOpcode() instanceof ArithmeticOpcode } } /** @@ -1050,7 +1063,7 @@ class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction * performed according to IEEE-754. */ class AddInstruction extends BinaryArithmeticInstruction { - AddInstruction() { getOpcode() instanceof Opcode::Add } + AddInstruction() { this.getOpcode() instanceof Opcode::Add } } /** @@ -1061,7 +1074,7 @@ class AddInstruction extends BinaryArithmeticInstruction { * according to IEEE-754. */ class SubInstruction extends BinaryArithmeticInstruction { - SubInstruction() { getOpcode() instanceof Opcode::Sub } + SubInstruction() { this.getOpcode() instanceof Opcode::Sub } } /** @@ -1072,7 +1085,7 @@ class SubInstruction extends BinaryArithmeticInstruction { * performed according to IEEE-754. */ class MulInstruction extends BinaryArithmeticInstruction { - MulInstruction() { getOpcode() instanceof Opcode::Mul } + MulInstruction() { this.getOpcode() instanceof Opcode::Mul } } /** @@ -1083,7 +1096,7 @@ class MulInstruction extends BinaryArithmeticInstruction { * to IEEE-754. */ class DivInstruction extends BinaryArithmeticInstruction { - DivInstruction() { getOpcode() instanceof Opcode::Div } + DivInstruction() { this.getOpcode() instanceof Opcode::Div } } /** @@ -1093,7 +1106,7 @@ class DivInstruction extends BinaryArithmeticInstruction { * division by zero or integer overflow is undefined. */ class RemInstruction extends BinaryArithmeticInstruction { - RemInstruction() { getOpcode() instanceof Opcode::Rem } + RemInstruction() { this.getOpcode() instanceof Opcode::Rem } } /** @@ -1104,14 +1117,14 @@ class RemInstruction extends BinaryArithmeticInstruction { * is performed according to IEEE-754. */ class NegateInstruction extends UnaryArithmeticInstruction { - NegateInstruction() { getOpcode() instanceof Opcode::Negate } + NegateInstruction() { this.getOpcode() instanceof Opcode::Negate } } /** * An instruction that computes the result of a bitwise operation. */ class BitwiseInstruction extends Instruction { - BitwiseInstruction() { getOpcode() instanceof BitwiseOpcode } + BitwiseInstruction() { this.getOpcode() instanceof BitwiseOpcode } } /** @@ -1130,7 +1143,7 @@ class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } * Both operands must have the same integer type, which will also be the result type. */ class BitAndInstruction extends BinaryBitwiseInstruction { - BitAndInstruction() { getOpcode() instanceof Opcode::BitAnd } + BitAndInstruction() { this.getOpcode() instanceof Opcode::BitAnd } } /** @@ -1139,7 +1152,7 @@ class BitAndInstruction extends BinaryBitwiseInstruction { * Both operands must have the same integer type, which will also be the result type. */ class BitOrInstruction extends BinaryBitwiseInstruction { - BitOrInstruction() { getOpcode() instanceof Opcode::BitOr } + BitOrInstruction() { this.getOpcode() instanceof Opcode::BitOr } } /** @@ -1148,7 +1161,7 @@ class BitOrInstruction extends BinaryBitwiseInstruction { * Both operands must have the same integer type, which will also be the result type. */ class BitXorInstruction extends BinaryBitwiseInstruction { - BitXorInstruction() { getOpcode() instanceof Opcode::BitXor } + BitXorInstruction() { this.getOpcode() instanceof Opcode::BitXor } } /** @@ -1159,7 +1172,7 @@ class BitXorInstruction extends BinaryBitwiseInstruction { * rightmost bits are zero-filled. */ class ShiftLeftInstruction extends BinaryBitwiseInstruction { - ShiftLeftInstruction() { getOpcode() instanceof Opcode::ShiftLeft } + ShiftLeftInstruction() { this.getOpcode() instanceof Opcode::ShiftLeft } } /** @@ -1172,7 +1185,7 @@ class ShiftLeftInstruction extends BinaryBitwiseInstruction { * of the left operand. */ class ShiftRightInstruction extends BinaryBitwiseInstruction { - ShiftRightInstruction() { getOpcode() instanceof Opcode::ShiftRight } + ShiftRightInstruction() { this.getOpcode() instanceof Opcode::ShiftRight } } /** @@ -1183,7 +1196,7 @@ class PointerArithmeticInstruction extends BinaryInstruction { int elementSize; PointerArithmeticInstruction() { - getOpcode() instanceof PointerArithmeticOpcode and + this.getOpcode() instanceof PointerArithmeticOpcode and elementSize = Raw::getInstructionElementSize(this) } @@ -1206,7 +1219,7 @@ class PointerArithmeticInstruction extends BinaryInstruction { * An instruction that adds or subtracts an integer offset from a pointer. */ class PointerOffsetInstruction extends PointerArithmeticInstruction { - PointerOffsetInstruction() { getOpcode() instanceof PointerOffsetOpcode } + PointerOffsetInstruction() { this.getOpcode() instanceof PointerOffsetOpcode } } /** @@ -1217,7 +1230,7 @@ class PointerOffsetInstruction extends PointerArithmeticInstruction { * overflow is undefined. */ class PointerAddInstruction extends PointerOffsetInstruction { - PointerAddInstruction() { getOpcode() instanceof Opcode::PointerAdd } + PointerAddInstruction() { this.getOpcode() instanceof Opcode::PointerAdd } } /** @@ -1228,7 +1241,7 @@ class PointerAddInstruction extends PointerOffsetInstruction { * pointer underflow is undefined. */ class PointerSubInstruction extends PointerOffsetInstruction { - PointerSubInstruction() { getOpcode() instanceof Opcode::PointerSub } + PointerSubInstruction() { this.getOpcode() instanceof Opcode::PointerSub } } /** @@ -1241,31 +1254,31 @@ class PointerSubInstruction extends PointerOffsetInstruction { * undefined. */ class PointerDiffInstruction extends PointerArithmeticInstruction { - PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } + PointerDiffInstruction() { this.getOpcode() instanceof Opcode::PointerDiff } } /** * An instruction whose result is computed from a single operand. */ class UnaryInstruction extends Instruction { - UnaryInstruction() { getOpcode() instanceof UnaryOpcode } + UnaryInstruction() { this.getOpcode() instanceof UnaryOpcode } /** * Gets the sole operand of this instruction. */ - final UnaryOperand getUnaryOperand() { result = getAnOperand() } + final UnaryOperand getUnaryOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the sole operand of this instruction. */ - final Instruction getUnary() { result = getUnaryOperand().getDef() } + final Instruction getUnary() { result = this.getUnaryOperand().getDef() } } /** * An instruction that converts the value of its operand to a value of a different type. */ class ConvertInstruction extends UnaryInstruction { - ConvertInstruction() { getOpcode() instanceof Opcode::Convert } + ConvertInstruction() { this.getOpcode() instanceof Opcode::Convert } } /** @@ -1279,7 +1292,7 @@ class ConvertInstruction extends UnaryInstruction { * `as` expression. */ class CheckedConvertOrNullInstruction extends UnaryInstruction { - CheckedConvertOrNullInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrNull } + CheckedConvertOrNullInstruction() { this.getOpcode() instanceof Opcode::CheckedConvertOrNull } } /** @@ -1293,7 +1306,7 @@ class CheckedConvertOrNullInstruction extends UnaryInstruction { * expression. */ class CheckedConvertOrThrowInstruction extends UnaryInstruction { - CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow } + CheckedConvertOrThrowInstruction() { this.getOpcode() instanceof Opcode::CheckedConvertOrThrow } } /** @@ -1306,7 +1319,7 @@ class CheckedConvertOrThrowInstruction extends UnaryInstruction { * the most-derived object. */ class CompleteObjectAddressInstruction extends UnaryInstruction { - CompleteObjectAddressInstruction() { getOpcode() instanceof Opcode::CompleteObjectAddress } + CompleteObjectAddressInstruction() { this.getOpcode() instanceof Opcode::CompleteObjectAddress } } /** @@ -1351,7 +1364,7 @@ class InheritanceConversionInstruction extends UnaryInstruction { * An instruction that converts from the address of a derived class to the address of a base class. */ class ConvertToBaseInstruction extends InheritanceConversionInstruction { - ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode } + ConvertToBaseInstruction() { this.getOpcode() instanceof ConvertToBaseOpcode } } /** @@ -1361,7 +1374,9 @@ class ConvertToBaseInstruction extends InheritanceConversionInstruction { * If the operand holds a null address, the result is a null address. */ class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { - ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase } + ConvertToNonVirtualBaseInstruction() { + this.getOpcode() instanceof Opcode::ConvertToNonVirtualBase + } } /** @@ -1371,7 +1386,7 @@ class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { * If the operand holds a null address, the result is a null address. */ class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { - ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase } + ConvertToVirtualBaseInstruction() { this.getOpcode() instanceof Opcode::ConvertToVirtualBase } } /** @@ -1381,7 +1396,7 @@ class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { * If the operand holds a null address, the result is a null address. */ class ConvertToDerivedInstruction extends InheritanceConversionInstruction { - ConvertToDerivedInstruction() { getOpcode() instanceof Opcode::ConvertToDerived } + ConvertToDerivedInstruction() { this.getOpcode() instanceof Opcode::ConvertToDerived } } /** @@ -1390,7 +1405,7 @@ class ConvertToDerivedInstruction extends InheritanceConversionInstruction { * The operand must have an integer type, which will also be the result type. */ class BitComplementInstruction extends UnaryBitwiseInstruction { - BitComplementInstruction() { getOpcode() instanceof Opcode::BitComplement } + BitComplementInstruction() { this.getOpcode() instanceof Opcode::BitComplement } } /** @@ -1399,14 +1414,14 @@ class BitComplementInstruction extends UnaryBitwiseInstruction { * The operand must have a Boolean type, which will also be the result type. */ class LogicalNotInstruction extends UnaryInstruction { - LogicalNotInstruction() { getOpcode() instanceof Opcode::LogicalNot } + LogicalNotInstruction() { this.getOpcode() instanceof Opcode::LogicalNot } } /** * An instruction that compares two numeric operands. */ class CompareInstruction extends BinaryInstruction { - CompareInstruction() { getOpcode() instanceof CompareOpcode } + CompareInstruction() { this.getOpcode() instanceof CompareOpcode } } /** @@ -1417,7 +1432,7 @@ class CompareInstruction extends BinaryInstruction { * unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareEQInstruction extends CompareInstruction { - CompareEQInstruction() { getOpcode() instanceof Opcode::CompareEQ } + CompareEQInstruction() { this.getOpcode() instanceof Opcode::CompareEQ } } /** @@ -1428,14 +1443,14 @@ class CompareEQInstruction extends CompareInstruction { * `left == right`. Floating-point comparison is performed according to IEEE-754. */ class CompareNEInstruction extends CompareInstruction { - CompareNEInstruction() { getOpcode() instanceof Opcode::CompareNE } + CompareNEInstruction() { this.getOpcode() instanceof Opcode::CompareNE } } /** * An instruction that does a relative comparison of two values, such as `<` or `>=`. */ class RelationalInstruction extends CompareInstruction { - RelationalInstruction() { getOpcode() instanceof RelationalOpcode } + RelationalInstruction() { this.getOpcode() instanceof RelationalOpcode } /** * Gets the operand on the "greater" (or "greater-or-equal") side @@ -1467,11 +1482,11 @@ class RelationalInstruction extends CompareInstruction { * are unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareLTInstruction extends RelationalInstruction { - CompareLTInstruction() { getOpcode() instanceof Opcode::CompareLT } + CompareLTInstruction() { this.getOpcode() instanceof Opcode::CompareLT } - override Instruction getLesser() { result = getLeft() } + override Instruction getLesser() { result = this.getLeft() } - override Instruction getGreater() { result = getRight() } + override Instruction getGreater() { result = this.getRight() } override predicate isStrict() { any() } } @@ -1484,11 +1499,11 @@ class CompareLTInstruction extends RelationalInstruction { * are unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareGTInstruction extends RelationalInstruction { - CompareGTInstruction() { getOpcode() instanceof Opcode::CompareGT } + CompareGTInstruction() { this.getOpcode() instanceof Opcode::CompareGT } - override Instruction getLesser() { result = getRight() } + override Instruction getLesser() { result = this.getRight() } - override Instruction getGreater() { result = getLeft() } + override Instruction getGreater() { result = this.getLeft() } override predicate isStrict() { any() } } @@ -1502,11 +1517,11 @@ class CompareGTInstruction extends RelationalInstruction { * are unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareLEInstruction extends RelationalInstruction { - CompareLEInstruction() { getOpcode() instanceof Opcode::CompareLE } + CompareLEInstruction() { this.getOpcode() instanceof Opcode::CompareLE } - override Instruction getLesser() { result = getLeft() } + override Instruction getLesser() { result = this.getLeft() } - override Instruction getGreater() { result = getRight() } + override Instruction getGreater() { result = this.getRight() } override predicate isStrict() { none() } } @@ -1520,11 +1535,11 @@ class CompareLEInstruction extends RelationalInstruction { * are unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareGEInstruction extends RelationalInstruction { - CompareGEInstruction() { getOpcode() instanceof Opcode::CompareGE } + CompareGEInstruction() { this.getOpcode() instanceof Opcode::CompareGE } - override Instruction getLesser() { result = getRight() } + override Instruction getLesser() { result = this.getRight() } - override Instruction getGreater() { result = getLeft() } + override Instruction getGreater() { result = this.getLeft() } override predicate isStrict() { none() } } @@ -1543,78 +1558,78 @@ class CompareGEInstruction extends RelationalInstruction { * of any case edge. */ class SwitchInstruction extends Instruction { - SwitchInstruction() { getOpcode() instanceof Opcode::Switch } + SwitchInstruction() { this.getOpcode() instanceof Opcode::Switch } /** Gets the operand that provides the integer value controlling the switch. */ - final ConditionOperand getExpressionOperand() { result = getAnOperand() } + final ConditionOperand getExpressionOperand() { result = this.getAnOperand() } /** Gets the instruction whose result provides the integer value controlling the switch. */ - final Instruction getExpression() { result = getExpressionOperand().getDef() } + final Instruction getExpression() { result = this.getExpressionOperand().getDef() } /** Gets the successor instructions along the case edges of the switch. */ - final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) } + final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = this.getSuccessor(edge)) } /** Gets the successor instruction along the default edge of the switch, if any. */ - final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) } + final Instruction getDefaultSuccessor() { result = this.getSuccessor(EdgeKind::defaultEdge()) } } /** * An instruction that calls a function. */ class CallInstruction extends Instruction { - CallInstruction() { getOpcode() instanceof Opcode::Call } + CallInstruction() { this.getOpcode() instanceof Opcode::Call } final override string getImmediateString() { - result = getStaticCallTarget().toString() + result = this.getStaticCallTarget().toString() or - not exists(getStaticCallTarget()) and result = "?" + not exists(this.getStaticCallTarget()) and result = "?" } /** * Gets the operand the specifies the target function of the call. */ - final CallTargetOperand getCallTargetOperand() { result = getAnOperand() } + final CallTargetOperand getCallTargetOperand() { result = this.getAnOperand() } /** * Gets the `Instruction` that computes the target function of the call. This is usually a * `FunctionAddress` instruction, but can also be an arbitrary instruction that produces a * function pointer. */ - final Instruction getCallTarget() { result = getCallTargetOperand().getDef() } + final Instruction getCallTarget() { result = this.getCallTargetOperand().getDef() } /** * Gets all of the argument operands of the call, including the `this` pointer, if any. */ - final ArgumentOperand getAnArgumentOperand() { result = getAnOperand() } + final ArgumentOperand getAnArgumentOperand() { result = this.getAnOperand() } /** * Gets the `Function` that the call targets, if this is statically known. */ final Language::Function getStaticCallTarget() { - result = getCallTarget().(FunctionAddressInstruction).getFunctionSymbol() + result = this.getCallTarget().(FunctionAddressInstruction).getFunctionSymbol() } /** * Gets all of the arguments of the call, including the `this` pointer, if any. */ - final Instruction getAnArgument() { result = getAnArgumentOperand().getDef() } + final Instruction getAnArgument() { result = this.getAnArgumentOperand().getDef() } /** * Gets the `this` pointer argument operand of the call, if any. */ - final ThisArgumentOperand getThisArgumentOperand() { result = getAnOperand() } + final ThisArgumentOperand getThisArgumentOperand() { result = this.getAnOperand() } /** * Gets the `this` pointer argument of the call, if any. */ - final Instruction getThisArgument() { result = getThisArgumentOperand().getDef() } + final Instruction getThisArgument() { result = this.getThisArgumentOperand().getDef() } /** * Gets the argument operand at the specified index. */ pragma[noinline] final PositionalArgumentOperand getPositionalArgumentOperand(int index) { - result = getAnOperand() and + result = this.getAnOperand() and result.getIndex() = index } @@ -1623,7 +1638,7 @@ class CallInstruction extends Instruction { */ pragma[noinline] final Instruction getPositionalArgument(int index) { - result = getPositionalArgumentOperand(index).getDef() + result = this.getPositionalArgumentOperand(index).getDef() } /** @@ -1631,16 +1646,16 @@ class CallInstruction extends Instruction { */ pragma[noinline] final ArgumentOperand getArgumentOperand(int index) { - index >= 0 and result = getPositionalArgumentOperand(index) + index >= 0 and result = this.getPositionalArgumentOperand(index) or - index = -1 and result = getThisArgumentOperand() + index = -1 and result = this.getThisArgumentOperand() } /** * Gets the argument at the specified index, or `this` if `index` is `-1`. */ pragma[noinline] - final Instruction getArgument(int index) { result = getArgumentOperand(index).getDef() } + final Instruction getArgument(int index) { result = this.getArgumentOperand(index).getDef() } /** * Gets the number of arguments of the call, including the `this` pointer, if any. @@ -1665,7 +1680,7 @@ class CallInstruction extends Instruction { * An instruction representing a side effect of a function call. */ class SideEffectInstruction extends Instruction { - SideEffectInstruction() { getOpcode() instanceof SideEffectOpcode } + SideEffectInstruction() { this.getOpcode() instanceof SideEffectOpcode } /** * Gets the instruction whose execution causes this side effect. @@ -1680,7 +1695,7 @@ class SideEffectInstruction extends Instruction { * accessed by that call. */ class CallSideEffectInstruction extends SideEffectInstruction { - CallSideEffectInstruction() { getOpcode() instanceof Opcode::CallSideEffect } + CallSideEffectInstruction() { this.getOpcode() instanceof Opcode::CallSideEffect } } /** @@ -1691,7 +1706,7 @@ class CallSideEffectInstruction extends SideEffectInstruction { * call target cannot write to escaped memory. */ class CallReadSideEffectInstruction extends SideEffectInstruction { - CallReadSideEffectInstruction() { getOpcode() instanceof Opcode::CallReadSideEffect } + CallReadSideEffectInstruction() { this.getOpcode() instanceof Opcode::CallReadSideEffect } } /** @@ -1699,33 +1714,33 @@ class CallReadSideEffectInstruction extends SideEffectInstruction { * specific parameter. */ class ReadSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { - ReadSideEffectInstruction() { getOpcode() instanceof ReadSideEffectOpcode } + ReadSideEffectInstruction() { this.getOpcode() instanceof ReadSideEffectOpcode } /** Gets the operand for the value that will be read from this instruction, if known. */ - final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + final SideEffectOperand getSideEffectOperand() { result = this.getAnOperand() } /** Gets the value that will be read from this instruction, if known. */ - final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + final Instruction getSideEffect() { result = this.getSideEffectOperand().getDef() } /** Gets the operand for the address from which this instruction may read. */ - final AddressOperand getArgumentOperand() { result = getAnOperand() } + final AddressOperand getArgumentOperand() { result = this.getAnOperand() } /** Gets the address from which this instruction may read. */ - final Instruction getArgumentDef() { result = getArgumentOperand().getDef() } + final Instruction getArgumentDef() { result = this.getArgumentOperand().getDef() } } /** * An instruction representing the read of an indirect parameter within a function call. */ class IndirectReadSideEffectInstruction extends ReadSideEffectInstruction { - IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect } + IndirectReadSideEffectInstruction() { this.getOpcode() instanceof Opcode::IndirectReadSideEffect } } /** * An instruction representing the read of an indirect buffer parameter within a function call. */ class BufferReadSideEffectInstruction extends ReadSideEffectInstruction { - BufferReadSideEffectInstruction() { getOpcode() instanceof Opcode::BufferReadSideEffect } + BufferReadSideEffectInstruction() { this.getOpcode() instanceof Opcode::BufferReadSideEffect } } /** @@ -1733,18 +1748,18 @@ class BufferReadSideEffectInstruction extends ReadSideEffectInstruction { */ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { SizedBufferReadSideEffectInstruction() { - getOpcode() instanceof Opcode::SizedBufferReadSideEffect + this.getOpcode() instanceof Opcode::SizedBufferReadSideEffect } /** * Gets the operand that holds the number of bytes read from the buffer. */ - final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + final BufferSizeOperand getBufferSizeOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the number of bytes read from the buffer. */ - final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } + final Instruction getBufferSize() { result = this.getBufferSizeOperand().getDef() } } /** @@ -1752,17 +1767,17 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { * specific parameter. */ class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { - WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode } + WriteSideEffectInstruction() { this.getOpcode() instanceof WriteSideEffectOpcode } /** * Get the operand that holds the address of the memory to be written. */ - final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + final AddressOperand getDestinationAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the memory to be written. */ - Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } + Instruction getDestinationAddress() { result = this.getDestinationAddressOperand().getDef() } } /** @@ -1770,7 +1785,7 @@ class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstructi */ class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction { IndirectMustWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::IndirectMustWriteSideEffect + this.getOpcode() instanceof Opcode::IndirectMustWriteSideEffect } } @@ -1780,7 +1795,7 @@ class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction */ class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { BufferMustWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::BufferMustWriteSideEffect + this.getOpcode() instanceof Opcode::BufferMustWriteSideEffect } } @@ -1790,18 +1805,18 @@ class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { */ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { SizedBufferMustWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect + this.getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect } /** * Gets the operand that holds the number of bytes written to the buffer. */ - final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + final BufferSizeOperand getBufferSizeOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the number of bytes written to the buffer. */ - final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } + final Instruction getBufferSize() { result = this.getBufferSizeOperand().getDef() } } /** @@ -1812,7 +1827,7 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi */ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { IndirectMayWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::IndirectMayWriteSideEffect + this.getOpcode() instanceof Opcode::IndirectMayWriteSideEffect } } @@ -1822,7 +1837,9 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. */ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { - BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect } + BufferMayWriteSideEffectInstruction() { + this.getOpcode() instanceof Opcode::BufferMayWriteSideEffect + } } /** @@ -1832,18 +1849,18 @@ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { */ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { SizedBufferMayWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect + this.getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect } /** * Gets the operand that holds the number of bytes written to the buffer. */ - final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + final BufferSizeOperand getBufferSizeOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the number of bytes written to the buffer. */ - final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } + final Instruction getBufferSize() { result = this.getBufferSizeOperand().getDef() } } /** @@ -1852,80 +1869,80 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio */ class InitializeDynamicAllocationInstruction extends SideEffectInstruction { InitializeDynamicAllocationInstruction() { - getOpcode() instanceof Opcode::InitializeDynamicAllocation + this.getOpcode() instanceof Opcode::InitializeDynamicAllocation } /** * Gets the operand that represents the address of the allocation this instruction is initializing. */ - final AddressOperand getAllocationAddressOperand() { result = getAnOperand() } + final AddressOperand getAllocationAddressOperand() { result = this.getAnOperand() } /** * Gets the address for the allocation this instruction is initializing. */ - final Instruction getAllocationAddress() { result = getAllocationAddressOperand().getDef() } + final Instruction getAllocationAddress() { result = this.getAllocationAddressOperand().getDef() } } /** * An instruction representing a GNU or MSVC inline assembly statement. */ class InlineAsmInstruction extends Instruction { - InlineAsmInstruction() { getOpcode() instanceof Opcode::InlineAsm } + InlineAsmInstruction() { this.getOpcode() instanceof Opcode::InlineAsm } } /** * An instruction that throws an exception. */ class ThrowInstruction extends Instruction { - ThrowInstruction() { getOpcode() instanceof ThrowOpcode } + ThrowInstruction() { this.getOpcode() instanceof ThrowOpcode } } /** * An instruction that throws a new exception. */ class ThrowValueInstruction extends ThrowInstruction { - ThrowValueInstruction() { getOpcode() instanceof Opcode::ThrowValue } + ThrowValueInstruction() { this.getOpcode() instanceof Opcode::ThrowValue } /** * Gets the address operand of the exception thrown by this instruction. */ - final AddressOperand getExceptionAddressOperand() { result = getAnOperand() } + final AddressOperand getExceptionAddressOperand() { result = this.getAnOperand() } /** * Gets the address of the exception thrown by this instruction. */ - final Instruction getExceptionAddress() { result = getExceptionAddressOperand().getDef() } + final Instruction getExceptionAddress() { result = this.getExceptionAddressOperand().getDef() } /** * Gets the operand for the exception thrown by this instruction. */ - final LoadOperand getExceptionOperand() { result = getAnOperand() } + final LoadOperand getExceptionOperand() { result = this.getAnOperand() } /** * Gets the exception thrown by this instruction. */ - final Instruction getException() { result = getExceptionOperand().getDef() } + final Instruction getException() { result = this.getExceptionOperand().getDef() } } /** * An instruction that re-throws the current exception. */ class ReThrowInstruction extends ThrowInstruction { - ReThrowInstruction() { getOpcode() instanceof Opcode::ReThrow } + ReThrowInstruction() { this.getOpcode() instanceof Opcode::ReThrow } } /** * An instruction that exits the current function by propagating an exception. */ class UnwindInstruction extends Instruction { - UnwindInstruction() { getOpcode() instanceof Opcode::Unwind } + UnwindInstruction() { this.getOpcode() instanceof Opcode::Unwind } } /** * An instruction that starts a `catch` handler. */ class CatchInstruction extends Instruction { - CatchInstruction() { getOpcode() instanceof CatchOpcode } + CatchInstruction() { this.getOpcode() instanceof CatchOpcode } } /** @@ -1935,7 +1952,7 @@ class CatchByTypeInstruction extends CatchInstruction { Language::LanguageType exceptionType; CatchByTypeInstruction() { - getOpcode() instanceof Opcode::CatchByType and + this.getOpcode() instanceof Opcode::CatchByType and exceptionType = Raw::getInstructionExceptionType(this) } @@ -1951,21 +1968,21 @@ class CatchByTypeInstruction extends CatchInstruction { * An instruction that catches any exception. */ class CatchAnyInstruction extends CatchInstruction { - CatchAnyInstruction() { getOpcode() instanceof Opcode::CatchAny } + CatchAnyInstruction() { this.getOpcode() instanceof Opcode::CatchAny } } /** * An instruction that initializes all escaped memory. */ class AliasedDefinitionInstruction extends Instruction { - AliasedDefinitionInstruction() { getOpcode() instanceof Opcode::AliasedDefinition } + AliasedDefinitionInstruction() { this.getOpcode() instanceof Opcode::AliasedDefinition } } /** * An instruction that consumes all escaped memory on exit from the function. */ class AliasedUseInstruction extends Instruction { - AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse } + AliasedUseInstruction() { this.getOpcode() instanceof Opcode::AliasedUse } } /** @@ -1979,7 +1996,7 @@ class AliasedUseInstruction extends Instruction { * runtime. */ class PhiInstruction extends Instruction { - PhiInstruction() { getOpcode() instanceof Opcode::Phi } + PhiInstruction() { this.getOpcode() instanceof Opcode::Phi } /** * Gets all of the instruction's `PhiInputOperand`s, representing the values that flow from each predecessor block. @@ -2047,29 +2064,29 @@ class PhiInstruction extends Instruction { * https://link.springer.com/content/pdf/10.1007%2F3-540-61053-7_66.pdf. */ class ChiInstruction extends Instruction { - ChiInstruction() { getOpcode() instanceof Opcode::Chi } + ChiInstruction() { this.getOpcode() instanceof Opcode::Chi } /** * Gets the operand that represents the previous state of all memory that might be aliased by the * memory write. */ - final ChiTotalOperand getTotalOperand() { result = getAnOperand() } + final ChiTotalOperand getTotalOperand() { result = this.getAnOperand() } /** * Gets the operand that represents the previous state of all memory that might be aliased by the * memory write. */ - final Instruction getTotal() { result = getTotalOperand().getDef() } + final Instruction getTotal() { result = this.getTotalOperand().getDef() } /** * Gets the operand that represents the new value written by the memory write. */ - final ChiPartialOperand getPartialOperand() { result = getAnOperand() } + final ChiPartialOperand getPartialOperand() { result = this.getAnOperand() } /** * Gets the operand that represents the new value written by the memory write. */ - final Instruction getPartial() { result = getPartialOperand().getDef() } + final Instruction getPartial() { result = this.getPartialOperand().getDef() } /** * Gets the bit range `[startBit, endBit)` updated by the partial operand of this `ChiInstruction`, relative to the start address of the total operand. @@ -2093,7 +2110,7 @@ class ChiInstruction extends Instruction { * or `Switch` instruction where that particular edge is infeasible. */ class UnreachedInstruction extends Instruction { - UnreachedInstruction() { getOpcode() instanceof Opcode::Unreached } + UnreachedInstruction() { this.getOpcode() instanceof Opcode::Unreached } } /** @@ -2106,7 +2123,7 @@ class BuiltInOperationInstruction extends Instruction { Language::BuiltInOperation operation; BuiltInOperationInstruction() { - getOpcode() instanceof BuiltInOperationOpcode and + this.getOpcode() instanceof BuiltInOperationOpcode and operation = Raw::getInstructionBuiltInOperation(this) } @@ -2122,9 +2139,9 @@ class BuiltInOperationInstruction extends Instruction { * actual operation is specified by the `getBuiltInOperation()` predicate. */ class BuiltInInstruction extends BuiltInOperationInstruction { - BuiltInInstruction() { getOpcode() instanceof Opcode::BuiltIn } + BuiltInInstruction() { this.getOpcode() instanceof Opcode::BuiltIn } - final override string getImmediateString() { result = getBuiltInOperation().toString() } + final override string getImmediateString() { result = this.getBuiltInOperation().toString() } } /** @@ -2135,7 +2152,7 @@ class BuiltInInstruction extends BuiltInOperationInstruction { * to the `...` parameter. */ class VarArgsStartInstruction extends UnaryInstruction { - VarArgsStartInstruction() { getOpcode() instanceof Opcode::VarArgsStart } + VarArgsStartInstruction() { this.getOpcode() instanceof Opcode::VarArgsStart } } /** @@ -2145,7 +2162,7 @@ class VarArgsStartInstruction extends UnaryInstruction { * a result. */ class VarArgsEndInstruction extends UnaryInstruction { - VarArgsEndInstruction() { getOpcode() instanceof Opcode::VarArgsEnd } + VarArgsEndInstruction() { this.getOpcode() instanceof Opcode::VarArgsEnd } } /** @@ -2155,7 +2172,7 @@ class VarArgsEndInstruction extends UnaryInstruction { * argument. */ class VarArgInstruction extends UnaryInstruction { - VarArgInstruction() { getOpcode() instanceof Opcode::VarArg } + VarArgInstruction() { this.getOpcode() instanceof Opcode::VarArg } } /** @@ -2166,7 +2183,7 @@ class VarArgInstruction extends UnaryInstruction { * argument of the `...` parameter. */ class NextVarArgInstruction extends UnaryInstruction { - NextVarArgInstruction() { getOpcode() instanceof Opcode::NextVarArg } + NextVarArgInstruction() { this.getOpcode() instanceof Opcode::NextVarArg } } /** @@ -2180,5 +2197,5 @@ class NextVarArgInstruction extends UnaryInstruction { * The result is the address of the newly allocated object. */ class NewObjInstruction extends Instruction { - NewObjInstruction() { getOpcode() instanceof Opcode::NewObj } + NewObjInstruction() { this.getOpcode() instanceof Opcode::NewObj } } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll index d7cf89ca9aa..85d217bd361 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll @@ -46,12 +46,12 @@ class Operand extends TStageOperand { /** * Gets the location of the source code for this operand. */ - final Language::Location getLocation() { result = getUse().getLocation() } + final Language::Location getLocation() { result = this.getUse().getLocation() } /** * Gets the function that contains this operand. */ - final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() } + final IRFunction getEnclosingIRFunction() { result = this.getUse().getEnclosingIRFunction() } /** * Gets the `Instruction` that consumes this operand. @@ -74,7 +74,7 @@ class Operand extends TStageOperand { */ final Instruction getDef() { result = this.getAnyDef() and - getDefinitionOverlap() instanceof MustExactlyOverlap + this.getDefinitionOverlap() instanceof MustExactlyOverlap } /** @@ -82,7 +82,7 @@ class Operand extends TStageOperand { * * Gets the `Instruction` that consumes this operand. */ - deprecated final Instruction getUseInstruction() { result = getUse() } + deprecated final Instruction getUseInstruction() { result = this.getUse() } /** * DEPRECATED: use `getAnyDef` or `getDef`. The exact replacement for this @@ -91,7 +91,7 @@ class Operand extends TStageOperand { * * Gets the `Instruction` whose result is the value of the operand. */ - deprecated final Instruction getDefinitionInstruction() { result = getAnyDef() } + deprecated final Instruction getDefinitionInstruction() { result = this.getAnyDef() } /** * Gets the overlap relationship between the operand's definition and its use. @@ -101,7 +101,9 @@ class Operand extends TStageOperand { /** * Holds if the result of the definition instruction does not exactly overlap this use. */ - final predicate isDefinitionInexact() { not getDefinitionOverlap() instanceof MustExactlyOverlap } + final predicate isDefinitionInexact() { + not this.getDefinitionOverlap() instanceof MustExactlyOverlap + } /** * Gets a prefix to use when dumping the operand in an operand list. @@ -121,7 +123,7 @@ class Operand extends TStageOperand { * For example: `this:r3_5` */ final string getDumpString() { - result = getDumpLabel() + getInexactSpecifier() + getDefinitionId() + result = this.getDumpLabel() + this.getInexactSpecifier() + this.getDefinitionId() } /** @@ -129,9 +131,9 @@ class Operand extends TStageOperand { * definition is not modeled in SSA. */ private string getDefinitionId() { - result = getAnyDef().getResultId() + result = this.getAnyDef().getResultId() or - not exists(getAnyDef()) and result = "m?" + not exists(this.getAnyDef()) and result = "m?" } /** @@ -140,7 +142,7 @@ class Operand extends TStageOperand { * the empty string. */ private string getInexactSpecifier() { - if isDefinitionInexact() then result = "~" else result = "" + if this.isDefinitionInexact() then result = "~" else result = "" } /** @@ -155,7 +157,7 @@ class Operand extends TStageOperand { * the definition type, such as in the case of a partial read or a read from a pointer that * has been cast to a different type. */ - Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() } + Language::LanguageType getLanguageType() { result = this.getAnyDef().getResultLanguageType() } /** * Gets the language-neutral type of the value consumed by this operand. This is usually the same @@ -164,7 +166,7 @@ class Operand extends TStageOperand { * from the definition type, such as in the case of a partial read or a read from a pointer that * has been cast to a different type. */ - final IRType getIRType() { result = getLanguageType().getIRType() } + final IRType getIRType() { result = this.getLanguageType().getIRType() } /** * Gets the type of the value consumed by this operand. This is usually the same as the @@ -173,7 +175,7 @@ class Operand extends TStageOperand { * the definition type, such as in the case of a partial read or a read from a pointer that * has been cast to a different type. */ - final Language::Type getType() { getLanguageType().hasType(result, _) } + final Language::Type getType() { this.getLanguageType().hasType(result, _) } /** * Holds if the value consumed by this operand is a glvalue. If this @@ -182,13 +184,13 @@ class Operand extends TStageOperand { * not hold, the value of the operand represents a value whose type is * given by `getType()`. */ - final predicate isGLValue() { getLanguageType().hasType(_, true) } + final predicate isGLValue() { this.getLanguageType().hasType(_, true) } /** * Gets the size of the value consumed by this operand, in bytes. If the operand does not have * a known constant size, this predicate does not hold. */ - final int getSize() { result = getLanguageType().getByteSize() } + final int getSize() { result = this.getLanguageType().getByteSize() } } /** @@ -205,7 +207,7 @@ class MemoryOperand extends Operand { /** * Gets the kind of memory access performed by the operand. */ - MemoryAccessKind getMemoryAccess() { result = getUse().getOpcode().getReadMemoryAccess() } + MemoryAccessKind getMemoryAccess() { result = this.getUse().getOpcode().getReadMemoryAccess() } /** * Holds if the memory access performed by this operand will not always read from every bit in the @@ -215,7 +217,7 @@ class MemoryOperand extends Operand { * conservative estimate of the memory that might actually be accessed at runtime (for example, * the global side effects of a function call). */ - predicate hasMayReadMemoryAccess() { getUse().getOpcode().hasMayReadMemoryAccess() } + predicate hasMayReadMemoryAccess() { this.getUse().getOpcode().hasMayReadMemoryAccess() } /** * Returns the operand that holds the memory address from which the current operand loads its @@ -223,8 +225,8 @@ class MemoryOperand extends Operand { * is `r1`. */ final AddressOperand getAddressOperand() { - getMemoryAccess().usesAddressOperand() and - result.getUse() = getUse() + this.getMemoryAccess().usesAddressOperand() and + result.getUse() = this.getUse() } } @@ -294,7 +296,7 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOpe result = unique(Instruction defInstr | hasDefinition(defInstr, _)) } - final override Overlap getDefinitionOverlap() { hasDefinition(_, result) } + final override Overlap getDefinitionOverlap() { this.hasDefinition(_, result) } pragma[noinline] private predicate hasDefinition(Instruction defInstr, Overlap overlap) { @@ -449,13 +451,17 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand { final override Overlap getDefinitionOverlap() { result = overlap } - final override int getDumpSortOrder() { result = 11 + getPredecessorBlock().getDisplayIndex() } - - final override string getDumpLabel() { - result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" + final override int getDumpSortOrder() { + result = 11 + this.getPredecessorBlock().getDisplayIndex() } - final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() } + final override string getDumpLabel() { + result = "from " + this.getPredecessorBlock().getDisplayIndex().toString() + ":" + } + + final override string getDumpId() { + result = this.getPredecessorBlock().getDisplayIndex().toString() + } /** * Gets the predecessor block from which this value comes. diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll index 4b86f9a7cec..bb8630a5e0c 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll @@ -24,7 +24,7 @@ class IRBlockBase extends TIRBlock { final string toString() { result = getFirstInstruction(this).toString() } /** Gets the source location of the first non-`Phi` instruction in this block. */ - final Language::Location getLocation() { result = getFirstInstruction().getLocation() } + final Language::Location getLocation() { result = this.getFirstInstruction().getLocation() } /** * INTERNAL: Do not use. @@ -39,7 +39,7 @@ class IRBlockBase extends TIRBlock { ) and this = rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 | - funcBlock.getEnclosingFunction() = getEnclosingFunction() and + funcBlock.getEnclosingFunction() = this.getEnclosingFunction() and funcBlock.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and // Ensure that the block containing `EnterFunction` always comes first. if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction @@ -59,15 +59,15 @@ class IRBlockBase extends TIRBlock { * Get the `Phi` instructions that appear at the start of this block. */ final PhiInstruction getAPhiInstruction() { - Construction::getPhiInstructionBlockStart(result) = getFirstInstruction() + Construction::getPhiInstructionBlockStart(result) = this.getFirstInstruction() } /** * Gets an instruction in this block. This includes `Phi` instructions. */ final Instruction getAnInstruction() { - result = getInstruction(_) or - result = getAPhiInstruction() + result = this.getInstruction(_) or + result = this.getAPhiInstruction() } /** @@ -78,7 +78,9 @@ class IRBlockBase extends TIRBlock { /** * Gets the last instruction in this block. */ - final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) } + final Instruction getLastInstruction() { + result = this.getInstruction(this.getInstructionCount() - 1) + } /** * Gets the number of non-`Phi` instructions in this block. @@ -149,7 +151,7 @@ class IRBlock extends IRBlockBase { * Block `A` dominates block `B` if any control flow path from the entry block of the function to * block `B` must pass through block `A`. A block always dominates itself. */ - final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } + final predicate dominates(IRBlock block) { this.strictlyDominates(block) or this = block } /** * Gets a block on the dominance frontier of this block. @@ -159,8 +161,8 @@ class IRBlock extends IRBlockBase { */ pragma[noinline] final IRBlock dominanceFrontier() { - dominates(result.getAPredecessor()) and - not strictlyDominates(result) + this.dominates(result.getAPredecessor()) and + not this.strictlyDominates(result) } /** @@ -189,7 +191,7 @@ class IRBlock extends IRBlockBase { * Block `A` post-dominates block `B` if any control flow path from `B` to the exit block of the * function must pass through block `A`. A block always post-dominates itself. */ - final predicate postDominates(IRBlock block) { strictlyPostDominates(block) or this = block } + final predicate postDominates(IRBlock block) { this.strictlyPostDominates(block) or this = block } /** * Gets a block on the post-dominance frontier of this block. @@ -199,16 +201,16 @@ class IRBlock extends IRBlockBase { */ pragma[noinline] final IRBlock postPominanceFrontier() { - postDominates(result.getASuccessor()) and - not strictlyPostDominates(result) + this.postDominates(result.getASuccessor()) and + not this.strictlyPostDominates(result) } /** * Holds if this block is reachable from the entry block of its function. */ final predicate isReachableFromFunctionEntry() { - this = getEnclosingIRFunction().getEntryBlock() or - getAPredecessor().isReachableFromFunctionEntry() + this = this.getEnclosingIRFunction().getEntryBlock() or + this.getAPredecessor().isReachableFromFunctionEntry() } } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll index 2fb3edad602..88a973fc5a8 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -41,7 +41,7 @@ class Instruction extends Construction::TStageInstruction { } /** Gets a textual representation of this element. */ - final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } + final string toString() { result = this.getOpcode().toString() + ": " + this.getAST().toString() } /** * Gets a string showing the result, opcode, and operands of the instruction, equivalent to what @@ -50,7 +50,8 @@ class Instruction extends Construction::TStageInstruction { * `mu0_28(int) = Store r0_26, r0_27` */ final string getDumpString() { - result = getResultString() + " = " + getOperationString() + " " + getOperandsString() + result = + this.getResultString() + " = " + this.getOperationString() + " " + this.getOperandsString() } private predicate shouldGenerateDumpStrings() { @@ -66,10 +67,13 @@ class Instruction extends Construction::TStageInstruction { * VariableAddress[x] */ final string getOperationString() { - shouldGenerateDumpStrings() and - if exists(getImmediateString()) - then result = getOperationPrefix() + getOpcode().toString() + "[" + getImmediateString() + "]" - else result = getOperationPrefix() + getOpcode().toString() + this.shouldGenerateDumpStrings() and + if exists(this.getImmediateString()) + then + result = + this.getOperationPrefix() + this.getOpcode().toString() + "[" + this.getImmediateString() + + "]" + else result = this.getOperationPrefix() + this.getOpcode().toString() } /** @@ -78,17 +82,17 @@ class Instruction extends Construction::TStageInstruction { string getImmediateString() { none() } private string getOperationPrefix() { - shouldGenerateDumpStrings() and + this.shouldGenerateDumpStrings() and if this instanceof SideEffectInstruction then result = "^" else result = "" } private string getResultPrefix() { - shouldGenerateDumpStrings() and - if getResultIRType() instanceof IRVoidType + this.shouldGenerateDumpStrings() and + if this.getResultIRType() instanceof IRVoidType then result = "v" else - if hasMemoryResult() - then if isResultModeled() then result = "m" else result = "mu" + if this.hasMemoryResult() + then if this.isResultModeled() then result = "m" else result = "mu" else result = "r" } @@ -97,7 +101,7 @@ class Instruction extends Construction::TStageInstruction { * used by debugging and printing code only. */ int getDisplayIndexInBlock() { - shouldGenerateDumpStrings() and + this.shouldGenerateDumpStrings() and exists(IRBlock block | this = block.getInstruction(result) or @@ -111,12 +115,12 @@ class Instruction extends Construction::TStageInstruction { } private int getLineRank() { - shouldGenerateDumpStrings() and + this.shouldGenerateDumpStrings() and this = rank[result](Instruction instr | instr = - getAnInstructionAtLine(getEnclosingIRFunction(), getLocation().getFile(), - getLocation().getStartLine()) + getAnInstructionAtLine(this.getEnclosingIRFunction(), this.getLocation().getFile(), + this.getLocation().getStartLine()) | instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock() ) @@ -130,8 +134,9 @@ class Instruction extends Construction::TStageInstruction { * Example: `r1_1` */ string getResultId() { - shouldGenerateDumpStrings() and - result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank() + this.shouldGenerateDumpStrings() and + result = + this.getResultPrefix() + this.getAST().getLocation().getStartLine() + "_" + this.getLineRank() } /** @@ -142,8 +147,8 @@ class Instruction extends Construction::TStageInstruction { * Example: `r1_1(int*)` */ final string getResultString() { - shouldGenerateDumpStrings() and - result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")" + this.shouldGenerateDumpStrings() and + result = this.getResultId() + "(" + this.getResultLanguageType().getDumpString() + ")" } /** @@ -153,10 +158,10 @@ class Instruction extends Construction::TStageInstruction { * Example: `func:r3_4, this:r3_5` */ string getOperandsString() { - shouldGenerateDumpStrings() and + this.shouldGenerateDumpStrings() and result = concat(Operand operand | - operand = getAnOperand() + operand = this.getAnOperand() | operand.getDumpString(), ", " order by operand.getDumpSortOrder() ) @@ -190,7 +195,7 @@ class Instruction extends Construction::TStageInstruction { * Gets the function that contains this instruction. */ final Language::Function getEnclosingFunction() { - result = getEnclosingIRFunction().getFunction() + result = this.getEnclosingIRFunction().getFunction() } /** @@ -208,7 +213,7 @@ class Instruction extends Construction::TStageInstruction { /** * Gets the location of the source code for this instruction. */ - final Language::Location getLocation() { result = getAST().getLocation() } + final Language::Location getLocation() { result = this.getAST().getLocation() } /** * Gets the `Expr` whose result is computed by this instruction, if any. The `Expr` may be a @@ -243,7 +248,7 @@ class Instruction extends Construction::TStageInstruction { * a result, its result type will be `IRVoidType`. */ cached - final IRType getResultIRType() { result = getResultLanguageType().getIRType() } + final IRType getResultIRType() { result = this.getResultLanguageType().getIRType() } /** * Gets the type of the result produced by this instruction. If the @@ -254,7 +259,7 @@ class Instruction extends Construction::TStageInstruction { */ final Language::Type getResultType() { exists(Language::LanguageType resultType | - resultType = getResultLanguageType() and + resultType = this.getResultLanguageType() and ( resultType.hasUnspecifiedType(result, _) or @@ -283,7 +288,7 @@ class Instruction extends Construction::TStageInstruction { * result of the `Load` instruction is a prvalue of type `int`, representing * the integer value loaded from variable `x`. */ - final predicate isGLValue() { getResultLanguageType().hasType(_, true) } + final predicate isGLValue() { this.getResultLanguageType().hasType(_, true) } /** * Gets the size of the result produced by this instruction, in bytes. If the @@ -292,7 +297,7 @@ class Instruction extends Construction::TStageInstruction { * If `this.isGLValue()` holds for this instruction, the value of * `getResultSize()` will always be the size of a pointer. */ - final int getResultSize() { result = getResultLanguageType().getByteSize() } + final int getResultSize() { result = this.getResultLanguageType().getByteSize() } /** * Gets the opcode that specifies the operation performed by this instruction. @@ -314,14 +319,16 @@ class Instruction extends Construction::TStageInstruction { /** * Holds if this instruction produces a memory result. */ - final predicate hasMemoryResult() { exists(getResultMemoryAccess()) } + final predicate hasMemoryResult() { exists(this.getResultMemoryAccess()) } /** * Gets the kind of memory access performed by this instruction's result. * Holds only for instructions with a memory result. */ pragma[inline] - final MemoryAccessKind getResultMemoryAccess() { result = getOpcode().getWriteMemoryAccess() } + final MemoryAccessKind getResultMemoryAccess() { + result = this.getOpcode().getWriteMemoryAccess() + } /** * Holds if the memory access performed by this instruction's result will not always write to @@ -332,7 +339,7 @@ class Instruction extends Construction::TStageInstruction { * (for example, the global side effects of a function call). */ pragma[inline] - final predicate hasResultMayMemoryAccess() { getOpcode().hasMayWriteMemoryAccess() } + final predicate hasResultMayMemoryAccess() { this.getOpcode().hasMayWriteMemoryAccess() } /** * Gets the operand that holds the memory address to which this instruction stores its @@ -340,7 +347,7 @@ class Instruction extends Construction::TStageInstruction { * is `r1`. */ final AddressOperand getResultAddressOperand() { - getResultMemoryAccess().usesAddressOperand() and + this.getResultMemoryAccess().usesAddressOperand() and result.getUse() = this } @@ -349,7 +356,7 @@ class Instruction extends Construction::TStageInstruction { * result, if any. For example, in `m3 = Store r1, r2`, the result of `getResultAddressOperand()` * is the instruction that defines `r1`. */ - final Instruction getResultAddress() { result = getResultAddressOperand().getDef() } + final Instruction getResultAddress() { result = this.getResultAddressOperand().getDef() } /** * Holds if the result of this instruction is precisely modeled in SSA. Always @@ -368,7 +375,7 @@ class Instruction extends Construction::TStageInstruction { */ final predicate isResultModeled() { // Register results are always in SSA form. - not hasMemoryResult() or + not this.hasMemoryResult() or Construction::hasModeledMemoryResult(this) } @@ -412,7 +419,7 @@ class Instruction extends Construction::TStageInstruction { /** * Gets all direct successors of this instruction. */ - final Instruction getASuccessor() { result = getSuccessor(_) } + final Instruction getASuccessor() { result = this.getSuccessor(_) } /** * Gets a predecessor of this instruction such that the predecessor reaches @@ -423,7 +430,7 @@ class Instruction extends Construction::TStageInstruction { /** * Gets all direct predecessors of this instruction. */ - final Instruction getAPredecessor() { result = getPredecessor(_) } + final Instruction getAPredecessor() { result = this.getPredecessor(_) } } /** @@ -543,7 +550,7 @@ class IndexedInstruction extends Instruction { * at this instruction. This instruction has no predecessors. */ class EnterFunctionInstruction extends Instruction { - EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction } + EnterFunctionInstruction() { this.getOpcode() instanceof Opcode::EnterFunction } } /** @@ -554,7 +561,7 @@ class EnterFunctionInstruction extends Instruction { * struct, or union, see `FieldAddressInstruction`. */ class VariableAddressInstruction extends VariableInstruction { - VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } + VariableAddressInstruction() { this.getOpcode() instanceof Opcode::VariableAddress } } /** @@ -566,7 +573,7 @@ class VariableAddressInstruction extends VariableInstruction { * The result has an `IRFunctionAddress` type. */ class FunctionAddressInstruction extends FunctionInstruction { - FunctionAddressInstruction() { getOpcode() instanceof Opcode::FunctionAddress } + FunctionAddressInstruction() { this.getOpcode() instanceof Opcode::FunctionAddress } } /** @@ -577,7 +584,7 @@ class FunctionAddressInstruction extends FunctionInstruction { * initializes that parameter. */ class InitializeParameterInstruction extends VariableInstruction { - InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter } + InitializeParameterInstruction() { this.getOpcode() instanceof Opcode::InitializeParameter } /** * Gets the parameter initialized by this instruction. @@ -603,7 +610,7 @@ class InitializeParameterInstruction extends VariableInstruction { * initialized elsewhere, would not otherwise have a definition in this function. */ class InitializeNonLocalInstruction extends Instruction { - InitializeNonLocalInstruction() { getOpcode() instanceof Opcode::InitializeNonLocal } + InitializeNonLocalInstruction() { this.getOpcode() instanceof Opcode::InitializeNonLocal } } /** @@ -611,7 +618,7 @@ class InitializeNonLocalInstruction extends Instruction { * with the value of that memory on entry to the function. */ class InitializeIndirectionInstruction extends VariableInstruction { - InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection } + InitializeIndirectionInstruction() { this.getOpcode() instanceof Opcode::InitializeIndirection } /** * Gets the parameter initialized by this instruction. @@ -635,24 +642,24 @@ class InitializeIndirectionInstruction extends VariableInstruction { * An instruction that initializes the `this` pointer parameter of the enclosing function. */ class InitializeThisInstruction extends Instruction { - InitializeThisInstruction() { getOpcode() instanceof Opcode::InitializeThis } + InitializeThisInstruction() { this.getOpcode() instanceof Opcode::InitializeThis } } /** * An instruction that computes the address of a non-static field of an object. */ class FieldAddressInstruction extends FieldInstruction { - FieldAddressInstruction() { getOpcode() instanceof Opcode::FieldAddress } + FieldAddressInstruction() { this.getOpcode() instanceof Opcode::FieldAddress } /** * Gets the operand that provides the address of the object containing the field. */ - final UnaryOperand getObjectAddressOperand() { result = getAnOperand() } + final UnaryOperand getObjectAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the object containing the field. */ - final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } + final Instruction getObjectAddress() { result = this.getObjectAddressOperand().getDef() } } /** @@ -661,17 +668,19 @@ class FieldAddressInstruction extends FieldInstruction { * This instruction is used for element access to C# arrays. */ class ElementsAddressInstruction extends UnaryInstruction { - ElementsAddressInstruction() { getOpcode() instanceof Opcode::ElementsAddress } + ElementsAddressInstruction() { this.getOpcode() instanceof Opcode::ElementsAddress } /** * Gets the operand that provides the address of the array object. */ - final UnaryOperand getArrayObjectAddressOperand() { result = getAnOperand() } + final UnaryOperand getArrayObjectAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the array object. */ - final Instruction getArrayObjectAddress() { result = getArrayObjectAddressOperand().getDef() } + final Instruction getArrayObjectAddress() { + result = this.getArrayObjectAddressOperand().getDef() + } } /** @@ -685,7 +694,7 @@ class ElementsAddressInstruction extends UnaryInstruction { * taken may want to ignore any function that contains an `ErrorInstruction`. */ class ErrorInstruction extends Instruction { - ErrorInstruction() { getOpcode() instanceof Opcode::Error } + ErrorInstruction() { this.getOpcode() instanceof Opcode::Error } } /** @@ -695,7 +704,7 @@ class ErrorInstruction extends Instruction { * an initializer, or whose initializer only partially initializes the variable. */ class UninitializedInstruction extends VariableInstruction { - UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized } + UninitializedInstruction() { this.getOpcode() instanceof Opcode::Uninitialized } /** * Gets the variable that is uninitialized. @@ -710,7 +719,7 @@ class UninitializedInstruction extends VariableInstruction { * least one instruction, even when the AST has no semantic effect. */ class NoOpInstruction extends Instruction { - NoOpInstruction() { getOpcode() instanceof Opcode::NoOp } + NoOpInstruction() { this.getOpcode() instanceof Opcode::NoOp } } /** @@ -732,32 +741,32 @@ class NoOpInstruction extends Instruction { * `void`-returning function. */ class ReturnInstruction extends Instruction { - ReturnInstruction() { getOpcode() instanceof ReturnOpcode } + ReturnInstruction() { this.getOpcode() instanceof ReturnOpcode } } /** * An instruction that returns control to the caller of the function, without returning a value. */ class ReturnVoidInstruction extends ReturnInstruction { - ReturnVoidInstruction() { getOpcode() instanceof Opcode::ReturnVoid } + ReturnVoidInstruction() { this.getOpcode() instanceof Opcode::ReturnVoid } } /** * An instruction that returns control to the caller of the function, including a return value. */ class ReturnValueInstruction extends ReturnInstruction { - ReturnValueInstruction() { getOpcode() instanceof Opcode::ReturnValue } + ReturnValueInstruction() { this.getOpcode() instanceof Opcode::ReturnValue } /** * Gets the operand that provides the value being returned by the function. */ - final LoadOperand getReturnValueOperand() { result = getAnOperand() } + final LoadOperand getReturnValueOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the value being returned by the function, if an * exact definition is available. */ - final Instruction getReturnValue() { result = getReturnValueOperand().getDef() } + final Instruction getReturnValue() { result = this.getReturnValueOperand().getDef() } } /** @@ -770,28 +779,28 @@ class ReturnValueInstruction extends ReturnInstruction { * that the caller initialized the memory pointed to by the parameter before the call. */ class ReturnIndirectionInstruction extends VariableInstruction { - ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } + ReturnIndirectionInstruction() { this.getOpcode() instanceof Opcode::ReturnIndirection } /** * Gets the operand that provides the value of the pointed-to memory. */ - final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + final SideEffectOperand getSideEffectOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the value of the pointed-to memory, if an exact * definition is available. */ - final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + final Instruction getSideEffect() { result = this.getSideEffectOperand().getDef() } /** * Gets the operand that provides the address of the pointed-to memory. */ - final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + final AddressOperand getSourceAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the pointed-to memory. */ - final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } + final Instruction getSourceAddress() { result = this.getSourceAddressOperand().getDef() } /** * Gets the parameter for which this instruction reads the final pointed-to value within the @@ -821,12 +830,12 @@ class ReturnIndirectionInstruction extends VariableInstruction { * * There are several different copy instructions, depending on the source and destination of the * copy operation: - * - `CopyInstruction` - Copies a register operand to a register result. + * - `CopyValueInstruction` - Copies a register operand to a register result. * - `LoadInstruction` - Copies a memory operand to a register result. * - `StoreInstruction` - Copies a register operand to a memory result. */ class CopyInstruction extends Instruction { - CopyInstruction() { getOpcode() instanceof CopyOpcode } + CopyInstruction() { this.getOpcode() instanceof CopyOpcode } /** * Gets the operand that provides the input value of the copy. @@ -837,16 +846,16 @@ class CopyInstruction extends Instruction { * Gets the instruction whose result provides the input value of the copy, if an exact definition * is available. */ - final Instruction getSourceValue() { result = getSourceValueOperand().getDef() } + final Instruction getSourceValue() { result = this.getSourceValueOperand().getDef() } } /** * An instruction that returns a register result containing a copy of its register operand. */ class CopyValueInstruction extends CopyInstruction, UnaryInstruction { - CopyValueInstruction() { getOpcode() instanceof Opcode::CopyValue } + CopyValueInstruction() { this.getOpcode() instanceof Opcode::CopyValue } - final override UnaryOperand getSourceValueOperand() { result = getAnOperand() } + final override UnaryOperand getSourceValueOperand() { result = this.getAnOperand() } } /** @@ -863,47 +872,49 @@ private string getAddressOperandDescription(AddressOperand operand) { * An instruction that returns a register result containing a copy of its memory operand. */ class LoadInstruction extends CopyInstruction { - LoadInstruction() { getOpcode() instanceof Opcode::Load } + LoadInstruction() { this.getOpcode() instanceof Opcode::Load } final override string getImmediateString() { - result = getAddressOperandDescription(getSourceAddressOperand()) + result = getAddressOperandDescription(this.getSourceAddressOperand()) } /** * Gets the operand that provides the address of the value being loaded. */ - final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + final AddressOperand getSourceAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the value being loaded. */ - final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } + final Instruction getSourceAddress() { result = this.getSourceAddressOperand().getDef() } - final override LoadOperand getSourceValueOperand() { result = getAnOperand() } + final override LoadOperand getSourceValueOperand() { result = this.getAnOperand() } } /** * An instruction that returns a memory result containing a copy of its register operand. */ class StoreInstruction extends CopyInstruction { - StoreInstruction() { getOpcode() instanceof Opcode::Store } + StoreInstruction() { this.getOpcode() instanceof Opcode::Store } final override string getImmediateString() { - result = getAddressOperandDescription(getDestinationAddressOperand()) + result = getAddressOperandDescription(this.getDestinationAddressOperand()) } /** * Gets the operand that provides the address of the location to which the value will be stored. */ - final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + final AddressOperand getDestinationAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the location to which the value will * be stored, if an exact definition is available. */ - final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } + final Instruction getDestinationAddress() { + result = this.getDestinationAddressOperand().getDef() + } - final override StoreValueOperand getSourceValueOperand() { result = getAnOperand() } + final override StoreValueOperand getSourceValueOperand() { result = this.getAnOperand() } } /** @@ -911,27 +922,27 @@ class StoreInstruction extends CopyInstruction { * operand. */ class ConditionalBranchInstruction extends Instruction { - ConditionalBranchInstruction() { getOpcode() instanceof Opcode::ConditionalBranch } + ConditionalBranchInstruction() { this.getOpcode() instanceof Opcode::ConditionalBranch } /** * Gets the operand that provides the Boolean condition controlling the branch. */ - final ConditionOperand getConditionOperand() { result = getAnOperand() } + final ConditionOperand getConditionOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the Boolean condition controlling the branch. */ - final Instruction getCondition() { result = getConditionOperand().getDef() } + final Instruction getCondition() { result = this.getConditionOperand().getDef() } /** * Gets the instruction to which control will flow if the condition is true. */ - final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) } + final Instruction getTrueSuccessor() { result = this.getSuccessor(EdgeKind::trueEdge()) } /** * Gets the instruction to which control will flow if the condition is false. */ - final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) } + final Instruction getFalseSuccessor() { result = this.getSuccessor(EdgeKind::falseEdge()) } } /** @@ -943,14 +954,14 @@ class ConditionalBranchInstruction extends Instruction { * successors. */ class ExitFunctionInstruction extends Instruction { - ExitFunctionInstruction() { getOpcode() instanceof Opcode::ExitFunction } + ExitFunctionInstruction() { this.getOpcode() instanceof Opcode::ExitFunction } } /** * An instruction whose result is a constant value. */ class ConstantInstruction extends ConstantValueInstruction { - ConstantInstruction() { getOpcode() instanceof Opcode::Constant } + ConstantInstruction() { this.getOpcode() instanceof Opcode::Constant } } /** @@ -959,7 +970,7 @@ class ConstantInstruction extends ConstantValueInstruction { class IntegerConstantInstruction extends ConstantInstruction { IntegerConstantInstruction() { exists(IRType resultType | - resultType = getResultIRType() and + resultType = this.getResultIRType() and (resultType instanceof IRIntegerType or resultType instanceof IRBooleanType) ) } @@ -969,7 +980,7 @@ class IntegerConstantInstruction extends ConstantInstruction { * An instruction whose result is a constant value of floating-point type. */ class FloatConstantInstruction extends ConstantInstruction { - FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType } + FloatConstantInstruction() { this.getResultIRType() instanceof IRFloatingPointType } } /** @@ -978,7 +989,9 @@ class FloatConstantInstruction extends ConstantInstruction { class StringConstantInstruction extends VariableInstruction { override IRStringLiteral var; - final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) } + final override string getImmediateString() { + result = Language::getStringLiteralText(this.getValue()) + } /** * Gets the string literal whose address is returned by this instruction. @@ -990,37 +1003,37 @@ class StringConstantInstruction extends VariableInstruction { * An instruction whose result is computed from two operands. */ class BinaryInstruction extends Instruction { - BinaryInstruction() { getOpcode() instanceof BinaryOpcode } + BinaryInstruction() { this.getOpcode() instanceof BinaryOpcode } /** * Gets the left operand of this binary instruction. */ - final LeftOperand getLeftOperand() { result = getAnOperand() } + final LeftOperand getLeftOperand() { result = this.getAnOperand() } /** * Gets the right operand of this binary instruction. */ - final RightOperand getRightOperand() { result = getAnOperand() } + final RightOperand getRightOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the value of the left operand of this binary * instruction. */ - final Instruction getLeft() { result = getLeftOperand().getDef() } + final Instruction getLeft() { result = this.getLeftOperand().getDef() } /** * Gets the instruction whose result provides the value of the right operand of this binary * instruction. */ - final Instruction getRight() { result = getRightOperand().getDef() } + final Instruction getRight() { result = this.getRightOperand().getDef() } /** * Holds if this instruction's operands are `op1` and `op2`, in either order. */ final predicate hasOperands(Operand op1, Operand op2) { - op1 = getLeftOperand() and op2 = getRightOperand() + op1 = this.getLeftOperand() and op2 = this.getRightOperand() or - op1 = getRightOperand() and op2 = getLeftOperand() + op1 = this.getRightOperand() and op2 = this.getLeftOperand() } } @@ -1028,7 +1041,7 @@ class BinaryInstruction extends Instruction { * An instruction that computes the result of an arithmetic operation. */ class ArithmeticInstruction extends Instruction { - ArithmeticInstruction() { getOpcode() instanceof ArithmeticOpcode } + ArithmeticInstruction() { this.getOpcode() instanceof ArithmeticOpcode } } /** @@ -1050,7 +1063,7 @@ class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction * performed according to IEEE-754. */ class AddInstruction extends BinaryArithmeticInstruction { - AddInstruction() { getOpcode() instanceof Opcode::Add } + AddInstruction() { this.getOpcode() instanceof Opcode::Add } } /** @@ -1061,7 +1074,7 @@ class AddInstruction extends BinaryArithmeticInstruction { * according to IEEE-754. */ class SubInstruction extends BinaryArithmeticInstruction { - SubInstruction() { getOpcode() instanceof Opcode::Sub } + SubInstruction() { this.getOpcode() instanceof Opcode::Sub } } /** @@ -1072,7 +1085,7 @@ class SubInstruction extends BinaryArithmeticInstruction { * performed according to IEEE-754. */ class MulInstruction extends BinaryArithmeticInstruction { - MulInstruction() { getOpcode() instanceof Opcode::Mul } + MulInstruction() { this.getOpcode() instanceof Opcode::Mul } } /** @@ -1083,7 +1096,7 @@ class MulInstruction extends BinaryArithmeticInstruction { * to IEEE-754. */ class DivInstruction extends BinaryArithmeticInstruction { - DivInstruction() { getOpcode() instanceof Opcode::Div } + DivInstruction() { this.getOpcode() instanceof Opcode::Div } } /** @@ -1093,7 +1106,7 @@ class DivInstruction extends BinaryArithmeticInstruction { * division by zero or integer overflow is undefined. */ class RemInstruction extends BinaryArithmeticInstruction { - RemInstruction() { getOpcode() instanceof Opcode::Rem } + RemInstruction() { this.getOpcode() instanceof Opcode::Rem } } /** @@ -1104,14 +1117,14 @@ class RemInstruction extends BinaryArithmeticInstruction { * is performed according to IEEE-754. */ class NegateInstruction extends UnaryArithmeticInstruction { - NegateInstruction() { getOpcode() instanceof Opcode::Negate } + NegateInstruction() { this.getOpcode() instanceof Opcode::Negate } } /** * An instruction that computes the result of a bitwise operation. */ class BitwiseInstruction extends Instruction { - BitwiseInstruction() { getOpcode() instanceof BitwiseOpcode } + BitwiseInstruction() { this.getOpcode() instanceof BitwiseOpcode } } /** @@ -1130,7 +1143,7 @@ class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } * Both operands must have the same integer type, which will also be the result type. */ class BitAndInstruction extends BinaryBitwiseInstruction { - BitAndInstruction() { getOpcode() instanceof Opcode::BitAnd } + BitAndInstruction() { this.getOpcode() instanceof Opcode::BitAnd } } /** @@ -1139,7 +1152,7 @@ class BitAndInstruction extends BinaryBitwiseInstruction { * Both operands must have the same integer type, which will also be the result type. */ class BitOrInstruction extends BinaryBitwiseInstruction { - BitOrInstruction() { getOpcode() instanceof Opcode::BitOr } + BitOrInstruction() { this.getOpcode() instanceof Opcode::BitOr } } /** @@ -1148,7 +1161,7 @@ class BitOrInstruction extends BinaryBitwiseInstruction { * Both operands must have the same integer type, which will also be the result type. */ class BitXorInstruction extends BinaryBitwiseInstruction { - BitXorInstruction() { getOpcode() instanceof Opcode::BitXor } + BitXorInstruction() { this.getOpcode() instanceof Opcode::BitXor } } /** @@ -1159,7 +1172,7 @@ class BitXorInstruction extends BinaryBitwiseInstruction { * rightmost bits are zero-filled. */ class ShiftLeftInstruction extends BinaryBitwiseInstruction { - ShiftLeftInstruction() { getOpcode() instanceof Opcode::ShiftLeft } + ShiftLeftInstruction() { this.getOpcode() instanceof Opcode::ShiftLeft } } /** @@ -1172,7 +1185,7 @@ class ShiftLeftInstruction extends BinaryBitwiseInstruction { * of the left operand. */ class ShiftRightInstruction extends BinaryBitwiseInstruction { - ShiftRightInstruction() { getOpcode() instanceof Opcode::ShiftRight } + ShiftRightInstruction() { this.getOpcode() instanceof Opcode::ShiftRight } } /** @@ -1183,7 +1196,7 @@ class PointerArithmeticInstruction extends BinaryInstruction { int elementSize; PointerArithmeticInstruction() { - getOpcode() instanceof PointerArithmeticOpcode and + this.getOpcode() instanceof PointerArithmeticOpcode and elementSize = Raw::getInstructionElementSize(this) } @@ -1206,7 +1219,7 @@ class PointerArithmeticInstruction extends BinaryInstruction { * An instruction that adds or subtracts an integer offset from a pointer. */ class PointerOffsetInstruction extends PointerArithmeticInstruction { - PointerOffsetInstruction() { getOpcode() instanceof PointerOffsetOpcode } + PointerOffsetInstruction() { this.getOpcode() instanceof PointerOffsetOpcode } } /** @@ -1217,7 +1230,7 @@ class PointerOffsetInstruction extends PointerArithmeticInstruction { * overflow is undefined. */ class PointerAddInstruction extends PointerOffsetInstruction { - PointerAddInstruction() { getOpcode() instanceof Opcode::PointerAdd } + PointerAddInstruction() { this.getOpcode() instanceof Opcode::PointerAdd } } /** @@ -1228,7 +1241,7 @@ class PointerAddInstruction extends PointerOffsetInstruction { * pointer underflow is undefined. */ class PointerSubInstruction extends PointerOffsetInstruction { - PointerSubInstruction() { getOpcode() instanceof Opcode::PointerSub } + PointerSubInstruction() { this.getOpcode() instanceof Opcode::PointerSub } } /** @@ -1241,31 +1254,31 @@ class PointerSubInstruction extends PointerOffsetInstruction { * undefined. */ class PointerDiffInstruction extends PointerArithmeticInstruction { - PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } + PointerDiffInstruction() { this.getOpcode() instanceof Opcode::PointerDiff } } /** * An instruction whose result is computed from a single operand. */ class UnaryInstruction extends Instruction { - UnaryInstruction() { getOpcode() instanceof UnaryOpcode } + UnaryInstruction() { this.getOpcode() instanceof UnaryOpcode } /** * Gets the sole operand of this instruction. */ - final UnaryOperand getUnaryOperand() { result = getAnOperand() } + final UnaryOperand getUnaryOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the sole operand of this instruction. */ - final Instruction getUnary() { result = getUnaryOperand().getDef() } + final Instruction getUnary() { result = this.getUnaryOperand().getDef() } } /** * An instruction that converts the value of its operand to a value of a different type. */ class ConvertInstruction extends UnaryInstruction { - ConvertInstruction() { getOpcode() instanceof Opcode::Convert } + ConvertInstruction() { this.getOpcode() instanceof Opcode::Convert } } /** @@ -1279,7 +1292,7 @@ class ConvertInstruction extends UnaryInstruction { * `as` expression. */ class CheckedConvertOrNullInstruction extends UnaryInstruction { - CheckedConvertOrNullInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrNull } + CheckedConvertOrNullInstruction() { this.getOpcode() instanceof Opcode::CheckedConvertOrNull } } /** @@ -1293,7 +1306,7 @@ class CheckedConvertOrNullInstruction extends UnaryInstruction { * expression. */ class CheckedConvertOrThrowInstruction extends UnaryInstruction { - CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow } + CheckedConvertOrThrowInstruction() { this.getOpcode() instanceof Opcode::CheckedConvertOrThrow } } /** @@ -1306,7 +1319,7 @@ class CheckedConvertOrThrowInstruction extends UnaryInstruction { * the most-derived object. */ class CompleteObjectAddressInstruction extends UnaryInstruction { - CompleteObjectAddressInstruction() { getOpcode() instanceof Opcode::CompleteObjectAddress } + CompleteObjectAddressInstruction() { this.getOpcode() instanceof Opcode::CompleteObjectAddress } } /** @@ -1351,7 +1364,7 @@ class InheritanceConversionInstruction extends UnaryInstruction { * An instruction that converts from the address of a derived class to the address of a base class. */ class ConvertToBaseInstruction extends InheritanceConversionInstruction { - ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode } + ConvertToBaseInstruction() { this.getOpcode() instanceof ConvertToBaseOpcode } } /** @@ -1361,7 +1374,9 @@ class ConvertToBaseInstruction extends InheritanceConversionInstruction { * If the operand holds a null address, the result is a null address. */ class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { - ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase } + ConvertToNonVirtualBaseInstruction() { + this.getOpcode() instanceof Opcode::ConvertToNonVirtualBase + } } /** @@ -1371,7 +1386,7 @@ class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { * If the operand holds a null address, the result is a null address. */ class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { - ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase } + ConvertToVirtualBaseInstruction() { this.getOpcode() instanceof Opcode::ConvertToVirtualBase } } /** @@ -1381,7 +1396,7 @@ class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { * If the operand holds a null address, the result is a null address. */ class ConvertToDerivedInstruction extends InheritanceConversionInstruction { - ConvertToDerivedInstruction() { getOpcode() instanceof Opcode::ConvertToDerived } + ConvertToDerivedInstruction() { this.getOpcode() instanceof Opcode::ConvertToDerived } } /** @@ -1390,7 +1405,7 @@ class ConvertToDerivedInstruction extends InheritanceConversionInstruction { * The operand must have an integer type, which will also be the result type. */ class BitComplementInstruction extends UnaryBitwiseInstruction { - BitComplementInstruction() { getOpcode() instanceof Opcode::BitComplement } + BitComplementInstruction() { this.getOpcode() instanceof Opcode::BitComplement } } /** @@ -1399,14 +1414,14 @@ class BitComplementInstruction extends UnaryBitwiseInstruction { * The operand must have a Boolean type, which will also be the result type. */ class LogicalNotInstruction extends UnaryInstruction { - LogicalNotInstruction() { getOpcode() instanceof Opcode::LogicalNot } + LogicalNotInstruction() { this.getOpcode() instanceof Opcode::LogicalNot } } /** * An instruction that compares two numeric operands. */ class CompareInstruction extends BinaryInstruction { - CompareInstruction() { getOpcode() instanceof CompareOpcode } + CompareInstruction() { this.getOpcode() instanceof CompareOpcode } } /** @@ -1417,7 +1432,7 @@ class CompareInstruction extends BinaryInstruction { * unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareEQInstruction extends CompareInstruction { - CompareEQInstruction() { getOpcode() instanceof Opcode::CompareEQ } + CompareEQInstruction() { this.getOpcode() instanceof Opcode::CompareEQ } } /** @@ -1428,14 +1443,14 @@ class CompareEQInstruction extends CompareInstruction { * `left == right`. Floating-point comparison is performed according to IEEE-754. */ class CompareNEInstruction extends CompareInstruction { - CompareNEInstruction() { getOpcode() instanceof Opcode::CompareNE } + CompareNEInstruction() { this.getOpcode() instanceof Opcode::CompareNE } } /** * An instruction that does a relative comparison of two values, such as `<` or `>=`. */ class RelationalInstruction extends CompareInstruction { - RelationalInstruction() { getOpcode() instanceof RelationalOpcode } + RelationalInstruction() { this.getOpcode() instanceof RelationalOpcode } /** * Gets the operand on the "greater" (or "greater-or-equal") side @@ -1467,11 +1482,11 @@ class RelationalInstruction extends CompareInstruction { * are unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareLTInstruction extends RelationalInstruction { - CompareLTInstruction() { getOpcode() instanceof Opcode::CompareLT } + CompareLTInstruction() { this.getOpcode() instanceof Opcode::CompareLT } - override Instruction getLesser() { result = getLeft() } + override Instruction getLesser() { result = this.getLeft() } - override Instruction getGreater() { result = getRight() } + override Instruction getGreater() { result = this.getRight() } override predicate isStrict() { any() } } @@ -1484,11 +1499,11 @@ class CompareLTInstruction extends RelationalInstruction { * are unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareGTInstruction extends RelationalInstruction { - CompareGTInstruction() { getOpcode() instanceof Opcode::CompareGT } + CompareGTInstruction() { this.getOpcode() instanceof Opcode::CompareGT } - override Instruction getLesser() { result = getRight() } + override Instruction getLesser() { result = this.getRight() } - override Instruction getGreater() { result = getLeft() } + override Instruction getGreater() { result = this.getLeft() } override predicate isStrict() { any() } } @@ -1502,11 +1517,11 @@ class CompareGTInstruction extends RelationalInstruction { * are unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareLEInstruction extends RelationalInstruction { - CompareLEInstruction() { getOpcode() instanceof Opcode::CompareLE } + CompareLEInstruction() { this.getOpcode() instanceof Opcode::CompareLE } - override Instruction getLesser() { result = getLeft() } + override Instruction getLesser() { result = this.getLeft() } - override Instruction getGreater() { result = getRight() } + override Instruction getGreater() { result = this.getRight() } override predicate isStrict() { none() } } @@ -1520,11 +1535,11 @@ class CompareLEInstruction extends RelationalInstruction { * are unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareGEInstruction extends RelationalInstruction { - CompareGEInstruction() { getOpcode() instanceof Opcode::CompareGE } + CompareGEInstruction() { this.getOpcode() instanceof Opcode::CompareGE } - override Instruction getLesser() { result = getRight() } + override Instruction getLesser() { result = this.getRight() } - override Instruction getGreater() { result = getLeft() } + override Instruction getGreater() { result = this.getLeft() } override predicate isStrict() { none() } } @@ -1543,78 +1558,78 @@ class CompareGEInstruction extends RelationalInstruction { * of any case edge. */ class SwitchInstruction extends Instruction { - SwitchInstruction() { getOpcode() instanceof Opcode::Switch } + SwitchInstruction() { this.getOpcode() instanceof Opcode::Switch } /** Gets the operand that provides the integer value controlling the switch. */ - final ConditionOperand getExpressionOperand() { result = getAnOperand() } + final ConditionOperand getExpressionOperand() { result = this.getAnOperand() } /** Gets the instruction whose result provides the integer value controlling the switch. */ - final Instruction getExpression() { result = getExpressionOperand().getDef() } + final Instruction getExpression() { result = this.getExpressionOperand().getDef() } /** Gets the successor instructions along the case edges of the switch. */ - final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) } + final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = this.getSuccessor(edge)) } /** Gets the successor instruction along the default edge of the switch, if any. */ - final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) } + final Instruction getDefaultSuccessor() { result = this.getSuccessor(EdgeKind::defaultEdge()) } } /** * An instruction that calls a function. */ class CallInstruction extends Instruction { - CallInstruction() { getOpcode() instanceof Opcode::Call } + CallInstruction() { this.getOpcode() instanceof Opcode::Call } final override string getImmediateString() { - result = getStaticCallTarget().toString() + result = this.getStaticCallTarget().toString() or - not exists(getStaticCallTarget()) and result = "?" + not exists(this.getStaticCallTarget()) and result = "?" } /** * Gets the operand the specifies the target function of the call. */ - final CallTargetOperand getCallTargetOperand() { result = getAnOperand() } + final CallTargetOperand getCallTargetOperand() { result = this.getAnOperand() } /** * Gets the `Instruction` that computes the target function of the call. This is usually a * `FunctionAddress` instruction, but can also be an arbitrary instruction that produces a * function pointer. */ - final Instruction getCallTarget() { result = getCallTargetOperand().getDef() } + final Instruction getCallTarget() { result = this.getCallTargetOperand().getDef() } /** * Gets all of the argument operands of the call, including the `this` pointer, if any. */ - final ArgumentOperand getAnArgumentOperand() { result = getAnOperand() } + final ArgumentOperand getAnArgumentOperand() { result = this.getAnOperand() } /** * Gets the `Function` that the call targets, if this is statically known. */ final Language::Function getStaticCallTarget() { - result = getCallTarget().(FunctionAddressInstruction).getFunctionSymbol() + result = this.getCallTarget().(FunctionAddressInstruction).getFunctionSymbol() } /** * Gets all of the arguments of the call, including the `this` pointer, if any. */ - final Instruction getAnArgument() { result = getAnArgumentOperand().getDef() } + final Instruction getAnArgument() { result = this.getAnArgumentOperand().getDef() } /** * Gets the `this` pointer argument operand of the call, if any. */ - final ThisArgumentOperand getThisArgumentOperand() { result = getAnOperand() } + final ThisArgumentOperand getThisArgumentOperand() { result = this.getAnOperand() } /** * Gets the `this` pointer argument of the call, if any. */ - final Instruction getThisArgument() { result = getThisArgumentOperand().getDef() } + final Instruction getThisArgument() { result = this.getThisArgumentOperand().getDef() } /** * Gets the argument operand at the specified index. */ pragma[noinline] final PositionalArgumentOperand getPositionalArgumentOperand(int index) { - result = getAnOperand() and + result = this.getAnOperand() and result.getIndex() = index } @@ -1623,7 +1638,7 @@ class CallInstruction extends Instruction { */ pragma[noinline] final Instruction getPositionalArgument(int index) { - result = getPositionalArgumentOperand(index).getDef() + result = this.getPositionalArgumentOperand(index).getDef() } /** @@ -1631,16 +1646,16 @@ class CallInstruction extends Instruction { */ pragma[noinline] final ArgumentOperand getArgumentOperand(int index) { - index >= 0 and result = getPositionalArgumentOperand(index) + index >= 0 and result = this.getPositionalArgumentOperand(index) or - index = -1 and result = getThisArgumentOperand() + index = -1 and result = this.getThisArgumentOperand() } /** * Gets the argument at the specified index, or `this` if `index` is `-1`. */ pragma[noinline] - final Instruction getArgument(int index) { result = getArgumentOperand(index).getDef() } + final Instruction getArgument(int index) { result = this.getArgumentOperand(index).getDef() } /** * Gets the number of arguments of the call, including the `this` pointer, if any. @@ -1665,7 +1680,7 @@ class CallInstruction extends Instruction { * An instruction representing a side effect of a function call. */ class SideEffectInstruction extends Instruction { - SideEffectInstruction() { getOpcode() instanceof SideEffectOpcode } + SideEffectInstruction() { this.getOpcode() instanceof SideEffectOpcode } /** * Gets the instruction whose execution causes this side effect. @@ -1680,7 +1695,7 @@ class SideEffectInstruction extends Instruction { * accessed by that call. */ class CallSideEffectInstruction extends SideEffectInstruction { - CallSideEffectInstruction() { getOpcode() instanceof Opcode::CallSideEffect } + CallSideEffectInstruction() { this.getOpcode() instanceof Opcode::CallSideEffect } } /** @@ -1691,7 +1706,7 @@ class CallSideEffectInstruction extends SideEffectInstruction { * call target cannot write to escaped memory. */ class CallReadSideEffectInstruction extends SideEffectInstruction { - CallReadSideEffectInstruction() { getOpcode() instanceof Opcode::CallReadSideEffect } + CallReadSideEffectInstruction() { this.getOpcode() instanceof Opcode::CallReadSideEffect } } /** @@ -1699,33 +1714,33 @@ class CallReadSideEffectInstruction extends SideEffectInstruction { * specific parameter. */ class ReadSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { - ReadSideEffectInstruction() { getOpcode() instanceof ReadSideEffectOpcode } + ReadSideEffectInstruction() { this.getOpcode() instanceof ReadSideEffectOpcode } /** Gets the operand for the value that will be read from this instruction, if known. */ - final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + final SideEffectOperand getSideEffectOperand() { result = this.getAnOperand() } /** Gets the value that will be read from this instruction, if known. */ - final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + final Instruction getSideEffect() { result = this.getSideEffectOperand().getDef() } /** Gets the operand for the address from which this instruction may read. */ - final AddressOperand getArgumentOperand() { result = getAnOperand() } + final AddressOperand getArgumentOperand() { result = this.getAnOperand() } /** Gets the address from which this instruction may read. */ - final Instruction getArgumentDef() { result = getArgumentOperand().getDef() } + final Instruction getArgumentDef() { result = this.getArgumentOperand().getDef() } } /** * An instruction representing the read of an indirect parameter within a function call. */ class IndirectReadSideEffectInstruction extends ReadSideEffectInstruction { - IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect } + IndirectReadSideEffectInstruction() { this.getOpcode() instanceof Opcode::IndirectReadSideEffect } } /** * An instruction representing the read of an indirect buffer parameter within a function call. */ class BufferReadSideEffectInstruction extends ReadSideEffectInstruction { - BufferReadSideEffectInstruction() { getOpcode() instanceof Opcode::BufferReadSideEffect } + BufferReadSideEffectInstruction() { this.getOpcode() instanceof Opcode::BufferReadSideEffect } } /** @@ -1733,18 +1748,18 @@ class BufferReadSideEffectInstruction extends ReadSideEffectInstruction { */ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { SizedBufferReadSideEffectInstruction() { - getOpcode() instanceof Opcode::SizedBufferReadSideEffect + this.getOpcode() instanceof Opcode::SizedBufferReadSideEffect } /** * Gets the operand that holds the number of bytes read from the buffer. */ - final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + final BufferSizeOperand getBufferSizeOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the number of bytes read from the buffer. */ - final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } + final Instruction getBufferSize() { result = this.getBufferSizeOperand().getDef() } } /** @@ -1752,17 +1767,17 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { * specific parameter. */ class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { - WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode } + WriteSideEffectInstruction() { this.getOpcode() instanceof WriteSideEffectOpcode } /** * Get the operand that holds the address of the memory to be written. */ - final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + final AddressOperand getDestinationAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the memory to be written. */ - Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } + Instruction getDestinationAddress() { result = this.getDestinationAddressOperand().getDef() } } /** @@ -1770,7 +1785,7 @@ class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstructi */ class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction { IndirectMustWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::IndirectMustWriteSideEffect + this.getOpcode() instanceof Opcode::IndirectMustWriteSideEffect } } @@ -1780,7 +1795,7 @@ class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction */ class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { BufferMustWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::BufferMustWriteSideEffect + this.getOpcode() instanceof Opcode::BufferMustWriteSideEffect } } @@ -1790,18 +1805,18 @@ class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { */ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { SizedBufferMustWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect + this.getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect } /** * Gets the operand that holds the number of bytes written to the buffer. */ - final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + final BufferSizeOperand getBufferSizeOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the number of bytes written to the buffer. */ - final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } + final Instruction getBufferSize() { result = this.getBufferSizeOperand().getDef() } } /** @@ -1812,7 +1827,7 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi */ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { IndirectMayWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::IndirectMayWriteSideEffect + this.getOpcode() instanceof Opcode::IndirectMayWriteSideEffect } } @@ -1822,7 +1837,9 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. */ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { - BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect } + BufferMayWriteSideEffectInstruction() { + this.getOpcode() instanceof Opcode::BufferMayWriteSideEffect + } } /** @@ -1832,18 +1849,18 @@ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { */ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { SizedBufferMayWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect + this.getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect } /** * Gets the operand that holds the number of bytes written to the buffer. */ - final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + final BufferSizeOperand getBufferSizeOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the number of bytes written to the buffer. */ - final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } + final Instruction getBufferSize() { result = this.getBufferSizeOperand().getDef() } } /** @@ -1852,80 +1869,80 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio */ class InitializeDynamicAllocationInstruction extends SideEffectInstruction { InitializeDynamicAllocationInstruction() { - getOpcode() instanceof Opcode::InitializeDynamicAllocation + this.getOpcode() instanceof Opcode::InitializeDynamicAllocation } /** * Gets the operand that represents the address of the allocation this instruction is initializing. */ - final AddressOperand getAllocationAddressOperand() { result = getAnOperand() } + final AddressOperand getAllocationAddressOperand() { result = this.getAnOperand() } /** * Gets the address for the allocation this instruction is initializing. */ - final Instruction getAllocationAddress() { result = getAllocationAddressOperand().getDef() } + final Instruction getAllocationAddress() { result = this.getAllocationAddressOperand().getDef() } } /** * An instruction representing a GNU or MSVC inline assembly statement. */ class InlineAsmInstruction extends Instruction { - InlineAsmInstruction() { getOpcode() instanceof Opcode::InlineAsm } + InlineAsmInstruction() { this.getOpcode() instanceof Opcode::InlineAsm } } /** * An instruction that throws an exception. */ class ThrowInstruction extends Instruction { - ThrowInstruction() { getOpcode() instanceof ThrowOpcode } + ThrowInstruction() { this.getOpcode() instanceof ThrowOpcode } } /** * An instruction that throws a new exception. */ class ThrowValueInstruction extends ThrowInstruction { - ThrowValueInstruction() { getOpcode() instanceof Opcode::ThrowValue } + ThrowValueInstruction() { this.getOpcode() instanceof Opcode::ThrowValue } /** * Gets the address operand of the exception thrown by this instruction. */ - final AddressOperand getExceptionAddressOperand() { result = getAnOperand() } + final AddressOperand getExceptionAddressOperand() { result = this.getAnOperand() } /** * Gets the address of the exception thrown by this instruction. */ - final Instruction getExceptionAddress() { result = getExceptionAddressOperand().getDef() } + final Instruction getExceptionAddress() { result = this.getExceptionAddressOperand().getDef() } /** * Gets the operand for the exception thrown by this instruction. */ - final LoadOperand getExceptionOperand() { result = getAnOperand() } + final LoadOperand getExceptionOperand() { result = this.getAnOperand() } /** * Gets the exception thrown by this instruction. */ - final Instruction getException() { result = getExceptionOperand().getDef() } + final Instruction getException() { result = this.getExceptionOperand().getDef() } } /** * An instruction that re-throws the current exception. */ class ReThrowInstruction extends ThrowInstruction { - ReThrowInstruction() { getOpcode() instanceof Opcode::ReThrow } + ReThrowInstruction() { this.getOpcode() instanceof Opcode::ReThrow } } /** * An instruction that exits the current function by propagating an exception. */ class UnwindInstruction extends Instruction { - UnwindInstruction() { getOpcode() instanceof Opcode::Unwind } + UnwindInstruction() { this.getOpcode() instanceof Opcode::Unwind } } /** * An instruction that starts a `catch` handler. */ class CatchInstruction extends Instruction { - CatchInstruction() { getOpcode() instanceof CatchOpcode } + CatchInstruction() { this.getOpcode() instanceof CatchOpcode } } /** @@ -1935,7 +1952,7 @@ class CatchByTypeInstruction extends CatchInstruction { Language::LanguageType exceptionType; CatchByTypeInstruction() { - getOpcode() instanceof Opcode::CatchByType and + this.getOpcode() instanceof Opcode::CatchByType and exceptionType = Raw::getInstructionExceptionType(this) } @@ -1951,21 +1968,21 @@ class CatchByTypeInstruction extends CatchInstruction { * An instruction that catches any exception. */ class CatchAnyInstruction extends CatchInstruction { - CatchAnyInstruction() { getOpcode() instanceof Opcode::CatchAny } + CatchAnyInstruction() { this.getOpcode() instanceof Opcode::CatchAny } } /** * An instruction that initializes all escaped memory. */ class AliasedDefinitionInstruction extends Instruction { - AliasedDefinitionInstruction() { getOpcode() instanceof Opcode::AliasedDefinition } + AliasedDefinitionInstruction() { this.getOpcode() instanceof Opcode::AliasedDefinition } } /** * An instruction that consumes all escaped memory on exit from the function. */ class AliasedUseInstruction extends Instruction { - AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse } + AliasedUseInstruction() { this.getOpcode() instanceof Opcode::AliasedUse } } /** @@ -1979,7 +1996,7 @@ class AliasedUseInstruction extends Instruction { * runtime. */ class PhiInstruction extends Instruction { - PhiInstruction() { getOpcode() instanceof Opcode::Phi } + PhiInstruction() { this.getOpcode() instanceof Opcode::Phi } /** * Gets all of the instruction's `PhiInputOperand`s, representing the values that flow from each predecessor block. @@ -2047,29 +2064,29 @@ class PhiInstruction extends Instruction { * https://link.springer.com/content/pdf/10.1007%2F3-540-61053-7_66.pdf. */ class ChiInstruction extends Instruction { - ChiInstruction() { getOpcode() instanceof Opcode::Chi } + ChiInstruction() { this.getOpcode() instanceof Opcode::Chi } /** * Gets the operand that represents the previous state of all memory that might be aliased by the * memory write. */ - final ChiTotalOperand getTotalOperand() { result = getAnOperand() } + final ChiTotalOperand getTotalOperand() { result = this.getAnOperand() } /** * Gets the operand that represents the previous state of all memory that might be aliased by the * memory write. */ - final Instruction getTotal() { result = getTotalOperand().getDef() } + final Instruction getTotal() { result = this.getTotalOperand().getDef() } /** * Gets the operand that represents the new value written by the memory write. */ - final ChiPartialOperand getPartialOperand() { result = getAnOperand() } + final ChiPartialOperand getPartialOperand() { result = this.getAnOperand() } /** * Gets the operand that represents the new value written by the memory write. */ - final Instruction getPartial() { result = getPartialOperand().getDef() } + final Instruction getPartial() { result = this.getPartialOperand().getDef() } /** * Gets the bit range `[startBit, endBit)` updated by the partial operand of this `ChiInstruction`, relative to the start address of the total operand. @@ -2093,7 +2110,7 @@ class ChiInstruction extends Instruction { * or `Switch` instruction where that particular edge is infeasible. */ class UnreachedInstruction extends Instruction { - UnreachedInstruction() { getOpcode() instanceof Opcode::Unreached } + UnreachedInstruction() { this.getOpcode() instanceof Opcode::Unreached } } /** @@ -2106,7 +2123,7 @@ class BuiltInOperationInstruction extends Instruction { Language::BuiltInOperation operation; BuiltInOperationInstruction() { - getOpcode() instanceof BuiltInOperationOpcode and + this.getOpcode() instanceof BuiltInOperationOpcode and operation = Raw::getInstructionBuiltInOperation(this) } @@ -2122,9 +2139,9 @@ class BuiltInOperationInstruction extends Instruction { * actual operation is specified by the `getBuiltInOperation()` predicate. */ class BuiltInInstruction extends BuiltInOperationInstruction { - BuiltInInstruction() { getOpcode() instanceof Opcode::BuiltIn } + BuiltInInstruction() { this.getOpcode() instanceof Opcode::BuiltIn } - final override string getImmediateString() { result = getBuiltInOperation().toString() } + final override string getImmediateString() { result = this.getBuiltInOperation().toString() } } /** @@ -2135,7 +2152,7 @@ class BuiltInInstruction extends BuiltInOperationInstruction { * to the `...` parameter. */ class VarArgsStartInstruction extends UnaryInstruction { - VarArgsStartInstruction() { getOpcode() instanceof Opcode::VarArgsStart } + VarArgsStartInstruction() { this.getOpcode() instanceof Opcode::VarArgsStart } } /** @@ -2145,7 +2162,7 @@ class VarArgsStartInstruction extends UnaryInstruction { * a result. */ class VarArgsEndInstruction extends UnaryInstruction { - VarArgsEndInstruction() { getOpcode() instanceof Opcode::VarArgsEnd } + VarArgsEndInstruction() { this.getOpcode() instanceof Opcode::VarArgsEnd } } /** @@ -2155,7 +2172,7 @@ class VarArgsEndInstruction extends UnaryInstruction { * argument. */ class VarArgInstruction extends UnaryInstruction { - VarArgInstruction() { getOpcode() instanceof Opcode::VarArg } + VarArgInstruction() { this.getOpcode() instanceof Opcode::VarArg } } /** @@ -2166,7 +2183,7 @@ class VarArgInstruction extends UnaryInstruction { * argument of the `...` parameter. */ class NextVarArgInstruction extends UnaryInstruction { - NextVarArgInstruction() { getOpcode() instanceof Opcode::NextVarArg } + NextVarArgInstruction() { this.getOpcode() instanceof Opcode::NextVarArg } } /** @@ -2180,5 +2197,5 @@ class NextVarArgInstruction extends UnaryInstruction { * The result is the address of the newly allocated object. */ class NewObjInstruction extends Instruction { - NewObjInstruction() { getOpcode() instanceof Opcode::NewObj } + NewObjInstruction() { this.getOpcode() instanceof Opcode::NewObj } } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Operand.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Operand.qll index d7cf89ca9aa..85d217bd361 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Operand.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Operand.qll @@ -46,12 +46,12 @@ class Operand extends TStageOperand { /** * Gets the location of the source code for this operand. */ - final Language::Location getLocation() { result = getUse().getLocation() } + final Language::Location getLocation() { result = this.getUse().getLocation() } /** * Gets the function that contains this operand. */ - final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() } + final IRFunction getEnclosingIRFunction() { result = this.getUse().getEnclosingIRFunction() } /** * Gets the `Instruction` that consumes this operand. @@ -74,7 +74,7 @@ class Operand extends TStageOperand { */ final Instruction getDef() { result = this.getAnyDef() and - getDefinitionOverlap() instanceof MustExactlyOverlap + this.getDefinitionOverlap() instanceof MustExactlyOverlap } /** @@ -82,7 +82,7 @@ class Operand extends TStageOperand { * * Gets the `Instruction` that consumes this operand. */ - deprecated final Instruction getUseInstruction() { result = getUse() } + deprecated final Instruction getUseInstruction() { result = this.getUse() } /** * DEPRECATED: use `getAnyDef` or `getDef`. The exact replacement for this @@ -91,7 +91,7 @@ class Operand extends TStageOperand { * * Gets the `Instruction` whose result is the value of the operand. */ - deprecated final Instruction getDefinitionInstruction() { result = getAnyDef() } + deprecated final Instruction getDefinitionInstruction() { result = this.getAnyDef() } /** * Gets the overlap relationship between the operand's definition and its use. @@ -101,7 +101,9 @@ class Operand extends TStageOperand { /** * Holds if the result of the definition instruction does not exactly overlap this use. */ - final predicate isDefinitionInexact() { not getDefinitionOverlap() instanceof MustExactlyOverlap } + final predicate isDefinitionInexact() { + not this.getDefinitionOverlap() instanceof MustExactlyOverlap + } /** * Gets a prefix to use when dumping the operand in an operand list. @@ -121,7 +123,7 @@ class Operand extends TStageOperand { * For example: `this:r3_5` */ final string getDumpString() { - result = getDumpLabel() + getInexactSpecifier() + getDefinitionId() + result = this.getDumpLabel() + this.getInexactSpecifier() + this.getDefinitionId() } /** @@ -129,9 +131,9 @@ class Operand extends TStageOperand { * definition is not modeled in SSA. */ private string getDefinitionId() { - result = getAnyDef().getResultId() + result = this.getAnyDef().getResultId() or - not exists(getAnyDef()) and result = "m?" + not exists(this.getAnyDef()) and result = "m?" } /** @@ -140,7 +142,7 @@ class Operand extends TStageOperand { * the empty string. */ private string getInexactSpecifier() { - if isDefinitionInexact() then result = "~" else result = "" + if this.isDefinitionInexact() then result = "~" else result = "" } /** @@ -155,7 +157,7 @@ class Operand extends TStageOperand { * the definition type, such as in the case of a partial read or a read from a pointer that * has been cast to a different type. */ - Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() } + Language::LanguageType getLanguageType() { result = this.getAnyDef().getResultLanguageType() } /** * Gets the language-neutral type of the value consumed by this operand. This is usually the same @@ -164,7 +166,7 @@ class Operand extends TStageOperand { * from the definition type, such as in the case of a partial read or a read from a pointer that * has been cast to a different type. */ - final IRType getIRType() { result = getLanguageType().getIRType() } + final IRType getIRType() { result = this.getLanguageType().getIRType() } /** * Gets the type of the value consumed by this operand. This is usually the same as the @@ -173,7 +175,7 @@ class Operand extends TStageOperand { * the definition type, such as in the case of a partial read or a read from a pointer that * has been cast to a different type. */ - final Language::Type getType() { getLanguageType().hasType(result, _) } + final Language::Type getType() { this.getLanguageType().hasType(result, _) } /** * Holds if the value consumed by this operand is a glvalue. If this @@ -182,13 +184,13 @@ class Operand extends TStageOperand { * not hold, the value of the operand represents a value whose type is * given by `getType()`. */ - final predicate isGLValue() { getLanguageType().hasType(_, true) } + final predicate isGLValue() { this.getLanguageType().hasType(_, true) } /** * Gets the size of the value consumed by this operand, in bytes. If the operand does not have * a known constant size, this predicate does not hold. */ - final int getSize() { result = getLanguageType().getByteSize() } + final int getSize() { result = this.getLanguageType().getByteSize() } } /** @@ -205,7 +207,7 @@ class MemoryOperand extends Operand { /** * Gets the kind of memory access performed by the operand. */ - MemoryAccessKind getMemoryAccess() { result = getUse().getOpcode().getReadMemoryAccess() } + MemoryAccessKind getMemoryAccess() { result = this.getUse().getOpcode().getReadMemoryAccess() } /** * Holds if the memory access performed by this operand will not always read from every bit in the @@ -215,7 +217,7 @@ class MemoryOperand extends Operand { * conservative estimate of the memory that might actually be accessed at runtime (for example, * the global side effects of a function call). */ - predicate hasMayReadMemoryAccess() { getUse().getOpcode().hasMayReadMemoryAccess() } + predicate hasMayReadMemoryAccess() { this.getUse().getOpcode().hasMayReadMemoryAccess() } /** * Returns the operand that holds the memory address from which the current operand loads its @@ -223,8 +225,8 @@ class MemoryOperand extends Operand { * is `r1`. */ final AddressOperand getAddressOperand() { - getMemoryAccess().usesAddressOperand() and - result.getUse() = getUse() + this.getMemoryAccess().usesAddressOperand() and + result.getUse() = this.getUse() } } @@ -294,7 +296,7 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOpe result = unique(Instruction defInstr | hasDefinition(defInstr, _)) } - final override Overlap getDefinitionOverlap() { hasDefinition(_, result) } + final override Overlap getDefinitionOverlap() { this.hasDefinition(_, result) } pragma[noinline] private predicate hasDefinition(Instruction defInstr, Overlap overlap) { @@ -449,13 +451,17 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand { final override Overlap getDefinitionOverlap() { result = overlap } - final override int getDumpSortOrder() { result = 11 + getPredecessorBlock().getDisplayIndex() } - - final override string getDumpLabel() { - result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" + final override int getDumpSortOrder() { + result = 11 + this.getPredecessorBlock().getDisplayIndex() } - final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() } + final override string getDumpLabel() { + result = "from " + this.getPredecessorBlock().getDisplayIndex().toString() + ":" + } + + final override string getDumpId() { + result = this.getPredecessorBlock().getDisplayIndex().toString() + } /** * Gets the predecessor block from which this value comes. diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll index a9f408bf161..3f0f4f10ee3 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll @@ -55,7 +55,7 @@ abstract class TranslatedExpr extends TranslatedElement { abstract predicate producesExprResult(); final CppType getResultType() { - if isResultGLValue() + if this.isResultGLValue() then result = getTypeForGLValue(expr.getType()) else result = getTypeForPRValue(expr.getType()) } @@ -128,9 +128,9 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext, TTranslatedConditionValue { TranslatedConditionValue() { this = TTranslatedConditionValue(expr) } - override TranslatedElement getChild(int id) { id = 0 and result = getCondition() } + override TranslatedElement getChild(int id) { id = 0 and result = this.getCondition() } - override Instruction getFirstInstruction() { result = getCondition().getFirstInstruction() } + override Instruction getFirstInstruction() { result = this.getCondition().getFirstInstruction() } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { ( @@ -146,46 +146,46 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext, tag = ConditionValueFalseConstantTag() ) and opcode instanceof Opcode::Constant and - resultType = getResultType() + resultType = this.getResultType() or ( tag = ConditionValueTrueStoreTag() or tag = ConditionValueFalseStoreTag() ) and opcode instanceof Opcode::Store and - resultType = getResultType() + resultType = this.getResultType() or tag = ConditionValueResultLoadTag() and opcode instanceof Opcode::Load and - resultType = getResultType() + resultType = this.getResultType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { kind instanceof GotoEdge and ( tag = ConditionValueTrueTempAddressTag() and - result = getInstruction(ConditionValueTrueConstantTag()) + result = this.getInstruction(ConditionValueTrueConstantTag()) or tag = ConditionValueTrueConstantTag() and - result = getInstruction(ConditionValueTrueStoreTag()) + result = this.getInstruction(ConditionValueTrueStoreTag()) or tag = ConditionValueTrueStoreTag() and - result = getInstruction(ConditionValueResultTempAddressTag()) + result = this.getInstruction(ConditionValueResultTempAddressTag()) or tag = ConditionValueFalseTempAddressTag() and - result = getInstruction(ConditionValueFalseConstantTag()) + result = this.getInstruction(ConditionValueFalseConstantTag()) or tag = ConditionValueFalseConstantTag() and - result = getInstruction(ConditionValueFalseStoreTag()) + result = this.getInstruction(ConditionValueFalseStoreTag()) or tag = ConditionValueFalseStoreTag() and - result = getInstruction(ConditionValueResultTempAddressTag()) + result = this.getInstruction(ConditionValueResultTempAddressTag()) or tag = ConditionValueResultTempAddressTag() and - result = getInstruction(ConditionValueResultLoadTag()) + result = this.getInstruction(ConditionValueResultLoadTag()) or tag = ConditionValueResultLoadTag() and - result = getParent().getChildSuccessor(this) + result = this.getParent().getChildSuccessor(this) ) } @@ -193,25 +193,25 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext, tag = ConditionValueTrueStoreTag() and ( operandTag instanceof AddressOperandTag and - result = getInstruction(ConditionValueTrueTempAddressTag()) + result = this.getInstruction(ConditionValueTrueTempAddressTag()) or operandTag instanceof StoreValueOperandTag and - result = getInstruction(ConditionValueTrueConstantTag()) + result = this.getInstruction(ConditionValueTrueConstantTag()) ) or tag = ConditionValueFalseStoreTag() and ( operandTag instanceof AddressOperandTag and - result = getInstruction(ConditionValueFalseTempAddressTag()) + result = this.getInstruction(ConditionValueFalseTempAddressTag()) or operandTag instanceof StoreValueOperandTag and - result = getInstruction(ConditionValueFalseConstantTag()) + result = this.getInstruction(ConditionValueFalseConstantTag()) ) or tag = ConditionValueResultLoadTag() and ( operandTag instanceof AddressOperandTag and - result = getInstruction(ConditionValueResultTempAddressTag()) + result = this.getInstruction(ConditionValueResultTempAddressTag()) ) } @@ -226,7 +226,7 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext, tag = ConditionValueFalseTempAddressTag() or tag = ConditionValueResultTempAddressTag() ) and - result = getTempVariable(ConditionValueTempVar()) + result = this.getTempVariable(ConditionValueTempVar()) } override string getInstructionConstantValue(InstructionTag tag) { @@ -235,18 +235,18 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext, tag = ConditionValueFalseConstantTag() and result = "0" } - override Instruction getResult() { result = getInstruction(ConditionValueResultLoadTag()) } + override Instruction getResult() { result = this.getInstruction(ConditionValueResultLoadTag()) } override Instruction getChildSuccessor(TranslatedElement child) { none() } override Instruction getChildTrueSuccessor(TranslatedCondition child) { - child = getCondition() and - result = getInstruction(ConditionValueTrueTempAddressTag()) + child = this.getCondition() and + result = this.getInstruction(ConditionValueTrueTempAddressTag()) } override Instruction getChildFalseSuccessor(TranslatedCondition child) { - child = getCondition() and - result = getInstruction(ConditionValueFalseTempAddressTag()) + child = this.getCondition() and + result = this.getInstruction(ConditionValueFalseTempAddressTag()) } private TranslatedCondition getCondition() { result = getTranslatedCondition(expr) } @@ -260,9 +260,11 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext, * temporary variable. */ abstract class TranslatedValueCategoryAdjustment extends TranslatedExpr { - final override Instruction getFirstInstruction() { result = getOperand().getFirstInstruction() } + final override Instruction getFirstInstruction() { + result = this.getOperand().getFirstInstruction() + } - final override TranslatedElement getChild(int id) { id = 0 and result = getOperand() } + final override TranslatedElement getChild(int id) { id = 0 and result = this.getOperand() } final override predicate producesExprResult() { // A temp object always produces the result of the expression. @@ -284,28 +286,28 @@ class TranslatedLoad extends TranslatedValueCategoryAdjustment, TTranslatedLoad override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = LoadTag() and opcode instanceof Opcode::Load and - resultType = getResultType() + resultType = this.getResultType() } override predicate isResultGLValue() { none() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = LoadTag() and - result = getParent().getChildSuccessor(this) and + result = this.getParent().getChildSuccessor(this) and kind instanceof GotoEdge } override Instruction getChildSuccessor(TranslatedElement child) { - child = getOperand() and result = getInstruction(LoadTag()) + child = this.getOperand() and result = this.getInstruction(LoadTag()) } - override Instruction getResult() { result = getInstruction(LoadTag()) } + override Instruction getResult() { result = this.getInstruction(LoadTag()) } override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { tag = LoadTag() and ( operandTag instanceof AddressOperandTag and - result = getOperand().getResult() + result = this.getOperand().getResult() ) } } @@ -337,28 +339,28 @@ class TranslatedSyntheticTemporaryObject extends TranslatedValueCategoryAdjustme override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = InitializerVariableAddressTag() and - result = getInstruction(InitializerStoreTag()) and + result = this.getInstruction(InitializerStoreTag()) and kind instanceof GotoEdge or tag = InitializerStoreTag() and - result = getParent().getChildSuccessor(this) and + result = this.getParent().getChildSuccessor(this) and kind instanceof GotoEdge } override Instruction getChildSuccessor(TranslatedElement child) { - child = getOperand() and result = getInstruction(InitializerVariableAddressTag()) + child = this.getOperand() and result = this.getInstruction(InitializerVariableAddressTag()) } - override Instruction getResult() { result = getInstruction(InitializerVariableAddressTag()) } + override Instruction getResult() { result = this.getInstruction(InitializerVariableAddressTag()) } override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { tag = InitializerStoreTag() and ( operandTag instanceof AddressOperandTag and - result = getInstruction(InitializerVariableAddressTag()) + result = this.getInstruction(InitializerVariableAddressTag()) or operandTag instanceof StoreValueOperandTag and - result = getOperand().getResult() + result = this.getOperand().getResult() ) } @@ -383,32 +385,32 @@ class TranslatedResultCopy extends TranslatedExpr, TTranslatedResultCopy { override string toString() { result = "Result of " + expr.toString() } - override Instruction getFirstInstruction() { result = getOperand().getFirstInstruction() } + override Instruction getFirstInstruction() { result = this.getOperand().getFirstInstruction() } - override TranslatedElement getChild(int id) { id = 0 and result = getOperand() } + override TranslatedElement getChild(int id) { id = 0 and result = this.getOperand() } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = ResultCopyTag() and opcode instanceof Opcode::CopyValue and - resultType = getOperand().getResultType() + resultType = this.getOperand().getResultType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = ResultCopyTag() and - result = getParent().getChildSuccessor(this) and + result = this.getParent().getChildSuccessor(this) and kind instanceof GotoEdge } override Instruction getChildSuccessor(TranslatedElement child) { - child = getOperand() and result = getInstruction(ResultCopyTag()) + child = this.getOperand() and result = this.getInstruction(ResultCopyTag()) } - override Instruction getResult() { result = getInstruction(ResultCopyTag()) } + override Instruction getResult() { result = this.getInstruction(ResultCopyTag()) } override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { tag = ResultCopyTag() and operandTag instanceof UnaryOperandTag and - result = getOperand().getResult() + result = this.getOperand().getResult() } final override predicate producesExprResult() { any() } @@ -419,23 +421,25 @@ class TranslatedResultCopy extends TranslatedExpr, TTranslatedResultCopy { class TranslatedCommaExpr extends TranslatedNonConstantExpr { override CommaExpr expr; - override Instruction getFirstInstruction() { result = getLeftOperand().getFirstInstruction() } - - override TranslatedElement getChild(int id) { - id = 0 and result = getLeftOperand() - or - id = 1 and result = getRightOperand() + override Instruction getFirstInstruction() { + result = this.getLeftOperand().getFirstInstruction() } - override Instruction getResult() { result = getRightOperand().getResult() } + override TranslatedElement getChild(int id) { + id = 0 and result = this.getLeftOperand() + or + id = 1 and result = this.getRightOperand() + } + + override Instruction getResult() { result = this.getRightOperand().getResult() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() } override Instruction getChildSuccessor(TranslatedElement child) { - child = getLeftOperand() and - result = getRightOperand().getFirstInstruction() + child = this.getLeftOperand() and + result = this.getRightOperand().getFirstInstruction() or - child = getRightOperand() and result = getParent().getChildSuccessor(this) + child = this.getRightOperand() and result = this.getParent().getChildSuccessor(this) } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { @@ -462,7 +466,7 @@ private int getElementSize(Type type) { abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr { override CrementOperation expr; - final override TranslatedElement getChild(int id) { id = 0 and result = getLoadedOperand() } + final override TranslatedElement getChild(int id) { id = 0 and result = this.getLoadedOperand() } final override string getInstructionConstantValue(InstructionTag tag) { tag = CrementConstantTag() and @@ -493,10 +497,10 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr { final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = CrementConstantTag() and opcode instanceof Opcode::Constant and - resultType = getConstantType() + resultType = this.getConstantType() or tag = CrementOpTag() and - opcode = getOpcode() and + opcode = this.getOpcode() and resultType = getTypeForPRValue(expr.getType()) or tag = CrementStoreTag() and @@ -508,49 +512,49 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr { tag = CrementOpTag() and ( operandTag instanceof LeftOperandTag and - result = getLoadedOperand().getResult() + result = this.getLoadedOperand().getResult() or operandTag instanceof RightOperandTag and - result = getInstruction(CrementConstantTag()) + result = this.getInstruction(CrementConstantTag()) ) or tag = CrementStoreTag() and ( operandTag instanceof AddressOperandTag and - result = getUnloadedOperand().getResult() + result = this.getUnloadedOperand().getResult() or operandTag instanceof StoreValueOperandTag and - result = getInstruction(CrementOpTag()) + result = this.getInstruction(CrementOpTag()) ) } final override Instruction getFirstInstruction() { - result = getLoadedOperand().getFirstInstruction() + result = this.getLoadedOperand().getFirstInstruction() } final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { kind instanceof GotoEdge and ( tag = CrementConstantTag() and - result = getInstruction(CrementOpTag()) + result = this.getInstruction(CrementOpTag()) or tag = CrementOpTag() and - result = getInstruction(CrementStoreTag()) + result = this.getInstruction(CrementStoreTag()) or tag = CrementStoreTag() and - result = getParent().getChildSuccessor(this) + result = this.getParent().getChildSuccessor(this) ) } final override Instruction getChildSuccessor(TranslatedElement child) { - child = getLoadedOperand() and result = getInstruction(CrementConstantTag()) + child = this.getLoadedOperand() and result = this.getInstruction(CrementConstantTag()) } final override int getInstructionElementSize(InstructionTag tag) { tag = CrementOpTag() and ( - getOpcode() instanceof Opcode::PointerAdd or - getOpcode() instanceof Opcode::PointerSub + this.getOpcode() instanceof Opcode::PointerAdd or + this.getOpcode() instanceof Opcode::PointerSub ) and result = getElementSize(expr.getType()) } @@ -567,7 +571,7 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr { /** * Gets the address to which the result of this crement will be stored. */ - final TranslatedExpr getUnloadedOperand() { result = getLoadedOperand().getOperand() } + final TranslatedExpr getUnloadedOperand() { result = this.getLoadedOperand().getOperand() } final Opcode getOpcode() { exists(Type resultType | @@ -601,18 +605,18 @@ class TranslatedPrefixCrementOperation extends TranslatedCrementOperation { // new value assigned to the operand. If this is C++, then the result is // an lvalue, but that lvalue is being loaded as part of this expression. // EDG doesn't mark this as a load. - result = getInstruction(CrementOpTag()) + result = this.getInstruction(CrementOpTag()) else // This is C++, where the result is an lvalue for the operand, and that // lvalue is not being loaded as part of this expression. - result = getUnloadedOperand().getResult() + result = this.getUnloadedOperand().getResult() } } class TranslatedPostfixCrementOperation extends TranslatedCrementOperation { override PostfixCrementOperation expr; - override Instruction getResult() { result = getLoadedOperand().getResult() } + override Instruction getResult() { result = this.getLoadedOperand().getResult() } } /** @@ -624,30 +628,30 @@ class TranslatedArrayExpr extends TranslatedNonConstantExpr { override ArrayExpr expr; final override Instruction getFirstInstruction() { - result = getBaseOperand().getFirstInstruction() + result = this.getBaseOperand().getFirstInstruction() } final override TranslatedElement getChild(int id) { - id = 0 and result = getBaseOperand() + id = 0 and result = this.getBaseOperand() or - id = 1 and result = getOffsetOperand() + id = 1 and result = this.getOffsetOperand() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = OnlyInstructionTag() and - result = getParent().getChildSuccessor(this) and + result = this.getParent().getChildSuccessor(this) and kind instanceof GotoEdge } override Instruction getChildSuccessor(TranslatedElement child) { - child = getBaseOperand() and - result = getOffsetOperand().getFirstInstruction() + child = this.getBaseOperand() and + result = this.getOffsetOperand().getFirstInstruction() or - child = getOffsetOperand() and - result = getInstruction(OnlyInstructionTag()) + child = this.getOffsetOperand() and + result = this.getInstruction(OnlyInstructionTag()) } - override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) } + override Instruction getResult() { result = this.getInstruction(OnlyInstructionTag()) } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = OnlyInstructionTag() and @@ -659,10 +663,10 @@ class TranslatedArrayExpr extends TranslatedNonConstantExpr { tag = OnlyInstructionTag() and ( operandTag instanceof LeftOperandTag and - result = getBaseOperand().getResult() + result = this.getBaseOperand().getResult() or operandTag instanceof RightOperandTag and - result = getOffsetOperand().getResult() + result = this.getOffsetOperand().getResult() ) } @@ -681,21 +685,23 @@ class TranslatedArrayExpr extends TranslatedNonConstantExpr { } abstract class TranslatedTransparentExpr extends TranslatedNonConstantExpr { - final override Instruction getFirstInstruction() { result = getOperand().getFirstInstruction() } + final override Instruction getFirstInstruction() { + result = this.getOperand().getFirstInstruction() + } - final override TranslatedElement getChild(int id) { id = 0 and result = getOperand() } + final override TranslatedElement getChild(int id) { id = 0 and result = this.getOperand() } final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() } final override Instruction getChildSuccessor(TranslatedElement child) { - child = getOperand() and result = getParent().getChildSuccessor(this) + child = this.getOperand() and result = this.getParent().getChildSuccessor(this) } final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { none() } - final override Instruction getResult() { result = getOperand().getResult() } + final override Instruction getResult() { result = this.getOperand().getResult() } final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { none() @@ -749,21 +755,23 @@ class TranslatedThisExpr extends TranslatedNonConstantExpr { or tag = ThisLoadTag() and opcode instanceof Opcode::Load and - resultType = getResultType() + resultType = this.getResultType() } - final override Instruction getResult() { result = getInstruction(ThisLoadTag()) } + final override Instruction getResult() { result = this.getInstruction(ThisLoadTag()) } - final override Instruction getFirstInstruction() { result = getInstruction(ThisAddressTag()) } + final override Instruction getFirstInstruction() { + result = this.getInstruction(ThisAddressTag()) + } final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { kind instanceof GotoEdge and tag = ThisAddressTag() and - result = getInstruction(ThisLoadTag()) + result = this.getInstruction(ThisLoadTag()) or kind instanceof GotoEdge and tag = ThisLoadTag() and - result = getParent().getChildSuccessor(this) + result = this.getParent().getChildSuccessor(this) } final override Instruction getChildSuccessor(TranslatedElement child) { none() } @@ -771,7 +779,7 @@ class TranslatedThisExpr extends TranslatedNonConstantExpr { final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { tag = ThisLoadTag() and operandTag instanceof AddressOperandTag and - result = getInstruction(ThisAddressTag()) + result = this.getInstruction(ThisAddressTag()) } override IRVariable getInstructionVariable(InstructionTag tag) { @@ -784,23 +792,23 @@ abstract class TranslatedVariableAccess extends TranslatedNonConstantExpr { override VariableAccess expr; final override TranslatedElement getChild(int id) { - id = 0 and result = getQualifier() // Might not exist + id = 0 and result = this.getQualifier() // Might not exist } final TranslatedExpr getQualifier() { result = getTranslatedExpr(expr.getQualifier().getFullyConverted()) } - override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) } + override Instruction getResult() { result = this.getInstruction(OnlyInstructionTag()) } final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = OnlyInstructionTag() and - result = getParent().getChildSuccessor(this) and + result = this.getParent().getChildSuccessor(this) and kind instanceof GotoEdge } final override Instruction getChildSuccessor(TranslatedElement child) { - child = getQualifier() and result = getInstruction(OnlyInstructionTag()) + child = this.getQualifier() and result = this.getInstruction(OnlyInstructionTag()) } } @@ -808,9 +816,9 @@ class TranslatedNonFieldVariableAccess extends TranslatedVariableAccess { TranslatedNonFieldVariableAccess() { not expr instanceof FieldAccess } override Instruction getFirstInstruction() { - if exists(getQualifier()) - then result = getQualifier().getFirstInstruction() - else result = getInstruction(OnlyInstructionTag()) + if exists(this.getQualifier()) + then result = this.getQualifier().getFirstInstruction() + else result = this.getInstruction(OnlyInstructionTag()) } override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { @@ -832,12 +840,12 @@ class TranslatedNonFieldVariableAccess extends TranslatedVariableAccess { class TranslatedFieldAccess extends TranslatedVariableAccess { override FieldAccess expr; - override Instruction getFirstInstruction() { result = getQualifier().getFirstInstruction() } + override Instruction getFirstInstruction() { result = this.getQualifier().getFirstInstruction() } override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { tag = OnlyInstructionTag() and operandTag instanceof UnaryOperandTag and - result = getQualifier().getResult() + result = this.getQualifier().getResult() } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { @@ -857,20 +865,20 @@ class TranslatedFunctionAccess extends TranslatedNonConstantExpr { override TranslatedElement getChild(int id) { none() } - override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) } + override Instruction getFirstInstruction() { result = this.getInstruction(OnlyInstructionTag()) } - override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) } + override Instruction getResult() { result = this.getInstruction(OnlyInstructionTag()) } final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = OnlyInstructionTag() and - result = getParent().getChildSuccessor(this) and + result = this.getParent().getChildSuccessor(this) and kind instanceof GotoEdge } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = OnlyInstructionTag() and opcode instanceof Opcode::FunctionAddress and - resultType = getResultType() + resultType = this.getResultType() } override Function getInstructionFunction(InstructionTag tag) { @@ -902,11 +910,13 @@ abstract class TranslatedConstantExpr extends TranslatedCoreExpr, TTranslatedVal isIRConstant(expr) } - final override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) } + final override Instruction getFirstInstruction() { + result = this.getInstruction(OnlyInstructionTag()) + } final override TranslatedElement getChild(int id) { none() } - final override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) } + final override Instruction getResult() { result = this.getInstruction(OnlyInstructionTag()) } final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { none() @@ -914,13 +924,13 @@ abstract class TranslatedConstantExpr extends TranslatedCoreExpr, TTranslatedVal final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = OnlyInstructionTag() and - opcode = getOpcode() and - resultType = getResultType() + opcode = this.getOpcode() and + resultType = this.getResultType() } final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = OnlyInstructionTag() and - result = getParent().getChildSuccessor(this) and + result = this.getParent().getChildSuccessor(this) and kind instanceof GotoEdge } @@ -962,12 +972,12 @@ abstract class TranslatedSingleInstructionExpr extends TranslatedNonConstantExpr abstract Opcode getOpcode(); final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { - opcode = getOpcode() and + opcode = this.getOpcode() and tag = OnlyInstructionTag() and - resultType = getResultType() + resultType = this.getResultType() } - final override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) } + final override Instruction getResult() { result = this.getInstruction(OnlyInstructionTag()) } } class TranslatedUnaryExpr extends TranslatedSingleInstructionExpr { @@ -978,23 +988,25 @@ class TranslatedUnaryExpr extends TranslatedSingleInstructionExpr { expr instanceof UnaryMinusExpr } - final override Instruction getFirstInstruction() { result = getOperand().getFirstInstruction() } + final override Instruction getFirstInstruction() { + result = this.getOperand().getFirstInstruction() + } - final override TranslatedElement getChild(int id) { id = 0 and result = getOperand() } + final override TranslatedElement getChild(int id) { id = 0 and result = this.getOperand() } final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = OnlyInstructionTag() and - result = getParent().getChildSuccessor(this) and + result = this.getParent().getChildSuccessor(this) and kind instanceof GotoEdge } final override Instruction getChildSuccessor(TranslatedElement child) { - child = getOperand() and result = getInstruction(OnlyInstructionTag()) + child = this.getOperand() and result = this.getInstruction(OnlyInstructionTag()) } final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { tag = OnlyInstructionTag() and - result = getOperand().getResult() and + result = this.getOperand().getResult() and operandTag instanceof UnaryOperandTag } @@ -1016,9 +1028,9 @@ class TranslatedUnaryExpr extends TranslatedSingleInstructionExpr { abstract class TranslatedConversion extends TranslatedNonConstantExpr { override Conversion expr; - override Instruction getFirstInstruction() { result = getOperand().getFirstInstruction() } + override Instruction getFirstInstruction() { result = this.getOperand().getFirstInstruction() } - final override TranslatedElement getChild(int id) { id = 0 and result = getOperand() } + final override TranslatedElement getChild(int id) { id = 0 and result = this.getOperand() } final TranslatedExpr getOperand() { result = getTranslatedExpr(expr.(Conversion).getExpr()) } } @@ -1030,26 +1042,26 @@ abstract class TranslatedConversion extends TranslatedNonConstantExpr { abstract class TranslatedSingleInstructionConversion extends TranslatedConversion { override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = OnlyInstructionTag() and - result = getParent().getChildSuccessor(this) and + result = this.getParent().getChildSuccessor(this) and kind instanceof GotoEdge } override Instruction getChildSuccessor(TranslatedElement child) { - child = getOperand() and result = getInstruction(OnlyInstructionTag()) + child = this.getOperand() and result = this.getInstruction(OnlyInstructionTag()) } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = OnlyInstructionTag() and - opcode = getOpcode() and - resultType = getResultType() + opcode = this.getOpcode() and + resultType = this.getResultType() } - override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) } + override Instruction getResult() { result = this.getInstruction(OnlyInstructionTag()) } override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { tag = OnlyInstructionTag() and operandTag instanceof UnaryOperandTag and - result = getOperand().getResult() + result = this.getOperand().getResult() } /** @@ -1133,37 +1145,37 @@ class TranslatedBoolConversion extends TranslatedConversion { kind instanceof GotoEdge and ( tag = BoolConversionConstantTag() and - result = getInstruction(BoolConversionCompareTag()) + result = this.getInstruction(BoolConversionCompareTag()) or tag = BoolConversionCompareTag() and - result = getParent().getChildSuccessor(this) + result = this.getParent().getChildSuccessor(this) ) } override Instruction getChildSuccessor(TranslatedElement child) { - child = getOperand() and result = getInstruction(BoolConversionConstantTag()) + child = this.getOperand() and result = this.getInstruction(BoolConversionConstantTag()) } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = BoolConversionConstantTag() and opcode instanceof Opcode::Constant and - resultType = getOperand().getResultType() + resultType = this.getOperand().getResultType() or tag = BoolConversionCompareTag() and opcode instanceof Opcode::CompareNE and resultType = getBoolType() } - override Instruction getResult() { result = getInstruction(BoolConversionCompareTag()) } + override Instruction getResult() { result = this.getInstruction(BoolConversionCompareTag()) } override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { tag = BoolConversionCompareTag() and ( operandTag instanceof LeftOperandTag and - result = getOperand().getResult() + result = this.getOperand().getResult() or operandTag instanceof RightOperandTag and - result = getInstruction(BoolConversionConstantTag()) + result = this.getInstruction(BoolConversionConstantTag()) ) } @@ -1250,44 +1262,46 @@ class TranslatedBinaryOperation extends TranslatedSingleInstructionExpr { expr instanceof ComparisonOperation } - override Instruction getFirstInstruction() { result = getLeftOperand().getFirstInstruction() } + override Instruction getFirstInstruction() { + result = this.getLeftOperand().getFirstInstruction() + } final override TranslatedElement getChild(int id) { - id = 0 and result = getLeftOperand() + id = 0 and result = this.getLeftOperand() or - id = 1 and result = getRightOperand() + id = 1 and result = this.getRightOperand() } final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { tag = OnlyInstructionTag() and - if swapOperandsOnOp() + if this.swapOperandsOnOp() then ( operandTag instanceof RightOperandTag and - result = getLeftOperand().getResult() + result = this.getLeftOperand().getResult() or operandTag instanceof LeftOperandTag and - result = getRightOperand().getResult() + result = this.getRightOperand().getResult() ) else ( operandTag instanceof LeftOperandTag and - result = getLeftOperand().getResult() + result = this.getLeftOperand().getResult() or operandTag instanceof RightOperandTag and - result = getRightOperand().getResult() + result = this.getRightOperand().getResult() ) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = OnlyInstructionTag() and - result = getParent().getChildSuccessor(this) and + result = this.getParent().getChildSuccessor(this) and kind instanceof GotoEdge } override Instruction getChildSuccessor(TranslatedElement child) { - child = getLeftOperand() and - result = getRightOperand().getFirstInstruction() + child = this.getLeftOperand() and + result = this.getRightOperand().getFirstInstruction() or - child = getRightOperand() and - result = getInstruction(OnlyInstructionTag()) + child = this.getRightOperand() and + result = this.getInstruction(OnlyInstructionTag()) } override Opcode getOpcode() { @@ -1299,18 +1313,20 @@ class TranslatedBinaryOperation extends TranslatedSingleInstructionExpr { override int getInstructionElementSize(InstructionTag tag) { tag = OnlyInstructionTag() and exists(Opcode opcode | - opcode = getOpcode() and + opcode = this.getOpcode() and ( opcode instanceof Opcode::PointerAdd or opcode instanceof Opcode::PointerSub or opcode instanceof Opcode::PointerDiff ) and - result = getElementSize(getPointerOperand().getExpr().getType()) + result = getElementSize(this.getPointerOperand().getExpr().getType()) ) } private TranslatedExpr getPointerOperand() { - if swapOperandsOnOp() then result = getRightOperand() else result = getLeftOperand() + if this.swapOperandsOnOp() + then result = this.getRightOperand() + else result = this.getLeftOperand() } private predicate swapOperandsOnOp() { @@ -1337,14 +1353,14 @@ class TranslatedAssignExpr extends TranslatedNonConstantExpr { override AssignExpr expr; final override TranslatedElement getChild(int id) { - id = 0 and result = getLeftOperand() + id = 0 and result = this.getLeftOperand() or - id = 1 and result = getRightOperand() + id = 1 and result = this.getRightOperand() } final override Instruction getFirstInstruction() { // Evaluation is right-to-left - result = getRightOperand().getFirstInstruction() + result = this.getRightOperand().getFirstInstruction() } final override Instruction getResult() { @@ -1354,11 +1370,11 @@ class TranslatedAssignExpr extends TranslatedNonConstantExpr { // value assigned to the left operand. If this is C++, then the result is // an lvalue, but that lvalue is being loaded as part of this expression. // EDG doesn't mark this as a load. - result = getRightOperand().getResult() + result = this.getRightOperand().getResult() else // This is C++, where the result is an lvalue for the left operand, // and that lvalue is not being loaded as part of this expression. - result = getLeftOperand().getResult() + result = this.getLeftOperand().getResult() } abstract Instruction getStoredValue(); @@ -1373,17 +1389,17 @@ class TranslatedAssignExpr extends TranslatedNonConstantExpr { override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = AssignmentStoreTag() and - result = getParent().getChildSuccessor(this) and + result = this.getParent().getChildSuccessor(this) and kind instanceof GotoEdge } override Instruction getChildSuccessor(TranslatedElement child) { // Operands are evaluated right-to-left. - child = getRightOperand() and - result = getLeftOperand().getFirstInstruction() + child = this.getRightOperand() and + result = this.getLeftOperand().getFirstInstruction() or - child = getLeftOperand() and - result = getInstruction(AssignmentStoreTag()) + child = this.getLeftOperand() and + result = this.getInstruction(AssignmentStoreTag()) } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { @@ -1396,10 +1412,10 @@ class TranslatedAssignExpr extends TranslatedNonConstantExpr { tag = AssignmentStoreTag() and ( operandTag instanceof AddressOperandTag and - result = getLeftOperand().getResult() + result = this.getLeftOperand().getResult() or operandTag instanceof StoreValueOperandTag and - result = getRightOperand().getResult() + result = this.getRightOperand().getResult() ) } } @@ -1408,14 +1424,14 @@ class TranslatedAssignOperation extends TranslatedNonConstantExpr { override AssignOperation expr; final override TranslatedElement getChild(int id) { - id = 0 and result = getLoadedLeftOperand() + id = 0 and result = this.getLoadedLeftOperand() or - id = 1 and result = getRightOperand() + id = 1 and result = this.getRightOperand() } final override Instruction getFirstInstruction() { // Evaluation is right-to-left - result = getRightOperand().getFirstInstruction() + result = this.getRightOperand().getFirstInstruction() } final override Instruction getResult() { @@ -1425,14 +1441,16 @@ class TranslatedAssignOperation extends TranslatedNonConstantExpr { // value assigned to the left operand. If this is C++, then the result is // an lvalue, but that lvalue is being loaded as part of this expression. // EDG doesn't mark this as a load. - result = getStoredValue() + result = this.getStoredValue() else // This is C++, where the result is an lvalue for the left operand, // and that lvalue is not being loaded as part of this expression. - result = getUnloadedLeftOperand().getResult() + result = this.getUnloadedLeftOperand().getResult() } - final TranslatedExpr getUnloadedLeftOperand() { result = getLoadedLeftOperand().getOperand() } + final TranslatedExpr getUnloadedLeftOperand() { + result = this.getLoadedLeftOperand().getOperand() + } /** * Gets the `TranslatedLoad` on the `e` in this `e += ...` which is the @@ -1454,38 +1472,38 @@ class TranslatedAssignOperation extends TranslatedNonConstantExpr { kind instanceof GotoEdge and ( tag = AssignOperationConvertLeftTag() and - result = getInstruction(AssignOperationOpTag()) + result = this.getInstruction(AssignOperationOpTag()) or ( tag = AssignOperationOpTag() and - if leftOperandNeedsConversion() - then result = getInstruction(AssignOperationConvertResultTag()) - else result = getInstruction(AssignmentStoreTag()) + if this.leftOperandNeedsConversion() + then result = this.getInstruction(AssignOperationConvertResultTag()) + else result = this.getInstruction(AssignmentStoreTag()) ) or tag = AssignOperationConvertResultTag() and - result = getInstruction(AssignmentStoreTag()) + result = this.getInstruction(AssignmentStoreTag()) or tag = AssignmentStoreTag() and - result = getParent().getChildSuccessor(this) + result = this.getParent().getChildSuccessor(this) ) } override Instruction getChildSuccessor(TranslatedElement child) { // Operands are evaluated right-to-left. - child = getRightOperand() and - result = getLoadedLeftOperand().getFirstInstruction() + child = this.getRightOperand() and + result = this.getLoadedLeftOperand().getFirstInstruction() or - child = getLoadedLeftOperand() and - if leftOperandNeedsConversion() - then result = getInstruction(AssignOperationConvertLeftTag()) - else result = getInstruction(AssignOperationOpTag()) + child = this.getLoadedLeftOperand() and + if this.leftOperandNeedsConversion() + then result = this.getInstruction(AssignOperationConvertLeftTag()) + else result = this.getInstruction(AssignOperationOpTag()) } private Instruction getStoredValue() { - if leftOperandNeedsConversion() - then result = getInstruction(AssignOperationConvertResultTag()) - else result = getInstruction(AssignOperationOpTag()) + if this.leftOperandNeedsConversion() + then result = this.getInstruction(AssignOperationConvertResultTag()) + else result = this.getInstruction(AssignOperationOpTag()) } private Type getConvertedLeftOperandType() { @@ -1502,15 +1520,15 @@ class TranslatedAssignOperation extends TranslatedNonConstantExpr { // anyway. If we really want to model this case perfectly, we'll need the // extractor to tell us what the promoted type of the left operand would // be. - result = getLoadedLeftOperand().getExpr().getType() + result = this.getLoadedLeftOperand().getExpr().getType() else // The right operand has already been converted to the type of the op. - result = getRightOperand().getExpr().getType() + result = this.getRightOperand().getExpr().getType() } private predicate leftOperandNeedsConversion() { - getConvertedLeftOperandType().getUnspecifiedType() != - getLoadedLeftOperand().getExpr().getUnspecifiedType() + this.getConvertedLeftOperandType().getUnspecifiedType() != + this.getLoadedLeftOperand().getExpr().getUnspecifiedType() } private Opcode getOpcode() { @@ -1541,64 +1559,64 @@ class TranslatedAssignOperation extends TranslatedNonConstantExpr { override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = AssignOperationOpTag() and - opcode = getOpcode() and - resultType = getTypeForPRValue(getConvertedLeftOperandType()) + opcode = this.getOpcode() and + resultType = getTypeForPRValue(this.getConvertedLeftOperandType()) or tag = AssignmentStoreTag() and opcode instanceof Opcode::Store and resultType = getTypeForPRValue(expr.getType()) // Always a prvalue or - leftOperandNeedsConversion() and + this.leftOperandNeedsConversion() and opcode instanceof Opcode::Convert and ( tag = AssignOperationConvertLeftTag() and - resultType = getTypeForPRValue(getConvertedLeftOperandType()) + resultType = getTypeForPRValue(this.getConvertedLeftOperandType()) or tag = AssignOperationConvertResultTag() and - resultType = getTypeForPRValue(getLoadedLeftOperand().getExpr().getType()) + resultType = getTypeForPRValue(this.getLoadedLeftOperand().getExpr().getType()) ) } override int getInstructionElementSize(InstructionTag tag) { tag = AssignOperationOpTag() and exists(Opcode opcode | - opcode = getOpcode() and + opcode = this.getOpcode() and (opcode instanceof Opcode::PointerAdd or opcode instanceof Opcode::PointerSub) ) and result = getElementSize(expr.getType()) } override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { - leftOperandNeedsConversion() and + this.leftOperandNeedsConversion() and tag = AssignOperationConvertLeftTag() and operandTag instanceof UnaryOperandTag and - result = getLoadedLeftOperand().getResult() + result = this.getLoadedLeftOperand().getResult() or tag = AssignOperationOpTag() and ( ( operandTag instanceof LeftOperandTag and - if leftOperandNeedsConversion() - then result = getInstruction(AssignOperationConvertLeftTag()) - else result = getLoadedLeftOperand().getResult() + if this.leftOperandNeedsConversion() + then result = this.getInstruction(AssignOperationConvertLeftTag()) + else result = this.getLoadedLeftOperand().getResult() ) or operandTag instanceof RightOperandTag and - result = getRightOperand().getResult() + result = this.getRightOperand().getResult() ) or - leftOperandNeedsConversion() and + this.leftOperandNeedsConversion() and tag = AssignOperationConvertResultTag() and operandTag instanceof UnaryOperandTag and - result = getInstruction(AssignOperationOpTag()) + result = this.getInstruction(AssignOperationOpTag()) or tag = AssignmentStoreTag() and ( operandTag instanceof AddressOperandTag and - result = getUnloadedLeftOperand().getResult() + result = this.getUnloadedLeftOperand().getResult() or operandTag instanceof StoreValueOperandTag and - result = getStoredValue() + result = this.getStoredValue() ) } } @@ -1619,7 +1637,7 @@ abstract class TranslatedAllocationSize extends TranslatedExpr, TTranslatedAlloc final override predicate producesExprResult() { none() } - final override Instruction getResult() { result = getInstruction(AllocationSizeTag()) } + final override Instruction getResult() { result = this.getInstruction(AllocationSizeTag()) } } TranslatedAllocationSize getTranslatedAllocationSize(NewOrNewArrayExpr newExpr) { @@ -1636,7 +1654,9 @@ TranslatedAllocationSize getTranslatedAllocationSize(NewOrNewArrayExpr newExpr) class TranslatedConstantAllocationSize extends TranslatedAllocationSize { TranslatedConstantAllocationSize() { not exists(expr.(NewArrayExpr).getExtent()) } - final override Instruction getFirstInstruction() { result = getInstruction(AllocationSizeTag()) } + final override Instruction getFirstInstruction() { + result = this.getInstruction(AllocationSizeTag()) + } final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = AllocationSizeTag() and @@ -1647,7 +1667,7 @@ class TranslatedConstantAllocationSize extends TranslatedAllocationSize { final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = AllocationSizeTag() and kind instanceof GotoEdge and - result = getParent().getChildSuccessor(this) + result = this.getParent().getChildSuccessor(this) } final override TranslatedElement getChild(int id) { none() } @@ -1672,7 +1692,9 @@ class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize { TranslatedNonConstantAllocationSize() { exists(expr.getExtent()) } - final override Instruction getFirstInstruction() { result = getExtent().getFirstInstruction() } + final override Instruction getFirstInstruction() { + result = this.getExtent().getFirstInstruction() + } final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { resultType = getTypeForPRValue(expr.getAllocator().getParameter(0).getType()) and @@ -1690,21 +1712,21 @@ class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize { kind instanceof GotoEdge and ( tag = AllocationExtentConvertTag() and - result = getInstruction(AllocationElementSizeTag()) + result = this.getInstruction(AllocationElementSizeTag()) or tag = AllocationElementSizeTag() and - result = getInstruction(AllocationSizeTag()) + result = this.getInstruction(AllocationSizeTag()) or tag = AllocationSizeTag() and - result = getParent().getChildSuccessor(this) + result = this.getParent().getChildSuccessor(this) ) } - final override TranslatedElement getChild(int id) { id = 0 and result = getExtent() } + final override TranslatedElement getChild(int id) { id = 0 and result = this.getExtent() } final override Instruction getChildSuccessor(TranslatedElement child) { - child = getExtent() and - result = getInstruction(AllocationExtentConvertTag()) + child = this.getExtent() and + result = this.getInstruction(AllocationExtentConvertTag()) } final override string getInstructionConstantValue(InstructionTag tag) { @@ -1715,14 +1737,16 @@ class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize { final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { tag = AllocationSizeTag() and ( - operandTag instanceof LeftOperandTag and result = getInstruction(AllocationExtentConvertTag()) + operandTag instanceof LeftOperandTag and + result = this.getInstruction(AllocationExtentConvertTag()) or - operandTag instanceof RightOperandTag and result = getInstruction(AllocationElementSizeTag()) + operandTag instanceof RightOperandTag and + result = this.getInstruction(AllocationElementSizeTag()) ) or tag = AllocationExtentConvertTag() and operandTag instanceof UnaryOperandTag and - result = getExtent().getResult() + result = this.getExtent().getResult() } private TranslatedExpr getExtent() { @@ -1806,7 +1830,7 @@ abstract class StructorCallContext extends TranslatedElement { class TranslatedDestructorFieldDestruction extends TranslatedNonConstantExpr, StructorCallContext { override DestructorFieldDestruction expr; - final override TranslatedElement getChild(int id) { id = 0 and result = getDestructorCall() } + final override TranslatedElement getChild(int id) { id = 0 and result = this.getDestructorCall() } final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = OnlyInstructionTag() and @@ -1817,17 +1841,19 @@ class TranslatedDestructorFieldDestruction extends TranslatedNonConstantExpr, St final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = OnlyInstructionTag() and kind instanceof GotoEdge and - result = getDestructorCall().getFirstInstruction() + result = this.getDestructorCall().getFirstInstruction() } final override Instruction getChildSuccessor(TranslatedElement child) { - child = getDestructorCall() and - result = getParent().getChildSuccessor(this) + child = this.getDestructorCall() and + result = this.getParent().getChildSuccessor(this) } final override Instruction getResult() { none() } - final override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) } + final override Instruction getFirstInstruction() { + result = this.getInstruction(OnlyInstructionTag()) + } final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { tag = OnlyInstructionTag() and @@ -1840,7 +1866,7 @@ class TranslatedDestructorFieldDestruction extends TranslatedNonConstantExpr, St result = expr.getTarget() } - final override Instruction getReceiver() { result = getInstruction(OnlyInstructionTag()) } + final override Instruction getReceiver() { result = this.getInstruction(OnlyInstructionTag()) } private TranslatedExpr getDestructorCall() { result = getTranslatedExpr(expr.getExpr()) } } @@ -1859,12 +1885,12 @@ abstract class TranslatedConditionalExpr extends TranslatedNonConstantExpr { // the condition directly to the appropriate then/else block via // `getChild[True|False]Successor()`. // The binary flavor will override this predicate to add the `ConditionalBranch`. - not resultIsVoid() and + not this.resultIsVoid() and ( ( - not thenIsVoid() and tag = ConditionValueTrueTempAddressTag() + not this.thenIsVoid() and tag = ConditionValueTrueTempAddressTag() or - not elseIsVoid() and tag = ConditionValueFalseTempAddressTag() + not this.elseIsVoid() and tag = ConditionValueFalseTempAddressTag() or tag = ConditionValueResultTempAddressTag() ) and @@ -1876,106 +1902,106 @@ abstract class TranslatedConditionalExpr extends TranslatedNonConstantExpr { ) or ( - not thenIsVoid() and tag = ConditionValueTrueStoreTag() + not this.thenIsVoid() and tag = ConditionValueTrueStoreTag() or - not elseIsVoid() and tag = ConditionValueFalseStoreTag() + not this.elseIsVoid() and tag = ConditionValueFalseStoreTag() ) and opcode instanceof Opcode::Store and - resultType = getResultType() + resultType = this.getResultType() or tag = ConditionValueResultLoadTag() and opcode instanceof Opcode::Load and - resultType = getResultType() + resultType = this.getResultType() ) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { - not resultIsVoid() and + not this.resultIsVoid() and kind instanceof GotoEdge and ( - not thenIsVoid() and + not this.thenIsVoid() and ( tag = ConditionValueTrueTempAddressTag() and - result = getInstruction(ConditionValueTrueStoreTag()) + result = this.getInstruction(ConditionValueTrueStoreTag()) or tag = ConditionValueTrueStoreTag() and - result = getInstruction(ConditionValueResultTempAddressTag()) + result = this.getInstruction(ConditionValueResultTempAddressTag()) ) or - not elseIsVoid() and + not this.elseIsVoid() and ( tag = ConditionValueFalseTempAddressTag() and - result = getInstruction(ConditionValueFalseStoreTag()) + result = this.getInstruction(ConditionValueFalseStoreTag()) or tag = ConditionValueFalseStoreTag() and - result = getInstruction(ConditionValueResultTempAddressTag()) + result = this.getInstruction(ConditionValueResultTempAddressTag()) ) or tag = ConditionValueResultTempAddressTag() and - result = getInstruction(ConditionValueResultLoadTag()) + result = this.getInstruction(ConditionValueResultLoadTag()) or tag = ConditionValueResultLoadTag() and - result = getParent().getChildSuccessor(this) + result = this.getParent().getChildSuccessor(this) ) } override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { - not resultIsVoid() and + not this.resultIsVoid() and ( - not thenIsVoid() and + not this.thenIsVoid() and tag = ConditionValueTrueStoreTag() and ( operandTag instanceof AddressOperandTag and - result = getInstruction(ConditionValueTrueTempAddressTag()) + result = this.getInstruction(ConditionValueTrueTempAddressTag()) or operandTag instanceof StoreValueOperandTag and - result = getThen().getResult() + result = this.getThen().getResult() ) or - not elseIsVoid() and + not this.elseIsVoid() and tag = ConditionValueFalseStoreTag() and ( operandTag instanceof AddressOperandTag and - result = getInstruction(ConditionValueFalseTempAddressTag()) + result = this.getInstruction(ConditionValueFalseTempAddressTag()) or operandTag instanceof StoreValueOperandTag and - result = getElse().getResult() + result = this.getElse().getResult() ) or tag = ConditionValueResultLoadTag() and ( operandTag instanceof AddressOperandTag and - result = getInstruction(ConditionValueResultTempAddressTag()) + result = this.getInstruction(ConditionValueResultTempAddressTag()) ) ) } final override predicate hasTempVariable(TempVariableTag tag, CppType type) { - not resultIsVoid() and + not this.resultIsVoid() and tag = ConditionValueTempVar() and - type = getResultType() + type = this.getResultType() } final override IRVariable getInstructionVariable(InstructionTag tag) { - not resultIsVoid() and + not this.resultIsVoid() and ( tag = ConditionValueTrueTempAddressTag() or tag = ConditionValueFalseTempAddressTag() or tag = ConditionValueResultTempAddressTag() ) and - result = getTempVariable(ConditionValueTempVar()) + result = this.getTempVariable(ConditionValueTempVar()) } final override Instruction getResult() { - not resultIsVoid() and - result = getInstruction(ConditionValueResultLoadTag()) + not this.resultIsVoid() and + result = this.getInstruction(ConditionValueResultLoadTag()) } override Instruction getChildSuccessor(TranslatedElement child) { - child = getElse() and - if elseIsVoid() - then result = getParent().getChildSuccessor(this) - else result = getInstruction(ConditionValueFalseTempAddressTag()) + child = this.getElse() and + if this.elseIsVoid() + then result = this.getParent().getChildSuccessor(this) + else result = this.getInstruction(ConditionValueFalseTempAddressTag()) } /** @@ -1990,7 +2016,7 @@ abstract class TranslatedConditionalExpr extends TranslatedNonConstantExpr { final TranslatedExpr getElse() { result = getTranslatedExpr(expr.getElse().getFullyConverted()) } final predicate thenIsVoid() { - getThen().getResultType().getIRType() instanceof IRVoidType + this.getThen().getResultType().getIRType() instanceof IRVoidType or // A `ThrowExpr.getType()` incorrectly returns the type of exception being // thrown, rather than `void`. Handle that case here. @@ -1998,14 +2024,14 @@ abstract class TranslatedConditionalExpr extends TranslatedNonConstantExpr { } private predicate elseIsVoid() { - getElse().getResultType().getIRType() instanceof IRVoidType + this.getElse().getResultType().getIRType() instanceof IRVoidType or // A `ThrowExpr.getType()` incorrectly returns the type of exception being // thrown, rather than `void`. Handle that case here. expr.getElse() instanceof ThrowExpr } - private predicate resultIsVoid() { getResultType().getIRType() instanceof IRVoidType } + private predicate resultIsVoid() { this.getResultType().getIRType() instanceof IRVoidType } } /** @@ -2017,34 +2043,34 @@ class TranslatedTernaryConditionalExpr extends TranslatedConditionalExpr, Condit TranslatedTernaryConditionalExpr() { not expr.isTwoOperand() } final override TranslatedElement getChild(int id) { - id = 0 and result = getCondition() + id = 0 and result = this.getCondition() or - id = 1 and result = getThen() + id = 1 and result = this.getThen() or - id = 2 and result = getElse() + id = 2 and result = this.getElse() } - override Instruction getFirstInstruction() { result = getCondition().getFirstInstruction() } + override Instruction getFirstInstruction() { result = this.getCondition().getFirstInstruction() } override Instruction getChildSuccessor(TranslatedElement child) { result = TranslatedConditionalExpr.super.getChildSuccessor(child) or ( - child = getThen() and - if thenIsVoid() - then result = getParent().getChildSuccessor(this) - else result = getInstruction(ConditionValueTrueTempAddressTag()) + child = this.getThen() and + if this.thenIsVoid() + then result = this.getParent().getChildSuccessor(this) + else result = this.getInstruction(ConditionValueTrueTempAddressTag()) ) } override Instruction getChildTrueSuccessor(TranslatedCondition child) { - child = getCondition() and - result = getThen().getFirstInstruction() + child = this.getCondition() and + result = this.getThen().getFirstInstruction() } override Instruction getChildFalseSuccessor(TranslatedCondition child) { - child = getCondition() and - result = getElse().getFirstInstruction() + child = this.getCondition() and + result = this.getElse().getFirstInstruction() } private TranslatedCondition getCondition() { @@ -2069,12 +2095,12 @@ class TranslatedBinaryConditionalExpr extends TranslatedConditionalExpr { final override TranslatedElement getChild(int id) { // We only truly have two children, because our "condition" and "then" are the same as far as // the extractor is concerned. - id = 0 and result = getCondition() + id = 0 and result = this.getCondition() or - id = 1 and result = getElse() + id = 1 and result = this.getElse() } - override Instruction getFirstInstruction() { result = getCondition().getFirstInstruction() } + override Instruction getFirstInstruction() { result = this.getCondition().getFirstInstruction() } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { super.hasInstruction(opcode, tag, resultType) @@ -2091,10 +2117,10 @@ class TranslatedBinaryConditionalExpr extends TranslatedConditionalExpr { tag = ValueConditionConditionalBranchTag() and ( kind instanceof TrueEdge and - result = getInstruction(ConditionValueTrueTempAddressTag()) + result = this.getInstruction(ConditionValueTrueTempAddressTag()) or kind instanceof FalseEdge and - result = getElse().getFirstInstruction() + result = this.getElse().getFirstInstruction() ) } @@ -2103,13 +2129,14 @@ class TranslatedBinaryConditionalExpr extends TranslatedConditionalExpr { or tag = ValueConditionConditionalBranchTag() and operandTag instanceof ConditionOperandTag and - result = getCondition().getResult() + result = this.getCondition().getResult() } override Instruction getChildSuccessor(TranslatedElement child) { result = super.getChildSuccessor(child) or - child = getCondition() and result = getInstruction(ValueConditionConditionalBranchTag()) + child = this.getCondition() and + result = this.getInstruction(ValueConditionConditionalBranchTag()) } private TranslatedExpr getCondition() { @@ -2154,10 +2181,10 @@ class TranslatedTemporaryObjectExpr extends TranslatedNonConstantExpr, } final override Instruction getInitializationSuccessor() { - result = getParent().getChildSuccessor(this) + result = this.getParent().getChildSuccessor(this) } - final override Instruction getResult() { result = getTargetAddress() } + final override Instruction getResult() { result = this.getTargetAddress() } } /** @@ -2168,14 +2195,14 @@ abstract class TranslatedThrowExpr extends TranslatedNonConstantExpr { override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = ThrowTag() and - opcode = getThrowOpcode() and + opcode = this.getThrowOpcode() and resultType = getVoidType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = ThrowTag() and kind instanceof ExceptionEdge and - result = getParent().getExceptionSuccessorInstruction() + result = this.getParent().getExceptionSuccessorInstruction() } override Instruction getResult() { none() } @@ -2202,11 +2229,13 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr, TranslatedVariableIn result = TranslatedVariableInitialization.super.getInstructionSuccessor(tag, kind) } - final override Instruction getInitializationSuccessor() { result = getInstruction(ThrowTag()) } + final override Instruction getInitializationSuccessor() { + result = this.getInstruction(ThrowTag()) + } final override predicate hasTempVariable(TempVariableTag tag, CppType type) { tag = ThrowTempVar() and - type = getTypeForPRValue(getExceptionType()) + type = getTypeForPRValue(this.getExceptionType()) } final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { @@ -2215,7 +2244,7 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr, TranslatedVariableIn tag = ThrowTag() and ( operandTag instanceof AddressOperandTag and - result = getInstruction(InitializerVariableAddressTag()) + result = this.getInstruction(InitializerVariableAddressTag()) ) } @@ -2224,10 +2253,10 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr, TranslatedVariableIn ) { tag = ThrowTag() and operandTag instanceof LoadOperandTag and - result = getTypeForPRValue(getExceptionType()) + result = getTypeForPRValue(this.getExceptionType()) } - override Type getTargetType() { result = getExceptionType() } + override Type getTargetType() { result = this.getExceptionType() } final override TranslatedInitialization getInitialization() { result = getTranslatedInitialization(expr.getExpr().getFullyConverted()) @@ -2248,7 +2277,7 @@ class TranslatedReThrowExpr extends TranslatedThrowExpr { override TranslatedElement getChild(int id) { none() } - override Instruction getFirstInstruction() { result = getInstruction(ThrowTag()) } + override Instruction getFirstInstruction() { result = this.getInstruction(ThrowTag()) } override Instruction getChildSuccessor(TranslatedElement child) { none() } @@ -2271,12 +2300,12 @@ class TranslatedBuiltInOperation extends TranslatedNonConstantExpr { not expr instanceof BuiltInVarArgCopy } - final override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) } + final override Instruction getResult() { result = this.getInstruction(OnlyInstructionTag()) } final override Instruction getFirstInstruction() { - if exists(getChild(0)) - then result = getChild(0).getFirstInstruction() - else result = getInstruction(OnlyInstructionTag()) + if exists(this.getChild(0)) + then result = this.getChild(0).getFirstInstruction() + else result = this.getInstruction(OnlyInstructionTag()) } final override TranslatedElement getChild(int id) { @@ -2286,31 +2315,31 @@ class TranslatedBuiltInOperation extends TranslatedNonConstantExpr { final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = OnlyInstructionTag() and kind instanceof GotoEdge and - result = getParent().getChildSuccessor(this) + result = this.getParent().getChildSuccessor(this) } final override Instruction getChildSuccessor(TranslatedElement child) { exists(int id | - child = getChild(id) and + child = this.getChild(id) and ( - result = getChild(id + 1).getFirstInstruction() + result = this.getChild(id + 1).getFirstInstruction() or - not exists(getChild(id + 1)) and result = getInstruction(OnlyInstructionTag()) + not exists(this.getChild(id + 1)) and result = this.getInstruction(OnlyInstructionTag()) ) ) } final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = OnlyInstructionTag() and - opcode = getOpcode() and - resultType = getResultType() + opcode = this.getOpcode() and + resultType = this.getResultType() } final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { tag = OnlyInstructionTag() and exists(int index | operandTag = positionalArgumentOperand(index) and - result = getChild(index).(TranslatedExpr).getResult() + result = this.getChild(index).(TranslatedExpr).getResult() ) } @@ -2391,12 +2420,12 @@ class TranslatedVarArgsStart extends TranslatedNonConstantExpr { } final override Instruction getFirstInstruction() { - result = getInstruction(VarArgsStartEllipsisAddressTag()) + result = this.getInstruction(VarArgsStartEllipsisAddressTag()) } final override Instruction getResult() { none() } - final override TranslatedElement getChild(int id) { id = 0 and result = getVAList() } + final override TranslatedElement getChild(int id) { id = 0 and result = this.getVAList() } private TranslatedExpr getVAList() { result = getTranslatedExpr(expr.getVAList().getFullyConverted()) @@ -2405,37 +2434,37 @@ class TranslatedVarArgsStart extends TranslatedNonConstantExpr { final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = VarArgsStartEllipsisAddressTag() and kind instanceof GotoEdge and - result = getInstruction(VarArgsStartTag()) + result = this.getInstruction(VarArgsStartTag()) or tag = VarArgsStartTag() and kind instanceof GotoEdge and - result = getVAList().getFirstInstruction() + result = this.getVAList().getFirstInstruction() or tag = VarArgsVAListStoreTag() and kind instanceof GotoEdge and - result = getParent().getChildSuccessor(this) + result = this.getParent().getChildSuccessor(this) } final override Instruction getChildSuccessor(TranslatedElement child) { - child = getVAList() and - result = getInstruction(VarArgsVAListStoreTag()) + child = this.getVAList() and + result = this.getInstruction(VarArgsVAListStoreTag()) } final override IRVariable getInstructionVariable(InstructionTag tag) { tag = VarArgsStartEllipsisAddressTag() and - result = getEnclosingFunction().getEllipsisVariable() + result = this.getEnclosingFunction().getEllipsisVariable() } final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { tag = VarArgsStartTag() and operandTag instanceof UnaryOperandTag and - result = getInstruction(VarArgsStartEllipsisAddressTag()) + result = this.getInstruction(VarArgsStartEllipsisAddressTag()) or tag = VarArgsVAListStoreTag() and ( - operandTag instanceof AddressOperandTag and result = getVAList().getResult() + operandTag instanceof AddressOperandTag and result = this.getVAList().getResult() or - operandTag instanceof StoreValueOperandTag and result = getInstruction(VarArgsStartTag()) + operandTag instanceof StoreValueOperandTag and result = this.getInstruction(VarArgsStartTag()) ) } } @@ -2453,7 +2482,7 @@ class TranslatedVarArg extends TranslatedNonConstantExpr { or tag = VarArgsArgAddressTag() and opcode instanceof Opcode::VarArg and - resultType = getResultType() + resultType = this.getResultType() or tag = VarArgsMoveNextTag() and opcode instanceof Opcode::NextVarArg and @@ -2464,11 +2493,13 @@ class TranslatedVarArg extends TranslatedNonConstantExpr { resultType = getTypeForPRValue(getVAListType(expr.getVAList().getFullyConverted())) } - final override Instruction getFirstInstruction() { result = getVAList().getFirstInstruction() } + final override Instruction getFirstInstruction() { + result = this.getVAList().getFirstInstruction() + } - final override Instruction getResult() { result = getInstruction(VarArgsArgAddressTag()) } + final override Instruction getResult() { result = this.getInstruction(VarArgsArgAddressTag()) } - final override TranslatedElement getChild(int id) { id = 0 and result = getVAList() } + final override TranslatedElement getChild(int id) { id = 0 and result = this.getVAList() } private TranslatedExpr getVAList() { result = getTranslatedExpr(expr.getVAList().getFullyConverted()) @@ -2477,46 +2508,47 @@ class TranslatedVarArg extends TranslatedNonConstantExpr { final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = VarArgsVAListLoadTag() and kind instanceof GotoEdge and - result = getInstruction(VarArgsArgAddressTag()) + result = this.getInstruction(VarArgsArgAddressTag()) or tag = VarArgsArgAddressTag() and kind instanceof GotoEdge and - result = getInstruction(VarArgsMoveNextTag()) + result = this.getInstruction(VarArgsMoveNextTag()) or tag = VarArgsMoveNextTag() and kind instanceof GotoEdge and - result = getInstruction(VarArgsVAListStoreTag()) + result = this.getInstruction(VarArgsVAListStoreTag()) or tag = VarArgsVAListStoreTag() and kind instanceof GotoEdge and - result = getParent().getChildSuccessor(this) + result = this.getParent().getChildSuccessor(this) } final override Instruction getChildSuccessor(TranslatedElement child) { - child = getVAList() and - result = getInstruction(VarArgsVAListLoadTag()) + child = this.getVAList() and + result = this.getInstruction(VarArgsVAListLoadTag()) } final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { tag = VarArgsVAListLoadTag() and ( operandTag instanceof AddressOperandTag and - result = getVAList().getResult() + result = this.getVAList().getResult() ) or tag = VarArgsArgAddressTag() and operandTag instanceof UnaryOperandTag and - result = getInstruction(VarArgsVAListLoadTag()) + result = this.getInstruction(VarArgsVAListLoadTag()) or tag = VarArgsMoveNextTag() and operandTag instanceof UnaryOperandTag and - result = getInstruction(VarArgsVAListLoadTag()) + result = this.getInstruction(VarArgsVAListLoadTag()) or tag = VarArgsVAListStoreTag() and ( - operandTag instanceof AddressOperandTag and result = getVAList().getResult() + operandTag instanceof AddressOperandTag and result = this.getVAList().getResult() or - operandTag instanceof StoreValueOperandTag and result = getInstruction(VarArgsMoveNextTag()) + operandTag instanceof StoreValueOperandTag and + result = this.getInstruction(VarArgsMoveNextTag()) ) } } @@ -2533,11 +2565,13 @@ class TranslatedVarArgsEnd extends TranslatedNonConstantExpr { resultType = getVoidType() } - final override Instruction getFirstInstruction() { result = getVAList().getFirstInstruction() } + final override Instruction getFirstInstruction() { + result = this.getVAList().getFirstInstruction() + } final override Instruction getResult() { none() } - final override TranslatedElement getChild(int id) { id = 0 and result = getVAList() } + final override TranslatedElement getChild(int id) { id = 0 and result = this.getVAList() } private TranslatedExpr getVAList() { result = getTranslatedExpr(expr.getVAList().getFullyConverted()) @@ -2546,18 +2580,18 @@ class TranslatedVarArgsEnd extends TranslatedNonConstantExpr { final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = OnlyInstructionTag() and kind instanceof GotoEdge and - result = getParent().getChildSuccessor(this) + result = this.getParent().getChildSuccessor(this) } final override Instruction getChildSuccessor(TranslatedElement child) { - child = getVAList() and - result = getInstruction(OnlyInstructionTag()) + child = this.getVAList() and + result = this.getInstruction(OnlyInstructionTag()) } final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { tag = OnlyInstructionTag() and operandTag instanceof UnaryOperandTag and - result = getVAList().getResult() + result = this.getVAList().getResult() } } @@ -2578,15 +2612,15 @@ class TranslatedVarArgCopy extends TranslatedNonConstantExpr { } final override Instruction getFirstInstruction() { - result = getSourceVAList().getFirstInstruction() + result = this.getSourceVAList().getFirstInstruction() } - final override Instruction getResult() { result = getInstruction(VarArgsVAListStoreTag()) } + final override Instruction getResult() { result = this.getInstruction(VarArgsVAListStoreTag()) } final override TranslatedElement getChild(int id) { - id = 0 and result = getDestinationVAList() + id = 0 and result = this.getDestinationVAList() or - id = 1 and result = getSourceVAList() + id = 1 and result = this.getSourceVAList() } private TranslatedExpr getDestinationVAList() { @@ -2600,33 +2634,34 @@ class TranslatedVarArgCopy extends TranslatedNonConstantExpr { final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = VarArgsVAListLoadTag() and kind instanceof GotoEdge and - result = getDestinationVAList().getFirstInstruction() + result = this.getDestinationVAList().getFirstInstruction() or tag = VarArgsVAListStoreTag() and kind instanceof GotoEdge and - result = getParent().getChildSuccessor(this) + result = this.getParent().getChildSuccessor(this) } final override Instruction getChildSuccessor(TranslatedElement child) { - child = getSourceVAList() and - result = getInstruction(VarArgsVAListLoadTag()) + child = this.getSourceVAList() and + result = this.getInstruction(VarArgsVAListLoadTag()) or - child = getDestinationVAList() and - result = getInstruction(VarArgsVAListStoreTag()) + child = this.getDestinationVAList() and + result = this.getInstruction(VarArgsVAListStoreTag()) } final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { tag = VarArgsVAListLoadTag() and ( operandTag instanceof AddressOperandTag and - result = getSourceVAList().getResult() + result = this.getSourceVAList().getResult() ) or tag = VarArgsVAListStoreTag() and ( - operandTag instanceof AddressOperandTag and result = getDestinationVAList().getResult() + operandTag instanceof AddressOperandTag and result = this.getDestinationVAList().getResult() or - operandTag instanceof StoreValueOperandTag and result = getInstruction(VarArgsVAListLoadTag()) + operandTag instanceof StoreValueOperandTag and + result = this.getInstruction(VarArgsVAListLoadTag()) ) } } @@ -2638,44 +2673,46 @@ abstract class TranslatedNewOrNewArrayExpr extends TranslatedNonConstantExpr, In override NewOrNewArrayExpr expr; final override TranslatedElement getChild(int id) { - id = 0 and result = getAllocatorCall() + id = 0 and result = this.getAllocatorCall() or - id = 1 and result = getInitialization() + id = 1 and result = this.getInitialization() } final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = OnlyInstructionTag() and opcode instanceof Opcode::Convert and - resultType = getResultType() + resultType = this.getResultType() } final override Instruction getFirstInstruction() { - result = getAllocatorCall().getFirstInstruction() + result = this.getAllocatorCall().getFirstInstruction() } - final override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) } + final override Instruction getResult() { result = this.getInstruction(OnlyInstructionTag()) } final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { kind instanceof GotoEdge and tag = OnlyInstructionTag() and - if exists(getInitialization()) - then result = getInitialization().getFirstInstruction() - else result = getParent().getChildSuccessor(this) + if exists(this.getInitialization()) + then result = this.getInitialization().getFirstInstruction() + else result = this.getParent().getChildSuccessor(this) } final override Instruction getChildSuccessor(TranslatedElement child) { - child = getAllocatorCall() and result = getInstruction(OnlyInstructionTag()) + child = this.getAllocatorCall() and result = this.getInstruction(OnlyInstructionTag()) or - child = getInitialization() and result = getParent().getChildSuccessor(this) + child = this.getInitialization() and result = this.getParent().getChildSuccessor(this) } final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { tag = OnlyInstructionTag() and operandTag instanceof UnaryOperandTag and - result = getAllocatorCall().getResult() + result = this.getAllocatorCall().getResult() } - final override Instruction getTargetAddress() { result = getInstruction(OnlyInstructionTag()) } + final override Instruction getTargetAddress() { + result = this.getInstruction(OnlyInstructionTag()) + } private TranslatedAllocatorCall getAllocatorCall() { result = getTranslatedAllocatorCall(expr) } @@ -2718,18 +2755,20 @@ class TranslatedNewArrayExpr extends TranslatedNewOrNewArrayExpr { class TranslatedDeleteArrayExprPlaceHolder extends TranslatedSingleInstructionExpr { override DeleteArrayExpr expr; - final override Instruction getFirstInstruction() { result = getOperand().getFirstInstruction() } + final override Instruction getFirstInstruction() { + result = this.getOperand().getFirstInstruction() + } - final override TranslatedElement getChild(int id) { id = 0 and result = getOperand() } + final override TranslatedElement getChild(int id) { id = 0 and result = this.getOperand() } final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = OnlyInstructionTag() and - result = getParent().getChildSuccessor(this) and + result = this.getParent().getChildSuccessor(this) and kind instanceof GotoEdge } final override Instruction getChildSuccessor(TranslatedElement child) { - child = getOperand() and result = getInstruction(OnlyInstructionTag()) + child = this.getOperand() and result = this.getInstruction(OnlyInstructionTag()) } final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { @@ -2752,18 +2791,20 @@ class TranslatedDeleteArrayExprPlaceHolder extends TranslatedSingleInstructionEx class TranslatedDeleteExprPlaceHolder extends TranslatedSingleInstructionExpr { override DeleteExpr expr; - final override Instruction getFirstInstruction() { result = getOperand().getFirstInstruction() } + final override Instruction getFirstInstruction() { + result = this.getOperand().getFirstInstruction() + } - final override TranslatedElement getChild(int id) { id = 0 and result = getOperand() } + final override TranslatedElement getChild(int id) { id = 0 and result = this.getOperand() } final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = OnlyInstructionTag() and - result = getParent().getChildSuccessor(this) and + result = this.getParent().getChildSuccessor(this) and kind instanceof GotoEdge } final override Instruction getChildSuccessor(TranslatedElement child) { - child = getOperand() and result = getInstruction(OnlyInstructionTag()) + child = this.getOperand() and result = this.getInstruction(OnlyInstructionTag()) } final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { @@ -2788,23 +2829,23 @@ class TranslatedDeleteExprPlaceHolder extends TranslatedSingleInstructionExpr { class TranslatedConditionDeclExpr extends TranslatedNonConstantExpr { override ConditionDeclExpr expr; - final override Instruction getFirstInstruction() { result = getDecl().getFirstInstruction() } + final override Instruction getFirstInstruction() { result = this.getDecl().getFirstInstruction() } final override TranslatedElement getChild(int id) { - id = 0 and result = getDecl() + id = 0 and result = this.getDecl() or - id = 1 and result = getConditionExpr() + id = 1 and result = this.getConditionExpr() } - override Instruction getResult() { result = getConditionExpr().getResult() } + override Instruction getResult() { result = this.getConditionExpr().getResult() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() } override Instruction getChildSuccessor(TranslatedElement child) { - child = getDecl() and - result = getConditionExpr().getFirstInstruction() + child = this.getDecl() and + result = this.getConditionExpr().getFirstInstruction() or - child = getConditionExpr() and result = getParent().getChildSuccessor(this) + child = this.getConditionExpr() and result = this.getParent().getChildSuccessor(this) } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { @@ -2826,34 +2867,34 @@ class TranslatedLambdaExpr extends TranslatedNonConstantExpr, InitializationCont override LambdaExpression expr; final override Instruction getFirstInstruction() { - result = getInstruction(InitializerVariableAddressTag()) + result = this.getInstruction(InitializerVariableAddressTag()) } - final override TranslatedElement getChild(int id) { id = 0 and result = getInitialization() } + final override TranslatedElement getChild(int id) { id = 0 and result = this.getInitialization() } - override Instruction getResult() { result = getInstruction(LoadTag()) } + override Instruction getResult() { result = this.getInstruction(LoadTag()) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = InitializerVariableAddressTag() and kind instanceof GotoEdge and - result = getInstruction(InitializerStoreTag()) + result = this.getInstruction(InitializerStoreTag()) or tag = InitializerStoreTag() and kind instanceof GotoEdge and ( - result = getInitialization().getFirstInstruction() + result = this.getInitialization().getFirstInstruction() or - not hasInitializer() and result = getInstruction(LoadTag()) + not this.hasInitializer() and result = this.getInstruction(LoadTag()) ) or tag = LoadTag() and kind instanceof GotoEdge and - result = getParent().getChildSuccessor(this) + result = this.getParent().getChildSuccessor(this) } override Instruction getChildSuccessor(TranslatedElement child) { - child = getInitialization() and - result = getInstruction(LoadTag()) + child = this.getInitialization() and + result = this.getInstruction(LoadTag()) } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { @@ -2873,12 +2914,12 @@ class TranslatedLambdaExpr extends TranslatedNonConstantExpr, InitializationCont override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { tag = InitializerStoreTag() and operandTag instanceof AddressOperandTag and - result = getInstruction(InitializerVariableAddressTag()) + result = this.getInstruction(InitializerVariableAddressTag()) or tag = LoadTag() and ( operandTag instanceof AddressOperandTag and - result = getInstruction(InitializerVariableAddressTag()) + result = this.getInstruction(InitializerVariableAddressTag()) ) } @@ -2887,7 +2928,7 @@ class TranslatedLambdaExpr extends TranslatedNonConstantExpr, InitializationCont tag = InitializerVariableAddressTag() or tag = InitializerStoreTag() ) and - result = getTempVariable(LambdaTempVar()) + result = this.getTempVariable(LambdaTempVar()) } override predicate hasTempVariable(TempVariableTag tag, CppType type) { @@ -2896,12 +2937,12 @@ class TranslatedLambdaExpr extends TranslatedNonConstantExpr, InitializationCont } final override Instruction getTargetAddress() { - result = getInstruction(InitializerVariableAddressTag()) + result = this.getInstruction(InitializerVariableAddressTag()) } final override Type getTargetType() { result = expr.getType() } - private predicate hasInitializer() { exists(getInitialization()) } + private predicate hasInitializer() { exists(this.getInitialization()) } private TranslatedInitialization getInitialization() { result = getTranslatedInitialization(expr.getChild(0).getFullyConverted()) @@ -2915,28 +2956,28 @@ class TranslatedLambdaExpr extends TranslatedNonConstantExpr, InitializationCont class TranslatedStmtExpr extends TranslatedNonConstantExpr { override StmtExpr expr; - final override Instruction getFirstInstruction() { result = getStmt().getFirstInstruction() } + final override Instruction getFirstInstruction() { result = this.getStmt().getFirstInstruction() } - final override TranslatedElement getChild(int id) { id = 0 and result = getStmt() } + final override TranslatedElement getChild(int id) { id = 0 and result = this.getStmt() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag instanceof OnlyInstructionTag and kind instanceof GotoEdge and - result = getParent().getChildSuccessor(this) + result = this.getParent().getChildSuccessor(this) } override Instruction getChildSuccessor(TranslatedElement child) { - child = getStmt() and - result = getInstruction(OnlyInstructionTag()) + child = this.getStmt() and + result = this.getInstruction(OnlyInstructionTag()) } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { opcode instanceof Opcode::CopyValue and tag instanceof OnlyInstructionTag and - resultType = getResultType() + resultType = this.getResultType() } - override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) } + override Instruction getResult() { result = this.getInstruction(OnlyInstructionTag()) } override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { tag instanceof OnlyInstructionTag and @@ -2950,13 +2991,15 @@ class TranslatedStmtExpr extends TranslatedNonConstantExpr { class TranslatedErrorExpr extends TranslatedSingleInstructionExpr { override ErrorExpr expr; - final override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) } + final override Instruction getFirstInstruction() { + result = this.getInstruction(OnlyInstructionTag()) + } final override TranslatedElement getChild(int id) { none() } final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = OnlyInstructionTag() and - result = getParent().getChildSuccessor(this) and + result = this.getParent().getChildSuccessor(this) and kind instanceof GotoEdge } @@ -3034,13 +3077,15 @@ class TranslatedAssumeExpr extends TranslatedSingleInstructionExpr { final override Opcode getOpcode() { result instanceof Opcode::NoOp } - final override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) } + final override Instruction getFirstInstruction() { + result = this.getInstruction(OnlyInstructionTag()) + } final override TranslatedElement getChild(int id) { none() } final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = OnlyInstructionTag() and - result = getParent().getChildSuccessor(this) and + result = this.getParent().getChildSuccessor(this) and kind instanceof GotoEdge } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll index 4b86f9a7cec..bb8630a5e0c 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll @@ -24,7 +24,7 @@ class IRBlockBase extends TIRBlock { final string toString() { result = getFirstInstruction(this).toString() } /** Gets the source location of the first non-`Phi` instruction in this block. */ - final Language::Location getLocation() { result = getFirstInstruction().getLocation() } + final Language::Location getLocation() { result = this.getFirstInstruction().getLocation() } /** * INTERNAL: Do not use. @@ -39,7 +39,7 @@ class IRBlockBase extends TIRBlock { ) and this = rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 | - funcBlock.getEnclosingFunction() = getEnclosingFunction() and + funcBlock.getEnclosingFunction() = this.getEnclosingFunction() and funcBlock.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and // Ensure that the block containing `EnterFunction` always comes first. if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction @@ -59,15 +59,15 @@ class IRBlockBase extends TIRBlock { * Get the `Phi` instructions that appear at the start of this block. */ final PhiInstruction getAPhiInstruction() { - Construction::getPhiInstructionBlockStart(result) = getFirstInstruction() + Construction::getPhiInstructionBlockStart(result) = this.getFirstInstruction() } /** * Gets an instruction in this block. This includes `Phi` instructions. */ final Instruction getAnInstruction() { - result = getInstruction(_) or - result = getAPhiInstruction() + result = this.getInstruction(_) or + result = this.getAPhiInstruction() } /** @@ -78,7 +78,9 @@ class IRBlockBase extends TIRBlock { /** * Gets the last instruction in this block. */ - final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) } + final Instruction getLastInstruction() { + result = this.getInstruction(this.getInstructionCount() - 1) + } /** * Gets the number of non-`Phi` instructions in this block. @@ -149,7 +151,7 @@ class IRBlock extends IRBlockBase { * Block `A` dominates block `B` if any control flow path from the entry block of the function to * block `B` must pass through block `A`. A block always dominates itself. */ - final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } + final predicate dominates(IRBlock block) { this.strictlyDominates(block) or this = block } /** * Gets a block on the dominance frontier of this block. @@ -159,8 +161,8 @@ class IRBlock extends IRBlockBase { */ pragma[noinline] final IRBlock dominanceFrontier() { - dominates(result.getAPredecessor()) and - not strictlyDominates(result) + this.dominates(result.getAPredecessor()) and + not this.strictlyDominates(result) } /** @@ -189,7 +191,7 @@ class IRBlock extends IRBlockBase { * Block `A` post-dominates block `B` if any control flow path from `B` to the exit block of the * function must pass through block `A`. A block always post-dominates itself. */ - final predicate postDominates(IRBlock block) { strictlyPostDominates(block) or this = block } + final predicate postDominates(IRBlock block) { this.strictlyPostDominates(block) or this = block } /** * Gets a block on the post-dominance frontier of this block. @@ -199,16 +201,16 @@ class IRBlock extends IRBlockBase { */ pragma[noinline] final IRBlock postPominanceFrontier() { - postDominates(result.getASuccessor()) and - not strictlyPostDominates(result) + this.postDominates(result.getASuccessor()) and + not this.strictlyPostDominates(result) } /** * Holds if this block is reachable from the entry block of its function. */ final predicate isReachableFromFunctionEntry() { - this = getEnclosingIRFunction().getEntryBlock() or - getAPredecessor().isReachableFromFunctionEntry() + this = this.getEnclosingIRFunction().getEntryBlock() or + this.getAPredecessor().isReachableFromFunctionEntry() } } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index 2fb3edad602..88a973fc5a8 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -41,7 +41,7 @@ class Instruction extends Construction::TStageInstruction { } /** Gets a textual representation of this element. */ - final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } + final string toString() { result = this.getOpcode().toString() + ": " + this.getAST().toString() } /** * Gets a string showing the result, opcode, and operands of the instruction, equivalent to what @@ -50,7 +50,8 @@ class Instruction extends Construction::TStageInstruction { * `mu0_28(int) = Store r0_26, r0_27` */ final string getDumpString() { - result = getResultString() + " = " + getOperationString() + " " + getOperandsString() + result = + this.getResultString() + " = " + this.getOperationString() + " " + this.getOperandsString() } private predicate shouldGenerateDumpStrings() { @@ -66,10 +67,13 @@ class Instruction extends Construction::TStageInstruction { * VariableAddress[x] */ final string getOperationString() { - shouldGenerateDumpStrings() and - if exists(getImmediateString()) - then result = getOperationPrefix() + getOpcode().toString() + "[" + getImmediateString() + "]" - else result = getOperationPrefix() + getOpcode().toString() + this.shouldGenerateDumpStrings() and + if exists(this.getImmediateString()) + then + result = + this.getOperationPrefix() + this.getOpcode().toString() + "[" + this.getImmediateString() + + "]" + else result = this.getOperationPrefix() + this.getOpcode().toString() } /** @@ -78,17 +82,17 @@ class Instruction extends Construction::TStageInstruction { string getImmediateString() { none() } private string getOperationPrefix() { - shouldGenerateDumpStrings() and + this.shouldGenerateDumpStrings() and if this instanceof SideEffectInstruction then result = "^" else result = "" } private string getResultPrefix() { - shouldGenerateDumpStrings() and - if getResultIRType() instanceof IRVoidType + this.shouldGenerateDumpStrings() and + if this.getResultIRType() instanceof IRVoidType then result = "v" else - if hasMemoryResult() - then if isResultModeled() then result = "m" else result = "mu" + if this.hasMemoryResult() + then if this.isResultModeled() then result = "m" else result = "mu" else result = "r" } @@ -97,7 +101,7 @@ class Instruction extends Construction::TStageInstruction { * used by debugging and printing code only. */ int getDisplayIndexInBlock() { - shouldGenerateDumpStrings() and + this.shouldGenerateDumpStrings() and exists(IRBlock block | this = block.getInstruction(result) or @@ -111,12 +115,12 @@ class Instruction extends Construction::TStageInstruction { } private int getLineRank() { - shouldGenerateDumpStrings() and + this.shouldGenerateDumpStrings() and this = rank[result](Instruction instr | instr = - getAnInstructionAtLine(getEnclosingIRFunction(), getLocation().getFile(), - getLocation().getStartLine()) + getAnInstructionAtLine(this.getEnclosingIRFunction(), this.getLocation().getFile(), + this.getLocation().getStartLine()) | instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock() ) @@ -130,8 +134,9 @@ class Instruction extends Construction::TStageInstruction { * Example: `r1_1` */ string getResultId() { - shouldGenerateDumpStrings() and - result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank() + this.shouldGenerateDumpStrings() and + result = + this.getResultPrefix() + this.getAST().getLocation().getStartLine() + "_" + this.getLineRank() } /** @@ -142,8 +147,8 @@ class Instruction extends Construction::TStageInstruction { * Example: `r1_1(int*)` */ final string getResultString() { - shouldGenerateDumpStrings() and - result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")" + this.shouldGenerateDumpStrings() and + result = this.getResultId() + "(" + this.getResultLanguageType().getDumpString() + ")" } /** @@ -153,10 +158,10 @@ class Instruction extends Construction::TStageInstruction { * Example: `func:r3_4, this:r3_5` */ string getOperandsString() { - shouldGenerateDumpStrings() and + this.shouldGenerateDumpStrings() and result = concat(Operand operand | - operand = getAnOperand() + operand = this.getAnOperand() | operand.getDumpString(), ", " order by operand.getDumpSortOrder() ) @@ -190,7 +195,7 @@ class Instruction extends Construction::TStageInstruction { * Gets the function that contains this instruction. */ final Language::Function getEnclosingFunction() { - result = getEnclosingIRFunction().getFunction() + result = this.getEnclosingIRFunction().getFunction() } /** @@ -208,7 +213,7 @@ class Instruction extends Construction::TStageInstruction { /** * Gets the location of the source code for this instruction. */ - final Language::Location getLocation() { result = getAST().getLocation() } + final Language::Location getLocation() { result = this.getAST().getLocation() } /** * Gets the `Expr` whose result is computed by this instruction, if any. The `Expr` may be a @@ -243,7 +248,7 @@ class Instruction extends Construction::TStageInstruction { * a result, its result type will be `IRVoidType`. */ cached - final IRType getResultIRType() { result = getResultLanguageType().getIRType() } + final IRType getResultIRType() { result = this.getResultLanguageType().getIRType() } /** * Gets the type of the result produced by this instruction. If the @@ -254,7 +259,7 @@ class Instruction extends Construction::TStageInstruction { */ final Language::Type getResultType() { exists(Language::LanguageType resultType | - resultType = getResultLanguageType() and + resultType = this.getResultLanguageType() and ( resultType.hasUnspecifiedType(result, _) or @@ -283,7 +288,7 @@ class Instruction extends Construction::TStageInstruction { * result of the `Load` instruction is a prvalue of type `int`, representing * the integer value loaded from variable `x`. */ - final predicate isGLValue() { getResultLanguageType().hasType(_, true) } + final predicate isGLValue() { this.getResultLanguageType().hasType(_, true) } /** * Gets the size of the result produced by this instruction, in bytes. If the @@ -292,7 +297,7 @@ class Instruction extends Construction::TStageInstruction { * If `this.isGLValue()` holds for this instruction, the value of * `getResultSize()` will always be the size of a pointer. */ - final int getResultSize() { result = getResultLanguageType().getByteSize() } + final int getResultSize() { result = this.getResultLanguageType().getByteSize() } /** * Gets the opcode that specifies the operation performed by this instruction. @@ -314,14 +319,16 @@ class Instruction extends Construction::TStageInstruction { /** * Holds if this instruction produces a memory result. */ - final predicate hasMemoryResult() { exists(getResultMemoryAccess()) } + final predicate hasMemoryResult() { exists(this.getResultMemoryAccess()) } /** * Gets the kind of memory access performed by this instruction's result. * Holds only for instructions with a memory result. */ pragma[inline] - final MemoryAccessKind getResultMemoryAccess() { result = getOpcode().getWriteMemoryAccess() } + final MemoryAccessKind getResultMemoryAccess() { + result = this.getOpcode().getWriteMemoryAccess() + } /** * Holds if the memory access performed by this instruction's result will not always write to @@ -332,7 +339,7 @@ class Instruction extends Construction::TStageInstruction { * (for example, the global side effects of a function call). */ pragma[inline] - final predicate hasResultMayMemoryAccess() { getOpcode().hasMayWriteMemoryAccess() } + final predicate hasResultMayMemoryAccess() { this.getOpcode().hasMayWriteMemoryAccess() } /** * Gets the operand that holds the memory address to which this instruction stores its @@ -340,7 +347,7 @@ class Instruction extends Construction::TStageInstruction { * is `r1`. */ final AddressOperand getResultAddressOperand() { - getResultMemoryAccess().usesAddressOperand() and + this.getResultMemoryAccess().usesAddressOperand() and result.getUse() = this } @@ -349,7 +356,7 @@ class Instruction extends Construction::TStageInstruction { * result, if any. For example, in `m3 = Store r1, r2`, the result of `getResultAddressOperand()` * is the instruction that defines `r1`. */ - final Instruction getResultAddress() { result = getResultAddressOperand().getDef() } + final Instruction getResultAddress() { result = this.getResultAddressOperand().getDef() } /** * Holds if the result of this instruction is precisely modeled in SSA. Always @@ -368,7 +375,7 @@ class Instruction extends Construction::TStageInstruction { */ final predicate isResultModeled() { // Register results are always in SSA form. - not hasMemoryResult() or + not this.hasMemoryResult() or Construction::hasModeledMemoryResult(this) } @@ -412,7 +419,7 @@ class Instruction extends Construction::TStageInstruction { /** * Gets all direct successors of this instruction. */ - final Instruction getASuccessor() { result = getSuccessor(_) } + final Instruction getASuccessor() { result = this.getSuccessor(_) } /** * Gets a predecessor of this instruction such that the predecessor reaches @@ -423,7 +430,7 @@ class Instruction extends Construction::TStageInstruction { /** * Gets all direct predecessors of this instruction. */ - final Instruction getAPredecessor() { result = getPredecessor(_) } + final Instruction getAPredecessor() { result = this.getPredecessor(_) } } /** @@ -543,7 +550,7 @@ class IndexedInstruction extends Instruction { * at this instruction. This instruction has no predecessors. */ class EnterFunctionInstruction extends Instruction { - EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction } + EnterFunctionInstruction() { this.getOpcode() instanceof Opcode::EnterFunction } } /** @@ -554,7 +561,7 @@ class EnterFunctionInstruction extends Instruction { * struct, or union, see `FieldAddressInstruction`. */ class VariableAddressInstruction extends VariableInstruction { - VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } + VariableAddressInstruction() { this.getOpcode() instanceof Opcode::VariableAddress } } /** @@ -566,7 +573,7 @@ class VariableAddressInstruction extends VariableInstruction { * The result has an `IRFunctionAddress` type. */ class FunctionAddressInstruction extends FunctionInstruction { - FunctionAddressInstruction() { getOpcode() instanceof Opcode::FunctionAddress } + FunctionAddressInstruction() { this.getOpcode() instanceof Opcode::FunctionAddress } } /** @@ -577,7 +584,7 @@ class FunctionAddressInstruction extends FunctionInstruction { * initializes that parameter. */ class InitializeParameterInstruction extends VariableInstruction { - InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter } + InitializeParameterInstruction() { this.getOpcode() instanceof Opcode::InitializeParameter } /** * Gets the parameter initialized by this instruction. @@ -603,7 +610,7 @@ class InitializeParameterInstruction extends VariableInstruction { * initialized elsewhere, would not otherwise have a definition in this function. */ class InitializeNonLocalInstruction extends Instruction { - InitializeNonLocalInstruction() { getOpcode() instanceof Opcode::InitializeNonLocal } + InitializeNonLocalInstruction() { this.getOpcode() instanceof Opcode::InitializeNonLocal } } /** @@ -611,7 +618,7 @@ class InitializeNonLocalInstruction extends Instruction { * with the value of that memory on entry to the function. */ class InitializeIndirectionInstruction extends VariableInstruction { - InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection } + InitializeIndirectionInstruction() { this.getOpcode() instanceof Opcode::InitializeIndirection } /** * Gets the parameter initialized by this instruction. @@ -635,24 +642,24 @@ class InitializeIndirectionInstruction extends VariableInstruction { * An instruction that initializes the `this` pointer parameter of the enclosing function. */ class InitializeThisInstruction extends Instruction { - InitializeThisInstruction() { getOpcode() instanceof Opcode::InitializeThis } + InitializeThisInstruction() { this.getOpcode() instanceof Opcode::InitializeThis } } /** * An instruction that computes the address of a non-static field of an object. */ class FieldAddressInstruction extends FieldInstruction { - FieldAddressInstruction() { getOpcode() instanceof Opcode::FieldAddress } + FieldAddressInstruction() { this.getOpcode() instanceof Opcode::FieldAddress } /** * Gets the operand that provides the address of the object containing the field. */ - final UnaryOperand getObjectAddressOperand() { result = getAnOperand() } + final UnaryOperand getObjectAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the object containing the field. */ - final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } + final Instruction getObjectAddress() { result = this.getObjectAddressOperand().getDef() } } /** @@ -661,17 +668,19 @@ class FieldAddressInstruction extends FieldInstruction { * This instruction is used for element access to C# arrays. */ class ElementsAddressInstruction extends UnaryInstruction { - ElementsAddressInstruction() { getOpcode() instanceof Opcode::ElementsAddress } + ElementsAddressInstruction() { this.getOpcode() instanceof Opcode::ElementsAddress } /** * Gets the operand that provides the address of the array object. */ - final UnaryOperand getArrayObjectAddressOperand() { result = getAnOperand() } + final UnaryOperand getArrayObjectAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the array object. */ - final Instruction getArrayObjectAddress() { result = getArrayObjectAddressOperand().getDef() } + final Instruction getArrayObjectAddress() { + result = this.getArrayObjectAddressOperand().getDef() + } } /** @@ -685,7 +694,7 @@ class ElementsAddressInstruction extends UnaryInstruction { * taken may want to ignore any function that contains an `ErrorInstruction`. */ class ErrorInstruction extends Instruction { - ErrorInstruction() { getOpcode() instanceof Opcode::Error } + ErrorInstruction() { this.getOpcode() instanceof Opcode::Error } } /** @@ -695,7 +704,7 @@ class ErrorInstruction extends Instruction { * an initializer, or whose initializer only partially initializes the variable. */ class UninitializedInstruction extends VariableInstruction { - UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized } + UninitializedInstruction() { this.getOpcode() instanceof Opcode::Uninitialized } /** * Gets the variable that is uninitialized. @@ -710,7 +719,7 @@ class UninitializedInstruction extends VariableInstruction { * least one instruction, even when the AST has no semantic effect. */ class NoOpInstruction extends Instruction { - NoOpInstruction() { getOpcode() instanceof Opcode::NoOp } + NoOpInstruction() { this.getOpcode() instanceof Opcode::NoOp } } /** @@ -732,32 +741,32 @@ class NoOpInstruction extends Instruction { * `void`-returning function. */ class ReturnInstruction extends Instruction { - ReturnInstruction() { getOpcode() instanceof ReturnOpcode } + ReturnInstruction() { this.getOpcode() instanceof ReturnOpcode } } /** * An instruction that returns control to the caller of the function, without returning a value. */ class ReturnVoidInstruction extends ReturnInstruction { - ReturnVoidInstruction() { getOpcode() instanceof Opcode::ReturnVoid } + ReturnVoidInstruction() { this.getOpcode() instanceof Opcode::ReturnVoid } } /** * An instruction that returns control to the caller of the function, including a return value. */ class ReturnValueInstruction extends ReturnInstruction { - ReturnValueInstruction() { getOpcode() instanceof Opcode::ReturnValue } + ReturnValueInstruction() { this.getOpcode() instanceof Opcode::ReturnValue } /** * Gets the operand that provides the value being returned by the function. */ - final LoadOperand getReturnValueOperand() { result = getAnOperand() } + final LoadOperand getReturnValueOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the value being returned by the function, if an * exact definition is available. */ - final Instruction getReturnValue() { result = getReturnValueOperand().getDef() } + final Instruction getReturnValue() { result = this.getReturnValueOperand().getDef() } } /** @@ -770,28 +779,28 @@ class ReturnValueInstruction extends ReturnInstruction { * that the caller initialized the memory pointed to by the parameter before the call. */ class ReturnIndirectionInstruction extends VariableInstruction { - ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } + ReturnIndirectionInstruction() { this.getOpcode() instanceof Opcode::ReturnIndirection } /** * Gets the operand that provides the value of the pointed-to memory. */ - final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + final SideEffectOperand getSideEffectOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the value of the pointed-to memory, if an exact * definition is available. */ - final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + final Instruction getSideEffect() { result = this.getSideEffectOperand().getDef() } /** * Gets the operand that provides the address of the pointed-to memory. */ - final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + final AddressOperand getSourceAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the pointed-to memory. */ - final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } + final Instruction getSourceAddress() { result = this.getSourceAddressOperand().getDef() } /** * Gets the parameter for which this instruction reads the final pointed-to value within the @@ -821,12 +830,12 @@ class ReturnIndirectionInstruction extends VariableInstruction { * * There are several different copy instructions, depending on the source and destination of the * copy operation: - * - `CopyInstruction` - Copies a register operand to a register result. + * - `CopyValueInstruction` - Copies a register operand to a register result. * - `LoadInstruction` - Copies a memory operand to a register result. * - `StoreInstruction` - Copies a register operand to a memory result. */ class CopyInstruction extends Instruction { - CopyInstruction() { getOpcode() instanceof CopyOpcode } + CopyInstruction() { this.getOpcode() instanceof CopyOpcode } /** * Gets the operand that provides the input value of the copy. @@ -837,16 +846,16 @@ class CopyInstruction extends Instruction { * Gets the instruction whose result provides the input value of the copy, if an exact definition * is available. */ - final Instruction getSourceValue() { result = getSourceValueOperand().getDef() } + final Instruction getSourceValue() { result = this.getSourceValueOperand().getDef() } } /** * An instruction that returns a register result containing a copy of its register operand. */ class CopyValueInstruction extends CopyInstruction, UnaryInstruction { - CopyValueInstruction() { getOpcode() instanceof Opcode::CopyValue } + CopyValueInstruction() { this.getOpcode() instanceof Opcode::CopyValue } - final override UnaryOperand getSourceValueOperand() { result = getAnOperand() } + final override UnaryOperand getSourceValueOperand() { result = this.getAnOperand() } } /** @@ -863,47 +872,49 @@ private string getAddressOperandDescription(AddressOperand operand) { * An instruction that returns a register result containing a copy of its memory operand. */ class LoadInstruction extends CopyInstruction { - LoadInstruction() { getOpcode() instanceof Opcode::Load } + LoadInstruction() { this.getOpcode() instanceof Opcode::Load } final override string getImmediateString() { - result = getAddressOperandDescription(getSourceAddressOperand()) + result = getAddressOperandDescription(this.getSourceAddressOperand()) } /** * Gets the operand that provides the address of the value being loaded. */ - final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + final AddressOperand getSourceAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the value being loaded. */ - final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } + final Instruction getSourceAddress() { result = this.getSourceAddressOperand().getDef() } - final override LoadOperand getSourceValueOperand() { result = getAnOperand() } + final override LoadOperand getSourceValueOperand() { result = this.getAnOperand() } } /** * An instruction that returns a memory result containing a copy of its register operand. */ class StoreInstruction extends CopyInstruction { - StoreInstruction() { getOpcode() instanceof Opcode::Store } + StoreInstruction() { this.getOpcode() instanceof Opcode::Store } final override string getImmediateString() { - result = getAddressOperandDescription(getDestinationAddressOperand()) + result = getAddressOperandDescription(this.getDestinationAddressOperand()) } /** * Gets the operand that provides the address of the location to which the value will be stored. */ - final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + final AddressOperand getDestinationAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the location to which the value will * be stored, if an exact definition is available. */ - final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } + final Instruction getDestinationAddress() { + result = this.getDestinationAddressOperand().getDef() + } - final override StoreValueOperand getSourceValueOperand() { result = getAnOperand() } + final override StoreValueOperand getSourceValueOperand() { result = this.getAnOperand() } } /** @@ -911,27 +922,27 @@ class StoreInstruction extends CopyInstruction { * operand. */ class ConditionalBranchInstruction extends Instruction { - ConditionalBranchInstruction() { getOpcode() instanceof Opcode::ConditionalBranch } + ConditionalBranchInstruction() { this.getOpcode() instanceof Opcode::ConditionalBranch } /** * Gets the operand that provides the Boolean condition controlling the branch. */ - final ConditionOperand getConditionOperand() { result = getAnOperand() } + final ConditionOperand getConditionOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the Boolean condition controlling the branch. */ - final Instruction getCondition() { result = getConditionOperand().getDef() } + final Instruction getCondition() { result = this.getConditionOperand().getDef() } /** * Gets the instruction to which control will flow if the condition is true. */ - final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) } + final Instruction getTrueSuccessor() { result = this.getSuccessor(EdgeKind::trueEdge()) } /** * Gets the instruction to which control will flow if the condition is false. */ - final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) } + final Instruction getFalseSuccessor() { result = this.getSuccessor(EdgeKind::falseEdge()) } } /** @@ -943,14 +954,14 @@ class ConditionalBranchInstruction extends Instruction { * successors. */ class ExitFunctionInstruction extends Instruction { - ExitFunctionInstruction() { getOpcode() instanceof Opcode::ExitFunction } + ExitFunctionInstruction() { this.getOpcode() instanceof Opcode::ExitFunction } } /** * An instruction whose result is a constant value. */ class ConstantInstruction extends ConstantValueInstruction { - ConstantInstruction() { getOpcode() instanceof Opcode::Constant } + ConstantInstruction() { this.getOpcode() instanceof Opcode::Constant } } /** @@ -959,7 +970,7 @@ class ConstantInstruction extends ConstantValueInstruction { class IntegerConstantInstruction extends ConstantInstruction { IntegerConstantInstruction() { exists(IRType resultType | - resultType = getResultIRType() and + resultType = this.getResultIRType() and (resultType instanceof IRIntegerType or resultType instanceof IRBooleanType) ) } @@ -969,7 +980,7 @@ class IntegerConstantInstruction extends ConstantInstruction { * An instruction whose result is a constant value of floating-point type. */ class FloatConstantInstruction extends ConstantInstruction { - FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType } + FloatConstantInstruction() { this.getResultIRType() instanceof IRFloatingPointType } } /** @@ -978,7 +989,9 @@ class FloatConstantInstruction extends ConstantInstruction { class StringConstantInstruction extends VariableInstruction { override IRStringLiteral var; - final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) } + final override string getImmediateString() { + result = Language::getStringLiteralText(this.getValue()) + } /** * Gets the string literal whose address is returned by this instruction. @@ -990,37 +1003,37 @@ class StringConstantInstruction extends VariableInstruction { * An instruction whose result is computed from two operands. */ class BinaryInstruction extends Instruction { - BinaryInstruction() { getOpcode() instanceof BinaryOpcode } + BinaryInstruction() { this.getOpcode() instanceof BinaryOpcode } /** * Gets the left operand of this binary instruction. */ - final LeftOperand getLeftOperand() { result = getAnOperand() } + final LeftOperand getLeftOperand() { result = this.getAnOperand() } /** * Gets the right operand of this binary instruction. */ - final RightOperand getRightOperand() { result = getAnOperand() } + final RightOperand getRightOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the value of the left operand of this binary * instruction. */ - final Instruction getLeft() { result = getLeftOperand().getDef() } + final Instruction getLeft() { result = this.getLeftOperand().getDef() } /** * Gets the instruction whose result provides the value of the right operand of this binary * instruction. */ - final Instruction getRight() { result = getRightOperand().getDef() } + final Instruction getRight() { result = this.getRightOperand().getDef() } /** * Holds if this instruction's operands are `op1` and `op2`, in either order. */ final predicate hasOperands(Operand op1, Operand op2) { - op1 = getLeftOperand() and op2 = getRightOperand() + op1 = this.getLeftOperand() and op2 = this.getRightOperand() or - op1 = getRightOperand() and op2 = getLeftOperand() + op1 = this.getRightOperand() and op2 = this.getLeftOperand() } } @@ -1028,7 +1041,7 @@ class BinaryInstruction extends Instruction { * An instruction that computes the result of an arithmetic operation. */ class ArithmeticInstruction extends Instruction { - ArithmeticInstruction() { getOpcode() instanceof ArithmeticOpcode } + ArithmeticInstruction() { this.getOpcode() instanceof ArithmeticOpcode } } /** @@ -1050,7 +1063,7 @@ class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction * performed according to IEEE-754. */ class AddInstruction extends BinaryArithmeticInstruction { - AddInstruction() { getOpcode() instanceof Opcode::Add } + AddInstruction() { this.getOpcode() instanceof Opcode::Add } } /** @@ -1061,7 +1074,7 @@ class AddInstruction extends BinaryArithmeticInstruction { * according to IEEE-754. */ class SubInstruction extends BinaryArithmeticInstruction { - SubInstruction() { getOpcode() instanceof Opcode::Sub } + SubInstruction() { this.getOpcode() instanceof Opcode::Sub } } /** @@ -1072,7 +1085,7 @@ class SubInstruction extends BinaryArithmeticInstruction { * performed according to IEEE-754. */ class MulInstruction extends BinaryArithmeticInstruction { - MulInstruction() { getOpcode() instanceof Opcode::Mul } + MulInstruction() { this.getOpcode() instanceof Opcode::Mul } } /** @@ -1083,7 +1096,7 @@ class MulInstruction extends BinaryArithmeticInstruction { * to IEEE-754. */ class DivInstruction extends BinaryArithmeticInstruction { - DivInstruction() { getOpcode() instanceof Opcode::Div } + DivInstruction() { this.getOpcode() instanceof Opcode::Div } } /** @@ -1093,7 +1106,7 @@ class DivInstruction extends BinaryArithmeticInstruction { * division by zero or integer overflow is undefined. */ class RemInstruction extends BinaryArithmeticInstruction { - RemInstruction() { getOpcode() instanceof Opcode::Rem } + RemInstruction() { this.getOpcode() instanceof Opcode::Rem } } /** @@ -1104,14 +1117,14 @@ class RemInstruction extends BinaryArithmeticInstruction { * is performed according to IEEE-754. */ class NegateInstruction extends UnaryArithmeticInstruction { - NegateInstruction() { getOpcode() instanceof Opcode::Negate } + NegateInstruction() { this.getOpcode() instanceof Opcode::Negate } } /** * An instruction that computes the result of a bitwise operation. */ class BitwiseInstruction extends Instruction { - BitwiseInstruction() { getOpcode() instanceof BitwiseOpcode } + BitwiseInstruction() { this.getOpcode() instanceof BitwiseOpcode } } /** @@ -1130,7 +1143,7 @@ class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } * Both operands must have the same integer type, which will also be the result type. */ class BitAndInstruction extends BinaryBitwiseInstruction { - BitAndInstruction() { getOpcode() instanceof Opcode::BitAnd } + BitAndInstruction() { this.getOpcode() instanceof Opcode::BitAnd } } /** @@ -1139,7 +1152,7 @@ class BitAndInstruction extends BinaryBitwiseInstruction { * Both operands must have the same integer type, which will also be the result type. */ class BitOrInstruction extends BinaryBitwiseInstruction { - BitOrInstruction() { getOpcode() instanceof Opcode::BitOr } + BitOrInstruction() { this.getOpcode() instanceof Opcode::BitOr } } /** @@ -1148,7 +1161,7 @@ class BitOrInstruction extends BinaryBitwiseInstruction { * Both operands must have the same integer type, which will also be the result type. */ class BitXorInstruction extends BinaryBitwiseInstruction { - BitXorInstruction() { getOpcode() instanceof Opcode::BitXor } + BitXorInstruction() { this.getOpcode() instanceof Opcode::BitXor } } /** @@ -1159,7 +1172,7 @@ class BitXorInstruction extends BinaryBitwiseInstruction { * rightmost bits are zero-filled. */ class ShiftLeftInstruction extends BinaryBitwiseInstruction { - ShiftLeftInstruction() { getOpcode() instanceof Opcode::ShiftLeft } + ShiftLeftInstruction() { this.getOpcode() instanceof Opcode::ShiftLeft } } /** @@ -1172,7 +1185,7 @@ class ShiftLeftInstruction extends BinaryBitwiseInstruction { * of the left operand. */ class ShiftRightInstruction extends BinaryBitwiseInstruction { - ShiftRightInstruction() { getOpcode() instanceof Opcode::ShiftRight } + ShiftRightInstruction() { this.getOpcode() instanceof Opcode::ShiftRight } } /** @@ -1183,7 +1196,7 @@ class PointerArithmeticInstruction extends BinaryInstruction { int elementSize; PointerArithmeticInstruction() { - getOpcode() instanceof PointerArithmeticOpcode and + this.getOpcode() instanceof PointerArithmeticOpcode and elementSize = Raw::getInstructionElementSize(this) } @@ -1206,7 +1219,7 @@ class PointerArithmeticInstruction extends BinaryInstruction { * An instruction that adds or subtracts an integer offset from a pointer. */ class PointerOffsetInstruction extends PointerArithmeticInstruction { - PointerOffsetInstruction() { getOpcode() instanceof PointerOffsetOpcode } + PointerOffsetInstruction() { this.getOpcode() instanceof PointerOffsetOpcode } } /** @@ -1217,7 +1230,7 @@ class PointerOffsetInstruction extends PointerArithmeticInstruction { * overflow is undefined. */ class PointerAddInstruction extends PointerOffsetInstruction { - PointerAddInstruction() { getOpcode() instanceof Opcode::PointerAdd } + PointerAddInstruction() { this.getOpcode() instanceof Opcode::PointerAdd } } /** @@ -1228,7 +1241,7 @@ class PointerAddInstruction extends PointerOffsetInstruction { * pointer underflow is undefined. */ class PointerSubInstruction extends PointerOffsetInstruction { - PointerSubInstruction() { getOpcode() instanceof Opcode::PointerSub } + PointerSubInstruction() { this.getOpcode() instanceof Opcode::PointerSub } } /** @@ -1241,31 +1254,31 @@ class PointerSubInstruction extends PointerOffsetInstruction { * undefined. */ class PointerDiffInstruction extends PointerArithmeticInstruction { - PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } + PointerDiffInstruction() { this.getOpcode() instanceof Opcode::PointerDiff } } /** * An instruction whose result is computed from a single operand. */ class UnaryInstruction extends Instruction { - UnaryInstruction() { getOpcode() instanceof UnaryOpcode } + UnaryInstruction() { this.getOpcode() instanceof UnaryOpcode } /** * Gets the sole operand of this instruction. */ - final UnaryOperand getUnaryOperand() { result = getAnOperand() } + final UnaryOperand getUnaryOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the sole operand of this instruction. */ - final Instruction getUnary() { result = getUnaryOperand().getDef() } + final Instruction getUnary() { result = this.getUnaryOperand().getDef() } } /** * An instruction that converts the value of its operand to a value of a different type. */ class ConvertInstruction extends UnaryInstruction { - ConvertInstruction() { getOpcode() instanceof Opcode::Convert } + ConvertInstruction() { this.getOpcode() instanceof Opcode::Convert } } /** @@ -1279,7 +1292,7 @@ class ConvertInstruction extends UnaryInstruction { * `as` expression. */ class CheckedConvertOrNullInstruction extends UnaryInstruction { - CheckedConvertOrNullInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrNull } + CheckedConvertOrNullInstruction() { this.getOpcode() instanceof Opcode::CheckedConvertOrNull } } /** @@ -1293,7 +1306,7 @@ class CheckedConvertOrNullInstruction extends UnaryInstruction { * expression. */ class CheckedConvertOrThrowInstruction extends UnaryInstruction { - CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow } + CheckedConvertOrThrowInstruction() { this.getOpcode() instanceof Opcode::CheckedConvertOrThrow } } /** @@ -1306,7 +1319,7 @@ class CheckedConvertOrThrowInstruction extends UnaryInstruction { * the most-derived object. */ class CompleteObjectAddressInstruction extends UnaryInstruction { - CompleteObjectAddressInstruction() { getOpcode() instanceof Opcode::CompleteObjectAddress } + CompleteObjectAddressInstruction() { this.getOpcode() instanceof Opcode::CompleteObjectAddress } } /** @@ -1351,7 +1364,7 @@ class InheritanceConversionInstruction extends UnaryInstruction { * An instruction that converts from the address of a derived class to the address of a base class. */ class ConvertToBaseInstruction extends InheritanceConversionInstruction { - ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode } + ConvertToBaseInstruction() { this.getOpcode() instanceof ConvertToBaseOpcode } } /** @@ -1361,7 +1374,9 @@ class ConvertToBaseInstruction extends InheritanceConversionInstruction { * If the operand holds a null address, the result is a null address. */ class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { - ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase } + ConvertToNonVirtualBaseInstruction() { + this.getOpcode() instanceof Opcode::ConvertToNonVirtualBase + } } /** @@ -1371,7 +1386,7 @@ class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { * If the operand holds a null address, the result is a null address. */ class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { - ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase } + ConvertToVirtualBaseInstruction() { this.getOpcode() instanceof Opcode::ConvertToVirtualBase } } /** @@ -1381,7 +1396,7 @@ class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { * If the operand holds a null address, the result is a null address. */ class ConvertToDerivedInstruction extends InheritanceConversionInstruction { - ConvertToDerivedInstruction() { getOpcode() instanceof Opcode::ConvertToDerived } + ConvertToDerivedInstruction() { this.getOpcode() instanceof Opcode::ConvertToDerived } } /** @@ -1390,7 +1405,7 @@ class ConvertToDerivedInstruction extends InheritanceConversionInstruction { * The operand must have an integer type, which will also be the result type. */ class BitComplementInstruction extends UnaryBitwiseInstruction { - BitComplementInstruction() { getOpcode() instanceof Opcode::BitComplement } + BitComplementInstruction() { this.getOpcode() instanceof Opcode::BitComplement } } /** @@ -1399,14 +1414,14 @@ class BitComplementInstruction extends UnaryBitwiseInstruction { * The operand must have a Boolean type, which will also be the result type. */ class LogicalNotInstruction extends UnaryInstruction { - LogicalNotInstruction() { getOpcode() instanceof Opcode::LogicalNot } + LogicalNotInstruction() { this.getOpcode() instanceof Opcode::LogicalNot } } /** * An instruction that compares two numeric operands. */ class CompareInstruction extends BinaryInstruction { - CompareInstruction() { getOpcode() instanceof CompareOpcode } + CompareInstruction() { this.getOpcode() instanceof CompareOpcode } } /** @@ -1417,7 +1432,7 @@ class CompareInstruction extends BinaryInstruction { * unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareEQInstruction extends CompareInstruction { - CompareEQInstruction() { getOpcode() instanceof Opcode::CompareEQ } + CompareEQInstruction() { this.getOpcode() instanceof Opcode::CompareEQ } } /** @@ -1428,14 +1443,14 @@ class CompareEQInstruction extends CompareInstruction { * `left == right`. Floating-point comparison is performed according to IEEE-754. */ class CompareNEInstruction extends CompareInstruction { - CompareNEInstruction() { getOpcode() instanceof Opcode::CompareNE } + CompareNEInstruction() { this.getOpcode() instanceof Opcode::CompareNE } } /** * An instruction that does a relative comparison of two values, such as `<` or `>=`. */ class RelationalInstruction extends CompareInstruction { - RelationalInstruction() { getOpcode() instanceof RelationalOpcode } + RelationalInstruction() { this.getOpcode() instanceof RelationalOpcode } /** * Gets the operand on the "greater" (or "greater-or-equal") side @@ -1467,11 +1482,11 @@ class RelationalInstruction extends CompareInstruction { * are unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareLTInstruction extends RelationalInstruction { - CompareLTInstruction() { getOpcode() instanceof Opcode::CompareLT } + CompareLTInstruction() { this.getOpcode() instanceof Opcode::CompareLT } - override Instruction getLesser() { result = getLeft() } + override Instruction getLesser() { result = this.getLeft() } - override Instruction getGreater() { result = getRight() } + override Instruction getGreater() { result = this.getRight() } override predicate isStrict() { any() } } @@ -1484,11 +1499,11 @@ class CompareLTInstruction extends RelationalInstruction { * are unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareGTInstruction extends RelationalInstruction { - CompareGTInstruction() { getOpcode() instanceof Opcode::CompareGT } + CompareGTInstruction() { this.getOpcode() instanceof Opcode::CompareGT } - override Instruction getLesser() { result = getRight() } + override Instruction getLesser() { result = this.getRight() } - override Instruction getGreater() { result = getLeft() } + override Instruction getGreater() { result = this.getLeft() } override predicate isStrict() { any() } } @@ -1502,11 +1517,11 @@ class CompareGTInstruction extends RelationalInstruction { * are unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareLEInstruction extends RelationalInstruction { - CompareLEInstruction() { getOpcode() instanceof Opcode::CompareLE } + CompareLEInstruction() { this.getOpcode() instanceof Opcode::CompareLE } - override Instruction getLesser() { result = getLeft() } + override Instruction getLesser() { result = this.getLeft() } - override Instruction getGreater() { result = getRight() } + override Instruction getGreater() { result = this.getRight() } override predicate isStrict() { none() } } @@ -1520,11 +1535,11 @@ class CompareLEInstruction extends RelationalInstruction { * are unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareGEInstruction extends RelationalInstruction { - CompareGEInstruction() { getOpcode() instanceof Opcode::CompareGE } + CompareGEInstruction() { this.getOpcode() instanceof Opcode::CompareGE } - override Instruction getLesser() { result = getRight() } + override Instruction getLesser() { result = this.getRight() } - override Instruction getGreater() { result = getLeft() } + override Instruction getGreater() { result = this.getLeft() } override predicate isStrict() { none() } } @@ -1543,78 +1558,78 @@ class CompareGEInstruction extends RelationalInstruction { * of any case edge. */ class SwitchInstruction extends Instruction { - SwitchInstruction() { getOpcode() instanceof Opcode::Switch } + SwitchInstruction() { this.getOpcode() instanceof Opcode::Switch } /** Gets the operand that provides the integer value controlling the switch. */ - final ConditionOperand getExpressionOperand() { result = getAnOperand() } + final ConditionOperand getExpressionOperand() { result = this.getAnOperand() } /** Gets the instruction whose result provides the integer value controlling the switch. */ - final Instruction getExpression() { result = getExpressionOperand().getDef() } + final Instruction getExpression() { result = this.getExpressionOperand().getDef() } /** Gets the successor instructions along the case edges of the switch. */ - final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) } + final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = this.getSuccessor(edge)) } /** Gets the successor instruction along the default edge of the switch, if any. */ - final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) } + final Instruction getDefaultSuccessor() { result = this.getSuccessor(EdgeKind::defaultEdge()) } } /** * An instruction that calls a function. */ class CallInstruction extends Instruction { - CallInstruction() { getOpcode() instanceof Opcode::Call } + CallInstruction() { this.getOpcode() instanceof Opcode::Call } final override string getImmediateString() { - result = getStaticCallTarget().toString() + result = this.getStaticCallTarget().toString() or - not exists(getStaticCallTarget()) and result = "?" + not exists(this.getStaticCallTarget()) and result = "?" } /** * Gets the operand the specifies the target function of the call. */ - final CallTargetOperand getCallTargetOperand() { result = getAnOperand() } + final CallTargetOperand getCallTargetOperand() { result = this.getAnOperand() } /** * Gets the `Instruction` that computes the target function of the call. This is usually a * `FunctionAddress` instruction, but can also be an arbitrary instruction that produces a * function pointer. */ - final Instruction getCallTarget() { result = getCallTargetOperand().getDef() } + final Instruction getCallTarget() { result = this.getCallTargetOperand().getDef() } /** * Gets all of the argument operands of the call, including the `this` pointer, if any. */ - final ArgumentOperand getAnArgumentOperand() { result = getAnOperand() } + final ArgumentOperand getAnArgumentOperand() { result = this.getAnOperand() } /** * Gets the `Function` that the call targets, if this is statically known. */ final Language::Function getStaticCallTarget() { - result = getCallTarget().(FunctionAddressInstruction).getFunctionSymbol() + result = this.getCallTarget().(FunctionAddressInstruction).getFunctionSymbol() } /** * Gets all of the arguments of the call, including the `this` pointer, if any. */ - final Instruction getAnArgument() { result = getAnArgumentOperand().getDef() } + final Instruction getAnArgument() { result = this.getAnArgumentOperand().getDef() } /** * Gets the `this` pointer argument operand of the call, if any. */ - final ThisArgumentOperand getThisArgumentOperand() { result = getAnOperand() } + final ThisArgumentOperand getThisArgumentOperand() { result = this.getAnOperand() } /** * Gets the `this` pointer argument of the call, if any. */ - final Instruction getThisArgument() { result = getThisArgumentOperand().getDef() } + final Instruction getThisArgument() { result = this.getThisArgumentOperand().getDef() } /** * Gets the argument operand at the specified index. */ pragma[noinline] final PositionalArgumentOperand getPositionalArgumentOperand(int index) { - result = getAnOperand() and + result = this.getAnOperand() and result.getIndex() = index } @@ -1623,7 +1638,7 @@ class CallInstruction extends Instruction { */ pragma[noinline] final Instruction getPositionalArgument(int index) { - result = getPositionalArgumentOperand(index).getDef() + result = this.getPositionalArgumentOperand(index).getDef() } /** @@ -1631,16 +1646,16 @@ class CallInstruction extends Instruction { */ pragma[noinline] final ArgumentOperand getArgumentOperand(int index) { - index >= 0 and result = getPositionalArgumentOperand(index) + index >= 0 and result = this.getPositionalArgumentOperand(index) or - index = -1 and result = getThisArgumentOperand() + index = -1 and result = this.getThisArgumentOperand() } /** * Gets the argument at the specified index, or `this` if `index` is `-1`. */ pragma[noinline] - final Instruction getArgument(int index) { result = getArgumentOperand(index).getDef() } + final Instruction getArgument(int index) { result = this.getArgumentOperand(index).getDef() } /** * Gets the number of arguments of the call, including the `this` pointer, if any. @@ -1665,7 +1680,7 @@ class CallInstruction extends Instruction { * An instruction representing a side effect of a function call. */ class SideEffectInstruction extends Instruction { - SideEffectInstruction() { getOpcode() instanceof SideEffectOpcode } + SideEffectInstruction() { this.getOpcode() instanceof SideEffectOpcode } /** * Gets the instruction whose execution causes this side effect. @@ -1680,7 +1695,7 @@ class SideEffectInstruction extends Instruction { * accessed by that call. */ class CallSideEffectInstruction extends SideEffectInstruction { - CallSideEffectInstruction() { getOpcode() instanceof Opcode::CallSideEffect } + CallSideEffectInstruction() { this.getOpcode() instanceof Opcode::CallSideEffect } } /** @@ -1691,7 +1706,7 @@ class CallSideEffectInstruction extends SideEffectInstruction { * call target cannot write to escaped memory. */ class CallReadSideEffectInstruction extends SideEffectInstruction { - CallReadSideEffectInstruction() { getOpcode() instanceof Opcode::CallReadSideEffect } + CallReadSideEffectInstruction() { this.getOpcode() instanceof Opcode::CallReadSideEffect } } /** @@ -1699,33 +1714,33 @@ class CallReadSideEffectInstruction extends SideEffectInstruction { * specific parameter. */ class ReadSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { - ReadSideEffectInstruction() { getOpcode() instanceof ReadSideEffectOpcode } + ReadSideEffectInstruction() { this.getOpcode() instanceof ReadSideEffectOpcode } /** Gets the operand for the value that will be read from this instruction, if known. */ - final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + final SideEffectOperand getSideEffectOperand() { result = this.getAnOperand() } /** Gets the value that will be read from this instruction, if known. */ - final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + final Instruction getSideEffect() { result = this.getSideEffectOperand().getDef() } /** Gets the operand for the address from which this instruction may read. */ - final AddressOperand getArgumentOperand() { result = getAnOperand() } + final AddressOperand getArgumentOperand() { result = this.getAnOperand() } /** Gets the address from which this instruction may read. */ - final Instruction getArgumentDef() { result = getArgumentOperand().getDef() } + final Instruction getArgumentDef() { result = this.getArgumentOperand().getDef() } } /** * An instruction representing the read of an indirect parameter within a function call. */ class IndirectReadSideEffectInstruction extends ReadSideEffectInstruction { - IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect } + IndirectReadSideEffectInstruction() { this.getOpcode() instanceof Opcode::IndirectReadSideEffect } } /** * An instruction representing the read of an indirect buffer parameter within a function call. */ class BufferReadSideEffectInstruction extends ReadSideEffectInstruction { - BufferReadSideEffectInstruction() { getOpcode() instanceof Opcode::BufferReadSideEffect } + BufferReadSideEffectInstruction() { this.getOpcode() instanceof Opcode::BufferReadSideEffect } } /** @@ -1733,18 +1748,18 @@ class BufferReadSideEffectInstruction extends ReadSideEffectInstruction { */ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { SizedBufferReadSideEffectInstruction() { - getOpcode() instanceof Opcode::SizedBufferReadSideEffect + this.getOpcode() instanceof Opcode::SizedBufferReadSideEffect } /** * Gets the operand that holds the number of bytes read from the buffer. */ - final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + final BufferSizeOperand getBufferSizeOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the number of bytes read from the buffer. */ - final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } + final Instruction getBufferSize() { result = this.getBufferSizeOperand().getDef() } } /** @@ -1752,17 +1767,17 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { * specific parameter. */ class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { - WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode } + WriteSideEffectInstruction() { this.getOpcode() instanceof WriteSideEffectOpcode } /** * Get the operand that holds the address of the memory to be written. */ - final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + final AddressOperand getDestinationAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the memory to be written. */ - Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } + Instruction getDestinationAddress() { result = this.getDestinationAddressOperand().getDef() } } /** @@ -1770,7 +1785,7 @@ class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstructi */ class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction { IndirectMustWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::IndirectMustWriteSideEffect + this.getOpcode() instanceof Opcode::IndirectMustWriteSideEffect } } @@ -1780,7 +1795,7 @@ class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction */ class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { BufferMustWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::BufferMustWriteSideEffect + this.getOpcode() instanceof Opcode::BufferMustWriteSideEffect } } @@ -1790,18 +1805,18 @@ class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { */ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { SizedBufferMustWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect + this.getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect } /** * Gets the operand that holds the number of bytes written to the buffer. */ - final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + final BufferSizeOperand getBufferSizeOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the number of bytes written to the buffer. */ - final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } + final Instruction getBufferSize() { result = this.getBufferSizeOperand().getDef() } } /** @@ -1812,7 +1827,7 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi */ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { IndirectMayWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::IndirectMayWriteSideEffect + this.getOpcode() instanceof Opcode::IndirectMayWriteSideEffect } } @@ -1822,7 +1837,9 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. */ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { - BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect } + BufferMayWriteSideEffectInstruction() { + this.getOpcode() instanceof Opcode::BufferMayWriteSideEffect + } } /** @@ -1832,18 +1849,18 @@ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { */ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { SizedBufferMayWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect + this.getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect } /** * Gets the operand that holds the number of bytes written to the buffer. */ - final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + final BufferSizeOperand getBufferSizeOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the number of bytes written to the buffer. */ - final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } + final Instruction getBufferSize() { result = this.getBufferSizeOperand().getDef() } } /** @@ -1852,80 +1869,80 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio */ class InitializeDynamicAllocationInstruction extends SideEffectInstruction { InitializeDynamicAllocationInstruction() { - getOpcode() instanceof Opcode::InitializeDynamicAllocation + this.getOpcode() instanceof Opcode::InitializeDynamicAllocation } /** * Gets the operand that represents the address of the allocation this instruction is initializing. */ - final AddressOperand getAllocationAddressOperand() { result = getAnOperand() } + final AddressOperand getAllocationAddressOperand() { result = this.getAnOperand() } /** * Gets the address for the allocation this instruction is initializing. */ - final Instruction getAllocationAddress() { result = getAllocationAddressOperand().getDef() } + final Instruction getAllocationAddress() { result = this.getAllocationAddressOperand().getDef() } } /** * An instruction representing a GNU or MSVC inline assembly statement. */ class InlineAsmInstruction extends Instruction { - InlineAsmInstruction() { getOpcode() instanceof Opcode::InlineAsm } + InlineAsmInstruction() { this.getOpcode() instanceof Opcode::InlineAsm } } /** * An instruction that throws an exception. */ class ThrowInstruction extends Instruction { - ThrowInstruction() { getOpcode() instanceof ThrowOpcode } + ThrowInstruction() { this.getOpcode() instanceof ThrowOpcode } } /** * An instruction that throws a new exception. */ class ThrowValueInstruction extends ThrowInstruction { - ThrowValueInstruction() { getOpcode() instanceof Opcode::ThrowValue } + ThrowValueInstruction() { this.getOpcode() instanceof Opcode::ThrowValue } /** * Gets the address operand of the exception thrown by this instruction. */ - final AddressOperand getExceptionAddressOperand() { result = getAnOperand() } + final AddressOperand getExceptionAddressOperand() { result = this.getAnOperand() } /** * Gets the address of the exception thrown by this instruction. */ - final Instruction getExceptionAddress() { result = getExceptionAddressOperand().getDef() } + final Instruction getExceptionAddress() { result = this.getExceptionAddressOperand().getDef() } /** * Gets the operand for the exception thrown by this instruction. */ - final LoadOperand getExceptionOperand() { result = getAnOperand() } + final LoadOperand getExceptionOperand() { result = this.getAnOperand() } /** * Gets the exception thrown by this instruction. */ - final Instruction getException() { result = getExceptionOperand().getDef() } + final Instruction getException() { result = this.getExceptionOperand().getDef() } } /** * An instruction that re-throws the current exception. */ class ReThrowInstruction extends ThrowInstruction { - ReThrowInstruction() { getOpcode() instanceof Opcode::ReThrow } + ReThrowInstruction() { this.getOpcode() instanceof Opcode::ReThrow } } /** * An instruction that exits the current function by propagating an exception. */ class UnwindInstruction extends Instruction { - UnwindInstruction() { getOpcode() instanceof Opcode::Unwind } + UnwindInstruction() { this.getOpcode() instanceof Opcode::Unwind } } /** * An instruction that starts a `catch` handler. */ class CatchInstruction extends Instruction { - CatchInstruction() { getOpcode() instanceof CatchOpcode } + CatchInstruction() { this.getOpcode() instanceof CatchOpcode } } /** @@ -1935,7 +1952,7 @@ class CatchByTypeInstruction extends CatchInstruction { Language::LanguageType exceptionType; CatchByTypeInstruction() { - getOpcode() instanceof Opcode::CatchByType and + this.getOpcode() instanceof Opcode::CatchByType and exceptionType = Raw::getInstructionExceptionType(this) } @@ -1951,21 +1968,21 @@ class CatchByTypeInstruction extends CatchInstruction { * An instruction that catches any exception. */ class CatchAnyInstruction extends CatchInstruction { - CatchAnyInstruction() { getOpcode() instanceof Opcode::CatchAny } + CatchAnyInstruction() { this.getOpcode() instanceof Opcode::CatchAny } } /** * An instruction that initializes all escaped memory. */ class AliasedDefinitionInstruction extends Instruction { - AliasedDefinitionInstruction() { getOpcode() instanceof Opcode::AliasedDefinition } + AliasedDefinitionInstruction() { this.getOpcode() instanceof Opcode::AliasedDefinition } } /** * An instruction that consumes all escaped memory on exit from the function. */ class AliasedUseInstruction extends Instruction { - AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse } + AliasedUseInstruction() { this.getOpcode() instanceof Opcode::AliasedUse } } /** @@ -1979,7 +1996,7 @@ class AliasedUseInstruction extends Instruction { * runtime. */ class PhiInstruction extends Instruction { - PhiInstruction() { getOpcode() instanceof Opcode::Phi } + PhiInstruction() { this.getOpcode() instanceof Opcode::Phi } /** * Gets all of the instruction's `PhiInputOperand`s, representing the values that flow from each predecessor block. @@ -2047,29 +2064,29 @@ class PhiInstruction extends Instruction { * https://link.springer.com/content/pdf/10.1007%2F3-540-61053-7_66.pdf. */ class ChiInstruction extends Instruction { - ChiInstruction() { getOpcode() instanceof Opcode::Chi } + ChiInstruction() { this.getOpcode() instanceof Opcode::Chi } /** * Gets the operand that represents the previous state of all memory that might be aliased by the * memory write. */ - final ChiTotalOperand getTotalOperand() { result = getAnOperand() } + final ChiTotalOperand getTotalOperand() { result = this.getAnOperand() } /** * Gets the operand that represents the previous state of all memory that might be aliased by the * memory write. */ - final Instruction getTotal() { result = getTotalOperand().getDef() } + final Instruction getTotal() { result = this.getTotalOperand().getDef() } /** * Gets the operand that represents the new value written by the memory write. */ - final ChiPartialOperand getPartialOperand() { result = getAnOperand() } + final ChiPartialOperand getPartialOperand() { result = this.getAnOperand() } /** * Gets the operand that represents the new value written by the memory write. */ - final Instruction getPartial() { result = getPartialOperand().getDef() } + final Instruction getPartial() { result = this.getPartialOperand().getDef() } /** * Gets the bit range `[startBit, endBit)` updated by the partial operand of this `ChiInstruction`, relative to the start address of the total operand. @@ -2093,7 +2110,7 @@ class ChiInstruction extends Instruction { * or `Switch` instruction where that particular edge is infeasible. */ class UnreachedInstruction extends Instruction { - UnreachedInstruction() { getOpcode() instanceof Opcode::Unreached } + UnreachedInstruction() { this.getOpcode() instanceof Opcode::Unreached } } /** @@ -2106,7 +2123,7 @@ class BuiltInOperationInstruction extends Instruction { Language::BuiltInOperation operation; BuiltInOperationInstruction() { - getOpcode() instanceof BuiltInOperationOpcode and + this.getOpcode() instanceof BuiltInOperationOpcode and operation = Raw::getInstructionBuiltInOperation(this) } @@ -2122,9 +2139,9 @@ class BuiltInOperationInstruction extends Instruction { * actual operation is specified by the `getBuiltInOperation()` predicate. */ class BuiltInInstruction extends BuiltInOperationInstruction { - BuiltInInstruction() { getOpcode() instanceof Opcode::BuiltIn } + BuiltInInstruction() { this.getOpcode() instanceof Opcode::BuiltIn } - final override string getImmediateString() { result = getBuiltInOperation().toString() } + final override string getImmediateString() { result = this.getBuiltInOperation().toString() } } /** @@ -2135,7 +2152,7 @@ class BuiltInInstruction extends BuiltInOperationInstruction { * to the `...` parameter. */ class VarArgsStartInstruction extends UnaryInstruction { - VarArgsStartInstruction() { getOpcode() instanceof Opcode::VarArgsStart } + VarArgsStartInstruction() { this.getOpcode() instanceof Opcode::VarArgsStart } } /** @@ -2145,7 +2162,7 @@ class VarArgsStartInstruction extends UnaryInstruction { * a result. */ class VarArgsEndInstruction extends UnaryInstruction { - VarArgsEndInstruction() { getOpcode() instanceof Opcode::VarArgsEnd } + VarArgsEndInstruction() { this.getOpcode() instanceof Opcode::VarArgsEnd } } /** @@ -2155,7 +2172,7 @@ class VarArgsEndInstruction extends UnaryInstruction { * argument. */ class VarArgInstruction extends UnaryInstruction { - VarArgInstruction() { getOpcode() instanceof Opcode::VarArg } + VarArgInstruction() { this.getOpcode() instanceof Opcode::VarArg } } /** @@ -2166,7 +2183,7 @@ class VarArgInstruction extends UnaryInstruction { * argument of the `...` parameter. */ class NextVarArgInstruction extends UnaryInstruction { - NextVarArgInstruction() { getOpcode() instanceof Opcode::NextVarArg } + NextVarArgInstruction() { this.getOpcode() instanceof Opcode::NextVarArg } } /** @@ -2180,5 +2197,5 @@ class NextVarArgInstruction extends UnaryInstruction { * The result is the address of the newly allocated object. */ class NewObjInstruction extends Instruction { - NewObjInstruction() { getOpcode() instanceof Opcode::NewObj } + NewObjInstruction() { this.getOpcode() instanceof Opcode::NewObj } } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll index d7cf89ca9aa..85d217bd361 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll @@ -46,12 +46,12 @@ class Operand extends TStageOperand { /** * Gets the location of the source code for this operand. */ - final Language::Location getLocation() { result = getUse().getLocation() } + final Language::Location getLocation() { result = this.getUse().getLocation() } /** * Gets the function that contains this operand. */ - final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() } + final IRFunction getEnclosingIRFunction() { result = this.getUse().getEnclosingIRFunction() } /** * Gets the `Instruction` that consumes this operand. @@ -74,7 +74,7 @@ class Operand extends TStageOperand { */ final Instruction getDef() { result = this.getAnyDef() and - getDefinitionOverlap() instanceof MustExactlyOverlap + this.getDefinitionOverlap() instanceof MustExactlyOverlap } /** @@ -82,7 +82,7 @@ class Operand extends TStageOperand { * * Gets the `Instruction` that consumes this operand. */ - deprecated final Instruction getUseInstruction() { result = getUse() } + deprecated final Instruction getUseInstruction() { result = this.getUse() } /** * DEPRECATED: use `getAnyDef` or `getDef`. The exact replacement for this @@ -91,7 +91,7 @@ class Operand extends TStageOperand { * * Gets the `Instruction` whose result is the value of the operand. */ - deprecated final Instruction getDefinitionInstruction() { result = getAnyDef() } + deprecated final Instruction getDefinitionInstruction() { result = this.getAnyDef() } /** * Gets the overlap relationship between the operand's definition and its use. @@ -101,7 +101,9 @@ class Operand extends TStageOperand { /** * Holds if the result of the definition instruction does not exactly overlap this use. */ - final predicate isDefinitionInexact() { not getDefinitionOverlap() instanceof MustExactlyOverlap } + final predicate isDefinitionInexact() { + not this.getDefinitionOverlap() instanceof MustExactlyOverlap + } /** * Gets a prefix to use when dumping the operand in an operand list. @@ -121,7 +123,7 @@ class Operand extends TStageOperand { * For example: `this:r3_5` */ final string getDumpString() { - result = getDumpLabel() + getInexactSpecifier() + getDefinitionId() + result = this.getDumpLabel() + this.getInexactSpecifier() + this.getDefinitionId() } /** @@ -129,9 +131,9 @@ class Operand extends TStageOperand { * definition is not modeled in SSA. */ private string getDefinitionId() { - result = getAnyDef().getResultId() + result = this.getAnyDef().getResultId() or - not exists(getAnyDef()) and result = "m?" + not exists(this.getAnyDef()) and result = "m?" } /** @@ -140,7 +142,7 @@ class Operand extends TStageOperand { * the empty string. */ private string getInexactSpecifier() { - if isDefinitionInexact() then result = "~" else result = "" + if this.isDefinitionInexact() then result = "~" else result = "" } /** @@ -155,7 +157,7 @@ class Operand extends TStageOperand { * the definition type, such as in the case of a partial read or a read from a pointer that * has been cast to a different type. */ - Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() } + Language::LanguageType getLanguageType() { result = this.getAnyDef().getResultLanguageType() } /** * Gets the language-neutral type of the value consumed by this operand. This is usually the same @@ -164,7 +166,7 @@ class Operand extends TStageOperand { * from the definition type, such as in the case of a partial read or a read from a pointer that * has been cast to a different type. */ - final IRType getIRType() { result = getLanguageType().getIRType() } + final IRType getIRType() { result = this.getLanguageType().getIRType() } /** * Gets the type of the value consumed by this operand. This is usually the same as the @@ -173,7 +175,7 @@ class Operand extends TStageOperand { * the definition type, such as in the case of a partial read or a read from a pointer that * has been cast to a different type. */ - final Language::Type getType() { getLanguageType().hasType(result, _) } + final Language::Type getType() { this.getLanguageType().hasType(result, _) } /** * Holds if the value consumed by this operand is a glvalue. If this @@ -182,13 +184,13 @@ class Operand extends TStageOperand { * not hold, the value of the operand represents a value whose type is * given by `getType()`. */ - final predicate isGLValue() { getLanguageType().hasType(_, true) } + final predicate isGLValue() { this.getLanguageType().hasType(_, true) } /** * Gets the size of the value consumed by this operand, in bytes. If the operand does not have * a known constant size, this predicate does not hold. */ - final int getSize() { result = getLanguageType().getByteSize() } + final int getSize() { result = this.getLanguageType().getByteSize() } } /** @@ -205,7 +207,7 @@ class MemoryOperand extends Operand { /** * Gets the kind of memory access performed by the operand. */ - MemoryAccessKind getMemoryAccess() { result = getUse().getOpcode().getReadMemoryAccess() } + MemoryAccessKind getMemoryAccess() { result = this.getUse().getOpcode().getReadMemoryAccess() } /** * Holds if the memory access performed by this operand will not always read from every bit in the @@ -215,7 +217,7 @@ class MemoryOperand extends Operand { * conservative estimate of the memory that might actually be accessed at runtime (for example, * the global side effects of a function call). */ - predicate hasMayReadMemoryAccess() { getUse().getOpcode().hasMayReadMemoryAccess() } + predicate hasMayReadMemoryAccess() { this.getUse().getOpcode().hasMayReadMemoryAccess() } /** * Returns the operand that holds the memory address from which the current operand loads its @@ -223,8 +225,8 @@ class MemoryOperand extends Operand { * is `r1`. */ final AddressOperand getAddressOperand() { - getMemoryAccess().usesAddressOperand() and - result.getUse() = getUse() + this.getMemoryAccess().usesAddressOperand() and + result.getUse() = this.getUse() } } @@ -294,7 +296,7 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOpe result = unique(Instruction defInstr | hasDefinition(defInstr, _)) } - final override Overlap getDefinitionOverlap() { hasDefinition(_, result) } + final override Overlap getDefinitionOverlap() { this.hasDefinition(_, result) } pragma[noinline] private predicate hasDefinition(Instruction defInstr, Overlap overlap) { @@ -449,13 +451,17 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand { final override Overlap getDefinitionOverlap() { result = overlap } - final override int getDumpSortOrder() { result = 11 + getPredecessorBlock().getDisplayIndex() } - - final override string getDumpLabel() { - result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" + final override int getDumpSortOrder() { + result = 11 + this.getPredecessorBlock().getDisplayIndex() } - final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() } + final override string getDumpLabel() { + result = "from " + this.getPredecessorBlock().getDisplayIndex().toString() + ":" + } + + final override string getDumpId() { + result = this.getPredecessorBlock().getDisplayIndex().toString() + } /** * Gets the predecessor block from which this value comes. diff --git a/cpp/ql/lib/semmle/code/cpp/ir/internal/IntegerInterval.qll b/cpp/ql/lib/semmle/code/cpp/ir/internal/IntegerInterval.qll index cd12b9b627a..4f8f4b4e672 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/internal/IntegerInterval.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/internal/IntegerInterval.qll @@ -18,10 +18,11 @@ Overlap getOverlap(IntValue defStart, IntValue defEnd, IntValue useStart, IntVal else if isLE(defStart, useStart) and isGE(defEnd, useEnd) then result instanceof MustTotallyOverlap - else - if isLE(defEnd, useStart) or isGE(defStart, useEnd) - then none() - else result instanceof MayPartiallyOverlap + else ( + not isLE(defEnd, useStart) and + not isGE(defStart, useEnd) and + result instanceof MayPartiallyOverlap + ) } /** diff --git a/cpp/ql/lib/semmle/code/cpp/metrics/MetricClass.qll b/cpp/ql/lib/semmle/code/cpp/metrics/MetricClass.qll index 33a256ce3e5..496e3f4511d 100644 --- a/cpp/ql/lib/semmle/code/cpp/metrics/MetricClass.qll +++ b/cpp/ql/lib/semmle/code/cpp/metrics/MetricClass.qll @@ -366,7 +366,7 @@ class MetricClass extends Class { 1 + count(string s | exists(Operation op | op = this.getAnEnclosedExpression() and s = op.getOperator()) - ) + count(string s | s = getAUsedHalsteadN1Operator()) + ) + count(string s | s = this.getAUsedHalsteadN1Operator()) } /** diff --git a/cpp/ql/lib/semmle/code/cpp/metrics/MetricFile.qll b/cpp/ql/lib/semmle/code/cpp/metrics/MetricFile.qll index f12d1011865..b3838ce4a5a 100644 --- a/cpp/ql/lib/semmle/code/cpp/metrics/MetricFile.qll +++ b/cpp/ql/lib/semmle/code/cpp/metrics/MetricFile.qll @@ -134,7 +134,7 @@ class MetricFile extends File { result = // avoid 0 values 1 + count(string s | exists(Operation op | op.getFile() = this and s = op.getOperator())) + - count(string s | s = getAUsedHalsteadN1Operator()) + count(string s | s = this.getAUsedHalsteadN1Operator()) } /** diff --git a/cpp/ql/lib/semmle/code/cpp/metrics/MetricFunction.qll b/cpp/ql/lib/semmle/code/cpp/metrics/MetricFunction.qll index 45036cfddf3..960e06c8375 100644 --- a/cpp/ql/lib/semmle/code/cpp/metrics/MetricFunction.qll +++ b/cpp/ql/lib/semmle/code/cpp/metrics/MetricFunction.qll @@ -41,7 +41,7 @@ class MetricFunction extends Function { * `&&`, and `||`) plus one. */ int getCyclomaticComplexity() { - result = 1 + cyclomaticComplexityBranches(getBlock()) and + result = 1 + cyclomaticComplexityBranches(this.getBlock()) and not this.isMultiplyDefined() } @@ -295,7 +295,7 @@ class MetricFunction extends Function { int getNestingDepth() { result = max(Stmt s, int aDepth | s.getEnclosingFunction() = this and nestingDepth(s, aDepth) | aDepth) and - not isMultiplyDefined() + not this.isMultiplyDefined() } } diff --git a/cpp/ql/lib/semmle/code/cpp/models/Models.qll b/cpp/ql/lib/semmle/code/cpp/models/Models.qll index f6e40d140bd..3eed4341cce 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/Models.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/Models.qll @@ -36,3 +36,4 @@ private import implementations.Select private import implementations.MySql private import implementations.SqLite3 private import implementations.PostgreSql +private import implementations.System diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/Allocation.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/Allocation.qll index 25dae1c2fd1..1da6e3b0b3b 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/Allocation.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/Allocation.qll @@ -15,10 +15,10 @@ private class MallocAllocationFunction extends AllocationFunction { MallocAllocationFunction() { // --- C library allocation - hasGlobalOrStdOrBslName("malloc") and // malloc(size) + this.hasGlobalOrStdOrBslName("malloc") and // malloc(size) sizeArg = 0 or - hasGlobalName([ + this.hasGlobalName([ // --- Windows Memory Management for Windows Drivers "MmAllocateContiguousMemory", // MmAllocateContiguousMemory(size, maxaddress) "MmAllocateContiguousNodeMemory", // MmAllocateContiguousNodeMemory(size, minaddress, maxaddress, bound, flag, prefer) @@ -39,7 +39,7 @@ private class MallocAllocationFunction extends AllocationFunction { ]) and sizeArg = 0 or - hasGlobalName([ + this.hasGlobalName([ // --- Windows Memory Management for Windows Drivers "ExAllocatePool", // ExAllocatePool(type, size) "ExAllocatePoolWithTag", // ExAllocatePool(type, size, tag) @@ -56,10 +56,10 @@ private class MallocAllocationFunction extends AllocationFunction { ]) and sizeArg = 1 or - hasGlobalName(["HeapAlloc"]) and // HeapAlloc(heap, flags, size) + this.hasGlobalName("HeapAlloc") and // HeapAlloc(heap, flags, size) sizeArg = 2 or - hasGlobalName([ + this.hasGlobalName([ // --- Windows Memory Management for Windows Drivers "MmAllocatePagesForMdl", // MmAllocatePagesForMdl(minaddress, maxaddress, skip, size) "MmAllocatePagesForMdlEx", // MmAllocatePagesForMdlEx(minaddress, maxaddress, skip, size, type, flags) @@ -79,7 +79,7 @@ private class AllocaAllocationFunction extends AllocationFunction { int sizeArg; AllocaAllocationFunction() { - hasGlobalName([ + this.hasGlobalName([ // --- stack allocation "alloca", // // alloca(size) "__builtin_alloca", // __builtin_alloca(size) @@ -104,7 +104,7 @@ private class CallocAllocationFunction extends AllocationFunction { CallocAllocationFunction() { // --- C library allocation - hasGlobalOrStdOrBslName("calloc") and // calloc(num, size) + this.hasGlobalOrStdOrBslName("calloc") and // calloc(num, size) sizeArg = 1 and multArg = 0 } @@ -124,11 +124,11 @@ private class ReallocAllocationFunction extends AllocationFunction { ReallocAllocationFunction() { // --- C library allocation - hasGlobalOrStdOrBslName("realloc") and // realloc(ptr, size) + this.hasGlobalOrStdOrBslName("realloc") and // realloc(ptr, size) sizeArg = 1 and reallocArg = 0 or - hasGlobalName([ + this.hasGlobalName([ // --- Windows Global / Local legacy allocation "LocalReAlloc", // LocalReAlloc(ptr, size, flags) "GlobalReAlloc", // GlobalReAlloc(ptr, size, flags) @@ -140,7 +140,7 @@ private class ReallocAllocationFunction extends AllocationFunction { sizeArg = 1 and reallocArg = 0 or - hasGlobalName("HeapReAlloc") and // HeapReAlloc(heap, flags, ptr, size) + this.hasGlobalName("HeapReAlloc") and // HeapReAlloc(heap, flags, ptr, size) sizeArg = 3 and reallocArg = 2 } @@ -156,7 +156,7 @@ private class ReallocAllocationFunction extends AllocationFunction { */ private class SizelessAllocationFunction extends AllocationFunction { SizelessAllocationFunction() { - hasGlobalName([ + this.hasGlobalName([ // --- Windows Memory Management for Windows Drivers "ExAllocateFromLookasideListEx", // ExAllocateFromLookasideListEx(list) "ExAllocateFromPagedLookasideList", // ExAllocateFromPagedLookasideList(list) @@ -209,18 +209,18 @@ private class CallAllocationExpr extends AllocationExpr, FunctionCall { AllocationFunction target; CallAllocationExpr() { - target = getTarget() and + target = this.getTarget() and // realloc(ptr, 0) only frees the pointer not ( exists(target.getReallocPtrArg()) and - getArgument(target.getSizeArg()).getValue().toInt() = 0 + this.getArgument(target.getSizeArg()).getValue().toInt() = 0 ) and // these are modelled directly (and more accurately), avoid duplication not exists(NewOrNewArrayExpr new | new.getAllocatorCall() = this) } override Expr getSizeExpr() { - exists(Expr sizeExpr | sizeExpr = getArgument(target.getSizeArg()) | + exists(Expr sizeExpr | sizeExpr = this.getArgument(target.getSizeArg()) | if exists(target.getSizeMult()) then result = sizeExpr else @@ -233,16 +233,18 @@ private class CallAllocationExpr extends AllocationExpr, FunctionCall { override int getSizeMult() { // malloc with multiplier argument that is a constant - result = getArgument(target.getSizeMult()).getValue().toInt() + result = this.getArgument(target.getSizeMult()).getValue().toInt() or // malloc with no multiplier argument not exists(target.getSizeMult()) and - deconstructSizeExpr(getArgument(target.getSizeArg()), _, result) + deconstructSizeExpr(this.getArgument(target.getSizeArg()), _, result) } - override int getSizeBytes() { result = getSizeExpr().getValue().toInt() * getSizeMult() } + override int getSizeBytes() { + result = this.getSizeExpr().getValue().toInt() * this.getSizeMult() + } - override Expr getReallocPtr() { result = getArgument(target.getReallocPtrArg()) } + override Expr getReallocPtr() { result = this.getArgument(target.getReallocPtrArg()) } override Type getAllocatedElementType() { result = @@ -259,11 +261,11 @@ private class CallAllocationExpr extends AllocationExpr, FunctionCall { private class NewAllocationExpr extends AllocationExpr, NewExpr { NewAllocationExpr() { this instanceof NewExpr } - override int getSizeBytes() { result = getAllocatedType().getSize() } + override int getSizeBytes() { result = this.getAllocatedType().getSize() } - override Type getAllocatedElementType() { result = getAllocatedType() } + override Type getAllocatedElementType() { result = this.getAllocatedType() } - override predicate requiresDealloc() { not exists(getPlacementPointer()) } + override predicate requiresDealloc() { not exists(this.getPlacementPointer()) } } /** @@ -274,18 +276,18 @@ private class NewArrayAllocationExpr extends AllocationExpr, NewArrayExpr { override Expr getSizeExpr() { // new array expr with variable size - result = getExtent() + result = this.getExtent() } override int getSizeMult() { // new array expr with variable size - exists(getExtent()) and - result = getAllocatedElementType().getSize() + exists(this.getExtent()) and + result = this.getAllocatedElementType().getSize() } override Type getAllocatedElementType() { result = NewArrayExpr.super.getAllocatedElementType() } - override int getSizeBytes() { result = getAllocatedType().getSize() } + override int getSizeBytes() { result = this.getAllocatedType().getSize() } - override predicate requiresDealloc() { not exists(getPlacementPointer()) } + override predicate requiresDealloc() { not exists(this.getPlacementPointer()) } } diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/GetDelim.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/GetDelim.qll index e2015406346..67950b6e135 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/GetDelim.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/GetDelim.qll @@ -8,7 +8,7 @@ import semmle.code.cpp.models.interfaces.FlowSource */ private class GetDelimFunction extends TaintFunction, AliasFunction, SideEffectFunction, RemoteFlowSourceFunction { - GetDelimFunction() { hasGlobalName(["getdelim", "getwdelim", "__getdelim"]) } + GetDelimFunction() { this.hasGlobalName(["getdelim", "getwdelim", "__getdelim"]) } override predicate hasTaintFlow(FunctionInput i, FunctionOutput o) { i.isParameter(3) and o.isParameterDeref(0) diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/Gets.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/Gets.qll index 08222c2cd6a..407c11834e5 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/Gets.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/Gets.qll @@ -19,7 +19,7 @@ private class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunctio // gets(str) // fgets(str, num, stream) // fgetws(wstr, num, stream) - hasGlobalOrStdOrBslName(["gets", "fgets", "fgetws"]) + this.hasGlobalOrStdOrBslName(["gets", "fgets", "fgetws"]) } override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { @@ -54,13 +54,13 @@ private class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunctio } override predicate hasArrayWithVariableSize(int bufParam, int countParam) { - not hasName("gets") and + not this.hasName("gets") and bufParam = 0 and countParam = 1 } override predicate hasArrayWithUnknownSize(int bufParam) { - hasName("gets") and + this.hasName("gets") and bufParam = 0 } diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/Memcpy.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/Memcpy.qll index b7d8aed60fa..a8d0e94f43c 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/Memcpy.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/Memcpy.qll @@ -44,27 +44,27 @@ private class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffect */ int getParamSize() { if this.hasGlobalName("memccpy") then result = 3 else result = 2 } - override predicate hasArrayInput(int bufParam) { bufParam = getParamSrc() } + override predicate hasArrayInput(int bufParam) { bufParam = this.getParamSrc() } - override predicate hasArrayOutput(int bufParam) { bufParam = getParamDest() } + override predicate hasArrayOutput(int bufParam) { bufParam = this.getParamDest() } override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { - input.isParameterDeref(getParamSrc()) and - output.isParameterDeref(getParamDest()) + input.isParameterDeref(this.getParamSrc()) and + output.isParameterDeref(this.getParamDest()) or - input.isParameterDeref(getParamSrc()) and + input.isParameterDeref(this.getParamSrc()) and output.isReturnValueDeref() or - input.isParameter(getParamDest()) and + input.isParameter(this.getParamDest()) and output.isReturnValue() } override predicate hasArrayWithVariableSize(int bufParam, int countParam) { ( - bufParam = getParamDest() or - bufParam = getParamSrc() + bufParam = this.getParamDest() or + bufParam = this.getParamSrc() ) and - countParam = getParamSize() + countParam = this.getParamSize() } override predicate hasOnlySpecificReadSideEffects() { any() } @@ -72,37 +72,37 @@ private class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffect override predicate hasOnlySpecificWriteSideEffects() { any() } override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) { - i = getParamDest() and + i = this.getParamDest() and buffer = true and // memccpy only writes until a given character `c` is found (if this.hasGlobalName("memccpy") then mustWrite = false else mustWrite = true) } override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { - i = getParamSrc() and buffer = true + i = this.getParamSrc() and buffer = true } override ParameterIndex getParameterSizeIndex(ParameterIndex i) { - result = getParamSize() and + result = this.getParamSize() and ( - i = getParamDest() or - i = getParamSrc() + i = this.getParamDest() or + i = this.getParamSrc() ) } override predicate parameterNeverEscapes(int index) { - index = getParamSrc() + index = this.getParamSrc() or - this.hasGlobalName("bcopy") and index = getParamDest() + this.hasGlobalName("bcopy") and index = this.getParamDest() } override predicate parameterEscapesOnlyViaReturn(int index) { - not this.hasGlobalName("bcopy") and index = getParamDest() + not this.hasGlobalName("bcopy") and index = this.getParamDest() } override predicate parameterIsAlwaysReturned(int index) { not this.hasGlobalName(["bcopy", mempcpy(), "memccpy"]) and - index = getParamDest() + index = this.getParamDest() } } diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/Memset.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/Memset.qll index d646be0363d..11ef853a0bc 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/Memset.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/Memset.qll @@ -31,17 +31,17 @@ private class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunct override predicate hasArrayWithVariableSize(int bufParam, int countParam) { bufParam = 0 and - (if hasGlobalName(bzero()) then countParam = 1 else countParam = 2) + (if this.hasGlobalName(bzero()) then countParam = 1 else countParam = 2) } - override predicate parameterNeverEscapes(int index) { hasGlobalName(bzero()) and index = 0 } + override predicate parameterNeverEscapes(int index) { this.hasGlobalName(bzero()) and index = 0 } override predicate parameterEscapesOnlyViaReturn(int index) { - not hasGlobalName(bzero()) and index = 0 + not this.hasGlobalName(bzero()) and index = 0 } override predicate parameterIsAlwaysReturned(int index) { - not hasGlobalName(bzero()) and index = 0 + not this.hasGlobalName(bzero()) and index = 0 } override predicate hasOnlySpecificReadSideEffects() { any() } @@ -54,7 +54,7 @@ private class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunct override ParameterIndex getParameterSizeIndex(ParameterIndex i) { i = 0 and - if hasGlobalName(bzero()) then result = 1 else result = 2 + if this.hasGlobalName(bzero()) then result = 1 else result = 2 } } diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/Pure.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/Pure.qll index d728a66463d..4efab29cabf 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/Pure.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/Pure.qll @@ -10,49 +10,49 @@ import semmle.code.cpp.models.interfaces.SideEffect private class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction, SideEffectFunction { PureStrFunction() { - hasGlobalOrStdOrBslName([ + this.hasGlobalOrStdOrBslName([ atoi(), "strcasestr", "strchnul", "strchr", "strchrnul", "strstr", "strpbrk", "strrchr", "strspn", strtol(), strrev(), strcmp(), strlwr(), strupr() ]) } override predicate hasArrayInput(int bufParam) { - getParameter(bufParam).getUnspecifiedType() instanceof PointerType + this.getParameter(bufParam).getUnspecifiedType() instanceof PointerType } override predicate hasArrayWithNullTerminator(int bufParam) { - getParameter(bufParam).getUnspecifiedType() instanceof PointerType + this.getParameter(bufParam).getUnspecifiedType() instanceof PointerType } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { exists(ParameterIndex i | ( input.isParameter(i) and - exists(getParameter(i)) + exists(this.getParameter(i)) or input.isParameterDeref(i) and - getParameter(i).getUnspecifiedType() instanceof PointerType + this.getParameter(i).getUnspecifiedType() instanceof PointerType ) and // Functions that end with _l also take a locale argument (always as the last argument), // and we don't want taint from those arguments. - (not this.getName().matches("%\\_l") or exists(getParameter(i + 1))) + (not this.getName().matches("%\\_l") or exists(this.getParameter(i + 1))) ) and ( output.isReturnValueDeref() and - getUnspecifiedType() instanceof PointerType + this.getUnspecifiedType() instanceof PointerType or output.isReturnValue() ) } override predicate parameterNeverEscapes(int i) { - getParameter(i).getUnspecifiedType() instanceof PointerType and - not parameterEscapesOnlyViaReturn(i) + this.getParameter(i).getUnspecifiedType() instanceof PointerType and + not this.parameterEscapesOnlyViaReturn(i) } override predicate parameterEscapesOnlyViaReturn(int i) { i = 0 and - getUnspecifiedType() instanceof PointerType + this.getUnspecifiedType() instanceof PointerType } override predicate parameterIsAlwaysReturned(int i) { none() } @@ -62,7 +62,7 @@ private class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunctio override predicate hasOnlySpecificWriteSideEffects() { any() } override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { - getParameter(i).getUnspecifiedType() instanceof PointerType and + this.getParameter(i).getUnspecifiedType() instanceof PointerType and buffer = true } } @@ -97,21 +97,21 @@ private string strcmp() { */ private class StrLenFunction extends AliasFunction, ArrayFunction, SideEffectFunction { StrLenFunction() { - hasGlobalOrStdOrBslName(["strlen", "strnlen", "wcslen"]) + this.hasGlobalOrStdOrBslName(["strlen", "strnlen", "wcslen"]) or - hasGlobalName(["_mbslen", "_mbslen_l", "_mbstrlen", "_mbstrlen_l"]) + this.hasGlobalName(["_mbslen", "_mbslen_l", "_mbstrlen", "_mbstrlen_l"]) } override predicate hasArrayInput(int bufParam) { - getParameter(bufParam).getUnspecifiedType() instanceof PointerType + this.getParameter(bufParam).getUnspecifiedType() instanceof PointerType } override predicate hasArrayWithNullTerminator(int bufParam) { - getParameter(bufParam).getUnspecifiedType() instanceof PointerType + this.getParameter(bufParam).getUnspecifiedType() instanceof PointerType } override predicate parameterNeverEscapes(int i) { - getParameter(i).getUnspecifiedType() instanceof PointerType + this.getParameter(i).getUnspecifiedType() instanceof PointerType } override predicate parameterEscapesOnlyViaReturn(int i) { none() } @@ -123,7 +123,7 @@ private class StrLenFunction extends AliasFunction, ArrayFunction, SideEffectFun override predicate hasOnlySpecificWriteSideEffects() { any() } override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { - getParameter(i).getUnspecifiedType() instanceof PointerType and + this.getParameter(i).getUnspecifiedType() instanceof PointerType and buffer = true } } @@ -133,12 +133,12 @@ private class StrLenFunction extends AliasFunction, ArrayFunction, SideEffectFun * side-effect free. Excludes functions modeled by `PureStrFunction` and `PureMemFunction`. */ private class PureFunction extends TaintFunction, SideEffectFunction { - PureFunction() { hasGlobalOrStdOrBslName(["abs", "labs"]) } + PureFunction() { this.hasGlobalOrStdOrBslName(["abs", "labs"]) } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { exists(ParameterIndex i | input.isParameter(i) and - exists(getParameter(i)) + exists(this.getParameter(i)) ) and output.isReturnValue() } @@ -155,44 +155,44 @@ private class PureFunction extends TaintFunction, SideEffectFunction { private class PureMemFunction extends AliasFunction, ArrayFunction, TaintFunction, SideEffectFunction { PureMemFunction() { - hasGlobalOrStdOrBslName([ + this.hasGlobalOrStdOrBslName([ "memchr", "__builtin_memchr", "memrchr", "rawmemchr", "memcmp", "__builtin_memcmp", "memmem" ]) or this.hasGlobalName("memfrob") } override predicate hasArrayInput(int bufParam) { - getParameter(bufParam).getUnspecifiedType() instanceof PointerType + this.getParameter(bufParam).getUnspecifiedType() instanceof PointerType } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { exists(ParameterIndex i | ( input.isParameter(i) and - exists(getParameter(i)) + exists(this.getParameter(i)) or input.isParameterDeref(i) and - getParameter(i).getUnspecifiedType() instanceof PointerType + this.getParameter(i).getUnspecifiedType() instanceof PointerType ) and // `memfrob` should not have taint from the size argument. (not this.hasGlobalName("memfrob") or i = 0) ) and ( output.isReturnValueDeref() and - getUnspecifiedType() instanceof PointerType + this.getUnspecifiedType() instanceof PointerType or output.isReturnValue() ) } override predicate parameterNeverEscapes(int i) { - getParameter(i).getUnspecifiedType() instanceof PointerType and - not parameterEscapesOnlyViaReturn(i) + this.getParameter(i).getUnspecifiedType() instanceof PointerType and + not this.parameterEscapesOnlyViaReturn(i) } override predicate parameterEscapesOnlyViaReturn(int i) { i = 0 and - getUnspecifiedType() instanceof PointerType + this.getUnspecifiedType() instanceof PointerType } override predicate parameterIsAlwaysReturned(int i) { none() } @@ -202,7 +202,7 @@ private class PureMemFunction extends AliasFunction, ArrayFunction, TaintFunctio override predicate hasOnlySpecificWriteSideEffects() { any() } override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { - getParameter(i).getUnspecifiedType() instanceof PointerType and + this.getParameter(i).getUnspecifiedType() instanceof PointerType and buffer = true } } diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/Recv.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/Recv.qll index 691ba528f42..0551185ba14 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/Recv.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/Recv.qll @@ -85,4 +85,6 @@ private class Recv extends AliasFunction, ArrayFunction, SideEffectFunction, ) and description = "Buffer read by " + this.getName() } + + override predicate hasSocketInput(FunctionInput input) { input.isParameter(0) } } diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/Send.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/Send.qll index 6086bc7748f..d871bad68af 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/Send.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/Send.qll @@ -60,4 +60,6 @@ private class Send extends AliasFunction, ArrayFunction, SideEffectFunction, Rem override predicate hasRemoteFlowSink(FunctionInput input, string description) { input.isParameterDeref(1) and description = "Buffer sent by " + this.getName() } + + override predicate hasSocketInput(FunctionInput input) { input.isParameter(0) } } diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/SmartPointer.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/SmartPointer.qll index e249a164061..389ce6c5ab0 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/SmartPointer.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/SmartPointer.qll @@ -72,9 +72,9 @@ private class MakeUniqueOrShared extends TaintFunction { // since these just take a size argument, which we don't want to propagate taint through. not this.isArray() and ( - input.isParameter([0 .. getNumberOfParameters() - 1]) + input.isParameter([0 .. this.getNumberOfParameters() - 1]) or - input.isParameterDeref([0 .. getNumberOfParameters() - 1]) + input.isParameterDeref([0 .. this.getNumberOfParameters() - 1]) ) and output.isReturnValue() } @@ -116,14 +116,14 @@ private class SmartPtrSetterFunction extends MemberFunction, AliasFunction, Side or // When taking ownership of a smart pointer via an rvalue reference, always overwrite the input // smart pointer. - getPointerInput().isParameterDeref(i) and + this.getPointerInput().isParameterDeref(i) and this.getParameter(i).getUnspecifiedType() instanceof RValueReferenceType and buffer = false and mustWrite = true } override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { - getPointerInput().isParameterDeref(i) and + this.getPointerInput().isParameterDeref(i) and buffer = false or not this instanceof Constructor and @@ -136,7 +136,7 @@ private class SmartPtrSetterFunction extends MemberFunction, AliasFunction, Side override predicate parameterEscapesOnlyViaReturn(int index) { none() } override predicate hasAddressFlow(FunctionInput input, FunctionOutput output) { - input = getPointerInput() and + input = this.getPointerInput() and output.isQualifierObject() or // Assignment operator always returns a reference to `*this`. diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/Sscanf.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/Sscanf.qll index b6120abf05a..42166ae9baa 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/Sscanf.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/Sscanf.qll @@ -23,15 +23,15 @@ private class SscanfModel extends ArrayFunction, TaintFunction, AliasFunction, S bufParam = this.(ScanfFunction).getInputParameterIndex() } - override predicate hasArrayInput(int bufParam) { hasArrayWithNullTerminator(bufParam) } + override predicate hasArrayInput(int bufParam) { this.hasArrayWithNullTerminator(bufParam) } private int getLengthParameterIndex() { result = this.(Snscanf).getInputLengthParameterIndex() } private int getLocaleParameterIndex() { this.getName().matches("%\\_l") and ( - if exists(getLengthParameterIndex()) - then result = getLengthParameterIndex() + 2 + if exists(this.getLengthParameterIndex()) + then result = this.getLengthParameterIndex() + 2 else result = 2 ) } @@ -40,11 +40,11 @@ private class SscanfModel extends ArrayFunction, TaintFunction, AliasFunction, S override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { input.isParameterDeref(this.(ScanfFunction).getInputParameterIndex()) and - output.isParameterDeref(any(int i | i >= getArgsStartPosition())) + output.isParameterDeref(any(int i | i >= this.getArgsStartPosition())) } override predicate parameterNeverEscapes(int index) { - index = [0 .. max(getACallToThisFunction().getNumberOfArguments())] + index = [0 .. max(this.getACallToThisFunction().getNumberOfArguments())] } override predicate parameterEscapesOnlyViaReturn(int index) { none() } @@ -56,7 +56,7 @@ private class SscanfModel extends ArrayFunction, TaintFunction, AliasFunction, S override predicate hasOnlySpecificWriteSideEffects() { any() } override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) { - i >= getArgsStartPosition() and + i >= this.getArgsStartPosition() and buffer = true and mustWrite = true } @@ -66,7 +66,7 @@ private class SscanfModel extends ArrayFunction, TaintFunction, AliasFunction, S i = [ this.(ScanfFunction).getInputParameterIndex(), - this.(ScanfFunction).getFormatParameterIndex(), getLocaleParameterIndex() + this.(ScanfFunction).getFormatParameterIndex(), this.getLocaleParameterIndex() ] } } diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdContainer.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdContainer.qll index 367db1613fc..c93a5ad147b 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdContainer.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdContainer.qll @@ -61,20 +61,20 @@ private class StdSequenceContainerConstructor extends Constructor, TaintFunction * value type of the container. */ int getAValueTypeParameterIndex() { - getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() = - getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector` + this.getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() = + this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector` } /** * Gets the index of a parameter to this function that is an iterator. */ - int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator } + int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // taint flow from any parameter of the value type to the returned object ( - input.isParameterDeref(getAValueTypeParameterIndex()) or - input.isParameter(getAnIteratorParameterIndex()) + input.isParameterDeref(this.getAValueTypeParameterIndex()) or + input.isParameter(this.getAnIteratorParameterIndex()) ) and ( output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object @@ -158,21 +158,21 @@ private class StdSequenceContainerInsert extends TaintFunction { * value type of the container. */ int getAValueTypeParameterIndex() { - getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() = - getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector` + this.getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() = + this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector` } /** * Gets the index of a parameter to this function that is an iterator. */ - int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator } + int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // flow from parameter to container itself (qualifier) and return value ( input.isQualifierObject() or - input.isParameterDeref(getAValueTypeParameterIndex()) or - input.isParameter(getAnIteratorParameterIndex()) + input.isParameterDeref(this.getAValueTypeParameterIndex()) or + input.isParameter(this.getAnIteratorParameterIndex()) ) and ( output.isQualifierObject() or @@ -197,20 +197,20 @@ private class StdSequenceContainerAssign extends TaintFunction { * value type of the container. */ int getAValueTypeParameterIndex() { - getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() = - getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector` + this.getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() = + this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector` } /** * Gets the index of a parameter to this function that is an iterator. */ - int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator } + int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // flow from parameter to container itself (qualifier) ( - input.isParameterDeref(getAValueTypeParameterIndex()) or - input.isParameter(getAnIteratorParameterIndex()) + input.isParameterDeref(this.getAValueTypeParameterIndex()) or + input.isParameter(this.getAnIteratorParameterIndex()) ) and output.isQualifierObject() } @@ -246,7 +246,7 @@ class StdVectorEmplace extends TaintFunction { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // flow from any parameter except the position iterator to qualifier and return value // (here we assume taint flow from any constructor parameter to the constructed object) - input.isParameterDeref([1 .. getNumberOfParameters() - 1]) and + input.isParameterDeref([1 .. this.getNumberOfParameters() - 1]) and ( output.isQualifierObject() or output.isReturnValue() @@ -263,7 +263,7 @@ class StdVectorEmplaceBack extends TaintFunction { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // flow from any parameter to qualifier // (here we assume taint flow from any constructor parameter to the constructed object) - input.isParameterDeref([0 .. getNumberOfParameters() - 1]) and + input.isParameterDeref([0 .. this.getNumberOfParameters() - 1]) and output.isQualifierObject() } } diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdMap.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdMap.qll index aecd98981e8..9dc220e79af 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdMap.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdMap.qll @@ -22,12 +22,12 @@ private class StdMapConstructor extends Constructor, TaintFunction { * Gets the index of a parameter to this function that is an iterator. */ int getAnIteratorParameterIndex() { - getParameter(result).getUnspecifiedType() instanceof Iterator + this.getParameter(result).getUnspecifiedType() instanceof Iterator } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // taint flow from any parameter of an iterator type to the qualifier - input.isParameterDeref(getAnIteratorParameterIndex()) and + input.isParameterDeref(this.getAnIteratorParameterIndex()) and ( output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object or @@ -47,7 +47,7 @@ private class StdMapInsert extends TaintFunction { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // flow from last parameter to qualifier and return value // (where the return value is a pair, this should really flow just to the first part of it) - input.isParameterDeref(getNumberOfParameters() - 1) and + input.isParameterDeref(this.getNumberOfParameters() - 1) and ( output.isQualifierObject() or output.isReturnValue() @@ -66,7 +66,7 @@ private class StdMapEmplace extends TaintFunction { // construct a pair, or a pair to be copied / moved) to the qualifier and // return value. // (where the return value is a pair, this should really flow just to the first part of it) - input.isParameterDeref(getNumberOfParameters() - 1) and + input.isParameterDeref(this.getNumberOfParameters() - 1) and ( output.isQualifierObject() or output.isReturnValue() @@ -87,9 +87,9 @@ private class StdMapTryEmplace extends TaintFunction { // flow from any parameter apart from the key to qualifier and return value // (here we assume taint flow from any constructor parameter to the constructed object) // (where the return value is a pair, this should really flow just to the first part of it) - exists(int arg | arg = [1 .. getNumberOfParameters() - 1] | + exists(int arg | arg = [1 .. this.getNumberOfParameters() - 1] | ( - not getUnspecifiedType() instanceof Iterator or + not this.getUnspecifiedType() instanceof Iterator or arg != 1 ) and input.isParameterDeref(arg) @@ -154,7 +154,7 @@ private class StdMapErase extends TaintFunction { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // flow from qualifier to iterator return value - getType().getUnderlyingType() instanceof Iterator and + this.getType().getUnderlyingType() instanceof Iterator and input.isQualifierObject() and output.isReturnValue() } diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdPair.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdPair.qll index 755f6a48520..c6fabcac314 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdPair.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdPair.qll @@ -51,13 +51,13 @@ private class StdPairConstructor extends Constructor, TaintFunction { * either value type of the pair. */ int getAValueTypeParameterIndex() { - getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() = - getDeclaringType().getTemplateArgument(_).(Type).getUnspecifiedType() // i.e. the `T1` or `T2` of this `std::pair` + this.getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() = + this.getDeclaringType().getTemplateArgument(_).(Type).getUnspecifiedType() // i.e. the `T1` or `T2` of this `std::pair` } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // taint flow from second parameter of a value type to the qualifier - getAValueTypeParameterIndex() = 1 and + this.getAValueTypeParameterIndex() = 1 and input.isParameterDeref(1) and ( output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdSet.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdSet.qll index d2e9892abcb..3e26d80c136 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdSet.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdSet.qll @@ -22,12 +22,12 @@ private class StdSetConstructor extends Constructor, TaintFunction { * Gets the index of a parameter to this function that is an iterator. */ int getAnIteratorParameterIndex() { - getParameter(result).getUnspecifiedType() instanceof Iterator + this.getParameter(result).getUnspecifiedType() instanceof Iterator } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // taint flow from any parameter of an iterator type to the qualifier - input.isParameterDeref(getAnIteratorParameterIndex()) and + input.isParameterDeref(this.getAnIteratorParameterIndex()) and ( output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object or @@ -45,7 +45,7 @@ private class StdSetInsert extends TaintFunction { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // flow from last parameter to qualifier and return value // (where the return value is a pair, this should really flow just to the first part of it) - input.isParameterDeref(getNumberOfParameters() - 1) and + input.isParameterDeref(this.getNumberOfParameters() - 1) and ( output.isQualifierObject() or output.isReturnValue() @@ -63,7 +63,7 @@ private class StdSetEmplace extends TaintFunction { // flow from any parameter to qualifier and return value // (here we assume taint flow from any constructor parameter to the constructed object) // (where the return value is a pair, this should really flow just to the first part of it) - input.isParameterDeref([0 .. getNumberOfParameters() - 1]) and + input.isParameterDeref([0 .. this.getNumberOfParameters() - 1]) and ( output.isQualifierObject() or output.isReturnValue() @@ -107,7 +107,7 @@ private class StdSetErase extends TaintFunction { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // flow from qualifier to iterator return value - getType().getUnderlyingType() instanceof Iterator and + this.getType().getUnderlyingType() instanceof Iterator and input.isQualifierObject() and output.isReturnValue() } diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll index 73a0f6edf26..ae190688b70 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll @@ -31,31 +31,31 @@ private class StdStringConstructor extends Constructor, TaintFunction { * character). */ int getAStringParameterIndex() { - exists(Type paramType | paramType = getParameter(result).getUnspecifiedType() | + exists(Type paramType | paramType = this.getParameter(result).getUnspecifiedType() | // e.g. `std::basic_string::CharT *` paramType instanceof PointerType or // e.g. `std::basic_string &`, avoiding `const Allocator&` paramType instanceof ReferenceType and not paramType.(ReferenceType).getBaseType() = - getDeclaringType().getTemplateArgument(2).(Type).getUnspecifiedType() + this.getDeclaringType().getTemplateArgument(2).(Type).getUnspecifiedType() or // i.e. `std::basic_string::CharT` - getParameter(result).getUnspecifiedType() = - getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() + this.getParameter(result).getUnspecifiedType() = + this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() ) } /** * Gets the index of a parameter to this function that is an iterator. */ - int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator } + int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // taint flow from any parameter of the value type to the returned object ( - input.isParameterDeref(getAStringParameterIndex()) or - input.isParameter(getAnIteratorParameterIndex()) + input.isParameterDeref(this.getAStringParameterIndex()) or + input.isParameter(this.getAnIteratorParameterIndex()) ) and ( output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object @@ -156,23 +156,23 @@ private class StdStringAppend extends TaintFunction { * character). */ int getAStringParameterIndex() { - getParameter(result).getType() instanceof PointerType or // e.g. `std::basic_string::CharT *` - getParameter(result).getType() instanceof ReferenceType or // e.g. `std::basic_string &` - getParameter(result).getUnspecifiedType() = - getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT` + this.getParameter(result).getType() instanceof PointerType or // e.g. `std::basic_string::CharT *` + this.getParameter(result).getType() instanceof ReferenceType or // e.g. `std::basic_string &` + this.getParameter(result).getUnspecifiedType() = + this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT` } /** * Gets the index of a parameter to this function that is an iterator. */ - int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator } + int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // flow from string and parameter to string (qualifier) and return value ( input.isQualifierObject() or - input.isParameterDeref(getAStringParameterIndex()) or - input.isParameter(getAnIteratorParameterIndex()) + input.isParameterDeref(this.getAStringParameterIndex()) or + input.isParameter(this.getAnIteratorParameterIndex()) ) and ( output.isQualifierObject() or @@ -197,22 +197,22 @@ private class StdStringAssign extends TaintFunction { * character). */ int getAStringParameterIndex() { - getParameter(result).getType() instanceof PointerType or // e.g. `std::basic_string::CharT *` - getParameter(result).getType() instanceof ReferenceType or // e.g. `std::basic_string &` - getParameter(result).getUnspecifiedType() = - getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT` + this.getParameter(result).getType() instanceof PointerType or // e.g. `std::basic_string::CharT *` + this.getParameter(result).getType() instanceof ReferenceType or // e.g. `std::basic_string &` + this.getParameter(result).getUnspecifiedType() = + this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT` } /** * Gets the index of a parameter to this function that is an iterator. */ - int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator } + int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // flow from parameter to string itself (qualifier) and return value ( - input.isParameterDeref(getAStringParameterIndex()) or - input.isParameter(getAnIteratorParameterIndex()) + input.isParameterDeref(this.getAStringParameterIndex()) or + input.isParameter(this.getAnIteratorParameterIndex()) ) and ( output.isQualifierObject() or @@ -574,12 +574,12 @@ private class StdStringStreamConstructor extends Constructor, TaintFunction { * Gets the index of a parameter to this function that is a string. */ int getAStringParameterIndex() { - getParameter(result).getType() instanceof ReferenceType // `const std::basic_string &` + this.getParameter(result).getType() instanceof ReferenceType // `const std::basic_string &` } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // taint flow from any parameter of string type to the returned object - input.isParameterDeref(getAStringParameterIndex()) and + input.isParameterDeref(this.getAStringParameterIndex()) and ( output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object or diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/Strcat.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/Strcat.qll index ee9af547582..1a65e7b6ca4 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/Strcat.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/Strcat.qll @@ -32,7 +32,7 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, Sid /** * Gets the index of the parameter that is the size of the copy (in characters). */ - int getParamSize() { exists(getParameter(2)) and result = 2 } + int getParamSize() { exists(this.getParameter(2)) and result = 2 } /** * Gets the index of the parameter that is the source of the copy. @@ -50,11 +50,11 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, Sid } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { - getName() = ["strncat", "wcsncat", "_mbsncat", "_mbsncat_l"] and + this.getName() = ["strncat", "wcsncat", "_mbsncat", "_mbsncat_l"] and input.isParameter(2) and output.isParameterDeref(0) or - getName() = ["_mbsncat_l", "_mbsnbcat_l"] and + this.getName() = ["_mbsncat_l", "_mbsnbcat_l"] and input.isParameter(3) and output.isParameterDeref(0) or diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/Strcpy.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/Strcpy.qll index 432fbf999ef..10b160dee47 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/Strcpy.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/Strcpy.qll @@ -45,22 +45,22 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid ) and // exclude the 2-parameter template versions // that find the size of a fixed size destination buffer. - getNumberOfParameters() = 3 + this.getNumberOfParameters() = 3 } /** * Holds if this is one of the `strcpy_s` variants. */ - private predicate isSVariant() { getName().matches("%\\_s") } + private predicate isSVariant() { this.getName().matches("%\\_s") } /** * Gets the index of the parameter that is the maximum size of the copy (in characters). */ int getParamSize() { - if isSVariant() + if this.isSVariant() then result = 1 else ( - getName().matches(["%ncpy%", "%nbcpy%", "%xfrm%"]) and + this.getName().matches(["%ncpy%", "%nbcpy%", "%xfrm%"]) and result = 2 ) } @@ -68,49 +68,49 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid /** * Gets the index of the parameter that is the source of the copy. */ - int getParamSrc() { if isSVariant() then result = 2 else result = 1 } + int getParamSrc() { if this.isSVariant() then result = 2 else result = 1 } /** * Gets the index of the parameter that is the destination of the copy. */ int getParamDest() { result = 0 } - override predicate hasArrayInput(int bufParam) { bufParam = getParamSrc() } + override predicate hasArrayInput(int bufParam) { bufParam = this.getParamSrc() } - override predicate hasArrayOutput(int bufParam) { bufParam = getParamDest() } + override predicate hasArrayOutput(int bufParam) { bufParam = this.getParamDest() } - override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = getParamSrc() } + override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = this.getParamSrc() } override predicate hasArrayWithVariableSize(int bufParam, int countParam) { - bufParam = getParamDest() and - countParam = getParamSize() + bufParam = this.getParamDest() and + countParam = this.getParamSize() } override predicate hasArrayWithUnknownSize(int bufParam) { - not exists(getParamSize()) and - bufParam = getParamDest() + not exists(this.getParamSize()) and + bufParam = this.getParamDest() } override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { - not exists(getParamSize()) and - input.isParameterDeref(getParamSrc()) and - output.isParameterDeref(getParamDest()) + not exists(this.getParamSize()) and + input.isParameterDeref(this.getParamSrc()) and + output.isParameterDeref(this.getParamDest()) or - not exists(getParamSize()) and - input.isParameterDeref(getParamSrc()) and + not exists(this.getParamSize()) and + input.isParameterDeref(this.getParamSrc()) and output.isReturnValueDeref() or - input.isParameter(getParamDest()) and + input.isParameter(this.getParamDest()) and output.isReturnValue() } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // these may do only a partial copy of the input buffer to the output // buffer - exists(getParamSize()) and - input.isParameter(getParamSrc()) and + exists(this.getParamSize()) and + input.isParameter(this.getParamSrc()) and ( - output.isParameterDeref(getParamDest()) or + output.isParameterDeref(this.getParamDest()) or output.isReturnValueDeref() ) } @@ -120,18 +120,18 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid override predicate hasOnlySpecificWriteSideEffects() { any() } override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) { - i = getParamDest() and + i = this.getParamDest() and buffer = true and mustWrite = false } override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { - i = getParamSrc() and + i = this.getParamSrc() and buffer = true } override ParameterIndex getParameterSizeIndex(ParameterIndex i) { - i = getParamDest() and - result = getParamSize() + i = this.getParamDest() and + result = this.getParamSize() } } diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/Strcrement.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/Strcrement.qll index 4c335c8581e..8f6c17aae54 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/Strcrement.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/Strcrement.qll @@ -29,10 +29,10 @@ private class Strcrement extends ArrayFunction, TaintFunction, SideEffectFunctio this.getParameter(bufParam).getUnspecifiedType() instanceof PointerType } - override predicate hasArrayInput(int bufParam) { hasArrayWithNullTerminator(bufParam) } + override predicate hasArrayInput(int bufParam) { this.hasArrayWithNullTerminator(bufParam) } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { - exists(int index | hasArrayInput(index) | + exists(int index | this.hasArrayInput(index) | input.isParameter(index) and output.isReturnValue() or input.isParameterDeref(index) and output.isReturnValueDeref() @@ -44,6 +44,6 @@ private class Strcrement extends ArrayFunction, TaintFunction, SideEffectFunctio override predicate hasOnlySpecificWriteSideEffects() { any() } override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { - hasArrayInput(i) and buffer = true + this.hasArrayInput(i) and buffer = true } } diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/Swap.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/Swap.qll index b79f7afe5d9..446e659fac5 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/Swap.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/Swap.qll @@ -31,7 +31,7 @@ private class MemberSwap extends TaintFunction, MemberFunction, AliasFunction { this.hasName("swap") and this.getNumberOfParameters() = 1 and this.getParameter(0).getType().(ReferenceType).getBaseType().getUnspecifiedType() = - getDeclaringType() + this.getDeclaringType() } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/System.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/System.qll new file mode 100644 index 00000000000..02a9d0d6744 --- /dev/null +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/System.qll @@ -0,0 +1,43 @@ +import cpp +import semmle.code.cpp.models.interfaces.SideEffect +import semmle.code.cpp.models.interfaces.Alias +import semmle.code.cpp.models.interfaces.CommandExecution + +/** + * A function for running a command using a command interpreter. + */ +private class SystemFunction extends CommandExecutionFunction, ArrayFunction, AliasFunction, + SideEffectFunction { + SystemFunction() { + hasGlobalOrStdName("system") or // system(command) + hasGlobalName("popen") or // popen(command, mode) + // Windows variants + hasGlobalName("_popen") or // _popen(command, mode) + hasGlobalName("_wpopen") or // _wpopen(command, mode) + hasGlobalName("_wsystem") // _wsystem(command) + } + + override predicate hasCommandArgument(FunctionInput input) { input.isParameterDeref(0) } + + override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 or bufParam = 1 } + + override predicate hasArrayInput(int bufParam) { bufParam = 0 or bufParam = 1 } + + override predicate parameterNeverEscapes(int index) { index = 0 or index = 1 } + + override predicate parameterEscapesOnlyViaReturn(int index) { none() } + + override predicate parameterIsAlwaysReturned(int index) { none() } + + override predicate hasOnlySpecificReadSideEffects() { any() } + + override predicate hasOnlySpecificWriteSideEffects() { + hasGlobalOrStdName("system") or + hasGlobalName("_wsystem") + } + + override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { + (i = 0 or i = 1) and + buffer = true + } +} diff --git a/cpp/ql/lib/semmle/code/cpp/models/interfaces/CommandExecution.qll b/cpp/ql/lib/semmle/code/cpp/models/interfaces/CommandExecution.qll new file mode 100644 index 00000000000..a6e32341140 --- /dev/null +++ b/cpp/ql/lib/semmle/code/cpp/models/interfaces/CommandExecution.qll @@ -0,0 +1,23 @@ +/** + * Provides classes for modeling functions that execute new programs by + * interpreting string data as shell commands. To use this QL library, create + * a QL class extending `CommandExecutionFunction` with a characteristic + * predicate that selects the function or set of functions you are modeling. + * Within that class, override the `hasCommandArgument` predicate to indicate + * which parameters are interpreted as shell commands. + */ + +import cpp +import FunctionInputsAndOutputs +import semmle.code.cpp.models.Models + +/** + * A function, such as `exec` or `popen` that starts a new process by + * interpreting a string as a shell command. + */ +abstract class CommandExecutionFunction extends Function { + /** + * Holds if `input` is interpreted as a shell command. + */ + abstract predicate hasCommandArgument(FunctionInput input); +} diff --git a/cpp/ql/lib/semmle/code/cpp/models/interfaces/FlowSource.qll b/cpp/ql/lib/semmle/code/cpp/models/interfaces/FlowSource.qll index 8c80377c8ec..d454257ea86 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/interfaces/FlowSource.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/interfaces/FlowSource.qll @@ -18,6 +18,12 @@ abstract class RemoteFlowSourceFunction extends Function { * Holds if remote data described by `description` flows from `output` of a call to this function. */ abstract predicate hasRemoteFlowSource(FunctionOutput output, string description); + + /** + * Holds if remote data from this source comes from a socket described by + * `input`. There is no result if a socket is not specified. + */ + predicate hasSocketInput(FunctionInput input) { none() } } /** @@ -51,4 +57,10 @@ abstract class RemoteFlowSinkFunction extends Function { * send over a network connection. */ abstract predicate hasRemoteFlowSink(FunctionInput input, string description); + + /** + * Holds if data put into this sink is transmitted through a socket described + * by `input`. There is no result if a socket is not specified. + */ + predicate hasSocketInput(FunctionInput input) { none() } } diff --git a/cpp/ql/lib/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll b/cpp/ql/lib/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll index 4ab55ee5b3f..5e899be68d4 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll @@ -44,7 +44,7 @@ class FunctionInput extends TFunctionInput { * Holds if this is the input value of the parameter with index `index`. * DEPRECATED: Use `isParameter(index)` instead. */ - deprecated final predicate isInParameter(ParameterIndex index) { isParameter(index) } + deprecated final predicate isInParameter(ParameterIndex index) { this.isParameter(index) } /** * Holds if this is the input value pointed to by a pointer parameter to a function, or the input @@ -70,7 +70,9 @@ class FunctionInput extends TFunctionInput { * `index`. * DEPRECATED: Use `isParameterDeref(index)` instead. */ - deprecated final predicate isInParameterPointer(ParameterIndex index) { isParameterDeref(index) } + deprecated final predicate isInParameterPointer(ParameterIndex index) { + this.isParameterDeref(index) + } /** * Holds if this is the input value pointed to by the `this` pointer of an instance member @@ -92,7 +94,7 @@ class FunctionInput extends TFunctionInput { * function. * DEPRECATED: Use `isQualifierObject()` instead. */ - deprecated final predicate isInQualifier() { isQualifierObject() } + deprecated final predicate isInQualifier() { this.isQualifierObject() } /** * Holds if this is the input value of the `this` pointer of an instance member function. @@ -314,7 +316,9 @@ class FunctionOutput extends TFunctionOutput { * index `index`. * DEPRECATED: Use `isParameterDeref(index)` instead. */ - deprecated final predicate isOutParameterPointer(ParameterIndex index) { isParameterDeref(index) } + deprecated final predicate isOutParameterPointer(ParameterIndex index) { + this.isParameterDeref(index) + } /** * Holds if this is the output value pointed to by the `this` pointer of an instance member @@ -336,7 +340,7 @@ class FunctionOutput extends TFunctionOutput { * function. * DEPRECATED: Use `isQualifierObject()` instead. */ - deprecated final predicate isOutQualifier() { isQualifierObject() } + deprecated final predicate isOutQualifier() { this.isQualifierObject() } /** * Holds if this is the value returned by a function. @@ -361,7 +365,7 @@ class FunctionOutput extends TFunctionOutput { * Holds if this is the value returned by a function. * DEPRECATED: Use `isReturnValue()` instead. */ - deprecated final predicate isOutReturnValue() { isReturnValue() } + deprecated final predicate isOutReturnValue() { this.isReturnValue() } /** * Holds if this is the output value pointed to by the return value of a function, if the function @@ -389,7 +393,7 @@ class FunctionOutput extends TFunctionOutput { * function returns a reference. * DEPRECATED: Use `isReturnValueDeref()` instead. */ - deprecated final predicate isOutReturnPointer() { isReturnValueDeref() } + deprecated final predicate isOutReturnPointer() { this.isReturnValueDeref() } /** * Holds if `i >= 0` and `isParameterDeref(i)` holds for this is the value, or diff --git a/cpp/ql/lib/semmle/code/cpp/padding/Padding.qll b/cpp/ql/lib/semmle/code/cpp/padding/Padding.qll index 7446569451d..e9bde69cfda 100644 --- a/cpp/ql/lib/semmle/code/cpp/padding/Padding.qll +++ b/cpp/ql/lib/semmle/code/cpp/padding/Padding.qll @@ -72,7 +72,7 @@ abstract class Architecture extends string { or t instanceof CharType and result = 8 or - t instanceof WideCharType and result = wideCharSize() + t instanceof WideCharType and result = this.wideCharSize() or t instanceof Char8Type and result = 8 or @@ -84,22 +84,22 @@ abstract class Architecture extends string { or t instanceof IntType and result = 32 or - t instanceof LongType and result = longSize() + t instanceof LongType and result = this.longSize() or - t instanceof LongLongType and result = longLongSize() + t instanceof LongLongType and result = this.longLongSize() or - result = enumBitSize(t.(Enum)) + result = this.enumBitSize(t.(Enum)) or - result = integralBitSize(t.(SpecifiedType).getBaseType()) + result = this.integralBitSize(t.(SpecifiedType).getBaseType()) or - result = integralBitSize(t.(TypedefType).getBaseType()) + result = this.integralBitSize(t.(TypedefType).getBaseType()) } /** * Gets the bit size of enum type `e`. */ int enumBitSize(Enum e) { - result = integralBitSize(e.getExplicitUnderlyingType()) + result = this.integralBitSize(e.getExplicitUnderlyingType()) or not exists(e.getExplicitUnderlyingType()) and result = 32 } @@ -108,7 +108,7 @@ abstract class Architecture extends string { * Gets the alignment of enum type `e`. */ int enumAlignment(Enum e) { - result = alignment(e.getExplicitUnderlyingType()) + result = this.alignment(e.getExplicitUnderlyingType()) or not exists(e.getExplicitUnderlyingType()) and result = 32 } @@ -120,26 +120,26 @@ abstract class Architecture extends string { */ cached int bitSize(Type t) { - result = integralBitSize(t) + result = this.integralBitSize(t) or t instanceof FloatType and result = 32 or t instanceof DoubleType and result = 64 or - t instanceof LongDoubleType and result = longDoubleSize() + t instanceof LongDoubleType and result = this.longDoubleSize() or - t instanceof PointerType and result = pointerSize() + t instanceof PointerType and result = this.pointerSize() or - t instanceof ReferenceType and result = pointerSize() + t instanceof ReferenceType and result = this.pointerSize() or - t instanceof FunctionPointerType and result = pointerSize() + t instanceof FunctionPointerType and result = this.pointerSize() or - result = bitSize(t.(SpecifiedType).getBaseType()) + result = this.bitSize(t.(SpecifiedType).getBaseType()) or - result = bitSize(t.(TypedefType).getBaseType()) + result = this.bitSize(t.(TypedefType).getBaseType()) or exists(ArrayType array | array = t | - result = array.getArraySize() * paddedSize(array.getBaseType()) + result = array.getArraySize() * this.paddedSize(array.getBaseType()) ) or result = t.(PaddedType).typeBitSize(this) @@ -155,7 +155,7 @@ abstract class Architecture extends string { or t instanceof CharType and result = 8 or - t instanceof WideCharType and result = wideCharSize() + t instanceof WideCharType and result = this.wideCharSize() or t instanceof Char8Type and result = 8 or @@ -169,27 +169,27 @@ abstract class Architecture extends string { or t instanceof FloatType and result = 32 or - t instanceof DoubleType and result = doubleAlign() + t instanceof DoubleType and result = this.doubleAlign() or - t instanceof LongType and result = longSize() + t instanceof LongType and result = this.longSize() or - t instanceof LongDoubleType and result = longDoubleAlign() + t instanceof LongDoubleType and result = this.longDoubleAlign() or - t instanceof LongLongType and result = longLongAlign() + t instanceof LongLongType and result = this.longLongAlign() or - t instanceof PointerType and result = pointerSize() + t instanceof PointerType and result = this.pointerSize() or - t instanceof FunctionPointerType and result = pointerSize() + t instanceof FunctionPointerType and result = this.pointerSize() or - t instanceof ReferenceType and result = pointerSize() + t instanceof ReferenceType and result = this.pointerSize() or - result = enumAlignment(t.(Enum)) + result = this.enumAlignment(t.(Enum)) or - result = alignment(t.(SpecifiedType).getBaseType()) + result = this.alignment(t.(SpecifiedType).getBaseType()) or - result = alignment(t.(TypedefType).getBaseType()) + result = this.alignment(t.(TypedefType).getBaseType()) or - result = alignment(t.(ArrayType).getBaseType()) + result = this.alignment(t.(ArrayType).getBaseType()) or result = t.(PaddedType).typeAlignment(this) } @@ -203,7 +203,7 @@ abstract class Architecture extends string { exists(Type realType | realType = stripSpecifiers(t) | if realType instanceof PaddedType then result = realType.(PaddedType).paddedSize(this) - else result = bitSize(realType) + else result = this.bitSize(realType) ) } @@ -429,7 +429,7 @@ class PaddedType extends Class { * Gets the number of bits wasted by padding at the end of this * struct. */ - int trailingPadding(Architecture arch) { result = paddedSize(arch) - arch.bitSize(this) } + int trailingPadding(Architecture arch) { result = this.paddedSize(arch) - arch.bitSize(this) } /** * Gets the number of bits wasted in this struct definition; that is. @@ -440,7 +440,7 @@ class PaddedType extends Class { * laid out one after another, and hence there is no padding between * them. */ - int wastedSpace(Architecture arch) { result = arch.paddedSize(this) - dataSize(arch) } + int wastedSpace(Architecture arch) { result = arch.paddedSize(this) - this.dataSize(arch) } /** * Gets the total size of all fields declared in this class, not including any @@ -448,8 +448,8 @@ class PaddedType extends Class { */ private int fieldDataSize(Architecture arch) { if this instanceof Union - then result = max(Field f | f = this.getAMember() | fieldSize(f, arch)) - else result = sum(Field f | f = this.getAMember() | fieldSize(f, arch)) + then result = max(Field f | f = this.getAMember() | this.fieldSize(f, arch)) + else result = sum(Field f | f = this.getAMember() | this.fieldSize(f, arch)) } /** @@ -472,7 +472,7 @@ class PaddedType extends Class { * reorganizing member structs' field layouts. */ int optimalSize(Architecture arch) { - result = alignUp(dataSize(arch), arch.alignment(this)).maximum(8) + result = alignUp(this.dataSize(arch), arch.alignment(this)).maximum(8) } /** @@ -490,11 +490,11 @@ class PaddedType extends Class { // but that uses a recursive aggregate, which isn't supported in // QL. We therefore use this slightly more complex implementation // instead. - result = biggestFieldSizeUpTo(lastFieldIndex(), arch) + result = this.biggestFieldSizeUpTo(this.lastFieldIndex(), arch) else // If we're not a union type, the size is the padded // sum of field sizes, padded. - result = fieldEnd(lastFieldIndex(), arch) + result = this.fieldEnd(this.lastFieldIndex(), arch) } /** @@ -522,8 +522,8 @@ class PaddedType extends Class { if index = 0 then result = 0 else - exists(Field f, int fSize | index = fieldIndex(f) and fSize = fieldSize(f, arch) | - result = fSize.maximum(biggestFieldSizeUpTo(index - 1, arch)) + exists(Field f, int fSize | index = this.fieldIndex(f) and fSize = this.fieldSize(f, arch) | + result = fSize.maximum(this.biggestFieldSizeUpTo(index - 1, arch)) ) } @@ -536,8 +536,10 @@ class PaddedType extends Class { if index = 0 then result = 1 // Minimum possible alignment else - exists(Field f, int fAlign | index = fieldIndex(f) and fAlign = arch.alignment(f.getType()) | - result = fAlign.maximum(biggestAlignmentUpTo(index - 1, arch)) + exists(Field f, int fAlign | + index = this.fieldIndex(f) and fAlign = arch.alignment(f.getType()) + | + result = fAlign.maximum(this.biggestAlignmentUpTo(index - 1, arch)) ) } @@ -545,17 +547,18 @@ class PaddedType extends Class { * Gets the 1-based index for each field. */ int fieldIndex(Field f) { - memberIndex(f) = rank[result](Field field, int index | memberIndex(field) = index | index) + this.memberIndex(f) = + rank[result](Field field, int index | this.memberIndex(field) = index | index) } - private int memberIndex(Field f) { result = min(int i | getCanonicalMember(i) = f) } + private int memberIndex(Field f) { result = min(int i | this.getCanonicalMember(i) = f) } /** * Gets the 1-based index for the last field. */ int lastFieldIndex() { - if exists(lastField()) - then result = fieldIndex(lastField()) + if exists(this.lastField()) + then result = this.fieldIndex(this.lastField()) else // Field indices are 1-based, so return 0 to represent the lack of fields. result = 0 @@ -566,25 +569,27 @@ class PaddedType extends Class { * `arch`. */ int fieldSize(Field f, Architecture arch) { - exists(fieldIndex(f)) and + exists(this.fieldIndex(f)) and if f instanceof BitField then result = f.(BitField).getNumBits() else result = arch.paddedSize(f.getType()) } /** Gets the last field of this type. */ - Field lastField() { fieldIndex(result) = max(Field other | | fieldIndex(other)) } + Field lastField() { this.fieldIndex(result) = max(Field other | | this.fieldIndex(other)) } /** * Gets the offset, in bits, of the end of the class' last base class * subobject, or zero if the class has no base classes. */ int baseClassEnd(Architecture arch) { - if exists(getABaseClass()) then result = arch.baseClassSize(getADerivation()) else result = 0 + if exists(this.getABaseClass()) + then result = arch.baseClassSize(this.getADerivation()) + else result = 0 } /** Gets the bitfield at field index `index`, if that field is a bitfield. */ - private BitField bitFieldAt(int index) { fieldIndex(result) = index } + private BitField bitFieldAt(int index) { this.fieldIndex(result) = index } /** * Gets the 0-based offset, in bits, of the first free bit after @@ -596,13 +601,13 @@ class PaddedType extends Class { then // Base case: No fields seen yet, so return the offset of the end of the // base class subojects. - result = baseClassEnd(arch) + result = this.baseClassEnd(arch) else - exists(Field f | index = fieldIndex(f) | - exists(int fSize | fSize = fieldSize(f, arch) | + exists(Field f | index = this.fieldIndex(f) | + exists(int fSize | fSize = this.fieldSize(f, arch) | // Recursive case: Take previous field's end point, pad and add // this field's size - exists(int firstFree | firstFree = fieldEnd(index - 1, arch) | + exists(int firstFree | firstFree = this.fieldEnd(index - 1, arch) | if f instanceof BitField then // Bitfield packing: @@ -629,9 +634,11 @@ class PaddedType extends Class { // No additional restrictions, so just pack it in with no padding. result = firstFree + fSize ) else ( - if exists(bitFieldAt(index - 1)) + if exists(this.bitFieldAt(index - 1)) then - exists(BitField previousBitField | previousBitField = bitFieldAt(index - 1) | + exists(BitField previousBitField | + previousBitField = this.bitFieldAt(index - 1) + | // Previous field was a bitfield. if nextSizeofBoundary >= (firstFree + fSize) and diff --git a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/RangeSSA.qll b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/RangeSSA.qll index bc66d9b2dd0..5aebf07f2f1 100644 --- a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/RangeSSA.qll +++ b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/RangeSSA.qll @@ -88,7 +88,7 @@ class RangeSsaDefinition extends ControlFlowNodeBase { ControlFlowNode getDefinition() { result = this } /** Gets the basic block containing this definition. */ - BasicBlock getBasicBlock() { result.contains(getDefinition()) } + BasicBlock getBasicBlock() { result.contains(this.getDefinition()) } /** Whether this definition is a phi node for variable `v`. */ predicate isPhiNode(StackVariable v) { exists(RangeSSA x | x.phi_node(v, this.(BasicBlock))) } diff --git a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll index 5ca9339ae01..0be94ed4e62 100644 --- a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll +++ b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll @@ -127,17 +127,22 @@ private string getValue(Expr e) { private class UnsignedBitwiseAndExpr extends BitwiseAndExpr { UnsignedBitwiseAndExpr() { ( - getLeftOperand().getFullyConverted().getType().getUnderlyingType().(IntegralType).isUnsigned() or - getValue(getLeftOperand().getFullyConverted()).toInt() >= 0 - ) and - ( - getRightOperand() + this.getLeftOperand() .getFullyConverted() .getType() .getUnderlyingType() .(IntegralType) .isUnsigned() or - getValue(getRightOperand().getFullyConverted()).toInt() >= 0 + getValue(this.getLeftOperand().getFullyConverted()).toInt() >= 0 + ) and + ( + this.getRightOperand() + .getFullyConverted() + .getType() + .getUnderlyingType() + .(IntegralType) + .isUnsigned() or + getValue(this.getRightOperand().getFullyConverted()).toInt() >= 0 ) } } @@ -1586,6 +1591,15 @@ private module SimpleRangeAnalysisCached { result = min([max(getTruncatedUpperBounds(expr)), getGuardedUpperBound(expr)]) } + /** Holds if the upper bound of `expr` may have been widened. This means the the upper bound is in practice likely to be overly wide. */ + cached + predicate upperBoundMayBeWidened(Expr e) { + isRecursiveExpr(e) and + // Widening is not a problem if the post-analysis in `getGuardedUpperBound` has overridden the widening. + // Note that the RHS of `<` may be multi-valued. + not getGuardedUpperBound(e) < getTruncatedUpperBounds(e) + } + /** * Holds if `expr` has a provably empty range. For example: * diff --git a/cpp/ql/lib/semmle/code/cpp/security/BufferWrite.qll b/cpp/ql/lib/semmle/code/cpp/security/BufferWrite.qll index 2726b2eca00..61845654721 100644 --- a/cpp/ql/lib/semmle/code/cpp/security/BufferWrite.qll +++ b/cpp/ql/lib/semmle/code/cpp/security/BufferWrite.qll @@ -77,21 +77,21 @@ abstract class BufferWrite extends Expr { * much smaller (8 bytes) than their true maximum length. This can be * helpful in determining the cause of a buffer overflow issue. */ - int getMaxDataLimited() { result = getMaxData() } + int getMaxDataLimited() { result = this.getMaxData() } /** * Gets the size of a single character of the type this * operation works with, in bytes. */ int getCharSize() { - result = getBufferType().(PointerType).getBaseType().getSize() or - result = getBufferType().(ArrayType).getBaseType().getSize() + result = this.getBufferType().(PointerType).getBaseType().getSize() or + result = this.getBufferType().(ArrayType).getBaseType().getSize() } /** * Gets a description of this buffer write. */ - string getBWDesc() { result = toString() } + string getBWDesc() { result = this.toString() } } /** @@ -109,7 +109,7 @@ abstract class BufferWriteCall extends BufferWrite, FunctionCall { } class StrCopyBW extends BufferWriteCall { StrcpyFunction f; - StrCopyBW() { getTarget() = f.(TopLevelFunction) } + StrCopyBW() { this.getTarget() = f.(TopLevelFunction) } /** * Gets the index of the parameter that is the maximum size of the copy (in characters). @@ -122,21 +122,22 @@ class StrCopyBW extends BufferWriteCall { int getParamSrc() { result = f.getParamSrc() } override Type getBufferType() { - result = this.getTarget().getParameter(getParamSrc()).getUnspecifiedType() + result = this.getTarget().getParameter(this.getParamSrc()).getUnspecifiedType() } - override Expr getASource() { result = getArgument(getParamSrc()) } + override Expr getASource() { result = this.getArgument(this.getParamSrc()) } - override Expr getDest() { result = getArgument(f.getParamDest()) } + override Expr getDest() { result = this.getArgument(f.getParamDest()) } - override predicate hasExplicitLimit() { exists(getParamSize()) } + override predicate hasExplicitLimit() { exists(this.getParamSize()) } override int getExplicitLimit() { - result = getArgument(getParamSize()).getValue().toInt() * getCharSize() + result = this.getArgument(this.getParamSize()).getValue().toInt() * this.getCharSize() } override int getMaxData() { - result = getArgument(getParamSrc()).(AnalysedString).getMaxLength() * getCharSize() + result = + this.getArgument(this.getParamSrc()).(AnalysedString).getMaxLength() * this.getCharSize() } } @@ -146,7 +147,7 @@ class StrCopyBW extends BufferWriteCall { class StrCatBW extends BufferWriteCall { StrcatFunction f; - StrCatBW() { getTarget() = f.(TopLevelFunction) } + StrCatBW() { this.getTarget() = f.(TopLevelFunction) } /** * Gets the index of the parameter that is the maximum size of the copy (in characters). @@ -159,21 +160,22 @@ class StrCatBW extends BufferWriteCall { int getParamSrc() { result = f.getParamSrc() } override Type getBufferType() { - result = this.getTarget().getParameter(getParamSrc()).getUnspecifiedType() + result = this.getTarget().getParameter(this.getParamSrc()).getUnspecifiedType() } - override Expr getASource() { result = getArgument(getParamSrc()) } + override Expr getASource() { result = this.getArgument(this.getParamSrc()) } - override Expr getDest() { result = getArgument(f.getParamDest()) } + override Expr getDest() { result = this.getArgument(f.getParamDest()) } - override predicate hasExplicitLimit() { exists(getParamSize()) } + override predicate hasExplicitLimit() { exists(this.getParamSize()) } override int getExplicitLimit() { - result = getArgument(getParamSize()).getValue().toInt() * getCharSize() + result = this.getArgument(this.getParamSize()).getValue().toInt() * this.getCharSize() } override int getMaxData() { - result = getArgument(getParamSrc()).(AnalysedString).getMaxLength() * getCharSize() + result = + this.getArgument(this.getParamSrc()).(AnalysedString).getMaxLength() * this.getCharSize() } } @@ -184,7 +186,7 @@ class SprintfBW extends BufferWriteCall { FormattingFunction f; SprintfBW() { - exists(string name | f = getTarget().(TopLevelFunction) and name = f.getName() | + exists(string name | f = this.getTarget().(TopLevelFunction) and name = f.getName() | /* * C sprintf variants: */ @@ -229,19 +231,19 @@ class SprintfBW extends BufferWriteCall { result = this.(FormattingFunctionCall).getFormatArgument(_) } - override Expr getDest() { result = getArgument(f.getOutputParameterIndex(false)) } + override Expr getDest() { result = this.getArgument(f.getOutputParameterIndex(false)) } override int getMaxData() { exists(FormatLiteral fl | fl = this.(FormattingFunctionCall).getFormat() and - result = fl.getMaxConvertedLength() * getCharSize() + result = fl.getMaxConvertedLength() * this.getCharSize() ) } override int getMaxDataLimited() { exists(FormatLiteral fl | fl = this.(FormattingFunctionCall).getFormat() and - result = fl.getMaxConvertedLengthLimited() * getCharSize() + result = fl.getMaxConvertedLengthLimited() * this.getCharSize() ) } } @@ -251,7 +253,7 @@ class SprintfBW extends BufferWriteCall { */ class SnprintfBW extends BufferWriteCall { SnprintfBW() { - exists(TopLevelFunction fn, string name | fn = getTarget() and name = fn.getName() | + exists(TopLevelFunction fn, string name | fn = this.getTarget() and name = fn.getName() | /* * C snprintf variants: */ @@ -326,25 +328,25 @@ class SnprintfBW extends BufferWriteCall { result = this.(FormattingFunctionCall).getFormatArgument(_) } - override Expr getDest() { result = getArgument(0) } + override Expr getDest() { result = this.getArgument(0) } - override predicate hasExplicitLimit() { exists(getParamSize()) } + override predicate hasExplicitLimit() { exists(this.getParamSize()) } override int getExplicitLimit() { - result = getArgument(getParamSize()).getValue().toInt() * getCharSize() + result = this.getArgument(this.getParamSize()).getValue().toInt() * this.getCharSize() } override int getMaxData() { exists(FormatLiteral fl | fl = this.(FormattingFunctionCall).getFormat() and - result = fl.getMaxConvertedLength() * getCharSize() + result = fl.getMaxConvertedLength() * this.getCharSize() ) } override int getMaxDataLimited() { exists(FormatLiteral fl | fl = this.(FormattingFunctionCall).getFormat() and - result = fl.getMaxConvertedLengthLimited() * getCharSize() + result = fl.getMaxConvertedLengthLimited() * this.getCharSize() ) } } @@ -354,7 +356,7 @@ class SnprintfBW extends BufferWriteCall { */ class GetsBW extends BufferWriteCall { GetsBW() { - getTarget().(TopLevelFunction).getName() = + this.getTarget().(TopLevelFunction).getName() = [ "gets", // gets(dst) "fgets", // fgets(dst, max_amount, src_stream) @@ -365,24 +367,24 @@ class GetsBW extends BufferWriteCall { /** * Gets the index of the parameter that is the maximum number of characters to be read. */ - int getParamSize() { if exists(getArgument(1)) then result = 1 else none() } + int getParamSize() { exists(this.getArgument(1)) and result = 1 } override Type getBufferType() { result = this.getTarget().getParameter(0).getUnspecifiedType() } override Expr getASource() { - if exists(getArgument(2)) - then result = getArgument(2) + if exists(this.getArgument(2)) + then result = this.getArgument(2) else // the source is input inside the 'gets' call itself result = this } - override Expr getDest() { result = getArgument(0) } + override Expr getDest() { result = this.getArgument(0) } - override predicate hasExplicitLimit() { exists(getParamSize()) } + override predicate hasExplicitLimit() { exists(this.getParamSize()) } override int getExplicitLimit() { - result = getArgument(getParamSize()).getValue().toInt() * getCharSize() + result = this.getArgument(this.getParamSize()).getValue().toInt() * this.getCharSize() } } @@ -438,7 +440,7 @@ class ScanfBW extends BufferWrite { exists(ScanfFunctionCall fc, ScanfFormatLiteral fl, int arg | this = fc.getArgument(arg) and fl = fc.getFormat() and - result = (fl.getMaxConvertedLength(arg - getParamArgs()) + 1) * getCharSize() // +1 is for the terminating null + result = (fl.getMaxConvertedLength(arg - this.getParamArgs()) + 1) * this.getCharSize() // +1 is for the terminating null ) } @@ -463,14 +465,14 @@ private int path_max() { class RealpathBW extends BufferWriteCall { RealpathBW() { exists(path_max()) and // Ignore realpath() calls if PATH_MAX cannot be determined - getTarget().hasGlobalName("realpath") // realpath(path, resolved_path); + this.getTarget().hasGlobalName("realpath") // realpath(path, resolved_path); } override Type getBufferType() { result = this.getTarget().getParameter(0).getUnspecifiedType() } - override Expr getDest() { result = getArgument(1) } + override Expr getDest() { result = this.getArgument(1) } - override Expr getASource() { result = getArgument(0) } + override Expr getASource() { result = this.getArgument(0) } override int getMaxData() { result = path_max() and diff --git a/cpp/ql/lib/semmle/code/cpp/security/CommandExecution.qll b/cpp/ql/lib/semmle/code/cpp/security/CommandExecution.qll index d9bb701be58..063c7300031 100644 --- a/cpp/ql/lib/semmle/code/cpp/security/CommandExecution.qll +++ b/cpp/ql/lib/semmle/code/cpp/security/CommandExecution.qll @@ -4,42 +4,20 @@ import cpp import semmle.code.cpp.security.FunctionWithWrappers import semmle.code.cpp.models.interfaces.SideEffect import semmle.code.cpp.models.interfaces.Alias +import semmle.code.cpp.models.interfaces.CommandExecution /** * A function for running a command using a command interpreter. */ -class SystemFunction extends FunctionWithWrappers, ArrayFunction, AliasFunction, SideEffectFunction { - SystemFunction() { - hasGlobalOrStdName("system") or // system(command) - hasGlobalName("popen") or // popen(command, mode) - // Windows variants - hasGlobalName("_popen") or // _popen(command, mode) - hasGlobalName("_wpopen") or // _wpopen(command, mode) - hasGlobalName("_wsystem") // _wsystem(command) - } - - override predicate interestingArg(int arg) { arg = 0 } - - override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 or bufParam = 1 } - - override predicate hasArrayInput(int bufParam) { bufParam = 0 or bufParam = 1 } - - override predicate parameterNeverEscapes(int index) { index = 0 or index = 1 } - - override predicate parameterEscapesOnlyViaReturn(int index) { none() } - - override predicate parameterIsAlwaysReturned(int index) { none() } - - override predicate hasOnlySpecificReadSideEffects() { any() } - - override predicate hasOnlySpecificWriteSideEffects() { - hasGlobalOrStdName("system") or - hasGlobalName("_wsystem") - } - - override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { - (i = 0 or i = 1) and - buffer = true +class SystemFunction extends FunctionWithWrappers instanceof CommandExecutionFunction { + override predicate interestingArg(int arg) { + exists(FunctionInput input | + this.(CommandExecutionFunction).hasCommandArgument(input) and + ( + input.isParameterDerefOrQualifierObject(arg) or + input.isParameterOrQualifierAddress(arg) + ) + ) } } @@ -50,35 +28,19 @@ class SystemFunction extends FunctionWithWrappers, ArrayFunction, AliasFunction, */ class VarargsExecFunctionCall extends FunctionCall { VarargsExecFunctionCall() { - getTarget().hasGlobalName("execl") or - getTarget().hasGlobalName("execle") or - getTarget().hasGlobalName("execlp") or - // Windows - getTarget().hasGlobalName("_execl") or - getTarget().hasGlobalName("_execle") or - getTarget().hasGlobalName("_execlp") or - getTarget().hasGlobalName("_execlpe") or - getTarget().hasGlobalName("_spawnl") or - getTarget().hasGlobalName("_spawnle") or - getTarget().hasGlobalName("_spawnlp") or - getTarget().hasGlobalName("_spawnlpe") or - getTarget().hasGlobalName("_wexecl") or - getTarget().hasGlobalName("_wexecle") or - getTarget().hasGlobalName("_wexeclp") or - getTarget().hasGlobalName("_wexeclpe") or - getTarget().hasGlobalName("_wspawnl") or - getTarget().hasGlobalName("_wspawnle") or - getTarget().hasGlobalName("_wspawnlp") or - getTarget().hasGlobalName("_wspawnlpe") + getTarget() + .hasGlobalName([ + "execl", "execle", "execlp", + // Windows + "_execl", "_execle", "_execlp", "_execlpe", "_spawnl", "_spawnle", "_spawnlp", + "_spawnlpe", "_wexecl", "_wexecle", "_wexeclp", "_wexeclpe", "_wspawnl", "_wspawnle", + "_wspawnlp", "_wspawnlpe" + ]) } /** Whether the last argument to the function is an environment pointer */ predicate hasEnvironmentArgument() { - getTarget().hasGlobalName("execle") or - getTarget().hasGlobalName("_execle") or - getTarget().hasGlobalName("_execlpe") or - getTarget().hasGlobalName("_wexecle") or - getTarget().hasGlobalName("_wexeclpe") + getTarget().hasGlobalName(["execle", "_execle", "_execlpe", "_wexecle", "_wexeclpe"]) } /** @@ -105,11 +67,7 @@ class VarargsExecFunctionCall extends FunctionCall { * all the other ones start with the command. */ private int getCommandIdx() { - if - getTarget().getName().matches("\\_spawn%") or - getTarget().getName().matches("\\_wspawn%") - then result = 1 - else result = 0 + if getTarget().getName().matches(["\\_spawn%", "\\_wspawn%"]) then result = 1 else result = 0 } } @@ -120,28 +78,14 @@ class VarargsExecFunctionCall extends FunctionCall { */ class ArrayExecFunctionCall extends FunctionCall { ArrayExecFunctionCall() { - getTarget().hasGlobalName("execv") or - getTarget().hasGlobalName("execvp") or - getTarget().hasGlobalName("execvpe") or - getTarget().hasGlobalName("execve") or - getTarget().hasGlobalName("fexecve") or - // Windows variants - getTarget().hasGlobalName("_execv") or - getTarget().hasGlobalName("_execve") or - getTarget().hasGlobalName("_execvp") or - getTarget().hasGlobalName("_execvpe") or - getTarget().hasGlobalName("_spawnv") or - getTarget().hasGlobalName("_spawnve") or - getTarget().hasGlobalName("_spawnvp") or - getTarget().hasGlobalName("_spawnvpe") or - getTarget().hasGlobalName("_wexecv") or - getTarget().hasGlobalName("_wexecve") or - getTarget().hasGlobalName("_wexecvp") or - getTarget().hasGlobalName("_wexecvpe") or - getTarget().hasGlobalName("_wspawnv") or - getTarget().hasGlobalName("_wspawnve") or - getTarget().hasGlobalName("_wspawnvp") or - getTarget().hasGlobalName("_wspawnvpe") + getTarget() + .hasGlobalName([ + "execv", "execvp", "execvpe", "execve", "fexecve", + // Windows variants + "_execv", "_execve", "_execvp", "_execvpe", "_spawnv", "_spawnve", "_spawnvp", + "_spawnvpe", "_wexecv", "_wexecve", "_wexecvp", "_wexecvpe", "_wspawnv", "_wspawnve", + "_wspawnvp", "_wspawnvpe" + ]) } /** The argument with the array of command arguments */ @@ -155,11 +99,7 @@ class ArrayExecFunctionCall extends FunctionCall { * all the other ones start with the command. */ private int getCommandIdx() { - if - getTarget().getName().matches("\\_spawn%") or - getTarget().getName().matches("\\_wspawn%") - then result = 1 - else result = 0 + if getTarget().getName().matches(["\\_spawn%", "\\_wspawn%"]) then result = 1 else result = 0 } } diff --git a/cpp/ql/lib/semmle/code/cpp/security/FileWrite.qll b/cpp/ql/lib/semmle/code/cpp/security/FileWrite.qll index 7c3d893b471..36f8b58d812 100644 --- a/cpp/ql/lib/semmle/code/cpp/security/FileWrite.qll +++ b/cpp/ql/lib/semmle/code/cpp/security/FileWrite.qll @@ -52,9 +52,9 @@ class BasicOStreamClass extends Type { */ class BasicOStreamCall extends FunctionCall { BasicOStreamCall() { - if getTarget() instanceof MemberFunction - then getQualifier().getType() instanceof BasicOStreamClass - else getArgument(0).getType() instanceof BasicOStreamClass + if this.getTarget() instanceof MemberFunction + then this.getQualifier().getType() instanceof BasicOStreamClass + else this.getArgument(0).getType() instanceof BasicOStreamClass } } @@ -77,10 +77,10 @@ abstract class ChainedOutputCall extends BasicOStreamCall { */ Expr getEndDest() { // recurse into the destination - result = getDest().(ChainedOutputCall).getEndDest() + result = this.getDest().(ChainedOutputCall).getEndDest() or // or return something other than a ChainedOutputCall - result = getDest() and + result = this.getDest() and not result instanceof ChainedOutputCall } } @@ -89,18 +89,18 @@ abstract class ChainedOutputCall extends BasicOStreamCall { * A call to `operator<<` on an output stream. */ class OperatorLShiftCall extends ChainedOutputCall { - OperatorLShiftCall() { getTarget().(Operator).hasName("operator<<") } + OperatorLShiftCall() { this.getTarget().(Operator).hasName("operator<<") } override Expr getSource() { - if getTarget() instanceof MemberFunction - then result = getArgument(0) - else result = getArgument(1) + if this.getTarget() instanceof MemberFunction + then result = this.getArgument(0) + else result = this.getArgument(1) } override Expr getDest() { - if getTarget() instanceof MemberFunction - then result = getQualifier() - else result = getArgument(0) + if this.getTarget() instanceof MemberFunction + then result = this.getQualifier() + else result = this.getArgument(0) } } @@ -108,22 +108,22 @@ class OperatorLShiftCall extends ChainedOutputCall { * A call to 'put'. */ class PutFunctionCall extends ChainedOutputCall { - PutFunctionCall() { getTarget().(MemberFunction).hasName("put") } + PutFunctionCall() { this.getTarget().(MemberFunction).hasName("put") } - override Expr getSource() { result = getArgument(0) } + override Expr getSource() { result = this.getArgument(0) } - override Expr getDest() { result = getQualifier() } + override Expr getDest() { result = this.getQualifier() } } /** * A call to 'write'. */ class WriteFunctionCall extends ChainedOutputCall { - WriteFunctionCall() { getTarget().(MemberFunction).hasName("write") } + WriteFunctionCall() { this.getTarget().(MemberFunction).hasName("write") } - override Expr getSource() { result = getArgument(0) } + override Expr getSource() { result = this.getArgument(0) } - override Expr getDest() { result = getQualifier() } + override Expr getDest() { result = this.getQualifier() } } /** diff --git a/cpp/ql/lib/semmle/code/cpp/security/FlowSources.qll b/cpp/ql/lib/semmle/code/cpp/security/FlowSources.qll index b080651951f..d2c90a38075 100644 --- a/cpp/ql/lib/semmle/code/cpp/security/FlowSources.qll +++ b/cpp/ql/lib/semmle/code/cpp/security/FlowSources.qll @@ -24,7 +24,7 @@ private class RemoteReturnSource extends RemoteFlowSource { RemoteReturnSource() { exists(RemoteFlowSourceFunction func, CallInstruction instr, FunctionOutput output | - asInstruction() = instr and + this.asInstruction() = instr and instr.getStaticCallTarget() = func and func.hasRemoteFlowSource(output, sourceType) and ( @@ -43,7 +43,7 @@ private class RemoteParameterSource extends RemoteFlowSource { RemoteParameterSource() { exists(RemoteFlowSourceFunction func, WriteSideEffectInstruction instr, FunctionOutput output | - asInstruction() = instr and + this.asInstruction() = instr and instr.getPrimaryInstruction().(CallInstruction).getStaticCallTarget() = func and func.hasRemoteFlowSource(output, sourceType) and output.isParameterDerefOrQualifierObject(instr.getIndex()) @@ -58,7 +58,7 @@ private class LocalReturnSource extends LocalFlowSource { LocalReturnSource() { exists(LocalFlowSourceFunction func, CallInstruction instr, FunctionOutput output | - asInstruction() = instr and + this.asInstruction() = instr and instr.getStaticCallTarget() = func and func.hasLocalFlowSource(output, sourceType) and ( @@ -77,7 +77,7 @@ private class LocalParameterSource extends LocalFlowSource { LocalParameterSource() { exists(LocalFlowSourceFunction func, WriteSideEffectInstruction instr, FunctionOutput output | - asInstruction() = instr and + this.asInstruction() = instr and instr.getPrimaryInstruction().(CallInstruction).getStaticCallTarget() = func and func.hasLocalFlowSource(output, sourceType) and output.isParameterDerefOrQualifierObject(instr.getIndex()) diff --git a/cpp/ql/lib/semmle/code/cpp/security/FunctionWithWrappers.qll b/cpp/ql/lib/semmle/code/cpp/security/FunctionWithWrappers.qll index 5451011b351..b7a7a95a427 100644 --- a/cpp/ql/lib/semmle/code/cpp/security/FunctionWithWrappers.qll +++ b/cpp/ql/lib/semmle/code/cpp/security/FunctionWithWrappers.qll @@ -17,7 +17,7 @@ import cpp import PrintfLike -private import TaintTracking +private import semmle.code.cpp.ir.dataflow.ResolveCall bindingset[index] private string toCause(Function func, int index) { @@ -77,7 +77,7 @@ abstract class FunctionWithWrappers extends Function { ) { // base case func = this and - interestingArg(paramIndex) and + this.interestingArg(paramIndex) and callChain = toCause(func, paramIndex) and depth = 0 or @@ -101,7 +101,7 @@ abstract class FunctionWithWrappers extends Function { private predicate wrapperFunctionAnyDepth(Function func, int paramIndex, string cause) { // base case func = this and - interestingArg(paramIndex) and + this.interestingArg(paramIndex) and cause = toCause(func, paramIndex) or // recursive step @@ -147,7 +147,7 @@ abstract class FunctionWithWrappers extends Function { ) or not this.wrapperFunctionLimitedDepth(func, paramIndex, _, _) and - cause = wrapperFunctionAnyDepthUnique(func, paramIndex) + cause = this.wrapperFunctionAnyDepthUnique(func, paramIndex) } /** diff --git a/cpp/ql/lib/semmle/code/cpp/security/OutputWrite.qll b/cpp/ql/lib/semmle/code/cpp/security/OutputWrite.qll index 9ed22aa970f..affb9954926 100644 --- a/cpp/ql/lib/semmle/code/cpp/security/OutputWrite.qll +++ b/cpp/ql/lib/semmle/code/cpp/security/OutputWrite.qll @@ -21,14 +21,12 @@ class OutputWrite extends Expr { * A standard output or standard error variable. */ private predicate outputVariable(Variable v) { - // standard output - v.hasName("cout") or - v.hasName("wcout") or - // standard error - v.hasName("cerr") or - v.hasName("clog") or - v.hasName("wcerr") or - v.hasName("wclog") + v.hasName([ + // standard output + "cout", "wcout", + // standard error + "cerr", "clog", "wcerr", "wclog" + ]) } /** @@ -64,10 +62,7 @@ private predicate outputWrite(Expr write, Expr source) { arg >= f.(FormattingFunction).getFormatParameterIndex() or // puts, putchar - ( - f.hasGlobalOrStdName("puts") or - f.hasGlobalOrStdName("putchar") - ) and + f.hasGlobalOrStdName(["puts", "putchar"]) and arg = 0 or exists(Call wrappedCall, Expr wrappedSource | diff --git a/cpp/ql/lib/semmle/code/cpp/security/Security.qll b/cpp/ql/lib/semmle/code/cpp/security/Security.qll index da808592b3e..7a73144f5fa 100644 --- a/cpp/ql/lib/semmle/code/cpp/security/Security.qll +++ b/cpp/ql/lib/semmle/code/cpp/security/Security.qll @@ -78,7 +78,7 @@ class SecurityOptions extends string { functionCall.getTarget().getName() = fname and ( fname = ["fgets", "gets"] or - userInputReturn(fname) + this.userInputReturn(fname) ) ) or diff --git a/cpp/ql/lib/semmle/code/cpp/security/SensitiveExprs.qll b/cpp/ql/lib/semmle/code/cpp/security/SensitiveExprs.qll index 22e0ee71b66..389129835cb 100644 --- a/cpp/ql/lib/semmle/code/cpp/security/SensitiveExprs.qll +++ b/cpp/ql/lib/semmle/code/cpp/security/SensitiveExprs.qll @@ -11,17 +11,8 @@ import cpp */ bindingset[s] private predicate suspicious(string s) { - ( - s.matches("%password%") or - s.matches("%passwd%") or - s.matches("%trusted%") - ) and - not ( - s.matches("%hash%") or - s.matches("%crypt%") or - s.matches("%file%") or - s.matches("%path%") - ) + s.matches(["%password%", "%passwd%", "%trusted%"]) and + not s.matches(["%hash%", "%crypt%", "%file%", "%path%"]) } /** @@ -29,7 +20,7 @@ private predicate suspicious(string s) { */ class SensitiveVariable extends Variable { SensitiveVariable() { - suspicious(getName().toLowerCase()) and + suspicious(this.getName().toLowerCase()) and not this.getUnspecifiedType() instanceof IntegralType } } @@ -39,7 +30,7 @@ class SensitiveVariable extends Variable { */ class SensitiveFunction extends Function { SensitiveFunction() { - suspicious(getName().toLowerCase()) and + suspicious(this.getName().toLowerCase()) and not this.getUnspecifiedType() instanceof IntegralType } } diff --git a/cpp/ql/lib/semmle/code/cpp/security/boostorg/asio/protocols.qll b/cpp/ql/lib/semmle/code/cpp/security/boostorg/asio/protocols.qll index e113d5e5745..c9d6b6613d8 100644 --- a/cpp/ql/lib/semmle/code/cpp/security/boostorg/asio/protocols.qll +++ b/cpp/ql/lib/semmle/code/cpp/security/boostorg/asio/protocols.qll @@ -113,7 +113,7 @@ module BoostorgAsio { result.getName() = "tls_server" ) or - result = getASslv23ProtocolConstant() + result = this.getASslv23ProtocolConstant() } /** diff --git a/cpp/ql/lib/semmle/code/cpp/stmts/Block.qll b/cpp/ql/lib/semmle/code/cpp/stmts/Block.qll index 3bebc660456..5fe8798cebb 100644 --- a/cpp/ql/lib/semmle/code/cpp/stmts/Block.qll +++ b/cpp/ql/lib/semmle/code/cpp/stmts/Block.qll @@ -76,9 +76,9 @@ class BlockStmt extends Stmt, @stmt_block { * the result is the expression statement `a = b`. */ Stmt getLastStmtIn() { - if getLastStmt() instanceof BlockStmt - then result = getLastStmt().(BlockStmt).getLastStmtIn() - else result = getLastStmt() + if this.getLastStmt() instanceof BlockStmt + then result = this.getLastStmt().(BlockStmt).getLastStmtIn() + else result = this.getLastStmt() } /** diff --git a/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll b/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll index ed1fb4fbb50..af68daf025c 100644 --- a/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll +++ b/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll @@ -27,10 +27,10 @@ class Stmt extends StmtParent, @stmt { */ BlockStmt getEnclosingBlock() { if - getParentStmt() instanceof BlockStmt and - not getParentStmt().(BlockStmt).getLocation() instanceof UnknownLocation - then result = getParentStmt() - else result = getParentStmt().getEnclosingBlock() + this.getParentStmt() instanceof BlockStmt and + not this.getParentStmt().(BlockStmt).getLocation() instanceof UnknownLocation + then result = this.getParentStmt() + else result = this.getParentStmt().getEnclosingBlock() } /** Gets a child of this statement. */ @@ -438,7 +438,7 @@ class WhileStmt extends Loop, @stmt_while { * while(1) { ...; if(b) break; ...; } * ``` */ - predicate conditionAlwaysTrue() { conditionAlwaysTrue(getCondition()) } + predicate conditionAlwaysTrue() { conditionAlwaysTrue(this.getCondition()) } /** * Holds if the loop condition is provably `false`. @@ -448,7 +448,7 @@ class WhileStmt extends Loop, @stmt_while { * while(0) { ...; } * ``` */ - predicate conditionAlwaysFalse() { conditionAlwaysFalse(getCondition()) } + predicate conditionAlwaysFalse() { conditionAlwaysFalse(this.getCondition()) } /** * Holds if the loop condition is provably `true` upon entry, @@ -857,7 +857,7 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for { * ``` * the result is `int x`. */ - LocalVariable getVariable() { result = getChild(4).(DeclStmt).getADeclaration() } + LocalVariable getVariable() { result = this.getChild(4).(DeclStmt).getADeclaration() } /** * Gets the expression giving the range to iterate over. @@ -868,10 +868,10 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for { * ``` * the result is `xs`. */ - Expr getRange() { result = getRangeVariable().getInitializer().getExpr() } + Expr getRange() { result = this.getRangeVariable().getInitializer().getExpr() } /** Gets the compiler-generated `__range` variable after desugaring. */ - LocalVariable getRangeVariable() { result = getChild(0).(DeclStmt).getADeclaration() } + LocalVariable getRangeVariable() { result = this.getChild(0).(DeclStmt).getADeclaration() } /** * Gets the compiler-generated `__begin != __end` which is the @@ -891,10 +891,10 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for { DeclStmt getBeginEndDeclaration() { result = this.getChild(1) } /** Gets the compiler-generated `__begin` variable after desugaring. */ - LocalVariable getBeginVariable() { result = getBeginEndDeclaration().getDeclaration(0) } + LocalVariable getBeginVariable() { result = this.getBeginEndDeclaration().getDeclaration(0) } /** Gets the compiler-generated `__end` variable after desugaring. */ - LocalVariable getEndVariable() { result = getBeginEndDeclaration().getDeclaration(1) } + LocalVariable getEndVariable() { result = this.getBeginEndDeclaration().getDeclaration(1) } /** * Gets the compiler-generated `++__begin` which is the update @@ -905,7 +905,7 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for { Expr getUpdate() { result = this.getChild(3) } /** Gets the compiler-generated `__begin` variable after desugaring. */ - LocalVariable getAnIterationVariable() { result = getBeginVariable() } + LocalVariable getAnIterationVariable() { result = this.getBeginVariable() } } /** @@ -1067,7 +1067,7 @@ class ForStmt extends Loop, @stmt_for { * for(x = 0; 1; ++x) { sum += x; } * ``` */ - predicate conditionAlwaysTrue() { conditionAlwaysTrue(getCondition()) } + predicate conditionAlwaysTrue() { conditionAlwaysTrue(this.getCondition()) } /** * Holds if the loop condition is provably `false`. @@ -1077,7 +1077,7 @@ class ForStmt extends Loop, @stmt_for { * for(x = 0; 0; ++x) { sum += x; } * ``` */ - predicate conditionAlwaysFalse() { conditionAlwaysFalse(getCondition()) } + predicate conditionAlwaysFalse() { conditionAlwaysFalse(this.getCondition()) } /** * Holds if the loop condition is provably `true` upon entry, @@ -1723,10 +1723,10 @@ class Handler extends Stmt, @stmt_handler { /** * Gets the block containing the implementation of this handler. */ - CatchBlock getBlock() { result = getChild(0) } + CatchBlock getBlock() { result = this.getChild(0) } /** Gets the 'try' statement corresponding to this 'catch block'. */ - TryStmt getTryStmt() { result = getParent() } + TryStmt getTryStmt() { result = this.getParent() } /** * Gets the parameter introduced by this 'catch block', if any. @@ -1734,7 +1734,7 @@ class Handler extends Stmt, @stmt_handler { * For example, `catch(std::exception& e)` introduces a * parameter `e`, whereas `catch(...)` does not introduce a parameter. */ - Parameter getParameter() { result = getBlock().getParameter() } + Parameter getParameter() { result = this.getBlock().getParameter() } override predicate mayBeImpure() { none() } @@ -1921,15 +1921,15 @@ class MicrosoftTryStmt extends Stmt, @stmt_microsoft_try { * This is a Microsoft C/C++ extension. */ class MicrosoftTryExceptStmt extends MicrosoftTryStmt { - MicrosoftTryExceptStmt() { getChild(1) instanceof Expr } + MicrosoftTryExceptStmt() { this.getChild(1) instanceof Expr } override string toString() { result = "__try { ... } __except( ... ) { ... }" } /** Gets the expression guarding the `__except` statement. */ - Expr getCondition() { result = getChild(1) } + Expr getCondition() { result = this.getChild(1) } /** Gets the `__except` statement (usually a `BlockStmt`). */ - Stmt getExcept() { result = getChild(2) } + Stmt getExcept() { result = this.getChild(2) } override string getAPrimaryQlClass() { result = "MicrosoftTryExceptStmt" } } @@ -1948,12 +1948,12 @@ class MicrosoftTryExceptStmt extends MicrosoftTryStmt { * This is a Microsoft C/C++ extension. */ class MicrosoftTryFinallyStmt extends MicrosoftTryStmt { - MicrosoftTryFinallyStmt() { not getChild(1) instanceof Expr } + MicrosoftTryFinallyStmt() { not this.getChild(1) instanceof Expr } override string toString() { result = "__try { ... } __finally { ... }" } /** Gets the `__finally` statement (usually a `BlockStmt`). */ - Stmt getFinally() { result = getChild(1) } + Stmt getFinally() { result = this.getChild(1) } override string getAPrimaryQlClass() { result = "MicrosoftTryFinallyStmt" } } diff --git a/cpp/ql/lib/semmlecode.cpp.dbscheme b/cpp/ql/lib/semmlecode.cpp.dbscheme index 7806a11dd7a..018f430097e 100644 --- a/cpp/ql/lib/semmlecode.cpp.dbscheme +++ b/cpp/ql/lib/semmlecode.cpp.dbscheme @@ -245,7 +245,7 @@ svnchurn( * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `file`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ locations_default( /** The location of an element that is not an expression or a statement. */ @@ -262,7 +262,7 @@ locations_default( * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `file`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ locations_stmt( /** The location of a statement. */ @@ -279,7 +279,7 @@ locations_stmt( * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `file`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ locations_expr( /** The location of an expression. */ diff --git a/cpp/ql/lib/tutorial.qll b/cpp/ql/lib/tutorial.qll new file mode 100644 index 00000000000..8cb1797a532 --- /dev/null +++ b/cpp/ql/lib/tutorial.qll @@ -0,0 +1,1207 @@ +/** + * This library is used in the QL detective tutorials. + * + * Note: Data is usually stored in a separate database and the QL libraries only contain predicates, + * but for this tutorial both the data and the predicates are stored in the library. + */ +class Person extends string { + Person() { + this = "Ronil" or + this = "Dina" or + this = "Ravi" or + this = "Bruce" or + this = "Jo" or + this = "Aida" or + this = "Esme" or + this = "Charlie" or + this = "Fred" or + this = "Meera" or + this = "Maya" or + this = "Chad" or + this = "Tiana" or + this = "Laura" or + this = "George" or + this = "Will" or + this = "Mary" or + this = "Almira" or + this = "Susannah" or + this = "Rhoda" or + this = "Cynthia" or + this = "Eunice" or + this = "Olive" or + this = "Virginia" or + this = "Angeline" or + this = "Helen" or + this = "Cornelia" or + this = "Harriet" or + this = "Mahala" or + this = "Abby" or + this = "Margaret" or + this = "Deb" or + this = "Minerva" or + this = "Severus" or + this = "Lavina" or + this = "Adeline" or + this = "Cath" or + this = "Elisa" or + this = "Lucretia" or + this = "Anne" or + this = "Eleanor" or + this = "Joanna" or + this = "Adam" or + this = "Agnes" or + this = "Rosanna" or + this = "Clara" or + this = "Melissa" or + this = "Amy" or + this = "Isabel" or + this = "Jemima" or + this = "Cordelia" or + this = "Melinda" or + this = "Delila" or + this = "Jeremiah" or + this = "Elijah" or + this = "Hester" or + this = "Walter" or + this = "Oliver" or + this = "Hugh" or + this = "Aaron" or + this = "Reuben" or + this = "Eli" or + this = "Amos" or + this = "Augustus" or + this = "Theodore" or + this = "Ira" or + this = "Timothy" or + this = "Cyrus" or + this = "Horace" or + this = "Simon" or + this = "Asa" or + this = "Frank" or + this = "Nelson" or + this = "Leonard" or + this = "Harrison" or + this = "Anthony" or + this = "Louis" or + this = "Milton" or + this = "Noah" or + this = "Cornelius" or + this = "Abdul" or + this = "Warren" or + this = "Harvey" or + this = "Dennis" or + this = "Wesley" or + this = "Sylvester" or + this = "Gilbert" or + this = "Sullivan" or + this = "Edmund" or + this = "Wilson" or + this = "Perry" or + this = "Matthew" or + this = "Simba" or + this = "Nala" or + this = "Rafiki" or + this = "Shenzi" or + this = "Ernest" or + this = "Gertrude" or + this = "Oscar" or + this = "Lilian" or + this = "Raymond" or + this = "Elgar" or + this = "Elmer" or + this = "Herbert" or + this = "Maude" or + this = "Mae" or + this = "Otto" or + this = "Edwin" or + this = "Ophelia" or + this = "Parsley" or + this = "Sage" or + this = "Rosemary" or + this = "Thyme" or + this = "Garfunkel" or + this = "King Basil" or + this = "Stephen" + } + + /** Gets the hair color of the person. If the person is bald, there is no result. */ + string getHairColor() { + this = "Ronil" and result = "black" + or + this = "Dina" and result = "black" + or + this = "Ravi" and result = "black" + or + this = "Bruce" and result = "brown" + or + this = "Jo" and result = "red" + or + this = "Aida" and result = "blond" + or + this = "Esme" and result = "blond" + or + this = "Fred" and result = "gray" + or + this = "Meera" and result = "brown" + or + this = "Maya" and result = "brown" + or + this = "Chad" and result = "brown" + or + this = "Tiana" and result = "black" + or + this = "Laura" and result = "blond" + or + this = "George" and result = "blond" + or + this = "Will" and result = "blond" + or + this = "Mary" and result = "blond" + or + this = "Almira" and result = "black" + or + this = "Susannah" and result = "blond" + or + this = "Rhoda" and result = "blond" + or + this = "Cynthia" and result = "gray" + or + this = "Eunice" and result = "white" + or + this = "Olive" and result = "brown" + or + this = "Virginia" and result = "brown" + or + this = "Angeline" and result = "red" + or + this = "Helen" and result = "white" + or + this = "Cornelia" and result = "gray" + or + this = "Harriet" and result = "white" + or + this = "Mahala" and result = "black" + or + this = "Abby" and result = "red" + or + this = "Margaret" and result = "brown" + or + this = "Deb" and result = "brown" + or + this = "Minerva" and result = "brown" + or + this = "Severus" and result = "black" + or + this = "Lavina" and result = "brown" + or + this = "Adeline" and result = "brown" + or + this = "Cath" and result = "brown" + or + this = "Elisa" and result = "brown" + or + this = "Lucretia" and result = "gray" + or + this = "Anne" and result = "black" + or + this = "Eleanor" and result = "brown" + or + this = "Joanna" and result = "brown" + or + this = "Adam" and result = "black" + or + this = "Agnes" and result = "black" + or + this = "Rosanna" and result = "gray" + or + this = "Clara" and result = "blond" + or + this = "Melissa" and result = "brown" + or + this = "Amy" and result = "brown" + or + this = "Isabel" and result = "black" + or + this = "Jemima" and result = "red" + or + this = "Cordelia" and result = "red" + or + this = "Melinda" and result = "gray" + or + this = "Delila" and result = "white" + or + this = "Jeremiah" and result = "gray" + or + this = "Hester" and result = "black" + or + this = "Walter" and result = "black" + or + this = "Aaron" and result = "gray" + or + this = "Reuben" and result = "gray" + or + this = "Eli" and result = "gray" + or + this = "Amos" and result = "white" + or + this = "Augustus" and result = "white" + or + this = "Theodore" and result = "white" + or + this = "Timothy" and result = "brown" + or + this = "Cyrus" and result = "brown" + or + this = "Horace" and result = "brown" + or + this = "Simon" and result = "brown" + or + this = "Asa" and result = "brown" + or + this = "Frank" and result = "brown" + or + this = "Nelson" and result = "black" + or + this = "Leonard" and result = "black" + or + this = "Harrison" and result = "black" + or + this = "Anthony" and result = "black" + or + this = "Louis" and result = "black" + or + this = "Milton" and result = "blond" + or + this = "Noah" and result = "blond" + or + this = "Cornelius" and result = "red" + or + this = "Abdul" and result = "brown" + or + this = "Warren" and result = "red" + or + this = "Harvey" and result = "blond" + or + this = "Dennis" and result = "blond" + or + this = "Wesley" and result = "brown" + or + this = "Sylvester" and result = "brown" + or + this = "Gilbert" and result = "brown" + or + this = "Sullivan" and result = "brown" + or + this = "Edmund" and result = "brown" + or + this = "Wilson" and result = "blond" + or + this = "Perry" and result = "black" + or + this = "Simba" and result = "brown" + or + this = "Nala" and result = "brown" + or + this = "Rafiki" and result = "red" + or + this = "Shenzi" and result = "gray" + or + this = "Ernest" and result = "blond" + or + this = "Gertrude" and result = "brown" + or + this = "Oscar" and result = "blond" + or + this = "Lilian" and result = "brown" + or + this = "Raymond" and result = "brown" + or + this = "Elgar" and result = "brown" + or + this = "Elmer" and result = "brown" + or + this = "Herbert" and result = "brown" + or + this = "Maude" and result = "brown" + or + this = "Mae" and result = "brown" + or + this = "Otto" and result = "black" + or + this = "Edwin" and result = "black" + or + this = "Ophelia" and result = "brown" + or + this = "Parsley" and result = "brown" + or + this = "Sage" and result = "brown" + or + this = "Rosemary" and result = "brown" + or + this = "Thyme" and result = "brown" + or + this = "Garfunkel" and result = "brown" + or + this = "King Basil" and result = "brown" + or + this = "Stephen" and result = "black" + or + this = "Stephen" and result = "gray" + } + + /** Gets the age of the person (in years). If the person is deceased, there is no result. */ + int getAge() { + this = "Ronil" and result = 21 + or + this = "Dina" and result = 53 + or + this = "Ravi" and result = 16 + or + this = "Bruce" and result = 35 + or + this = "Jo" and result = 47 + or + this = "Aida" and result = 26 + or + this = "Esme" and result = 25 + or + this = "Charlie" and result = 31 + or + this = "Fred" and result = 68 + or + this = "Meera" and result = 62 + or + this = "Maya" and result = 29 + or + this = "Chad" and result = 49 + or + this = "Tiana" and result = 18 + or + this = "Laura" and result = 2 + or + this = "George" and result = 3 + or + this = "Will" and result = 41 + or + this = "Mary" and result = 51 + or + this = "Almira" and result = 1 + or + this = "Susannah" and result = 97 + or + this = "Rhoda" and result = 39 + or + this = "Cynthia" and result = 89 + or + this = "Eunice" and result = 83 + or + this = "Olive" and result = 25 + or + this = "Virginia" and result = 52 + or + this = "Angeline" and result = 22 + or + this = "Helen" and result = 79 + or + this = "Cornelia" and result = 59 + or + this = "Harriet" and result = 57 + or + this = "Mahala" and result = 61 + or + this = "Abby" and result = 24 + or + this = "Margaret" and result = 59 + or + this = "Deb" and result = 31 + or + this = "Minerva" and result = 72 + or + this = "Severus" and result = 61 + or + this = "Lavina" and result = 33 + or + this = "Adeline" and result = 17 + or + this = "Cath" and result = 22 + or + this = "Elisa" and result = 9 + or + this = "Lucretia" and result = 56 + or + this = "Anne" and result = 11 + or + this = "Eleanor" and result = 80 + or + this = "Joanna" and result = 43 + or + this = "Adam" and result = 37 + or + this = "Agnes" and result = 47 + or + this = "Rosanna" and result = 61 + or + this = "Clara" and result = 31 + or + this = "Melissa" and result = 37 + or + this = "Amy" and result = 12 + or + this = "Isabel" and result = 6 + or + this = "Jemima" and result = 16 + or + this = "Cordelia" and result = 21 + or + this = "Melinda" and result = 55 + or + this = "Delila" and result = 66 + or + this = "Jeremiah" and result = 54 + or + this = "Elijah" and result = 42 + or + this = "Hester" and result = 68 + or + this = "Walter" and result = 66 + or + this = "Oliver" and result = 33 + or + this = "Hugh" and result = 51 + or + this = "Aaron" and result = 49 + or + this = "Reuben" and result = 58 + or + this = "Eli" and result = 70 + or + this = "Amos" and result = 65 + or + this = "Augustus" and result = 56 + or + this = "Theodore" and result = 69 + or + this = "Ira" and result = 1 + or + this = "Timothy" and result = 54 + or + this = "Cyrus" and result = 78 + or + this = "Horace" and result = 34 + or + this = "Simon" and result = 23 + or + this = "Asa" and result = 28 + or + this = "Frank" and result = 59 + or + this = "Nelson" and result = 38 + or + this = "Leonard" and result = 58 + or + this = "Harrison" and result = 7 + or + this = "Anthony" and result = 2 + or + this = "Louis" and result = 34 + or + this = "Milton" and result = 36 + or + this = "Noah" and result = 48 + or + this = "Cornelius" and result = 41 + or + this = "Abdul" and result = 67 + or + this = "Warren" and result = 47 + or + this = "Harvey" and result = 31 + or + this = "Dennis" and result = 39 + or + this = "Wesley" and result = 13 + or + this = "Sylvester" and result = 19 + or + this = "Gilbert" and result = 16 + or + this = "Sullivan" and result = 17 + or + this = "Edmund" and result = 29 + or + this = "Wilson" and result = 27 + or + this = "Perry" and result = 31 + or + this = "Matthew" and result = 55 + or + this = "Simba" and result = 8 + or + this = "Nala" and result = 7 + or + this = "Rafiki" and result = 76 + or + this = "Shenzi" and result = 67 + } + + /** Gets the height of the person (in cm). If the person is deceased, there is no result. */ + float getHeight() { + this = "Ronil" and result = 183.0 + or + this = "Dina" and result = 155.1 + or + this = "Ravi" and result = 175.2 + or + this = "Bruce" and result = 191.3 + or + this = "Jo" and result = 163.4 + or + this = "Aida" and result = 182.6 + or + this = "Esme" and result = 176.9 + or + this = "Charlie" and result = 189.7 + or + this = "Fred" and result = 179.4 + or + this = "Meera" and result = 160.1 + or + this = "Maya" and result = 153.0 + or + this = "Chad" and result = 168.5 + or + this = "Tiana" and result = 149.7 + or + this = "Laura" and result = 87.5 + or + this = "George" and result = 96.4 + or + this = "Will" and result = 167.1 + or + this = "Mary" and result = 159.8 + or + this = "Almira" and result = 62.1 + or + this = "Susannah" and result = 145.8 + or + this = "Rhoda" and result = 180.1 + or + this = "Cynthia" and result = 161.8 + or + this = "Eunice" and result = 153.2 + or + this = "Olive" and result = 179.9 + or + this = "Virginia" and result = 165.1 + or + this = "Angeline" and result = 172.3 + or + this = "Helen" and result = 163.1 + or + this = "Cornelia" and result = 160.8 + or + this = "Harriet" and result = 163.2 + or + this = "Mahala" and result = 157.7 + or + this = "Abby" and result = 174.5 + or + this = "Margaret" and result = 165.6 + or + this = "Deb" and result = 171.6 + or + this = "Minerva" and result = 168.7 + or + this = "Severus" and result = 188.8 + or + this = "Lavina" and result = 155.1 + or + this = "Adeline" and result = 165.5 + or + this = "Cath" and result = 147.8 + or + this = "Elisa" and result = 129.4 + or + this = "Lucretia" and result = 153.6 + or + this = "Anne" and result = 140.4 + or + this = "Eleanor" and result = 151.1 + or + this = "Joanna" and result = 167.2 + or + this = "Adam" and result = 155.5 + or + this = "Agnes" and result = 156.8 + or + this = "Rosanna" and result = 162.4 + or + this = "Clara" and result = 158.6 + or + this = "Melissa" and result = 182.3 + or + this = "Amy" and result = 147.1 + or + this = "Isabel" and result = 121.4 + or + this = "Jemima" and result = 149.8 + or + this = "Cordelia" and result = 151.7 + or + this = "Melinda" and result = 154.4 + or + this = "Delila" and result = 163.4 + or + this = "Jeremiah" and result = 167.5 + or + this = "Elijah" and result = 184.5 + or + this = "Hester" and result = 152.7 + or + this = "Walter" and result = 159.6 + or + this = "Oliver" and result = 192.4 + or + this = "Hugh" and result = 173.1 + or + this = "Aaron" and result = 176.6 + or + this = "Reuben" and result = 169.9 + or + this = "Eli" and result = 180.4 + or + this = "Amos" and result = 167.4 + or + this = "Augustus" and result = 156.5 + or + this = "Theodore" and result = 176.6 + or + this = "Ira" and result = 54.1 + or + this = "Timothy" and result = 172.2 + or + this = "Cyrus" and result = 157.9 + or + this = "Horace" and result = 169.3 + or + this = "Simon" and result = 157.1 + or + this = "Asa" and result = 149.4 + or + this = "Frank" and result = 167.2 + or + this = "Nelson" and result = 173.0 + or + this = "Leonard" and result = 172.0 + or + this = "Harrison" and result = 126.0 + or + this = "Anthony" and result = 98.4 + or + this = "Louis" and result = 186.8 + or + this = "Milton" and result = 157.8 + or + this = "Noah" and result = 190.5 + or + this = "Cornelius" and result = 183.1 + or + this = "Abdul" and result = 182.0 + or + this = "Warren" and result = 175.0 + or + this = "Harvey" and result = 169.3 + or + this = "Dennis" and result = 160.4 + or + this = "Wesley" and result = 139.8 + or + this = "Sylvester" and result = 188.2 + or + this = "Gilbert" and result = 177.6 + or + this = "Sullivan" and result = 168.3 + or + this = "Edmund" and result = 159.2 + or + this = "Wilson" and result = 167.6 + or + this = "Perry" and result = 189.1 + or + this = "Matthew" and result = 167.2 + or + this = "Simba" and result = 140.1 + or + this = "Nala" and result = 138.0 + or + this = "Rafiki" and result = 139.3 + or + this = "Shenzi" and result = 171.1 + } + + /** Gets the location of the person's home ("north", "south", "east", or "west"). If the person is deceased, there is no result. */ + string getLocation() { + this = "Ronil" and result = "north" + or + this = "Dina" and result = "north" + or + this = "Ravi" and result = "north" + or + this = "Bruce" and result = "south" + or + this = "Jo" and result = "west" + or + this = "Aida" and result = "east" + or + this = "Esme" and result = "east" + or + this = "Charlie" and result = "south" + or + this = "Fred" and result = "west" + or + this = "Meera" and result = "south" + or + this = "Maya" and result = "south" + or + this = "Chad" and result = "south" + or + this = "Tiana" and result = "west" + or + this = "Laura" and result = "south" + or + this = "George" and result = "south" + or + this = "Will" and result = "south" + or + this = "Mary" and result = "south" + or + this = "Almira" and result = "south" + or + this = "Susannah" and result = "north" + or + this = "Rhoda" and result = "north" + or + this = "Cynthia" and result = "north" + or + this = "Eunice" and result = "north" + or + this = "Olive" and result = "west" + or + this = "Virginia" and result = "west" + or + this = "Angeline" and result = "west" + or + this = "Helen" and result = "west" + or + this = "Cornelia" and result = "east" + or + this = "Harriet" and result = "east" + or + this = "Mahala" and result = "east" + or + this = "Abby" and result = "east" + or + this = "Margaret" and result = "east" + or + this = "Deb" and result = "east" + or + this = "Minerva" and result = "south" + or + this = "Severus" and result = "north" + or + this = "Lavina" and result = "east" + or + this = "Adeline" and result = "west" + or + this = "Cath" and result = "east" + or + this = "Elisa" and result = "east" + or + this = "Lucretia" and result = "north" + or + this = "Anne" and result = "north" + or + this = "Eleanor" and result = "south" + or + this = "Joanna" and result = "south" + or + this = "Adam" and result = "east" + or + this = "Agnes" and result = "east" + or + this = "Rosanna" and result = "east" + or + this = "Clara" and result = "east" + or + this = "Melissa" and result = "west" + or + this = "Amy" and result = "west" + or + this = "Isabel" and result = "west" + or + this = "Jemima" and result = "west" + or + this = "Cordelia" and result = "west" + or + this = "Melinda" and result = "west" + or + this = "Delila" and result = "south" + or + this = "Jeremiah" and result = "north" + or + this = "Elijah" and result = "north" + or + this = "Hester" and result = "east" + or + this = "Walter" and result = "east" + or + this = "Oliver" and result = "east" + or + this = "Hugh" and result = "south" + or + this = "Aaron" and result = "south" + or + this = "Reuben" and result = "west" + or + this = "Eli" and result = "west" + or + this = "Amos" and result = "east" + or + this = "Augustus" and result = "south" + or + this = "Theodore" and result = "west" + or + this = "Ira" and result = "south" + or + this = "Timothy" and result = "north" + or + this = "Cyrus" and result = "north" + or + this = "Horace" and result = "east" + or + this = "Simon" and result = "east" + or + this = "Asa" and result = "east" + or + this = "Frank" and result = "west" + or + this = "Nelson" and result = "west" + or + this = "Leonard" and result = "west" + or + this = "Harrison" and result = "north" + or + this = "Anthony" and result = "north" + or + this = "Louis" and result = "north" + or + this = "Milton" and result = "south" + or + this = "Noah" and result = "south" + or + this = "Cornelius" and result = "east" + or + this = "Abdul" and result = "east" + or + this = "Warren" and result = "west" + or + this = "Harvey" and result = "west" + or + this = "Dennis" and result = "west" + or + this = "Wesley" and result = "west" + or + this = "Sylvester" and result = "south" + or + this = "Gilbert" and result = "east" + or + this = "Sullivan" and result = "east" + or + this = "Edmund" and result = "north" + or + this = "Wilson" and result = "north" + or + this = "Perry" and result = "west" + or + this = "Matthew" and result = "east" + or + this = "Simba" and result = "south" + or + this = "Nala" and result = "south" + or + this = "Rafiki" and result = "north" + or + this = "Shenzi" and result = "west" + } + + /** Holds if the person is deceased. */ + predicate isDeceased() { + this = "Ernest" or + this = "Gertrude" or + this = "Oscar" or + this = "Lilian" or + this = "Edwin" or + this = "Raymond" or + this = "Elgar" or + this = "Elmer" or + this = "Herbert" or + this = "Maude" or + this = "Mae" or + this = "Otto" or + this = "Ophelia" or + this = "Parsley" or + this = "Sage" or + this = "Rosemary" or + this = "Thyme" or + this = "Garfunkel" or + this = "King Basil" + } + + /** Gets a parent of the person (alive or deceased). */ + Person getAParent() { + this = "Stephen" and result = "Edmund" + or + this = "Edmund" and result = "Augustus" + or + this = "Augustus" and result = "Stephen" + or + this = "Abby" and result = "Cornelia" + or + this = "Abby" and result = "Amos" + or + this = "Abdul" and result = "Susannah" + or + this = "Adam" and result = "Amos" + or + this = "Adeline" and result = "Melinda" + or + this = "Adeline" and result = "Frank" + or + this = "Agnes" and result = "Abdul" + or + this = "Aida" and result = "Agnes" + or + this = "Almira" and result = "Sylvester" + or + this = "Amos" and result = "Eunice" + or + this = "Amy" and result = "Noah" + or + this = "Amy" and result = "Chad" + or + this = "Angeline" and result = "Reuben" + or + this = "Angeline" and result = "Lucretia" + or + this = "Anne" and result = "Rhoda" + or + this = "Anne" and result = "Louis" + or + this = "Anthony" and result = "Lavina" + or + this = "Anthony" and result = "Asa" + or + this = "Asa" and result = "Cornelia" + or + this = "Cath" and result = "Harriet" + or + this = "Charlie" and result = "Matthew" + or + this = "Clara" and result = "Ernest" + or + this = "Cornelia" and result = "Cynthia" + or + this = "Cornelius" and result = "Eli" + or + this = "Deb" and result = "Margaret" + or + this = "Dennis" and result = "Fred" + or + this = "Eli" and result = "Susannah" + or + this = "Elijah" and result = "Delila" + or + this = "Elisa" and result = "Deb" + or + this = "Elisa" and result = "Horace" + or + this = "Esme" and result = "Margaret" + or + this = "Frank" and result = "Eleanor" + or + this = "Frank" and result = "Cyrus" + or + this = "George" and result = "Maya" + or + this = "George" and result = "Wilson" + or + this = "Gilbert" and result = "Cornelius" + or + this = "Harriet" and result = "Cynthia" + or + this = "Harrison" and result = "Louis" + or + this = "Harvey" and result = "Fred" + or + this = "Helen" and result = "Susannah" + or + this = "Hester" and result = "Edwin" + or + this = "Hugh" and result = "Cyrus" + or + this = "Hugh" and result = "Helen" + or + this = "Ira" and result = "Maya" + or + this = "Ira" and result = "Wilson" + or + this = "Isabel" and result = "Perry" + or + this = "Isabel" and result = "Harvey" + or + this = "Jemima" and result = "Melinda" + or + this = "Jemima" and result = "Frank" + or + this = "Ernest" and result = "Lilian" + or + this = "Ernest" and result = "Oscar" + or + this = "Gertrude" and result = "Ophelia" + or + this = "Gertrude" and result = "Raymond" + or + this = "Lilian" and result = "Elgar" + or + this = "Lilian" and result = "Mae" + or + this = "Raymond" and result = "Elgar" + or + this = "Raymond" and result = "Mae" + or + this = "Elmer" and result = "Ophelia" + or + this = "Elmer" and result = "Raymond" + or + this = "Herbert" and result = "Ophelia" + or + this = "Herbert" and result = "Raymond" + or + this = "Maude" and result = "Ophelia" + or + this = "Maude" and result = "Raymond" + or + this = "Otto" and result = "Elgar" + or + this = "Otto" and result = "Mae" + or + this = "Edwin" and result = "Otto" + or + this = "Parsley" and result = "Simon" + or + this = "Parsley" and result = "Garfunkel" + or + this = "Sage" and result = "Simon" + or + this = "Sage" and result = "Garfunkel" + or + this = "Rosemary" and result = "Simon" + or + this = "Rosemary" and result = "Garfunkel" + or + this = "Thyme" and result = "Simon" + or + this = "Thyme" and result = "Garfunkel" + or + this = "King Basil" and result = "Ophelia" + or + this = "King Basil" and result = "Raymond" + or + this = "Jo" and result = "Theodore" + or + this = "Joanna" and result = "Shenzi" + or + this = "Laura" and result = "Maya" + or + this = "Laura" and result = "Wilson" + or + this = "Lavina" and result = "Mahala" + or + this = "Lavina" and result = "Walter" + or + this = "Leonard" and result = "Cyrus" + or + this = "Leonard" and result = "Helen" + or + this = "Lucretia" and result = "Eleanor" + or + this = "Lucretia" and result = "Cyrus" + or + this = "Mahala" and result = "Eunice" + or + this = "Margaret" and result = "Cynthia" + or + this = "Matthew" and result = "Cyrus" + or + this = "Matthew" and result = "Helen" + or + this = "Maya" and result = "Meera" + or + this = "Melinda" and result = "Rafiki" + or + this = "Melissa" and result = "Mahala" + or + this = "Melissa" and result = "Walter" + or + this = "Nala" and result = "Bruce" + or + this = "Nelson" and result = "Mahala" + or + this = "Nelson" and result = "Walter" + or + this = "Noah" and result = "Eli" + or + this = "Olive" and result = "Reuben" + or + this = "Olive" and result = "Lucretia" + or + this = "Oliver" and result = "Matthew" + or + this = "Perry" and result = "Leonard" + or + this = "Ravi" and result = "Dina" + or + this = "Simba" and result = "Will" + or + this = "Simon" and result = "Margaret" + or + this = "Sullivan" and result = "Cornelius" + or + this = "Sylvester" and result = "Timothy" + or + this = "Theodore" and result = "Susannah" + or + this = "Tiana" and result = "Jo" + or + this = "Virginia" and result = "Helen" + or + this = "Warren" and result = "Shenzi" + or + this = "Wesley" and result = "Warren" + or + this = "Wesley" and result = "Jo" + or + this = "Will" and result = "Eli" + } + + /** Holds if the person is allowed in the region. Initially, all villagers are allowed in every region. */ + predicate isAllowedIn(string region) { + region = "north" or + region = "south" or + region = "east" or + region = "west" + } +} + +/** Returns a parent of the person. */ +Person parentOf(Person p) { result = p.getAParent() } diff --git a/cpp/ql/src/AlertSuppression.ql b/cpp/ql/src/AlertSuppression.ql index 6258e8f7818..9a3983ed515 100644 --- a/cpp/ql/src/AlertSuppression.ql +++ b/cpp/ql/src/AlertSuppression.ql @@ -60,20 +60,18 @@ class SuppressionComment extends Comment { /** * The scope of an alert suppression comment. */ -class SuppressionScope extends ElementBase { - SuppressionScope() { this instanceof SuppressionComment } - +class SuppressionScope extends ElementBase instanceof SuppressionComment { /** * Holds if this element is at the specified location. * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { - this.(SuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn) + super.covers(filepath, startline, startcolumn, endline, endcolumn) } } diff --git a/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyFields.ql b/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyFields.ql index 5f11a9e0830..d2c69ba5fff 100644 --- a/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyFields.ql +++ b/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyFields.ql @@ -68,12 +68,12 @@ class VariableDeclarationLine extends TVariableDeclarationInfo { /** * Gets the start column of the first `VariableDeclarationEntry` on this line. */ - int getStartColumn() { result = min(getAVDE().getLocation().getStartColumn()) } + int getStartColumn() { result = min(this.getAVDE().getLocation().getStartColumn()) } /** * Gets the end column of the last `VariableDeclarationEntry` on this line. */ - int getEndColumn() { result = max(getAVDE().getLocation().getEndColumn()) } + int getEndColumn() { result = max(this.getAVDE().getLocation().getEndColumn()) } /** * Gets the rank of this `VariableDeclarationLine` in its file and class @@ -89,14 +89,14 @@ class VariableDeclarationLine extends TVariableDeclarationInfo { */ VariableDeclarationLine getNext() { result = TVariableDeclarationLine(c, f, _) and - result.getRank() = getRank() + 1 + result.getRank() = this.getRank() + 1 } /** * Gets the `VariableDeclarationLine` following this one, if it is nearby. */ VariableDeclarationLine getProximateNext() { - result = getNext() and + result = this.getNext() and result.getLine() <= this.getLine() + 3 } @@ -114,14 +114,14 @@ class VariableDeclarationGroup extends VariableDeclarationLine { // there is no `VariableDeclarationLine` within three lines previously not any(VariableDeclarationLine prev).getProximateNext() = this and // `end` is the last transitively proximate line - end = getProximateNext*() and + end = this.getProximateNext*() and not exists(end.getProximateNext()) } predicate hasLocationInfo(string path, int startline, int startcol, int endline, int endcol) { path = f.getAbsolutePath() and - startline = getLine() and - startcol = getStartColumn() and + startline = this.getLine() and + startcol = this.getStartColumn() and endline = end.getLine() and endcol = end.getEndColumn() } @@ -132,18 +132,18 @@ class VariableDeclarationGroup extends VariableDeclarationLine { int getCount() { result = count(VariableDeclarationLine l | - l = getProximateNext*() + l = this.getProximateNext*() | l.getAVDE().getVariable().getName() ) } override string toString() { - getCount() = 1 and - result = "declaration of " + getAVDE().getVariable().getName() + this.getCount() = 1 and + result = "declaration of " + this.getAVDE().getVariable().getName() or - getCount() > 1 and - result = "group of " + getCount() + " fields here" + this.getCount() > 1 and + result = "group of " + this.getCount() + " fields here" } } diff --git a/cpp/ql/src/Best Practices/Magic Constants/MagicConstants.qll b/cpp/ql/src/Best Practices/Magic Constants/MagicConstants.qll index 587b64b60b3..fce3d286a5f 100644 --- a/cpp/ql/src/Best Practices/Magic Constants/MagicConstants.qll +++ b/cpp/ql/src/Best Practices/Magic Constants/MagicConstants.qll @@ -58,15 +58,7 @@ predicate intTrivial(Literal lit) { exists(string v | trivialIntValue(v) and v = predicate longTrivial(Literal lit) { exists(string v | trivialLongValue(v) and v = lit.getValue()) } predicate powerOfTen(float f) { - f = 10 or - f = 100 or - f = 1000 or - f = 10000 or - f = 100000 or - f = 1000000 or - f = 10000000 or - f = 100000000 or - f = 1000000000 + f = [10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000] } predicate floatTrivial(Literal lit) { diff --git a/cpp/ql/src/Critical/OverflowStatic.ql b/cpp/ql/src/Critical/OverflowStatic.ql index 7c447c12323..8b09931cd4a 100644 --- a/cpp/ql/src/Critical/OverflowStatic.ql +++ b/cpp/ql/src/Critical/OverflowStatic.ql @@ -5,7 +5,7 @@ * @kind problem * @problem.severity warning * @security-severity 9.3 - * @precision medium + * @precision high * @id cpp/static-buffer-overflow * @tags reliability * security @@ -55,6 +55,8 @@ predicate overflowOffsetInLoop(BufferAccess bufaccess, string msg) { loop.counter().getAnAccess() = bufaccess.getArrayOffset() and // Ensure that we don't have an upper bound on the array index that's less than the buffer size. not upperBound(bufaccess.getArrayOffset().getFullyConverted()) < bufaccess.bufferSize() and + // The upper bounds analysis must not have been widended + not upperBoundMayBeWidened(bufaccess.getArrayOffset().getFullyConverted()) and msg = "Potential buffer-overflow: counter '" + loop.counter().toString() + "' <= " + loop.limit().toString() + " but '" + bufaccess.buffer().getName() + "' has " + @@ -130,11 +132,13 @@ predicate outOfBounds(BufferAccess bufaccess, string msg) { ( access > size or - access = size and not exists(AddressOfExpr addof | bufaccess = addof.getOperand()) + access = size and + not exists(AddressOfExpr addof | bufaccess = addof.getOperand()) and + not exists(BuiltInOperationBuiltInOffsetOf offsetof | offsetof.getAChild() = bufaccess) ) and msg = "Potential buffer-overflow: '" + buf + "' has size " + size.toString() + " but '" + buf + "[" + - access.toString() + "]' is accessed here." + access.toString() + "]' may be accessed here." ) } diff --git a/cpp/ql/src/Diagnostics/ExtractionErrors.ql b/cpp/ql/src/Diagnostics/ExtractionErrors.ql deleted file mode 100644 index a445c3bc0c4..00000000000 --- a/cpp/ql/src/Diagnostics/ExtractionErrors.ql +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @name Extraction errors - * @description List all extraction errors for files in the source code directory. - * @kind diagnostic - * @id cpp/diagnostics/extraction-errors - */ - -import cpp -import ExtractionErrors - -from ExtractionError error -where - error instanceof ExtractionUnknownError or - exists(error.getFile().getRelativePath()) -select error, "Extraction failed in " + error.getFile() + " with error " + error.getErrorMessage(), - error.getSeverity() diff --git a/cpp/ql/src/Diagnostics/ExtractionErrors.qll b/cpp/ql/src/Diagnostics/ExtractionProblems.qll similarity index 50% rename from cpp/ql/src/Diagnostics/ExtractionErrors.qll rename to cpp/ql/src/Diagnostics/ExtractionProblems.qll index 4cf6f8145f8..c96e2e926e8 100644 --- a/cpp/ql/src/Diagnostics/ExtractionErrors.qll +++ b/cpp/ql/src/Diagnostics/ExtractionProblems.qll @@ -1,12 +1,12 @@ /** - * Provides a common hierarchy of all types of errors that can occur during extraction. + * Provides a common hierarchy of all types of problems that can occur during extraction. */ import cpp /* * A note about how the C/C++ extractor emits diagnostics: - * When the extractor frontend encounters an error, it emits a diagnostic message, + * When the extractor frontend encounters a problem, it emits a diagnostic message, * that includes a message, location and severity. * However, that process is best-effort and may fail (e.g. due to lack of memory). * Thus, if the extractor emitted at least one diagnostic of severity discretionary @@ -15,17 +15,17 @@ import cpp * In the common case, this means that a compilation during which one or more errors happened also gets * the catch-all diagnostic. * This diagnostic has the empty string as file path. - * We filter out these useless diagnostics if there is at least one error-level diagnostic + * We filter out these useless diagnostics if there is at least one warning-level diagnostic * for the affected compilation in the database. * Otherwise, we show it to indicate that something went wrong and that we * don't know what exactly happened. */ /** - * An error that, if present, leads to a file being marked as non-successfully extracted. + * A problem with a file that, if present, leads to a file being marked as non-successfully extracted. */ -class ReportableError extends Diagnostic { - ReportableError() { +class ReportableWarning extends Diagnostic { + ReportableWarning() { ( this instanceof CompilerDiscretionaryError or this instanceof CompilerError or @@ -36,39 +36,35 @@ class ReportableError extends Diagnostic { } } -private newtype TExtractionError = - TReportableError(ReportableError err) or +private newtype TExtractionProblem = + TReportableWarning(ReportableWarning err) or TCompilationFailed(Compilation c, File f) { f = c.getAFileCompiled() and not c.normalTermination() } or // Show the catch-all diagnostic (see note above) only if we haven't seen any other error-level diagnostic // for that compilation - TUnknownError(CompilerError err) { - not exists(ReportableError e | e.getCompilation() = err.getCompilation()) + TUnknownProblem(CompilerError err) { + not exists(ReportableWarning e | e.getCompilation() = err.getCompilation()) } /** - * Superclass for the extraction error hierarchy. + * Superclass for the extraction problem hierarchy. */ -class ExtractionError extends TExtractionError { - /** Gets the string representation of the error. */ +class ExtractionProblem extends TExtractionProblem { + /** Gets the string representation of the problem. */ string toString() { none() } - /** Gets the error message for this error. */ - string getErrorMessage() { none() } + /** Gets the problem message for this problem. */ + string getProblemMessage() { none() } - /** Gets the file this error occured in. */ + /** Gets the file this problem occured in. */ File getFile() { none() } - /** Gets the location this error occured in. */ + /** Gets the location this problem occured in. */ Location getLocation() { none() } - /** Gets the SARIF severity of this error. */ - int getSeverity() { - // Unfortunately, we can't distinguish between errors and fatal errors in SARIF, - // so all errors have severity 2. - result = 2 - } + /** Gets the SARIF severity of this problem. */ + int getSeverity() { none() } } /** @@ -79,7 +75,7 @@ class ExtractionError extends TExtractionError { * - stack overflow * - out of memory */ -class ExtractionUnrecoverableError extends ExtractionError, TCompilationFailed { +class ExtractionUnrecoverableError extends ExtractionProblem, TCompilationFailed { Compilation c; File f; @@ -89,49 +85,67 @@ class ExtractionUnrecoverableError extends ExtractionError, TCompilationFailed { result = "Unrecoverable extraction error while compiling " + f.toString() } - override string getErrorMessage() { result = "unrecoverable compilation failure." } + override string getProblemMessage() { result = "unrecoverable compilation failure." } override File getFile() { result = f } override Location getLocation() { result = f.getLocation() } + + override int getSeverity() { + // These extractor errors break the analysis, so we mark them in SARIF as + // [errors](https://docs.oasis-open.org/sarif/sarif/v2.1.0/csprd01/sarif-v2.1.0-csprd01.html#_Toc10541338). + result = 2 + } } /** - * A recoverable extraction error. + * A recoverable extraction warning. * These are compiler errors from the frontend. * Upon encountering one of these, we still continue extraction, but the * database will be incomplete for that file. */ -class ExtractionRecoverableError extends ExtractionError, TReportableError { - ReportableError err; +class ExtractionRecoverableWarning extends ExtractionProblem, TReportableWarning { + ReportableWarning err; - ExtractionRecoverableError() { this = TReportableError(err) } + ExtractionRecoverableWarning() { this = TReportableWarning(err) } override string toString() { result = "Recoverable extraction error: " + err } - override string getErrorMessage() { result = err.getFullMessage() } + override string getProblemMessage() { result = err.getFullMessage() } override File getFile() { result = err.getFile() } override Location getLocation() { result = err.getLocation() } + + override int getSeverity() { + // Recoverable extraction problems don't tend to break the analysis, so we mark them in SARIF as + // [warnings](https://docs.oasis-open.org/sarif/sarif/v2.1.0/csprd01/sarif-v2.1.0-csprd01.html#_Toc10541338). + result = 1 + } } /** - * An unknown error happened during extraction. - * These are only displayed if we know that we encountered an error during extraction, + * An unknown problem happened during extraction. + * These are only displayed if we know that we encountered an problem during extraction, * but, for some reason, failed to emit a proper diagnostic with location information - * and error message. + * and problem message. */ -class ExtractionUnknownError extends ExtractionError, TUnknownError { +class ExtractionUnknownProblem extends ExtractionProblem, TUnknownProblem { CompilerError err; - ExtractionUnknownError() { this = TUnknownError(err) } + ExtractionUnknownProblem() { this = TUnknownProblem(err) } - override string toString() { result = "Unknown extraction error: " + err } + override string toString() { result = "Unknown extraction problem: " + err } - override string getErrorMessage() { result = err.getFullMessage() } + override string getProblemMessage() { result = err.getFullMessage() } override File getFile() { result = err.getFile() } override Location getLocation() { result = err.getLocation() } + + override int getSeverity() { + // Unknown extraction problems don't tend to break the analysis, so we mark them in SARIF as + // [warnings](https://docs.oasis-open.org/sarif/sarif/v2.1.0/csprd01/sarif-v2.1.0-csprd01.html#_Toc10541338). + result = 1 + } } diff --git a/cpp/ql/src/Diagnostics/ExtractionWarnings.ql b/cpp/ql/src/Diagnostics/ExtractionWarnings.ql new file mode 100644 index 00000000000..dcfb599bbeb --- /dev/null +++ b/cpp/ql/src/Diagnostics/ExtractionWarnings.ql @@ -0,0 +1,18 @@ +/** + * @name Extraction warnings + * @description List all extraction warnings for files in the source code directory. + * @kind diagnostic + * @id cpp/diagnostics/extraction-warnings + */ + +import cpp +import ExtractionProblems + +from ExtractionProblem warning +where + warning instanceof ExtractionRecoverableWarning and exists(warning.getFile().getRelativePath()) + or + warning instanceof ExtractionUnknownProblem +select warning, + "Extraction failed in " + warning.getFile() + " with warning " + warning.getProblemMessage(), + warning.getSeverity() diff --git a/cpp/ql/src/Diagnostics/FailedExtractorInvocations.ql b/cpp/ql/src/Diagnostics/FailedExtractorInvocations.ql index 8672d674d2d..fdc02f12135 100644 --- a/cpp/ql/src/Diagnostics/FailedExtractorInvocations.ql +++ b/cpp/ql/src/Diagnostics/FailedExtractorInvocations.ql @@ -13,6 +13,9 @@ string describe(Compilation c) { else result = "extractor invocation " + concat(int i | | c.getArgument(i), " " order by i) } +/** Gets the SARIF severity level that indicates an error. */ +private int getErrorSeverity() { result = 2 } + from Compilation c where not c.normalTermination() -select "Extraction aborted for " + describe(c) +select "Extraction aborted for " + describe(c), getErrorSeverity() diff --git a/cpp/ql/src/Diagnostics/SuccessfullyExtractedFiles.ql b/cpp/ql/src/Diagnostics/SuccessfullyExtractedFiles.ql index a920af8e767..3e9fb12d935 100644 --- a/cpp/ql/src/Diagnostics/SuccessfullyExtractedFiles.ql +++ b/cpp/ql/src/Diagnostics/SuccessfullyExtractedFiles.ql @@ -1,15 +1,15 @@ /** * @name Successfully extracted files - * @description Lists all files in the source code directory that were extracted without encountering an error in the file. + * @description Lists all files in the source code directory that were extracted without encountering a problem in the file. * @kind diagnostic * @id cpp/diagnostics/successfully-extracted-files */ import cpp -import ExtractionErrors +import ExtractionProblems from File f where - not exists(ExtractionError e | e.getFile() = f) and + not exists(ExtractionProblem e | e.getFile() = f) and exists(f.getRelativePath()) -select f, "" +select f, "File successfully extracted" diff --git a/cpp/ql/src/Documentation/CommentedOutCode.qll b/cpp/ql/src/Documentation/CommentedOutCode.qll index 172474bbe29..a4e5b948630 100644 --- a/cpp/ql/src/Documentation/CommentedOutCode.qll +++ b/cpp/ql/src/Documentation/CommentedOutCode.qll @@ -198,7 +198,7 @@ class CommentBlock extends Comment { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/cpp/ql/src/JPL_C/LOC-2/Rule 09/Semaphores.qll b/cpp/ql/src/JPL_C/LOC-2/Rule 09/Semaphores.qll index 0b60a3b9877..543c516e4bf 100644 --- a/cpp/ql/src/JPL_C/LOC-2/Rule 09/Semaphores.qll +++ b/cpp/ql/src/JPL_C/LOC-2/Rule 09/Semaphores.qll @@ -50,7 +50,7 @@ class SemaphoreTake extends LockOperation { result.(SemaphoreGive).getLocked() = this.getLocked() } - override string say() { result = "semaphore take of " + getLocked().getName() } + override string say() { result = "semaphore take of " + this.getLocked().getName() } } class SemaphoreGive extends UnlockOperation { @@ -76,13 +76,13 @@ class LockingPrimitive extends FunctionCall, LockOperation { this.getTarget().getName().replaceAll("Lock", "Unlock") } - override string say() { result = "call to " + getLocked().getName() } + override string say() { result = "call to " + this.getLocked().getName() } } class UnlockingPrimitive extends FunctionCall, UnlockOperation { UnlockingPrimitive() { this.getTarget().getName() = ["taskUnlock", "intUnlock", "taskRtpUnlock"] } - Function getLocked() { result = getMatchingLock().getLocked() } + Function getLocked() { result = this.getMatchingLock().getLocked() } override LockOperation getMatchingLock() { this = result.getMatchingUnlock() } } diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql index 03ad085b6d3..ba7a6b58aa0 100644 --- a/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql +++ b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql @@ -166,6 +166,7 @@ class VarAnalyzableExpr extends AnalyzableExpr, VariableAccess { * Holds if `t` is not an instance of `IntegralType`, * or if `me` cannot be proven to not overflow */ +pragma[inline] predicate overflows(MulExpr me, Type t) { t instanceof IntegralType implies diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/PointlessComparison.ql b/cpp/ql/src/Likely Bugs/Arithmetic/PointlessComparison.ql index 94a6c403937..e2fe02be867 100644 --- a/cpp/ql/src/Likely Bugs/Arithmetic/PointlessComparison.ql +++ b/cpp/ql/src/Likely Bugs/Arithmetic/PointlessComparison.ql @@ -50,10 +50,7 @@ where // If either of the operands is constant, then don't include it. ( if cmp.getLeftOperand().isConstant() - then - if cmp.getRightOperand().isConstant() - then none() // Both operands are constant so don't create a message. - else reason = rightReason + then not cmp.getRightOperand().isConstant() and reason = rightReason else if cmp.getRightOperand().isConstant() then reason = leftReason diff --git a/cpp/ql/src/Likely Bugs/Conversion/NonzeroValueCastToPointer.ql b/cpp/ql/src/Likely Bugs/Conversion/NonzeroValueCastToPointer.ql index 87f52198b0d..47842118874 100644 --- a/cpp/ql/src/Likely Bugs/Conversion/NonzeroValueCastToPointer.ql +++ b/cpp/ql/src/Likely Bugs/Conversion/NonzeroValueCastToPointer.ql @@ -13,14 +13,15 @@ import cpp predicate commonErrorCode(string value) { - value = "0" or - value = "1" or - value = "-1" or - value = "18446744073709551615" or // 2^64-1, i.e. -1 as an unsigned int64 - value = "4294967295" or // 2^32-1, i.e. -1 as an unsigned int32 - value = "3735928559" or // 0xdeadbeef - value = "3735929054" or // 0xdeadc0de - value = "3405691582" // 0xcafebabe + value = + [ + "0", "1", "-1", // common error codes + "18446744073709551615", // 2^64-1, i.e. -1 as an unsigned int64 + "4294967295", // 2^32-1, i.e. -1 as an unsigned int32 + "3735928559", // 0xdeadbeef + "3735929054", // 0xdeadc0de + "3405691582" // 0xcafebabe + ] } from Expr e diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql b/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql index 2e643070a19..ed378dce60a 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql @@ -53,6 +53,7 @@ class ImproperNullTerminationReachability extends StackVariableReachabilityWithR override predicate isBarrier(ControlFlowNode node, StackVariable v) { exprDefinition(v, node, _) or mayAddNullTerminator(node, v.getAnAccess()) or + node.(AddressOfExpr).getOperand() = v.getAnAccess() or // address taken isSinkActual(node, v) // only report first use } } diff --git a/cpp/ql/src/Likely Bugs/Memory Management/NtohlArrayNoBound.qll b/cpp/ql/src/Likely Bugs/Memory Management/NtohlArrayNoBound.qll index 9606fb968ec..37d5a5e5c1b 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/NtohlArrayNoBound.qll +++ b/cpp/ql/src/Likely Bugs/Memory Management/NtohlArrayNoBound.qll @@ -126,13 +126,7 @@ class MallocSizeExpr extends BufferAccess, FunctionCall { } class NetworkFunctionCall extends FunctionCall { - NetworkFunctionCall() { - getTarget().hasName("ntohd") or - getTarget().hasName("ntohf") or - getTarget().hasName("ntohl") or - getTarget().hasName("ntohll") or - getTarget().hasName("ntohs") - } + NetworkFunctionCall() { getTarget().hasName(["ntohd", "ntohf", "ntohl", "ntohll", "ntohs"]) } } class NetworkToBufferSizeConfiguration extends DataFlow::Configuration { diff --git a/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.ql b/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.ql index 8e7bc5bfcf4..f7eca2304b3 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.ql @@ -43,23 +43,25 @@ predicate isSizePlus(Expr e, BufferSizeExpr baseSize, int plus) { predicate strncpyFunction(Function f, int argDest, int argSrc, int argLimit) { exists(string name | name = f.getName() | - ( - name = "strcpy_s" or // strcpy_s(dst, max_amount, src) - name = "wcscpy_s" or // wcscpy_s(dst, max_amount, src) - name = "_mbscpy_s" // _mbscpy_s(dst, max_amount, src) - ) and + name = + [ + "strcpy_s", // strcpy_s(dst, max_amount, src) + "wcscpy_s", // wcscpy_s(dst, max_amount, src) + "_mbscpy_s" // _mbscpy_s(dst, max_amount, src) + ] and argDest = 0 and argSrc = 2 and argLimit = 1 or - ( - name = "strncpy" or // strncpy(dst, src, max_amount) - name = "strncpy_l" or // strncpy_l(dst, src, max_amount, locale) - name = "wcsncpy" or // wcsncpy(dst, src, max_amount) - name = "_wcsncpy_l" or // _wcsncpy_l(dst, src, max_amount, locale) - name = "_mbsncpy" or // _mbsncpy(dst, src, max_amount) - name = "_mbsncpy_l" // _mbsncpy_l(dst, src, max_amount, locale) - ) and + name = + [ + "strncpy", // strncpy(dst, src, max_amount) + "strncpy_l", // strncpy_l(dst, src, max_amount, locale) + "wcsncpy", // wcsncpy(dst, src, max_amount) + "_wcsncpy_l", // _wcsncpy_l(dst, src, max_amount, locale) + "_mbsncpy", // _mbsncpy(dst, src, max_amount) + "_mbsncpy_l" // _mbsncpy_l(dst, src, max_amount, locale) + ] and argDest = 0 and argSrc = 1 and argLimit = 2 diff --git a/cpp/ql/src/Metrics/Internal/CallableExtents.ql b/cpp/ql/src/Metrics/Internal/CallableExtents.ql index 80c798b19ff..7a376c6da72 100644 --- a/cpp/ql/src/Metrics/Internal/CallableExtents.ql +++ b/cpp/ql/src/Metrics/Internal/CallableExtents.ql @@ -18,7 +18,7 @@ class RangeFunction extends Function { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { super.getLocation().hasLocationInfo(path, sl, sc, _, _) and diff --git a/cpp/ql/src/Power of 10/Rule 1/UseOfJmp.ql b/cpp/ql/src/Power of 10/Rule 1/UseOfJmp.ql index 9a6d143bfb6..0a98eafc70f 100644 --- a/cpp/ql/src/Power of 10/Rule 1/UseOfJmp.ql +++ b/cpp/ql/src/Power of 10/Rule 1/UseOfJmp.ql @@ -15,10 +15,7 @@ import cpp class ForbiddenFunction extends Function { ForbiddenFunction() { exists(string name | name = this.getName() | - name = "setjmp" or - name = "longjmp" or - name = "sigsetjmp" or - name = "siglongjmp" + name = ["setjmp", "longjmp", "sigsetjmp", "siglongjmp"] ) } } diff --git a/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql index 5e22506d03a..fc8023053b0 100644 --- a/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql +++ b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql @@ -26,12 +26,8 @@ import TaintedWithPath class FileFunction extends FunctionWithWrappers { FileFunction() { exists(string nme | this.hasGlobalName(nme) | - nme = "fopen" or - nme = "_fopen" or - nme = "_wfopen" or - nme = "open" or - nme = "_open" or - nme = "_wopen" or + nme = ["fopen", "_fopen", "_wfopen", "open", "_open", "_wopen"] + or // create file function on windows nme.matches("CreateFile%") ) @@ -40,10 +36,7 @@ class FileFunction extends FunctionWithWrappers { or // on any of the fstream classes, or filebuf exists(string nme | this.getDeclaringType().hasQualifiedName("std", nme) | - nme = "basic_fstream" or - nme = "basic_ifstream" or - nme = "basic_ofstream" or - nme = "basic_filebuf" + nme = ["basic_fstream", "basic_ifstream", "basic_ofstream", "basic_filebuf"] ) and // we look for either the open method or the constructor (this.getName() = "open" or this instanceof Constructor) diff --git a/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.c b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.c index da5950c5fe5..c61adeee651 100644 --- a/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.c +++ b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.c @@ -9,7 +9,7 @@ int main(int argc, char** argv) { system(command1); } - { + { // GOOD: the user string is encoded by a library routine. char userNameQuoted[1000] = {0}; encodeShellString(userNameQuoted, 1000, userName); diff --git a/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql index 5f516eec83b..26652d9c1da 100644 --- a/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql +++ b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql @@ -3,10 +3,10 @@ * @description Using user-supplied data in an OS command, without * neutralizing special elements, can make code vulnerable * to command injection. - * @kind problem + * @kind path-problem * @problem.severity error * @security-severity 9.8 - * @precision low + * @precision high * @id cpp/command-line-injection * @tags security * external/cwe/cwe-078 @@ -16,13 +16,204 @@ import cpp import semmle.code.cpp.security.CommandExecution import semmle.code.cpp.security.Security -import semmle.code.cpp.security.TaintTracking +import semmle.code.cpp.valuenumbering.GlobalValueNumbering +import semmle.code.cpp.ir.IR +import semmle.code.cpp.ir.dataflow.TaintTracking +import semmle.code.cpp.ir.dataflow.TaintTracking2 +import semmle.code.cpp.security.FlowSources +import semmle.code.cpp.models.implementations.Strcat -from Expr taintedArg, Expr taintSource, string taintCause, string callChain +Expr sinkAsArgumentIndirection(DataFlow::Node sink) { + result = + sink.asOperand() + .(SideEffectOperand) + .getAddressOperand() + .getAnyDef() + .getUnconvertedResultExpression() +} + +/** + * Holds if `fst` is a string that is used in a format or concatenation function resulting in `snd`, + * and is *not* placed at the start of the resulting string. This indicates that the author did not + * expect `fst` to control what program is run if the resulting string is eventually interpreted as + * a command line, for example as an argument to `system`. + */ +predicate interestingConcatenation(DataFlow::Node fst, DataFlow::Node snd) { + exists(FormattingFunctionCall call, int index, FormatLiteral literal | + sinkAsArgumentIndirection(fst) = call.getConversionArgument(index) and + snd.asDefiningArgument() = call.getOutputArgument(false) and + literal = call.getFormat() and + not literal.getConvSpecOffset(index) = 0 and + literal.getConversionChar(index) = ["s", "S"] + ) + or + // strcat and friends + exists(StrcatFunction strcatFunc, CallInstruction call, ReadSideEffectInstruction rse | + call.getStaticCallTarget() = strcatFunc and + rse.getArgumentDef() = call.getArgument(strcatFunc.getParamSrc()) and + fst.asOperand() = rse.getSideEffectOperand() and + snd.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() = + call.getArgument(strcatFunc.getParamDest()) + ) + or + exists(CallInstruction call, Operator op, ReadSideEffectInstruction rse | + call.getStaticCallTarget() = op and + op.hasQualifiedName("std", "operator+") and + op.getType().(UserType).hasQualifiedName("std", "basic_string") and + call.getArgument(1) = rse.getArgumentOperand().getAnyDef() and // left operand + fst.asOperand() = rse.getSideEffectOperand() and + call = snd.asInstruction() + ) +} + +class TaintToConcatenationConfiguration extends TaintTracking::Configuration { + TaintToConcatenationConfiguration() { this = "TaintToConcatenationConfiguration" } + + override predicate isSource(DataFlow::Node source) { source instanceof FlowSource } + + override predicate isSink(DataFlow::Node sink) { interestingConcatenation(sink, _) } + + override predicate isSanitizer(DataFlow::Node node) { + node.asInstruction().getResultType() instanceof IntegralType + or + node.asInstruction().getResultType() instanceof FloatingPointType + } +} + +class ExecTaintConfiguration extends TaintTracking2::Configuration { + ExecTaintConfiguration() { this = "ExecTaintConfiguration" } + + override predicate isSource(DataFlow::Node source) { + exists(DataFlow::Node prevSink, TaintToConcatenationConfiguration conf | + conf.hasFlow(_, prevSink) and + interestingConcatenation(prevSink, source) + ) + } + + override predicate isSink(DataFlow::Node sink) { + shellCommand(sinkAsArgumentIndirection(sink), _) + } + + override predicate isSanitizerOut(DataFlow::Node node) { + isSink(node) // Prevent duplicates along a call chain, since `shellCommand` will include wrappers + } +} + +module StitchedPathGraph { + // There's a different PathNode class for each DataFlowImplN.qll, so we can't simply combine the + // PathGraph predicates directly. Instead, we use a newtype so there's a single type that + // contains both sets of PathNodes. + newtype TMergedPathNode = + TPathNode1(DataFlow::PathNode node) or + TPathNode2(DataFlow2::PathNode node) + + // this wraps the toString and location predicates so we can use the merged node type in a + // selection + class MergedPathNode extends TMergedPathNode { + string toString() { + exists(DataFlow::PathNode n | + this = TPathNode1(n) and + result = n.toString() + ) + or + exists(DataFlow2::PathNode n | + this = TPathNode2(n) and + result = n.toString() + ) + } + + DataFlow::Node getNode() { + exists(DataFlow::PathNode n | + this = TPathNode1(n) and + result = n.getNode() + ) + or + exists(DataFlow2::PathNode n | + this = TPathNode2(n) and + result = n.getNode() + ) + } + + DataFlow::PathNode getPathNode1() { this = TPathNode1(result) } + + DataFlow2::PathNode getPathNode2() { this = TPathNode2(result) } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + exists(DataFlow::PathNode n | + this = TPathNode1(n) and + n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + ) + or + exists(DataFlow2::PathNode n | + this = TPathNode2(n) and + n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + ) + } + } + + query predicate edges(MergedPathNode a, MergedPathNode b) { + exists(DataFlow::PathNode an, DataFlow::PathNode bn | + a = TPathNode1(an) and + b = TPathNode1(bn) and + DataFlow::PathGraph::edges(an, bn) + ) + or + exists(DataFlow2::PathNode an, DataFlow2::PathNode bn | + a = TPathNode2(an) and + b = TPathNode2(bn) and + DataFlow2::PathGraph::edges(an, bn) + ) + or + // This is where paths from the two configurations are connected. `interestingConcatenation` + // is the only thing in this module that's actually specific to the query - everything else is + // just using types and predicates from the DataFlow library. + interestingConcatenation(a.getNode(), b.getNode()) and + a instanceof TPathNode1 and + b instanceof TPathNode2 + } + + query predicate nodes(MergedPathNode mpn, string key, string val) { + // here we just need the union of the underlying `nodes` predicates + exists(DataFlow::PathNode n | + mpn = TPathNode1(n) and + DataFlow::PathGraph::nodes(n, key, val) + ) + or + exists(DataFlow2::PathNode n | + mpn = TPathNode2(n) and + DataFlow2::PathGraph::nodes(n, key, val) + ) + } + + query predicate subpaths( + MergedPathNode arg, MergedPathNode par, MergedPathNode ret, MergedPathNode out + ) { + // just forward subpaths from the underlying libraries. This might be slightly awkward when + // the concatenation is deep in a call chain. + DataFlow::PathGraph::subpaths(arg.getPathNode1(), par.getPathNode1(), ret.getPathNode1(), + out.getPathNode1()) + or + DataFlow2::PathGraph::subpaths(arg.getPathNode2(), par.getPathNode2(), ret.getPathNode2(), + out.getPathNode2()) + } +} + +import StitchedPathGraph + +from + DataFlow::PathNode sourceNode, DataFlow::PathNode concatSink, DataFlow2::PathNode concatSource, + DataFlow2::PathNode sinkNode, string taintCause, string callChain, + TaintToConcatenationConfiguration conf1, ExecTaintConfiguration conf2 where - shellCommand(taintedArg, callChain) and - tainted(taintSource, taintedArg) and - isUserInput(taintSource, taintCause) -select taintedArg, - "This argument to an OS command is derived from $@ and then passed to " + callChain, taintSource, - "user input (" + taintCause + ")" + taintCause = sourceNode.getNode().(FlowSource).getSourceType() and + conf1.hasFlowPath(sourceNode, concatSink) and + interestingConcatenation(concatSink.getNode(), concatSource.getNode()) and // this loses call context + conf2.hasFlowPath(concatSource, sinkNode) and + shellCommand(sinkAsArgumentIndirection(sinkNode.getNode()), callChain) +select sinkAsArgumentIndirection(sinkNode.getNode()), TPathNode1(sourceNode).(MergedPathNode), + TPathNode2(sinkNode).(MergedPathNode), + "This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to " + + callChain, sourceNode, "user input (" + taintCause + ")", concatSource, + concatSource.toString() diff --git a/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql b/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql index b2844c319ba..3b0e3f5198d 100644 --- a/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql +++ b/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql @@ -21,11 +21,7 @@ class TaintSource extends VariableAccess { this.getTarget() instanceof SemanticStackVariable and x.isUserInput(this, cause) | - cause = "read" or - cause = "fread" or - cause = "recv" or - cause = "recvfrom" or - cause = "recvmsg" + cause = ["read", "fread", "recv", "recvfrom", "recvmsg"] ) } diff --git a/cpp/ql/src/Security/CWE/CWE-311/CleartextStorage.inc.qhelp b/cpp/ql/src/Security/CWE/CWE-311/CleartextStorage.inc.qhelp index eb9a6f8adce..f5e978e05cb 100644 --- a/cpp/ql/src/Security/CWE/CWE-311/CleartextStorage.inc.qhelp +++ b/cpp/ql/src/Security/CWE/CWE-311/CleartextStorage.inc.qhelp @@ -9,7 +9,7 @@ storage.

-

Ensure that sensitive information is always encrypted before being stored, especially before writing to a file. +

Ensure that sensitive information is always encrypted before being stored to a file or transmitted over the network. It may be wise to encrypt information before it is put into a buffer that may be readable in memory.

In general, decrypt sensitive information only at the point where it is necessary for it to be used in diff --git a/cpp/ql/src/Security/CWE/CWE-311/CleartextTransmission.qhelp b/cpp/ql/src/Security/CWE/CWE-311/CleartextTransmission.qhelp new file mode 100644 index 00000000000..d715abca84c --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-311/CleartextTransmission.qhelp @@ -0,0 +1,5 @@ + + + diff --git a/cpp/ql/src/Security/CWE/CWE-311/CleartextTransmission.ql b/cpp/ql/src/Security/CWE/CWE-311/CleartextTransmission.ql new file mode 100644 index 00000000000..d7e5343d6dc --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-311/CleartextTransmission.ql @@ -0,0 +1,124 @@ +/** + * @name Cleartext transmission of sensitive information + * @description Transmitting sensitive information across a network in + * cleartext can expose it to an attacker. + * @kind path-problem + * @problem.severity warning + * @security-severity 7.5 + * @precision medium + * @id cpp/cleartext-transmission + * @tags security + * external/cwe/cwe-319 + */ + +import cpp +import semmle.code.cpp.security.SensitiveExprs +import semmle.code.cpp.dataflow.TaintTracking +import semmle.code.cpp.models.interfaces.FlowSource +import DataFlow::PathGraph + +/** + * A function call that sends or receives data over a network. + */ +abstract class NetworkSendRecv extends FunctionCall { + /** + * Gets the expression for the socket or similar object used for sending or + * receiving data (if any). + */ + abstract Expr getSocketExpr(); + + /** + * Gets the expression for the buffer to be sent from / received into. + */ + abstract Expr getDataExpr(); +} + +/** + * A function call that sends data over a network. + * + * note: functions such as `write` may be writing to a network source or a file. We could attempt to determine which, and sort results into `cpp/cleartext-transmission` and perhaps `cpp/cleartext-storage-file`. In practice it usually isn't very important which query reports a result as long as its reported exactly once. + */ +class NetworkSend extends NetworkSendRecv { + RemoteFlowSinkFunction target; + + NetworkSend() { target = this.getTarget() } + + override Expr getSocketExpr() { + exists(FunctionInput input, int arg | + target.hasSocketInput(input) and + input.isParameter(arg) and + result = this.getArgument(arg) + ) + } + + override Expr getDataExpr() { + exists(FunctionInput input, int arg | + target.hasRemoteFlowSink(input, _) and + input.isParameterDeref(arg) and + result = this.getArgument(arg) + ) + } +} + +/** + * A function call that receives data over a network. + */ +class NetworkRecv extends NetworkSendRecv { + RemoteFlowSourceFunction target; + + NetworkRecv() { target = this.getTarget() } + + override Expr getSocketExpr() { + exists(FunctionInput input, int arg | + target.hasSocketInput(input) and + input.isParameter(arg) and + result = this.getArgument(arg) + ) + } + + override Expr getDataExpr() { + exists(FunctionOutput output, int arg | + target.hasRemoteFlowSource(output, _) and + output.isParameterDeref(arg) and + result = this.getArgument(arg) + ) + } +} + +/** + * Taint flow from a sensitive expression to a network operation with data + * tainted by that expression. + */ +class SensitiveSendRecvConfiguration extends TaintTracking::Configuration { + SensitiveSendRecvConfiguration() { this = "SensitiveSendRecvConfiguration" } + + override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof SensitiveExpr } + + override predicate isSink(DataFlow::Node sink) { + exists(NetworkSendRecv transmission | + sink.asExpr() = transmission.getDataExpr() and + // a zero socket descriptor is standard input, which is not interesting for this query. + not exists(Zero zero | + DataFlow::localFlow(DataFlow::exprNode(zero), + DataFlow::exprNode(transmission.getSocketExpr())) + ) + ) + } +} + +from + SensitiveSendRecvConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink, + NetworkSendRecv transmission, string msg +where + config.hasFlowPath(source, sink) and + sink.getNode().asExpr() = transmission.getDataExpr() and + if transmission instanceof NetworkSend + then + msg = + "This operation transmits '" + sink.toString() + + "', which may contain unencrypted sensitive data from $@" + else + msg = + "This operation receives into '" + sink.toString() + + "', which may put unencrypted sensitive data into $@" +select transmission, source, sink, msg, source, source.getNode().asExpr().toString() diff --git a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingCommon.qll b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingCommon.qll index 4854c1dc38e..9978d9ece0b 100644 --- a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingCommon.qll +++ b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingCommon.qll @@ -121,16 +121,14 @@ predicate exprSourceType(Expr use, Type sourceType, Location sourceLoc) { else if use instanceof CrementOperation then exprSourceType(use.(CrementOperation).getOperand(), sourceType, sourceLoc) - else + else ( // Conversions are not in the AST, so ignore them. - if use instanceof Conversion - then none() - else ( - // Source expressions - sourceType = use.getUnspecifiedType() and - isPointerType(sourceType) and - sourceLoc = use.getLocation() - ) + not use instanceof Conversion and + // Source expressions + sourceType = use.getUnspecifiedType() and + isPointerType(sourceType) and + sourceLoc = use.getLocation() + ) } /** diff --git a/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql index bbe3b0805e1..8d016751f39 100644 --- a/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql +++ b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql @@ -103,12 +103,7 @@ private predicate posixSystemInfo(FunctionCall source, Element use) { // - various filesystem parameters // int uname(struct utsname *buf) // - OS name and version - ( - source.getTarget().hasName("confstr") or - source.getTarget().hasName("statvfs") or - source.getTarget().hasName("fstatvfs") or - source.getTarget().hasName("uname") - ) and + source.getTarget().hasName(["confstr", "statvfs", "fstatvfs", "uname"]) and use = source.getArgument(1) } @@ -128,14 +123,9 @@ private predicate posixPWInfo(FunctionCall source, Element use) { // struct group *getgrnam(const char *name); // struct group *getgrgid(gid_t); // struct group *getgrent(void); - ( - source.getTarget().hasName("getpwnam") or - source.getTarget().hasName("getpwuid") or - source.getTarget().hasName("getpwent") or - source.getTarget().hasName("getgrnam") or - source.getTarget().hasName("getgrgid") or - source.getTarget().hasName("getgrent") - ) and + source + .getTarget() + .hasName(["getpwnam", "getpwuid", "getpwent", "getgrnam", "getgrgid", "getgrent"]) and use = source or // int getpwnam_r(const char *name, struct passwd *pwd, @@ -146,31 +136,15 @@ private predicate posixPWInfo(FunctionCall source, Element use) { // char *buf, size_t buflen, struct group **result); // int getgrnam_r(const char *name, struct group *grp, // char *buf, size_t buflen, struct group **result); - ( - source.getTarget().hasName("getpwnam_r") or - source.getTarget().hasName("getpwuid_r") or - source.getTarget().hasName("getgrgid_r") or - source.getTarget().hasName("getgrnam_r") - ) and - ( - use = source.getArgument(1) or - use = source.getArgument(2) or - use = source.getArgument(4) - ) + source.getTarget().hasName(["getpwnam_r", "getpwuid_r", "getgrgid_r", "getgrnam_r"]) and + use = source.getArgument([1, 2, 4]) or // int getpwent_r(struct passwd *pwd, char *buffer, size_t bufsize, // struct passwd **result); // int getgrent_r(struct group *gbuf, char *buf, // size_t buflen, struct group **gbufp); - ( - source.getTarget().hasName("getpwent_r") or - source.getTarget().hasName("getgrent_r") - ) and - ( - use = source.getArgument(0) or - use = source.getArgument(1) or - use = source.getArgument(3) - ) + source.getTarget().hasName(["getpwent_r", "getgrent_r"]) and + use = source.getArgument([0, 1, 3]) } /** @@ -190,13 +164,11 @@ private predicate windowsSystemInfo(FunctionCall source, Element use) { // BOOL WINAPI GetVersionEx(_Inout_ LPOSVERSIONINFO lpVersionInfo); // void WINAPI GetSystemInfo(_Out_ LPSYSTEM_INFO lpSystemInfo); // void WINAPI GetNativeSystemInfo(_Out_ LPSYSTEM_INFO lpSystemInfo); - ( - source.getTarget().hasGlobalName("GetVersionEx") or - source.getTarget().hasGlobalName("GetVersionExA") or - source.getTarget().hasGlobalName("GetVersionExW") or - source.getTarget().hasGlobalName("GetSystemInfo") or - source.getTarget().hasGlobalName("GetNativeSystemInfo") - ) and + source + .getTarget() + .hasGlobalName([ + "GetVersionEx", "GetVersionExA", "GetVersionExW", "GetSystemInfo", "GetNativeSystemInfo" + ]) and use = source.getArgument(0) } @@ -216,11 +188,11 @@ private predicate windowsFolderPath(FunctionCall source, Element use) { // _In_ int csidl, // _In_ BOOL fCreate // ); - ( - source.getTarget().hasGlobalName("SHGetSpecialFolderPath") or - source.getTarget().hasGlobalName("SHGetSpecialFolderPathA") or - source.getTarget().hasGlobalName("SHGetSpecialFolderPathW") - ) and + source + .getTarget() + .hasGlobalName([ + "SHGetSpecialFolderPath", "SHGetSpecialFolderPathA", "SHGetSpecialFolderPathW" + ]) and use = source.getArgument(1) or // HRESULT SHGetKnownFolderPath( @@ -239,11 +211,7 @@ private predicate windowsFolderPath(FunctionCall source, Element use) { // _In_ DWORD dwFlags, // _Out_ LPTSTR pszPath // ); - ( - source.getTarget().hasGlobalName("SHGetFolderPath") or - source.getTarget().hasGlobalName("SHGetFolderPathA") or - source.getTarget().hasGlobalName("SHGetFolderPathW") - ) and + source.getTarget().hasGlobalName(["SHGetFolderPath", "SHGetFolderPathA", "SHGetFolderPathW"]) and use = source.getArgument(4) or // HRESULT SHGetFolderPathAndSubDir( @@ -254,11 +222,11 @@ private predicate windowsFolderPath(FunctionCall source, Element use) { // _In_ LPCTSTR pszSubDir, // _Out_ LPTSTR pszPath // ); - ( - source.getTarget().hasGlobalName("SHGetFolderPathAndSubDir") or - source.getTarget().hasGlobalName("SHGetFolderPathAndSubDirA") or - source.getTarget().hasGlobalName("SHGetFolderPathAndSubDirW") - ) and + source + .getTarget() + .hasGlobalName([ + "SHGetFolderPathAndSubDir", "SHGetFolderPathAndSubDirA", "SHGetFolderPathAndSubDirW" + ]) and use = source.getArgument(5) } @@ -273,11 +241,7 @@ class WindowsFolderPath extends SystemData { } private predicate logonUser(FunctionCall source, VariableAccess use) { - ( - source.getTarget().hasGlobalName("LogonUser") or - source.getTarget().hasGlobalName("LogonUserW") or - source.getTarget().hasGlobalName("LogonUserA") - ) and + source.getTarget().hasGlobalName(["LogonUser", "LogonUserW", "LogonUserA"]) and use = source.getAnArgument() } @@ -297,11 +261,7 @@ private predicate regQuery(FunctionCall source, VariableAccess use) { // _Out_opt_ LPTSTR lpValue, // _Inout_opt_ PLONG lpcbValue // ); - ( - source.getTarget().hasGlobalName("RegQueryValue") or - source.getTarget().hasGlobalName("RegQueryValueA") or - source.getTarget().hasGlobalName("RegQueryValueW") - ) and + source.getTarget().hasGlobalName(["RegQueryValue", "RegQueryValueA", "RegQueryValueW"]) and use = source.getArgument(2) or // LONG WINAPI RegQueryMultipleValues( @@ -311,11 +271,11 @@ private predicate regQuery(FunctionCall source, VariableAccess use) { // _Out_opt_ LPTSTR lpValueBuf, // _Inout_opt_ LPDWORD ldwTotsize // ); - ( - source.getTarget().hasGlobalName("RegQueryMultipleValues") or - source.getTarget().hasGlobalName("RegQueryMultipleValuesA") or - source.getTarget().hasGlobalName("RegQueryMultipleValuesW") - ) and + source + .getTarget() + .hasGlobalName([ + "RegQueryMultipleValues", "RegQueryMultipleValuesA", "RegQueryMultipleValuesW" + ]) and use = source.getArgument(3) or // LONG WINAPI RegQueryValueEx( @@ -326,11 +286,7 @@ private predicate regQuery(FunctionCall source, VariableAccess use) { // _Out_opt_ LPBYTE lpData, // _Inout_opt_ LPDWORD lpcbData // ); - ( - source.getTarget().hasGlobalName("RegQueryValueEx") or - source.getTarget().hasGlobalName("RegQueryValueExA") or - source.getTarget().hasGlobalName("RegQueryValueExW") - ) and + source.getTarget().hasGlobalName(["RegQueryValueEx", "RegQueryValueExA", "RegQueryValueExW"]) and use = source.getArgument(4) or // LONG WINAPI RegGetValue( @@ -342,11 +298,7 @@ private predicate regQuery(FunctionCall source, VariableAccess use) { // _Out_opt_ PVOID pvData, // _Inout_opt_ LPDWORD pcbData // ); - ( - source.getTarget().hasGlobalName("RegGetValue") or - source.getTarget().hasGlobalName("RegGetValueA") or - source.getTarget().hasGlobalName("RegGetValueW") - ) and + source.getTarget().hasGlobalName(["RegGetValue", "RegGetValueA", "RegGetValueW"]) and use = source.getArgument(5) } @@ -408,12 +360,7 @@ private predicate socketOutput(FunctionCall call, Expr data) { // const struct sockaddr *dest_addr, socklen_t addrlen); // ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); // int write(int handle, void *buffer, int nbyte); - ( - call.getTarget().hasGlobalName("send") or - call.getTarget().hasGlobalName("sendto") or - call.getTarget().hasGlobalName("sendmsg") or - call.getTarget().hasGlobalName("write") - ) and + call.getTarget().hasGlobalName(["send", "sendto", "sendmsg", "write"]) and data = call.getArgument(1) and socketFileDescriptor(call.getArgument(0)) ) diff --git a/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql b/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql index 28e7295dcdc..357e6375570 100644 --- a/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql +++ b/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql @@ -4,10 +4,13 @@ * @kind problem * @id cpp/incorrect-allocation-error-handling * @problem.severity warning + * @security-severity 7.5 * @precision medium * @tags correctness * security * external/cwe/cwe-570 + * external/cwe/cwe-252 + * external/cwe/cwe-755 */ import cpp diff --git a/cpp/ql/src/definitions.qll b/cpp/ql/src/definitions.qll index eac03ce7082..cb229d66ef1 100644 --- a/cpp/ql/src/definitions.qll +++ b/cpp/ql/src/definitions.qll @@ -24,7 +24,7 @@ class Top extends Element { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ pragma[noopt] final predicate hasLocationInfo( diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1041/FindWrapperFunctions.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-1041/FindWrapperFunctions.cpp new file mode 100644 index 00000000000..a04107bbadf --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1041/FindWrapperFunctions.cpp @@ -0,0 +1,18 @@ +... +int myFclose(FILE * fmy) +{ + if(!fclose(fmy)) { + fmy = NULL; + return 0; + } + return -1; +} +... + fe = fopen("myFile.txt", "wt"); + ... + fclose(fe); // BAD +... + fe = fopen("myFile.txt", "wt"); + ... + myFclose(fe); // GOOD +... diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1041/FindWrapperFunctions.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-1041/FindWrapperFunctions.qhelp new file mode 100644 index 00000000000..cc8519a3410 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1041/FindWrapperFunctions.qhelp @@ -0,0 +1,23 @@ + + + +

The presence of a shell function with additional check indicates the possible risks of the call. Use this check everywhere.

+ + + + +

The following example demonstrates fallacious and fixed methods of using wrapper functions.

+ + +
+ + +
  • + CERT C Coding Standard: + JNI00-J. Define wrappers around native methods. +
  • + +
    + diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1041/FindWrapperFunctions.ql b/cpp/ql/src/experimental/Security/CWE/CWE-1041/FindWrapperFunctions.ql new file mode 100644 index 00000000000..4b147fa3612 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1041/FindWrapperFunctions.ql @@ -0,0 +1,141 @@ +/** + * @name Missed opportunity to call wrapper function + * @description If a wrapper function is defined for a given function, any call to the given function should be via the wrapper function. + * @kind problem + * @id cpp/call-to-function-without-wrapper + * @problem.severity warning + * @precision medium + * @tags correctness + * maintainability + * security + * external/cwe/cwe-1041 + */ + +import cpp +import semmle.code.cpp.valuenumbering.GlobalValueNumbering +import semmle.code.cpp.commons.Assertions + +/** + * A function call that is used in error situations (logging, throwing an exception, abnormal termination). + */ +class CallUsedToHandleErrors extends FunctionCall { + CallUsedToHandleErrors() { + // call that is known to not return + not exists(this.(ControlFlowNode).getASuccessor()) + or + // call throwing an exception + exists(ThrowExpr tex | tex = this.(ControlFlowNode).getASuccessor()) + or + // call logging a message, possibly an error + exists(FormattingFunction ff | ff = this.(ControlFlowNode).getASuccessor()) + or + // enabling recursive search + exists(CallUsedToHandleErrors fr | getTarget() = fr.getEnclosingFunction()) + } +} + +/** Holds if the conditions for a call outside the wrapper function are met. */ +predicate conditionsOutsideWrapper(FunctionCall fcp) { + fcp.getNumberOfArguments() > 0 and + not exists(ConditionalStmt cdtmp | fcp.getEnclosingStmt().getParentStmt*() = cdtmp) and + not exists(Loop lptmp | fcp.getEnclosingStmt().getParentStmt*() = lptmp) and + not exists(ReturnStmt rttmp | fcp.getEnclosingStmt().getParentStmt*() = rttmp) and + not exists(FunctionCall fctmp2 | fcp = fctmp2.getAnArgument().getAChild*()) and + not exists(Assignment astmp | fcp = astmp.getRValue().getAChild*()) and + not exists(Initializer intmp | fcp = intmp.getExpr().getAChild*()) and + not exists(Assertion astmp | fcp = astmp.getAsserted().getAChild*()) and + not exists(Operation optmp | fcp = optmp.getAChild*()) and + not exists(ArrayExpr aetmp | fcp = aetmp.getAChild*()) and + not exists(ExprCall ectmp | fcp = ectmp.getAnArgument().getAChild*()) +} + +/** Holds if the conditions for calling `fcp` inside the `fnp` wrapper function are met. */ +pragma[inline] +predicate conditionsInsideWrapper(FunctionCall fcp, Function fnp) { + not exists(FunctionCall fctmp2 | + fctmp2.getEnclosingFunction() = fnp and fcp = fctmp2.getAnArgument().getAChild*() + ) and + not fcp instanceof CallUsedToHandleErrors and + not fcp.getAnArgument().isConstant() and + fcp.getEnclosingFunction() = fnp and + fnp.getNumberOfParameters() > 0 and + // the call arguments must be passed through the arguments of the wrapper function + forall(int i | i in [0 .. fcp.getNumberOfArguments() - 1] | + globalValueNumber(fcp.getArgument(i)) = globalValueNumber(fnp.getAParameter().getAnAccess()) + ) and + // there should be no more than one required call inside the wrapper function + not exists(FunctionCall fctmp | + fctmp.getTarget() = fcp.getTarget() and + fctmp.getFile() = fcp.getFile() and + fctmp != fcp and + fctmp.getEnclosingFunction() = fnp + ) and + // inside the wrapper function there should be no calls without paths to the desired function + not exists(FunctionCall fctmp | + fctmp.getEnclosingFunction() = fnp and + fctmp.getFile() = fcp.getFile() and + fctmp != fcp and + ( + fctmp = fcp.getAPredecessor+() + or + not exists(FunctionCall fctmp1 | + fctmp1 = fcp and + ( + fctmp.getASuccessor+() = fctmp1 or + fctmp.getAPredecessor+() = fctmp1 + ) + ) + ) + ) +} + +/** Holds if the conditions for the wrapper function are met. */ +pragma[inline] +predicate conditionsForWrapper(FunctionCall fcp, Function fnp) { + not exists(ExprCall ectmp | fnp = ectmp.getEnclosingFunction()) and + not exists(Loop lp | lp.getEnclosingFunction() = fnp) and + not exists(SwitchStmt sw | sw.getEnclosingFunction() = fnp) and + not fnp instanceof Operator and + // inside the wrapper function there should be checks of arguments or the result, + // perhaps by means of passing the latter as an argument to some function + ( + exists(IfStmt ifs | + ifs.getEnclosingFunction() = fnp and + ( + globalValueNumber(ifs.getCondition().getAChild*()) = globalValueNumber(fcp.getAnArgument()) and + ifs.getASuccessor*() = fcp + or + ifs.getCondition().getAChild() = fcp + ) + ) + or + exists(FunctionCall fctmp | + fctmp.getEnclosingFunction() = fnp and + globalValueNumber(fctmp.getAnArgument().getAChild*()) = globalValueNumber(fcp) + ) + ) and + // inside the wrapper function there must be a function call to handle the error + exists(CallUsedToHandleErrors fctmp | + fctmp.getEnclosingFunction() = fnp and + forall(int i | i in [0 .. fnp.getNumberOfParameters() - 1] | + fnp.getParameter(i).getAnAccess().getTarget() = + fcp.getAnArgument().(VariableAccess).getTarget() or + fnp.getParameter(i).getUnspecifiedType() instanceof Class or + fnp.getParameter(i).getUnspecifiedType().(ReferenceType).getBaseType() instanceof Class or + fnp.getParameter(i).getAnAccess().getTarget() = + fctmp.getAnArgument().(VariableAccess).getTarget() + ) + ) +} + +from FunctionCall fc, Function fn +where + exists(FunctionCall fctmp | + conditionsInsideWrapper(fctmp, fn) and + conditionsForWrapper(fctmp, fn) and + conditionsOutsideWrapper(fc) and + fctmp.getTarget() = fc.getTarget() and + fc.getEnclosingFunction() != fn and + fc.getEnclosingFunction().getMetrics().getNumberOfCalls() > fn.getMetrics().getNumberOfCalls() + ) +select fc, "Consider changing the call to $@", fn, fn.getName() diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-273/PrivilegeDroppingOutoforder.ql b/cpp/ql/src/experimental/Security/CWE/CWE-273/PrivilegeDroppingOutoforder.ql index 7798203205a..9d2faf793c5 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-273/PrivilegeDroppingOutoforder.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-273/PrivilegeDroppingOutoforder.ql @@ -44,14 +44,13 @@ class SetuidLikeWrapperCall extends FunctionCall { class CallBeforeSetuidFunctionCall extends FunctionCall { CallBeforeSetuidFunctionCall() { - ( - getTarget().hasGlobalName("setgid") or - getTarget().hasGlobalName("setresgid") or - // Compatibility may require skipping initgroups and setgroups return checks. - // A stricter best practice is to check the result and errnor for EPERM. - getTarget().hasGlobalName("initgroups") or - getTarget().hasGlobalName("setgroups") - ) and + getTarget() + .hasGlobalName([ + "setgid", "setresgid", + // Compatibility may require skipping initgroups and setgroups return checks. + // A stricter best practice is to check the result and errnor for EPERM. + "initgroups", "setgroups" + ]) and // setgid/setresgid/etc with the root group are false positives. not argumentMayBeRoot(getArgument(0)) } diff --git a/cpp/ql/src/external/CodeDuplication.qll b/cpp/ql/src/external/CodeDuplication.qll index 8cc56d12e19..2656378bf62 100644 --- a/cpp/ql/src/external/CodeDuplication.qll +++ b/cpp/ql/src/external/CodeDuplication.qll @@ -38,10 +38,10 @@ class Copy extends @duplication_or_similarity { int sourceStartColumn() { tokens(this, 0, _, result, _, _) } /** Gets the line on which the last token in this block ends. */ - int sourceEndLine() { tokens(this, lastToken(), _, _, result, _) } + int sourceEndLine() { tokens(this, this.lastToken(), _, _, result, _) } /** Gets the column on which the last token in this block ends. */ - int sourceEndColumn() { tokens(this, lastToken(), _, _, _, result) } + int sourceEndColumn() { tokens(this, this.lastToken(), _, _, _, result) } /** Gets the number of lines containing at least (part of) one token in this block. */ int sourceLines() { result = this.sourceEndLine() + 1 - this.sourceStartLine() } @@ -61,16 +61,16 @@ class Copy extends @duplication_or_similarity { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { - sourceFile().getAbsolutePath() = filepath and - startline = sourceStartLine() and - startcolumn = sourceStartColumn() and - endline = sourceEndLine() and - endcolumn = sourceEndColumn() + this.sourceFile().getAbsolutePath() = filepath and + startline = this.sourceStartLine() and + startcolumn = this.sourceStartColumn() and + endline = this.sourceEndLine() and + endcolumn = this.sourceEndColumn() } /** Gets a textual representation of this element. */ @@ -79,13 +79,15 @@ class Copy extends @duplication_or_similarity { /** A block of duplicated code. */ class DuplicateBlock extends Copy, @duplication { - override string toString() { result = "Duplicate code: " + sourceLines() + " duplicated lines." } + override string toString() { + result = "Duplicate code: " + this.sourceLines() + " duplicated lines." + } } /** A block of similar code. */ class SimilarBlock extends Copy, @similarity { override string toString() { - result = "Similar code: " + sourceLines() + " almost duplicated lines." + result = "Similar code: " + this.sourceLines() + " almost duplicated lines." } } diff --git a/cpp/ql/src/external/DefectFilter.qll b/cpp/ql/src/external/DefectFilter.qll index 675f3b25faa..b932ffd0470 100644 --- a/cpp/ql/src/external/DefectFilter.qll +++ b/cpp/ql/src/external/DefectFilter.qll @@ -8,7 +8,7 @@ import cpp * column `startcolumn` of line `startline` to column `endcolumn` of line `endline` * in file `filepath`. * - * For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * For more information, see [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ external predicate defectResults( int id, string queryPath, string file, int startline, int startcol, int endline, int endcol, diff --git a/cpp/ql/src/external/MetricFilter.qll b/cpp/ql/src/external/MetricFilter.qll index dd9cece78ce..0315cd23c8d 100644 --- a/cpp/ql/src/external/MetricFilter.qll +++ b/cpp/ql/src/external/MetricFilter.qll @@ -8,7 +8,7 @@ import cpp * column `startcolumn` of line `startline` to column `endcolumn` of line `endline` * in file `filepath`. * - * For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * For more information, see [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ external predicate metricResults( int id, string queryPath, string file, int startline, int startcol, int endline, int endcol, @@ -67,7 +67,7 @@ class MetricResult extends int { /** Gets the URL corresponding to the location of this query result. */ string getURL() { result = - "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" + - getEndLine() + ":" + getEndColumn() + "file://" + this.getFile().getAbsolutePath() + ":" + this.getStartLine() + ":" + + this.getStartColumn() + ":" + this.getEndLine() + ":" + this.getEndColumn() } } diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 53.1.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 53.1.ql index 608374b241c..91b5e4e8b98 100644 --- a/cpp/ql/src/jsf/4.09 Style/AV Rule 53.1.ql +++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 53.1.ql @@ -14,12 +14,5 @@ import cpp from Include i, string name where name = i.getIncludeText() and - ( - name.matches("%'%") or - name.matches("%\\\\%") or - name.matches("%/*%") or - name.matches("%//%") or - name.matches("%\"%\"%\"%") or - name.matches("%<%\"%>%") - ) + name.matches(["%'%", "%\\\\%", "%/*%", "%//%", "%\"%\"%\"%", "%<%\"%>%"]) select i, "AV Rule 53.1: Invalid character sequence in header file name '" + name + "'" diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql index 3ae7bc65b45..adeb54746df 100644 --- a/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql @@ -230,13 +230,13 @@ predicate leakedInSameMethod(Resource r, Expr acquire) { ) ) ) - or - exists(FunctionAccess fa, string kind | - // the address of a function that releases `r` is taken (and likely - // used to release `r` at some point). - r.acquisitionWithRequiredKind(acquire, kind) and - fa.getTarget() = r.getAReleaseExpr(kind).getEnclosingFunction() - ) + ) + or + exists(FunctionAccess fa, string kind | + // the address of a function that releases `r` is taken (and likely + // used to release `r` at some point). + r.acquisitionWithRequiredKind(acquire, kind) and + fa.getTarget() = r.getAReleaseExpr(kind).getEnclosingFunction() ) } diff --git a/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 209.ql b/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 209.ql index b881c5ed601..64a130f46e0 100644 --- a/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 209.ql +++ b/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 209.ql @@ -15,13 +15,7 @@ import cpp from Element u, ArithmeticType at where - ( - at.hasName("int") or - at.hasName("short") or - at.hasName("long") or - at.hasName("float") or - at.hasName("double") - ) and + at.hasName(["int", "short", "long", "float", "double"]) and u = at.getATypeNameUse() and not at instanceof WideCharType select u, "AV Rule 209: The basic types of int, short, long, float and double shall not be used." diff --git a/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll b/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll index d351bac89a8..52a790cca28 100644 --- a/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll +++ b/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll @@ -4,7 +4,7 @@ * (in that the `.expected` file should always be empty). * * To add this framework to a new language: - * - Add a file `InlineExpectationsTestPrivate.qll` that defines a `LineComment` class. This class + * - Add a file `InlineExpectationsTestPrivate.qll` that defines a `ExpectationComment` class. This class * must support a `getContents` method that returns the contents of the given comment, _excluding_ * the comment indicator itself. It should also define `toString` and `getLocation` as usual. * @@ -60,8 +60,8 @@ * * Example: * ```cpp - * int i = x + 5; // $const=5 - * int j = y + (7 - 3) // $const=7 const=3 const=4 // The result of the subtraction is a constant. + * int i = x + 5; // $ const=5 + * int j = y + (7 - 3) // $ const=7 const=3 const=4 // The result of the subtraction is a constant. * ``` * * For tests that contain known missing and spurious results, it is possible to further @@ -194,7 +194,7 @@ private int getEndOfColumnPosition(int start, string content) { } private predicate getAnExpectation( - LineComment comment, TColumn column, string expectation, string tags, string value + ExpectationComment comment, TColumn column, string expectation, string tags, string value ) { exists(string content | content = comment.getContents().regexpCapture(expectationCommentPattern(), 1) and @@ -247,14 +247,14 @@ private newtype TFailureLocatable = ) { test.hasActualResult(location, element, tag, value) } or - TValidExpectation(LineComment comment, string tag, string value, string knownFailure) { + TValidExpectation(ExpectationComment comment, string tag, string value, string knownFailure) { exists(TColumn column, string tags | getAnExpectation(comment, column, _, tags, value) and tag = tags.splitAt(",") and knownFailure = getColumnString(column) ) } or - TInvalidExpectation(LineComment comment, string expectation) { + TInvalidExpectation(ExpectationComment comment, string expectation) { getAnExpectation(comment, _, expectation, _, _) and not expectation.regexpMatch(expectationPattern()) } @@ -292,7 +292,7 @@ class ActualResult extends FailureLocatable, TActualResult { } abstract private class Expectation extends FailureLocatable { - LineComment comment; + ExpectationComment comment; override string toString() { result = comment.toString() } diff --git a/cpp/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll b/cpp/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll index f3aae029eb4..adad4238095 100644 --- a/cpp/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll +++ b/cpp/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll @@ -1,16 +1,16 @@ import cpp -private newtype TLineComment = MkLineComment(CppStyleComment c) +private newtype TExpectationComment = MkExpectationComment(CppStyleComment c) /** * Represents a line comment in the CPP style. * Unlike the `CppStyleComment` class, however, the string returned by `getContents` does _not_ * include the preceding comment marker (`//`). */ -class LineComment extends TLineComment { +class ExpectationComment extends TExpectationComment { CppStyleComment comment; - LineComment() { this = MkLineComment(comment) } + ExpectationComment() { this = MkExpectationComment(comment) } /** Returns the contents of the given comment, _without_ the preceding comment marker (`//`). */ string getContents() { result = comment.getContents().suffix(2) } diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1041/semmle/tests/FindWrapperFunctions.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1041/semmle/tests/FindWrapperFunctions.expected new file mode 100644 index 00000000000..b8152734e2d --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1041/semmle/tests/FindWrapperFunctions.expected @@ -0,0 +1 @@ +| test.cpp:23:3:23:8 | call to fclose | Consider changing the call to $@ | test.cpp:9:6:9:13 | myFclose | myFclose | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1041/semmle/tests/FindWrapperFunctions.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1041/semmle/tests/FindWrapperFunctions.qlref new file mode 100644 index 00000000000..22dae13892f --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1041/semmle/tests/FindWrapperFunctions.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-1041/FindWrapperFunctions.ql diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1041/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1041/semmle/tests/test.cpp new file mode 100644 index 00000000000..4f862a324e5 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1041/semmle/tests/test.cpp @@ -0,0 +1,27 @@ +#define NULL (0) +typedef int FILE; +FILE *fopen(const char *filename, const char *mode); +int fclose(FILE *stream); +extern FILE * fe; +extern int printf(const char *fmt, ...); +void exit(int status); + +void myFclose(FILE * fmy) +{ + int i; + if(fmy) { + i = fclose(fmy); + fmy = NULL; + printf("close end is code %d",i); + if(i!=0) exit(1); + } +} + +int main(int argc, char *argv[]) +{ + fe = fopen("myFile.txt", "wt"); + fclose(fe); // BAD + fe = fopen("myFile.txt", "wt"); + myFclose(fe); // GOOD + return 0; +} diff --git a/cpp/ql/test/include/iterator.h b/cpp/ql/test/include/iterator.h new file mode 100644 index 00000000000..77758bfa8da --- /dev/null +++ b/cpp/ql/test/include/iterator.h @@ -0,0 +1,97 @@ +#if !defined(CODEQL_ITERATOR_H) +#define CODEQL_ITERATOR_H + +typedef unsigned long size_t; + +#include "type_traits.h" + +namespace std { + struct ptrdiff_t; + + template struct iterator_traits; + + template + struct iterator { + typedef Category iterator_category; + + iterator(); + iterator(iterator > const &other); // non-const -> const conversion constructor + + iterator &operator++(); + iterator operator++(int); + iterator &operator--(); + iterator operator--(int); + bool operator==(iterator other) const; + bool operator!=(iterator other) const; + reference_type operator*() const; + pointer_type operator->() const; + iterator operator+(int); + iterator operator-(int); + iterator &operator+=(int); + iterator &operator-=(int); + int operator-(iterator); + reference_type operator[](int); + }; + + struct input_iterator_tag {}; + struct forward_iterator_tag : public input_iterator_tag {}; + struct bidirectional_iterator_tag : public forward_iterator_tag {}; + struct random_access_iterator_tag : public bidirectional_iterator_tag {}; + + struct output_iterator_tag {}; + + template + class back_insert_iterator { + protected: + Container* container = nullptr; + public: + using iterator_category = output_iterator_tag; + using value_type = void; + using difference_type = ptrdiff_t; + using pointer = void; + using reference = void; + using container_type = Container; + constexpr back_insert_iterator() noexcept = default; + constexpr explicit back_insert_iterator(Container& x); + back_insert_iterator& operator=(const typename Container::value_type& value); + back_insert_iterator& operator=(typename Container::value_type&& value); + back_insert_iterator& operator*(); + back_insert_iterator& operator++(); + back_insert_iterator operator++(int); + }; + + template + constexpr back_insert_iterator back_inserter(Container& x) { + return back_insert_iterator(x); + } + + template + class front_insert_iterator { + protected: + Container* container = nullptr; + public: + using iterator_category = output_iterator_tag; + using value_type = void; + using difference_type = ptrdiff_t; + using pointer = void; + using reference = void; + using container_type = Container; + constexpr front_insert_iterator() noexcept = default; + constexpr explicit front_insert_iterator(Container& x); + constexpr front_insert_iterator& operator=(const typename Container::value_type& value); + constexpr front_insert_iterator& operator=(typename Container::value_type&& value); + constexpr front_insert_iterator& operator*(); + constexpr front_insert_iterator& operator++(); + constexpr front_insert_iterator operator++(int); + }; + template + constexpr front_insert_iterator front_inserter(Container& x) { + return front_insert_iterator(x); + } +} + +#endif \ No newline at end of file diff --git a/cpp/ql/test/include/string.h b/cpp/ql/test/include/string.h new file mode 100644 index 00000000000..8d577c350f9 --- /dev/null +++ b/cpp/ql/test/include/string.h @@ -0,0 +1,85 @@ +#if !defined(CODEQL_STRING_H) +#define CODEQL_STRING_H + +#include "iterator.h" + +namespace std +{ + template struct char_traits; + + typedef size_t streamsize; + + template class allocator { + public: + allocator() throw(); + typedef size_t size_type; + }; + + template, class Allocator = allocator > + class basic_string { + public: + using value_type = charT; + using reference = value_type&; + using const_reference = const value_type&; + typedef typename Allocator::size_type size_type; + static const size_type npos = -1; + + explicit basic_string(const Allocator& a = Allocator()); + basic_string(const charT* s, const Allocator& a = Allocator()); + template basic_string(InputIterator begin, InputIterator end, const Allocator& a = Allocator()); + + const charT* c_str() const; + charT* data() noexcept; + size_t length() const; + + typedef std::iterator iterator; + typedef std::iterator const_iterator; + + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + const_iterator cbegin() const; + const_iterator cend() const; + + void push_back(charT c); + + const charT& front() const; + charT& front(); + const charT& back() const; + charT& back(); + + const_reference operator[](size_type pos) const; + reference operator[](size_type pos); + const_reference at(size_type n) const; + reference at(size_type n); + template basic_string& operator+=(const T& t); + basic_string& operator+=(const charT* s); + basic_string& append(const basic_string& str); + basic_string& append(const charT* s); + basic_string& append(size_type n, charT c); + template basic_string& append(InputIterator first, InputIterator last); + basic_string& assign(const basic_string& str); + basic_string& assign(size_type n, charT c); + template basic_string& assign(InputIterator first, InputIterator last); + basic_string& insert(size_type pos, const basic_string& str); + basic_string& insert(size_type pos, size_type n, charT c); + basic_string& insert(size_type pos, const charT* s); + iterator insert(const_iterator p, size_type n, charT c); + template iterator insert(const_iterator p, InputIterator first, InputIterator last); + basic_string& replace(size_type pos1, size_type n1, const basic_string& str); + basic_string& replace(size_type pos1, size_type n1, size_type n2, charT c); + size_type copy(charT* s, size_type n, size_type pos = 0) const; + void clear() noexcept; + basic_string substr(size_type pos = 0, size_type n = npos) const; + void swap(basic_string& s) noexcept/*(allocator_traits::propagate_on_container_swap::value || allocator_traits::is_always_equal::value)*/; + }; + + template basic_string operator+(const basic_string& lhs, const basic_string& rhs); + template basic_string operator+(const basic_string& lhs, const charT* rhs); + template basic_string operator+(const charT* lhs, const basic_string& rhs); + + typedef basic_string string; +} + +#endif \ No newline at end of file diff --git a/cpp/ql/test/include/type_traits.h b/cpp/ql/test/include/type_traits.h index 19bdd46906b..dba04f36cad 100644 --- a/cpp/ql/test/include/type_traits.h +++ b/cpp/ql/test/include/type_traits.h @@ -1,21 +1,44 @@ #if !defined(CODEQL_TYPE_TRAITS_H) #define CODEQL_TYPE_TRAITS_H +typedef unsigned long size_t; + namespace std { - template - struct remove_reference { + template + struct remove_const { typedef T type; }; + + template + struct remove_const { typedef T type; }; + + // `remove_const_t` removes any `const` specifier from `T` + template + using remove_const_t = typename remove_const::type; + + template + struct remove_reference { typedef T type; }; + + template + struct remove_reference { typedef T type; }; + + template + struct remove_reference { typedef T type; }; + + // `remove_reference_t` removes any `&` from `T` + template + using remove_reference_t = typename remove_reference::type; + + template + struct decay_impl { typedef T type; }; - template - struct remove_reference { - typedef T type; + template + struct decay_impl { + typedef T* type; }; - template - struct remove_reference { - typedef T type; - }; + template + using decay_t = typename decay_impl>::type; } #endif diff --git a/cpp/ql/test/library-tests/access/noPublic/noPublic.ql b/cpp/ql/test/library-tests/access/noPublic/noPublic.ql index e6d78dcfd5a..c249ea45d74 100644 --- a/cpp/ql/test/library-tests/access/noPublic/noPublic.ql +++ b/cpp/ql/test/library-tests/access/noPublic/noPublic.ql @@ -3,5 +3,5 @@ import cpp from AccessSpecifier spec // There is no way to create "protected" access without writing the keyword // `protected` in the source, so we don't need to test for that. -where spec.hasName("private") or spec.hasName("public") +where spec.hasName(["private", "public"]) select spec diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass_declonly.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass_declonly.cpp index f8169db1128..307e1ecaa76 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass_declonly.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass_declonly.cpp @@ -64,6 +64,6 @@ void test_copyableclass_declonly() sink(s1); // $ ast,ir sink(s2); // $ ast,ir - sink(s3 = source()); // $ ast MISSING: ir + sink(s3 = source()); // $ ast,ir } } diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp index aabd898830e..4e85a6b9aa3 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp @@ -415,10 +415,10 @@ void test_unordered_map() sink(m30["abc"]); sink(m31.try_emplace("abc", source(), 2)); // $ ast,ir sink(m31); // $ ast,ir - sink(m31["abc"]); // $ ast MISSING: ir + sink(m31["abc"]); // $ ast,ir sink(m32.try_emplace("abc", 1, source())); // $ ast,ir sink(m32); // $ ast,ir - sink(m32["abc"]); // $ ast MISSING: ir + sink(m32["abc"]); // $ ast,ir // additional emplace test cases std::unordered_map m33; diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp index 0e29d314887..9ce653c654a 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp @@ -30,20 +30,20 @@ void test_string() sink(b); sink(c); // $ ast,ir sink(b.c_str()); - sink(c.c_str()); // $ ast MISSING: ir + sink(c.c_str()); // $ ast,ir } void test_strings2() { string path1 = user_input(); - sink(path1.c_str(), "r"); // $ ast MISSING: ir + sink(path1.c_str(), "r"); // $ ast,ir string path2; path2 = user_input(); - sink(path2.c_str(), "r"); // $ ast MISSING: ir + sink(path2.c_str(), "r"); // $ ast,ir string path3(user_input()); - sink(path3.c_str(), "r"); // $ ast MISSING: ir + sink(path3.c_str(), "r"); // $ ast,ir } void test_string3() @@ -67,7 +67,7 @@ void test_string4() // convert back std::string -> char * cs = ss.c_str(); - sink(cs); // $ ast MISSING: ir + sink(cs); // $ ast,ir sink(ss); // $ ast,ir } @@ -159,12 +159,12 @@ void test_string_append() { sink(s5); // $ ast,ir s6 = s3; - sink(s6 += s4); // $ ast MISSING: ir + sink(s6 += s4); // $ ast,ir sink(s6); // $ ast,ir s7 = s3; - sink(s7 += source()); // $ ast MISSING: ir - sink(s7 += " "); // $ ast MISSING: ir + sink(s7 += source()); // $ ast,ir + sink(s7 += " "); // $ ast,ir sink(s7); // $ ast,ir s8 = s3; @@ -196,10 +196,10 @@ void test_string_assign() { sink(s3.assign(s1)); sink(s3); - sink(s4.assign(s2)); // $ ast MISSING: ir + sink(s4.assign(s2)); // $ ast,ir sink(s4); // $ ast,ir - sink(s5.assign(10, c)); // $ ast MISSING: ir + sink(s5.assign(10, c)); // $ ast,ir sink(s5); // $ ast,ir sink(s6.assign(s1)); @@ -217,15 +217,15 @@ void test_string_insert() { sink(s3); s4 = s2; - sink(s4.insert(0, s1)); // $ ast MISSING: ir + sink(s4.insert(0, s1)); // $ ast,ir sink(s4); // $ ast,ir s5 = s1; - sink(s5.insert(0, s2)); // $ ast MISSING: ir + sink(s5.insert(0, s2)); // $ ast,ir sink(s5); // $ ast,ir s6 = s1; - sink(s6.insert(0, 10, c)); // $ ast MISSING: ir + sink(s6.insert(0, 10, c)); // $ ast,ir sink(s6); // $ ast,ir } @@ -240,15 +240,15 @@ void test_string_replace() { sink(s3); s4 = s2; - sink(s4.replace(1, 2, s1)); // $ ast MISSING: ir + sink(s4.replace(1, 2, s1)); // $ ast,ir sink(s4); // $ ast,ir s5 = s1; - sink(s5.replace(1, 2, s2)); // $ ast MISSING: ir + sink(s5.replace(1, 2, s2)); // $ ast,ir sink(s5); // $ ast,ir s6 = s1; - sink(s6.replace(1, 2, 10, c)); // $ ast MISSING: ir + sink(s6.replace(1, 2, 10, c)); // $ ast,ir sink(s6); // $ ast,ir } @@ -309,7 +309,7 @@ void test_string_data() std::string b(source()); sink(a.data()); - sink(b.data()); // $ ast MISSING: ir + sink(b.data()); // $ ast,ir sink(a.length()); sink(b.length()); } @@ -360,7 +360,7 @@ void test_string_iterators() { std::string s4("world"); sink(s1); - sink(s1.append(s2.begin(), s2.end())); // $ ast MISSING: ir + sink(s1.append(s2.begin(), s2.end())); // $ ast,ir sink(s1); // $ ast,ir sink(s3); @@ -433,7 +433,7 @@ void test_string_insert_more() sink(s1.insert(0, cs1)); sink(s1); - sink(s2.insert(0, cs2)); // $ ast MISSING: ir + sink(s2.insert(0, cs2)); // $ ast,ir sink(s2); // $ ast,ir } @@ -446,7 +446,7 @@ void test_string_iterator_methods() sink(a.insert(a.begin(), 10, 'x')); sink(a); - sink(b.insert(b.begin(), 10, ns_char::source())); // $ ast MISSING: ir + sink(b.insert(b.begin(), 10, ns_char::source())); // $ ast,ir sink(b); // $ ast,ir } @@ -459,10 +459,10 @@ void test_string_iterator_methods() sink(c.insert(c.end(), s1.begin(), s1.end())); sink(c); - sink(d.insert(d.end(), s2.begin(), s2.end())); // $ ast MISSING: ir + sink(d.insert(d.end(), s2.begin(), s2.end())); // $ ast,ir sink(d); // $ ast,ir - sink(s2.insert(s2.end(), s1.begin(), s1.end())); // $ ast MISSING: ir + sink(s2.insert(s2.end(), s1.begin(), s1.end())); // $ ast,ir sink(s2); // $ ast,ir } @@ -475,10 +475,10 @@ void test_string_iterator_methods() sink(e.append(s3.begin(), s3.end())); sink(e); - sink(f.append(s4.begin(), s4.end())); // $ ast MISSING: ir + sink(f.append(s4.begin(), s4.end())); // $ ast,ir sink(f); // $ ast,ir - sink(s4.append(s3.begin(), s3.end())); // $ ast MISSING: ir + sink(s4.append(s3.begin(), s3.end())); // $ ast,ir sink(s4); // $ ast,ir } @@ -491,7 +491,7 @@ void test_string_iterator_methods() sink(g.assign(s5.cbegin(), s5.cend())); sink(g); - sink(h.assign(s6.cbegin(), s6.cend())); // $ ast MISSING: ir + sink(h.assign(s6.cbegin(), s6.cend())); // $ ast,ir sink(h); // $ ast,ir sink(s6.assign(s5.cbegin(), s5.cend())); @@ -519,8 +519,8 @@ void test_string_front_back() { sink(a.front()); sink(a.back()); a.push_back(ns_char::source()); - sink(a.front()); // $ SPURIOUS: ast - sink(a.back()); // $ ast MISSING: ir + sink(a.front()); // $ SPURIOUS: ast,ir + sink(a.back()); // $ ast,ir } void test_string_return_assign() { @@ -533,12 +533,12 @@ void test_string_return_assign() { std::string f("ff"); sink( a += (b += "bb") ); - sink( c += (d += source()) ); // $ ast MISSING: ir - sink( (e += "ee") += source() ); // $ ast MISSING: ir - sink( (f += source()) += "ff" ); // $ ast MISSING: ir + sink( c += (d += source()) ); // $ ast,ir + sink( (e += "ee") += source() ); // $ ast,ir + sink( (f += source()) += "ff" ); // $ ast,ir sink(a); sink(b); - sink(c); // $ ast MISSING: ir + sink(c); // $ ast,ir sink(d); // $ ast,ir sink(e); // $ ast MISSING: ir sink(f); // $ ast,ir @@ -553,12 +553,12 @@ void test_string_return_assign() { std::string f("ff"); sink( a.assign(b.assign("bb")) ); - sink( c.assign(d.assign(source())) ); // $ ast MISSING: ir - sink( e.assign("ee").assign(source()) ); // $ ast MISSING: ir + sink( c.assign(d.assign(source())) ); // $ ast,ir + sink( e.assign("ee").assign(source()) ); // $ ast,ir sink( f.assign(source()).assign("ff") ); sink(a); sink(b); - sink(c); // $ ast MISSING: ir + sink(c); // $ ast,ir sink(d); // $ ast,ir sink(e); // $ ast MISSING: ir sink(f); // $ SPURIOUS: ast,ir diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp index fab96fc878d..249c8ac21bf 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp @@ -53,15 +53,15 @@ void test_stringstream_string(int amount) sink(ss7); // $ SPURIOUS: ast,ir sink(ss8.put('a')); - sink(ss9.put(ns_char::source())); // $ ast MISSING: ir - sink(ss10.put('a').put(ns_char::source()).put('z')); // $ ast MISSING: ir + sink(ss9.put(ns_char::source())); // $ ast,ir + sink(ss10.put('a').put(ns_char::source()).put('z')); // $ ast,ir sink(ss8); sink(ss9); // $ ast,ir sink(ss10); // $ ast MISSING: ir sink(ss11.write("begin", 5)); - sink(ss12.write(source(), 5)); // $ ast MISSING: ir - sink(ss13.write("begin", 5).write(source(), amount).write("end", 3)); // $ ast MISSING: ir + sink(ss12.write(source(), 5)); // $ ast,ir + sink(ss13.write("begin", 5).write(source(), amount).write("end", 3)); // $ ast,ir sink(ss11); sink(ss12); // $ ast,ir sink(ss13); // $ ast MISSING: ir @@ -73,7 +73,7 @@ void test_stringstream_int(int source) int v1 = 0, v2 = 0; sink(ss1 << 1234); - sink(ss2 << source); // $ ast MISSING: ir + sink(ss2 << source); // $ ast,ir sink(ss1 >> v1); sink(ss2 >> v2); // $ ast,ir @@ -97,7 +97,7 @@ void test_stringstream_constructors() std::stringstream ss6; sink(ss5 = std::stringstream("abc")); - sink(ss6 = std::stringstream(source())); // $ ast MISSING: ir + sink(ss6 = std::stringstream(source())); // $ ast,ir sink(ss1); sink(ss2); // $ ast,ir @@ -193,7 +193,7 @@ void test_stringstream_putback() sink(ss.get()); sink(ss.putback('b')); sink(ss.get()); - sink(ss.putback(ns_char::source())); // $ ast MISSING: ir + sink(ss.putback(ns_char::source())); // $ ast,ir sink(ss.get()); // $ ast,ir } @@ -263,6 +263,6 @@ void test_chaining() sink(b1); // $ ast,ir sink(b2); // $ ast,ir - sink(ss2.write("abc", 3).flush().write(source(), 3).flush().write("xyz", 3)); // $ ast MISSING: ir + sink(ss2.write("abc", 3).flush().write(source(), 3).flush().write("xyz", 3)); // $ ast,ir sink(ss2); // $ ast MISSING: ir } diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp index 9434dbe52ae..92a44f2950c 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp @@ -466,7 +466,7 @@ void test_qualifiers() sink(d.getString()); d.setString(strings::source()); sink(d); // $ ast,ir - sink(d.getString()); // $ ast MISSING: ir + sink(d.getString()); // $ ast,ir } // --- non-standard swap --- diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp index 4f0c8fab414..aacef1f4a5b 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp @@ -68,8 +68,8 @@ void test_element_taint(int x) { v5.push_back(source()); sink(v5); // $ ast,ir - sink(v5.front()); // $ SPURIOUS: ast - sink(v5.back()); // $ ast MISSING: ir + sink(v5.front()); // $ SPURIOUS: ast,ir + sink(v5.back()); // $ ast,ir v6.data()[2] = source(); sink(v6); // $ ast MISSING: ir @@ -81,8 +81,8 @@ void test_element_taint(int x) { v7.insert(it, source()); } sink(v7); // $ ast,ir - sink(v7.front()); // $ ast MISSING: ir - sink(v7.back()); // $ SPURIOUS: ast + sink(v7.front()); // $ ast,ir + sink(v7.back()); // $ SPURIOUS: ast,ir { const std::vector &v8c = v8; @@ -283,8 +283,8 @@ void test_data_more() { v1.push_back(source()); sink(v1); // $ ast,ir - sink(v1.data()); // $ ast MISSING: ir - sink(v1.data()[2]); // $ ast MISSING: ir + sink(v1.data()); // $ ast,ir + sink(v1.data()[2]); // $ ast,ir *(v2.data()) = ns_int::source(); sink(v2); // $ ast MISSING: ir @@ -305,10 +305,10 @@ void test_vector_insert() { sink(a.insert(a.end(), b.begin(), b.end())); sink(a); - sink(c.insert(c.end(), d.begin(), d.end())); // $ ast MISSING: ir + sink(c.insert(c.end(), d.begin(), d.end())); // $ ast,ir sink(c); // $ ast,ir - sink(d.insert(d.end(), a.begin(), a.end())); // $ ast MISSING: ir + sink(d.insert(d.end(), a.begin(), a.end())); // $ ast,ir sink(d); // $ ast,ir } diff --git a/cpp/ql/test/library-tests/ir/points_to/points_to.ql b/cpp/ql/test/library-tests/ir/points_to/points_to.ql index da090c5552e..b639a235ee8 100644 --- a/cpp/ql/test/library-tests/ir/points_to/points_to.ql +++ b/cpp/ql/test/library-tests/ir/points_to/points_to.ql @@ -3,12 +3,7 @@ private import TestUtilities.InlineExpectationsTest private import semmle.code.cpp.ir.internal.IntegerConstant as Ints private predicate ignoreAllocation(string name) { - name = "i" or - name = "p" or - name = "q" or - name = "s" or - name = "t" or - name = "?{AllAliased}" + name = ["i", "p", "q", "s", "t", "?{AllAliased}"] } private predicate ignoreFile(File file) { diff --git a/cpp/ql/test/query-tests/Critical/OverflowStatic/OverflowStatic.expected b/cpp/ql/test/query-tests/Critical/OverflowStatic/OverflowStatic.expected index 9ecfb2303db..0e5bbee7d73 100644 --- a/cpp/ql/test/query-tests/Critical/OverflowStatic/OverflowStatic.expected +++ b/cpp/ql/test/query-tests/Critical/OverflowStatic/OverflowStatic.expected @@ -5,14 +5,10 @@ | test2.c:33:26:33:27 | 46 | Potential buffer-overflow: 'buffer' has size 40 not 46. | | test2.c:34:22:34:23 | 47 | Potential buffer-overflow: 'buffer' has size 40 not 47. | | test2.c:35:23:35:24 | 48 | Potential buffer-overflow: 'buffer' has size 40 not 48. | -| test.c:14:9:14:13 | access to array | Potential buffer-overflow: 'xs' has size 5 but 'xs[5]' is accessed here. | -| test.c:15:9:15:13 | access to array | Potential buffer-overflow: 'xs' has size 5 but 'xs[6]' is accessed here. | -| test.c:20:9:20:18 | access to array | Potential buffer-overflow: 'ys' has size 5 but 'ys[5]' is accessed here. | -| test.c:21:9:21:18 | access to array | Potential buffer-overflow: 'ys' has size 5 but 'ys[6]' is accessed here. | -| test.c:47:3:47:18 | access to array | Potential buffer-overflow: 'ptr' has size 8 but 'ptr[8]' is accessed here. | -| test.c:54:3:54:26 | access to array | Potential buffer-overflow: 'ptr' has size 8 but 'ptr[8]' is accessed here. | -| test.c:61:3:61:18 | access to array | Potential buffer-overflow: 'ptr' has size 8 but 'ptr[8]' is accessed here. | -| test.c:72:3:72:11 | access to array | Potential buffer-overflow: 'buf' has size 1 but 'buf[1]' is accessed here. | +| test.c:14:9:14:13 | access to array | Potential buffer-overflow: 'xs' has size 5 but 'xs[5]' may be accessed here. | +| test.c:15:9:15:13 | access to array | Potential buffer-overflow: 'xs' has size 5 but 'xs[6]' may be accessed here. | +| test.c:20:9:20:18 | access to array | Potential buffer-overflow: 'ys' has size 5 but 'ys[5]' may be accessed here. | +| test.c:21:9:21:18 | access to array | Potential buffer-overflow: 'ys' has size 5 but 'ys[6]' may be accessed here. | | test.cpp:19:3:19:12 | access to array | Potential buffer-overflow: counter 'i' <= 3 but 'buffer1' has 3 elements. | | test.cpp:20:3:20:12 | access to array | Potential buffer-overflow: counter 'i' <= 3 but 'buffer2' has 3 elements. | | test.cpp:24:27:24:27 | 4 | Potential buffer-overflow: 'buffer1' has size 3 not 4. | diff --git a/cpp/ql/test/query-tests/Critical/OverflowStatic/test.c b/cpp/ql/test/query-tests/Critical/OverflowStatic/test.c index 4da951a3139..3c726a452b9 100644 --- a/cpp/ql/test/query-tests/Critical/OverflowStatic/test.c +++ b/cpp/ql/test/query-tests/Critical/OverflowStatic/test.c @@ -44,21 +44,21 @@ void union_test() { union u u; u.ptr[0] = 0; // GOOD u.ptr[sizeof(u)-1] = 0; // GOOD - u.ptr[sizeof(u)] = 0; // BAD + u.ptr[sizeof(u)] = 0; // BAD [NOT DETECTED] } void test_struct_union() { struct { union u u; } v; v.u.ptr[0] = 0; // GOOD v.u.ptr[sizeof(union u)-1] = 0; // GOOD - v.u.ptr[sizeof(union u)] = 0; // BAD + v.u.ptr[sizeof(union u)] = 0; // BAD [NOT DETECTED] } void union_test2() { union { char ptr[1]; unsigned long value; } u; u.ptr[0] = 0; // GOOD u.ptr[sizeof(u)-1] = 0; // GOOD - u.ptr[sizeof(u)] = 0; // BAD + u.ptr[sizeof(u)] = 0; // BAD [NOT DETECTED] } typedef struct { @@ -69,5 +69,5 @@ typedef struct { void test_alloc() { // Special case of taking sizeof without any addition or multiplications var_buf *b = malloc(sizeof(var_buf)); - b->buf[1] = 0; // BAD + b->buf[1] = 0; // BAD [NOT DETECTED] } diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/ImproperNullTermination.expected b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/ImproperNullTermination.expected index c83205ef49f..19edbd28c6a 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/ImproperNullTermination.expected +++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/ImproperNullTermination.expected @@ -1,27 +1,29 @@ -| test.cpp:25:10:25:16 | buffer1 | Variable $@ may not be null terminated. | test.cpp:22:8:22:14 | buffer1 | buffer1 | -| test.cpp:26:10:26:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:23:8:23:14 | buffer2 | buffer2 | -| test.cpp:39:10:39:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:35:8:35:14 | buffer2 | buffer2 | -| test.cpp:59:10:59:13 | ptr1 | Variable $@ may not be null terminated. | test.cpp:56:9:56:12 | ptr1 | ptr1 | -| test.cpp:69:10:69:16 | buffer1 | Variable $@ may not be null terminated. | test.cpp:64:8:64:14 | buffer1 | buffer1 | -| test.cpp:70:10:70:12 | ptr | Variable $@ may not be null terminated. | test.cpp:64:8:64:14 | buffer1 | buffer1 | -| test.cpp:81:10:81:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:65:8:65:14 | buffer2 | buffer2 | -| test.cpp:82:10:82:12 | ptr | Variable $@ may not be null terminated. | test.cpp:65:8:65:14 | buffer2 | buffer2 | -| test.cpp:93:10:93:15 | buffer | Variable $@ may not be null terminated. | test.cpp:86:8:86:13 | buffer | buffer | -| test.cpp:116:10:116:15 | buffer | Variable $@ may not be null terminated. | test.cpp:109:8:109:13 | buffer | buffer | -| test.cpp:130:14:130:19 | buffer | Variable $@ may not be null terminated. | test.cpp:127:7:127:12 | buffer | buffer | -| test.cpp:139:10:139:15 | buffer | Variable $@ may not be null terminated. | test.cpp:136:8:136:13 | buffer | buffer | -| test.cpp:147:14:147:19 | buffer | Variable $@ may not be null terminated. | test.cpp:143:8:143:13 | buffer | buffer | -| test.cpp:182:10:182:15 | buffer | Variable $@ may not be null terminated. | test.cpp:178:8:178:13 | buffer | buffer | -| test.cpp:234:10:234:15 | buffer | Variable $@ may not be null terminated. | test.cpp:232:8:232:13 | buffer | buffer | -| test.cpp:262:10:262:15 | buffer | Variable $@ may not be null terminated. | test.cpp:259:8:259:13 | buffer | buffer | -| test.cpp:283:10:283:15 | buffer | Variable $@ may not be null terminated. | test.cpp:280:8:280:13 | buffer | buffer | -| test.cpp:300:10:300:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:295:8:295:14 | buffer2 | buffer2 | -| test.cpp:312:10:312:15 | buffer | Variable $@ may not be null terminated. | test.cpp:308:8:308:13 | buffer | buffer | -| test.cpp:327:18:327:23 | buffer | Variable $@ may not be null terminated. | test.cpp:326:8:326:13 | buffer | buffer | -| test.cpp:346:11:346:16 | buffer | Variable $@ may not be null terminated. | test.cpp:341:8:341:13 | buffer | buffer | +| test.cpp:26:10:26:16 | buffer1 | Variable $@ may not be null terminated. | test.cpp:23:8:23:14 | buffer1 | buffer1 | +| test.cpp:27:10:27:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:24:8:24:14 | buffer2 | buffer2 | +| test.cpp:40:10:40:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:36:8:36:14 | buffer2 | buffer2 | +| test.cpp:60:10:60:13 | ptr1 | Variable $@ may not be null terminated. | test.cpp:57:9:57:12 | ptr1 | ptr1 | +| test.cpp:70:10:70:16 | buffer1 | Variable $@ may not be null terminated. | test.cpp:65:8:65:14 | buffer1 | buffer1 | +| test.cpp:71:10:71:12 | ptr | Variable $@ may not be null terminated. | test.cpp:65:8:65:14 | buffer1 | buffer1 | +| test.cpp:82:10:82:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:66:8:66:14 | buffer2 | buffer2 | +| test.cpp:83:10:83:12 | ptr | Variable $@ may not be null terminated. | test.cpp:66:8:66:14 | buffer2 | buffer2 | +| test.cpp:94:10:94:15 | buffer | Variable $@ may not be null terminated. | test.cpp:87:8:87:13 | buffer | buffer | +| test.cpp:117:10:117:15 | buffer | Variable $@ may not be null terminated. | test.cpp:110:8:110:13 | buffer | buffer | +| test.cpp:131:14:131:19 | buffer | Variable $@ may not be null terminated. | test.cpp:128:7:128:12 | buffer | buffer | +| test.cpp:140:10:140:15 | buffer | Variable $@ may not be null terminated. | test.cpp:137:8:137:13 | buffer | buffer | +| test.cpp:148:14:148:19 | buffer | Variable $@ may not be null terminated. | test.cpp:144:8:144:13 | buffer | buffer | +| test.cpp:183:10:183:15 | buffer | Variable $@ may not be null terminated. | test.cpp:179:8:179:13 | buffer | buffer | +| test.cpp:236:10:236:15 | buffer | Variable $@ may not be null terminated. | test.cpp:234:8:234:13 | buffer | buffer | +| test.cpp:264:10:264:15 | buffer | Variable $@ may not be null terminated. | test.cpp:261:8:261:13 | buffer | buffer | +| test.cpp:285:10:285:15 | buffer | Variable $@ may not be null terminated. | test.cpp:282:8:282:13 | buffer | buffer | +| test.cpp:302:10:302:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:297:8:297:14 | buffer2 | buffer2 | +| test.cpp:314:10:314:15 | buffer | Variable $@ may not be null terminated. | test.cpp:310:8:310:13 | buffer | buffer | +| test.cpp:336:18:336:23 | buffer | Variable $@ may not be null terminated. | test.cpp:335:8:335:13 | buffer | buffer | | test.cpp:355:11:355:16 | buffer | Variable $@ may not be null terminated. | test.cpp:350:8:350:13 | buffer | buffer | -| test.cpp:365:19:365:25 | buffer2 | Variable $@ may not be null terminated. | test.cpp:363:8:363:14 | buffer2 | buffer2 | -| test.cpp:392:17:392:22 | buffer | Variable $@ may not be null terminated. | test.cpp:390:8:390:13 | buffer | buffer | -| test.cpp:398:18:398:23 | buffer | Variable $@ may not be null terminated. | test.cpp:396:8:396:13 | buffer | buffer | -| test.cpp:444:10:444:15 | buffer | Variable $@ may not be null terminated. | test.cpp:442:8:442:13 | buffer | buffer | -| test.cpp:450:16:450:21 | buffer | Variable $@ may not be null terminated. | test.cpp:448:8:448:13 | buffer | buffer | +| test.cpp:364:11:364:16 | buffer | Variable $@ may not be null terminated. | test.cpp:359:8:359:13 | buffer | buffer | +| test.cpp:392:11:392:16 | buffer | Variable $@ may not be null terminated. | test.cpp:381:8:381:13 | buffer | buffer | +| test.cpp:410:11:410:16 | buffer | Variable $@ may not be null terminated. | test.cpp:397:8:397:13 | buffer | buffer | +| test.cpp:421:19:421:25 | buffer2 | Variable $@ may not be null terminated. | test.cpp:419:8:419:14 | buffer2 | buffer2 | +| test.cpp:448:17:448:22 | buffer | Variable $@ may not be null terminated. | test.cpp:446:8:446:13 | buffer | buffer | +| test.cpp:454:18:454:23 | buffer | Variable $@ may not be null terminated. | test.cpp:452:8:452:13 | buffer | buffer | +| test.cpp:513:10:513:15 | buffer | Variable $@ may not be null terminated. | test.cpp:511:8:511:13 | buffer | buffer | +| test.cpp:519:16:519:21 | buffer | Variable $@ may not be null terminated. | test.cpp:517:8:517:13 | buffer | buffer | diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/ImproperNullTerminationTainted.expected b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/ImproperNullTerminationTainted.expected index 3bf8c4d3652..b0a0d6cbb66 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/ImproperNullTerminationTainted.expected +++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/ImproperNullTerminationTainted.expected @@ -1,2 +1,2 @@ -| test.cpp:410:10:410:15 | buffer | $@ flows to here and may not be null terminated. | test.cpp:409:18:409:23 | buffer | User-provided value | -| test.cpp:425:10:425:15 | buffer | $@ flows to here and may not be null terminated. | test.cpp:424:9:424:14 | buffer | User-provided value | +| test.cpp:466:10:466:15 | buffer | $@ flows to here and may not be null terminated. | test.cpp:465:18:465:23 | buffer | User-provided value | +| test.cpp:481:10:481:15 | buffer | $@ flows to here and may not be null terminated. | test.cpp:480:9:480:14 | buffer | User-provided value | diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/test.cpp b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/test.cpp index 45410c15ebf..9adf409af4a 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/test.cpp +++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/test.cpp @@ -6,6 +6,7 @@ size_t strlen(const char *s); char *strcpy(char *s1, const char *s2); char *strcat(char *s1, const char *s2); char *strdup(const char *s1); +long int strtol(const char* nptr, char** endptr, int base); void *malloc(size_t size); void *memset(void *s, int c, size_t n); void *memcpy(void *s1, const void *s2, size_t n); @@ -225,6 +226,7 @@ void test_readlink(int fd, const char *path, size_t sz) void doNothing(char *data) { }; void doNothing2(const char *data); void clearBuffer(char *data, size_t size); +char *id(char *data) { return data; } void test_strcat() { @@ -318,6 +320,13 @@ void test_strcat() clearBuffer(buffer, 1024); strcat(buffer, "content"); // GOOD } + + { + char buffer[1024]; + + clearBuffer(id(buffer), 1024); + strcat(buffer, "content"); // GOOD + } } void test_strlen(bool cond1, bool cond2) @@ -354,6 +363,53 @@ void test_strlen(bool cond1, bool cond2) if (cond2) strlen(buffer); // BAD } + + { + char buffer[1024]; + + if (cond1) + { + buffer[0] = 0; + } else { + buffer[0] = 0; + } + + strlen(buffer); // GOOD + } + + { + char buffer[1024]; + int init = 0; + + if (cond1) + { + buffer[0] = 0; + init = 1; + } + + if (init != 0) + { + strlen(buffer); // GOOD [FALSE POSITIVE] + } + } + + { + char buffer[1024]; + int init = 0; + + if (cond1) + { + buffer[0] = 0; + init = 1; + } + + if (init == 0) + { + // ... + } else { + strlen(buffer); // GOOD [FALSE POSITIVE] + } + } } void test_strcpy() @@ -434,6 +490,19 @@ void test_read_fread(int read_src, FILE *s) } } +void test_strtol() +{ + { + char buffer[100]; + char *after_ptr; + long int num; + + strcpy(buffer, "123abc"); + num = strtol("123abc", &after_ptr, 10); + strlen(after_ptr); // GOOD + } +} + int printf(const char *format, ...); void test_printf(char *str) @@ -466,3 +535,4 @@ void test_printf(char *str) printf("%s", copied_str); // GOOD } } + diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-078/SAMATE/ExecTainted/ExecTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-078/SAMATE/ExecTainted/ExecTainted.expected index 6f011708465..51c8906abb5 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-078/SAMATE/ExecTainted/ExecTainted.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-078/SAMATE/ExecTainted/ExecTainted.expected @@ -1 +1,17 @@ -| tests.cpp:53:16:53:19 | data | This argument to an OS command is derived from $@ and then passed to system(string) | tests.cpp:33:34:33:39 | call to getenv | user input (getenv) | +edges +| tests.cpp:33:34:33:39 | call to getenv | tests.cpp:38:39:38:49 | environment indirection | +| tests.cpp:38:25:38:36 | strncat output argument | tests.cpp:42:5:42:16 | Phi | +| tests.cpp:38:39:38:49 | environment indirection | tests.cpp:38:25:38:36 | strncat output argument | +| tests.cpp:38:39:38:49 | environment indirection | tests.cpp:38:25:38:36 | strncat output argument | +| tests.cpp:42:5:42:16 | Phi | tests.cpp:51:22:51:25 | badSource output argument | +| tests.cpp:51:22:51:25 | badSource output argument | tests.cpp:53:16:53:19 | data indirection | +nodes +| tests.cpp:33:34:33:39 | call to getenv | semmle.label | call to getenv | +| tests.cpp:38:25:38:36 | strncat output argument | semmle.label | strncat output argument | +| tests.cpp:38:39:38:49 | environment indirection | semmle.label | environment indirection | +| tests.cpp:42:5:42:16 | Phi | semmle.label | Phi | +| tests.cpp:51:22:51:25 | badSource output argument | semmle.label | badSource output argument | +| tests.cpp:53:16:53:19 | data indirection | semmle.label | data indirection | +subpaths +#select +| tests.cpp:53:16:53:19 | data | tests.cpp:33:34:33:39 | call to getenv | tests.cpp:53:16:53:19 | data indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string) | tests.cpp:33:34:33:39 | call to getenv | user input (an environment variable) | tests.cpp:38:25:38:36 | strncat output argument | strncat output argument | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/ExecTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/ExecTainted.expected index a78a7a99d90..4b1b5a61ace 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/ExecTainted.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/ExecTainted.expected @@ -1 +1,85 @@ -| test.c:21:12:21:19 | command1 | This argument to an OS command is derived from $@ and then passed to system(string) | test.c:14:20:14:23 | argv | user input (argv) | +edges +| test.cpp:16:20:16:23 | argv | test.cpp:22:45:22:52 | userName indirection | +| test.cpp:22:13:22:20 | sprintf output argument | test.cpp:23:12:23:19 | command1 indirection | +| test.cpp:22:45:22:52 | userName indirection | test.cpp:22:13:22:20 | sprintf output argument | +| test.cpp:22:45:22:52 | userName indirection | test.cpp:22:13:22:20 | sprintf output argument | +| test.cpp:47:21:47:26 | call to getenv | test.cpp:50:35:50:43 | envCflags indirection | +| test.cpp:50:11:50:17 | sprintf output argument | test.cpp:51:10:51:16 | command indirection | +| test.cpp:50:35:50:43 | envCflags indirection | test.cpp:50:11:50:17 | sprintf output argument | +| test.cpp:50:35:50:43 | envCflags indirection | test.cpp:50:11:50:17 | sprintf output argument | +| test.cpp:62:9:62:16 | fread output argument | test.cpp:64:20:64:27 | filename indirection | +| test.cpp:64:11:64:17 | strncat output argument | test.cpp:65:10:65:16 | command indirection | +| test.cpp:64:20:64:27 | filename indirection | test.cpp:64:11:64:17 | strncat output argument | +| test.cpp:64:20:64:27 | filename indirection | test.cpp:64:11:64:17 | strncat output argument | +| test.cpp:82:9:82:16 | fread output argument | test.cpp:84:20:84:27 | filename indirection | +| test.cpp:84:11:84:17 | strncat output argument | test.cpp:85:32:85:38 | command indirection | +| test.cpp:84:20:84:27 | filename indirection | test.cpp:84:11:84:17 | strncat output argument | +| test.cpp:84:20:84:27 | filename indirection | test.cpp:84:11:84:17 | strncat output argument | +| test.cpp:91:9:91:16 | fread output argument | test.cpp:93:17:93:24 | filename indirection | +| test.cpp:93:11:93:14 | strncat output argument | test.cpp:94:45:94:48 | path indirection | +| test.cpp:93:17:93:24 | filename indirection | test.cpp:93:11:93:14 | strncat output argument | +| test.cpp:93:17:93:24 | filename indirection | test.cpp:93:11:93:14 | strncat output argument | +| test.cpp:106:20:106:25 | call to getenv | test.cpp:107:33:107:36 | path indirection | +| test.cpp:107:31:107:31 | call to operator+ | test.cpp:108:18:108:22 | call to c_str indirection | +| test.cpp:107:33:107:36 | path indirection | test.cpp:107:31:107:31 | call to operator+ | +| test.cpp:107:33:107:36 | path indirection | test.cpp:107:31:107:31 | call to operator+ | +| test.cpp:113:20:113:25 | call to getenv | test.cpp:114:19:114:22 | path indirection | +| test.cpp:114:17:114:17 | Call | test.cpp:114:25:114:29 | call to c_str indirection | +| test.cpp:114:19:114:22 | path indirection | test.cpp:114:17:114:17 | Call | +| test.cpp:114:19:114:22 | path indirection | test.cpp:114:17:114:17 | Call | +| test.cpp:119:20:119:25 | call to getenv | test.cpp:120:19:120:22 | path indirection | +| test.cpp:120:17:120:17 | Call | test.cpp:120:10:120:30 | call to data indirection | +| test.cpp:120:19:120:22 | path indirection | test.cpp:120:17:120:17 | Call | +| test.cpp:120:19:120:22 | path indirection | test.cpp:120:17:120:17 | Call | +| test.cpp:140:9:140:11 | fread output argument | test.cpp:142:31:142:33 | str indirection | +| test.cpp:142:11:142:17 | sprintf output argument | test.cpp:143:10:143:16 | command indirection | +| test.cpp:142:31:142:33 | str indirection | test.cpp:142:11:142:17 | sprintf output argument | +| test.cpp:142:31:142:33 | str indirection | test.cpp:142:11:142:17 | sprintf output argument | +nodes +| test.cpp:16:20:16:23 | argv | semmle.label | argv | +| test.cpp:22:13:22:20 | sprintf output argument | semmle.label | sprintf output argument | +| test.cpp:22:45:22:52 | userName indirection | semmle.label | userName indirection | +| test.cpp:23:12:23:19 | command1 indirection | semmle.label | command1 indirection | +| test.cpp:47:21:47:26 | call to getenv | semmle.label | call to getenv | +| test.cpp:50:11:50:17 | sprintf output argument | semmle.label | sprintf output argument | +| test.cpp:50:35:50:43 | envCflags indirection | semmle.label | envCflags indirection | +| test.cpp:51:10:51:16 | command indirection | semmle.label | command indirection | +| test.cpp:62:9:62:16 | fread output argument | semmle.label | fread output argument | +| test.cpp:64:11:64:17 | strncat output argument | semmle.label | strncat output argument | +| test.cpp:64:20:64:27 | filename indirection | semmle.label | filename indirection | +| test.cpp:65:10:65:16 | command indirection | semmle.label | command indirection | +| test.cpp:82:9:82:16 | fread output argument | semmle.label | fread output argument | +| test.cpp:84:11:84:17 | strncat output argument | semmle.label | strncat output argument | +| test.cpp:84:20:84:27 | filename indirection | semmle.label | filename indirection | +| test.cpp:85:32:85:38 | command indirection | semmle.label | command indirection | +| test.cpp:91:9:91:16 | fread output argument | semmle.label | fread output argument | +| test.cpp:93:11:93:14 | strncat output argument | semmle.label | strncat output argument | +| test.cpp:93:17:93:24 | filename indirection | semmle.label | filename indirection | +| test.cpp:94:45:94:48 | path indirection | semmle.label | path indirection | +| test.cpp:106:20:106:25 | call to getenv | semmle.label | call to getenv | +| test.cpp:107:31:107:31 | call to operator+ | semmle.label | call to operator+ | +| test.cpp:107:33:107:36 | path indirection | semmle.label | path indirection | +| test.cpp:108:18:108:22 | call to c_str indirection | semmle.label | call to c_str indirection | +| test.cpp:113:20:113:25 | call to getenv | semmle.label | call to getenv | +| test.cpp:114:17:114:17 | Call | semmle.label | Call | +| test.cpp:114:19:114:22 | path indirection | semmle.label | path indirection | +| test.cpp:114:25:114:29 | call to c_str indirection | semmle.label | call to c_str indirection | +| test.cpp:119:20:119:25 | call to getenv | semmle.label | call to getenv | +| test.cpp:120:10:120:30 | call to data indirection | semmle.label | call to data indirection | +| test.cpp:120:17:120:17 | Call | semmle.label | Call | +| test.cpp:120:19:120:22 | path indirection | semmle.label | path indirection | +| test.cpp:140:9:140:11 | fread output argument | semmle.label | fread output argument | +| test.cpp:142:11:142:17 | sprintf output argument | semmle.label | sprintf output argument | +| test.cpp:142:31:142:33 | str indirection | semmle.label | str indirection | +| test.cpp:143:10:143:16 | command indirection | semmle.label | command indirection | +subpaths +#select +| test.cpp:23:12:23:19 | command1 | test.cpp:16:20:16:23 | argv | test.cpp:23:12:23:19 | command1 indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string) | test.cpp:16:20:16:23 | argv | user input (a command-line argument) | test.cpp:22:13:22:20 | sprintf output argument | sprintf output argument | +| test.cpp:51:10:51:16 | command | test.cpp:47:21:47:26 | call to getenv | test.cpp:51:10:51:16 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string) | test.cpp:47:21:47:26 | call to getenv | user input (an environment variable) | test.cpp:50:11:50:17 | sprintf output argument | sprintf output argument | +| test.cpp:65:10:65:16 | command | test.cpp:62:9:62:16 | fread output argument | test.cpp:65:10:65:16 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string) | test.cpp:62:9:62:16 | fread output argument | user input (String read by fread) | test.cpp:64:11:64:17 | strncat output argument | strncat output argument | +| test.cpp:85:32:85:38 | command | test.cpp:82:9:82:16 | fread output argument | test.cpp:85:32:85:38 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl | test.cpp:82:9:82:16 | fread output argument | user input (String read by fread) | test.cpp:84:11:84:17 | strncat output argument | strncat output argument | +| test.cpp:94:45:94:48 | path | test.cpp:91:9:91:16 | fread output argument | test.cpp:94:45:94:48 | path indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl | test.cpp:91:9:91:16 | fread output argument | user input (String read by fread) | test.cpp:93:11:93:14 | strncat output argument | strncat output argument | +| test.cpp:108:18:108:22 | call to c_str | test.cpp:106:20:106:25 | call to getenv | test.cpp:108:18:108:22 | call to c_str indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string) | test.cpp:106:20:106:25 | call to getenv | user input (an environment variable) | test.cpp:107:31:107:31 | call to operator+ | call to operator+ | +| test.cpp:114:25:114:29 | call to c_str | test.cpp:113:20:113:25 | call to getenv | test.cpp:114:25:114:29 | call to c_str indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string) | test.cpp:113:20:113:25 | call to getenv | user input (an environment variable) | test.cpp:114:17:114:17 | Call | Call | +| test.cpp:120:25:120:28 | call to data | test.cpp:119:20:119:25 | call to getenv | test.cpp:120:10:120:30 | call to data indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string) | test.cpp:119:20:119:25 | call to getenv | user input (an environment variable) | test.cpp:120:17:120:17 | Call | Call | +| test.cpp:143:10:143:16 | command | test.cpp:140:9:140:11 | fread output argument | test.cpp:143:10:143:16 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string) | test.cpp:140:9:140:11 | fread output argument | user input (String read by fread) | test.cpp:142:11:142:17 | sprintf output argument | sprintf output argument | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/test.c b/cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/test.c deleted file mode 100644 index c3155787368..00000000000 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/test.c +++ /dev/null @@ -1,33 +0,0 @@ -// Semmle test case for rule ExecTainted.ql (Uncontrolled data used in OS command) -// Associated with CWE-078: OS Command Injection. http://cwe.mitre.org/data/definitions/78.html - -///// Library routines ///// - -int sprintf(char *s, const char *format, ...); -int system(const char *string); - -extern void encodeShellString(char *shellStr, int maxChars, const char* cStr); - -///// Test code ///// - -int main(int argc, char** argv) { - char *userName = argv[2]; - - { - // BAD: a string from the user is injected directly into - // a command. - char command1[1000] = {0}; - sprintf(command1, "userinfo -v \"%s\"", userName); - system(command1); - } - - { - // GOOD: the user string is encoded by a library routine. - char userNameQuoted[1000] = {0}; - encodeShellString(userNameQuoted, 1000, userName); - char command2[1000] = {0}; - sprintf(command2, "userinfo -v %s", userNameQuoted); - system(command2); - } -} - diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/test.cpp new file mode 100644 index 00000000000..8407ffb68da --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/test.cpp @@ -0,0 +1,174 @@ +// Semmle test case for rule ExecTainted.ql (Uncontrolled data used in OS command) +// Associated with CWE-078: OS Command Injection. http://cwe.mitre.org/data/definitions/78.html + +///// Library routines ///// + +int sprintf(char *s, const char *format, ...); +int system(const char *string); + +char *getenv(char *var); + +extern void encodeShellString(char *shellStr, int maxChars, const char* cStr); +#include "../../../../../../include/string.h" +///// Test code ///// + +int main(int argc, char** argv) { + char *userName = argv[2]; + + { + // BAD: a string from the user is injected directly into + // a command. + char command1[1000] = {0}; + sprintf(command1, "userinfo -v \"%s\"", userName); + system(command1); + } + + { + // GOOD: the user string is encoded by a library routine. + char userNameQuoted[1000] = {0}; + encodeShellString(userNameQuoted, 1000, userName); + char command2[1000] = {0}; + sprintf(command2, "userinfo -v %s", userNameQuoted); + system(command2); + } +} + +void test2(char* arg2) { + // GOOD?: the user string is the *first* part of the command, like $CC in many environments + char *envCC = getenv("CC"); + + char command[1000]; + sprintf("%s %s", envCC, arg2); + system(command); +} + +void test3(char* arg1) { + // GOOD?: the user string is a `$CFLAGS` environment variable + char *envCflags = getenv("CFLAGS"); + + char command[1000]; + sprintf(command, "%s %s", arg1, envCflags); + system(command); +} + +typedef unsigned long size_t; +typedef void FILE; +size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); +char *strncat(char *s1, const char *s2, size_t n); + +void test4(FILE *f) { + // BAD: the user string is injected directly into a command + char command[1000] = "mv ", filename[1000]; + fread(filename, 1, 1000, f); + + strncat(command, filename, 1000); + system(command); +} + +void test5(FILE *f) { + // GOOD?: the user string is the start of a command + char command[1000], filename[1000] = " test.txt"; + fread(command, 1, 1000, f); + + strncat(command, filename, 1000); + system(command); +} + +int execl(char *path, char *arg1, ...); + +void test6(FILE *f) { + // BAD: the user string is injected directly into a command + char command[1000] = "mv ", filename[1000]; + fread(filename, 1, 1000, f); + + strncat(command, filename, 1000); + execl("/bin/sh", "sh", "-c", command); +} + +void test7(FILE *f) { + // GOOD [FALSE POSITIVE]: the user string is a positional argument to a shell script + char path[1000] = "/home/me/", filename[1000]; + fread(filename, 1, 1000, f); + + strncat(path, filename, 1000); + execl("/bin/sh", "sh", "-c", "script.sh", path); +} + +void test8(char *arg2) { + // GOOD?: the user string is the *first* part of the command, like $CC in many environments + std::string envCC(getenv("CC")); + std::string command = envCC + arg2; + system(command.c_str()); +} + +void test9(FILE *f) { + // BAD: the user string is injected directly into a command + std::string path(getenv("something")); + std::string command = "mv " + path; + system(command.c_str()); +} + +void test10(FILE *f) { + // BAD: the user string is injected directly into a command + std::string path(getenv("something")); + system(("mv " + path).c_str()); +} + +void test11(FILE *f) { + // BAD: the user string is injected directly into a command + std::string path(getenv("something")); + system(("mv " + path).data()); +} + +int atoi(char *); + +void test12(FILE *f) { + char temp[10]; + char command[1000]; + + fread(temp, 1, 10, f); + + int x = atoi(temp); + sprintf(command, "tail -n %d foo.log", x); + system(command); // GOOD: the user string was converted to an integer and back +} + +void test13(FILE *f) { + char str[1000]; + char command[1000]; + + fread(str, 1, 1000, f); + + sprintf(command, "echo %s", str); + system(command); // BAD: the user string was printed into the command with the %s specifier +} + +void test14(FILE *f) { + char str[1000]; + char command[1000]; + + fread(str, 1, 1000, f); + + sprintf(command, "echo %p", str); + system(command); // GOOD: the user string's address was printed into the command with the %p specifier +} + +void test15(FILE *f) { + char temp[10]; + char command[1000]; + + fread(temp, 1, 10, f); + + int x = atoi(temp); + + char temp2[10]; + sprintf(temp2, "%d", x); + sprintf(command, "tail -n %s foo.log", temp2); + + system(command); // GOOD: the user string was converted to an integer and back +} + + +// TODO: test for call context sensitivity at concatenation site + +// open question: do we want to report certain sources even when they're the start of the string? diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowBuffer.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowBuffer.expected index 77751b66d4e..795d83587c3 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowBuffer.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowBuffer.expected @@ -72,12 +72,9 @@ | unions.cpp:30:2:30:7 | call to memset | This 'memset' operation accesses 200 bytes but the $@ is only 100 bytes. | unions.cpp:15:7:15:11 | small | destination buffer | | unions.cpp:34:2:34:7 | call to memset | This 'memset' operation accesses 200 bytes but the $@ is only 100 bytes. | unions.cpp:16:7:16:11 | large | destination buffer | | unions.cpp:34:2:34:7 | call to memset | This 'memset' operation accesses 200 bytes but the $@ is only 100 bytes. | unions.cpp:34:14:34:18 | large | destination buffer | -| var_size_struct.cpp:54:5:54:14 | access to array | This array indexing operation accesses byte offset 1 but the $@ is only 1 byte. | var_size_struct.cpp:32:8:32:10 | str | array | -| var_size_struct.cpp:55:5:55:14 | access to array | This array indexing operation accesses byte offset 1 but the $@ is only 1 byte. | var_size_struct.cpp:38:8:38:10 | str | array | | var_size_struct.cpp:71:3:71:8 | call to memset | This 'memset' operation accesses 1025 bytes but the $@ is only 1024 bytes. | var_size_struct.cpp:63:8:63:11 | data | destination buffer | | var_size_struct.cpp:73:3:73:9 | call to strncpy | This 'strncpy' operation may access 1025 bytes but the $@ is only 1024 bytes. | var_size_struct.cpp:63:8:63:11 | data | destination buffer | | var_size_struct.cpp:87:3:87:19 | access to array | This array indexing operation accesses byte offset 67 but the $@ is only 64 bytes. | var_size_struct.cpp:78:7:78:14 | elements | array | | var_size_struct.cpp:99:3:99:8 | call to memset | This 'memset' operation accesses 129 bytes but the $@ is only 128 bytes. | var_size_struct.cpp:92:8:92:10 | str | destination buffer | | var_size_struct.cpp:101:3:101:8 | call to memset | This 'memset' operation accesses 129 bytes but the $@ is only 128 bytes. | var_size_struct.cpp:92:8:92:10 | str | destination buffer | | var_size_struct.cpp:103:3:103:9 | call to strncpy | This 'strncpy' operation may access 129 bytes but the $@ is only 128 bytes. | var_size_struct.cpp:92:8:92:10 | str | destination buffer | -| var_size_struct.cpp:169:3:169:8 | call to memset | This 'memset' operation accesses 100 bytes but the $@ is only 1 byte. | var_size_struct.cpp:125:17:125:19 | arr | destination buffer | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowStatic.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowStatic.expected index ef334a73a2c..ac44bbf028d 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowStatic.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowStatic.expected @@ -3,8 +3,6 @@ | tests.cpp:163:3:163:11 | access to array | Potential buffer-overflow: counter 'k' <= 100 but 'buffer' has 100 elements. | | tests.cpp:164:8:164:16 | access to array | Potential buffer-overflow: counter 'k' <= 100 but 'buffer' has 100 elements. | | tests.cpp:245:42:245:42 | 6 | Potential buffer-overflow: 'global_array_5' has size 5 not 6. | -| tests.cpp:349:2:349:14 | access to array | Potential buffer-overflow: 'charArray' has size 10 but 'charArray[10]' is accessed here. | -| tests.cpp:350:17:350:29 | access to array | Potential buffer-overflow: 'charArray' has size 10 but 'charArray[10]' is accessed here. | -| var_size_struct.cpp:54:5:54:14 | access to array | Potential buffer-overflow: 'str' has size 1 but 'str[1]' is accessed here. | -| var_size_struct.cpp:55:5:55:14 | access to array | Potential buffer-overflow: 'str' has size 1 but 'str[1]' is accessed here. | +| tests.cpp:349:2:349:14 | access to array | Potential buffer-overflow: 'charArray' has size 10 but 'charArray[10]' may be accessed here. | +| tests.cpp:350:17:350:29 | access to array | Potential buffer-overflow: 'charArray' has size 10 but 'charArray[10]' may be accessed here. | | var_size_struct.cpp:103:39:103:41 | 129 | Potential buffer-overflow: 'str' has size 128 not 129. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/var_size_struct.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/var_size_struct.cpp index c006c35fe9b..a514135f348 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/var_size_struct.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/var_size_struct.cpp @@ -51,8 +51,8 @@ void testVarString(int n) { s1->str[1] = '?'; // GOOD s2->str[1] = '?'; // GOOD s3->str[1] = '?'; // GOOD - s4->str[1] = '?'; // BAD - s5->str[1] = '?'; // BAD + s4->str[1] = '?'; // BAD [NOT DETECTED] + s5->str[1] = '?'; // BAD [NOT DETECTED] } } @@ -166,7 +166,7 @@ void useVarStruct34(varStruct5 *vs5) { void testVarStruct34(varStruct3 *vs3, varStruct4 *vs4, varStruct5 *vs5, varStruct6 *vs6, varStruct7 *vs7, varStruct8 *vs8, varStruct9 *vs9) { memset(vs3->arr, 'x', 100); // GOOD: it's variable size, we don't know how big so shouldn't flag - memset(vs4->arr, 'x', 100); // BAD: it's not variable size, so this is a buffer overflow + memset(vs4->arr, 'x', 100); // BAD: [NOT DETECTED] it's not variable size, so this is a buffer overflow memset(vs5->arr, 'x', 100); // GOOD: it's variable size, we don't know how big so shouldn't flag memset(vs6->arr, 'x', 100); // GOOD: it's variable size, we don't know how big so shouldn't flag memset(vs7->arr, 'x', 100); // GOOD: it's variable size, we don't know how big so shouldn't flag diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/varsize.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/varsize.expected index d9e9effde62..79406c3eaef 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/varsize.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/varsize.expected @@ -1,12 +1,17 @@ | var_size_struct.cpp:13:8:13:17 | VarString1 | var_size_struct.cpp:15:8:15:10 | str | | var_size_struct.cpp:18:8:18:17 | VarString2 | var_size_struct.cpp:20:8:20:10 | str | | var_size_struct.cpp:24:8:24:17 | VarString3 | var_size_struct.cpp:26:8:26:10 | str | +| var_size_struct.cpp:30:8:30:17 | VarString4 | var_size_struct.cpp:32:8:32:10 | str | +| var_size_struct.cpp:36:8:36:17 | VarString5 | var_size_struct.cpp:38:8:38:10 | str | | var_size_struct.cpp:36:8:36:17 | VarString5 | var_size_struct.cpp:39:8:39:11 | str2 | | var_size_struct.cpp:61:8:61:17 | varStruct1 | var_size_struct.cpp:63:8:63:11 | data | | var_size_struct.cpp:76:8:76:17 | varStruct2 | var_size_struct.cpp:78:7:78:14 | elements | +| var_size_struct.cpp:106:8:106:20 | notVarStruct2 | var_size_struct.cpp:107:8:107:10 | str | | var_size_struct.cpp:119:8:119:17 | varStruct3 | var_size_struct.cpp:121:17:121:19 | arr | +| var_size_struct.cpp:123:8:123:17 | varStruct4 | var_size_struct.cpp:125:17:125:19 | arr | | var_size_struct.cpp:127:8:127:17 | varStruct5 | var_size_struct.cpp:129:17:129:19 | arr | | var_size_struct.cpp:131:8:131:17 | varStruct6 | var_size_struct.cpp:133:17:133:19 | arr | | var_size_struct.cpp:135:8:135:17 | varStruct7 | var_size_struct.cpp:137:17:137:19 | arr | | var_size_struct.cpp:139:8:139:17 | varStruct8 | var_size_struct.cpp:141:9:141:11 | arr | | var_size_struct.cpp:143:8:143:17 | varStruct9 | var_size_struct.cpp:145:17:145:19 | arr | +| var_size_struct.cpp:181:8:181:18 | PseudoUnion | var_size_struct.cpp:183:7:183:10 | data | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextTransmission.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextTransmission.expected new file mode 100644 index 00000000000..3534c2c7cad --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextTransmission.expected @@ -0,0 +1,49 @@ +edges +| test3.cpp:68:21:68:29 | password1 | test3.cpp:70:15:70:17 | ptr | +| test3.cpp:75:15:75:22 | password | test3.cpp:77:15:77:17 | ptr | +| test3.cpp:106:20:106:25 | buffer | test3.cpp:108:14:108:19 | buffer | +| test3.cpp:111:28:111:33 | buffer | test3.cpp:113:9:113:14 | buffer | +| test3.cpp:120:9:120:23 | global_password | test3.cpp:138:16:138:29 | call to get_global_str | +| test3.cpp:128:11:128:18 | password | test3.cpp:106:20:106:25 | buffer | +| test3.cpp:132:21:132:22 | call to id | test3.cpp:134:15:134:17 | ptr | +| test3.cpp:132:24:132:32 | password1 | test3.cpp:111:28:111:33 | buffer | +| test3.cpp:132:24:132:32 | password1 | test3.cpp:132:21:132:22 | call to id | +| test3.cpp:138:16:138:29 | call to get_global_str | test3.cpp:140:15:140:18 | data | +| test3.cpp:151:19:151:26 | password | test3.cpp:153:15:153:20 | buffer | +nodes +| test3.cpp:20:15:20:23 | password1 | semmle.label | password1 | +| test3.cpp:24:15:24:23 | password2 | semmle.label | password2 | +| test3.cpp:41:15:41:22 | password | semmle.label | password | +| test3.cpp:49:15:49:22 | password | semmle.label | password | +| test3.cpp:68:21:68:29 | password1 | semmle.label | password1 | +| test3.cpp:70:15:70:17 | ptr | semmle.label | ptr | +| test3.cpp:75:15:75:22 | password | semmle.label | password | +| test3.cpp:77:15:77:17 | ptr | semmle.label | ptr | +| test3.cpp:95:12:95:19 | password | semmle.label | password | +| test3.cpp:106:20:106:25 | buffer | semmle.label | buffer | +| test3.cpp:108:14:108:19 | buffer | semmle.label | buffer | +| test3.cpp:111:28:111:33 | buffer | semmle.label | buffer | +| test3.cpp:113:9:113:14 | buffer | semmle.label | buffer | +| test3.cpp:120:9:120:23 | global_password | semmle.label | global_password | +| test3.cpp:128:11:128:18 | password | semmle.label | password | +| test3.cpp:132:21:132:22 | call to id | semmle.label | call to id | +| test3.cpp:132:24:132:32 | password1 | semmle.label | password1 | +| test3.cpp:134:15:134:17 | ptr | semmle.label | ptr | +| test3.cpp:138:16:138:29 | call to get_global_str | semmle.label | call to get_global_str | +| test3.cpp:140:15:140:18 | data | semmle.label | data | +| test3.cpp:151:19:151:26 | password | semmle.label | password | +| test3.cpp:153:15:153:20 | buffer | semmle.label | buffer | +subpaths +| test3.cpp:132:24:132:32 | password1 | test3.cpp:111:28:111:33 | buffer | test3.cpp:113:9:113:14 | buffer | test3.cpp:132:21:132:22 | call to id | +#select +| test3.cpp:20:3:20:6 | call to send | test3.cpp:20:15:20:23 | password1 | test3.cpp:20:15:20:23 | password1 | This operation transmits 'password1', which may contain unencrypted sensitive data from $@ | test3.cpp:20:15:20:23 | password1 | password1 | +| test3.cpp:24:3:24:6 | call to send | test3.cpp:24:15:24:23 | password2 | test3.cpp:24:15:24:23 | password2 | This operation transmits 'password2', which may contain unencrypted sensitive data from $@ | test3.cpp:24:15:24:23 | password2 | password2 | +| test3.cpp:41:3:41:6 | call to recv | test3.cpp:41:15:41:22 | password | test3.cpp:41:15:41:22 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:41:15:41:22 | password | password | +| test3.cpp:49:3:49:6 | call to recv | test3.cpp:49:15:49:22 | password | test3.cpp:49:15:49:22 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:49:15:49:22 | password | password | +| test3.cpp:70:3:70:6 | call to send | test3.cpp:68:21:68:29 | password1 | test3.cpp:70:15:70:17 | ptr | This operation transmits 'ptr', which may contain unencrypted sensitive data from $@ | test3.cpp:68:21:68:29 | password1 | password1 | +| test3.cpp:77:3:77:6 | call to recv | test3.cpp:75:15:75:22 | password | test3.cpp:77:15:77:17 | ptr | This operation receives into 'ptr', which may put unencrypted sensitive data into $@ | test3.cpp:75:15:75:22 | password | password | +| test3.cpp:95:3:95:6 | call to read | test3.cpp:95:12:95:19 | password | test3.cpp:95:12:95:19 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:95:12:95:19 | password | password | +| test3.cpp:108:2:108:5 | call to recv | test3.cpp:128:11:128:18 | password | test3.cpp:108:14:108:19 | buffer | This operation receives into 'buffer', which may put unencrypted sensitive data into $@ | test3.cpp:128:11:128:18 | password | password | +| test3.cpp:134:3:134:6 | call to send | test3.cpp:132:24:132:32 | password1 | test3.cpp:134:15:134:17 | ptr | This operation transmits 'ptr', which may contain unencrypted sensitive data from $@ | test3.cpp:132:24:132:32 | password1 | password1 | +| test3.cpp:140:3:140:6 | call to send | test3.cpp:120:9:120:23 | global_password | test3.cpp:140:15:140:18 | data | This operation transmits 'data', which may contain unencrypted sensitive data from $@ | test3.cpp:120:9:120:23 | global_password | global_password | +| test3.cpp:153:3:153:6 | call to send | test3.cpp:151:19:151:26 | password | test3.cpp:153:15:153:20 | buffer | This operation transmits 'buffer', which may contain unencrypted sensitive data from $@ | test3.cpp:151:19:151:26 | password | password | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextTransmission.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextTransmission.qlref new file mode 100644 index 00000000000..bb3fc66f1f1 --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextTransmission.qlref @@ -0,0 +1 @@ +Security/CWE/CWE-311/CleartextTransmission.ql \ No newline at end of file diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/test3.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/test3.cpp new file mode 100644 index 00000000000..010ed2c8062 --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/test3.cpp @@ -0,0 +1,155 @@ + +typedef unsigned long size_t; +#define STDIN_FILENO (0) + +size_t strlen(const char *s); + +void send(int fd, const void *buf, size_t bufLen, int d); +void recv(int fd, void *buf, size_t bufLen, int d); +void read(int fd, void *buf, size_t bufLen); + +void LogonUserA(int a, int b, const char *password, int d, int e, int f); + +int val(); + +void test_send(const char *password1, const char *password2, const char *password_hash, const char *message) +{ + { + LogonUserA(val(), val(), password1, val(), val(), val()); // proof `password1` is plaintext + + send(val(), password1, strlen(password1), val()); // BAD: `password1` is sent plaintext (certainly) + } + + { + send(val(), password2, strlen(password2), val()); // BAD: `password2` is sent plaintext (probably) + } + + { + send(val(), password_hash, strlen(password_hash), val()); // GOOD: `password_hash` is sent encrypted + } + + { + send(val(), message, strlen(message), val()); // GOOD: `message` is not a password + } +} + +void test_receive() +{ + { + char password[256]; + + recv(val(), password, 256, val()); // BAD: `password` is received plaintext (certainly) + + LogonUserA(val(), val(), password, val(), val(), val()); // (proof `password` is plaintext) + } + + { + char password[256]; + + recv(val(), password, 256, val()); // BAD: `password` is received plaintext (probably) + } + + { + char password_hash[256]; + + recv(val(), password_hash, 256, val()); // GOOD: `password` is received encrypted + } + + { + char message[256]; + + recv(val(), message, 256, val()); // GOOD: `message` is not a password + } +} + +void test_dataflow(const char *password1) +{ + { + const char *ptr = password1; + + send(val(), ptr, strlen(ptr), val()); // BAD: `password` is sent plaintext + } + + { + char password[256]; + char *ptr = password; + + recv(val(), ptr, 256, val()); // BAD: `password` is received plaintext + } + + { + char buffer[256]; + + recv(val(), buffer, 256, val()); // BAD: `password` is received plaintext [NOT DETECTED] + + char *password = buffer; + } +} + +void test_read() +{ + { + char password[256]; + int fd = val(); + + read(fd, password, 256); // BAD: `password` is received plaintext + } + + { + char password[256]; + int fd = STDIN_FILENO; + + read(fd, password, 256); // GOOD: `password` is received from stdin, not a network socket + } +} + +void my_recv(char *buffer, size_t bufferSize) +{ + recv(val(), buffer, bufferSize, val()); +} + +const char *id(const char *buffer) +{ + return buffer; +} + +char *global_password; + +char *get_global_str() +{ + return global_password; +} + +void test_interprocedural(const char *password1) +{ + { + char password[256]; + + my_recv(password, 256); // BAD: `password` is received plaintext [detected on line 108] + } + + { + const char *ptr = id(password1); + + send(val(), ptr, strlen(ptr), val()); // BAD: `password1` is sent plaintext + } + + { + char *data = get_global_str(); + + send(val(), data, strlen(data), val()); // BAD: `global_password` is sent plaintext + } +} + +char *strncpy(char *s1, const char *s2, size_t n); + +void test_taint(const char *password) +{ + { + char buffer[16]; + + strncpy(buffer, password, 16); + buffer[15] = 0; + send(val(), buffer, 16, val()); // BAD: `password` is (partially) sent plaintext + } +} diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-367/semmle/TOCTOUFilesystemRace.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-367/semmle/TOCTOUFilesystemRace.expected index f0f7aaaefee..413d3dbccf9 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-367/semmle/TOCTOUFilesystemRace.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-367/semmle/TOCTOUFilesystemRace.expected @@ -5,9 +5,13 @@ | test2.cpp:130:7:130:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:130:13:130:16 | path | filename | test2.cpp:128:21:128:27 | buf_ptr | checked | | test2.cpp:157:7:157:10 | call to open | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:157:12:157:15 | path | filename | test2.cpp:155:6:155:9 | call to stat | checked | | test2.cpp:170:7:170:10 | call to open | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:170:12:170:15 | path | filename | test2.cpp:168:6:168:10 | call to lstat | checked | -| test2.cpp:245:3:245:7 | call to chmod | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:245:9:245:12 | path | filename | test2.cpp:238:6:238:10 | call to fopen | checked | -| test2.cpp:277:7:277:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:277:13:277:16 | path | filename | test2.cpp:275:6:275:11 | call to access | checked | -| test2.cpp:303:7:303:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:303:13:303:16 | path | filename | test2.cpp:301:7:301:12 | call to access | checked | -| test2.cpp:317:7:317:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:317:13:317:16 | path | filename | test2.cpp:313:6:313:11 | call to access | checked | -| test2.cpp:348:3:348:7 | call to chmod | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:348:9:348:12 | path | filename | test2.cpp:341:6:341:10 | call to fopen | checked | -| test2.cpp:356:3:356:7 | call to chmod | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:356:9:356:13 | path2 | filename | test2.cpp:354:7:354:12 | call to rename | checked | +| test2.cpp:209:7:209:10 | call to open | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:209:12:209:15 | path | filename | test2.cpp:207:6:207:9 | call to stat | checked | +| test2.cpp:228:8:228:11 | call to open | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:228:13:228:16 | path | filename | test2.cpp:224:6:224:9 | call to stat | checked | +| test2.cpp:228:8:228:11 | call to open | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:228:13:228:16 | path | filename | test2.cpp:226:7:226:9 | buf | checked | +| test2.cpp:249:6:249:10 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:249:12:249:15 | path | filename | test2.cpp:244:6:244:9 | call to stat | checked | +| test2.cpp:297:3:297:7 | call to chmod | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:297:9:297:12 | path | filename | test2.cpp:290:6:290:10 | call to fopen | checked | +| test2.cpp:329:7:329:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:329:13:329:16 | path | filename | test2.cpp:327:6:327:11 | call to access | checked | +| test2.cpp:355:7:355:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:355:13:355:16 | path | filename | test2.cpp:353:7:353:12 | call to access | checked | +| test2.cpp:369:7:369:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:369:13:369:16 | path | filename | test2.cpp:365:6:365:11 | call to access | checked | +| test2.cpp:400:3:400:7 | call to chmod | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:400:9:400:12 | path | filename | test2.cpp:393:6:393:10 | call to fopen | checked | +| test2.cpp:408:3:408:7 | call to chmod | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:408:9:408:13 | path2 | filename | test2.cpp:406:7:406:12 | call to rename | checked | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-367/semmle/test2.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-367/semmle/test2.cpp index e1317e9a9ca..14875b9d367 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-367/semmle/test2.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-367/semmle/test2.cpp @@ -199,6 +199,58 @@ void test2_10(int dir, const char *path, int arg) // ... } +void test2_11(const char *path, int arg) +{ + stat_data buf; + int f; + + if (stat(path, &buf)) + { + f = open(path, arg); // GOOD (here stat is just a redundant check that the file exists / path is valid, confirmed by the return value of open) [FALSE POSITIVE] + if (f == -1) + { + // handle error + } + + // ... + } +} + +void test2_12(const char *path, int arg) +{ + stat_data buf; + int f; + + if (stat(path, &buf)) + { + if (buf.foo == 11) // check a property of the file + { + f = open(path, arg); // BAD + if (f == -1) + { + // handle error + } + } + + // ... + } +} + +void test2_13(const char *path, int arg) +{ + stat_data buf; + FILE *f; + + if (stat(path, &buf)) // check the file does *not* exist + { + return; + } + + f = fopen(path, "wt"); // BAD + + // ... +} + // --- open -> stat --- void test3_1(const char *path, int arg) diff --git a/cpp/upgrades/7806a11dd7ab6611c4245b2e96b8ed13cb5c6056/old.dbscheme b/cpp/upgrades/7806a11dd7ab6611c4245b2e96b8ed13cb5c6056/old.dbscheme new file mode 100644 index 00000000000..7806a11dd7a --- /dev/null +++ b/cpp/upgrades/7806a11dd7ab6611c4245b2e96b8ed13cb5c6056/old.dbscheme @@ -0,0 +1,2136 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The date of the snapshot. + */ +snapshotDate(unique date snapshotDate : date ref); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Data used by the 'duplicate code' detection. + */ +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'similar code' detection. + */ +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +@duplication_or_similarity = @duplication | @similarity + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +#keyset[id, offset] +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +/* + case @macroinvocations.kind of + 1 = macro expansion + | 2 = other macro reference + ; +*/ +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* + case @function.kind of + 1 = normal + | 2 = constructor + | 3 = destructor + | 4 = conversion + | 5 = operator + | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk + ; +*/ +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point(int id: @function ref, unique int entry_point: @stmt ref); + +function_return_type(int id: @function ref, int return_type: @type ref); + +/** If `function` is a coroutine, then this gives the + std::experimental::resumable_traits instance associated with it, + and the variables representing the `handle` and `promise` for it. */ +coroutine( + unique int function: @function ref, + int traits: @type ref, + int handle: @variable ref, + int promise: @variable ref +); + +/** The `new` function used for allocating the coroutine state, if any. */ +coroutine_new( + unique int function: @function ref, + int new: @function ref +); + +/** The `delete` function used for deallocating the coroutine state, if any. */ +coroutine_delete( + unique int function: @function ref, + int delete: @function ref +); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + +member_function_this_type(unique int id: @function ref, int this_type: @type ref); + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref, + int enclosing : @element ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides(int new: @function ref, int old: @function ref); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/* + Built-in types are the fundamental types, e.g., integral, floating, and void. + + case @builtintype.kind of + 1 = error + | 2 = unknown + | 3 = void + | 4 = boolean + | 5 = char + | 6 = unsigned_char + | 7 = signed_char + | 8 = short + | 9 = unsigned_short + | 10 = signed_short + | 11 = int + | 12 = unsigned_int + | 13 = signed_int + | 14 = long + | 15 = unsigned_long + | 16 = signed_long + | 17 = long_long + | 18 = unsigned_long_long + | 19 = signed_long_long + | 20 = __int8 // Microsoft-specific + | 21 = __int16 // Microsoft-specific + | 22 = __int32 // Microsoft-specific + | 23 = __int64 // Microsoft-specific + | 24 = float + | 25 = double + | 26 = long_double + | 27 = _Complex_float // C99-specific + | 28 = _Complex_double // C99-specific + | 29 = _Complex_long double // C99-specific + | 30 = _Imaginary_float // C99-specific + | 31 = _Imaginary_double // C99-specific + | 32 = _Imaginary_long_double // C99-specific + | 33 = wchar_t // Microsoft-specific + | 34 = decltype_nullptr // C++11 + | 35 = __int128 + | 36 = unsigned___int128 + | 37 = signed___int128 + | 38 = __float128 + | 39 = _Complex___float128 + | 40 = _Decimal32 + | 41 = _Decimal64 + | 42 = _Decimal128 + | 43 = char16_t + | 44 = char32_t + | 45 = _Float32 + | 46 = _Float32x + | 47 = _Float64 + | 48 = _Float64x + | 49 = _Float128 + | 50 = _Float128x + | 51 = char8_t + ; +*/ +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/* + Derived types are types that are directly derived from existing types and + point to, refer to, transform type data to return a new type. + + case @derivedtype.kind of + 1 = pointer + | 2 = reference + | 3 = type_with_specifiers + | 4 = array + | 5 = gnu_vector + | 6 = routineptr + | 7 = routinereference + | 8 = rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated + | 10 = block + ; +*/ +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +/** + * An instance of the C++11 `decltype` operator. For example: + * ``` + * int a; + * decltype(1+a) b; + * ``` + * Here `expr` is `1+a`. + * + * Sometimes an additional pair of parentheses around the expression + * would change the semantics of this decltype, e.g. + * ``` + * struct A { double x; }; + * const A* a = new A(); + * decltype( a->x ); // type is double + * decltype((a->x)); // type is const double& + * ``` + * (Please consult the C++11 standard for more details). + * `parentheses_would_change_meaning` is `true` iff that is the case. + */ +#keyset[id, expr] +decltypes( + int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* + case @usertype.kind of + 1 = struct + | 2 = class + | 3 = union + | 4 = enum + | 5 = typedef // classic C: typedef typedef type name + | 6 = template + | 7 = template_parameter + | 8 = template_template_parameter + | 9 = proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated + | 13 = scoped_enum + | 14 = using_alias // a using name = type style typedef + ; +*/ +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + unique string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + | @temp_init + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall(unique int caller: @funbindexpr ref, int kind: int ref); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + | @co_await + | @co_yield + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +| 327 = @co_await +| 328 = @co_yield +| 329 = @temp_init +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. + */ +#keyset[aggregate, field] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. + */ +#keyset[aggregate, element_index] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +| 37 = @stmt_co_return +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* XML Files */ + +xmlEncoding(unique int id: @file ref, string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters + | @xmlelement + | @xmlcomment + | @xmlattribute + | @xmldtd + | @file + | @xmlnamespace; diff --git a/cpp/upgrades/7806a11dd7ab6611c4245b2e96b8ed13cb5c6056/semmlecode.cpp.dbscheme b/cpp/upgrades/7806a11dd7ab6611c4245b2e96b8ed13cb5c6056/semmlecode.cpp.dbscheme new file mode 100644 index 00000000000..018f430097e --- /dev/null +++ b/cpp/upgrades/7806a11dd7ab6611c4245b2e96b8ed13cb5c6056/semmlecode.cpp.dbscheme @@ -0,0 +1,2136 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The date of the snapshot. + */ +snapshotDate(unique date snapshotDate : date ref); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Data used by the 'duplicate code' detection. + */ +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'similar code' detection. + */ +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +@duplication_or_similarity = @duplication | @similarity + +/** + * Data used by the 'duplicate code' and 'similar code' detection. + */ +#keyset[id, offset] +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +/* + case @macroinvocations.kind of + 1 = macro expansion + | 2 = other macro reference + ; +*/ +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* + case @function.kind of + 1 = normal + | 2 = constructor + | 3 = destructor + | 4 = conversion + | 5 = operator + | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk + ; +*/ +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point(int id: @function ref, unique int entry_point: @stmt ref); + +function_return_type(int id: @function ref, int return_type: @type ref); + +/** If `function` is a coroutine, then this gives the + std::experimental::resumable_traits instance associated with it, + and the variables representing the `handle` and `promise` for it. */ +coroutine( + unique int function: @function ref, + int traits: @type ref, + int handle: @variable ref, + int promise: @variable ref +); + +/** The `new` function used for allocating the coroutine state, if any. */ +coroutine_new( + unique int function: @function ref, + int new: @function ref +); + +/** The `delete` function used for deallocating the coroutine state, if any. */ +coroutine_delete( + unique int function: @function ref, + int delete: @function ref +); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + +member_function_this_type(unique int id: @function ref, int this_type: @type ref); + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref, + int enclosing : @element ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides(int new: @function ref, int old: @function ref); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/* + Built-in types are the fundamental types, e.g., integral, floating, and void. + + case @builtintype.kind of + 1 = error + | 2 = unknown + | 3 = void + | 4 = boolean + | 5 = char + | 6 = unsigned_char + | 7 = signed_char + | 8 = short + | 9 = unsigned_short + | 10 = signed_short + | 11 = int + | 12 = unsigned_int + | 13 = signed_int + | 14 = long + | 15 = unsigned_long + | 16 = signed_long + | 17 = long_long + | 18 = unsigned_long_long + | 19 = signed_long_long + | 20 = __int8 // Microsoft-specific + | 21 = __int16 // Microsoft-specific + | 22 = __int32 // Microsoft-specific + | 23 = __int64 // Microsoft-specific + | 24 = float + | 25 = double + | 26 = long_double + | 27 = _Complex_float // C99-specific + | 28 = _Complex_double // C99-specific + | 29 = _Complex_long double // C99-specific + | 30 = _Imaginary_float // C99-specific + | 31 = _Imaginary_double // C99-specific + | 32 = _Imaginary_long_double // C99-specific + | 33 = wchar_t // Microsoft-specific + | 34 = decltype_nullptr // C++11 + | 35 = __int128 + | 36 = unsigned___int128 + | 37 = signed___int128 + | 38 = __float128 + | 39 = _Complex___float128 + | 40 = _Decimal32 + | 41 = _Decimal64 + | 42 = _Decimal128 + | 43 = char16_t + | 44 = char32_t + | 45 = _Float32 + | 46 = _Float32x + | 47 = _Float64 + | 48 = _Float64x + | 49 = _Float128 + | 50 = _Float128x + | 51 = char8_t + ; +*/ +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/* + Derived types are types that are directly derived from existing types and + point to, refer to, transform type data to return a new type. + + case @derivedtype.kind of + 1 = pointer + | 2 = reference + | 3 = type_with_specifiers + | 4 = array + | 5 = gnu_vector + | 6 = routineptr + | 7 = routinereference + | 8 = rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated + | 10 = block + ; +*/ +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +/** + * An instance of the C++11 `decltype` operator. For example: + * ``` + * int a; + * decltype(1+a) b; + * ``` + * Here `expr` is `1+a`. + * + * Sometimes an additional pair of parentheses around the expression + * would change the semantics of this decltype, e.g. + * ``` + * struct A { double x; }; + * const A* a = new A(); + * decltype( a->x ); // type is double + * decltype((a->x)); // type is const double& + * ``` + * (Please consult the C++11 standard for more details). + * `parentheses_would_change_meaning` is `true` iff that is the case. + */ +#keyset[id, expr] +decltypes( + int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* + case @usertype.kind of + 1 = struct + | 2 = class + | 3 = union + | 4 = enum + | 5 = typedef // classic C: typedef typedef type name + | 6 = template + | 7 = template_parameter + | 8 = template_template_parameter + | 9 = proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated + | 13 = scoped_enum + | 14 = using_alias // a using name = type style typedef + ; +*/ +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + unique string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + | @temp_init + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall(unique int caller: @funbindexpr ref, int kind: int ref); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + | @co_await + | @co_yield + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +| 327 = @co_await +| 328 = @co_yield +| 329 = @temp_init +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. + */ +#keyset[aggregate, field] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. + */ +#keyset[aggregate, element_index] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +| 37 = @stmt_co_return +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* XML Files */ + +xmlEncoding(unique int id: @file ref, string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters + | @xmlelement + | @xmlcomment + | @xmlattribute + | @xmldtd + | @file + | @xmlnamespace; diff --git a/cpp/upgrades/7806a11dd7ab6611c4245b2e96b8ed13cb5c6056/upgrade.properties b/cpp/upgrades/7806a11dd7ab6611c4245b2e96b8ed13cb5c6056/upgrade.properties new file mode 100644 index 00000000000..63872bd6f10 --- /dev/null +++ b/cpp/upgrades/7806a11dd7ab6611c4245b2e96b8ed13cb5c6056/upgrade.properties @@ -0,0 +1,2 @@ +description: Non-functional change to dbscheme comments +compatibility: full diff --git a/cpp/upgrades/qlpack.yml b/cpp/upgrades/qlpack.yml index acc305bb6a2..7f18cb54c8e 100644 --- a/cpp/upgrades/qlpack.yml +++ b/cpp/upgrades/qlpack.yml @@ -1,3 +1,4 @@ name: codeql/cpp-upgrades upgrades: . version: 0.0.2 +library: true diff --git a/csharp/change-notes/2021-10-04-constand-condition.md b/csharp/change-notes/2021-10-04-constand-condition.md new file mode 100644 index 00000000000..70ad5b5ea75 --- /dev/null +++ b/csharp/change-notes/2021-10-04-constand-condition.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Discards in tuple patterns, for example `(_, string s)`, are no longer flagged by the query "Constant condition". \ No newline at end of file diff --git a/csharp/change-notes/2021-10-04-dead-store-of-local.md b/csharp/change-notes/2021-10-04-dead-store-of-local.md new file mode 100644 index 00000000000..307f10af654 --- /dev/null +++ b/csharp/change-notes/2021-10-04-dead-store-of-local.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* `using` declarations are no longer flagged by the query "Useless assignment to local variable". diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs index a478047ac7b..123bc315bfa 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs @@ -178,7 +178,13 @@ namespace Semmle.Extraction.CSharp.Entities var defaultValue = parameter.ExplicitDefaultValue; - if (parameter.Type is INamedTypeSymbol nt && nt.EnumUnderlyingType is not null) + var type = parameter.Type; + if (type.IsBoundNullable() && type is INamedTypeSymbol named) + { + type = named.TypeArguments[0]; + } + + if (type is INamedTypeSymbol nt && nt.EnumUnderlyingType is not null) { // = (MyEnum)1, = MyEnum.Value1, = default(MyEnum), = new MyEnum() // we're generating a (MyEnum)value cast expression: @@ -194,7 +200,7 @@ namespace Semmle.Extraction.CSharp.Entities return Default.CreateGenerated(cx, parent, childIndex, location, parameter.Type.IsReferenceType ? ValueAsString(null) : null); } - if (parameter.Type.SpecialType == SpecialType.System_Object) + if (parameter.Type.SpecialType is SpecialType.System_Object) { // this can happen in VB.NET cx.ExtractionError($"Extracting default argument value 'object {parameter.Name} = default' instead of 'object {parameter.Name} = {defaultValue}'. The latter is not supported in C#.", @@ -205,7 +211,7 @@ namespace Semmle.Extraction.CSharp.Entities } // const literal: - return Literal.CreateGenerated(cx, parent, childIndex, parameter.Type, defaultValue, location); + return Literal.CreateGenerated(cx, parent, childIndex, type, defaultValue, location); } /// diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Factory.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Factory.cs index 63389822cad..a92715c4e1c 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Factory.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Factory.cs @@ -14,7 +14,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { if (info.Node is null) { - info.Context.ModelError("Attempt to create a null expression"); + info.Context.ModelError(info.Location, "Attempt to create a null expression"); return new Unknown(info); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Literal.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Literal.cs index bac257e9e34..387e8074f5f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Literal.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Literal.cs @@ -32,10 +32,10 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions } var type = info.Type?.Symbol; - return GetExprKind(type, info.Node, info.Context); + return GetExprKind(type, info.Node, info.Location, info.Context); } - private static ExprKind GetExprKind(ITypeSymbol? type, ExpressionSyntax? expr, Context context) + private static ExprKind GetExprKind(ITypeSymbol? type, ExpressionSyntax? expr, Extraction.Entities.Location loc, Context context) { switch (type?.SpecialType) { @@ -75,10 +75,11 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions case null: default: + var kind = type?.SpecialType.ToString() ?? "null"; if (expr is not null) - context.ModelError(expr, "Unhandled literal type"); + context.ModelError(expr, $"Unhandled literal type {kind}"); else - context.ModelError("Unhandled literal type"); + context.ModelError(loc, $"Unhandled literal type {kind}"); return ExprKind.UNKNOWN; } } @@ -86,11 +87,12 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions public static Expression CreateGenerated(Context cx, IExpressionParentEntity parent, int childIndex, ITypeSymbol type, object? value, Extraction.Entities.Location location) { + var kind = value is null ? ExprKind.NULL_LITERAL : GetExprKind(type, null, location, cx); var info = new ExpressionInfo( cx, AnnotatedTypeSymbol.CreateNotAnnotated(type), location, - GetExprKind(type, null, cx), + kind, parent, childIndex, true, diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs index d01a3f37ac8..3140815d323 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs @@ -492,8 +492,18 @@ namespace Semmle.Extraction.CSharp /// /// Gets a list of all `csharp.{hash}.txt` files currently written to the log directory. /// - public static IEnumerable GetCSharpArgsLogs() => - Directory.EnumerateFiles(GetCSharpLogDirectory(), "csharp.*.txt"); + public static IEnumerable GetCSharpArgsLogs() + { + try + { + return Directory.EnumerateFiles(GetCSharpLogDirectory(), "csharp.*.txt"); + } + catch (DirectoryNotFoundException) + { + // If the directory does not exist, there are no log files + return Enumerable.Empty(); + } + } private static string GetCSharpLogDirectory() { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Populators/CompilationUnitVisitor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Populators/CompilationUnitVisitor.cs index 6116c3511a1..be53e48f319 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Populators/CompilationUnitVisitor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Populators/CompilationUnitVisitor.cs @@ -59,8 +59,15 @@ namespace Semmle.Extraction.CSharp.Populators return; } - var entryPoint = Cx.Compilation.GetEntryPoint(System.Threading.CancellationToken.None)!; + var entryPoint = Cx.Compilation.GetEntryPoint(System.Threading.CancellationToken.None); var entryMethod = Method.Create(Cx, entryPoint); + if (entryMethod is null) + { + Cx.ExtractionError("No entry method found. Skipping the extraction of global statements.", + null, Cx.CreateLocation(globalStatements[0].GetLocation()), null, Severity.Info); + return; + } + var block = GlobalStatementsBlock.Create(Cx, entryMethod); for (var i = 0; i < globalStatements.Count; i++) diff --git a/csharp/extractor/Semmle.Extraction/Context.cs b/csharp/extractor/Semmle.Extraction/Context.cs index abbabcdd198..99da916d294 100644 --- a/csharp/extractor/Semmle.Extraction/Context.cs +++ b/csharp/extractor/Semmle.Extraction/Context.cs @@ -426,6 +426,16 @@ namespace Semmle.Extraction ReportError(new InternalError(symbol, msg)); } + /// + /// Signal an error in the program model. + /// + /// The location of the error. + /// The error message. + public void ModelError(Entities.Location loc, string msg) + { + ReportError(new InternalError(loc.ReportingLocation, msg)); + } + /// /// Signal an error in the program model. /// diff --git a/csharp/extractor/Semmle.Extraction/InternalError.cs b/csharp/extractor/Semmle.Extraction/InternalError.cs index a90685e068f..fe9701488c7 100644 --- a/csharp/extractor/Semmle.Extraction/InternalError.cs +++ b/csharp/extractor/Semmle.Extraction/InternalError.cs @@ -23,6 +23,13 @@ namespace Semmle.Extraction Location = node.GetLocation(); } + public InternalError(Location? loc, string msg) + { + Text = msg; + EntityText = ""; + Location = loc; + } + public InternalError(string msg) { Text = msg; diff --git a/csharp/ql/examples/qlpack.yml b/csharp/ql/examples/qlpack.yml index 573e7a673d0..fd3bccb2005 100644 --- a/csharp/ql/examples/qlpack.yml +++ b/csharp/ql/examples/qlpack.yml @@ -1,4 +1,4 @@ -name: codeql-csharp-examples +name: codeql/csharp-examples version: 0.0.2 dependencies: codeql/csharp-all: "*" diff --git a/csharp/ql/lib/semmle/code/asp/WebConfig.qll b/csharp/ql/lib/semmle/code/asp/WebConfig.qll index ed0f9aef451..16d5393afc2 100644 --- a/csharp/ql/lib/semmle/code/asp/WebConfig.qll +++ b/csharp/ql/lib/semmle/code/asp/WebConfig.qll @@ -8,7 +8,7 @@ import csharp * A `Web.config` file. */ class WebConfigXML extends XMLFile { - WebConfigXML() { getName().matches("%Web.config") } + WebConfigXML() { this.getName().matches("%Web.config") } } /** A `` tag in an ASP.NET configuration file. */ @@ -73,12 +73,14 @@ class FormsElement extends XMLElement { /** * Gets attribute's `requireSSL` value. */ - string getRequireSSL() { result = getAttribute("requireSSL").getValue().trim().toLowerCase() } + string getRequireSSL() { + result = this.getAttribute("requireSSL").getValue().trim().toLowerCase() + } /** * Holds if `requireSSL` value is true. */ - predicate isRequireSSL() { getRequireSSL() = "true" } + predicate isRequireSSL() { this.getRequireSSL() = "true" } } /** A `` tag in an ASP.NET configuration file. */ @@ -89,26 +91,28 @@ class HttpCookiesElement extends XMLElement { * Gets attribute's `httpOnlyCookies` value. */ string getHttpOnlyCookies() { - result = getAttribute("httpOnlyCookies").getValue().trim().toLowerCase() + result = this.getAttribute("httpOnlyCookies").getValue().trim().toLowerCase() } /** * Holds if there is any chance that `httpOnlyCookies` is set to `true`. */ - predicate isHttpOnlyCookies() { getHttpOnlyCookies() = "true" } + predicate isHttpOnlyCookies() { this.getHttpOnlyCookies() = "true" } /** * Gets attribute's `requireSSL` value. */ - string getRequireSSL() { result = getAttribute("requireSSL").getValue().trim().toLowerCase() } + string getRequireSSL() { + result = this.getAttribute("requireSSL").getValue().trim().toLowerCase() + } /** * Holds if there is any chance that `requireSSL` is set to `true` either globally or for Forms. */ predicate isRequireSSL() { - getRequireSSL() = "true" + this.getRequireSSL() = "true" or - not getRequireSSL() = "false" and // not set all, i.e. default - exists(FormsElement forms | forms.getFile() = getFile() | forms.isRequireSSL()) + not this.getRequireSSL() = "false" and // not set all, i.e. default + exists(FormsElement forms | forms.getFile() = this.getFile() | forms.isRequireSSL()) } } diff --git a/csharp/ql/lib/semmle/code/cil/Access.qll b/csharp/ql/lib/semmle/code/cil/Access.qll index 6d72a48ff1b..5fecd8acb10 100644 --- a/csharp/ql/lib/semmle/code/cil/Access.qll +++ b/csharp/ql/lib/semmle/code/cil/Access.qll @@ -20,7 +20,7 @@ class VariableAccess extends Access, @cil_access { } /** An instruction that reads a variable. */ class ReadAccess extends VariableAccess, Expr, @cil_read_access { - override Type getType() { result = getTarget().getType() } + override Type getType() { result = this.getTarget().getType() } } /** An instruction yielding an address. */ @@ -49,7 +49,7 @@ class ParameterReadAccess extends ParameterAccess, ReadAccess { class ParameterWriteAccess extends ParameterAccess, WriteAccess { override int getPopCount() { result = 1 } - override Expr getExpr() { result = getOperand(0) } + override Expr getExpr() { result = this.getOperand(0) } } /** An access to the `this` parameter. */ @@ -71,9 +71,9 @@ class LocalVariableAccess extends StackVariableAccess, @cil_local_access { class LocalVariableWriteAccess extends LocalVariableAccess, WriteAccess { override int getPopCount() { result = 1 } - override Expr getExpr() { result = getOperand(0) } + override Expr getExpr() { result = this.getOperand(0) } - override string getExtra() { result = "L" + getTarget().getIndex() } + override string getExtra() { result = "L" + this.getTarget().getIndex() } } /** An instruction that reads a local variable. */ @@ -85,7 +85,7 @@ class LocalVariableReadAccess extends LocalVariableAccess, ReadAccess { class FieldAccess extends VariableAccess, @cil_field_access { override Field getTarget() { result = VariableAccess.super.getTarget() } - override string getExtra() { result = getTarget().getName() } + override string getExtra() { result = this.getTarget().getName() } /** Gets the qualifier of the access, if any. */ abstract Expr getQualifier(); diff --git a/csharp/ql/lib/semmle/code/cil/BasicBlock.qll b/csharp/ql/lib/semmle/code/cil/BasicBlock.qll index 0c9c0b8ad07..2680cb0a769 100644 --- a/csharp/ql/lib/semmle/code/cil/BasicBlock.qll +++ b/csharp/ql/lib/semmle/code/cil/BasicBlock.qll @@ -10,7 +10,7 @@ private import CIL */ class BasicBlock extends Cached::TBasicBlockStart { /** Gets an immediate successor of this basic block, if any. */ - BasicBlock getASuccessor() { result.getFirstNode() = getLastNode().getASuccessor() } + BasicBlock getASuccessor() { result.getFirstNode() = this.getLastNode().getASuccessor() } /** Gets an immediate predecessor of this basic block, if any. */ BasicBlock getAPredecessor() { result.getASuccessor() = this } @@ -31,7 +31,7 @@ class BasicBlock extends Cached::TBasicBlockStart { * The basic block on line 2 is an immediate `true` successor of the * basic block on line 1. */ - BasicBlock getATrueSuccessor() { result.getFirstNode() = getLastNode().getTrueSuccessor() } + BasicBlock getATrueSuccessor() { result.getFirstNode() = this.getLastNode().getTrueSuccessor() } /** * Gets an immediate `false` successor, if any. @@ -49,22 +49,22 @@ class BasicBlock extends Cached::TBasicBlockStart { * The basic block on line 2 is an immediate `false` successor of the * basic block on line 1. */ - BasicBlock getAFalseSuccessor() { result.getFirstNode() = getLastNode().getFalseSuccessor() } + BasicBlock getAFalseSuccessor() { result.getFirstNode() = this.getLastNode().getFalseSuccessor() } /** Gets the control flow node at a specific (zero-indexed) position in this basic block. */ - ControlFlowNode getNode(int pos) { Cached::bbIndex(getFirstNode(), result, pos) } + ControlFlowNode getNode(int pos) { Cached::bbIndex(this.getFirstNode(), result, pos) } /** Gets a control flow node in this basic block. */ - ControlFlowNode getANode() { result = getNode(_) } + ControlFlowNode getANode() { result = this.getNode(_) } /** Gets the first control flow node in this basic block. */ ControlFlowNode getFirstNode() { this = Cached::TBasicBlockStart(result) } /** Gets the last control flow node in this basic block. */ - ControlFlowNode getLastNode() { result = getNode(length() - 1) } + ControlFlowNode getLastNode() { result = this.getNode(this.length() - 1) } /** Gets the length of this basic block. */ - int length() { result = strictcount(getANode()) } + int length() { result = strictcount(this.getANode()) } /** * Holds if this basic block strictly dominates basic block `bb`. @@ -114,7 +114,7 @@ class BasicBlock extends Cached::TBasicBlockStart { */ predicate dominates(BasicBlock bb) { bb = this or - strictlyDominates(bb) + this.strictlyDominates(bb) } /** @@ -140,14 +140,14 @@ class BasicBlock extends Cached::TBasicBlockStart { * does not dominate the basic block on line 6. */ predicate inDominanceFrontier(BasicBlock df) { - dominatesPredecessor(df) and - not strictlyDominates(df) + this.dominatesPredecessor(df) and + not this.strictlyDominates(df) } /** * Holds if this basic block dominates a predecessor of `df`. */ - private predicate dominatesPredecessor(BasicBlock df) { dominates(df.getAPredecessor()) } + private predicate dominatesPredecessor(BasicBlock df) { this.dominates(df.getAPredecessor()) } /** * Gets the basic block that immediately dominates this basic block, if any. @@ -226,7 +226,7 @@ class BasicBlock extends Cached::TBasicBlockStart { * post-dominates itself. */ predicate postDominates(BasicBlock bb) { - strictlyPostDominates(bb) or + this.strictlyPostDominates(bb) or this = bb } @@ -239,7 +239,7 @@ class BasicBlock extends Cached::TBasicBlockStart { predicate inLoop() { this.getASuccessor+() = this } /** Gets a textual representation of this basic block. */ - string toString() { result = getFirstNode().toString() } + string toString() { result = this.getFirstNode().toString() } /** Gets the location of this basic block. */ Location getLocation() { result = this.getFirstNode().getLocation() } @@ -325,16 +325,16 @@ private predicate exitBB(BasicBlock bb) { not exists(bb.getLastNode().getASucces * A basic block with more than one predecessor. */ class JoinBlock extends BasicBlock { - JoinBlock() { getFirstNode().isJoin() } + JoinBlock() { this.getFirstNode().isJoin() } } /** A basic block that terminates in a condition, splitting the subsequent control flow. */ class ConditionBlock extends BasicBlock { ConditionBlock() { exists(BasicBlock succ | - succ = getATrueSuccessor() + succ = this.getATrueSuccessor() or - succ = getAFalseSuccessor() + succ = this.getAFalseSuccessor() ) } @@ -380,16 +380,16 @@ class ConditionBlock extends BasicBlock { */ exists(BasicBlock succ | - isCandidateSuccessor(succ, testIsTrue) and + this.isCandidateSuccessor(succ, testIsTrue) and succ.dominates(controlled) ) } private predicate isCandidateSuccessor(BasicBlock succ, boolean testIsTrue) { ( - testIsTrue = true and succ = getATrueSuccessor() + testIsTrue = true and succ = this.getATrueSuccessor() or - testIsTrue = false and succ = getAFalseSuccessor() + testIsTrue = false and succ = this.getAFalseSuccessor() ) and forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != this | succ.dominates(pred)) } diff --git a/csharp/ql/lib/semmle/code/cil/ConsistencyChecks.qll b/csharp/ql/lib/semmle/code/cil/ConsistencyChecks.qll index 02cfd149886..262bb58ab9c 100644 --- a/csharp/ql/lib/semmle/code/cil/ConsistencyChecks.qll +++ b/csharp/ql/lib/semmle/code/cil/ConsistencyChecks.qll @@ -62,7 +62,7 @@ abstract class InstructionViolation extends CfgViolation, CfgCheck { override string toString() { result = instruction.getImplementation().getMethod().toStringWithTypes() + ": " + - instruction.toString() + ", " + getInstructionsUpTo() + instruction.toString() + ", " + this.getInstructionsUpTo() } } @@ -126,7 +126,7 @@ class MissingOperand extends InstructionViolation { } override string getMessage() { - result = "This instruction is missing operand " + getMissingOperand() + result = "This instruction is missing operand " + this.getMissingOperand() } } @@ -364,7 +364,7 @@ class TypeViolation extends ConsistencyViolation, TypeCheck { /** Gets the type containing the violation. */ Type getType() { this = TypeCheck(result) } - override string toString() { result = getType().toString() } + override string toString() { result = this.getType().toString() } abstract override string getMessage(); } @@ -374,7 +374,7 @@ class TypeViolation extends ConsistencyViolation, TypeCheck { */ class TypeIsBothConstructedAndUnbound extends TypeViolation { TypeIsBothConstructedAndUnbound() { - getType() instanceof ConstructedGeneric and getType() instanceof UnboundGeneric + this.getType() instanceof ConstructedGeneric and this.getType() instanceof UnboundGeneric } override string getMessage() { result = "Type is both constructed and unbound" } @@ -397,16 +397,16 @@ class InconsistentTypeLocation extends TypeViolation { */ class TypeParameterMismatch extends TypeViolation { TypeParameterMismatch() { - getType().(ConstructedGeneric).getNumberOfTypeArguments() != - getType().getUnboundType().(UnboundGeneric).getNumberOfTypeParameters() + this.getType().(ConstructedGeneric).getNumberOfTypeArguments() != + this.getType().getUnboundType().(UnboundGeneric).getNumberOfTypeParameters() } override string getMessage() { result = - "Constructed type (" + getType().toStringWithTypes() + ") has " + - getType().(ConstructedGeneric).getNumberOfTypeArguments() + - " type arguments and unbound type (" + getType().getUnboundType().toStringWithTypes() + - ") has " + getType().getUnboundType().(UnboundGeneric).getNumberOfTypeParameters() + + "Constructed type (" + this.getType().toStringWithTypes() + ") has " + + this.getType().(ConstructedGeneric).getNumberOfTypeArguments() + + " type arguments and unbound type (" + this.getType().getUnboundType().toStringWithTypes() + + ") has " + this.getType().getUnboundType().(UnboundGeneric).getNumberOfTypeParameters() + " type parameters" } } @@ -418,7 +418,7 @@ class MethodViolation extends ConsistencyViolation, DeclarationCheck { /** Gets the method containing the violation. */ Method getMethod() { this = DeclarationCheck(result) } - override string toString() { result = getMethod().toString() } + override string toString() { result = this.getMethod().toString() } override string getMessage() { none() } } @@ -440,14 +440,15 @@ class InconsistentMethodLocation extends MethodViolation { */ class ConstructedMethodTypeParams extends MethodViolation { ConstructedMethodTypeParams() { - getMethod().(ConstructedGeneric).getNumberOfTypeArguments() != - getMethod().getUnboundDeclaration().(UnboundGeneric).getNumberOfTypeParameters() + this.getMethod().(ConstructedGeneric).getNumberOfTypeArguments() != + this.getMethod().getUnboundDeclaration().(UnboundGeneric).getNumberOfTypeParameters() } override string getMessage() { result = - "The constructed method " + getMethod().toStringWithTypes() + - " does not match unbound method " + getMethod().getUnboundDeclaration().toStringWithTypes() + "The constructed method " + this.getMethod().toStringWithTypes() + + " does not match unbound method " + + this.getMethod().getUnboundDeclaration().toStringWithTypes() } } @@ -477,8 +478,8 @@ class InvalidOverride extends MethodViolation { private Method base; InvalidOverride() { - base = getMethod().getOverriddenMethod() and - not getMethod().getDeclaringType().getABaseType+() = base.getDeclaringType() and + base = this.getMethod().getOverriddenMethod() and + not this.getMethod().getDeclaringType().getABaseType+() = base.getDeclaringType() and base.getDeclaringType().isUnboundDeclaration() // Bases classes of constructed types aren't extracted properly. } @@ -493,7 +494,9 @@ class InvalidOverride extends MethodViolation { * A pointer type that does not have a pointee type. */ class InvalidPointerType extends TypeViolation { - InvalidPointerType() { exists(PointerType p | p = getType() | count(p.getReferentType()) != 1) } + InvalidPointerType() { + exists(PointerType p | p = this.getType() | count(p.getReferentType()) != 1) + } override string getMessage() { result = "Invalid Pointertype.getPointeeType()" } } @@ -502,7 +505,9 @@ class InvalidPointerType extends TypeViolation { * An array with an invalid `getElementType`. */ class ArrayTypeMissingElement extends TypeViolation { - ArrayTypeMissingElement() { exists(ArrayType t | t = getType() | count(t.getElementType()) != 1) } + ArrayTypeMissingElement() { + exists(ArrayType t | t = this.getType() | count(t.getElementType()) != 1) + } override string getMessage() { result = "Invalid ArrayType.getElementType()" } } @@ -511,7 +516,7 @@ class ArrayTypeMissingElement extends TypeViolation { * An array with an invalid `getRank`. */ class ArrayTypeInvalidRank extends TypeViolation { - ArrayTypeInvalidRank() { exists(ArrayType t | t = getType() | not t.getRank() > 0) } + ArrayTypeInvalidRank() { exists(ArrayType t | t = this.getType() | not t.getRank() > 0) } override string getMessage() { result = "Invalid ArrayType.getRank()" } } @@ -564,7 +569,7 @@ abstract class DeclarationViolation extends ConsistencyViolation, DeclarationChe /** Gets the member containing the potential violation. */ Declaration getDeclaration() { this = DeclarationCheck(result) } - override string toString() { result = getDeclaration().toString() } + override string toString() { result = this.getDeclaration().toString() } } /** @@ -572,7 +577,7 @@ abstract class DeclarationViolation extends ConsistencyViolation, DeclarationChe */ class PropertyWithNoAccessors extends DeclarationViolation { PropertyWithNoAccessors() { - exists(Property p | p = getDeclaration() | not exists(p.getAnAccessor())) + exists(Property p | p = this.getDeclaration() | not exists(p.getAnAccessor())) } override string getMessage() { result = "Property has no accessors" } @@ -646,7 +651,7 @@ class TypeMultiplyDefined extends TypeViolation, DisabledCheck { override string getMessage() { result = - "This type (" + getType().toStringWithTypes() + ") has " + + "This type (" + this.getType().toStringWithTypes() + ") has " + count(Type t | not t instanceof ConstructedGeneric and t.toStringWithTypes() = this.getType().toStringWithTypes() @@ -669,11 +674,11 @@ class MissingCilDeclaration extends ConsistencyViolation, MissingCSharpCheck { override string getMessage() { result = - "Cannot locate CIL for " + getDeclaration().toStringWithTypes() + " of class " + - getDeclaration().getPrimaryQlClasses() + "Cannot locate CIL for " + this.getDeclaration().toStringWithTypes() + " of class " + + this.getDeclaration().getPrimaryQlClasses() } - override string toString() { result = getDeclaration().toStringWithTypes() } + override string toString() { result = this.getDeclaration().toStringWithTypes() } } /** @@ -717,21 +722,23 @@ private predicate expectedCilDeclaration(CS::Declaration decl) { /** A member with an invalid name. */ class MemberWithInvalidName extends DeclarationViolation { MemberWithInvalidName() { - exists(string name | name = getDeclaration().(Member).getName() | + exists(string name | name = this.getDeclaration().(Member).getName() | exists(name.indexOf(".")) and not name = ".ctor" and not name = ".cctor" ) } - override string getMessage() { result = "Invalid name " + getDeclaration().(Member).getName() } + override string getMessage() { + result = "Invalid name " + this.getDeclaration().(Member).getName() + } } class ConstructedSourceDeclarationMethod extends MethodViolation { Method method; ConstructedSourceDeclarationMethod() { - method = getMethod() and + method = this.getMethod() and method = method.getUnboundDeclaration() and ( method instanceof ConstructedGeneric or @@ -751,7 +758,7 @@ class DeclarationWithMultipleLabels extends DeclarationViolation { } override string getMessage() { - result = "Multiple labels " + concat(getDeclaration().getLabel(), ", ") + result = "Multiple labels " + concat(this.getDeclaration().getLabel(), ", ") } } diff --git a/csharp/ql/lib/semmle/code/cil/ControlFlow.qll b/csharp/ql/lib/semmle/code/cil/ControlFlow.qll index 52a2ddc3376..8b6d6c70a05 100644 --- a/csharp/ql/lib/semmle/code/cil/ControlFlow.qll +++ b/csharp/ql/lib/semmle/code/cil/ControlFlow.qll @@ -23,13 +23,13 @@ class ControlFlowNode extends @cil_controlflow_node { int getPopCount() { result = 0 } /** Gets a successor of this node, if any. */ - final Instruction getASuccessor() { result = getASuccessorType(_) } + final Instruction getASuccessor() { result = this.getASuccessorType(_) } /** Gets a true successor of this node, if any. */ - final Instruction getTrueSuccessor() { result = getASuccessorType(any(TrueFlow f)) } + final Instruction getTrueSuccessor() { result = this.getASuccessorType(any(TrueFlow f)) } /** Gets a false successor of this node, if any. */ - final Instruction getFalseSuccessor() { result = getASuccessorType(any(FalseFlow f)) } + final Instruction getFalseSuccessor() { result = this.getASuccessorType(any(FalseFlow f)) } /** Gets a successor to this node, of type `type`, if any. */ cached @@ -57,7 +57,7 @@ class ControlFlowNode extends @cil_controlflow_node { } /** Gets an operand of this instruction, if any. */ - ControlFlowNode getAnOperand() { result = getOperand(_) } + ControlFlowNode getAnOperand() { result = this.getOperand(_) } /** Gets an expression that consumes the output of this instruction on the stack. */ Instruction getParentExpr() { this = result.getAnOperand() } @@ -86,17 +86,17 @@ class ControlFlowNode extends @cil_controlflow_node { ) } - private int getStackDelta() { result = getPushCount() - getPopCount() } + private int getStackDelta() { result = this.getPushCount() - this.getPopCount() } /** Gets the stack size before this instruction. */ - int getStackSizeBefore() { result = getAPredecessor().getStackSizeAfter() } + int getStackSizeBefore() { result = this.getAPredecessor().getStackSizeAfter() } /** Gets the stack size after this instruction. */ final int getStackSizeAfter() { // This is a guard to prevent ill formed programs // and other logic errors going into an infinite loop. - result in [0 .. getImplementation().getStackSize()] and - result = getStackSizeBefore() + getStackDelta() + result in [0 .. this.getImplementation().getStackSize()] and + result = this.getStackSizeBefore() + this.getStackDelta() } /** Gets the method containing this control flow node. */ diff --git a/csharp/ql/lib/semmle/code/cil/Declaration.qll b/csharp/ql/lib/semmle/code/cil/Declaration.qll index a747d4a6d80..178b5c9966e 100644 --- a/csharp/ql/lib/semmle/code/cil/Declaration.qll +++ b/csharp/ql/lib/semmle/code/cil/Declaration.qll @@ -68,7 +68,7 @@ class Member extends DotNet::Member, Declaration, @cil_member { /** Holds if this member has a security attribute. */ predicate hasSecurity() { cil_security(this) } - override Location getLocation() { result = getDeclaringType().getLocation() } + override Location getLocation() { result = this.getDeclaringType().getLocation() } } /** A property. */ @@ -87,24 +87,25 @@ class Property extends DotNet::Property, Member, CustomModifierReceiver, @cil_pr override Setter getSetter() { this = result.getProperty() } /** Gets an accessor of this property. */ - Accessor getAnAccessor() { result = getGetter() or result = getSetter() } + Accessor getAnAccessor() { result = this.getGetter() or result = this.getSetter() } - override string toString() { result = "property " + getName() } + override string toString() { result = "property " + this.getName() } override string toStringWithTypes() { result = - getType().toStringWithTypes() + " " + getDeclaringType().toStringWithTypes() + "." + getName() + this.getType().toStringWithTypes() + " " + this.getDeclaringType().toStringWithTypes() + "." + + this.getName() } } /** A property that is trivial (wraps a field). */ class TrivialProperty extends Property { TrivialProperty() { - getGetter().(TrivialGetter).getField() = getSetter().(TrivialSetter).getField() + this.getGetter().(TrivialGetter).getField() = this.getSetter().(TrivialSetter).getField() } /** Gets the underlying field of this property. */ - Field getField() { result = getGetter().(TrivialGetter).getField() } + Field getField() { result = this.getGetter().(TrivialGetter).getField() } } /** An event. */ @@ -125,9 +126,9 @@ class Event extends DotNet::Event, Member, @cil_event { /** Gets the raiser. */ Method getRaiser() { cil_raiser(this, result) } - override string toString() { result = "event " + getName() } + override string toString() { result = "event " + this.getName() } override string toStringWithTypes() { - result = getDeclaringType().toStringWithTypes() + "." + getName() + result = this.getDeclaringType().toStringWithTypes() + "." + this.getName() } } diff --git a/csharp/ql/lib/semmle/code/cil/Generics.qll b/csharp/ql/lib/semmle/code/cil/Generics.qll index a742a142cc4..2e702e68ffe 100644 --- a/csharp/ql/lib/semmle/code/cil/Generics.qll +++ b/csharp/ql/lib/semmle/code/cil/Generics.qll @@ -45,5 +45,5 @@ class ConstructedType extends ConstructedGeneric, Type { /** A constructed generic method. */ class ConstructedMethod extends ConstructedGeneric, Method { - final override UnboundGenericMethod getUnboundGeneric() { result = getUnboundMethod() } + final override UnboundGenericMethod getUnboundGeneric() { result = this.getUnboundMethod() } } diff --git a/csharp/ql/lib/semmle/code/cil/Instruction.qll b/csharp/ql/lib/semmle/code/cil/Instruction.qll index 3e620031264..fa9753e1f0c 100644 --- a/csharp/ql/lib/semmle/code/cil/Instruction.qll +++ b/csharp/ql/lib/semmle/code/cil/Instruction.qll @@ -4,15 +4,17 @@ private import CIL /** An instruction. */ class Instruction extends Element, ControlFlowNode, DataFlowNode, @cil_instruction { - override string toString() { result = getOpcodeName() } + override string toString() { result = this.getOpcodeName() } /** Gets a more verbose textual representation of this instruction. */ - string toStringExtra() { result = getIndex() + ": " + getOpcodeName() + getExtraStr() } + string toStringExtra() { + result = this.getIndex() + ": " + this.getOpcodeName() + this.getExtraStr() + } /** Gets the method containing this instruction. */ override MethodImplementation getImplementation() { cil_instruction(this, _, _, result) } - override Method getMethod() { result = getImplementation().getMethod() } + override Method getMethod() { result = this.getImplementation().getMethod() } /** * Gets the index of this instruction. @@ -30,7 +32,7 @@ class Instruction extends Element, ControlFlowNode, DataFlowNode, @cil_instructi string getExtra() { none() } private string getExtraStr() { - if exists(getExtra()) then result = " " + getExtra() else result = "" + if exists(this.getExtra()) then result = " " + this.getExtra() else result = "" } /** Gets the declaration accessed by this instruction, if any. */ @@ -39,8 +41,8 @@ class Instruction extends Element, ControlFlowNode, DataFlowNode, @cil_instructi /** Gets a successor instruction to this instruction. */ override Instruction getASuccessorType(FlowType t) { t instanceof NormalFlow and - canFlowNext() and - result = this.getImplementation().getInstruction(getIndex() + 1) + this.canFlowNext() and + result = this.getImplementation().getInstruction(this.getIndex() + 1) } /** Holds if this instruction passes control flow into the next instruction. */ @@ -61,7 +63,7 @@ class Instruction extends Element, ControlFlowNode, DataFlowNode, @cil_instructi override Location getALocation() { cil_instruction_location(this, result) // The source code, if available or - result = getImplementation().getLocation() // The containing assembly + result = this.getImplementation().getLocation() // The containing assembly } override Location getLocation() { result = Element.super.getLocation() } diff --git a/csharp/ql/lib/semmle/code/cil/InstructionGroups.qll b/csharp/ql/lib/semmle/code/cil/InstructionGroups.qll index e4aeb05a839..5dac4bf7291 100644 --- a/csharp/ql/lib/semmle/code/cil/InstructionGroups.qll +++ b/csharp/ql/lib/semmle/code/cil/InstructionGroups.qll @@ -14,7 +14,7 @@ class Expr extends DotNet::Expr, Instruction, @cil_expr { override Type getType() { result = Instruction.super.getType() } - override Method getEnclosingCallable() { result = getImplementation().getMethod() } + override Method getEnclosingCallable() { result = this.getImplementation().getMethod() } /** * The "parent" of a CIL expression is taken to be the instruction @@ -28,13 +28,13 @@ class Branch extends Instruction, @cil_jump { /** Gets the instruction that is jumped to. */ Instruction getTarget() { cil_jump(this, result) } - override string getExtra() { result = getTarget().getIndex() + ":" } + override string getExtra() { result = this.getTarget().getIndex() + ":" } } /** An instruction that unconditionally jumps to another instruction. */ class UnconditionalBranch extends Branch, @cil_unconditional_jump { override Instruction getASuccessorType(FlowType t) { - t instanceof NormalFlow and result = getTarget() + t instanceof NormalFlow and result = this.getTarget() } override predicate canFlowNext() { none() } @@ -43,9 +43,9 @@ class UnconditionalBranch extends Branch, @cil_unconditional_jump { /** An instruction that jumps to a target based on a condition. */ class ConditionalBranch extends Branch, @cil_conditional_jump { override Instruction getASuccessorType(FlowType t) { - t instanceof TrueFlow and result = getTarget() + t instanceof TrueFlow and result = this.getTarget() or - t instanceof FalseFlow and result = getImplementation().getInstruction(getIndex() + 1) + t instanceof FalseFlow and result = this.getImplementation().getInstruction(this.getIndex() + 1) } override int getPushCount() { result = 0 } @@ -61,7 +61,7 @@ class UnaryExpr extends Expr, @cil_unary_expr { override int getPopCount() { result = 1 } /** Gets the operand of this unary expression. */ - Expr getOperand() { result = getOperand(0) } + Expr getOperand() { result = this.getOperand(0) } } /** A binary expression that compares two values. */ @@ -73,8 +73,8 @@ class ComparisonOperation extends BinaryExpr, @cil_comparison_operation { class BinaryArithmeticExpr extends BinaryExpr, @cil_binary_arithmetic_operation { override Type getType() { exists(Type t0, Type t1 | - t0 = getOperand(0).getType().getUnderlyingType() and - t1 = getOperand(1).getType().getUnderlyingType() + t0 = this.getOperand(0).getType().getUnderlyingType() and + t1 = this.getOperand(1).getType().getUnderlyingType() | t0 = t1 and result = t0 or @@ -100,7 +100,7 @@ class UnaryBitwiseOperation extends UnaryExpr, @cil_unary_bitwise_operation { /** A unary expression that converts a value from one primitive type to another. */ class Conversion extends UnaryExpr, @cil_conversion_operation { /** Gets the expression being converted. */ - Expr getExpr() { result = getOperand(0) } + Expr getExpr() { result = this.getOperand(0) } } /** A branch that leaves the scope of a `Handler`. */ @@ -111,7 +111,7 @@ class Literal extends DotNet::Literal, Expr, @cil_literal { /** Gets the pushed value. */ override string getValue() { cil_value(this, result) } - override string getExtra() { result = getValue() } + override string getExtra() { result = this.getValue() } } /** An integer literal. */ @@ -149,44 +149,44 @@ class Call extends Expr, DotNet::Call, @cil_call_any { /** Gets the method that is called. */ override Method getTarget() { cil_access(this, result) } - override Method getARuntimeTarget() { result = getTarget().getAnOverrider*() } + override Method getARuntimeTarget() { result = this.getTarget().getAnOverrider*() } - override string getExtra() { result = getTarget().getQualifiedName() } + override string getExtra() { result = this.getTarget().getQualifiedName() } /** * Gets the return type of the call. Methods that do not return a value * return the `void` type, `System.Void`, although the value of `getPushCount` is * 0 in this case. */ - override Type getType() { result = getTarget().getReturnType() } + override Type getType() { result = this.getTarget().getReturnType() } // The number of items popped/pushed from the stack // depends on the target of the call. - override int getPopCount() { result = getTarget().getCallPopCount() } + override int getPopCount() { result = this.getTarget().getCallPopCount() } - override int getPushCount() { result = getTarget().getCallPushCount() } + override int getPushCount() { result = this.getTarget().getCallPushCount() } /** * Holds if this is a "tail call", meaning that control does not return to the * calling method. */ predicate isTailCall() { - getImplementation().getInstruction(getIndex() - 1) instanceof Opcodes::Tail + this.getImplementation().getInstruction(this.getIndex() - 1) instanceof Opcodes::Tail } /** Holds if this call is virtual and could go to an overriding method. */ predicate isVirtual() { none() } - override Expr getRawArgument(int i) { result = getOperand(getPopCount() - i - 1) } + override Expr getRawArgument(int i) { result = this.getOperand(this.getPopCount() - i - 1) } /** Gets the qualifier of this call, if any. */ - Expr getQualifier() { result = getRawArgument(0) and not getTarget().isStatic() } + Expr getQualifier() { result = this.getRawArgument(0) and not this.getTarget().isStatic() } override Expr getArgument(int i) { - if getTarget().isStatic() - then result = getRawArgument(i) + if this.getTarget().isStatic() + then result = this.getRawArgument(i) else ( - result = getRawArgument(i + 1) and i >= 0 + result = this.getRawArgument(i + 1) and i >= 0 ) } @@ -217,10 +217,10 @@ class VirtualCall extends Call { /** A read of an array element. */ class ReadArrayElement extends BinaryExpr, @cil_read_array { /** Gets the array being read. */ - Expr getArray() { result = getOperand(1) } + Expr getArray() { result = this.getOperand(1) } /** Gets the index into the array. */ - Expr getArrayIndex() { result = getOperand(0) } + Expr getArrayIndex() { result = this.getOperand(0) } } /** A write of an array element. */ @@ -233,14 +233,14 @@ class WriteArrayElement extends Instruction, @cil_write_array { /** A `return` statement. */ class Return extends Instruction, @cil_ret { /** Gets the expression being returned, if any. */ - Expr getExpr() { result = getOperand(0) } + Expr getExpr() { result = this.getOperand(0) } override predicate canFlowNext() { none() } } /** A `throw` statement. */ class Throw extends Instruction, DotNet::Throw, @cil_throw_any { - override Expr getExpr() { result = getOperand(0) } + override Expr getExpr() { result = this.getOperand(0) } override predicate canFlowNext() { none() } } @@ -250,10 +250,10 @@ class StoreIndirect extends Instruction, @cil_stind { override int getPopCount() { result = 2 } /** Gets the location to store the value at. */ - Expr getAddress() { result = getOperand(1) } + Expr getAddress() { result = this.getOperand(1) } /** Gets the value to store. */ - Expr getExpr() { result = getOperand(0) } + Expr getExpr() { result = this.getOperand(0) } } /** Loads a value from an address/location. */ diff --git a/csharp/ql/lib/semmle/code/cil/Instructions.qll b/csharp/ql/lib/semmle/code/cil/Instructions.qll index e385ceced31..5752ae45b20 100644 --- a/csharp/ql/lib/semmle/code/cil/Instructions.qll +++ b/csharp/ql/lib/semmle/code/cil/Instructions.qll @@ -83,21 +83,21 @@ module Opcodes { class Ldc_i4 extends IntLiteral, @cil_ldc_i4 { override string getOpcodeName() { result = "ldc.i4" } - override string getExtra() { result = getValue() } + override string getExtra() { result = this.getValue() } } /** An `ldc.i8` instruction. */ class Ldc_i8 extends IntLiteral, @cil_ldc_i8 { override string getOpcodeName() { result = "ldc.i8" } - override string getExtra() { result = getValue() } + override string getExtra() { result = this.getValue() } } /** An `ldc.i4.s` instruction. */ class Ldc_i4_s extends IntLiteral, @cil_ldc_i4_s { override string getOpcodeName() { result = "ldc.i4.s" } - override string getExtra() { result = getValue() } + override string getExtra() { result = this.getValue() } } /** An `ldnull` instruction. */ @@ -115,7 +115,7 @@ module Opcodes { class Ldc_r4 extends FloatLiteral, @cil_ldc_r4 { override string getOpcodeName() { result = "ldc.r4" } - override string getExtra() { result = getValue() } + override string getExtra() { result = this.getValue() } override Type getType() { result instanceof FloatType } } @@ -124,7 +124,7 @@ module Opcodes { class Ldc_r8 extends FloatLiteral, @cil_ldc_r8 { override string getOpcodeName() { result = "ldc.r8" } - override string getExtra() { result = getValue() } + override string getExtra() { result = this.getValue() } override Type getType() { result instanceof DoubleType } } @@ -199,9 +199,9 @@ module Opcodes { override string getOpcodeName() { result = "neg" } override NumericType getType() { - result = getOperand().getType() + result = this.getOperand().getType() or - getOperand().getType() instanceof Enum and result instanceof IntType + this.getOperand().getType() instanceof Enum and result instanceof IntType } } @@ -260,7 +260,7 @@ module Opcodes { override int getPushCount() { result = 2 } // This is the only instruction that pushes 2 items - override Type getType() { result = getOperand(0).getType() } + override Type getType() { result = this.getOperand(0).getType() } } /** A `ret` instruction. */ @@ -270,7 +270,7 @@ module Opcodes { override predicate canFlowNext() { none() } override int getPopCount() { - if getImplementation().getMethod().returnsVoid() then result = 0 else result = 1 + if this.getImplementation().getMethod().returnsVoid() then result = 0 else result = 1 } } @@ -283,7 +283,7 @@ module Opcodes { class Ldstr extends StringLiteral, @cil_ldstr { override string getOpcodeName() { result = "ldstr" } - override string getExtra() { result = "\"" + getValue() + "\"" } + override string getExtra() { result = "\"" + this.getValue() + "\"" } override Type getType() { result instanceof StringType } } @@ -427,11 +427,14 @@ module Opcodes { override Instruction getASuccessorType(FlowType t) { t instanceof NormalFlow and - (result = getTarget(_) or result = getImplementation().getInstruction(getIndex() + 1)) + ( + result = this.getTarget(_) or + result = this.getImplementation().getInstruction(this.getIndex() + 1) + ) } override string getExtra() { - result = concat(int n | exists(getTarget(n)) | getTarget(n).getIndex() + ":", " ") + result = concat(int n | exists(this.getTarget(n)) | this.getTarget(n).getIndex() + ":", " ") } } @@ -493,9 +496,9 @@ module Opcodes { // The number of items popped/pushed from the stack depends on the target of // the call. Also, we need to pop the function pointer itself too. - override int getPopCount() { result = getTargetType().getCallPopCount() + 1 } + override int getPopCount() { result = this.getTargetType().getCallPopCount() + 1 } - override int getPushCount() { result = getTargetType().getCallPushCount() } + override int getPushCount() { result = this.getTargetType().getCallPushCount() } } /** A `callvirt` instruction. */ @@ -524,49 +527,49 @@ module Opcodes { override BoolType getType() { exists(result) } /** Gets the type that is being tested against. */ - Type getTestedType() { result = getAccess() } + Type getTestedType() { result = this.getAccess() } - override string getExtra() { result = getTestedType().getQualifiedName() } + override string getExtra() { result = this.getTestedType().getQualifiedName() } } /** A `castclass` instruction. */ class Castclass extends UnaryExpr, @cil_castclass { override string getOpcodeName() { result = "castclass" } - override Type getType() { result = getAccess() } + override Type getType() { result = this.getAccess() } /** Gets the type that is being cast to. */ - Type getTestedType() { result = getAccess() } + Type getTestedType() { result = this.getAccess() } - override string getExtra() { result = getTestedType().getQualifiedName() } + override string getExtra() { result = this.getTestedType().getQualifiedName() } } /** An `stloc.0` instruction. */ class Stloc_0 extends LocalVariableWriteAccess, @cil_stloc_0 { override string getOpcodeName() { result = "stloc.0" } - override LocalVariable getTarget() { result = getImplementation().getLocalVariable(0) } + override LocalVariable getTarget() { result = this.getImplementation().getLocalVariable(0) } } /** An `stloc.1` instruction. */ class Stloc_1 extends LocalVariableWriteAccess, @cil_stloc_1 { override string getOpcodeName() { result = "stloc.1" } - override LocalVariable getTarget() { result = getImplementation().getLocalVariable(1) } + override LocalVariable getTarget() { result = this.getImplementation().getLocalVariable(1) } } /** An `stloc.2` instruction. */ class Stloc_2 extends LocalVariableWriteAccess, @cil_stloc_2 { override string getOpcodeName() { result = "stloc.2" } - override LocalVariable getTarget() { result = getImplementation().getLocalVariable(2) } + override LocalVariable getTarget() { result = this.getImplementation().getLocalVariable(2) } } /** An `stloc.3` instruction. */ class Stloc_3 extends LocalVariableWriteAccess, @cil_stloc_3 { override string getOpcodeName() { result = "stloc.3" } - override LocalVariable getTarget() { result = getImplementation().getLocalVariable(3) } + override LocalVariable getTarget() { result = this.getImplementation().getLocalVariable(3) } } /** An `stloc.s` instruction. */ @@ -587,28 +590,28 @@ module Opcodes { class Ldloc_0 extends LocalVariableReadAccess, @cil_ldloc_0 { override string getOpcodeName() { result = "ldloc.0" } - override LocalVariable getTarget() { result = getImplementation().getLocalVariable(0) } + override LocalVariable getTarget() { result = this.getImplementation().getLocalVariable(0) } } /** An `ldloc.1` instruction. */ class Ldloc_1 extends LocalVariableReadAccess, @cil_ldloc_1 { override string getOpcodeName() { result = "ldloc.1" } - override LocalVariable getTarget() { result = getImplementation().getLocalVariable(1) } + override LocalVariable getTarget() { result = this.getImplementation().getLocalVariable(1) } } /** An `ldloc.2` instruction. */ class Ldloc_2 extends LocalVariableReadAccess, @cil_ldloc_2 { override string getOpcodeName() { result = "ldloc.2" } - override LocalVariable getTarget() { result = getImplementation().getLocalVariable(2) } + override LocalVariable getTarget() { result = this.getImplementation().getLocalVariable(2) } } /** An `ldloc.3` instruction. */ class Ldloc_3 extends LocalVariableReadAccess, @cil_ldloc_3 { override string getOpcodeName() { result = "ldloc.3" } - override LocalVariable getTarget() { result = getImplementation().getLocalVariable(3) } + override LocalVariable getTarget() { result = this.getImplementation().getLocalVariable(3) } } /** An `ldloc.s` instruction. */ @@ -617,7 +620,7 @@ module Opcodes { override LocalVariable getTarget() { cil_access(this, result) } - override string getExtra() { result = "L" + getTarget().getIndex() } + override string getExtra() { result = "L" + this.getTarget().getIndex() } } /** An `ldloca.s` instruction. */ @@ -626,7 +629,7 @@ module Opcodes { override LocalVariable getTarget() { cil_access(this, result) } - override string getExtra() { result = "L" + getTarget().getIndex() } + override string getExtra() { result = "L" + this.getTarget().getIndex() } } /** An `ldloc` instruction. */ @@ -635,7 +638,7 @@ module Opcodes { override LocalVariable getTarget() { cil_access(this, result) } - override string getExtra() { result = "L" + getTarget().getIndex() } + override string getExtra() { result = "L" + this.getTarget().getIndex() } } /** An `ldarg.0` instruction. */ @@ -643,7 +646,7 @@ module Opcodes { override string getOpcodeName() { result = "ldarg.0" } override MethodParameter getTarget() { - result = getImplementation().getMethod().getRawParameter(0) + result = this.getImplementation().getMethod().getRawParameter(0) } } @@ -652,7 +655,7 @@ module Opcodes { override string getOpcodeName() { result = "ldarg.1" } override MethodParameter getTarget() { - result = getImplementation().getMethod().getRawParameter(1) + result = this.getImplementation().getMethod().getRawParameter(1) } } @@ -661,7 +664,7 @@ module Opcodes { override string getOpcodeName() { result = "ldarg.2" } override MethodParameter getTarget() { - result = getImplementation().getMethod().getRawParameter(2) + result = this.getImplementation().getMethod().getRawParameter(2) } } @@ -670,7 +673,7 @@ module Opcodes { override string getOpcodeName() { result = "ldarg.3" } override MethodParameter getTarget() { - result = getImplementation().getMethod().getRawParameter(3) + result = this.getImplementation().getMethod().getRawParameter(3) } } @@ -710,7 +713,7 @@ module Opcodes { override int getPopCount() { result = 1 } - override Expr getQualifier() { result = getOperand(0) } + override Expr getQualifier() { result = this.getOperand(0) } } /** An `ldflda` instruction. */ @@ -719,7 +722,7 @@ module Opcodes { override int getPopCount() { result = 1 } - override Expr getQualifier() { result = getOperand(0) } + override Expr getQualifier() { result = this.getOperand(0) } } /** An `ldsfld` instruction. */ @@ -746,9 +749,9 @@ module Opcodes { override int getPopCount() { result = 2 } - override Expr getQualifier() { result = getOperand(1) } + override Expr getQualifier() { result = this.getOperand(1) } - override Expr getExpr() { result = getOperand(0) } + override Expr getExpr() { result = this.getOperand(0) } } /** An `stsfld` instruction. */ @@ -759,7 +762,7 @@ module Opcodes { override Expr getQualifier() { none() } - override Expr getExpr() { result = getOperand(0) } + override Expr getExpr() { result = this.getOperand(0) } } /** A `newobj` instruction. */ @@ -772,7 +775,7 @@ module Opcodes { override Type getType() { result = this.getTarget().getDeclaringType() } - override Expr getArgument(int i) { result = getRawArgument(i) } + override Expr getArgument(int i) { result = this.getRawArgument(i) } pragma[noinline] private Parameter getARawTargetParameter() { result = this.getTarget().getARawParameter() } @@ -796,21 +799,21 @@ module Opcodes { class Box extends UnaryExpr, @cil_box { override string getOpcodeName() { result = "box" } - override Type getType() { result = getAccess() } + override Type getType() { result = this.getAccess() } } /** An `unbox.any` instruction. */ class Unbox_any extends UnaryExpr, @cil_unbox_any { override string getOpcodeName() { result = "unbox.any" } - override Type getType() { result = getAccess() } + override Type getType() { result = this.getAccess() } } /** An `unbox` instruction. */ class Unbox extends UnaryExpr, @cil_unbox { override string getOpcodeName() { result = "unbox" } - override Type getType() { result = getAccess() } + override Type getType() { result = this.getAccess() } } /** An `ldobj` instruction. */ @@ -820,7 +823,7 @@ module Opcodes { /** Gets the type of the object. */ Type getTarget() { cil_access(this, result) } - override Type getType() { result = getAccess() } + override Type getType() { result = this.getAccess() } } /** An `ldtoken` instruction. */ @@ -867,31 +870,31 @@ module Opcodes { // Note that this is technically wrong - it should be // result.(ArrayType).getElementType() = getAccess() // However the (ArrayType) may not be in the database. - result = getAccess() + result = this.getAccess() } - override string getExtra() { result = getType().getQualifiedName() } + override string getExtra() { result = this.getType().getQualifiedName() } } /** An `ldelem` instruction. */ class Ldelem extends ReadArrayElement, @cil_ldelem { override string getOpcodeName() { result = "ldelem" } - override Type getType() { result = getAccess() } + override Type getType() { result = this.getAccess() } } /** An `ldelem.ref` instruction. */ class Ldelem_ref extends ReadArrayElement, @cil_ldelem_ref { override string getOpcodeName() { result = "ldelem.ref" } - override Type getType() { result = getArray().getType() } + override Type getType() { result = this.getArray().getType() } } /** An `ldelema` instruction. */ class Ldelema extends ReadArrayElement, ReadRef, @cil_ldelema { override string getOpcodeName() { result = "ldelema" } - override Type getType() { result = getAccess() } + override Type getType() { result = this.getAccess() } } /** An `stelem.ref` instruction. */ @@ -1410,7 +1413,7 @@ module Opcodes { override int getPopCount() { result = 1 } - override Type getType() { result = getAccess() } + override Type getType() { result = this.getAccess() } } /** A `refanytype` instruction. */ diff --git a/csharp/ql/lib/semmle/code/cil/Method.qll b/csharp/ql/lib/semmle/code/cil/Method.qll index 82bde17a477..461a020972b 100644 --- a/csharp/ql/lib/semmle/code/cil/Method.qll +++ b/csharp/ql/lib/semmle/code/cil/Method.qll @@ -28,13 +28,13 @@ class MethodImplementation extends EntryPoint, @cil_method_implementation { LocalVariable getLocalVariable(int n) { cil_local_variable(result, this, n, _) } /** Gets a local variable of this implementation, if any. */ - LocalVariable getALocalVariable() { result = getLocalVariable(_) } + LocalVariable getALocalVariable() { result = this.getLocalVariable(_) } /** Gets an instruction in this implementation, if any. */ - Instruction getAnInstruction() { result = getInstruction(_) } + Instruction getAnInstruction() { result = this.getInstruction(_) } /** Gets the total number of instructions in this implementation. */ - int getNumberOfInstructions() { result = count(getAnInstruction()) } + int getNumberOfInstructions() { result = count(this.getAnInstruction()) } /** Gets the `i`th handler in this implementation. */ Handler getHandler(int i) { result.getImplementation() = this and result.getIndex() = i } @@ -49,7 +49,7 @@ class MethodImplementation extends EntryPoint, @cil_method_implementation { /** Gets the maximum stack size of this implementation. */ int getStackSize() { cil_method_stack_size(this, result) } - override string toString() { result = getMethod().toString() } + override string toString() { result = this.getMethod().toString() } /** Gets a string representing the disassembly of this implementation. */ string getDisassembly() { @@ -75,13 +75,13 @@ class Method extends DotNet::Callable, Element, Member, TypeContainer, DataFlowN MethodImplementation getAnImplementation() { result.getMethod() = this } /** Gets the "best" implementation of this method, if any. */ - BestImplementation getImplementation() { result = getAnImplementation() } + BestImplementation getImplementation() { result = this.getAnImplementation() } override Method getMethod() { result = this } override string getName() { cil_method(this, result, _, _) } - override string getUndecoratedName() { result = getName() } + override string getUndecoratedName() { result = this.getName() } override string toString() { result = this.getName() } @@ -92,25 +92,29 @@ class Method extends DotNet::Callable, Element, Member, TypeContainer, DataFlowN override Location getALocation() { cil_method_location(this.getUnboundDeclaration(), result) } override MethodParameter getParameter(int n) { - if isStatic() then result = getRawParameter(n) else (result = getRawParameter(n + 1) and n >= 0) + if this.isStatic() + then result = this.getRawParameter(n) + else ( + result = this.getRawParameter(n + 1) and n >= 0 + ) } - override Type getType() { result = getReturnType() } + override Type getType() { result = this.getReturnType() } /** Gets the return type of this method. */ override Type getReturnType() { cil_method(this, _, _, result) } /** Holds if the return type is `void`. */ - predicate returnsVoid() { getReturnType() instanceof VoidType } + predicate returnsVoid() { this.getReturnType() instanceof VoidType } /** Gets the number of stack items pushed in a call to this method. */ - int getCallPushCount() { if returnsVoid() then result = 0 else result = 1 } + int getCallPushCount() { if this.returnsVoid() then result = 0 else result = 1 } /** Gets the number of stack items popped in a call to this method. */ - int getCallPopCount() { result = count(getRawParameter(_)) } + int getCallPopCount() { result = count(this.getRawParameter(_)) } /** Gets a method called by this method. */ - Method getACallee() { result = getImplementation().getAnInstruction().(Call).getTarget() } + Method getACallee() { result = this.getImplementation().getAnInstruction().(Call).getTarget() } /** Holds if this method is `virtual`. */ predicate isVirtual() { cil_virtual(this) } @@ -129,43 +133,45 @@ class Method extends DotNet::Callable, Element, Member, TypeContainer, DataFlowN /** Gets the unbound declaration of this method, or the method itself. */ Method getUnboundMethod() { cil_method_source_declaration(this, result) } - override Method getUnboundDeclaration() { result = getUnboundMethod() } + override Method getUnboundDeclaration() { result = this.getUnboundMethod() } /** Holds if this method is an instance constructor. */ - predicate isInstanceConstructor() { isSpecial() and getName() = ".ctor" } + predicate isInstanceConstructor() { this.isSpecial() and this.getName() = ".ctor" } /** Holds if this method is a static class constructor. */ - predicate isStaticConstructor() { isSpecial() and getName() = ".cctor" } + predicate isStaticConstructor() { this.isSpecial() and this.getName() = ".cctor" } /** Holds if this method is a constructor (static or instance). */ - predicate isConstructor() { isStaticConstructor() or isInstanceConstructor() } + predicate isConstructor() { this.isStaticConstructor() or this.isInstanceConstructor() } /** Holds if this method is a destructor/finalizer. */ - predicate isFinalizer() { getOverriddenMethod*().getQualifiedName() = "System.Object.Finalize" } + predicate isFinalizer() { + this.getOverriddenMethod*().getQualifiedName() = "System.Object.Finalize" + } /** Holds if this method is an operator. */ - predicate isOperator() { isSpecial() and getName().matches("op\\_%") } + predicate isOperator() { this.isSpecial() and this.getName().matches("op\\_%") } /** Holds if this method is a getter. */ - predicate isGetter() { isSpecial() and getName().matches("get\\_%") } + predicate isGetter() { this.isSpecial() and this.getName().matches("get\\_%") } /** Holds if this method is a setter. */ - predicate isSetter() { isSpecial() and getName().matches("set\\_%") } + predicate isSetter() { this.isSpecial() and this.getName().matches("set\\_%") } /** Holds if this method is an adder/add event accessor. */ - predicate isAdder() { isSpecial() and getName().matches("add\\_%") } + predicate isAdder() { this.isSpecial() and this.getName().matches("add\\_%") } /** Holds if this method is a remover/remove event accessor. */ - predicate isRemove() { isSpecial() and getName().matches("remove\\_%") } + predicate isRemove() { this.isSpecial() and this.getName().matches("remove\\_%") } /** Holds if this method is an implicit conversion operator. */ - predicate isImplicitConversion() { isSpecial() and getName() = "op_Implicit" } + predicate isImplicitConversion() { this.isSpecial() and this.getName() = "op_Implicit" } /** Holds if this method is an explicit conversion operator. */ - predicate isExplicitConversion() { isSpecial() and getName() = "op_Explicit" } + predicate isExplicitConversion() { this.isSpecial() and this.getName() = "op_Explicit" } /** Holds if this method is a conversion operator. */ - predicate isConversion() { isImplicitConversion() or isExplicitConversion() } + predicate isConversion() { this.isImplicitConversion() or this.isExplicitConversion() } /** * Gets a method that is overridden, either in a base class @@ -176,7 +182,7 @@ class Method extends DotNet::Callable, Element, Member, TypeContainer, DataFlowN /** Gets a method that overrides this method, if any. */ final Method getAnOverrider() { result.getOverriddenMethod() = this } - override predicate hasBody() { exists(getImplementation()) } + override predicate hasBody() { exists(this.getImplementation()) } override predicate canReturn(DotNet::Expr expr) { exists(Return ret | ret.getImplementation() = this.getImplementation() and expr = ret.getExpr()) @@ -206,7 +212,7 @@ class InstanceConstructor extends Constructor { /** A method that always returns the `this` parameter. */ class ChainingMethod extends Method { ChainingMethod() { - forex(Return ret | ret = getImplementation().getAnInstruction() | + forex(Return ret | ret = this.getImplementation().getAnInstruction() | ret.getExpr() instanceof ThisAccess ) } @@ -231,7 +237,7 @@ class Getter extends Accessor { */ class TrivialGetter extends Method { TrivialGetter() { - exists(MethodImplementation impl | impl = getAnImplementation() | + exists(MethodImplementation impl | impl = this.getAnImplementation() | impl.getInstruction(0) instanceof ThisAccess and impl.getInstruction(1) instanceof FieldReadAccess and impl.getInstruction(2) instanceof Return @@ -239,7 +245,9 @@ class TrivialGetter extends Method { } /** Gets the underlying field of this getter. */ - Field getField() { getImplementation().getAnInstruction().(FieldReadAccess).getTarget() = result } + Field getField() { + this.getImplementation().getAnInstruction().(FieldReadAccess).getTarget() = result + } } /** A setter. */ @@ -262,7 +270,7 @@ class Setter extends Accessor { */ class TrivialSetter extends Method { TrivialSetter() { - exists(MethodImplementation impl | impl = getImplementation() | + exists(MethodImplementation impl | impl = this.getImplementation() | impl.getInstruction(0) instanceof ThisAccess and impl.getInstruction(1).(ParameterReadAccess).getTarget().getIndex() = 1 and impl.getInstruction(2) instanceof FieldWriteAccess @@ -271,7 +279,7 @@ class TrivialSetter extends Method { /** Gets the underlying field of this setter. */ Field getField() { - result = getImplementation().getAnInstruction().(FieldWriteAccess).getTarget() + result = this.getImplementation().getAnInstruction().(FieldWriteAccess).getTarget() } } @@ -283,5 +291,5 @@ class Operator extends Method { Operator() { this.isOperator() } /** Gets the name of the implementing method (for compatibility with C# data model). */ - string getFunctionName() { result = getName() } + string getFunctionName() { result = this.getName() } } diff --git a/csharp/ql/lib/semmle/code/cil/Type.qll b/csharp/ql/lib/semmle/code/cil/Type.qll index a081d62b7ee..7aeaf9a6495 100644 --- a/csharp/ql/lib/semmle/code/cil/Type.qll +++ b/csharp/ql/lib/semmle/code/cil/Type.qll @@ -19,7 +19,7 @@ class TypeContainer extends DotNet::NamedElement, @cil_type_container { /** A namespace. */ class Namespace extends DotNet::Namespace, TypeContainer, @namespace { - override string toString() { result = getQualifiedName() } + override string toString() { result = this.getQualifiedName() } override Namespace getParent() { result = this.getParentNamespace() } @@ -39,7 +39,7 @@ class Type extends DotNet::Type, Declaration, TypeContainer, @cil_type { override string toString() { result = this.getName() } /** Gets the containing type of this type, if any. */ - override Type getDeclaringType() { result = getParent() } + override Type getDeclaringType() { result = this.getParent() } /** Gets a member of this type, if any. */ Member getAMember() { result.getDeclaringType() = this } @@ -96,13 +96,13 @@ class Type extends DotNet::Type, Declaration, TypeContainer, @cil_type { Type getABaseInterface() { cil_base_interface(this, result) } /** Gets an immediate base type of this type, if any. */ - Type getABaseType() { result = getBaseClass() or result = getABaseInterface() } + Type getABaseType() { result = this.getBaseClass() or result = this.getABaseInterface() } /** Gets an immediate subtype of this type, if any. */ Type getASubtype() { result.getABaseType() = this } /** Gets the namespace directly containing this type, if any. */ - Namespace getNamespace() { result = getParent() } + Namespace getNamespace() { result = this.getParent() } /** * Gets an index for implicit conversions. A type can be converted to another numeric type diff --git a/csharp/ql/lib/semmle/code/cil/Types.qll b/csharp/ql/lib/semmle/code/cil/Types.qll index d4d9342b73d..1dfaa0191a1 100644 --- a/csharp/ql/lib/semmle/code/cil/Types.qll +++ b/csharp/ql/lib/semmle/code/cil/Types.qll @@ -12,7 +12,7 @@ class TypeParameter extends DotNet::TypeParameter, Type, @cil_typeparameter { /** Gets the generic type/method declaring this type parameter. */ TypeContainer getGeneric() { cil_type_parameter(result, _, this) } - override Location getLocation() { result = getParent().getLocation() } + override Location getLocation() { result = this.getParent().getLocation() } /** Holds if this type parameter has the `new` constraint. */ predicate isDefaultConstructible() { cil_typeparam_new(this) } @@ -34,11 +34,11 @@ class TypeParameter extends DotNet::TypeParameter, Type, @cil_typeparameter { /** A value or reference type. */ class ValueOrRefType extends DotNet::ValueOrRefType, Type, @cil_valueorreftype { - override ValueOrRefType getDeclaringType() { result = getParent() } + override ValueOrRefType getDeclaringType() { result = this.getParent() } override string getUndecoratedName() { cil_type(this, result, _, _, _) } - override Namespace getDeclaringNamespace() { result = getNamespace() } + override Namespace getDeclaringNamespace() { result = this.getNamespace() } override ValueOrRefType getABaseType() { result = Type.super.getABaseType() } } @@ -79,7 +79,7 @@ class ArrayType extends DotNet::ArrayType, Type, @cil_array_type { override string toStringWithTypes() { result = DotNet::ArrayType.super.toStringWithTypes() } - override Location getLocation() { result = getElementType().getLocation() } + override Location getLocation() { result = this.getElementType().getLocation() } override ValueOrRefType getABaseType() { result = Type.super.getABaseType() } } @@ -92,7 +92,7 @@ class PointerType extends DotNet::PointerType, PrimitiveType, @cil_pointer_type override string getName() { result = DotNet::PointerType.super.getName() } - override Location getLocation() { result = getReferentType().getLocation() } + override Location getLocation() { result = this.getReferentType().getLocation() } override string toString() { result = DotNet::PointerType.super.toString() } @@ -312,13 +312,13 @@ class FunctionPointerType extends Type, CustomModifierReceiver, Parameterizable, override string toString() { result = Type.super.toString() } /** Holds if the return type is `void`. */ - predicate returnsVoid() { getReturnType() instanceof VoidType } + predicate returnsVoid() { this.getReturnType() instanceof VoidType } /** Gets the number of stack items pushed in a call to this method. */ - int getCallPushCount() { if returnsVoid() then result = 0 else result = 1 } + int getCallPushCount() { if this.returnsVoid() then result = 0 else result = 1 } /** Gets the number of stack items popped in a call to this method. */ - int getCallPopCount() { result = count(getRawParameter(_)) } + int getCallPopCount() { result = count(this.getRawParameter(_)) } - override string getLabel() { result = getName() } + override string getLabel() { result = this.getName() } } diff --git a/csharp/ql/lib/semmle/code/cil/Variable.qll b/csharp/ql/lib/semmle/code/cil/Variable.qll index 3a247e1f0d1..604f2c2b646 100644 --- a/csharp/ql/lib/semmle/code/cil/Variable.qll +++ b/csharp/ql/lib/semmle/code/cil/Variable.qll @@ -17,10 +17,10 @@ class Variable extends DotNet::Variable, Declaration, DataFlowNode, @cil_variabl VariableAccess getAnAccess() { result.getTarget() = this } /** Gets a read access to this variable, if any. */ - ReadAccess getARead() { result = getAnAccess() } + ReadAccess getARead() { result = this.getAnAccess() } /** Gets a write access to this variable, if any. */ - WriteAccess getAWrite() { result = getAnAccess() } + WriteAccess getAWrite() { result = this.getAnAccess() } override string toString() { result = Declaration.super.toString() } @@ -40,20 +40,21 @@ class StackVariable extends Variable, @cil_stack_variable { class LocalVariable extends StackVariable, @cil_local_variable { override string toString() { result = - "Local variable " + getIndex() + " of method " + getImplementation().getMethod().getName() + "Local variable " + this.getIndex() + " of method " + + this.getImplementation().getMethod().getName() } /** Gets the method implementation defining this local variable. */ MethodImplementation getImplementation() { this = result.getALocalVariable() } /** Gets the index number of this local variable. This is not usually significant. */ - int getIndex() { this = getImplementation().getLocalVariable(result) } + int getIndex() { this = this.getImplementation().getLocalVariable(result) } override Type getType() { cil_local_variable(this, _, _, result) } - override Location getLocation() { result = getImplementation().getLocation() } + override Location getLocation() { result = this.getImplementation().getLocation() } - override Method getMethod() { result = getImplementation().getMethod() } + override Method getMethod() { result = this.getImplementation().getMethod() } } /** A parameter of a `Method` or `FunctionPointerType`. */ @@ -64,7 +65,7 @@ class Parameter extends DotNet::Parameter, CustomModifierReceiver, @cil_paramete int getIndex() { cil_parameter(this, _, result, _) } override string toString() { - result = "Parameter " + getIndex() + " of " + getDeclaringElement().getName() + result = "Parameter " + this.getIndex() + " of " + this.getDeclaringElement().getName() } override Type getType() { cil_parameter(this, _, _, result) } @@ -82,23 +83,25 @@ class Parameter extends DotNet::Parameter, CustomModifierReceiver, @cil_paramete predicate hasInFlag() { cil_parameter_in(this) } /** Holds if this parameter has C# `out` semantics. */ - override predicate isOut() { hasOutFlag() and not hasInFlag() } + override predicate isOut() { this.hasOutFlag() and not this.hasInFlag() } /** Holds if this parameter has C# `ref` semantics. */ - override predicate isRef() { hasOutFlag() and hasInFlag() } + override predicate isRef() { this.hasOutFlag() and this.hasInFlag() } - override string toStringWithTypes() { result = getPrefix() + getType().toStringWithTypes() } + override string toStringWithTypes() { + result = this.getPrefix() + this.getType().toStringWithTypes() + } private string getPrefix() { - if isOut() + if this.isOut() then result = "out " else - if isRef() + if this.isRef() then result = "ref " else result = "" } - override Location getLocation() { result = getDeclaringElement().getLocation() } + override Location getLocation() { result = this.getDeclaringElement().getLocation() } } /** A method parameter. */ @@ -110,11 +113,11 @@ class MethodParameter extends Parameter, StackVariable { /** Gets a parameter in an overridden method. */ MethodParameter getOverriddenParameter() { - result = getMethod().getOverriddenMethod().getRawParameter(getRawPosition()) + result = this.getMethod().getOverriddenMethod().getRawParameter(this.getRawPosition()) } override MethodParameter getUnboundDeclaration() { - result = getMethod().getUnboundDeclaration().getRawParameter(getRawPosition()) + result = this.getMethod().getUnboundDeclaration().getRawParameter(this.getRawPosition()) } override string toString() { result = Parameter.super.toString() } @@ -136,10 +139,10 @@ class ThisParameter extends MethodParameter { /** A field. */ class Field extends DotNet::Field, Variable, Member, CustomModifierReceiver, @cil_field { - override string toString() { result = getName() } + override string toString() { result = this.getName() } override string toStringWithTypes() { - result = getDeclaringType().toStringWithTypes() + "." + getName() + result = this.getDeclaringType().toStringWithTypes() + "." + this.getName() } override string getName() { cil_field(this, _, result, _) } @@ -148,5 +151,5 @@ class Field extends DotNet::Field, Variable, Member, CustomModifierReceiver, @ci override ValueOrRefType getDeclaringType() { cil_field(this, result, _, _) } - override Location getLocation() { result = getDeclaringType().getLocation() } + override Location getLocation() { result = this.getDeclaringType().getLocation() } } diff --git a/csharp/ql/lib/semmle/code/cil/internal/SsaImplCommon.qll b/csharp/ql/lib/semmle/code/cil/internal/SsaImplCommon.qll index 884f4406d01..395cb5cb171 100644 --- a/csharp/ql/lib/semmle/code/cil/internal/SsaImplCommon.qll +++ b/csharp/ql/lib/semmle/code/cil/internal/SsaImplCommon.qll @@ -156,6 +156,7 @@ private predicate dominatesPredecessor(BasicBlock bb1, BasicBlock bb2) { } /** Holds if `df` is in the dominance frontier of `bb`. */ +pragma[noinline] private predicate inDominanceFrontier(BasicBlock bb, BasicBlock df) { dominatesPredecessor(bb, df) and not strictlyDominates(bb, df) diff --git a/csharp/ql/lib/semmle/code/csharp/AnnotatedType.qll b/csharp/ql/lib/semmle/code/csharp/AnnotatedType.qll index 37aa2b23410..8afdbd0d4a3 100644 --- a/csharp/ql/lib/semmle/code/csharp/AnnotatedType.qll +++ b/csharp/ql/lib/semmle/code/csharp/AnnotatedType.qll @@ -67,7 +67,7 @@ private module Annotations { Nullability() { this = TNullability(nullability) } - override string toString() { result = getMemberString() + getSelfNullability() } + override string toString() { result = this.getMemberString() + this.getSelfNullability() } language[monotonicAggregates] private string getMemberString() { @@ -125,7 +125,9 @@ private module Annotations { } /** Gets a textual representation of this type annotation. */ - string toString() { result = getTypePrefix() + getNullability() + getTypeSuffix() } + string toString() { + result = this.getTypePrefix() + this.getNullability() + this.getTypeSuffix() + } private int getFlags() { this = TAnnotationFlags(result, _) } @@ -136,7 +138,7 @@ private module Annotations { /** Gets an annotation in this set of annotations. */ TypeAnnotation getAnAnnotation() { - isSet(result.getBit()) + this.isSet(result.getBit()) or result = this.getNullability() } @@ -298,7 +300,7 @@ class AnnotatedType extends TAnnotatedType { /** Gets a textual representation of this annotated type. */ string toString() { result = - annotations.getTypePrefix() + getUnderlyingType().toStringWithTypes() + + annotations.getTypePrefix() + this.getUnderlyingType().toStringWithTypes() + annotations.getTypeSuffix() } @@ -327,7 +329,7 @@ class AnnotatedType extends TAnnotatedType { /** Gets a type annotation of this annotated type. */ private Annotations::TypeAnnotation getAnAnnotation() { - result = getAnnotations().getAnAnnotation() + result = this.getAnnotations().getAnAnnotation() } /** Holds if the type is a non-nullable reference, for example, `string` in a nullable-enabled context. */ @@ -376,7 +378,7 @@ class AnnotatedArrayType extends AnnotatedType { private string getDimensionString(AnnotatedType elementType) { exists(AnnotatedType et, string res | - et = getElementType() and + et = this.getElementType() and res = type.getArraySuffix() and if et.getUnderlyingType() instanceof ArrayType and not et.isNullableRefType() then result = res + et.(AnnotatedArrayType).getDimensionString(elementType) diff --git a/csharp/ql/lib/semmle/code/csharp/Attribute.qll b/csharp/ql/lib/semmle/code/csharp/Attribute.qll index 2085c4c650a..dae9f8a9fad 100644 --- a/csharp/ql/lib/semmle/code/csharp/Attribute.qll +++ b/csharp/ql/lib/semmle/code/csharp/Attribute.qll @@ -25,7 +25,7 @@ class Attributable extends @attributable { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -89,7 +89,7 @@ class Attribute extends TopLevelExprParent, @attribute { override Location getALocation() { attribute_location(this, result) } override string toString() { - exists(string type, string name | type = getType().getName() | + exists(string type, string name | type = this.getType().getName() | (if type.matches("%Attribute") then name = type.prefix(type.length() - 9) else name = type) and result = "[" + name + "(...)]" ) diff --git a/csharp/ql/lib/semmle/code/csharp/Callable.qll b/csharp/ql/lib/semmle/code/csharp/Callable.qll index 133ae86d551..41641a9d032 100644 --- a/csharp/ql/lib/semmle/code/csharp/Callable.qll +++ b/csharp/ql/lib/semmle/code/csharp/Callable.qll @@ -117,7 +117,7 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal final BlockStmt getAStatementBody() { result = this.getStatementBody() } /** Holds if this callable has a statement body. */ - final predicate hasStatementBody() { exists(getStatementBody()) } + final predicate hasStatementBody() { exists(this.getStatementBody()) } /** * Gets the expression body of this callable (if any), specified by `=>`. @@ -157,7 +157,7 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal deprecated final Expr getAnExpressionBody() { result = this.getExpressionBody() } /** Holds if this callable has an expression body. */ - final predicate hasExpressionBody() { exists(getExpressionBody()) } + final predicate hasExpressionBody() { exists(this.getExpressionBody()) } /** Gets the entry point in the control graph for this callable. */ ControlFlow::Nodes::EntryNode getEntryPoint() { result.getCallable() = this } @@ -218,7 +218,9 @@ class Callable extends DotNet::Callable, Parameterizable, ExprOrStmtParent, @cal exists(YieldReturnStmt yield | yield.getEnclosingCallable() = this | e = yield.getExpr()) } - override string toStringWithTypes() { result = getName() + "(" + parameterTypesToString() + ")" } + override string toStringWithTypes() { + result = this.getName() + "(" + this.parameterTypesToString() + ")" + } /** Gets a `Call` that has this callable as a target. */ Call getACall() { this = result.getTarget() } @@ -270,18 +272,18 @@ class Method extends Callable, Virtualizable, Attributable, @method { override Location getALocation() { method_location(this, result) } /** Holds if this method is an extension method. */ - predicate isExtensionMethod() { getParameter(0).hasExtensionMethodModifier() } + predicate isExtensionMethod() { this.getParameter(0).hasExtensionMethodModifier() } /** Gets the type of the `params` parameter of this method, if any. */ Type getParamsType() { - exists(Parameter last | last = getParameter(getNumberOfParameters() - 1) | + exists(Parameter last | last = this.getParameter(this.getNumberOfParameters() - 1) | last.isParams() and result = last.getType().(ArrayType).getElementType() ) } /** Holds if this method has a `params` parameter. */ - predicate hasParams() { exists(getParamsType()) } + predicate hasParams() { exists(this.getParamsType()) } // Remove when `Callable.isOverridden()` is removed override predicate isOverridden() { Virtualizable.super.isOverridden() } @@ -316,7 +318,7 @@ class ExtensionMethod extends Method { /** Gets the type being extended by this method. */ pragma[noinline] - Type getExtendedType() { result = getParameter(0).getType() } + Type getExtendedType() { result = this.getParameter(0).getType() } override string getAPrimaryQlClass() { result = "ExtensionMethod" } } @@ -355,7 +357,7 @@ class Constructor extends DotNet::Constructor, Callable, Member, Attributable, @ ConstructorInitializer getInitializer() { result = this.getChildExpr(-1) } /** Holds if this constructor has an initializer. */ - predicate hasInitializer() { exists(getInitializer()) } + predicate hasInitializer() { exists(this.getInitializer()) } override ValueOrRefType getDeclaringType() { constructors(this, _, result, _) } @@ -467,7 +469,7 @@ class Operator extends Callable, Member, Attributable, @operator { override string toString() { result = Callable.super.toString() } - override Parameter getRawParameter(int i) { result = getParameter(i) } + override Parameter getRawParameter(int i) { result = this.getParameter(i) } } /** A clone method on a record. */ @@ -999,10 +1001,10 @@ class LocalFunction extends Callable, Modifiable, Attributable, @local_function override Type getReturnType() { local_functions(this, _, result, _) } - override Element getParent() { result = getStatement().getParent() } + override Element getParent() { result = this.getStatement().getParent() } /** Gets the local function statement defining this function. */ - LocalFunctionStmt getStatement() { result.getLocalFunction() = getUnboundDeclaration() } + LocalFunctionStmt getStatement() { result.getLocalFunction() = this.getUnboundDeclaration() } override Callable getEnclosingCallable() { result = this.getStatement().getEnclosingCallable() } @@ -1011,9 +1013,9 @@ class LocalFunction extends Callable, Modifiable, Attributable, @local_function name = this.getName() } - override Location getALocation() { result = getStatement().getALocation() } + override Location getALocation() { result = this.getStatement().getALocation() } - override Parameter getRawParameter(int i) { result = getParameter(i) } + override Parameter getRawParameter(int i) { result = this.getParameter(i) } override string getAPrimaryQlClass() { result = "LocalFunction" } diff --git a/csharp/ql/lib/semmle/code/csharp/Comments.qll b/csharp/ql/lib/semmle/code/csharp/Comments.qll index 41f4e5b0be8..9a611b851e8 100644 --- a/csharp/ql/lib/semmle/code/csharp/Comments.qll +++ b/csharp/ql/lib/semmle/code/csharp/Comments.qll @@ -11,7 +11,7 @@ import Location /** * A single line of comment. * - * Either a single line comment (`SingleLineComment`), an XML comment (`XmlComment`), + * Either a single line comment (`SinglelineComment`), an XML comment (`XmlComment`), * or a line in a multi-line comment (`MultilineComment`). */ class CommentLine extends @commentline { diff --git a/csharp/ql/lib/semmle/code/csharp/Conversion.qll b/csharp/ql/lib/semmle/code/csharp/Conversion.qll index 0c560d1d86f..d62055b6a17 100644 --- a/csharp/ql/lib/semmle/code/csharp/Conversion.qll +++ b/csharp/ql/lib/semmle/code/csharp/Conversion.qll @@ -552,11 +552,16 @@ private predicate defaultDynamicConversion(Type fromType, Type toType) { fromType instanceof RefType and toType instanceof DynamicType } +pragma[noinline] +private predicate systemDelegateBaseType(RefType t) { + t = any(SystemDelegateClass c).getABaseType*() +} + // This is a deliberate, small cartesian product, so we have manually lifted it to force the // evaluator to evaluate it in its entirety, rather than trying to optimize it in context. pragma[noinline] private predicate defaultDelegateConversion(RefType fromType, RefType toType) { - fromType instanceof DelegateType and toType = any(SystemDelegateClass c).getABaseType*() + fromType instanceof DelegateType and systemDelegateBaseType(toType) } private predicate convRefTypeRefType(RefType fromType, RefType toType) { diff --git a/csharp/ql/lib/semmle/code/csharp/Element.qll b/csharp/ql/lib/semmle/code/csharp/Element.qll index fbd96f6086d..390a7b16632 100644 --- a/csharp/ql/lib/semmle/code/csharp/Element.qll +++ b/csharp/ql/lib/semmle/code/csharp/Element.qll @@ -31,7 +31,7 @@ class Element extends DotNet::Element, @element { Element getParent() { result.getAChild() = this } /** Gets a child of this element, if any. */ - Element getAChild() { result = getChild(_) } + Element getAChild() { result = this.getChild(_) } /** Gets the `i`th child of this element (zero-based). */ Element getChild(int i) { none() } diff --git a/csharp/ql/lib/semmle/code/csharp/Event.qll b/csharp/ql/lib/semmle/code/csharp/Event.qll index 7cbfda76877..810cffa927a 100644 --- a/csharp/ql/lib/semmle/code/csharp/Event.qll +++ b/csharp/ql/lib/semmle/code/csharp/Event.qll @@ -29,10 +29,10 @@ class Event extends DeclarationWithAccessors, @event { EventAccessor getAnEventAccessor() { result.getDeclaration() = this } /** Gets the `add` accessor of this event, if any. */ - AddEventAccessor getAddEventAccessor() { result = getAnEventAccessor() } + AddEventAccessor getAddEventAccessor() { result = this.getAnEventAccessor() } /** Gets the `remove` accessor of this event, if any. */ - RemoveEventAccessor getRemoveEventAccessor() { result = getAnEventAccessor() } + RemoveEventAccessor getRemoveEventAccessor() { result = this.getAnEventAccessor() } /** * Holds if this event can be used like a field within its declaring type @@ -111,9 +111,9 @@ class EventAccessor extends Accessor, @event_accessor { * ``` */ class AddEventAccessor extends EventAccessor, @add_event_accessor { - override string getName() { result = "add" + "_" + getDeclaration().getName() } + override string getName() { result = "add" + "_" + this.getDeclaration().getName() } - override string getUndecoratedName() { result = "add" + "_" + getDeclaration().getName() } + override string getUndecoratedName() { result = "add" + "_" + this.getDeclaration().getName() } override string getAPrimaryQlClass() { result = "AddEventAccessor" } } @@ -132,9 +132,9 @@ class AddEventAccessor extends EventAccessor, @add_event_accessor { * ``` */ class RemoveEventAccessor extends EventAccessor, @remove_event_accessor { - override string getName() { result = "remove" + "_" + getDeclaration().getName() } + override string getName() { result = "remove" + "_" + this.getDeclaration().getName() } - override string getUndecoratedName() { result = "remove" + "_" + getDeclaration().getName() } + override string getUndecoratedName() { result = "remove" + "_" + this.getDeclaration().getName() } override string getAPrimaryQlClass() { result = "RemoveEventAccessor" } } diff --git a/csharp/ql/lib/semmle/code/csharp/File.qll b/csharp/ql/lib/semmle/code/csharp/File.qll index 8ee61b29085..55fd2ccdc81 100644 --- a/csharp/ql/lib/semmle/code/csharp/File.qll +++ b/csharp/ql/lib/semmle/code/csharp/File.qll @@ -33,7 +33,7 @@ class Container extends @container { /** * Gets a URL representing the location of this container. * - * For more information see [Providing URLs](https://help.semmle.com/QL/learn-ql/ql/locations.html#providing-urls). + * For more information see [Providing URLs](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/#providing-urls). */ string getURL() { none() } @@ -47,7 +47,7 @@ class Container extends @container { */ string getRelativePath() { exists(string absPath, string pref | - absPath = getAbsolutePath() and sourceLocationPrefix(pref) + absPath = this.getAbsolutePath() and sourceLocationPrefix(pref) | absPath = pref and result = "" or @@ -74,7 +74,7 @@ class Container extends @container { * */ string getBaseName() { - result = getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1) + result = this.getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1) } /** @@ -100,7 +100,9 @@ class Container extends @container { * "/tmp/x.tar.gz""gz" * */ - string getExtension() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) } + string getExtension() { + result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) + } /** * Gets the stem of this container, that is, the prefix of its base name up to @@ -119,7 +121,9 @@ class Container extends @container { * "/tmp/x.tar.gz""x.tar" * */ - string getStem() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) } + string getStem() { + result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) + } /** Gets the parent container of this file or folder, if any. */ Container getParentContainer() { containerparent(result, this) } @@ -128,52 +132,52 @@ class Container extends @container { Container getAChildContainer() { this = result.getParentContainer() } /** Gets a file in this container. */ - File getAFile() { result = getAChildContainer() } + File getAFile() { result = this.getAChildContainer() } /** Gets the file in this container that has the given `baseName`, if any. */ File getFile(string baseName) { - result = getAFile() and + result = this.getAFile() and result.getBaseName() = baseName } /** Gets a sub-folder in this container. */ - Folder getAFolder() { result = getAChildContainer() } + Folder getAFolder() { result = this.getAChildContainer() } /** Gets the sub-folder in this container that has the given `baseName`, if any. */ Folder getFolder(string baseName) { - result = getAFolder() and + result = this.getAFolder() and result.getBaseName() = baseName } /** Gets the file or sub-folder in this container that has the given `name`, if any. */ Container getChildContainer(string name) { - result = getAChildContainer() and + result = this.getAChildContainer() and result.getBaseName() = name } /** Gets the file in this container that has the given `stem` and `extension`, if any. */ File getFile(string stem, string extension) { - result = getAChildContainer() and + result = this.getAChildContainer() and result.getStem() = stem and result.getExtension() = extension } /** Gets a sub-folder contained in this container. */ - Folder getASubFolder() { result = getAChildContainer() } + Folder getASubFolder() { result = this.getAChildContainer() } /** * Gets a textual representation of the path of this container. * * This is the absolute path of the container. */ - string toString() { result = getAbsolutePath() } + string toString() { result = this.getAbsolutePath() } } /** A folder. */ class Folder extends Container, @folder { override string getAbsolutePath() { folders(this, result) } - override string getURL() { result = "folder://" + getAbsolutePath() } + override string getURL() { result = "folder://" + this.getAbsolutePath() } } /** A file. */ diff --git a/csharp/ql/lib/semmle/code/csharp/Generics.qll b/csharp/ql/lib/semmle/code/csharp/Generics.qll index 25a3679715b..9190523e3c0 100644 --- a/csharp/ql/lib/semmle/code/csharp/Generics.qll +++ b/csharp/ql/lib/semmle/code/csharp/Generics.qll @@ -71,14 +71,14 @@ class ConstructedGeneric extends DotNet::ConstructedGeneric, Generic { override UnboundGeneric getUnboundGeneric() { constructed_generic(this, result) } override UnboundGeneric getUnboundDeclaration() { - result = getUnboundGeneric().getUnboundDeclaration() + result = this.getUnboundGeneric().getUnboundDeclaration() } override int getNumberOfTypeArguments() { result = count(int i | type_arguments(_, i, this)) } override Type getTypeArgument(int i) { none() } - override Type getATypeArgument() { result = getTypeArgument(_) } + override Type getATypeArgument() { result = this.getTypeArgument(_) } /** Gets the annotated type of type argument `i`. */ final AnnotatedType getAnnotatedTypeArgument(int i) { result.appliesToTypeArgument(this, i) } @@ -141,7 +141,7 @@ class UnboundGenericType extends ValueOrRefType, UnboundGeneric { result = ValueOrRefType.super.getUnboundDeclaration() } - final override Type getChild(int n) { result = getTypeParameter(n) } + final override Type getChild(int n) { result = this.getTypeParameter(n) } override string toStringWithTypes() { result = this.getUndecoratedName() + "<" + getTypeParametersToString(this) + ">" @@ -173,7 +173,7 @@ class TypeParameter extends DotNet::TypeParameter, Type, @type_parameter { TypeParameterConstraints getConstraints() { result.getTypeParameter() = this } override predicate isRefType() { - exists(TypeParameterConstraints tpc | tpc = getConstraints() | + exists(TypeParameterConstraints tpc | tpc = this.getConstraints() | tpc.hasRefTypeConstraint() or tpc.getATypeConstraint() instanceof Class or tpc.getATypeConstraint().(TypeParameter).isRefType() @@ -182,7 +182,7 @@ class TypeParameter extends DotNet::TypeParameter, Type, @type_parameter { } override predicate isValueType() { - exists(TypeParameterConstraints tpc | tpc = getConstraints() | + exists(TypeParameterConstraints tpc | tpc = this.getConstraints() | tpc.hasValueTypeConstraint() or tpc.getATypeConstraint().(TypeParameter).isValueType() ) @@ -219,9 +219,9 @@ class TypeParameter extends DotNet::TypeParameter, Type, @type_parameter { /** Gets a non-type-parameter type that was transitively supplied for this parameter. */ Type getAnUltimatelySuppliedType() { - result = getASuppliedType() and not result instanceof TypeParameter + result = this.getASuppliedType() and not result instanceof TypeParameter or - result = getASuppliedType().(TypeParameter).getAnUltimatelySuppliedType() + result = this.getASuppliedType().(TypeParameter).getAnUltimatelySuppliedType() } override int getIndex() { type_parameters(this, result, _, _) } @@ -376,8 +376,8 @@ class UnboundGenericDelegateType extends DelegateType, UnboundGenericType { override string toStringWithTypes() { result = - getUndecoratedName() + "<" + getTypeParametersToString(this) + ">(" + parameterTypesToString() - + ")" + this.getUndecoratedName() + "<" + getTypeParametersToString(this) + ">(" + + this.parameterTypesToString() + ")" } } @@ -404,7 +404,7 @@ class ConstructedType extends ValueOrRefType, ConstructedGeneric { override UnboundGenericType getUnboundGeneric() { constructed_generic(this, getTypeRef(result)) } - final override Type getChild(int n) { result = getTypeArgument(n) } + final override Type getChild(int n) { result = this.getTypeArgument(n) } final override string toStringWithTypes() { result = this.getUndecoratedName() + "<" + getTypeArgumentsToString(this) + ">" @@ -542,12 +542,12 @@ class UnboundGenericMethod extends Method, UnboundGeneric { override string toStringWithTypes() { result = - getUndecoratedName() + "<" + getTypeParametersToString(this) + ">" + "(" + - parameterTypesToString() + ")" + this.getUndecoratedName() + "<" + getTypeParametersToString(this) + ">" + "(" + + this.parameterTypesToString() + ")" } final override string getName() { - result = getUndecoratedName() + "<" + getTypeParameterCommas(this) + ">" + result = this.getUndecoratedName() + "<" + getTypeParameterCommas(this) + ">" } final override string getUndecoratedName() { methods(this, result, _, _, _) } @@ -580,8 +580,8 @@ class ConstructedMethod extends Method, ConstructedGeneric { override string toStringWithTypes() { result = - getUndecoratedName() + "<" + getTypeArgumentsToString(this) + ">" + "(" + - parameterTypesToString() + ")" + this.getUndecoratedName() + "<" + getTypeArgumentsToString(this) + ">" + "(" + + this.parameterTypesToString() + ")" } override UnboundGenericMethod getUnboundDeclaration() { @@ -589,12 +589,12 @@ class ConstructedMethod extends Method, ConstructedGeneric { } final override string getName() { - result = getUndecoratedName() + "<" + getTypeArgumentsNames(this) + ">" + result = this.getUndecoratedName() + "<" + getTypeArgumentsNames(this) + ">" } override predicate hasQualifiedName(string qualifier, string name) { - qualifier = getDeclaringType().getQualifiedName() and - name = getUndecoratedName() + "<" + getTypeArgumentsQualifiedNames(this) + ">" + qualifier = this.getDeclaringType().getQualifiedName() and + name = this.getUndecoratedName() + "<" + getTypeArgumentsQualifiedNames(this) + ">" } final override string getUndecoratedName() { methods(this, result, _, _, _) } diff --git a/csharp/ql/lib/semmle/code/csharp/Implements.qll b/csharp/ql/lib/semmle/code/csharp/Implements.qll index c2d33d102dd..cc821b52823 100644 --- a/csharp/ql/lib/semmle/code/csharp/Implements.qll +++ b/csharp/ql/lib/semmle/code/csharp/Implements.qll @@ -398,6 +398,15 @@ private module Gvn { ) } + pragma[noinline] + private predicate toStringPart(int i, int j) { + exists(Unification::GenericType t, int children | + t = this.getConstructedGenericDeclaringTypeAt(i) and + children = t.getNumberOfArgumentsSelf() and + if children = 0 then j = 0 else j in [0 .. 2 * children] + ) + } + language[monotonicAggregates] string toString() { this.isFullyConstructed() and @@ -406,11 +415,7 @@ private module Gvn { or result = strictconcat(int i, int j | - exists(Unification::GenericType t, int children | - t = this.getConstructedGenericDeclaringTypeAt(i) and - children = t.getNumberOfArgumentsSelf() and - if children = 0 then j = 0 else j in [0 .. 2 * children] - ) + this.toStringPart(i, j) | this.toStringConstructedPart(i, j) order by i desc, j ) diff --git a/csharp/ql/lib/semmle/code/csharp/Location.qll b/csharp/ql/lib/semmle/code/csharp/Location.qll index 97f9302c474..22d87f42424 100644 --- a/csharp/ql/lib/semmle/code/csharp/Location.qll +++ b/csharp/ql/lib/semmle/code/csharp/Location.qll @@ -28,7 +28,7 @@ class Location extends @location { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/csharp/ql/lib/semmle/code/csharp/Member.qll b/csharp/ql/lib/semmle/code/csharp/Member.qll index 9f8408621fc..40b887f052a 100644 --- a/csharp/ql/lib/semmle/code/csharp/Member.qll +++ b/csharp/ql/lib/semmle/code/csharp/Member.qll @@ -155,7 +155,9 @@ class Modifiable extends Declaration, @modifiable { * Holds if this declaration is effectively `public`, meaning that it can be * referenced outside the declaring assembly. */ - predicate isEffectivelyPublic() { not isEffectivelyPrivate() and not isEffectivelyInternal() } + predicate isEffectivelyPublic() { + not this.isEffectivelyPrivate() and not this.isEffectivelyInternal() + } } /** A declaration that is a member of a type. */ @@ -193,12 +195,12 @@ class Virtualizable extends Member, @virtualizable { override predicate isPublic() { Member.super.isPublic() or - implementsExplicitInterface() + this.implementsExplicitInterface() } override predicate isPrivate() { super.isPrivate() and - not implementsExplicitInterface() + not this.implementsExplicitInterface() } /** @@ -211,17 +213,17 @@ class Virtualizable extends Member, @virtualizable { /** * Holds if this member implements an interface member explicitly. */ - predicate implementsExplicitInterface() { exists(getExplicitlyImplementedInterface()) } + predicate implementsExplicitInterface() { exists(this.getExplicitlyImplementedInterface()) } /** Holds if this member can be overridden or implemented. */ predicate isOverridableOrImplementable() { - not isSealed() and - not getDeclaringType().isSealed() and + not this.isSealed() and + not this.getDeclaringType().isSealed() and ( - isVirtual() or - isOverride() or - isAbstract() or - getDeclaringType() instanceof Interface + this.isVirtual() or + this.isOverride() or + this.isAbstract() or + this.getDeclaringType() instanceof Interface ) } @@ -243,10 +245,10 @@ class Virtualizable extends Member, @virtualizable { Virtualizable getAnOverrider() { this = result.getOverridee() } /** Holds if this member is overridden by some other member. */ - predicate isOverridden() { exists(getAnOverrider()) } + predicate isOverridden() { exists(this.getAnOverrider()) } /** Holds if this member overrides another member. */ - predicate overrides() { exists(getOverridee()) } + predicate overrides() { exists(this.getOverridee()) } /** * Gets the interface member that is immediately implemented by this member, if any. @@ -274,7 +276,7 @@ class Virtualizable extends Member, @virtualizable { Virtualizable getImplementee(ValueOrRefType t) { implements(this, result, t) } /** Gets the interface member that is immediately implemented by this member, if any. */ - Virtualizable getImplementee() { result = getImplementee(_) } + Virtualizable getImplementee() { result = this.getImplementee(_) } /** * Gets a member that immediately implements this interface member, if any. @@ -338,8 +340,8 @@ class Virtualizable extends Member, @virtualizable { | this = implementation or - getOverridee+() = implementation and - getDeclaringType().getABaseType+() = implementationType + this.getOverridee+() = implementation and + this.getDeclaringType().getABaseType+() = implementationType ) } @@ -355,10 +357,10 @@ class Virtualizable extends Member, @virtualizable { Virtualizable getAnUltimateImplementor() { this = result.getAnUltimateImplementee() } /** Holds if this interface member is implemented by some other member. */ - predicate isImplemented() { exists(getAnImplementor()) } + predicate isImplemented() { exists(this.getAnImplementor()) } /** Holds if this member implements (transitively) an interface member. */ - predicate implements() { exists(getAnUltimateImplementee()) } + predicate implements() { exists(this.getAnUltimateImplementee()) } /** * Holds if this member overrides or implements (reflexively, transitively) @@ -366,8 +368,8 @@ class Virtualizable extends Member, @virtualizable { */ predicate overridesOrImplementsOrEquals(Virtualizable that) { this = that or - getOverridee+() = that or - getAnUltimateImplementee() = that + this.getOverridee+() = that or + this.getAnUltimateImplementee() = that } } @@ -386,7 +388,7 @@ class Parameterizable extends DotNet::Parameterizable, Declaration, @parameteriz */ private string parameterTypeToString(int i) { exists(Parameter p, string prefix | - p = getParameter(i) and + p = this.getParameter(i) and result = prefix + p.getType().toStringWithTypes() | if p.isOut() @@ -407,6 +409,7 @@ class Parameterizable extends DotNet::Parameterizable, Declaration, @parameteriz */ language[monotonicAggregates] string parameterTypesToString() { - result = concat(int i | exists(getParameter(i)) | parameterTypeToString(i), ", " order by i) + result = + concat(int i | exists(this.getParameter(i)) | this.parameterTypeToString(i), ", " order by i) } } diff --git a/csharp/ql/lib/semmle/code/csharp/Modifier.qll b/csharp/ql/lib/semmle/code/csharp/Modifier.qll index 542598d204e..39652070af3 100644 --- a/csharp/ql/lib/semmle/code/csharp/Modifier.qll +++ b/csharp/ql/lib/semmle/code/csharp/Modifier.qll @@ -19,10 +19,5 @@ class Modifier extends Element, @modifier { * An access modifier: `public`, `private`, `internal` or `protected`. */ class AccessModifier extends Modifier { - AccessModifier() { - hasName("public") or - hasName("private") or - hasName("internal") or - hasName("protected") - } + AccessModifier() { this.hasName(["public", "private", "internal", "protected"]) } } diff --git a/csharp/ql/lib/semmle/code/csharp/Preprocessor.qll b/csharp/ql/lib/semmle/code/csharp/Preprocessor.qll index daf4978da53..3342dd5c59c 100644 --- a/csharp/ql/lib/semmle/code/csharp/Preprocessor.qll +++ b/csharp/ql/lib/semmle/code/csharp/Preprocessor.qll @@ -289,7 +289,7 @@ class IfDirective extends ConditionalDirective, @directive_if { } /** Gets a sibling `#elif` or `#else` preprocessor directive. */ - BranchDirective getASiblingDirective() { result = getSiblingDirective(_) } + BranchDirective getASiblingDirective() { result = this.getSiblingDirective(_) } override string toString() { result = "#if ..." } diff --git a/csharp/ql/lib/semmle/code/csharp/PrintAst.qll b/csharp/ql/lib/semmle/code/csharp/PrintAst.qll index a701c7bfbf3..a3d36fba69d 100644 --- a/csharp/ql/lib/semmle/code/csharp/PrintAst.qll +++ b/csharp/ql/lib/semmle/code/csharp/PrintAst.qll @@ -171,7 +171,7 @@ class PrintAstNode extends TPrintAstNode { /** * Gets a child of this node. */ - final PrintAstNode getAChild() { result = getChild(_) } + final PrintAstNode getAChild() { result = this.getChild(_) } /** * Gets the parent of this node, if any. @@ -189,7 +189,7 @@ class PrintAstNode extends TPrintAstNode { */ string getProperty(string key) { key = "semmle.label" and - result = toString() + result = this.toString() } /** @@ -198,7 +198,7 @@ class PrintAstNode extends TPrintAstNode { * this. */ string getChildEdgeLabel(int childIndex) { - exists(getChild(childIndex)) and + exists(this.getChild(childIndex)) and result = childIndex.toString() } } diff --git a/csharp/ql/lib/semmle/code/csharp/Property.qll b/csharp/ql/lib/semmle/code/csharp/Property.qll index 5464142a085..a91ac6f13a4 100644 --- a/csharp/ql/lib/semmle/code/csharp/Property.qll +++ b/csharp/ql/lib/semmle/code/csharp/Property.qll @@ -53,10 +53,10 @@ class DeclarationWithAccessors extends AssignableMember, Virtualizable, Attribut class DeclarationWithGetSetAccessors extends DeclarationWithAccessors, TopLevelExprParent, @assignable_with_accessors { /** Gets the `get` accessor of this declaration, if any. */ - Getter getGetter() { result = getAnAccessor() } + Getter getGetter() { result = this.getAnAccessor() } /** Gets the `set` accessor of this declaration, if any. */ - Setter getSetter() { result = getAnAccessor() } + Setter getSetter() { result = this.getAnAccessor() } override DeclarationWithGetSetAccessors getOverridee() { result = DeclarationWithAccessors.super.getOverridee() @@ -182,10 +182,10 @@ class Property extends DotNet::Property, DeclarationWithGetSetAccessors, @proper or // For library types, we don't know about assignments in constructors. We instead assume that // arguments passed to parameters of constructors with suitable names. - getDeclaringType().fromLibrary() and + this.getDeclaringType().fromLibrary() and exists(Parameter param, Constructor c, string propertyName | - propertyName = getName() and - c = getDeclaringType().getAConstructor() and + propertyName = this.getName() and + c = this.getDeclaringType().getAConstructor() and param = c.getAParameter() and // Find a constructor parameter with the same name, but with a lower case initial letter. param.hasName(propertyName.charAt(0).toLowerCase() + propertyName.suffix(1)) @@ -256,7 +256,7 @@ class Indexer extends DeclarationWithGetSetAccessors, Parameterizable, @indexer override string getUndecoratedName() { indexers(this, result, _, _, _) } /** Gets the dimension of this indexer, that is, its number of parameters. */ - int getDimension() { result = getNumberOfParameters() } + int getDimension() { result = this.getNumberOfParameters() } override ValueOrRefType getDeclaringType() { indexers(this, _, result, _, _) } @@ -304,7 +304,9 @@ class Indexer extends DeclarationWithGetSetAccessors, Parameterizable, @indexer override Location getALocation() { indexer_location(this, result) } - override string toStringWithTypes() { result = getName() + "[" + parameterTypesToString() + "]" } + override string toStringWithTypes() { + result = this.getName() + "[" + this.parameterTypesToString() + "]" + } override string getAPrimaryQlClass() { result = "Indexer" } } @@ -368,17 +370,17 @@ class Accessor extends Callable, Modifiable, Attributable, @callable_accessor { * ``` */ override Modifier getAModifier() { - result = getAnAccessModifier() + result = this.getAnAccessModifier() or - result = getDeclaration().getAModifier() and - not (result instanceof AccessModifier and exists(getAnAccessModifier())) + result = this.getDeclaration().getAModifier() and + not (result instanceof AccessModifier and exists(this.getAnAccessModifier())) } override Accessor getUnboundDeclaration() { accessors(this, _, _, _, result) } override Location getALocation() { accessor_location(this, result) } - override string toString() { result = getName() } + override string toString() { result = this.getName() } } /** @@ -395,11 +397,11 @@ class Accessor extends Callable, Modifiable, Attributable, @callable_accessor { * ``` */ class Getter extends Accessor, @getter { - override string getName() { result = "get" + "_" + getDeclaration().getName() } + override string getName() { result = "get" + "_" + this.getDeclaration().getName() } - override string getUndecoratedName() { result = "get" + "_" + getDeclaration().getName() } + override string getUndecoratedName() { result = "get" + "_" + this.getDeclaration().getName() } - override Type getReturnType() { result = getDeclaration().getType() } + override Type getReturnType() { result = this.getDeclaration().getType() } /** * Gets the field used in the trival implementation of this getter, if any. @@ -417,8 +419,8 @@ class Getter extends Accessor, @getter { */ Field trivialGetterField() { exists(ReturnStmt ret | - getStatementBody().getNumberOfStmts() = 1 and - getStatementBody().getAChild() = ret and + this.getStatementBody().getNumberOfStmts() = 1 and + this.getStatementBody().getAChild() = ret and ret.getExpr() = result.getAnAccess() ) } @@ -444,9 +446,9 @@ class Getter extends Accessor, @getter { * ``` */ class Setter extends Accessor, @setter { - override string getName() { result = "set" + "_" + getDeclaration().getName() } + override string getName() { result = "set" + "_" + this.getDeclaration().getName() } - override string getUndecoratedName() { result = "set" + "_" + getDeclaration().getName() } + override string getUndecoratedName() { result = "set" + "_" + this.getDeclaration().getName() } override Type getReturnType() { exists(this) and // needed to avoid compiler warning @@ -469,8 +471,8 @@ class Setter extends Accessor, @setter { */ Field trivialSetterField() { exists(AssignExpr assign | - getStatementBody().getNumberOfStmts() = 1 and - assign.getParent() = getStatementBody().getAChild() and + this.getStatementBody().getNumberOfStmts() = 1 and + assign.getParent() = this.getStatementBody().getAChild() and assign.getLValue() = result.getAnAccess() and assign.getRValue() = accessToValue() ) @@ -521,9 +523,9 @@ private ParameterAccess accessToValue() { */ class TrivialProperty extends Property { TrivialProperty() { - isAutoImplemented() + this.isAutoImplemented() or - getGetter().trivialGetterField() = getSetter().trivialSetterField() + this.getGetter().trivialGetterField() = this.getSetter().trivialSetterField() or exists(CIL::TrivialProperty prop | this.matchesHandle(prop)) } diff --git a/csharp/ql/lib/semmle/code/csharp/Stmt.qll b/csharp/ql/lib/semmle/code/csharp/Stmt.qll index 2ccd57078db..be074c176ba 100644 --- a/csharp/ql/lib/semmle/code/csharp/Stmt.qll +++ b/csharp/ql/lib/semmle/code/csharp/Stmt.qll @@ -65,10 +65,10 @@ class BlockStmt extends Stmt, @block_stmt { int getNumberOfStmts() { result = count(this.getAStmt()) } /** Gets the first statement in this block, if any. */ - Stmt getFirstStmt() { result = getStmt(0) } + Stmt getFirstStmt() { result = this.getStmt(0) } /** Gets the last statement in this block, if any. */ - Stmt getLastStmt() { result = getStmt(getNumberOfStmts() - 1) } + Stmt getLastStmt() { result = this.getStmt(this.getNumberOfStmts() - 1) } /** Holds if this block is an empty block with no statements. */ predicate isEmpty() { not exists(this.getAStmt()) } @@ -79,8 +79,8 @@ class BlockStmt extends Stmt, @block_stmt { } override Stmt stripSingletonBlocks() { - if getNumberOfStmts() = 1 - then result = getAChildStmt().stripSingletonBlocks() + if this.getNumberOfStmts() = 1 + then result = this.getAChildStmt().stripSingletonBlocks() else result = this } @@ -420,7 +420,7 @@ class ForStmt extends LoopStmt, @for_stmt { * } * ``` */ - Expr getAnInitializer() { result = getInitializer(_) } + Expr getAnInitializer() { result = this.getInitializer(_) } /** * Gets the `n`th initializer expression of this `for` loop @@ -451,7 +451,7 @@ class ForStmt extends LoopStmt, @for_stmt { * } * ``` */ - Expr getAnUpdate() { result = getUpdate(_) } + Expr getAnUpdate() { result = this.getUpdate(_) } /** * Gets the `n`th update expression of this `for` loop (starting at index 0). @@ -519,7 +519,7 @@ class ForeachStmt extends LoopStmt, @foreach_stmt { * ``` */ LocalVariableDeclExpr getVariableDeclExpr(int i) { - result = getVariableDeclTuple().getArgument(i) + result = this.getVariableDeclTuple().getArgument(i) or i = 0 and result = this.getChild(0) } @@ -547,7 +547,7 @@ class ForeachStmt extends LoopStmt, @foreach_stmt { * } * ``` */ - LocalVariable getVariable(int i) { result = getVariableDeclExpr(i).getVariable() } + LocalVariable getVariable(int i) { result = this.getVariableDeclExpr(i).getVariable() } /** * Gets a local variable of this `foreach` loop. @@ -560,7 +560,7 @@ class ForeachStmt extends LoopStmt, @foreach_stmt { * } * ``` */ - LocalVariable getAVariable() { result = getVariable(_) } + LocalVariable getAVariable() { result = this.getVariable(_) } /** * Gets a local variable declaration of this `foreach` loop. @@ -573,7 +573,7 @@ class ForeachStmt extends LoopStmt, @foreach_stmt { * } * ``` */ - LocalVariableDeclExpr getAVariableDeclExpr() { result = getVariableDeclExpr(_) } + LocalVariableDeclExpr getAVariableDeclExpr() { result = this.getVariableDeclExpr(_) } override Expr getCondition() { none() } @@ -690,8 +690,8 @@ class GotoLabelStmt extends GotoStmt, @goto_stmt { /** Gets the target statement that this `goto` statement jumps to. */ LabeledStmt getTarget() { - result.getEnclosingCallable() = getEnclosingCallable() and - result.getLabel() = getLabel() + result.getEnclosingCallable() = this.getEnclosingCallable() and + result.getLabel() = this.getLabel() } override string getAPrimaryQlClass() { result = "GotoLabelStmt" } @@ -717,7 +717,7 @@ class GotoCaseStmt extends GotoStmt, @goto_case_stmt { /** Gets the constant expression that this `goto case` statement jumps to. */ Expr getExpr() { result = this.getChild(0) } - override string getLabel() { result = getExpr().getValue() } + override string getLabel() { result = this.getExpr().getValue() } override string toString() { result = "goto case ...;" } @@ -764,14 +764,14 @@ class ThrowStmt extends JumpStmt, ThrowElement, @throw_stmt { override ExceptionClass getThrownExceptionType() { result = ThrowElement.super.getThrownExceptionType() or - result = getRethrowParent().(CatchClause).getCaughtExceptionType() + result = this.getRethrowParent().(CatchClause).getCaughtExceptionType() } private ControlFlowElement getRethrowParent() { - result = this and not exists(getExpr()) + result = this and not exists(this.getExpr()) or exists(ControlFlowElement mid | - mid = getRethrowParent() and + mid = this.getRethrowParent() and not mid instanceof CatchClause and result = mid.getParent() ) @@ -785,7 +785,7 @@ class ThrowStmt extends JumpStmt, ThrowElement, @throw_stmt { * and may be thrown as an exception. */ class ExceptionClass extends Class { - ExceptionClass() { getBaseClass*() instanceof SystemExceptionClass } + ExceptionClass() { this.getBaseClass*() instanceof SystemExceptionClass } } /** @@ -897,13 +897,15 @@ class TryStmt extends Stmt, @try_stmt { override string getAPrimaryQlClass() { result = "TryStmt" } /** Gets the `catch` clause that handles an exception of type `ex`, if any. */ - CatchClause getAnExceptionHandler(ExceptionClass ex) { result = clauseHandlesException(ex, 0) } + CatchClause getAnExceptionHandler(ExceptionClass ex) { + result = this.clauseHandlesException(ex, 0) + } /** * Holds if catch clause `cc` definitely handles exceptions of type `ex`. */ predicate definitelyHandles(ExceptionClass ex, CatchClause cc) { - cc = getACatchClause() and + cc = this.getACatchClause() and not exists(cc.getFilterClause()) and ( cc.getCaughtExceptionType() = ex.getBaseClass*() @@ -913,22 +915,22 @@ class TryStmt extends Stmt, @try_stmt { } private predicate maybeHandles(ExceptionClass ex, CatchClause cc) { - cc = getACatchClause() and + cc = this.getACatchClause() and cc.getCaughtExceptionType().getBaseClass*() = ex } private CatchClause clauseHandlesException(ExceptionClass ex, int n) { - exists(CatchClause clause | clause = getCatchClause(n) | - if definitelyHandles(ex, clause) + exists(CatchClause clause | clause = this.getCatchClause(n) | + if this.definitelyHandles(ex, clause) then result = clause else - if maybeHandles(ex, clause) + if this.maybeHandles(ex, clause) then result = clause or - result = clauseHandlesException(ex, n + 1) + result = this.clauseHandlesException(ex, n + 1) else // Does not handle - result = clauseHandlesException(ex, n + 1) + result = this.clauseHandlesException(ex, n + 1) ) } @@ -939,10 +941,10 @@ class TryStmt extends Stmt, @try_stmt { * `try` statement. */ ControlFlowElement getATriedElement() { - result = getBlock() + result = this.getBlock() or exists(ControlFlowElement mid | - mid = getATriedElement() and + mid = this.getATriedElement() and not mid instanceof TryStmt and result = getAChild(mid, mid.getEnclosingCallable()) ) @@ -996,10 +998,10 @@ class CatchClause extends Stmt, @catch { * } * ``` */ - Expr getFilterClause() { result = getChild(2) } + Expr getFilterClause() { result = this.getChild(2) } /** Holds if this `catch` clause has a filter. */ - predicate hasFilterClause() { exists(getFilterClause()) } + predicate hasFilterClause() { exists(this.getFilterClause()) } /** Holds if this is the last `catch` clause in the `try` statement that it belongs to. */ predicate isLast() { @@ -1120,7 +1122,7 @@ class LockStmt extends Stmt, @lock_stmt { override string toString() { result = "lock (...) {...}" } /** Gets the variable being locked, if any. */ - Variable getLockVariable() { result.getAnAccess() = getExpr() } + Variable getLockVariable() { result.getAnAccess() = this.getExpr() } /** Gets a statement in the scope of this `lock` statement. */ Stmt getALockedStmt() { @@ -1128,14 +1130,14 @@ class LockStmt extends Stmt, @lock_stmt { // delegates and lambdas result.getParent() = this or - exists(Stmt mid | mid = getALockedStmt() and result.getParent() = mid) + exists(Stmt mid | mid = this.getALockedStmt() and result.getParent() = mid) } /** Holds if this statement is of the form `lock(this) { ... }`. */ - predicate isLockThis() { getExpr() instanceof ThisAccess } + predicate isLockThis() { this.getExpr() instanceof ThisAccess } /** Gets the type `T` if this statement is of the form `lock(typeof(T)) { ... }`. */ - Type getLockTypeObject() { result = getExpr().(TypeofExpr).getTypeAccess().getTarget() } + Type getLockTypeObject() { result = this.getExpr().(TypeofExpr).getTypeAccess().getTarget() } override string getAPrimaryQlClass() { result = "LockStmt" } } @@ -1453,7 +1455,7 @@ class LocalFunctionStmt extends Stmt, @local_function_stmt { /** Gets the local function defined by this statement. */ LocalFunction getLocalFunction() { local_function_stmts(this, result) } - override string toString() { result = getLocalFunction().getName() + "(...)" } + override string toString() { result = this.getLocalFunction().getName() + "(...)" } override string getAPrimaryQlClass() { result = "LocalFunctionStmt" } } diff --git a/csharp/ql/lib/semmle/code/csharp/Type.qll b/csharp/ql/lib/semmle/code/csharp/Type.qll index d7a15000bbf..109c1df00c7 100644 --- a/csharp/ql/lib/semmle/code/csharp/Type.qll +++ b/csharp/ql/lib/semmle/code/csharp/Type.qll @@ -37,7 +37,7 @@ class Type extends DotNet::Type, Member, TypeContainer, @type { predicate containsTypeParameters() { this instanceof TypeParameter or - not this instanceof UnboundGenericType and getAChild().containsTypeParameters() + not this instanceof UnboundGenericType and this.getAChild().containsTypeParameters() } /** Holds if this type is a reference type, or a type parameter that is a reference type. */ @@ -133,8 +133,8 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_ /** Gets an immediate base type of this type, if any. */ override ValueOrRefType getABaseType() { - result = getBaseClass() or - result = getABaseInterface() + result = this.getBaseClass() or + result = this.getABaseInterface() } /** Gets an immediate subtype of this type, if any. */ @@ -200,9 +200,9 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_ */ pragma[inline] predicate hasCallable(Callable c) { - hasMethod(c) + this.hasMethod(c) or - hasMember(c.(Accessor).getDeclaration()) + this.hasMember(c.(Accessor).getDeclaration()) } /** @@ -234,63 +234,63 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_ or hasNonOverriddenMember(this.getBaseClass+(), m) or - hasOverriddenMember(m) + this.hasOverriddenMember(m) } cached private predicate hasOverriddenMember(Virtualizable v) { v.isOverridden() and - v = getAMember() + v = this.getAMember() or - getBaseClass().(ValueOrRefType).hasOverriddenMember(v) and + this.getBaseClass().(ValueOrRefType).hasOverriddenMember(v) and not v.isPrivate() and - not memberOverrides(v) + not this.memberOverrides(v) } // Predicate folding for proper join-order pragma[noinline] private predicate memberOverrides(Virtualizable v) { - getAMember().(Virtualizable).getOverridee() = v + this.getAMember().(Virtualizable).getOverridee() = v } /** Gets a field (or member constant) with the given name. */ - Field getField(string name) { result = getAMember() and result.hasName(name) } + Field getField(string name) { result = this.getAMember() and result.hasName(name) } /** Gets a field (or member constant) of this type, if any. */ Field getAField() { result = this.getField(_) } /** Gets a member constant of this type, if any. */ - MemberConstant getAConstant() { result = getAMember() } + MemberConstant getAConstant() { result = this.getAMember() } /** Gets a method of this type, if any. */ - Method getAMethod() { result = getAMember() } + Method getAMethod() { result = this.getAMember() } /** Gets a method of this type with the given name. */ - Method getAMethod(string name) { result = getAMember() and result.hasName(name) } + Method getAMethod(string name) { result = this.getAMember() and result.hasName(name) } /** Gets a property of this type, if any. */ - Property getAProperty() { result = getAMember() } + Property getAProperty() { result = this.getAMember() } /** Gets a named property of this type. */ - Property getProperty(string name) { result = getAMember() and result.hasName(name) } + Property getProperty(string name) { result = this.getAMember() and result.hasName(name) } /** Gets an indexer of this type, if any. */ - Indexer getAnIndexer() { result = getAMember() } + Indexer getAnIndexer() { result = this.getAMember() } /** Gets an event of this type, if any. */ - Event getAnEvent() { result = getAMember() } + Event getAnEvent() { result = this.getAMember() } /** Gets a user-defined operator of this type, if any. */ - Operator getAnOperator() { result = getAMember() } + Operator getAnOperator() { result = this.getAMember() } /** Gets a static or instance constructor of this type, if any. */ - Constructor getAConstructor() { result = getAMember() } + Constructor getAConstructor() { result = this.getAMember() } /** Gets the static constructor of this type, if any. */ - StaticConstructor getStaticConstructor() { result = getAMember() } + StaticConstructor getStaticConstructor() { result = this.getAMember() } /** Gets a nested type of this type, if any. */ - NestedType getANestedType() { result = getAMember() } + NestedType getANestedType() { result = this.getAMember() } /** Gets the number of types that directly depend on this type. */ int getAfferentCoupling() { afferentCoupling(this, result) } @@ -675,10 +675,10 @@ class Enum extends ValueType, @enum_type { */ class Struct extends ValueType, @struct_type { /** Holds if this `struct` has a `ref` modifier. */ - predicate isRef() { hasModifier("ref") } + predicate isRef() { this.hasModifier("ref") } /** Holds if this `struct` has a `readonly` modifier. */ - predicate isReadonly() { hasModifier("readonly") } + predicate isReadonly() { this.hasModifier("readonly") } override string getAPrimaryQlClass() { result = "Struct" } } @@ -695,7 +695,7 @@ class RefType extends ValueOrRefType, @ref_type { /** Gets a member that overrides a non-abstract member in a super type, if any. */ private Virtualizable getAnOverrider() { - getAMember() = result and + this.getAMember() = result and exists(Virtualizable v | result.getOverridee() = v and not v.isAbstract()) } @@ -897,14 +897,14 @@ class FunctionPointerType extends Type, Parameterizable, @function_pointer_type } /** Gets an unmanaged calling convention. */ - Type getAnUnmanagedCallingConvention() { result = getUnmanagedCallingConvention(_) } + Type getAnUnmanagedCallingConvention() { result = this.getUnmanagedCallingConvention(_) } /** Gets the annotated return type of this function pointer type. */ AnnotatedType getAnnotatedReturnType() { result.appliesTo(this) } override string getAPrimaryQlClass() { result = "FunctionPointerType" } - override string getLabel() { result = getName() } + override string getLabel() { result = this.getName() } } /** @@ -922,13 +922,15 @@ class NullableType extends ValueType, DotNet::ConstructedGeneric, @nullable_type */ Type getUnderlyingType() { nullable_underlying_type(this, getTypeRef(result)) } - override string toStringWithTypes() { result = getUnderlyingType().toStringWithTypes() + "?" } + override string toStringWithTypes() { + result = this.getUnderlyingType().toStringWithTypes() + "?" + } - override Type getChild(int n) { result = getUnderlyingType() and n = 0 } + override Type getChild(int n) { result = this.getUnderlyingType() and n = 0 } - override Location getALocation() { result = getUnderlyingType().getALocation() } + override Location getALocation() { result = this.getUnderlyingType().getALocation() } - override Type getTypeArgument(int p) { p = 0 and result = getUnderlyingType() } + override Type getTypeArgument(int p) { p = 0 and result = this.getUnderlyingType() } override string getAPrimaryQlClass() { result = "NullableType" } @@ -966,8 +968,8 @@ class ArrayType extends DotNet::ArrayType, RefType, @array_type { /** Holds if this array type has the same shape (dimension and rank) as `that` array type. */ predicate hasSameShapeAs(ArrayType that) { - getDimension() = that.getDimension() and - getRank() = that.getRank() + this.getDimension() = that.getDimension() and + this.getRank() = that.getRank() } /** @@ -981,7 +983,7 @@ class ArrayType extends DotNet::ArrayType, RefType, @array_type { private string getDimensionString(Type elementType) { exists(Type et, string res | et = this.getElementType() and - res = getArraySuffix() and + res = this.getArraySuffix() and if et instanceof ArrayType then result = res + et.(ArrayType).getDimensionString(elementType) else ( @@ -996,7 +998,7 @@ class ArrayType extends DotNet::ArrayType, RefType, @array_type { ) } - override Type getChild(int n) { result = getElementType() and n = 0 } + override Type getChild(int n) { result = this.getElementType() and n = 0 } override Location getALocation() { type_location(this, result) @@ -1021,13 +1023,15 @@ class PointerType extends DotNet::PointerType, Type, @pointer_type { override string toStringWithTypes() { result = DotNet::PointerType.super.toStringWithTypes() } - override Type getChild(int n) { result = getReferentType() and n = 0 } + override Type getChild(int n) { result = this.getReferentType() and n = 0 } final override string getName() { types(this, _, result) } - final override string getUndecoratedName() { result = getReferentType().getUndecoratedName() } + final override string getUndecoratedName() { + result = this.getReferentType().getUndecoratedName() + } - override Location getALocation() { result = getReferentType().getALocation() } + override Location getALocation() { result = this.getReferentType().getALocation() } override string toString() { result = DotNet::PointerType.super.toString() } @@ -1082,10 +1086,10 @@ class TupleType extends ValueType, @tuple_type { * Gets the type of the `n`th element of this tuple, indexed from 0. * For example, the 0th (first) element type of `(int, string)` is `int`. */ - Type getElementType(int n) { result = getElement(n).getType() } + Type getElementType(int n) { result = this.getElement(n).getType() } /** Gets an element of this tuple. */ - Field getAnElement() { result = getElement(_) } + Field getAnElement() { result = this.getElement(_) } override Location getALocation() { type_location(this, result) } @@ -1093,23 +1097,27 @@ class TupleType extends ValueType, @tuple_type { * Gets the arity of this tuple. For example, the arity of * `(int, int, double)` is 3. */ - int getArity() { result = count(getAnElement()) } + int getArity() { result = count(this.getAnElement()) } language[monotonicAggregates] override string toStringWithTypes() { result = "(" + - concat(Type t, int i | t = getElement(i).getType() | t.toStringWithTypes(), ", " order by i) - + ")" + concat(Type t, int i | + t = this.getElement(i).getType() + | + t.toStringWithTypes(), ", " order by i + ) + ")" } language[monotonicAggregates] override string getName() { result = - "(" + concat(Type t, int i | t = getElement(i).getType() | t.getName(), "," order by i) + ")" + "(" + concat(Type t, int i | t = this.getElement(i).getType() | t.getName(), "," order by i) + + ")" } - override string getLabel() { result = getUnderlyingType().getLabel() } + override string getLabel() { result = this.getUnderlyingType().getLabel() } override Type getChild(int i) { result = this.getUnderlyingType().getChild(i) } diff --git a/csharp/ql/lib/semmle/code/csharp/Unification.qll b/csharp/ql/lib/semmle/code/csharp/Unification.qll index 173feedb695..d9f39cec603 100644 --- a/csharp/ql/lib/semmle/code/csharp/Unification.qll +++ b/csharp/ql/lib/semmle/code/csharp/Unification.qll @@ -277,6 +277,18 @@ module Gvn { ) } + pragma[noinline] + private predicate toStringPart(int i, int j) { + exists(int offset | + exists(GenericType t, int children | + t = this.getConstructedGenericDeclaringTypeAt(i) and + children = t.getNumberOfArgumentsSelf() and + (if this.isDeclaringTypeAt(i) then offset = 1 else offset = 0) and + if children = 0 then j in [0 .. offset] else j in [0 .. 2 * children + offset] + ) + ) + } + language[monotonicAggregates] string toString() { this.isFullyConstructed() and @@ -284,13 +296,8 @@ module Gvn { result = k.toStringBuiltin(this.getArg(0).toString()) or result = - strictconcat(int i, int j, int offset | - exists(GenericType t, int children | - t = this.getConstructedGenericDeclaringTypeAt(i) and - children = t.getNumberOfArgumentsSelf() and - (if this.isDeclaringTypeAt(i) then offset = 1 else offset = 0) and - if children = 0 then j in [0 .. offset] else j in [0 .. 2 * children + offset] - ) + strictconcat(int i, int j | + this.toStringPart(i, j) | this.toStringConstructedPart(i, j) order by i desc, j ) diff --git a/csharp/ql/lib/semmle/code/csharp/Variable.qll b/csharp/ql/lib/semmle/code/csharp/Variable.qll index a13175dfeb0..6592320fdd7 100644 --- a/csharp/ql/lib/semmle/code/csharp/Variable.qll +++ b/csharp/ql/lib/semmle/code/csharp/Variable.qll @@ -149,7 +149,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top predicate isIn() { params(this, _, _, _, 5, _, _) } /** Holds if this parameter is an output or reference parameter. */ - predicate isOutOrRef() { isOut() or isRef() } + predicate isOutOrRef() { this.isOut() or this.isRef() } /** * Holds if this parameter is a parameter array. For example, `args` @@ -210,7 +210,7 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top Expr getDefaultValue() { result = this.getUnboundDeclaration().getChildExpr(0) } /** Holds if this parameter has a default value. */ - predicate hasDefaultValue() { exists(getDefaultValue()) } + predicate hasDefaultValue() { exists(this.getDefaultValue()) } /** Gets the callable to which this parameter belongs, if any. */ override Callable getCallable() { result = this.getDeclaringElement() } @@ -238,7 +238,9 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top * `y` is `5`, and the assigned arguments to `z` are `3` and `6`, respectively. */ pragma[nomagic] - Expr getAnAssignedArgument() { result = getCallable().getACall().getArgumentForParameter(this) } + Expr getAnAssignedArgument() { + result = this.getCallable().getACall().getArgumentForParameter(this) + } /** Holds if this parameter is potentially overwritten in the body of its callable. */ predicate isOverwritten() { @@ -323,7 +325,7 @@ class LocalVariable extends LocalScopeVariable, @local_variable { /** Gets the enclosing callable of this local variable. */ Callable getEnclosingCallable() { result = this.getVariableDeclExpr().getEnclosingCallable() } - override Callable getCallable() { result = getEnclosingCallable() } + override Callable getCallable() { result = this.getEnclosingCallable() } override predicate isRef() { localvars(this, 3, _, _, _, _) } diff --git a/csharp/ql/lib/semmle/code/csharp/XML.qll b/csharp/ql/lib/semmle/code/csharp/XML.qll index 5871fed0ddd..76f3b3cb022 100755 --- a/csharp/ql/lib/semmle/code/csharp/XML.qll +++ b/csharp/ql/lib/semmle/code/csharp/XML.qll @@ -24,7 +24,7 @@ class XMLLocatable extends @xmllocatable, TXMLLocatable { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -108,7 +108,7 @@ class XMLParent extends @xmlparent { } /** Gets the text value contained in this XML parent. */ - string getTextValue() { result = allCharactersString() } + string getTextValue() { result = this.allCharactersString() } /** Gets a printable representation of this XML parent. */ string toString() { result = this.getName() } @@ -119,7 +119,7 @@ class XMLFile extends XMLParent, File { XMLFile() { xmlEncoding(this, _) } /** Gets a printable representation of this XML file. */ - override string toString() { result = getName() } + override string toString() { result = this.getName() } /** Gets the name of this XML file. */ override string getName() { result = File.super.getAbsolutePath() } @@ -129,14 +129,14 @@ class XMLFile extends XMLParent, File { * * Gets the path of this XML file. */ - deprecated string getPath() { result = getAbsolutePath() } + deprecated string getPath() { result = this.getAbsolutePath() } /** * DEPRECATED: Use `getParentContainer().getAbsolutePath()` instead. * * Gets the path of the folder that contains this XML file. */ - deprecated string getFolder() { result = getParentContainer().getAbsolutePath() } + deprecated string getFolder() { result = this.getParentContainer().getAbsolutePath() } /** Gets the encoding of this XML file. */ string getEncoding() { xmlEncoding(this, result) } @@ -200,7 +200,7 @@ class XMLDTD extends XMLLocatable, @xmldtd { */ class XMLElement extends @xmlelement, XMLParent, XMLLocatable { /** Holds if this XML element has the given `name`. */ - predicate hasName(string name) { name = getName() } + predicate hasName(string name) { name = this.getName() } /** Gets the name of this XML element. */ override string getName() { xmlElements(this, result, _, _, _) } @@ -239,7 +239,7 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable { string getAttributeValue(string name) { result = this.getAttribute(name).getValue() } /** Gets a printable representation of this XML element. */ - override string toString() { result = getName() } + override string toString() { result = this.getName() } } /** diff --git a/csharp/ql/lib/semmle/code/csharp/commons/Assertions.qll b/csharp/ql/lib/semmle/code/csharp/commons/Assertions.qll index d425ec118ed..4f364147395 100644 --- a/csharp/ql/lib/semmle/code/csharp/commons/Assertions.qll +++ b/csharp/ql/lib/semmle/code/csharp/commons/Assertions.qll @@ -42,7 +42,7 @@ abstract class AssertMethod extends Method { * * Gets the index of a parameter being asserted. */ - deprecated final int getAssertionIndex() { result = getAnAssertionIndex() } + deprecated final int getAssertionIndex() { result = this.getAnAssertionIndex() } /** Gets the parameter at position `i` being asserted. */ final Parameter getAssertedParameter(int i) { @@ -55,7 +55,7 @@ abstract class AssertMethod extends Method { * * Gets a parameter being asserted. */ - deprecated final Parameter getAssertedParameter() { result = getAssertedParameter(_) } + deprecated final Parameter getAssertedParameter() { result = this.getAssertedParameter(_) } /** Gets the failure type if the assertion fails for argument `i`, if any. */ abstract AssertionFailure getAssertionFailure(int i); diff --git a/csharp/ql/lib/semmle/code/csharp/commons/Collections.qll b/csharp/ql/lib/semmle/code/csharp/commons/Collections.qll index cee7b647fe7..0121ef13f8c 100644 --- a/csharp/ql/lib/semmle/code/csharp/commons/Collections.qll +++ b/csharp/ql/lib/semmle/code/csharp/commons/Collections.qll @@ -3,23 +3,12 @@ import csharp private string modifyMethodName() { - result = "Add" or - result = "AddFirst" or - result = "AddLast" or - result = "Clear" or - result = "Enqueue" or - result = "ExceptWith" or - result = "Insert" or - result = "IntersectWith" or - result = "Push" or - result = "Remove" or - result = "RemoveAt" or - result = "RemoveFirst" or - result = "RemoveLast" or - result = "Set" or - result = "SetAll" or - result = "SymmetricExceptWith" or - result = "UnionWith" + result = + [ + "Add", "AddFirst", "AddLast", "Clear", "Enqueue", "ExceptWith", "Insert", "IntersectWith", + "Push", "Remove", "RemoveAt", "RemoveFirst", "RemoveLast", "Set", "SetAll", + "SymmetricExceptWith", "UnionWith" + ] } /** A method call that modifies a collection. */ @@ -39,45 +28,27 @@ class CollectionModificationAccess extends Access { } private string collectionTypeName() { - result = "ArrayList" or - result = "BitArray" or - result = "Hashtable" or - result = "ICollection" or - result = "IDictionary" or - result = "IList" or - result = "Queue" or - result = "ReadOnlyCollectionBase" or - result = "SortedList" or - result = "Stack" + result = + [ + "ArrayList", "BitArray", "Hashtable", "ICollection", "IDictionary", "IList", "Queue", + "ReadOnlyCollectionBase", "SortedList", "Stack" + ] } -private string collectionNamespaceName() { - result = "Mono.Collections" or - result = "System.Collections" -} +private string collectionNamespaceName() { result = ["Mono.Collections", "System.Collections"] } private string genericCollectionNamespaceName() { - result = "Mono.Collections.Generic" or - result = "System.Collections.Generic" + result = ["Mono.Collections.Generic", "System.Collections.Generic"] } private string genericCollectionTypeName() { - result = "Dictionary<,>" or - result = "HashSet<>" or - result = "ICollection<>" or - result = "IDictionary<,>" or - result = "IList<>" or - result = "ISet<>" or - result = "LinkedList<>" or - result = "List<>" or - result = "Queue<>" or - result = "SortedDictionary<,>" or - result = "SortedList<,>" or - result = "SortedSet<>" or - result = "Stack<>" or - result = "SynchronizedCollection<>" or - result = "SynchronizedKeyedCollection<>" or - result = "SynchronizedReadOnlyCollection<>" + result = + [ + "Dictionary<,>", "HashSet<>", "ICollection<>", "IDictionary<,>", "IList<>", "ISet<>", + "LinkedList<>", "List<>", "Queue<>", "SortedDictionary<,>", "SortedList<,>", "SortedSet<>", + "Stack<>", "SynchronizedCollection<>", "SynchronizedKeyedCollection<>", + "SynchronizedReadOnlyCollection<>" + ] } /** A collection type. */ @@ -105,36 +76,18 @@ class EmptyCollectionCreation extends ObjectCreation { } private string readonlyMethodName() { - result = "BinarySearch" or - result = "Clone" or - result = "Contains" or - result = "ContainsKey" or - result = "ContainsValue" or - result = "CopyTo" or - result = "Equals" or - result = "FixedArray" or - result = "FixedSize" or - result = "Get" or - result = "GetEnumerator" or - result = "GetHashCode" or - result = "GetRange" or - result = "IndexOf" or - result = "IsProperSubsetOf" or - result = "IsProperSupersetOf" or - result = "IsSubsetOf" or - result = "IsSupersetOf" or - result = "LastIndexOf" or - result = "MemberwiseClone" or - result = "Peek" or - result = "ToArray" or - result = "ToString" or - result = "TryGetValue" + result = + [ + "BinarySearch", "Clone", "Contains", "ContainsKey", "ContainsValue", "CopyTo", "Equals", + "FixedArray", "FixedSize", "Get", "GetEnumerator", "GetHashCode", "GetRange", "IndexOf", + "IsProperSubsetOf", "IsProperSupersetOf", "IsSubsetOf", "IsSupersetOf", "LastIndexOf", + "MemberwiseClone", "Peek", "ToArray", "ToString", "TryGetValue" + ] } private string noAddMethodName() { result = readonlyMethodName() or - result = "Dequeue" or - result = "Pop" + result = ["Dequeue", "Pop"] } /** Holds if `a` is an access that does not modify a collection. */ diff --git a/csharp/ql/lib/semmle/code/csharp/commons/GeneratedCode.qll b/csharp/ql/lib/semmle/code/csharp/commons/GeneratedCode.qll index 42e04ddcbb9..38d559d8ffd 100644 --- a/csharp/ql/lib/semmle/code/csharp/commons/GeneratedCode.qll +++ b/csharp/ql/lib/semmle/code/csharp/commons/GeneratedCode.qll @@ -57,7 +57,7 @@ abstract class GeneratedCodeComment extends CommentLine { } */ class GenericGeneratedCodeComment extends GeneratedCodeComment { GenericGeneratedCodeComment() { - exists(string line, string entity, string was, string automatically | line = getText() | + exists(string line, string entity, string was, string automatically | line = this.getText() | entity = "file|class|interface|art[ei]fact|module|script" and was = "was|is|has been" and automatically = "automatically |mechanically |auto[- ]?" and @@ -70,7 +70,7 @@ class GenericGeneratedCodeComment extends GeneratedCodeComment { /** A comment warning against modifications. */ class DontModifyMarkerComment extends GeneratedCodeComment { DontModifyMarkerComment() { - exists(string line | line = getText() | + exists(string line | line = this.getText() | line.regexpMatch("(?i).*\\bGenerated by\\b.*\\bDo not edit\\b.*") or line.regexpMatch("(?i).*\\bAny modifications to this file will be lost\\b.*") ) diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll index b4448a71380..08e5925ad50 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll @@ -11,7 +11,7 @@ private import ControlFlow::SuccessorTypes */ class BasicBlock extends TBasicBlockStart { /** Gets an immediate successor of this basic block, if any. */ - BasicBlock getASuccessor() { result.getFirstNode() = getLastNode().getASuccessor() } + BasicBlock getASuccessor() { result.getFirstNode() = this.getLastNode().getASuccessor() } /** Gets an immediate successor of this basic block of a given type, if any. */ BasicBlock getASuccessorByType(ControlFlow::SuccessorType t) { @@ -42,7 +42,7 @@ class BasicBlock extends TBasicBlockStart { * The basic block on line 2 is an immediate `true` successor of the * basic block on line 1. */ - BasicBlock getATrueSuccessor() { result.getFirstNode() = getLastNode().getATrueSuccessor() } + BasicBlock getATrueSuccessor() { result.getFirstNode() = this.getLastNode().getATrueSuccessor() } /** * Gets an immediate `false` successor, if any. @@ -60,25 +60,27 @@ class BasicBlock extends TBasicBlockStart { * The basic block on line 2 is an immediate `false` successor of the * basic block on line 1. */ - BasicBlock getAFalseSuccessor() { result.getFirstNode() = getLastNode().getAFalseSuccessor() } + BasicBlock getAFalseSuccessor() { + result.getFirstNode() = this.getLastNode().getAFalseSuccessor() + } /** Gets the control flow node at a specific (zero-indexed) position in this basic block. */ - ControlFlow::Node getNode(int pos) { bbIndex(getFirstNode(), result, pos) } + ControlFlow::Node getNode(int pos) { bbIndex(this.getFirstNode(), result, pos) } /** Gets a control flow node in this basic block. */ - ControlFlow::Node getANode() { result = getNode(_) } + ControlFlow::Node getANode() { result = this.getNode(_) } /** Gets the first control flow node in this basic block. */ ControlFlow::Node getFirstNode() { this = TBasicBlockStart(result) } /** Gets the last control flow node in this basic block. */ - ControlFlow::Node getLastNode() { result = getNode(length() - 1) } + ControlFlow::Node getLastNode() { result = this.getNode(this.length() - 1) } /** Gets the callable that this basic block belongs to. */ final Callable getCallable() { result = this.getFirstNode().getEnclosingCallable() } /** Gets the length of this basic block. */ - int length() { result = strictcount(getANode()) } + int length() { result = strictcount(this.getANode()) } /** * Holds if this basic block immediately dominates basic block `bb`. @@ -151,7 +153,7 @@ class BasicBlock extends TBasicBlockStart { */ predicate dominates(BasicBlock bb) { bb = this or - strictlyDominates(bb) + this.strictlyDominates(bb) } /** @@ -177,14 +179,14 @@ class BasicBlock extends TBasicBlockStart { * does not dominate the basic block on line 6. */ predicate inDominanceFrontier(BasicBlock df) { - dominatesPredecessor(df) and - not strictlyDominates(df) + this.dominatesPredecessor(df) and + not this.strictlyDominates(df) } /** * Holds if this basic block dominates a predecessor of `df`. */ - private predicate dominatesPredecessor(BasicBlock df) { dominates(df.getAPredecessor()) } + private predicate dominatesPredecessor(BasicBlock df) { this.dominates(df.getAPredecessor()) } /** * Gets the basic block that immediately dominates this basic block, if any. @@ -263,7 +265,7 @@ class BasicBlock extends TBasicBlockStart { * post-dominates itself. */ predicate postDominates(BasicBlock bb) { - strictlyPostDominates(bb) or + this.strictlyPostDominates(bb) or this = bb } @@ -276,10 +278,10 @@ class BasicBlock extends TBasicBlockStart { predicate inLoop() { this.getASuccessor+() = this } /** Gets a textual representation of this basic block. */ - string toString() { result = getFirstNode().toString() } + string toString() { result = this.getFirstNode().toString() } /** Gets the location of this basic block. */ - Location getLocation() { result = getFirstNode().getLocation() } + Location getLocation() { result = this.getFirstNode().getLocation() } } /** @@ -420,7 +422,7 @@ private module JoinBlockPredecessors { /** A basic block with more than one predecessor. */ class JoinBlock extends BasicBlock { - JoinBlock() { getFirstNode().isJoin() } + JoinBlock() { this.getFirstNode().isJoin() } /** * Gets the `i`th predecessor of this join block, with respect to some diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowElement.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowElement.qll index 7601b83f6b8..9e7fd92d2a4 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowElement.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowElement.qll @@ -72,13 +72,14 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element { ControlFlowElement getAReachableElement() { // Reachable in same basic block exists(BasicBlock bb, int i, int j | - bb.getNode(i) = getAControlFlowNode() and + bb.getNode(i) = this.getAControlFlowNode() and bb.getNode(j) = result.getAControlFlowNode() and i < j ) or // Reachable in different basic blocks - getAControlFlowNode().getBasicBlock().getASuccessor+().getANode() = result.getAControlFlowNode() + this.getAControlFlowNode().getBasicBlock().getASuccessor+().getANode() = + result.getAControlFlowNode() } pragma[noinline] diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowGraph.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowGraph.qll index 96b73d8978d..c94184b4f66 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowGraph.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowGraph.qll @@ -33,10 +33,10 @@ module ControlFlow { ControlFlowElement getElement() { none() } /** Gets the location of this control flow node. */ - Location getLocation() { result = getElement().getLocation() } + Location getLocation() { result = this.getElement().getLocation() } /** Holds if this control flow node has conditional successors. */ - predicate isCondition() { exists(getASuccessorByType(any(ConditionalSuccessor e))) } + predicate isCondition() { exists(this.getASuccessorByType(any(ConditionalSuccessor e))) } /** Gets the basic block that this control flow node belongs to. */ BasicBlock getBasicBlock() { result.getANode() = this } @@ -67,7 +67,7 @@ module ControlFlow { // potentially very large predicate, so must be inlined pragma[inline] predicate dominates(Node that) { - strictlyDominates(that) + this.strictlyDominates(that) or this = that } @@ -138,7 +138,7 @@ module ControlFlow { // potentially very large predicate, so must be inlined pragma[inline] predicate postDominates(Node that) { - strictlyPostDominates(that) + this.strictlyPostDominates(that) or this = that } @@ -186,13 +186,13 @@ module ControlFlow { Node getASuccessorByType(SuccessorType t) { result = getASuccessor(this, t) } /** Gets an immediate successor, if any. */ - Node getASuccessor() { result = getASuccessorByType(_) } + Node getASuccessor() { result = this.getASuccessorByType(_) } /** Gets an immediate predecessor node of a given flow type, if any. */ Node getAPredecessorByType(SuccessorType t) { result.getASuccessorByType(t) = this } /** Gets an immediate predecessor, if any. */ - Node getAPredecessor() { result = getAPredecessorByType(_) } + Node getAPredecessor() { result = this.getAPredecessorByType(_) } /** * Gets an immediate `true` successor, if any. @@ -211,7 +211,7 @@ module ControlFlow { * on line 1. */ Node getATrueSuccessor() { - result = getASuccessorByType(any(BooleanSuccessor t | t.getValue() = true)) + result = this.getASuccessorByType(any(BooleanSuccessor t | t.getValue() = true)) } /** @@ -231,7 +231,7 @@ module ControlFlow { * on line 1. */ Node getAFalseSuccessor() { - result = getASuccessorByType(any(BooleanSuccessor t | t.getValue() = false)) + result = this.getASuccessorByType(any(BooleanSuccessor t | t.getValue() = false)) } /** Holds if this node has more than one predecessor. */ @@ -285,7 +285,7 @@ module ControlFlow { override Callable getEnclosingCallable() { result = this.getCallable() } - override Location getLocation() { result = getCallable().getLocation() } + override Location getLocation() { result = this.getCallable().getLocation() } override string toString() { exists(string s | @@ -293,7 +293,7 @@ module ControlFlow { or normal = false and s = "abnormal" | - result = "exit " + getCallable() + " (" + s + ")" + result = "exit " + this.getCallable() + " (" + s + ")" ) } } @@ -307,9 +307,9 @@ module ControlFlow { override Callable getEnclosingCallable() { result = this.getCallable() } - override Location getLocation() { result = getCallable().getLocation() } + override Location getLocation() { result = this.getCallable().getLocation() } - override string toString() { result = "exit " + getCallable().toString() } + override string toString() { result = "exit " + this.getCallable().toString() } } /** diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll index 5a402717401..5e3f00c3c5e 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll @@ -1740,7 +1740,7 @@ module Internal { e = this.getAChildExpr() or exists(Expr mid | - descendant(mid) and + this.descendant(mid) and not interestingDescendantCandidate(mid) and e = mid.getAChildExpr() ) @@ -1748,7 +1748,7 @@ module Internal { /** Holds if `e` is an interesting descendant of this descendant. */ predicate interestingDescendant(Expr e) { - descendant(e) and + this.descendant(e) and interestingDescendantCandidate(e) } } @@ -1797,7 +1797,7 @@ module Internal { override predicate candidate(ControlFlowElement x, ControlFlowElement y) { exists(BasicBlock bb, Declaration d | - candidateAux(x, d, bb) and + this.candidateAux(x, d, bb) and y = any(AccessOrCallExpr e | e.getAControlFlowNode().getBasicBlock() = bb and diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll index 8a02fb95dee..82eb5d302ad 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll @@ -495,7 +495,7 @@ module Expressions { // Flow from last element of left operand to first element of right operand last(this.getLeftOperand(), pred, c) and c.(NullnessCompletion).isNull() and - first(getRightOperand(), succ) + first(this.getRightOperand(), succ) or // Post-order: flow from last element of left operand to element itself last(this.getLeftOperand(), pred, c) and @@ -504,7 +504,7 @@ module Expressions { not c.(NullnessCompletion).isNull() or // Post-order: flow from last element of right operand to element itself - last(getRightOperand(), pred, c) and + last(this.getRightOperand(), pred, c) and c instanceof NormalCompletion and succ = this } @@ -575,7 +575,7 @@ module Expressions { PostOrderTree.super.last(last, c) or // Qualifier exits with a `null` completion - lastQualifier(last, c) and + this.lastQualifier(last, c) and c.(NullnessCompletion).isNull() } @@ -1483,7 +1483,7 @@ module Statements { ) or // Flow into `finally` block - pred = getAFinallyPredecessor(c, true) and + pred = this.getAFinallyPredecessor(c, true) and first(this.getFinally(), succ) } } diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll index 31155dea0ae..de44808b18e 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll @@ -69,9 +69,9 @@ class PreBasicBlock extends ControlFlowElement { ControlFlowElement getFirstElement() { result = this } - ControlFlowElement getLastElement() { result = this.getElement(length() - 1) } + ControlFlowElement getLastElement() { result = this.getElement(this.length() - 1) } - int length() { result = strictcount(getAnElement()) } + int length() { result = strictcount(this.getAnElement()) } predicate immediatelyDominates(PreBasicBlock bb) { bbIDominates(this, bb) } @@ -117,7 +117,7 @@ class ConditionBlock extends PreBasicBlock { pragma[nomagic] predicate controls(PreBasicBlock controlled, SuccessorTypes::ConditionalSuccessor s) { - exists(PreBasicBlock succ, ConditionalCompletion c | immediatelyControls(succ, c) | + exists(PreBasicBlock succ, ConditionalCompletion c | this.immediatelyControls(succ, c) | succ.dominates(controlled) and s = c.getAMatchingSuccessorType() ) diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Splitting.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Splitting.qll index 4d1d39de988..83ea302e691 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Splitting.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Splitting.qll @@ -628,7 +628,7 @@ module FinallySplitting { */ private predicate exit(ControlFlowElement pred, Completion c, boolean inherited) { exists(TryStmt try, FinallySplitType type | - exit0(pred, try, this.getNestLevel(), c) and + this.exit0(pred, try, this.getNestLevel(), c) and type = this.getType() | if last(try.getFinally(), pred, c) @@ -690,18 +690,18 @@ module FinallySplitting { override predicate hasExit(ControlFlowElement pred, ControlFlowElement succ, Completion c) { succ(pred, succ, c) and ( - exit(pred, c, _) + this.exit(pred, c, _) or - exit(pred, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion(), _) + this.exit(pred, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion(), _) ) } override predicate hasExitScope(CfgScope scope, ControlFlowElement last, Completion c) { scopeLast(scope, last, c) and ( - exit(last, c, _) + this.exit(last, c, _) or - exit(last, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion(), _) + this.exit(last, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion(), _) ) } diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/SuccessorType.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/SuccessorType.qll index 648c2cd847c..76da2fb62ef 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/SuccessorType.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/SuccessorType.qll @@ -77,7 +77,7 @@ module SuccessorTypes { class BooleanSuccessor extends ConditionalSuccessor, TBooleanSuccessor { override boolean getValue() { this = TBooleanSuccessor(result) } - override string toString() { result = getValue().toString() } + override string toString() { result = this.getValue().toString() } } /** @@ -310,7 +310,7 @@ module SuccessorTypes { /** Gets the type of exception. */ ExceptionClass getExceptionClass() { this = TExceptionSuccessor(result) } - override string toString() { result = "exception(" + getExceptionClass().getName() + ")" } + override string toString() { result = "exception(" + this.getExceptionClass().getName() + ")" } } /** diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll index 884f4406d01..395cb5cb171 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll @@ -156,6 +156,7 @@ private predicate dominatesPredecessor(BasicBlock bb1, BasicBlock bb2) { } /** Holds if `df` is in the dominance frontier of `bb`. */ +pragma[noinline] private predicate inDominanceFrontier(BasicBlock bb, BasicBlock df) { dominatesPredecessor(bb, df) and not strictlyDominates(bb, df) diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/Bound.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/Bound.qll index b129203db70..55a8b1f4c3f 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/Bound.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/Bound.qll @@ -30,7 +30,7 @@ abstract class Bound extends TBound { * The location spans column `sc` of line `sl` to * column `ec` of line `el` in file `path`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0 diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll index c7140238213..b689602d7bf 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll @@ -404,6 +404,7 @@ private string paramsString(InterpretedCallable c) { ) } +pragma[nomagic] private Element interpretElement0( string namespace, string type, boolean subtypes, string name, string signature ) { diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/FlowSummary.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/FlowSummary.qll index a20c3876050..3e5c8e51ba5 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/FlowSummary.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/FlowSummary.qll @@ -78,4 +78,21 @@ module SummaryComponentStack { class SummarizedCallable = Impl::Public::SummarizedCallable; +private class SummarizedCallableDefaultClearsContent extends Impl::Public::SummarizedCallable { + SummarizedCallableDefaultClearsContent() { + this instanceof Impl::Public::SummarizedCallable or none() + } + + // By default, we assume that all stores into arguments are definite + override predicate clearsContent(int i, DataFlow::Content content) { + exists(SummaryComponentStack output | + this.propagatesFlow(_, output, _) and + output.drop(_) = + SummaryComponentStack::push(SummaryComponent::content(content), + SummaryComponentStack::argument(i)) and + not content instanceof DataFlow::ElementContent + ) + } +} + class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack; diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll index f36783f56c6..f405484a55d 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll @@ -505,20 +505,20 @@ class SystemBooleanFlow extends LibraryTypeDataFlow, SystemBooleanStruct { CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c, boolean preservesValue ) { - methodFlow(source, sink, c) and + this.methodFlow(source, sink, c) and preservesValue = false } private predicate methodFlow( CallableFlowSource source, CallableFlowSink sink, SourceDeclarationMethod m ) { - m = getParseMethod() and + m = this.getParseMethod() and ( source = TCallableFlowSourceArg(0) and sink = TCallableFlowSinkReturn() ) or - m = getTryParseMethod() and + m = this.getTryParseMethod() and ( source = TCallableFlowSourceArg(0) and ( @@ -537,12 +537,12 @@ class SystemUriFlow extends LibraryTypeDataFlow, SystemUriClass { boolean preservesValue ) { ( - constructorFlow(source, sink, c) + this.constructorFlow(source, sink, c) or - methodFlow(source, sink, c) + this.methodFlow(source, sink, c) or exists(Property p | - propertyFlow(p) and + this.propertyFlow(p) and source = TCallableFlowSourceQualifier() and sink = TCallableFlowSinkReturn() and c = p.getGetter() @@ -552,7 +552,7 @@ class SystemUriFlow extends LibraryTypeDataFlow, SystemUriClass { } private predicate constructorFlow(CallableFlowSource source, CallableFlowSink sink, Constructor c) { - c = getAMember() and + c = this.getAMember() and c.getParameter(0).getType() instanceof StringType and source = TCallableFlowSourceArg(0) and sink = TCallableFlowSinkReturn() @@ -567,11 +567,11 @@ class SystemUriFlow extends LibraryTypeDataFlow, SystemUriClass { } private predicate propertyFlow(Property p) { - p = getPathAndQueryProperty() + p = this.getPathAndQueryProperty() or - p = getQueryProperty() + p = this.getQueryProperty() or - p = getOriginalStringProperty() + p = this.getOriginalStringProperty() } } @@ -582,15 +582,15 @@ class SystemIOStringReaderFlow extends LibraryTypeDataFlow, SystemIOStringReader boolean preservesValue ) { ( - constructorFlow(source, sink, c) + this.constructorFlow(source, sink, c) or - methodFlow(source, sink, c) + this.methodFlow(source, sink, c) ) and preservesValue = false } private predicate constructorFlow(CallableFlowSource source, CallableFlowSink sink, Constructor c) { - c = getAMember() and + c = this.getAMember() and c.getParameter(0).getType() instanceof StringType and source = TCallableFlowSourceArg(0) and sink = TCallableFlowSinkReturn() @@ -599,7 +599,7 @@ class SystemIOStringReaderFlow extends LibraryTypeDataFlow, SystemIOStringReader private predicate methodFlow( CallableFlowSource source, CallableFlowSink sink, SourceDeclarationMethod m ) { - m.getDeclaringType() = getABaseType*() and + m.getDeclaringType() = this.getABaseType*() and m.getName().matches("Read%") and source = TCallableFlowSourceQualifier() and sink = TCallableFlowSinkReturn() @@ -612,17 +612,17 @@ class SystemStringFlow extends LibraryTypeDataFlow, SystemStringClass { CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, SourceDeclarationCallable c, boolean preservesValue ) { - constructorFlow(source, sourceAp, sink, sinkAp, c) and + this.constructorFlow(source, sourceAp, sink, sinkAp, c) and preservesValue = false or - methodFlow(source, sourceAp, sink, sinkAp, c, preservesValue) + this.methodFlow(source, sourceAp, sink, sinkAp, c, preservesValue) } private predicate constructorFlow( CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, Constructor c ) { - c = getAMember() and + c = this.getAMember() and c.getParameter(0).getType().(ArrayType).getElementType() instanceof CharType and source = TCallableFlowSourceArg(0) and sourceAp = AccessPath::element() and @@ -641,14 +641,14 @@ class SystemStringFlow extends LibraryTypeDataFlow, SystemStringClass { sinkAp = AccessPath::empty() and preservesValue = true or - m = getSplitMethod() and + m = this.getSplitMethod() and source = TCallableFlowSourceQualifier() and sourceAp = AccessPath::empty() and sink = TCallableFlowSinkReturn() and sinkAp = AccessPath::element() and preservesValue = false or - m = getReplaceMethod() and + m = this.getReplaceMethod() and sourceAp = AccessPath::empty() and sinkAp = AccessPath::empty() and ( @@ -661,21 +661,21 @@ class SystemStringFlow extends LibraryTypeDataFlow, SystemStringClass { preservesValue = false ) or - m = getSubstringMethod() and + m = this.getSubstringMethod() and source = TCallableFlowSourceQualifier() and sourceAp = AccessPath::empty() and sink = TCallableFlowSinkReturn() and sinkAp = AccessPath::empty() and preservesValue = false or - m = getCloneMethod() and + m = this.getCloneMethod() and source = TCallableFlowSourceQualifier() and sourceAp = AccessPath::empty() and sink = TCallableFlowSinkReturn() and sinkAp = AccessPath::empty() and preservesValue = true or - m = getInsertMethod() and + m = this.getInsertMethod() and sourceAp = AccessPath::empty() and sinkAp = AccessPath::empty() and ( @@ -688,21 +688,21 @@ class SystemStringFlow extends LibraryTypeDataFlow, SystemStringClass { preservesValue = false ) or - m = getNormalizeMethod() and + m = this.getNormalizeMethod() and source = TCallableFlowSourceQualifier() and sourceAp = AccessPath::empty() and sink = TCallableFlowSinkReturn() and sinkAp = AccessPath::empty() and preservesValue = false or - m = getRemoveMethod() and + m = this.getRemoveMethod() and source = TCallableFlowSourceQualifier() and sourceAp = AccessPath::empty() and sink = TCallableFlowSinkReturn() and sinkAp = AccessPath::empty() and preservesValue = false or - m = getAMethod() and + m = this.getAMethod() and m.getName().regexpMatch("((ToLower|ToUpper)(Invariant)?)|(Trim(Start|End)?)|(Pad(Left|Right))") and source = TCallableFlowSourceQualifier() and sourceAp = AccessPath::empty() and @@ -710,7 +710,7 @@ class SystemStringFlow extends LibraryTypeDataFlow, SystemStringClass { sinkAp = AccessPath::empty() and preservesValue = false or - m = getConcatMethod() and + m = this.getConcatMethod() and exists(int i | source = getFlowSourceArg(m, i, sourceAp) and sink = TCallableFlowSinkReturn() and @@ -718,20 +718,20 @@ class SystemStringFlow extends LibraryTypeDataFlow, SystemStringClass { preservesValue = false ) or - m = getCopyMethod() and + m = this.getCopyMethod() and source = TCallableFlowSourceArg(0) and sourceAp = AccessPath::empty() and sink = TCallableFlowSinkReturn() and sinkAp = AccessPath::empty() and preservesValue = true or - m = getJoinMethod() and + m = this.getJoinMethod() and source = getFlowSourceArg(m, [0, 1], sourceAp) and sink = TCallableFlowSinkReturn() and sinkAp = AccessPath::empty() and preservesValue = false or - m = getFormatMethod() and + m = this.getFormatMethod() and exists(int i | (m.getParameter(0).getType() instanceof SystemIFormatProviderInterface implies i != 0) and source = getFlowSourceArg(m, i, sourceAp) and @@ -749,10 +749,10 @@ class SystemTextStringBuilderFlow extends LibraryTypeDataFlow, SystemTextStringB SourceDeclarationCallable c, boolean preservesValue ) { ( - constructorFlow(source, sourceAp, sink, sinkAp, c) and + this.constructorFlow(source, sourceAp, sink, sinkAp, c) and preservesValue = true or - methodFlow(source, sourceAp, sink, sinkAp, c, preservesValue) + this.methodFlow(source, sourceAp, sink, sinkAp, c, preservesValue) ) } @@ -760,7 +760,7 @@ class SystemTextStringBuilderFlow extends LibraryTypeDataFlow, SystemTextStringB CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, Constructor c ) { - c = getAMember() and + c = this.getAMember() and c.getParameter(0).getType() instanceof StringType and source = TCallableFlowSourceArg(0) and sourceAp = AccessPath::empty() and @@ -894,7 +894,7 @@ class IEnumerableFlow extends LibraryTypeDataFlow, RefType { ) { preservesValue = true and ( - methodFlowLINQExtensions(source, sourceAp, sink, sinkAp, c) + this.methodFlowLINQExtensions(source, sourceAp, sink, sinkAp, c) or c = this.getFind() and sourceAp = AccessPath::element() and @@ -1674,14 +1674,14 @@ class SystemConvertFlow extends LibraryTypeDataFlow, SystemConvertClass { CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c, boolean preservesValue ) { - methodFlow(source, sink, c) and + this.methodFlow(source, sink, c) and preservesValue = false } private predicate methodFlow( CallableFlowSource source, CallableFlowSink sink, SourceDeclarationMethod m ) { - m = getAMethod() and + m = this.getAMethod() and source = TCallableFlowSourceArg(0) and sink = TCallableFlowSinkReturn() } @@ -1694,7 +1694,7 @@ class SystemWebHttpCookieFlow extends LibraryTypeDataFlow, SystemWebHttpCookie { boolean preservesValue ) { exists(Property p | - propertyFlow(p) and + this.propertyFlow(p) and source = TCallableFlowSourceQualifier() and sink = TCallableFlowSinkReturn() and c = p.getGetter() @@ -1703,8 +1703,8 @@ class SystemWebHttpCookieFlow extends LibraryTypeDataFlow, SystemWebHttpCookie { } private predicate propertyFlow(Property p) { - p = getValueProperty() or - p = getValuesProperty() + p = this.getValueProperty() or + p = this.getValuesProperty() } } @@ -1715,7 +1715,7 @@ class SystemNetCookieFlow extends LibraryTypeDataFlow, SystemNetCookieClass { boolean preservesValue ) { exists(Property p | - propertyFlow(p) and + this.propertyFlow(p) and source = TCallableFlowSourceQualifier() and sink = TCallableFlowSinkReturn() and c = p.getGetter() @@ -1733,7 +1733,7 @@ class SystemNetIPHostEntryFlow extends LibraryTypeDataFlow, SystemNetIPHostEntry boolean preservesValue ) { exists(Property p | - propertyFlow(p) and + this.propertyFlow(p) and source = TCallableFlowSourceQualifier() and sink = TCallableFlowSinkReturn() and c = p.getGetter() @@ -1742,8 +1742,8 @@ class SystemNetIPHostEntryFlow extends LibraryTypeDataFlow, SystemNetIPHostEntry } private predicate propertyFlow(Property p) { - p = getHostNameProperty() or - p = getAliasesProperty() + p = this.getHostNameProperty() or + p = this.getAliasesProperty() } } @@ -1755,7 +1755,7 @@ class SystemWebUIWebControlsTextBoxFlow extends LibraryTypeDataFlow, boolean preservesValue ) { exists(Property p | - propertyFlow(p) and + this.propertyFlow(p) and source = TCallableFlowSourceQualifier() and sink = TCallableFlowSinkReturn() and c = p.getGetter() @@ -1763,7 +1763,7 @@ class SystemWebUIWebControlsTextBoxFlow extends LibraryTypeDataFlow, preservesValue = false } - private predicate propertyFlow(Property p) { p = getTextProperty() } + private predicate propertyFlow(Property p) { p = this.getTextProperty() } } /** Data flow for `System.Collections.Generic.KeyValuePair`. */ @@ -1864,11 +1864,11 @@ class SystemThreadingTasksTaskFlow extends LibraryTypeDataFlow, SystemThreadingT SourceDeclarationCallable c, boolean preservesValue ) { ( - constructorFlow(source, sink, c) and + this.constructorFlow(source, sink, c) and sourceAp = AccessPath::empty() and sinkAp = AccessPath::empty() or - methodFlow(source, sourceAp, sink, sinkAp, c) + this.methodFlow(source, sourceAp, sink, sinkAp, c) ) and preservesValue = true } @@ -1954,9 +1954,9 @@ class SystemThreadingTasksTaskTFlow extends LibraryTypeDataFlow, SystemThreading SourceDeclarationCallable c, boolean preservesValue ) { ( - constructorFlow(source, sourceAp, sink, sinkAp, c) + this.constructorFlow(source, sourceAp, sink, sinkAp, c) or - methodFlow(source, sourceAp, sink, sinkAp, c) + this.methodFlow(source, sourceAp, sink, sinkAp, c) ) and preservesValue = true or @@ -2101,14 +2101,14 @@ private class SystemRuntimeCompilerServicesConfiguredTaskAwaitableTFlow extends class SystemThreadingTasksFactoryFlow extends LibraryTypeDataFlow { SystemThreadingTasksFactoryFlow() { this instanceof SystemThreadingTasksClass and - getName().regexpMatch("TaskFactory(<>)?") + this.getName().regexpMatch("TaskFactory(<>)?") } override predicate callableFlow( CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, SourceDeclarationCallable c, boolean preservesValue ) { - methodFlow(source, sourceAp, sink, sinkAp, c) and + this.methodFlow(source, sourceAp, sink, sinkAp, c) and preservesValue = true } @@ -2236,12 +2236,12 @@ library class SystemTextEncodingFlow extends LibraryTypeDataFlow, SystemTextEnco preservesValue = false and c = this.getAMethod() and exists(Method m | m.getAnOverrider*().getUnboundDeclaration() = c | - m = getGetBytesMethod() and + m = this.getGetBytesMethod() and source = getFlowSourceArg(m, 0, sourceAp) and sink = TCallableFlowSinkReturn() and sinkAp = AccessPath::empty() or - m = [getGetStringMethod(), getGetCharsMethod()] and + m = [this.getGetStringMethod(), this.getGetCharsMethod()] and source = TCallableFlowSourceArg(0) and sourceAp = AccessPath::element() and sink = TCallableFlowSinkReturn() and @@ -2257,9 +2257,9 @@ library class SystemIOMemoryStreamFlow extends LibraryTypeDataFlow, SystemIOMemo boolean preservesValue ) { ( - constructorFlow(source, sink, c) + this.constructorFlow(source, sink, c) or - c = getToArrayMethod().getAnOverrider*() and + c = this.getToArrayMethod().getAnOverrider*() and source = TCallableFlowSourceQualifier() and sink = TCallableFlowSinkReturn() ) and @@ -2267,7 +2267,7 @@ library class SystemIOMemoryStreamFlow extends LibraryTypeDataFlow, SystemIOMemo } private predicate constructorFlow(CallableFlowSource source, CallableFlowSink sink, Constructor c) { - c = getAMember() and + c = this.getAMember() and c.getParameter(0).getType().(ArrayType).getElementType() instanceof ByteType and source = TCallableFlowSourceArg(0) and sink = TCallableFlowSinkReturn() @@ -2281,17 +2281,17 @@ class SystemIOStreamFlow extends LibraryTypeDataFlow, SystemIOStreamClass { boolean preservesValue ) { ( - c = getAReadMethod().getAnOverrider*() and + c = this.getAReadMethod().getAnOverrider*() and c.getParameter(0).getType().(ArrayType).getElementType() instanceof ByteType and sink = TCallableFlowSinkArg(0) and source = TCallableFlowSourceQualifier() or - c = getAWriteMethod().getAnOverrider*() and + c = this.getAWriteMethod().getAnOverrider*() and c.getParameter(0).getType().(ArrayType).getElementType() instanceof ByteType and source = TCallableFlowSourceArg(0) and sink = TCallableFlowSinkQualifier() or - c = any(Method m | m = getAMethod() and m.getName().matches("CopyTo%")).getAnOverrider*() and + c = any(Method m | m = this.getAMethod() and m.getName().matches("CopyTo%")).getAnOverrider*() and c.getParameter(0).getType() instanceof SystemIOStreamClass and source = TCallableFlowSourceQualifier() and sink = TCallableFlowSinkArg(0) @@ -2307,12 +2307,12 @@ class SystemIOCompressionDeflateStreamFlow extends LibraryTypeDataFlow, CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c, boolean preservesValue ) { - constructorFlow(source, sink, c) and + this.constructorFlow(source, sink, c) and preservesValue = false } private predicate constructorFlow(CallableFlowSource source, CallableFlowSink sink, Constructor c) { - c = getAMember() and + c = this.getAMember() and source = TCallableFlowSourceArg(0) and sink = TCallableFlowSinkReturn() } @@ -2324,7 +2324,7 @@ class SystemXmlXmlReaderFlow extends LibraryTypeDataFlow, SystemXmlXmlReaderClas CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c, boolean preservesValue ) { - c = getCreateMethod() and + c = this.getCreateMethod() and source = TCallableFlowSourceArg(0) and sink = TCallableFlowSinkReturn() and preservesValue = false @@ -2337,7 +2337,7 @@ class SystemXmlXmlDocumentFlow extends LibraryTypeDataFlow, SystemXmlXmlDocument CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c, boolean preservesValue ) { - c = getLoadMethod() and + c = this.getLoadMethod() and source = TCallableFlowSourceArg(0) and sink = TCallableFlowSinkQualifier() and preservesValue = false @@ -2352,13 +2352,13 @@ class SystemXmlXmlNodeFlow extends LibraryTypeDataFlow, SystemXmlXmlNodeClass { ) { ( exists(Property p | - p = getAProperty() and + p = this.getAProperty() and c = p.getGetter() and source = TCallableFlowSourceQualifier() and sink = TCallableFlowSinkReturn() ) or - c = getASelectNodeMethod() and + c = this.getASelectNodeMethod() and source = TCallableFlowSourceQualifier() and sink = TCallableFlowSinkReturn() ) and @@ -2372,7 +2372,7 @@ class SystemXmlXmlNamedNodeMapFlow extends LibraryTypeDataFlow, SystemXmlXmlName CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c, boolean preservesValue ) { - c = getGetNamedItemMethod() and + c = this.getGetNamedItemMethod() and source = TCallableFlowSourceQualifier() and sink = TCallableFlowSinkReturn() and preservesValue = true @@ -2385,14 +2385,14 @@ class SystemIOPathFlow extends LibraryTypeDataFlow, SystemIOPathClass { CallableFlowSource source, AccessPath sourceAp, CallableFlowSink sink, AccessPath sinkAp, SourceDeclarationCallable c, boolean preservesValue ) { - c = getAMethod("Combine") and + c = this.getAMethod("Combine") and source = getFlowSourceArg(c, _, sourceAp) and sink = TCallableFlowSinkReturn() and sinkAp = AccessPath::empty() and preservesValue = false or exists(Parameter p | - c = getAMethod() and + c = this.getAMethod() and c.getName().matches("Get%") and p = c.getAParameter() and p.hasName("path") and @@ -2411,10 +2411,10 @@ class SystemWebHttpUtilityFlow extends LibraryTypeDataFlow, SystemWebHttpUtility boolean preservesValue ) { ( - c = getAnHtmlAttributeEncodeMethod() or - c = getAnHtmlEncodeMethod() or - c = getAJavaScriptStringEncodeMethod() or - c = getAnUrlEncodeMethod() + c = this.getAnHtmlAttributeEncodeMethod() or + c = this.getAnHtmlEncodeMethod() or + c = this.getAJavaScriptStringEncodeMethod() or + c = this.getAnUrlEncodeMethod() ) and source = TCallableFlowSourceArg(0) and sink = TCallableFlowSinkReturn() and @@ -2429,8 +2429,8 @@ class SystemWebHttpServerUtilityFlow extends LibraryTypeDataFlow, SystemWebHttpS boolean preservesValue ) { ( - c = getAnHtmlEncodeMethod() or - c = getAnUrlEncodeMethod() + c = this.getAnHtmlEncodeMethod() or + c = this.getAnUrlEncodeMethod() ) and source = TCallableFlowSourceArg(0) and sink = TCallableFlowSinkReturn() and @@ -2445,8 +2445,8 @@ class SystemNetWebUtilityFlow extends LibraryTypeDataFlow, SystemNetWebUtility { boolean preservesValue ) { ( - c = getAnHtmlEncodeMethod() or - c = getAnUrlEncodeMethod() + c = this.getAnHtmlEncodeMethod() or + c = this.getAnUrlEncodeMethod() ) and source = TCallableFlowSourceArg(0) and sink = TCallableFlowSinkReturn() and diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll index 44307d68e1f..4f70b53275d 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll @@ -76,9 +76,9 @@ module Ssa { override Callable getEnclosingCallable() { this = SsaImpl::TLocalVar(result, _) } - override string toString() { result = getAssignable().getName() } + override string toString() { result = this.getAssignable().getName() } - override Location getLocation() { result = getAssignable().getLocation() } + override Location getLocation() { result = this.getAssignable().getLocation() } } /** A fully qualified field or property. */ @@ -105,7 +105,7 @@ module Ssa { ) } - override Location getLocation() { result = getFirstAccess().getLocation() } + override Location getLocation() { result = this.getFirstAccess().getLocation() } } /** A plain field or property. */ @@ -115,8 +115,8 @@ module Ssa { override string toString() { exists(Assignable f, string prefix | - f = getAssignable() and - result = prefix + "." + getAssignable() + f = this.getAssignable() and + result = prefix + "." + this.getAssignable() | if f.(Modifiable).isStatic() then prefix = f.getDeclaringType().getQualifiedName() @@ -134,7 +134,7 @@ module Ssa { override SourceVariable getQualifier() { this = SsaImpl::TQualifiedFieldOrProp(_, result, _) } - override string toString() { result = getQualifier() + "." + getAssignable() } + override string toString() { result = this.getQualifier() + "." + this.getAssignable() } } } @@ -611,20 +611,20 @@ module Ssa { * and which targets the same assignable as this SSA definition. */ final AssignableDefinition getAPossibleDefinition() { - exists(Callable setter | SsaImpl::updatesNamedFieldOrProp(_, _, getCall(), _, setter) | + exists(Callable setter | SsaImpl::updatesNamedFieldOrProp(_, _, this.getCall(), _, setter) | result.getEnclosingCallable() = setter and result.getTarget() = this.getSourceVariable().getAssignable() ) or - SsaImpl::updatesCapturedVariable(_, _, getCall(), _, result, _) and + SsaImpl::updatesCapturedVariable(_, _, this.getCall(), _, result, _) and result.getTarget() = this.getSourceVariable().getAssignable() } override string toString() { - result = getToStringPrefix(this) + "SSA call def(" + getSourceVariable() + ")" + result = getToStringPrefix(this) + "SSA call def(" + this.getSourceVariable() + ")" } - override Location getLocation() { result = getCall().getLocation() } + override Location getLocation() { result = this.getCall().getLocation() } } /** @@ -649,10 +649,10 @@ module Ssa { final Definition getQualifierDefinition() { result = q } override string toString() { - result = getToStringPrefix(this) + "SSA qualifier def(" + getSourceVariable() + ")" + result = getToStringPrefix(this) + "SSA qualifier def(" + this.getSourceVariable() + ")" } - override Location getLocation() { result = getQualifierDefinition().getLocation() } + override Location getLocation() { result = this.getQualifierDefinition().getLocation() } } /** @@ -699,7 +699,7 @@ module Ssa { } override string toString() { - result = getToStringPrefix(this) + "SSA phi(" + getSourceVariable() + ")" + result = getToStringPrefix(this) + "SSA phi(" + this.getSourceVariable() + ")" } /* diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll index d38975f24bb..0182040fd11 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll @@ -262,6 +262,19 @@ abstract class DataFlowCall extends TDataFlowCall { /** Gets the location of this call. */ abstract Location getLocation(); + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + final predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } } /** A non-delegate C# call relevant for data flow. */ diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index f588a25a176..e11244c42b0 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -801,6 +801,9 @@ private module Cached { exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) } + cached + predicate allowParameterReturnInSelfCached(ParamNode p) { allowParameterReturnInSelf(p) } + cached newtype TCallContext = TAnyCallContext() or @@ -937,7 +940,7 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall { } override predicate relevantFor(DataFlowCallable callable) { - recordDataFlowCallSite(getCall(), callable) + recordDataFlowCallSite(this.getCall(), callable) } override predicate matchesCall(DataFlowCall call) { call = this.getCall() } @@ -1236,6 +1239,13 @@ class TypedContent extends MkTypedContent { /** Gets a textual representation of this content. */ string toString() { result = c.toString() } + + /** + * Holds if access paths with this `TypedContent` at their head always should + * be tracked at high precision. This disables adaptive access path precision + * for such access paths. + */ + predicate forceHighPrecision() { forceHighPrecision(c) } } /** @@ -1250,7 +1260,7 @@ abstract class AccessPathFront extends TAccessPathFront { TypedContent getHead() { this = TFrontHead(result) } - predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) } + predicate isClearedAt(Node n) { clearsContentCached(n, this.getHead().getContent()) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll index a55e65a81f6..dd64fc70039 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll @@ -175,6 +175,7 @@ module Consistency { query predicate postWithInFlow(Node n, string msg) { isPostUpdateNode(n) and + not clearsContent(n, _) and simpleLocalFlowStep(_, n) and msg = "PostUpdateNode should not be the target of local flow." } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll index ca4d0fa98e7..d47cfd95d8f 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -310,6 +310,18 @@ module LocalFlow { result = n.(ExplicitParameterNode).getSsaDefinition() } + /** + * Holds if there is a local use-use flow step from `nodeFrom` to `nodeTo` + * involving SSA definition `def`. + */ + predicate localSsaFlowStepUseUse(Ssa::Definition def, Node nodeFrom, Node nodeTo) { + exists(ControlFlow::Node cfnFrom, ControlFlow::Node cfnTo | + SsaImpl::adjacentReadPairSameVar(def, cfnFrom, cfnTo) and + nodeTo = TExprNode(cfnTo) and + nodeFrom = TExprNode(cfnFrom) + ) + } + /** * Holds if there is a local flow step from `nodeFrom` to `nodeTo` involving * SSA definition `def. @@ -322,14 +334,7 @@ module LocalFlow { ) or // Flow from read to next read - exists(ControlFlow::Node cfnFrom, ControlFlow::Node cfnTo | - SsaImpl::adjacentReadPairSameVar(def, cfnFrom, cfnTo) and - nodeTo = TExprNode(cfnTo) - | - nodeFrom = TExprNode(cfnFrom) - or - cfnFrom = nodeFrom.(PostUpdateNode).getPreUpdateNode().getControlFlowNode() - ) + localSsaFlowStepUseUse(def, nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo) or // Flow into phi node exists(Ssa::PhiNode phi | @@ -399,6 +404,12 @@ module LocalFlow { predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) or + exists(Ssa::Definition def | + LocalFlow::localSsaFlowStepUseUse(def, nodeFrom, nodeTo) and + not FlowSummaryImpl::Private::Steps::summaryClearsContentArg(nodeFrom, _) and + not LocalFlow::usesInstanceField(def) + ) + or LocalFlow::localFlowCapturedVarStep(nodeFrom, nodeTo) or FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true) @@ -716,6 +727,8 @@ private module Cached { predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) { LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) or + LocalFlow::localSsaFlowStepUseUse(_, nodeFrom, nodeTo) + or exists(Ssa::Definition def | LocalFlow::localSsaFlowStep(def, nodeFrom, nodeTo) and LocalFlow::usesInstanceField(def) @@ -940,13 +953,9 @@ private module ParameterNodes { import ParameterNodes /** A data-flow node that represents a call argument. */ -class ArgumentNode extends Node { - ArgumentNode() { this instanceof ArgumentNodeImpl } - +class ArgumentNode extends Node instanceof ArgumentNodeImpl { /** Holds if this argument occurs at the given position in the given call. */ - final predicate argumentOf(DataFlowCall call, int pos) { - this.(ArgumentNodeImpl).argumentOf(call, pos) - } + final predicate argumentOf(DataFlowCall call, int pos) { super.argumentOf(call, pos) } } abstract private class ArgumentNodeImpl extends Node { @@ -1695,9 +1704,6 @@ predicate clearsContent(Node n, Content c) { or fieldOrPropertyStore(_, c, _, n.(ObjectInitializerNode).getInitializer(), false) or - FlowSummaryImpl::Private::Steps::summaryStoresIntoArg(c, n) and - not c instanceof ElementContent - or FlowSummaryImpl::Private::Steps::summaryClearsContent(n, c) or exists(WithExpr we, ObjectInitializer oi, FieldOrProperty f | @@ -1918,6 +1924,12 @@ private predicate viableConstantBooleanParamArg( int accessPathLimit() { result = 5 } +/** + * Holds if access paths with `c` at their head always should be tracked at high + * precision. This disables adaptive access path precision for such access paths. + */ +predicate forceHighPrecision(Content c) { c instanceof ElementContent } + /** The unit type. */ private newtype TUnit = TMkUnit() @@ -2001,3 +2013,14 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves preservesValue = false ) } + +/** + * Holds if flow is allowed to pass from parameter `p` and back to itself as a + * side-effect, resulting in a summary from `p` to itself. + * + * One example would be to allow flow like `p.foo = p.bar;`, which is disallowed + * by default as a heuristic. + */ +predicate allowParameterReturnInSelf(ParameterNode p) { + FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(p) +} diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll index bfc2f5469d0..fa99d518bbd 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll @@ -58,7 +58,7 @@ class Node extends TNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -98,9 +98,7 @@ class ExprNode extends Node, TExprNode_ { * The value of a parameter at function entry, viewed as a node in a data * flow graph. */ -class ParameterNode extends Node { - ParameterNode() { this instanceof ParameterNodeImpl } - +class ParameterNode extends Node instanceof ParameterNodeImpl { /** Gets the parameter corresponding to this node, if any. */ DotNet::Parameter getParameter() { exists(DataFlowCallable c, int i | this.isParameterOf(c, i) and result = c.getParameter(i)) @@ -110,9 +108,7 @@ class ParameterNode extends Node { * Holds if this node is the parameter of callable `c` at the specified * (zero-based) position. */ - predicate isParameterOf(DataFlowCallable c, int i) { - this.(ParameterNodeImpl).isParameterOf(c, i) - } + predicate isParameterOf(DataFlowCallable c, int i) { super.isParameterOf(c, i) } } /** A definition, viewed as a node in a data flow graph. */ diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll index 523516e60f8..5955285bd6f 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll @@ -186,10 +186,17 @@ module Private { TArgumentSummaryComponent(int i) { parameterPosition(i) } or TReturnSummaryComponent(ReturnKind rk) + private TSummaryComponent thisParam() { + result = TParameterSummaryComponent(instanceParameterPosition()) + } + newtype TSummaryComponentStack = TSingletonSummaryComponentStack(SummaryComponent c) or TConsSummaryComponentStack(SummaryComponent head, SummaryComponentStack tail) { tail.(RequiredSummaryComponentStack).required(head) + or + tail.(RequiredSummaryComponentStack).required(TParameterSummaryComponent(_)) and + head = thisParam() } pragma[nomagic] @@ -198,20 +205,65 @@ module Private { boolean preservesValue ) { c.propagatesFlow(input, output, preservesValue) + or + // observe side effects of callbacks on input arguments + c.propagatesFlow(output, input, preservesValue) and + preservesValue = true and + isCallbackParameter(input) and + isContentOfArgument(output) + or + // flow from the receiver of a callback into the instance-parameter + exists(SummaryComponentStack s, SummaryComponentStack callbackRef | + c.propagatesFlow(s, _, _) or c.propagatesFlow(_, s, _) + | + callbackRef = s.drop(_) and + (isCallbackParameter(callbackRef) or callbackRef.head() = TReturnSummaryComponent(_)) and + input = callbackRef.tail() and + output = TConsSummaryComponentStack(thisParam(), input) and + preservesValue = true + ) + } + + private predicate isCallbackParameter(SummaryComponentStack s) { + s.head() = TParameterSummaryComponent(_) and exists(s.tail()) + } + + private predicate isContentOfArgument(SummaryComponentStack s) { + s.head() = TContentSummaryComponent(_) and isContentOfArgument(s.tail()) + or + s = TSingletonSummaryComponentStack(TArgumentSummaryComponent(_)) + } + + private predicate outputState(SummarizedCallable c, SummaryComponentStack s) { + summary(c, _, s, _) + or + exists(SummaryComponentStack out | + outputState(c, out) and + out.head() = TContentSummaryComponent(_) and + s = out.tail() + ) + or + // Add the argument node corresponding to the requested post-update node + inputState(c, s) and isCallbackParameter(s) + } + + private predicate inputState(SummarizedCallable c, SummaryComponentStack s) { + summary(c, s, _, _) + or + exists(SummaryComponentStack inp | inputState(c, inp) and s = inp.tail()) + or + exists(SummaryComponentStack out | + outputState(c, out) and + out.head() = TParameterSummaryComponent(_) and + s = out.tail() + ) } private newtype TSummaryNodeState = - TSummaryNodeInputState(SummaryComponentStack s) { - exists(SummaryComponentStack input | - summary(_, input, _, _) and - s = input.drop(_) - ) - } or - TSummaryNodeOutputState(SummaryComponentStack s) { - exists(SummaryComponentStack output | - summary(_, _, output, _) and - s = output.drop(_) - ) + TSummaryNodeInputState(SummaryComponentStack s) { inputState(_, s) } or + TSummaryNodeOutputState(SummaryComponentStack s) { outputState(_, s) } or + TSummaryNodeClearsContentState(int i, boolean post) { + any(SummarizedCallable sc).clearsContent(i, _) and post in [false, true] } /** @@ -238,20 +290,14 @@ module Private { pragma[nomagic] predicate isInputState(SummarizedCallable c, SummaryComponentStack s) { this = TSummaryNodeInputState(s) and - exists(SummaryComponentStack input | - summary(c, input, _, _) and - s = input.drop(_) - ) + inputState(c, s) } /** Holds if this state is a valid output state for `c`. */ pragma[nomagic] predicate isOutputState(SummarizedCallable c, SummaryComponentStack s) { this = TSummaryNodeOutputState(s) and - exists(SummaryComponentStack output | - summary(c, _, output, _) and - s = output.drop(_) - ) + outputState(c, s) } /** Gets a textual representation of this state. */ @@ -265,6 +311,12 @@ module Private { this = TSummaryNodeOutputState(s) and result = "to write: " + s ) + or + exists(int i, boolean post, string postStr | + this = TSummaryNodeClearsContentState(i, post) and + (if post = true then postStr = " (post)" else postStr = "") and + result = "clear: " + i + postStr + ) } } @@ -286,6 +338,11 @@ module Private { not parameterReadState(c, state, _) or state.isOutputState(c, _) + or + exists(int i | + c.clearsContent(i, _) and + state = TSummaryNodeClearsContentState(i, _) + ) } pragma[noinline] @@ -321,6 +378,8 @@ module Private { parameterReadState(c, _, i) or isParameterPostUpdate(_, c, i) + or + c.clearsContent(i, _) } private predicate callbackOutput( @@ -331,19 +390,12 @@ module Private { receiver = summaryNodeInputState(c, s.drop(1)) } - private Node pre(Node post) { - summaryPostUpdateNode(post, result) - or - not summaryPostUpdateNode(post, _) and - result = post - } - private predicate callbackInput( SummarizedCallable c, SummaryComponentStack s, Node receiver, int i ) { any(SummaryNodeState state).isOutputState(c, s) and s.head() = TParameterSummaryComponent(i) and - receiver = pre(summaryNodeOutputState(c, s.drop(1))) + receiver = summaryNodeInputState(c, s.drop(1)) } /** Holds if a call targeting `receiver` should be synthesized inside `c`. */ @@ -395,11 +447,17 @@ module Private { or exists(int i | head = TParameterSummaryComponent(i) | result = - getCallbackParameterType(getNodeType(summaryNodeOutputState(pragma[only_bind_out](c), + getCallbackParameterType(getNodeType(summaryNodeInputState(pragma[only_bind_out](c), s.drop(1))), i) ) ) ) + or + exists(SummarizedCallable c, int i, ParamNode p | + n = summaryNode(c, TSummaryNodeClearsContentState(i, false)) and + p.isParameterOf(c, i) and + result = getNodeType(p) + ) } /** Holds if summary node `out` contains output of kind `rk` from call `c`. */ @@ -421,10 +479,19 @@ module Private { } /** Holds if summary node `post` is a post-update node with pre-update node `pre`. */ - predicate summaryPostUpdateNode(Node post, ParamNode pre) { + predicate summaryPostUpdateNode(Node post, Node pre) { exists(SummarizedCallable c, int i | isParameterPostUpdate(post, c, i) and - pre.isParameterOf(c, i) + pre.(ParamNode).isParameterOf(c, i) + or + pre = summaryNode(c, TSummaryNodeClearsContentState(i, false)) and + post = summaryNode(c, TSummaryNodeClearsContentState(i, true)) + ) + or + exists(SummarizedCallable callable, SummaryComponentStack s | + callbackInput(callable, s, _, _) and + pre = summaryNodeOutputState(callable, s) and + post = summaryNodeInputState(callable, s) ) } @@ -436,6 +503,17 @@ module Private { ) } + /** + * Holds if flow is allowed to pass from parameter `p`, to a return + * node, and back out to `p`. + */ + predicate summaryAllowParameterReturnInSelf(ParamNode p) { + exists(SummarizedCallable c, int i | + c.clearsContent(i, _) and + p.isParameterOf(c, i) + ) + } + /** Provides a compilation of flow summaries to atomic data-flow steps. */ module Steps { /** @@ -462,7 +540,21 @@ module Private { // for `StringBuilder.append(x)` with a specified value flow from qualifier to // return value and taint flow from argument 0 to the qualifier, then this // allows us to infer taint flow from argument 0 to the return value. - summaryPostUpdateNode(pred, succ) and preservesValue = true + succ instanceof ParamNode and + summaryPostUpdateNode(pred, succ) and + preservesValue = true + or + // Similarly we would like to chain together summaries where values get passed + // into callbacks along the way. + pred instanceof ArgNode and + summaryPostUpdateNode(succ, pred) and + preservesValue = true + or + exists(SummarizedCallable c, int i | + pred.(ParamNode).isParameterOf(c, i) and + succ = summaryNode(c, TSummaryNodeClearsContentState(i, _)) and + preservesValue = true + ) } /** @@ -490,10 +582,39 @@ module Private { } /** - * Holds if values stored inside content `c` are cleared when passed as - * input of type `input` in `call`. + * Holds if values stored inside content `c` are cleared at `n`. `n` is a + * synthesized summary node, so in order for values to be cleared at calls + * to the relevant method, it is important that flow does not pass over + * the argument, either via use-use flow or def-use flow. + * + * Example: + * + * ``` + * a.b = taint; + * a.clearB(); // assume we have a flow summary for `clearB` that clears `b` on the qualifier + * sink(a.b); + * ``` + * + * In the above, flow should not pass from `a` on the first line (or the second + * line) to `a` on the third line. Instead, there will be synthesized flow from + * `a` on line 2 to the post-update node for `a` on that line (via an intermediate + * node where field `b` is cleared). */ - predicate summaryClearsContent(ArgNode arg, Content c) { + predicate summaryClearsContent(Node n, Content c) { + exists(SummarizedCallable sc, int i | + n = summaryNode(sc, TSummaryNodeClearsContentState(i, true)) and + sc.clearsContent(i, c) + ) + } + + /** + * Holds if values stored inside content `c` are cleared inside a + * callable to which `arg` is an argument. + * + * In such cases, it is important to prevent use-use flow out of + * `arg` (see comment for `summaryClearsContent`). + */ + predicate summaryClearsContentArg(ArgNode arg, Content c) { exists(DataFlowCall call, int i | viableCallable(call).(SummarizedCallable).clearsContent(i, c) and arg.argumentOf(call, i) @@ -553,25 +674,6 @@ module Private { ret.getKind() = rk ) } - - /** - * Holds if data is written into content `c` of argument `arg` using a flow summary. - * - * Depending on the type of `c`, this predicate may be relevant to include in the - * definition of `clearsContent()`. - */ - predicate summaryStoresIntoArg(Content c, Node arg) { - exists(ParamUpdateReturnKind rk, ReturnNodeExt ret, PostUpdateNode out | - exists(DataFlowCall call, SummarizedCallable callable | - getNodeEnclosingCallable(ret) = callable and - viableCallable(call) = callable and - summaryStoreStep(_, c, ret) and - ret.getKind() = pragma[only_bind_into](rk) and - out = rk.getAnOutNode(call) and - arg = out.getPreUpdateNode() - ) - ) - } } /** @@ -824,4 +926,95 @@ module Private { ) } } + + /** + * Provides query predicates for rendering the generated data flow graph for + * a summarized callable. + * + * Import this module into a `.ql` file of `@kind graph` to render the graph. + * The graph is restricted to callables from `RelevantSummarizedCallable`. + */ + module RenderSummarizedCallable { + /** A summarized callable to include in the graph. */ + abstract class RelevantSummarizedCallable extends SummarizedCallable { } + + private newtype TNodeOrCall = + MkNode(Node n) { + exists(RelevantSummarizedCallable c | + n = summaryNode(c, _) + or + n.(ParamNode).isParameterOf(c, _) + ) + } or + MkCall(DataFlowCall call) { + call = summaryDataFlowCall(_) and + call.getEnclosingCallable() instanceof RelevantSummarizedCallable + } + + private class NodeOrCall extends TNodeOrCall { + Node asNode() { this = MkNode(result) } + + DataFlowCall asCall() { this = MkCall(result) } + + string toString() { + result = this.asNode().toString() + or + result = this.asCall().toString() + } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.asNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + or + this.asCall().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + } + + query predicate nodes(NodeOrCall n, string key, string val) { + key = "semmle.label" and val = n.toString() + } + + private predicate edgesComponent(NodeOrCall a, NodeOrCall b, string value) { + exists(boolean preservesValue | + Private::Steps::summaryLocalStep(a.asNode(), b.asNode(), preservesValue) and + if preservesValue = true then value = "value" else value = "taint" + ) + or + exists(Content c | + Private::Steps::summaryReadStep(a.asNode(), c, b.asNode()) and + value = "read (" + c + ")" + or + Private::Steps::summaryStoreStep(a.asNode(), c, b.asNode()) and + value = "store (" + c + ")" + or + Private::Steps::summaryClearsContent(a.asNode(), c) and + b = a and + value = "clear (" + c + ")" + ) + or + summaryPostUpdateNode(b.asNode(), a.asNode()) and + value = "post-update" + or + b.asCall() = summaryDataFlowCall(a.asNode()) and + value = "receiver" + or + exists(int i | + summaryArgumentNode(b.asCall(), a.asNode(), i) and + value = "argument (" + i + ")" + ) + } + + query predicate edges(NodeOrCall a, NodeOrCall b, string key, string value) { + key = "semmle.label" and + value = strictconcat(string s | edgesComponent(a, b, s) | s, " / ") + } + } } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll index b0f67e8692f..822822a24c6 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll @@ -16,6 +16,9 @@ private import semmle.code.csharp.dataflow.ExternalFlow /** Holds is `i` is a valid parameter position. */ predicate parameterPosition(int i) { i in [-1 .. any(Parameter p).getPosition()] } +/** Gets the parameter position of the instance parameter. */ +int instanceParameterPosition() { none() } // disables implicit summary flow to `this` for callbacks + /** Gets the synthesized summary data-flow node for the given values. */ Node summaryNode(SummarizedCallable c, SummaryNodeState state) { result = TSummaryNode(c, state) } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll index 884f4406d01..395cb5cb171 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll @@ -156,6 +156,7 @@ private predicate dominatesPredecessor(BasicBlock bb1, BasicBlock bb2) { } /** Holds if `df` is in the dominance frontier of `bb`. */ +pragma[noinline] private predicate inDominanceFrontier(BasicBlock bb, BasicBlock df) { dominatesPredecessor(bb, df) and not strictlyDominates(bb, df) diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll index 884f4406d01..395cb5cb171 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll @@ -156,6 +156,7 @@ private predicate dominatesPredecessor(BasicBlock bb1, BasicBlock bb2) { } /** Holds if `df` is in the dominance frontier of `bb`. */ +pragma[noinline] private predicate inDominanceFrontier(BasicBlock bb, BasicBlock df) { dominatesPredecessor(bb, df) and not strictlyDominates(bb, df) diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll index 2f8e93a4b75..067a9b94f45 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll @@ -89,14 +89,12 @@ private module Impl { } /** An expression whose value may control the execution of another element. */ - class Guard extends Expr { - Guard() { this instanceof G::Guard } - + class Guard extends Expr instanceof G::Guard { /** * Holds if basic block `bb` is guarded by this guard having value `v`. */ predicate controlsBasicBlock(ControlFlow::BasicBlock bb, G::AbstractValue v) { - this.(G::Guard).controlsBasicBlock(bb, v) + super.controlsBasicBlock(bb, v) } /** @@ -108,7 +106,7 @@ private module Impl { exists(Expr e1_, Expr e2_ | e1 = unique(ExprNode cfn | hasChild(this, e1_, _, cfn) | cfn) and e2 = unique(ExprNode cfn | hasChild(this, e2_, _, cfn) | cfn) and - this.(G::Guard).isEquality(e1_, e2_, polarity) + super.isEquality(e1_, e2_, polarity) ) } } @@ -421,9 +419,9 @@ module ExprNode { * "else" expression of this conditional expression. */ ExprNode getBranchExpr(boolean branch) { - branch = true and result = getTrueExpr() + branch = true and result = this.getTrueExpr() or - branch = false and result = getFalseExpr() + branch = false and result = this.getFalseExpr() } } } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll index 558ecd1b88b..e450c11b5ab 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll @@ -28,7 +28,7 @@ class SsaReadPositionBlock extends SsaReadPosition, TSsaReadPositionBlock { /** Gets the basic block corresponding to this position. */ BasicBlock getBlock() { this = TSsaReadPositionBlock(result) } - override predicate hasReadOfVar(SsaVariable v) { getBlock() = getAReadBasicBlock(v) } + override predicate hasReadOfVar(SsaVariable v) { this.getBlock() = getAReadBasicBlock(v) } override string toString() { result = "block" } } @@ -49,8 +49,8 @@ class SsaReadPositionPhiInputEdge extends SsaReadPosition, TSsaReadPositionPhiIn /** Holds if `inp` is an input to `phi` along this edge. */ predicate phiInput(SsaPhiNode phi, SsaVariable inp) { - phi.hasInputFromBlock(inp, getOrigBlock()) and - getPhiBlock() = phi.getBasicBlock() + phi.hasInputFromBlock(inp, this.getOrigBlock()) and + this.getPhiBlock() = phi.getBasicBlock() } override string toString() { result = "edge" } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll index f4f73b8247c..acb029c23d9 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration { predicate isSanitizer(DataFlow::Node node) { none() } final override predicate isBarrier(DataFlow::Node node) { - isSanitizer(node) or + this.isSanitizer(node) or defaultTaintSanitizer(node) } /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } - final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } + final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) } /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } - final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } + final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } - final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } + final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { + this.isSanitizerGuard(guard) + } /** * Holds if the additional taint propagation step from `node1` to `node2` @@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration { predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() } final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - isAdditionalTaintStep(node1, node2) or + this.isAdditionalTaintStep(node1, node2) or defaultAdditionalTaintStep(node1, node2) } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll index f4f73b8247c..acb029c23d9 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration { predicate isSanitizer(DataFlow::Node node) { none() } final override predicate isBarrier(DataFlow::Node node) { - isSanitizer(node) or + this.isSanitizer(node) or defaultTaintSanitizer(node) } /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } - final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } + final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) } /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } - final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } + final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } - final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } + final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { + this.isSanitizerGuard(guard) + } /** * Holds if the additional taint propagation step from `node1` to `node2` @@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration { predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() } final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - isAdditionalTaintStep(node1, node2) or + this.isAdditionalTaintStep(node1, node2) or defaultAdditionalTaintStep(node1, node2) } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll index f4f73b8247c..acb029c23d9 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll @@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration { predicate isSanitizer(DataFlow::Node node) { none() } final override predicate isBarrier(DataFlow::Node node) { - isSanitizer(node) or + this.isSanitizer(node) or defaultTaintSanitizer(node) } /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } - final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } + final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) } /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } - final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } + final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } - final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } + final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { + this.isSanitizerGuard(guard) + } /** * Holds if the additional taint propagation step from `node1` to `node2` @@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration { predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() } final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - isAdditionalTaintStep(node1, node2) or + this.isAdditionalTaintStep(node1, node2) or defaultAdditionalTaintStep(node1, node2) } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll index f4f73b8247c..acb029c23d9 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll @@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration { predicate isSanitizer(DataFlow::Node node) { none() } final override predicate isBarrier(DataFlow::Node node) { - isSanitizer(node) or + this.isSanitizer(node) or defaultTaintSanitizer(node) } /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } - final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } + final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) } /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } - final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } + final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } - final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } + final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { + this.isSanitizerGuard(guard) + } /** * Holds if the additional taint propagation step from `node1` to `node2` @@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration { predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() } final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - isAdditionalTaintStep(node1, node2) or + this.isAdditionalTaintStep(node1, node2) or defaultAdditionalTaintStep(node1, node2) } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll index f4f73b8247c..acb029c23d9 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll @@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration { predicate isSanitizer(DataFlow::Node node) { none() } final override predicate isBarrier(DataFlow::Node node) { - isSanitizer(node) or + this.isSanitizer(node) or defaultTaintSanitizer(node) } /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } - final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } + final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) } /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } - final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } + final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } - final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } + final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { + this.isSanitizerGuard(guard) + } /** * Holds if the additional taint propagation step from `node1` to `node2` @@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration { predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() } final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - isAdditionalTaintStep(node1, node2) or + this.isAdditionalTaintStep(node1, node2) or defaultAdditionalTaintStep(node1, node2) } diff --git a/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll b/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll index d0b4ef45ce8..509bdfb5e04 100644 --- a/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll +++ b/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll @@ -11,10 +11,10 @@ private import RuntimeCallable /** A call. */ class DispatchCall extends Internal::TDispatchCall { /** Gets a textual representation of this call. */ - string toString() { result = getCall().toString() } + string toString() { result = this.getCall().toString() } /** Gets the location of this call. */ - Location getLocation() { result = getCall().getLocation() } + Location getLocation() { result = this.getCall().getLocation() } /** Gets the underlying expression of this call. */ Expr getCall() { result = Internal::getCall(this) } @@ -209,7 +209,7 @@ private module Internal { abstract Expr getArgument(int i); /** Gets the number of arguments of this call. */ - int getNumberOfArguments() { result = count(int i | exists(getArgument(i))) } + int getNumberOfArguments() { result = count(int i | exists(this.getArgument(i))) } /** Gets the qualifier of this call, if any. */ abstract Expr getQualifier(); @@ -506,12 +506,12 @@ private module Internal { } override RuntimeCallable getADynamicTarget() { - result = getAViableInherited() + result = this.getAViableInherited() or - result = getAViableOverrider() + result = this.getAViableOverrider() or // Simple case: target method cannot be overridden - result = getAStaticTarget() and + result = this.getAStaticTarget() and not result instanceof OverridableCallable } @@ -779,9 +779,9 @@ private module Internal { ) } - override Expr getQualifier() { result = getCall().getQualifier() } + override Expr getQualifier() { result = this.getCall().getQualifier() } - override Method getAStaticTarget() { result = getCall().getTarget() } + override Method getAStaticTarget() { result = this.getCall().getTarget() } } /** @@ -793,24 +793,24 @@ private module Internal { private class DispatchAccessorCall extends DispatchMethodOrAccessorCall, TDispatchAccessorCall { override AccessorCall getCall() { this = TDispatchAccessorCall(result) } - override Expr getArgument(int i) { result = getCall().getArgument(i) } + override Expr getArgument(int i) { result = this.getCall().getArgument(i) } - override Expr getQualifier() { result = getCall().(MemberAccess).getQualifier() } + override Expr getQualifier() { result = this.getCall().(MemberAccess).getQualifier() } - override Accessor getAStaticTarget() { result = getCall().getTarget() } + override Accessor getAStaticTarget() { result = this.getCall().getTarget() } override RuntimeAccessor getADynamicTarget() { result = DispatchMethodOrAccessorCall.super.getADynamicTarget() and // Calls to accessors may have `dynamic` expression arguments, // so we need to check that the types match - forall(Type argumentType, int i | hasDynamicArg(i, argumentType) | + forall(Type argumentType, int i | this.hasDynamicArg(i, argumentType) | argumentType.isImplicitlyConvertibleTo(result.getParameter(i).getType()) ) } private predicate hasDynamicArg(int i, Type argumentType) { exists(Expr argument | - argument = getArgument(i) and + argument = this.getArgument(i) and argument.stripImplicitCasts().getType() instanceof DynamicType and argumentType = getAPossibleType(argument, _) ) @@ -896,7 +896,7 @@ private module Internal { // names and number of parameters. This set is further reduced in // `getADynamicTarget()` by taking type information into account. override Callable getAStaticTarget() { - result = getACallableWithMatchingName() and + result = this.getACallableWithMatchingName() and exists(int minArgs | minArgs = count(Parameter p | @@ -904,16 +904,19 @@ private module Internal { not p.hasDefaultValue() and not p.isParams() ) and - getNumberOfArguments() >= minArgs and - (result.(Method).hasParams() or getNumberOfArguments() <= result.getNumberOfParameters()) + this.getNumberOfArguments() >= minArgs and + ( + result.(Method).hasParams() or + this.getNumberOfArguments() <= result.getNumberOfParameters() + ) ) } private RuntimeCallable getACallableWithMatchingName() { - result.(Operator).getFunctionName() = getName() + result.(Operator).getFunctionName() = this.getName() or not result instanceof Operator and - result.getUndecoratedName() = getName() + result.getUndecoratedName() = this.getName() } // A callable is viable if the following conditions are all satisfied: @@ -987,7 +990,7 @@ private module Internal { * type of one of the arguments. */ RuntimeCallable getADynamicTargetCandidate() { - result = getAStaticTarget() and + result = this.getAStaticTarget() and ( result = getADynamicTargetCandidateInstanceMethod(this.getAQualifierType()) or @@ -999,13 +1002,13 @@ private module Internal { result instanceof RuntimeInstanceAccessor and this.hasUnknownQualifierType() or - result = getADynamicTargetCandidateOperator() + result = this.getADynamicTargetCandidateOperator() ) } pragma[noinline] private RuntimeOperator getADynamicTargetCandidateOperator() { - result = getAStaticTarget() and + result = this.getAStaticTarget() and result.getDeclaringType() = result.getAParameter().getType() } } @@ -1138,8 +1141,8 @@ private module Internal { result = DispatchReflectionOrDynamicCall.super.getADynamicTargetCandidate() or // Static callables can be called using reflection as well - result = getAStaticTarget() and - result.getDeclaringType() = getStaticType() and + result = this.getAStaticTarget() and + result.getDeclaringType() = this.getStaticType() and result.(Modifiable).isStatic() } @@ -1147,7 +1150,7 @@ private module Internal { override Expr getArgument(int i) { exists(int args, ArrayCreation ac | this = TDispatchReflectionCall(_, _, _, _, args) and - ac = getAMethodCallArgSource(getCall().getArgument(args)) and + ac = getAMethodCallArgSource(this.getCall().getArgument(args)) and result = ac.getInitializer().getElement(i) ) } @@ -1158,20 +1161,20 @@ private module Internal { TDispatchDynamicMethodCall { override DynamicMethodCall getCall() { this = TDispatchDynamicMethodCall(result) } - override string getName() { result = getCall().getLateBoundTargetName() } + override string getName() { result = this.getCall().getLateBoundTargetName() } - override Expr getQualifier() { result = getCall().getQualifier() } + override Expr getQualifier() { result = this.getCall().getQualifier() } override RuntimeMethod getADynamicTargetCandidate() { - if exists(getCall().getTarget()) + if exists(this.getCall().getTarget()) then // static method call - result = getCall().getTarget() + result = this.getCall().getTarget() else result = DispatchReflectionOrDynamicCall.super.getADynamicTargetCandidate() } // Does not take named arguments into account - override Expr getArgument(int i) { result = getCall().getArgument(i) } + override Expr getArgument(int i) { result = this.getCall().getArgument(i) } } /** An operator call using dynamic types. */ @@ -1181,14 +1184,14 @@ private module Internal { override string getName() { exists(Operator o | - o.getName() = getCall().getLateBoundTargetName() and + o.getName() = this.getCall().getLateBoundTargetName() and result = o.getFunctionName() ) } override Expr getQualifier() { none() } - override Expr getArgument(int i) { result = getCall().getArgument(i) } + override Expr getArgument(int i) { result = this.getCall().getArgument(i) } } /** A (potential) call to a property accessor using dynamic types. */ @@ -1255,7 +1258,7 @@ private module Internal { any(DynamicMemberAccess dma | this = TDispatchDynamicEventAccess(_, dma, _)).getQualifier() } - override Expr getArgument(int i) { i = 0 and result = getCall().getRValue() } + override Expr getArgument(int i) { i = 0 and result = this.getCall().getRValue() } } /** A call to a constructor using dynamic types. */ @@ -1267,9 +1270,9 @@ private module Internal { override Expr getQualifier() { none() } - override Expr getArgument(int i) { result = getCall().getArgument(i) } + override Expr getArgument(int i) { result = this.getCall().getArgument(i) } - override RuntimeCallable getADynamicTargetCandidate() { result = getCall().getTarget() } + override RuntimeCallable getADynamicTargetCandidate() { result = this.getCall().getTarget() } } /** A call where the target can be resolved statically. */ @@ -1285,8 +1288,8 @@ private module Internal { ) } - override Callable getAStaticTarget() { result = getCall().getTarget() } + override Callable getAStaticTarget() { result = this.getCall().getTarget() } - override RuntimeCallable getADynamicTarget() { result = getCall().getTarget() } + override RuntimeCallable getADynamicTarget() { result = this.getCall().getTarget() } } } diff --git a/csharp/ql/lib/semmle/code/csharp/dispatch/OverridableCallable.qll b/csharp/ql/lib/semmle/code/csharp/dispatch/OverridableCallable.qll index 4913ea2bc3c..dc963881cbf 100644 --- a/csharp/ql/lib/semmle/code/csharp/dispatch/OverridableCallable.qll +++ b/csharp/ql/lib/semmle/code/csharp/dispatch/OverridableCallable.qll @@ -58,13 +58,13 @@ class OverridableCallable extends Callable { * `I.M.getAnImplementorSubType(D) = C.M`. */ private Callable getAnImplementorSubType(ValueOrRefType t) { - result = getAnImplementor(t) + result = this.getAnImplementor(t) or exists(ValueOrRefType mid | - result = getAnImplementorSubType(mid) and + result = this.getAnImplementorSubType(mid) and t.getBaseClass() = mid and // There must be no other implementation of this callable in `t` - forall(Callable other | other = getAnImplementor(t) | other = result) + forall(Callable other | other = this.getAnImplementor(t) | other = result) ) } @@ -107,8 +107,8 @@ class OverridableCallable extends Callable { * implements this interface callable, if any. */ private Callable getAnOverridingImplementor() { - result = getAnUltimateImplementor() and - not result = getAnImplementor(_) + result = this.getAnUltimateImplementor() and + not result = this.getAnImplementor(_) } /** @@ -150,10 +150,10 @@ class OverridableCallable extends Callable { } private Callable getInherited1(ValueOrRefType t) { - result = getInherited0(t) + result = this.getInherited0(t) or // An interface implementation - result = getAnImplementorSubType(t) + result = this.getAnImplementorSubType(t) } pragma[noinline] @@ -171,7 +171,7 @@ class OverridableCallable extends Callable { private predicate isDeclaringSubType(ValueOrRefType t) { t = this.getDeclaringType() or - exists(ValueOrRefType mid | isDeclaringSubType(mid) | t = mid.getASubType()) + exists(ValueOrRefType mid | this.isDeclaringSubType(mid) | t = mid.getASubType()) } pragma[noinline] @@ -232,7 +232,7 @@ class OverridableAccessor extends Accessor, OverridableCallable { override Accessor getAnImplementor(ValueOrRefType t) { exists(Virtualizable implementor, int kind | - getAnImplementorAux(t, implementor, kind) and + this.getAnImplementorAux(t, implementor, kind) and result.getDeclaration() = implementor and getAccessorKind(result) = kind ) @@ -241,7 +241,7 @@ class OverridableAccessor extends Accessor, OverridableCallable { // predicate folding to get proper join order private predicate getAnImplementorAux(ValueOrRefType t, Virtualizable implementor, int kind) { exists(Virtualizable implementee | - implementee = getDeclaration() and + implementee = this.getDeclaration() and kind = getAccessorKind(this) and implementor = implementee.getAnImplementor(t) ) @@ -249,7 +249,7 @@ class OverridableAccessor extends Accessor, OverridableCallable { override Accessor getAnUltimateImplementor() { exists(Virtualizable implementor, int kind | - getAnUltimateImplementorAux(implementor, kind) and + this.getAnUltimateImplementorAux(implementor, kind) and result.getDeclaration() = implementor and getAccessorKind(result) = kind ) @@ -258,7 +258,7 @@ class OverridableAccessor extends Accessor, OverridableCallable { // predicate folding to get proper join order private predicate getAnUltimateImplementorAux(Virtualizable implementor, int kind) { exists(Virtualizable implementee | - implementee = getDeclaration() and + implementee = this.getDeclaration() and kind = getAccessorKind(this) and implementor = implementee.getAnUltimateImplementor() ) diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Access.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Access.qll index ab3ea182056..9d7cf3a5867 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/Access.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/Access.qll @@ -115,7 +115,7 @@ class MemberAccess extends Access, QualifiableExpr, @member_access_expr { not exists(MemberInitializer mi | mi.getLValue() = this) } - override Member getQualifiedDeclaration() { result = getTarget() } + override Member getQualifiedDeclaration() { result = this.getTarget() } override Member getTarget() { none() } } @@ -147,8 +147,8 @@ class AssignableAccess extends Access, @assignable_access_expr { * or a `ref` argument in a method call. */ predicate isOutOrRefArgument() { - isOutArgument() or - isRefArgument() + this.isOutArgument() or + this.isRefArgument() } /** @@ -507,7 +507,7 @@ class ElementAccess extends AssignableAccess, QualifiableExpr, @element_access_e * Gets an index expression of this element access, for example * `1` in `x[0, 1]`. */ - Expr getAnIndex() { result = getIndex(_) } + Expr getAnIndex() { result = this.getIndex(_) } /** * Gets the `i`th index expression of this element access, for example the @@ -515,7 +515,7 @@ class ElementAccess extends AssignableAccess, QualifiableExpr, @element_access_e */ Expr getIndex(int i) { result = this.getChild(i) and i >= 0 } - override Assignable getQualifiedDeclaration() { result = getTarget() } + override Assignable getQualifiedDeclaration() { result = this.getTarget() } } /** @@ -615,7 +615,7 @@ class IndexerWrite extends IndexerAccess, ElementWrite { } * ``` */ class VirtualIndexerAccess extends IndexerAccess { - VirtualIndexerAccess() { targetIsOverridableOrImplementable() } + VirtualIndexerAccess() { this.targetIsOverridableOrImplementable() } } /** @@ -647,7 +647,7 @@ library class EventAccessExpr extends Expr, @event_access_expr { * ``` */ class EventAccess extends AssignableMemberAccess, EventAccessExpr { - override Event getTarget() { result = getEvent() } + override Event getTarget() { result = this.getEvent() } override string getAPrimaryQlClass() { result = "EventAccess" } } @@ -707,7 +707,7 @@ class EventWrite extends EventAccess, AssignableWrite { } * ``` */ class VirtualEventAccess extends EventAccess { - VirtualEventAccess() { targetIsOverridableOrImplementable() } + VirtualEventAccess() { this.targetIsOverridableOrImplementable() } } /** @@ -787,7 +787,7 @@ class LocalFunctionAccess extends CallableAccess { * ``` */ class VirtualMethodAccess extends MethodAccess { - VirtualMethodAccess() { targetIsOverridableOrImplementable() } + VirtualMethodAccess() { this.targetIsOverridableOrImplementable() } } /** diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/ArithmeticOperation.qll b/csharp/ql/lib/semmle/code/csharp/exprs/ArithmeticOperation.qll index ac98c0eafcf..f20bfba1589 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/ArithmeticOperation.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/ArithmeticOperation.qll @@ -138,10 +138,10 @@ class DivExpr extends BinaryArithmeticOperation, @div_expr { override string getOperator() { result = "/" } /** Gets the numerator of this division operation. */ - Expr getNumerator() { result = getLeftOperand() } + Expr getNumerator() { result = this.getLeftOperand() } /** Gets the denominator of this division operation. */ - Expr getDenominator() { result = getRightOperand() } + Expr getDenominator() { result = this.getRightOperand() } override string getAPrimaryQlClass() { result = "DivExpr" } } diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Assignment.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Assignment.qll index 88ef770160a..562a4dd9cd5 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/Assignment.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/Assignment.qll @@ -27,7 +27,7 @@ class Assignment extends Operation, @assign_expr { Expr getRValue() { result = this.getChild(0) } /** Gets the variable being assigned to, if any. */ - Variable getTargetVariable() { result.getAnAccess() = getLValue() } + Variable getTargetVariable() { result.getAnAccess() = this.getLValue() } override string getOperator() { none() } } @@ -38,7 +38,7 @@ class Assignment extends Operation, @assign_expr { class LocalVariableDeclAndInitExpr extends LocalVariableDeclExpr, Assignment { override string getOperator() { result = "=" } - override LocalVariable getTargetVariable() { result = getVariable() } + override LocalVariable getTargetVariable() { result = this.getVariable() } override LocalVariableAccess getLValue() { result = Assignment.super.getLValue() } @@ -86,7 +86,7 @@ class AssignOperation extends Assignment, @assign_op_expr { * If an expanded version exists, then it is used in the control * flow graph. */ - predicate hasExpandedAssignment() { exists(getExpandedAssignment()) } + predicate hasExpandedAssignment() { exists(this.getExpandedAssignment()) } override string toString() { result = "... " + this.getOperator() + " ..." } } diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll index 6dc88e941ef..a4c4ab1b670 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll @@ -47,7 +47,7 @@ class Call extends DotNet::Call, Expr, @call { override Expr getRawArgument(int i) { result = this.getArgument(i) } - override Expr getAnArgument() { result = getArgument(_) } + override Expr getAnArgument() { result = this.getArgument(_) } /** Gets the number of arguments of this call. */ int getNumberOfArguments() { result = count(this.getAnArgument()) } @@ -80,7 +80,7 @@ class Call extends DotNet::Call, Expr, @call { */ cached override Expr getArgumentForParameter(DotNet::Parameter p) { - getTarget().getAParameter() = p and + this.getTarget().getAParameter() = p and ( // Appears in the positional part of the call result = this.getImplicitArgument(p.getPosition()) and @@ -94,7 +94,7 @@ class Call extends DotNet::Call, Expr, @call { ) or // Appears in the named part of the call - result = getExplicitArgument(p.getName()) and + result = this.getExplicitArgument(p.getName()) and (p.(Parameter).isParams() implies isValidExplicitParamsType(p, result.getType())) ) } @@ -112,13 +112,13 @@ class Call extends DotNet::Call, Expr, @call { pragma[noinline] private Expr getImplicitArgument(int pos) { - result = getArgument(pos) and + result = this.getArgument(pos) and not exists(result.getExplicitArgumentName()) } pragma[nomagic] private Expr getExplicitArgument(string name) { - result = getAnArgument() and + result = this.getAnArgument() and result.getExplicitArgumentName() = name } @@ -131,7 +131,7 @@ class Call extends DotNet::Call, Expr, @call { */ Expr getArgumentForName(string name) { exists(Parameter p | - result = getArgumentForParameter(p) and + result = this.getArgumentForParameter(p) and p.hasName(name) ) } @@ -219,7 +219,7 @@ class Call extends DotNet::Call, Expr, @call { */ Expr getRuntimeArgumentForParameter(Parameter p) { exists(Callable c | - c = getARuntimeTarget() and + c = this.getARuntimeTarget() and p = c.getAParameter() and result = this.getRuntimeArgument(p.getPosition()) ) @@ -231,7 +231,7 @@ class Call extends DotNet::Call, Expr, @call { */ Expr getRuntimeArgumentForName(string name) { exists(Parameter p | - result = getRuntimeArgumentForParameter(p) and + result = this.getRuntimeArgumentForParameter(p) and p.hasName(name) ) } @@ -240,19 +240,19 @@ class Call extends DotNet::Call, Expr, @call { * Gets an argument that corresponds to a parameter of a potential * run-time target of this call. */ - Expr getARuntimeArgument() { result = getRuntimeArgument(_) } + Expr getARuntimeArgument() { result = this.getRuntimeArgument(_) } /** * Gets the number of arguments that correspond to a parameter of a potential * run-time target of this call. */ - int getNumberOfRuntimeArguments() { result = count(getARuntimeArgument()) } + int getNumberOfRuntimeArguments() { result = count(this.getARuntimeArgument()) } /** * Holds if this call has no arguments that correspond to a parameter of a * potential (run-time) target of this call. */ - predicate hasNoRuntimeArguments() { not exists(getARuntimeArgument()) } + predicate hasNoRuntimeArguments() { not exists(this.getARuntimeArgument()) } override string toString() { result = "call" } } @@ -295,19 +295,19 @@ private predicate isValidExplicitParamsType(Parameter p, Type t) { class MethodCall extends Call, QualifiableExpr, LateBindableExpr, @method_invocation_expr { override Method getTarget() { expr_call(this, result) } - override Method getQualifiedDeclaration() { result = getTarget() } + override Method getQualifiedDeclaration() { result = this.getTarget() } override string toString() { result = "call to method " + concat(this.getTarget().getName()) } override string getAPrimaryQlClass() { result = "MethodCall" } override Expr getRawArgument(int i) { - if exists(getQualifier()) + if exists(this.getQualifier()) then - i = 0 and result = getQualifier() + i = 0 and result = this.getQualifier() or - result = getArgument(i - 1) - else result = getArgument(i) + result = this.getArgument(i - 1) + else result = this.getArgument(i) } } @@ -336,7 +336,7 @@ class ExtensionMethodCall extends MethodCall { override Expr getArgument(int i) { exists(int j | result = this.getChildExpr(j) | - if isOrdinaryStaticCall() then (j = i and j >= 0) else (j = i - 1 and j >= -1) + if this.isOrdinaryStaticCall() then (j = i and j >= 0) else (j = i - 1 and j >= -1) ) } @@ -379,8 +379,8 @@ class ExtensionMethodCall extends MethodCall { */ class VirtualMethodCall extends MethodCall { VirtualMethodCall() { - not getQualifier() instanceof BaseAccess and - getTarget().isOverridableOrImplementable() + not this.getQualifier() instanceof BaseAccess and + this.getTarget().isOverridableOrImplementable() } } @@ -573,7 +573,7 @@ class DelegateLikeCall extends Call, DelegateLikeCall_ { ) } - override Expr getRuntimeArgument(int i) { result = getArgument(i) } + override Expr getRuntimeArgument(int i) { result = this.getArgument(i) } } /** @@ -618,11 +618,11 @@ class DelegateCall extends DelegateLikeCall, @delegate_invocation_expr { } deprecated private AddEventSource getAnAddEventSourceSameEnclosingCallable() { - result = getAnAddEventSource(this.getEnclosingCallable()) + result = this.getAnAddEventSource(this.getEnclosingCallable()) } deprecated private AddEventSource getAnAddEventSourceDifferentEnclosingCallable() { - exists(Callable c | result = getAnAddEventSource(c) | c != this.getEnclosingCallable()) + exists(Callable c | result = this.getAnAddEventSource(c) | c != this.getEnclosingCallable()) } /** @@ -683,7 +683,7 @@ class AccessorCall extends Call, QualifiableExpr, @call_access_expr { */ class PropertyCall extends AccessorCall, PropertyAccessExpr { override Accessor getTarget() { - exists(PropertyAccess pa, Property p | pa = this and p = getProperty() | + exists(PropertyAccess pa, Property p | pa = this and p = this.getProperty() | pa instanceof AssignableRead and result = p.getGetter() or pa instanceof AssignableWrite and result = p.getSetter() @@ -718,7 +718,7 @@ class PropertyCall extends AccessorCall, PropertyAccessExpr { */ class IndexerCall extends AccessorCall, IndexerAccessExpr { override Accessor getTarget() { - exists(IndexerAccess ia, Indexer i | ia = this and i = getIndexer() | + exists(IndexerAccess ia, Indexer i | ia = this and i = this.getIndexer() | ia instanceof AssignableRead and result = i.getGetter() or ia instanceof AssignableWrite and result = i.getSetter() @@ -761,7 +761,7 @@ class IndexerCall extends AccessorCall, IndexerAccessExpr { class EventCall extends AccessorCall, EventAccessExpr { override EventAccessor getTarget() { exists(Event e, AddOrRemoveEventExpr aoree | - e = getEvent() and + e = this.getEvent() and aoree.getLValue() = this | aoree instanceof AddEventExpr and result = e.getAddEventAccessor() @@ -799,7 +799,7 @@ class EventCall extends AccessorCall, EventAccessExpr { class LocalFunctionCall extends Call, @local_function_invocation_expr { override LocalFunction getTarget() { expr_call(this, result) } - override string toString() { result = "call to local function " + getTarget().getName() } + override string toString() { result = "call to local function " + this.getTarget().getName() } override string getAPrimaryQlClass() { result = "LocalFunctionCall" } } diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Creation.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Creation.qll index c9ae3919004..84bcf7b87bc 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/Creation.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/Creation.qll @@ -45,7 +45,7 @@ class ObjectInitializer extends ObjectOrCollectionInitializer, @object_init_expr * } * ``` */ - MemberInitializer getAMemberInitializer() { result = getMemberInitializer(_) } + MemberInitializer getAMemberInitializer() { result = this.getMemberInitializer(_) } /** * Gets the `i`th member initializer of this object initializer. For example, @@ -122,7 +122,7 @@ class CollectionInitializer extends ObjectOrCollectionInitializer, @collection_i * }; * ``` */ - ElementInitializer getAnElementInitializer() { result = getElementInitializer(_) } + ElementInitializer getAnElementInitializer() { result = this.getElementInitializer(_) } /** * Gets the `i`th element initializer of this collection initializer, for @@ -180,7 +180,7 @@ class ElementInitializer extends MethodCall { */ class ObjectCreation extends Call, LateBindableExpr, @object_creation_expr { /** Gets the type of the newly created object. */ - ValueOrRefType getObjectType() { result = getType() } + ValueOrRefType getObjectType() { result = this.getType() } override Constructor getTarget() { expr_call(this, result) } @@ -320,7 +320,7 @@ class ArrayInitializer extends Expr, @array_init_expr { * }; * ``` */ - Expr getAnElement() { result = getElement(_) } + Expr getAnElement() { result = this.getElement(_) } /** * Gets the `i`th element of this array initializer, for example the second @@ -365,7 +365,7 @@ class ArrayCreation extends Expr, @array_creation_expr { * new int[5, 10] * ``` */ - Expr getALengthArgument() { result = getLengthArgument(_) } + Expr getALengthArgument() { result = this.getLengthArgument(_) } /** * Gets the `i`th dimension's length argument of this array creation, for @@ -427,7 +427,7 @@ class AnonymousFunctionExpr extends Expr, Callable, Modifiable, @anonymous_funct override string toString() { result = Expr.super.toString() } - override string toStringWithTypes() { result = toString() } + override string toStringWithTypes() { result = this.toString() } } /** diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Dynamic.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Dynamic.qll index ea6012ca3e1..eda31432f38 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/Dynamic.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/Dynamic.qll @@ -15,7 +15,7 @@ private import semmle.code.csharp.dispatch.Dispatch * (`DynamicAccessorCall`), or a dynamic element access (`DynamicElementAccess`). */ class DynamicExpr extends LateBindableExpr { - DynamicExpr() { isLateBound() } + DynamicExpr() { this.isLateBound() } } /** @@ -67,7 +67,7 @@ class DynamicObjectCreation extends DynamicExpr, ObjectCreation { * may not be known at compile-time (as in the example above). */ class DynamicMethodCall extends DynamicExpr, MethodCall { - override string toString() { result = "dynamic call to method " + getLateBoundTargetName() } + override string toString() { result = "dynamic call to method " + this.getLateBoundTargetName() } override string getAPrimaryQlClass() { result = "DynamicMethodCall" } } @@ -97,7 +97,9 @@ class DynamicMethodCall extends DynamicExpr, MethodCall { * target operator may not be known at compile-time (as in the example above). */ class DynamicOperatorCall extends DynamicExpr, OperatorCall { - override string toString() { result = "dynamic call to operator " + getLateBoundTargetName() } + override string toString() { + result = "dynamic call to operator " + this.getLateBoundTargetName() + } override string getAPrimaryQlClass() { result = "DynamicOperatorCall" } } @@ -189,7 +191,9 @@ class DynamicAccess extends DynamicExpr { */ class DynamicMemberAccess extends DynamicAccess, MemberAccess, AssignableAccess, @dynamic_member_access_expr { - override string toString() { result = "dynamic access to member " + getLateBoundTargetName() } + override string toString() { + result = "dynamic access to member " + this.getLateBoundTargetName() + } override string getAPrimaryQlClass() { result = "DynamicMemberAccess" } diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll index 0988bb84340..47477afe2b9 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll @@ -103,7 +103,7 @@ class Expr extends DotNet::Expr, ControlFlowElement, @expr { class LateBindableExpr extends Expr, @late_bindable_expr { /** Holds if this expression is late bound. */ predicate isLateBound() { - exists(getLateBoundTargetName()) or + exists(this.getLateBoundTargetName()) or isDynamicMemberAccess(this) or isDynamicElementAccess(this) } @@ -221,9 +221,9 @@ class BinaryOperation extends Operation, @bin_op { /** Gets the other operand of this binary operation, given operand `o`. */ Expr getOtherOperand(Expr o) { - o = getLeftOperand() and result = getRightOperand() + o = this.getLeftOperand() and result = this.getRightOperand() or - o = getRightOperand() and result = getLeftOperand() + o = this.getRightOperand() and result = this.getLeftOperand() } override string getOperator() { none() } @@ -368,7 +368,7 @@ class RelationalPatternExpr extends PatternExpr, @relational_pattern_expr { /** Gets the expression of this relational pattern. */ Expr getExpr() { result = this.getChild(0) } - override string toString() { result = getOperator() + " ..." } + override string toString() { result = this.getOperator() + " ..." } } /** A less-than pattern, for example `< 10` in `x is < 10`. */ @@ -520,7 +520,7 @@ class NotPatternExpr extends UnaryPatternExpr, @not_pattern_expr { /** A binary pattern. For example, `1 or 2`. */ class BinaryPatternExpr extends PatternExpr, @binary_pattern_expr { /** Gets a pattern. */ - PatternExpr getAnOperand() { result = getLeftOperand() or result = getRightOperand() } + PatternExpr getAnOperand() { result = this.getLeftOperand() or result = this.getRightOperand() } /** Gets the left pattern. */ PatternExpr getLeftOperand() { result = this.getChild(0) } @@ -743,7 +743,7 @@ class DefaultValueExpr extends Expr, @default_expr { TypeAccess getTypeAccess() { result = this.getChild(0) } override string toString() { - if exists(getTypeAccess()) then result = "default(...)" else result = "default" + if exists(this.getTypeAccess()) then result = "default(...)" else result = "default" } override string getAPrimaryQlClass() { result = "DefaultValueExpr" } @@ -757,7 +757,7 @@ class SizeofExpr extends UnaryOperation, @sizeof_expr { * Gets the type access in this `sizeof` expression, for example `int` in * `sizeof(int)`. */ - TypeAccess getTypeAccess() { result = getOperand() } + TypeAccess getTypeAccess() { result = this.getOperand() } override string getOperator() { result = "sizeof(..)" } @@ -830,7 +830,7 @@ class AddressOfExpr extends UnaryOperation, @address_of_expr { */ class AwaitExpr extends Expr, @await_expr { /** Gets the expression being awaited. */ - Expr getExpr() { result = getChild(0) } + Expr getExpr() { result = this.getChild(0) } override string toString() { result = "await ..." } @@ -881,7 +881,7 @@ class InterpolatedStringExpr extends Expr, @interpolated_string_expr { * element (`getText(0)` gets the text). */ Expr getInsert(int i) { - result = getChild(i) and + result = this.getChild(i) and not result instanceof StringLiteral } @@ -891,13 +891,13 @@ class InterpolatedStringExpr extends Expr, @interpolated_string_expr { * `$"Hello, {name}!"`. Note that there is no text element at index `i = 1`, * but instead an insert (`getInsert(1)` gets the insert). */ - StringLiteral getText(int i) { result = getChild(i) } + StringLiteral getText(int i) { result = this.getChild(i) } /** Gets an insert in this interpolated string. */ - Expr getAnInsert() { result = getInsert(_) } + Expr getAnInsert() { result = this.getInsert(_) } /** Gets a text element in this interpolated string. */ - StringLiteral getAText() { result = getText(_) } + StringLiteral getAText() { result = this.getText(_) } } /** @@ -914,7 +914,7 @@ class ThrowElement extends ControlFlowElement, DotNet::Throw, @throw_element { /** Gets the type of exception being thrown. */ Class getThrownExceptionType() { - result = getExpr().getType() + result = this.getExpr().getType() or // Corner case: `throw null` this.getExpr().getType() instanceof NullType and @@ -958,7 +958,7 @@ class QualifiableExpr extends Expr, @qualifiable_expr { Expr getQualifier() { result = this.getChildExpr(-1) } /** Holds if this expression is qualified. */ - final predicate hasQualifier() { exists(getQualifier()) } + final predicate hasQualifier() { exists(this.getQualifier()) } /** Holds if this expression has an implicit `this` qualifier. */ predicate hasImplicitThisQualifier() { this.getQualifier().(ThisAccess).isImplicit() } @@ -1029,10 +1029,10 @@ class TupleExpr extends Expr, @tuple_expr { override string toString() { result = "(..., ...)" } /** Gets the `i`th argument of this tuple. */ - Expr getArgument(int i) { result = getChild(i) } + Expr getArgument(int i) { result = this.getChild(i) } /** Gets an argument of this tuple. */ - Expr getAnArgument() { result = getArgument(_) } + Expr getAnArgument() { result = this.getArgument(_) } /** Holds if this tuple is a read access. */ deprecated predicate isReadAccess() { not this = getAnAssignOrForeachChild() } @@ -1057,11 +1057,11 @@ class TupleExpr extends Expr, @tuple_expr { */ class RefExpr extends Expr, @ref_expr { /** Gets the expression being referenced. */ - Expr getExpr() { result = getChild(0) } + Expr getExpr() { result = this.getChild(0) } override string toString() { result = "ref ..." } - override Type getType() { result = getExpr().getType() } + override Type getType() { result = this.getExpr().getType() } override string getAPrimaryQlClass() { result = "RefExpr" } } @@ -1154,7 +1154,7 @@ class DefineSymbolExpr extends Expr, @define_symbol_expr { /** Gets the name of the symbol. */ string getName() { directive_define_symbols(this, result) } - override string toString() { result = getName() } + override string toString() { result = this.getName() } override string getAPrimaryQlClass() { result = "DefineSymbolExpr" } } diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Literal.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Literal.qll index 0ca9f6d0db0..842e27fb96b 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/Literal.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/Literal.qll @@ -23,9 +23,9 @@ class Literal extends DotNet::Literal, Expr, @literal_expr { class BoolLiteral extends Literal, @bool_literal_expr { /** Gets the value of this Boolean literal. */ boolean getBoolValue() { - getValue() = "true" and result = true + this.getValue() = "true" and result = true or - getValue() = "false" and result = false + this.getValue() = "false" and result = false } override string getAPrimaryQlClass() { result = "BoolLiteral" } @@ -105,7 +105,7 @@ class DecimalLiteral extends RealLiteral, @decimal_literal_expr { * A `string` literal, for example `"Hello, World!"`. */ class StringLiteral extends DotNet::StringLiteral, Literal, @string_literal_expr { - override string toString() { result = "\"" + getValue().replaceAll("\"", "\\\"") + "\"" } + override string toString() { result = "\"" + this.getValue().replaceAll("\"", "\\\"") + "\"" } override string getAPrimaryQlClass() { result = "StringLiteral" } } diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/EntityFramework.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/EntityFramework.qll index 36882d3b12e..723832906c6 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/EntityFramework.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/EntityFramework.qll @@ -239,7 +239,7 @@ module EntityFramework { private class SystemDataEntityDbSetSqlQuerySinkModelCsv extends SinkModelCsv { override predicate row(string row) { row = - ["System.Data.Entity;DbSet;false;SqlQuery;(System.String,System.Object[]);;Argument[0];sql"] + "System.Data.Entity;DbSet;false;SqlQuery;(System.String,System.Object[]);;Argument[0];sql" } } @@ -317,7 +317,7 @@ module EntityFramework { dist = 0 ) or - step(_, _, c1, t1, dist - 1) and + this.step(_, _, c1, t1, dist - 1) and dist < DataFlowPrivate::accessPathLimit() - 1 and not isNotMapped(t2) and ( @@ -374,11 +374,11 @@ module EntityFramework { } private predicate stepRev(Content c1, Type t1, Content c2, Type t2, int dist) { - step(c1, t1, c2, t2, dist) and - c2.(PropertyContent).getProperty() = getAColumnProperty(dist) + this.step(c1, t1, c2, t2, dist) and + c2.(PropertyContent).getProperty() = this.getAColumnProperty(dist) or - stepRev(c2, t2, _, _, dist + 1) and - step(c1, t1, c2, t2, dist) + this.stepRev(c2, t2, _, _, dist + 1) and + this.step(c1, t1, c2, t2, dist) } /** Gets a `SaveChanges[Async]` method. */ @@ -453,8 +453,8 @@ module EntityFramework { ) { exists(Property mapped | preservesValue = true and - input(input, mapped) and - output(output, mapped) + this.input(input, mapped) and + this.output(output, mapped) ) } } diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/Format.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/Format.qll index 3c659d86d46..2ffbfe6e7e1 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/Format.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/Format.qll @@ -155,13 +155,13 @@ class InvalidFormatString extends StringLiteral { int oldstartcolumn, int padding | this.getLocation().hasLocationInfo(filepath, startline, oldstartcolumn, endline, endcolumn) and - startcolumn = padding + oldstartcolumn + getInvalidOffset() and + startcolumn = padding + oldstartcolumn + this.getInvalidOffset() and toUrl(filepath, startline, startcolumn, endline, endcolumn, result) | // Single-line string literal beginning " or @" // Figure out the correct indent. startline = endline and - padding = endcolumn - oldstartcolumn - getValue().length() + padding = endcolumn - oldstartcolumn - this.getValue().length() or // Multi-line literal beginning @" startline != endline and diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/JsonNET.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/JsonNET.qll index abd820bdfe4..c0c1a765469 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/JsonNET.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/JsonNET.qll @@ -62,25 +62,25 @@ module JsonNET { boolean preservesValue ) { // ToString methods - c = getAToStringMethod() and + c = this.getAToStringMethod() and preservesValue = false and source = any(CallableFlowSourceArg arg | arg.getArgumentIndex() = 0) and sink instanceof CallableFlowSinkReturn or // Deserialize methods - c = getADeserializeMethod() and + c = this.getADeserializeMethod() and preservesValue = false and source = any(CallableFlowSourceArg arg | arg.getArgumentIndex() = 0) and sink instanceof CallableFlowSinkReturn or // Serialize methods - c = getASerializeMethod() and + c = this.getASerializeMethod() and preservesValue = false and source = any(CallableFlowSourceArg arg | arg.getArgumentIndex() = 0) and sink instanceof CallableFlowSinkReturn or // Populate methods - c = getAPopulateMethod() and + c = this.getAPopulateMethod() and preservesValue = false and source = any(CallableFlowSourceArg arg | arg.getArgumentIndex() = 0) and sink = any(CallableFlowSinkArg arg | arg.getArgumentIndex() = 1) @@ -120,21 +120,13 @@ module JsonNET { SerializedMember() { // This member has a Json attribute exists(Class attribute | attribute = this.getAnAttribute().getType() | - attribute.hasName("JsonPropertyAttribute") - or - attribute.hasName("JsonDictionaryAttribute") - or - attribute.hasName("JsonRequiredAttribute") - or - attribute.hasName("JsonArrayAttribute") - or - attribute.hasName("JsonConverterAttribute") - or - attribute.hasName("JsonExtensionDataAttribute") - or - attribute.hasName("SerializableAttribute") // System.SerializableAttribute - or - attribute.hasName("DataMemberAttribute") // System.DataMemberAttribute + attribute + .hasName([ + "JsonPropertyAttribute", "JsonDictionaryAttribute", "JsonRequiredAttribute", + "JsonArrayAttribute", "JsonConverterAttribute", "JsonExtensionDataAttribute", + "SerializableAttribute", // System.SerializableAttribute + "DataMemberAttribute" // System.DataMemberAttribute + ]) ) or // This field is a member of an explicitly serialized type @@ -175,7 +167,7 @@ module JsonNET { /** Any attribute class that marks a member to not be serialized. */ private class NotSerializedAttributeClass extends JsonClass { NotSerializedAttributeClass() { - this.hasName("JsonIgnoreAttribute") or this.hasName("NonSerializedAttribute") + this.hasName(["JsonIgnoreAttribute", "NonSerializedAttribute"]) } } diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/Moq.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/Moq.qll index 92811122696..e0705ac7d98 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/Moq.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/Moq.qll @@ -21,7 +21,7 @@ class ReturnsMethod extends Method { */ Expr getAReturnedExpr() { exists(MethodCall mc, Expr arg | - mc = getACall() and + mc = this.getACall() and arg = mc.getArgument(0) | if arg instanceof LambdaExpr then arg.(LambdaExpr).canReturn(result) else result = arg diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/NHibernate.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/NHibernate.qll index ddf7fd410bf..6ce6efb815f 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/NHibernate.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/NHibernate.qll @@ -28,15 +28,7 @@ module NHibernate { /** Gets a type parameter that specifies a mapped class. */ TypeParameter getAMappedObjectTp() { - exists(string methodName | - methodName = "Load<>" - or - methodName = "Merge<>" - or - methodName = "Get<>" - or - methodName = "Query<>" - | + exists(string methodName | methodName = ["Load<>", "Merge<>", "Get<>", "Query<>"] | result = this.getAMethod(methodName).(UnboundGenericMethod).getTypeParameter(0) ) } diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/System.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/System.qll index 20ab350ffd4..e33004f109d 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/System.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/System.qll @@ -157,7 +157,7 @@ class SystemIComparableTInterface extends SystemUnboundGenericInterface { result.getDeclaringType() = this and result.hasName("CompareTo") and result.getNumberOfParameters() = 1 and - result.getParameter(0).getType() = getTypeParameter(0) and + result.getParameter(0).getType() = this.getTypeParameter(0) and result.getReturnType() instanceof IntType } } @@ -171,7 +171,7 @@ class SystemIEquatableTInterface extends SystemUnboundGenericInterface { result.getDeclaringType() = this and result.hasName("Equals") and result.getNumberOfParameters() = 1 and - result.getParameter(0).getType() = getTypeParameter(0) and + result.getParameter(0).getType() = this.getTypeParameter(0) and result.getReturnType() instanceof BoolType } } @@ -239,7 +239,7 @@ class SystemLazyClass extends SystemUnboundGenericClass { Property getValueProperty() { result.getDeclaringType() = this and result.hasName("Value") and - result.getType() = getTypeParameter(0) + result.getType() = this.getTypeParameter(0) } } @@ -254,7 +254,7 @@ class SystemNullableStruct extends SystemUnboundGenericStruct { Property getValueProperty() { result.getDeclaringType() = this and result.hasName("Value") and - result.getType() = getTypeParameter(0) + result.getType() = this.getTypeParameter(0) } /** Gets the `HasValue` property. */ @@ -268,7 +268,7 @@ class SystemNullableStruct extends SystemUnboundGenericStruct { Method getAGetValueOrDefaultMethod() { result.getDeclaringType() = this and result.hasName("GetValueOrDefault") and - result.getReturnType() = getTypeParameter(0) + result.getReturnType() = this.getTypeParameter(0) } } @@ -588,7 +588,7 @@ class IEquatableEqualsMethod extends Method { m = any(SystemIEquatableTInterface i).getAConstructedGeneric().getAMethod() and m.getUnboundDeclaration() = any(SystemIEquatableTInterface i).getEqualsMethod() | - this = m or getAnUltimateImplementee() = m + this = m or this.getAnUltimateImplementee() = m ) } } @@ -677,7 +677,7 @@ class DisposeMethod extends Method { /** A method with the signature `void Dispose(bool)`. */ library class DisposeBoolMethod extends Method { DisposeBoolMethod() { - hasName("Dispose") and + this.hasName("Dispose") and this.getReturnType() instanceof VoidType and this.getNumberOfParameters() = 1 and this.getParameter(0).getType() instanceof BoolType diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/WCF.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/WCF.qll index 655648d88c9..befb5f3ae1f 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/WCF.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/WCF.qll @@ -49,7 +49,7 @@ class OperationMethod extends Method { i.getAnAttribute() instanceof ServiceContractAttribute and m.getDeclaringType() = i and m.getAnAttribute() instanceof OperationContractAttribute and - getImplementee() = m + this.getImplementee() = m ) } } diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/microsoft/AspNetCore.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/microsoft/AspNetCore.qll index 5fe6665bd47..a918b603818 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/microsoft/AspNetCore.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/microsoft/AspNetCore.qll @@ -6,129 +6,133 @@ import semmle.code.csharp.frameworks.Microsoft /** The `Microsoft.AspNetCore` namespace. */ class MicrosoftAspNetCoreNamespace extends Namespace { MicrosoftAspNetCoreNamespace() { - getParentNamespace() instanceof MicrosoftNamespace and - hasName("AspNetCore") + this.getParentNamespace() instanceof MicrosoftNamespace and + this.hasName("AspNetCore") } } /** The `Microsoft.AspNetCore.Mvc` namespace. */ class MicrosoftAspNetCoreMvcNamespace extends Namespace { MicrosoftAspNetCoreMvcNamespace() { - getParentNamespace() instanceof MicrosoftAspNetCoreNamespace and - hasName("Mvc") + this.getParentNamespace() instanceof MicrosoftAspNetCoreNamespace and + this.hasName("Mvc") } } /** The 'Microsoft.AspNetCore.Mvc.ViewFeatures' namespace. */ class MicrosoftAspNetCoreMvcViewFeatures extends Namespace { MicrosoftAspNetCoreMvcViewFeatures() { - getParentNamespace() instanceof MicrosoftAspNetCoreMvcNamespace and - hasName("ViewFeatures") + this.getParentNamespace() instanceof MicrosoftAspNetCoreMvcNamespace and + this.hasName("ViewFeatures") } } /** The 'Microsoft.AspNetCore.Mvc.Rendering' namespace. */ class MicrosoftAspNetCoreMvcRendering extends Namespace { MicrosoftAspNetCoreMvcRendering() { - getParentNamespace() instanceof MicrosoftAspNetCoreMvcNamespace and - hasName("Rendering") + this.getParentNamespace() instanceof MicrosoftAspNetCoreMvcNamespace and + this.hasName("Rendering") } } /** An attribute whose type is in the `Microsoft.AspNetCore.Mvc` namespace. */ class MicrosoftAspNetCoreMvcAttribute extends Attribute { MicrosoftAspNetCoreMvcAttribute() { - getType().getNamespace() instanceof MicrosoftAspNetCoreMvcNamespace + this.getType().getNamespace() instanceof MicrosoftAspNetCoreMvcNamespace } } /** A `Microsoft.AspNetCore.Mvc.HttpPost` attribute. */ class MicrosoftAspNetCoreMvcHttpPostAttribute extends MicrosoftAspNetCoreMvcAttribute { - MicrosoftAspNetCoreMvcHttpPostAttribute() { getType().hasName("HttpPostAttribute") } + MicrosoftAspNetCoreMvcHttpPostAttribute() { this.getType().hasName("HttpPostAttribute") } } /** A `Microsoft.AspNetCore.Mvc.HttpPut` attribute. */ class MicrosoftAspNetCoreMvcHttpPutAttribute extends MicrosoftAspNetCoreMvcAttribute { - MicrosoftAspNetCoreMvcHttpPutAttribute() { getType().hasName("HttpPutAttribute") } + MicrosoftAspNetCoreMvcHttpPutAttribute() { this.getType().hasName("HttpPutAttribute") } } /** A `Microsoft.AspNetCore.Mvc.HttpDelete` attribute. */ class MicrosoftAspNetCoreMvcHttpDeleteAttribute extends MicrosoftAspNetCoreMvcAttribute { - MicrosoftAspNetCoreMvcHttpDeleteAttribute() { getType().hasName("HttpDeleteAttribute") } + MicrosoftAspNetCoreMvcHttpDeleteAttribute() { this.getType().hasName("HttpDeleteAttribute") } } /** A `Microsoft.AspNetCore.Mvc.NonAction` attribute. */ class MicrosoftAspNetCoreMvcNonActionAttribute extends MicrosoftAspNetCoreMvcAttribute { - MicrosoftAspNetCoreMvcNonActionAttribute() { getType().hasName("NonActionAttribute") } + MicrosoftAspNetCoreMvcNonActionAttribute() { this.getType().hasName("NonActionAttribute") } } /** The `Microsoft.AspNetCore.Antiforgery` namespace. */ class MicrosoftAspNetCoreAntiforgeryNamespace extends Namespace { MicrosoftAspNetCoreAntiforgeryNamespace() { - getParentNamespace() instanceof MicrosoftAspNetCoreNamespace and - hasName("Antiforgery") + this.getParentNamespace() instanceof MicrosoftAspNetCoreNamespace and + this.hasName("Antiforgery") } } /** The `Microsoft.AspNetCore.Mvc.Filters` namespace. */ class MicrosoftAspNetCoreMvcFilters extends Namespace { MicrosoftAspNetCoreMvcFilters() { - getParentNamespace() instanceof MicrosoftAspNetCoreMvcNamespace and - hasName("Filters") + this.getParentNamespace() instanceof MicrosoftAspNetCoreMvcNamespace and + this.hasName("Filters") } } /** The `Microsoft.AspNetCore.Mvc.Filters.IFilterMetadataInterface` interface. */ class MicrosoftAspNetCoreMvcIFilterMetadataInterface extends Interface { MicrosoftAspNetCoreMvcIFilterMetadataInterface() { - getNamespace() instanceof MicrosoftAspNetCoreMvcFilters and - hasName("IFilterMetadata") + this.getNamespace() instanceof MicrosoftAspNetCoreMvcFilters and + this.hasName("IFilterMetadata") } } /** The `Microsoft.AspNetCore.IAuthorizationFilter` interface. */ class MicrosoftAspNetCoreIAuthorizationFilterInterface extends Interface { MicrosoftAspNetCoreIAuthorizationFilterInterface() { - getNamespace() instanceof MicrosoftAspNetCoreMvcFilters and - hasName("IAsyncAuthorizationFilter") + this.getNamespace() instanceof MicrosoftAspNetCoreMvcFilters and + this.hasName("IAsyncAuthorizationFilter") } /** Gets the `OnAuthorizationAsync` method. */ - Method getOnAuthorizationMethod() { result = getAMethod("OnAuthorizationAsync") } + Method getOnAuthorizationMethod() { result = this.getAMethod("OnAuthorizationAsync") } } /** The `Microsoft.AspNetCore.IAntiforgery` interface. */ class MicrosoftAspNetCoreIAntiForgeryInterface extends Interface { MicrosoftAspNetCoreIAntiForgeryInterface() { - getNamespace() instanceof MicrosoftAspNetCoreAntiforgeryNamespace and - hasName("IAntiforgery") + this.getNamespace() instanceof MicrosoftAspNetCoreAntiforgeryNamespace and + this.hasName("IAntiforgery") } /** Gets the `ValidateRequestAsync` method. */ - Method getValidateMethod() { result = getAMethod("ValidateRequestAsync") } + Method getValidateMethod() { result = this.getAMethod("ValidateRequestAsync") } } /** The `Microsoft.AspNetCore.DefaultAntiForgery` class, or another user-supplied class that implements `IAntiForgery`. */ class AntiForgeryClass extends Class { - AntiForgeryClass() { getABaseInterface*() instanceof MicrosoftAspNetCoreIAntiForgeryInterface } + AntiForgeryClass() { + this.getABaseInterface*() instanceof MicrosoftAspNetCoreIAntiForgeryInterface + } /** Gets the `ValidateRequestAsync` method. */ - Method getValidateMethod() { result = getAMethod("ValidateRequestAsync") } + Method getValidateMethod() { result = this.getAMethod("ValidateRequestAsync") } } /** An authorization filter class defined by AspNetCore or the user. */ class AuthorizationFilterClass extends Class { AuthorizationFilterClass() { - getABaseInterface*() instanceof MicrosoftAspNetCoreIAuthorizationFilterInterface + this.getABaseInterface*() instanceof MicrosoftAspNetCoreIAuthorizationFilterInterface } /** Gets the `OnAuthorization` method provided by this filter. */ - Method getOnAuthorizationMethod() { result = getAMethod("OnAuthorizationAsync") } + Method getOnAuthorizationMethod() { result = this.getAMethod("OnAuthorizationAsync") } } /** An attribute whose type has a name like `[Auto...]Validate[...]Anti[Ff]orgery[...Token]Attribute`. */ class ValidateAntiForgeryAttribute extends Attribute { - ValidateAntiForgeryAttribute() { getType().getName().matches("%Validate%Anti_orgery%Attribute") } + ValidateAntiForgeryAttribute() { + this.getType().getName().matches("%Validate%Anti_orgery%Attribute") + } } /** @@ -137,43 +141,43 @@ class ValidateAntiForgeryAttribute extends Attribute { */ class ValidateAntiforgeryTokenAuthorizationFilter extends Class { ValidateAntiforgeryTokenAuthorizationFilter() { - getABaseInterface*() instanceof MicrosoftAspNetCoreMvcIFilterMetadataInterface and - getName().matches("%Validate%Anti_orgery%") + this.getABaseInterface*() instanceof MicrosoftAspNetCoreMvcIFilterMetadataInterface and + this.getName().matches("%Validate%Anti_orgery%") } } /** The `Microsoft.AspNetCore.Mvc.Filters.FilterCollection` class. */ class MicrosoftAspNetCoreMvcFilterCollection extends Class { MicrosoftAspNetCoreMvcFilterCollection() { - getNamespace() instanceof MicrosoftAspNetCoreMvcFilters and - hasName("FilterCollection") + this.getNamespace() instanceof MicrosoftAspNetCoreMvcFilters and + this.hasName("FilterCollection") } /** Gets an `Add` method. */ Method getAddMethod() { - result = getAMethod("Add") or - result = getABaseType().getAMethod("Add") + result = this.getAMethod("Add") or + result = this.getABaseType().getAMethod("Add") } } /** The `Microsoft.AspNetCore.Mvc.MvcOptions` class. */ class MicrosoftAspNetCoreMvcOptions extends Class { MicrosoftAspNetCoreMvcOptions() { - getNamespace() instanceof MicrosoftAspNetCoreMvcNamespace and - hasName("MvcOptions") + this.getNamespace() instanceof MicrosoftAspNetCoreMvcNamespace and + this.hasName("MvcOptions") } /** Gets the `Filters` property. */ - Property getFilterCollectionProperty() { result = getProperty("Filters") } + Property getFilterCollectionProperty() { result = this.getProperty("Filters") } } /** The base class for controllers in MVC, i.e. `Microsoft.AspNetCore.Mvc.Controller` or `Microsoft.AspNetCore.Mvc.ControllerBase` class. */ class MicrosoftAspNetCoreMvcControllerBaseClass extends Class { MicrosoftAspNetCoreMvcControllerBaseClass() { - getNamespace() instanceof MicrosoftAspNetCoreMvcNamespace and + this.getNamespace() instanceof MicrosoftAspNetCoreMvcNamespace and ( - hasName("Controller") or - hasName("ControllerBase") + this.hasName("Controller") or + this.hasName("ControllerBase") ) } } @@ -181,12 +185,12 @@ class MicrosoftAspNetCoreMvcControllerBaseClass extends Class { /** A subtype of `Microsoft.AspNetCore.Mvc.Controller` or `Microsoft.AspNetCore.Mvc.ControllerBase`. */ class MicrosoftAspNetCoreMvcController extends Class { MicrosoftAspNetCoreMvcController() { - getABaseType*() instanceof MicrosoftAspNetCoreMvcControllerBaseClass + this.getABaseType*() instanceof MicrosoftAspNetCoreMvcControllerBaseClass } /** Gets an action method for this controller. */ Method getAnActionMethod() { - result = getAMethod() and + result = this.getAMethod() and result.isPublic() and not result.isStatic() and not result.getAnAttribute() instanceof MicrosoftAspNetCoreMvcNonActionAttribute @@ -208,12 +212,12 @@ class MicrosoftAspNetCoreMvcController extends Class { /** The `Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper` interface. */ class MicrosoftAspNetCoreMvcRenderingIHtmlHelperInterface extends Interface { MicrosoftAspNetCoreMvcRenderingIHtmlHelperInterface() { - getNamespace() instanceof MicrosoftAspNetCoreMvcRendering and - hasName("IHtmlHelper") + this.getNamespace() instanceof MicrosoftAspNetCoreMvcRendering and + this.hasName("IHtmlHelper") } /** Gets the `Raw` method. */ - Method getRawMethod() { result = getAMethod("Raw") } + Method getRawMethod() { result = this.getAMethod("Raw") } } /** A class deriving from `Microsoft.AspNetCore.Mvc.Razor.RazorPageBase`, implements Razor page in ASPNET Core. */ @@ -223,7 +227,7 @@ class MicrosoftAspNetCoreMvcRazorPageBase extends Class { } /** Gets the `WriteLiteral` method. */ - Method getWriteLiteralMethod() { result = getAMethod("WriteLiteral") } + Method getWriteLiteralMethod() { result = this.getAMethod("WriteLiteral") } } /** A class deriving from `Microsoft.AspNetCore.Http.HttpRequest`, implements `HttpRequest` in ASP.NET Core. */ diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/system/collections/Generic.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/system/collections/Generic.qll index a3616e57522..2b632d2b07c 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/system/collections/Generic.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/system/collections/Generic.qll @@ -41,8 +41,8 @@ class SystemCollectionsGenericIComparerTInterface extends SystemCollectionsGener result.getDeclaringType() = this and result.hasName("Compare") and result.getNumberOfParameters() = 2 and - result.getParameter(0).getType() = getTypeParameter(0) and - result.getParameter(1).getType() = getTypeParameter(0) and + result.getParameter(0).getType() = this.getTypeParameter(0) and + result.getParameter(1).getType() = this.getTypeParameter(0) and result.getReturnType() instanceof IntType } } @@ -56,8 +56,8 @@ class SystemCollectionsGenericIEqualityComparerTInterface extends SystemCollecti result.getDeclaringType() = this and result.hasName("Equals") and result.getNumberOfParameters() = 2 and - result.getParameter(0).getType() = getTypeParameter(0) and - result.getParameter(1).getType() = getTypeParameter(0) and + result.getParameter(0).getType() = this.getTypeParameter(0) and + result.getParameter(1).getType() = this.getTypeParameter(0) and result.getReturnType() instanceof BoolType } } diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/system/data/SqlClient.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/system/data/SqlClient.qll index 858100fe7f7..c3b6f1fdd6d 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/system/data/SqlClient.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/system/data/SqlClient.qll @@ -13,7 +13,7 @@ class SystemDataSqlClientNamespace extends Namespace { /** A class in the `System.Data.SqlClient` namespace. */ class SystemDataSqlClientClass extends Class { - SystemDataSqlClientClass() { getNamespace() instanceof SystemDataSqlClientNamespace } + SystemDataSqlClientClass() { this.getNamespace() instanceof SystemDataSqlClientNamespace } } /** The `System.Data.SqlClient.SqlDataAdapter` class. */ diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/system/text/RegularExpressions.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/system/text/RegularExpressions.qll index 1820192da11..531fa6ef721 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/system/text/RegularExpressions.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/system/text/RegularExpressions.qll @@ -67,7 +67,7 @@ class RegexOperation extends Call { */ Expr getInput() { if this instanceof MethodCall - then result = getArgumentForName("input") + then result = this.getArgumentForName("input") else exists(MethodCall call | call.getTarget() = any(SystemTextRegularExpressionsRegexClass rs).getAMethod() and diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/system/web/Mvc.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/system/web/Mvc.qll index 78aaa6dc065..b2051a8464f 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/system/web/Mvc.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/system/web/Mvc.qll @@ -6,8 +6,8 @@ private import semmle.code.csharp.frameworks.system.Web /** The `System.Web.Mvc` namespace. */ class SystemWebMvcNamespace extends Namespace { SystemWebMvcNamespace() { - getParentNamespace() instanceof SystemWebNamespace and - hasName("Mvc") + this.getParentNamespace() instanceof SystemWebNamespace and + this.hasName("Mvc") } } @@ -31,7 +31,7 @@ class SystemWebMvcHtmlHelperClass extends SystemWebMvcClass { /** An attribute whose type is in the `System.Web.Mvc` namespace. */ class SystemWebMvcAttribute extends Attribute { - SystemWebMvcAttribute() { getType().getNamespace() instanceof SystemWebMvcNamespace } + SystemWebMvcAttribute() { this.getType().getNamespace() instanceof SystemWebMvcNamespace } } /** An attribute whose type is `System.Web.Mvc.HttpPost`. */ diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/system/web/WebPages.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/system/web/WebPages.qll index 0d43f76719b..915acbfe41f 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/system/web/WebPages.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/system/web/WebPages.qll @@ -6,16 +6,16 @@ private import semmle.code.csharp.frameworks.system.Web /** The `System.Web.WebPages` namespace. */ class SystemWebWebPagesNamespace extends Namespace { SystemWebWebPagesNamespace() { - getParentNamespace() instanceof SystemWebNamespace and - hasName("WebPages") + this.getParentNamespace() instanceof SystemWebNamespace and + this.hasName("WebPages") } } /** The `System.Web.WebPages.WebPageExecutingBase` class. */ class SystemWebWebPagesWebPageExecutingBaseClass extends Class { SystemWebWebPagesWebPageExecutingBaseClass() { - getNamespace() instanceof SystemWebWebPagesNamespace and - hasName("WebPageExecutingBase") + this.getNamespace() instanceof SystemWebWebPagesNamespace and + this.hasName("WebPageExecutingBase") } } @@ -24,8 +24,8 @@ class WebPageClass extends Class { WebPageClass() { this.getBaseClass*() instanceof SystemWebWebPagesWebPageExecutingBaseClass } /** Gets the `WriteLiteral` method. */ - Method getWriteLiteralMethod() { result = getAMethod("WriteLiteral") } + Method getWriteLiteralMethod() { result = this.getAMethod("WriteLiteral") } /** Gets the `WriteLiteralTo` method. */ - Method getWriteLiteralToMethod() { result = getAMethod("WriteLiteralTo") } + Method getWriteLiteralToMethod() { result = this.getAMethod("WriteLiteralTo") } } diff --git a/csharp/ql/lib/semmle/code/csharp/security/PrivateData.qll b/csharp/ql/lib/semmle/code/csharp/security/PrivateData.qll index 10bb06679b7..3ca81e614ad 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/PrivateData.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/PrivateData.qll @@ -14,26 +14,22 @@ import semmle.code.csharp.frameworks.system.windows.Forms /** A string for `match` that identifies strings that look like they represent private data. */ private string privateNames() { - // Inspired by the list on https://cwe.mitre.org/data/definitions/359.html - // Government identifiers, such as Social Security Numbers - result = "%social%security%number%" or - // Contact information, such as home addresses and telephone numbers - result = "%postcode%" or - result = "%zipcode%" or - result = "%telephone%" or - // Geographic location - where the user is (or was) - result = "%latitude%" or - result = "%longitude%" or - // Financial data - such as credit card numbers, salary, bank accounts, and debts - result = "%creditcard%" or - result = "%salary%" or - result = "%bankaccount%" or - // Communications - e-mail addresses, private e-mail messages, SMS text messages, chat logs, etc. - result = "%email%" or - result = "%mobile%" or - result = "%employer%" or - // Health - medical conditions, insurance status, prescription records - result = "%medical%" + result = + [ + // Inspired by the list on https://cwe.mitre.org/data/definitions/359.html + // Government identifiers, such as Social Security Numbers + "%social%security%number%", + // Contact information, such as home addresses and telephone numbers + "%postcode%", "%zipcode%", "%telephone%", + // Geographic location - where the user is (or was) + "%latitude%", "%longitude%", + // Financial data - such as credit card numbers, salary, bank accounts, and debts + "%creditcard%", "%salary%", "%bankaccount%", + // Communications - e-mail addresses, private e-mail messages, SMS text messages, chat logs, etc. + "%email%", "%mobile%", "%employer%", + // Health - medical conditions, insurance status, prescription records + "%medical%" + ] } /** An expression that might contain private data. */ diff --git a/csharp/ql/lib/semmle/code/csharp/security/SensitiveActions.qll b/csharp/ql/lib/semmle/code/csharp/security/SensitiveActions.qll index cc7701ad318..483000895aa 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/SensitiveActions.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/SensitiveActions.qll @@ -72,7 +72,7 @@ class SensitiveProperty extends Property { /** A parameter to a library method that may hold a sensitive value. */ class SensitiveLibraryParameter extends Parameter { SensitiveLibraryParameter() { - fromLibrary() and + this.fromLibrary() and exists(string s | this.getName().toLowerCase() = s | s.matches(suspicious())) } } diff --git a/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll b/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll index 915dae12a9f..3cf3fa107bf 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll @@ -21,7 +21,7 @@ module HardcodedSymmetricEncryptionKey { abstract class Sanitizer extends DataFlow::ExprNode { } private class ByteArrayType extends ArrayType { - ByteArrayType() { getElementType() instanceof ByteType } + ByteArrayType() { this.getElementType() instanceof ByteType } } private class ByteArrayLiteralSource extends Source { @@ -49,7 +49,7 @@ module HardcodedSymmetricEncryptionKey { private class SymmetricEncryptionCreateEncryptorSink extends Sink { SymmetricEncryptionCreateEncryptorSink() { exists(SymmetricAlgorithm ag, MethodCall mc | mc = ag.getASymmetricEncryptor() | - asExpr() = mc.getArgumentForName("rgbKey") + this.asExpr() = mc.getArgumentForName("rgbKey") ) } @@ -59,7 +59,7 @@ module HardcodedSymmetricEncryptionKey { private class SymmetricEncryptionCreateDecryptorSink extends Sink { SymmetricEncryptionCreateDecryptorSink() { exists(SymmetricAlgorithm ag, MethodCall mc | mc = ag.getASymmetricDecryptor() | - asExpr() = mc.getArgumentForName("rgbKey") + this.asExpr() = mc.getArgumentForName("rgbKey") ) } diff --git a/csharp/ql/lib/semmle/code/csharp/security/dataflow/ExternalAPIsQuery.qll b/csharp/ql/lib/semmle/code/csharp/security/dataflow/ExternalAPIsQuery.qll index bccd71d7096..fd643b5b7f0 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/dataflow/ExternalAPIsQuery.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/dataflow/ExternalAPIsQuery.qll @@ -69,7 +69,7 @@ class ExternalAPIDataNode extends DataFlow::Node { int getIndex() { result = i } /** Gets the description of the callable being called. */ - string getCallableDescription() { result = getCallable().getQualifiedName() } + string getCallableDescription() { result = this.getCallable().getQualifiedName() } } /** A configuration for tracking flow from `RemoteFlowSource`s to `ExternalAPIDataNode`s. */ @@ -108,7 +108,7 @@ class ExternalAPIUsedWithUntrustedData extends TExternalAPI { /** Gets the number of untrusted sources used with this external API. */ int getNumberOfUntrustedSources() { - result = count(getUntrustedDataNode().getAnUntrustedSource()) + result = count(this.getUntrustedDataNode().getAnUntrustedSource()) } /** Gets a textual representation of this element. */ diff --git a/csharp/ql/lib/semmle/code/csharp/security/dataflow/XSSSinks.qll b/csharp/ql/lib/semmle/code/csharp/security/dataflow/XSSSinks.qll index 4be005be4de..d0999605b61 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/dataflow/XSSSinks.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/dataflow/XSSSinks.qll @@ -166,7 +166,7 @@ class AspInlineMember extends AspInlineCode { Member getMember() { result = member } /** Gets the type of this member. */ - Type getType() { result = getMemberType(getMember()) } + Type getType() { result = getMemberType(this.getMember()) } } /** Gets a value that is written to the member accessed by the given `AspInlineMember`. */ @@ -251,7 +251,7 @@ private class HttpResponseBaseSink extends Sink { */ private class StringContentSinkModelCsv extends SinkModelCsv { override predicate row(string row) { - row = ["System.Net.Http;StringContent;false;StringContent;;;Argument[0];xss"] + row = "System.Net.Http;StringContent;false;StringContent;;;Argument[0];xss" } } diff --git a/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsinks/ExternalLocationSink.qll b/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsinks/ExternalLocationSink.qll index 673473eb49b..30736bdabf6 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsinks/ExternalLocationSink.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsinks/ExternalLocationSink.qll @@ -38,12 +38,7 @@ class TraceMessageSink extends ExternalLocationSink { trace.hasQualifiedName("System.Diagnostics", "TraceSource") | this.getExpr() = trace.getAMethod().getACall().getArgumentForName(parameterName) and - ( - parameterName = "format" or - parameterName = "args" or - parameterName = "message" or - parameterName = "category" - ) + parameterName = ["format", "args", "message", "category"] ) } } diff --git a/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll b/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll index 25647e50f2e..2448f80e8ba 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll @@ -43,15 +43,8 @@ class AspNetQueryStringMember extends Member { * request. */ private string getHttpRequestFlowPropertyNames() { - result = "QueryString" or - result = "Headers" or - result = "RawUrl" or - result = "Url" or - result = "Cookies" or - result = "Form" or - result = "Params" or - result = "Path" or - result = "PathInfo" + result = + ["QueryString", "Headers", "RawUrl", "Url", "Cookies", "Form", "Params", "Path", "PathInfo"] } /** A data flow source of remote user input (ASP.NET query string). */ @@ -86,7 +79,7 @@ class AspNetUnvalidatedQueryStringRemoteFlowSource extends AspNetRemoteFlowSourc /** A data flow source of remote user input (ASP.NET user input). */ class AspNetUserInputRemoteFlowSource extends AspNetRemoteFlowSource, DataFlow::ExprNode { - AspNetUserInputRemoteFlowSource() { getType() instanceof SystemWebUIWebControlsTextBoxClass } + AspNetUserInputRemoteFlowSource() { this.getType() instanceof SystemWebUIWebControlsTextBoxClass } override string getSourceType() { result = "ASP.NET user input" } } @@ -113,7 +106,7 @@ class AspNetServiceRemoteFlowSource extends RemoteFlowSource, DataFlow::Paramete /** A data flow source of remote user input (ASP.NET request message). */ class SystemNetHttpRequestMessageRemoteFlowSource extends RemoteFlowSource, DataFlow::ExprNode { SystemNetHttpRequestMessageRemoteFlowSource() { - getType() instanceof SystemWebHttpRequestMessageClass + this.getType() instanceof SystemWebHttpRequestMessageClass } override string getSourceType() { result = "ASP.NET request message" } diff --git a/csharp/ql/lib/semmle/code/csharp/security/xml/InsecureXMLQuery.qll b/csharp/ql/lib/semmle/code/csharp/security/xml/InsecureXMLQuery.qll index 2483452113a..17856d3e7e8 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/xml/InsecureXMLQuery.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/xml/InsecureXMLQuery.qll @@ -157,8 +157,8 @@ module XmlReader { override predicate isUnsafe(string reason) { exists(string dtdReason, string resolverReason | - dtdEnabled(dtdReason, _) and - insecureResolver(resolverReason, _) and + this.dtdEnabled(dtdReason, _) and + this.insecureResolver(resolverReason, _) and reason = dtdReason + ", " + resolverReason ) } diff --git a/csharp/ql/lib/semmle/code/dotnet/Callable.qll b/csharp/ql/lib/semmle/code/dotnet/Callable.qll index 2beccfe422c..0a63e5c95cd 100644 --- a/csharp/ql/lib/semmle/code/dotnet/Callable.qll +++ b/csharp/ql/lib/semmle/code/dotnet/Callable.qll @@ -75,7 +75,7 @@ class Callable extends Parameterizable, @dotnet_callable { } private string getReturnTypeLabel() { - result = getReturnType().getLabel() + result = this.getReturnType().getLabel() or not exists(this.getReturnType()) and result = "System.Void" } diff --git a/csharp/ql/lib/semmle/code/dotnet/Declaration.qll b/csharp/ql/lib/semmle/code/dotnet/Declaration.qll index 7f78077d766..60e434f9ae6 100644 --- a/csharp/ql/lib/semmle/code/dotnet/Declaration.qll +++ b/csharp/ql/lib/semmle/code/dotnet/Declaration.qll @@ -16,7 +16,7 @@ class Declaration extends NamedElement, @dotnet_declaration { string getUndecoratedName() { none() } /** Holds if this element has undecorated name 'name'. */ - final predicate hasUndecoratedName(string name) { name = getUndecoratedName() } + final predicate hasUndecoratedName(string name) { name = this.getUndecoratedName() } /** Gets the type containing this declaration, if any. */ Type getDeclaringType() { none() } diff --git a/csharp/ql/lib/semmle/code/dotnet/Element.qll b/csharp/ql/lib/semmle/code/dotnet/Element.qll index 3b1955887f9..d8c27f88e10 100644 --- a/csharp/ql/lib/semmle/code/dotnet/Element.qll +++ b/csharp/ql/lib/semmle/code/dotnet/Element.qll @@ -35,7 +35,7 @@ class Element extends @dotnet_element { * Gets the "language" of this program element, as defined by the extension of the filename. * For example, C# has language "cs", and Visual Basic has language "vb". */ - final string getLanguage() { result = getLocation().getFile().getExtension() } + final string getLanguage() { result = this.getLocation().getFile().getExtension() } /** Gets the full textual representation of this element, including type information. */ string toStringWithTypes() { result = this.toString() } @@ -43,7 +43,7 @@ class Element extends @dotnet_element { /** * Gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs. */ - final string getPrimaryQlClasses() { result = concat(getAPrimaryQlClass(), ",") } + final string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") } /** * Gets the name of a primary CodeQL class to which this element belongs. @@ -66,7 +66,7 @@ class NamedElement extends Element, @dotnet_named_element { string getName() { none() } /** Holds if this element has name 'name'. */ - final predicate hasName(string name) { name = getName() } + final predicate hasName(string name) { name = this.getName() } /** * Gets the fully qualified name of this element, for example the diff --git a/csharp/ql/lib/semmle/code/dotnet/Expr.qll b/csharp/ql/lib/semmle/code/dotnet/Expr.qll index e5bea3a52d7..15d658f54c2 100644 --- a/csharp/ql/lib/semmle/code/dotnet/Expr.qll +++ b/csharp/ql/lib/semmle/code/dotnet/Expr.qll @@ -45,7 +45,7 @@ class Call extends Expr, @dotnet_call { Expr getArgument(int i) { none() } /** Gets an argument to this call. */ - Expr getAnArgument() { result = getArgument(_) } + Expr getAnArgument() { result = this.getArgument(_) } /** Gets the expression that is supplied for parameter `p`. */ Expr getArgumentForParameter(Parameter p) { none() } diff --git a/csharp/ql/lib/semmle/code/dotnet/Generics.qll b/csharp/ql/lib/semmle/code/dotnet/Generics.qll index 9b236cdbfb9..f84718d4b82 100644 --- a/csharp/ql/lib/semmle/code/dotnet/Generics.qll +++ b/csharp/ql/lib/semmle/code/dotnet/Generics.qll @@ -14,7 +14,7 @@ abstract class UnboundGeneric extends Generic { abstract TypeParameter getTypeParameter(int i); /** Gets a type parameter. */ - TypeParameter getATypeParameter() { result = getTypeParameter(_) } + TypeParameter getATypeParameter() { result = this.getTypeParameter(_) } /** * Gets one of the constructed versions of this declaration, @@ -32,7 +32,7 @@ abstract class ConstructedGeneric extends Generic { abstract Type getTypeArgument(int i); /** Gets a type argument. */ - Type getATypeArgument() { result = getTypeArgument(_) } + Type getATypeArgument() { result = this.getTypeArgument(_) } /** * Gets the unbound generic declaration from which this declaration was diff --git a/csharp/ql/lib/semmle/code/dotnet/Namespace.qll b/csharp/ql/lib/semmle/code/dotnet/Namespace.qll index 55b42e737db..324448728de 100644 --- a/csharp/ql/lib/semmle/code/dotnet/Namespace.qll +++ b/csharp/ql/lib/semmle/code/dotnet/Namespace.qll @@ -33,7 +33,7 @@ class Namespace extends Declaration, @namespace { override string toString() { result = this.getQualifiedName() } /** Holds if this is the global namespace. */ - final predicate isGlobalNamespace() { getName() = "" } + final predicate isGlobalNamespace() { this.getName() = "" } /** Gets the simple name of this namespace, for example `IO` in `System.IO`. */ final override string getName() { namespaces(this, result) } diff --git a/csharp/ql/lib/semmle/code/dotnet/Type.qll b/csharp/ql/lib/semmle/code/dotnet/Type.qll index 5dc9c409c18..81fefa96550 100644 --- a/csharp/ql/lib/semmle/code/dotnet/Type.qll +++ b/csharp/ql/lib/semmle/code/dotnet/Type.qll @@ -24,11 +24,11 @@ class ValueOrRefType extends Type, @dotnet_valueorreftype { Namespace getDeclaringNamespace() { none() } private string getPrefixWithTypes() { - result = getDeclaringType().getLabel() + "." + result = this.getDeclaringType().getLabel() + "." or - if getDeclaringNamespace().isGlobalNamespace() + if this.getDeclaringNamespace().isGlobalNamespace() then result = "" - else result = getDeclaringNamespace().getQualifiedName() + "." + else result = this.getDeclaringNamespace().getQualifiedName() + "." } pragma[noinline] @@ -64,9 +64,9 @@ class TypeParameter extends Type, @dotnet_type_parameter { /** Gets the index of this type parameter. For example the index of `U` in `Func` is 1. */ int getIndex() { none() } - final override string getLabel() { result = "!" + getIndex() } + final override string getLabel() { result = "!" + this.getIndex() } - override string getUndecoratedName() { result = "!" + getIndex() } + override string getUndecoratedName() { result = "!" + this.getIndex() } } /** A pointer type. */ @@ -76,9 +76,9 @@ class PointerType extends Type, @dotnet_pointer_type { override string getName() { result = this.getReferentType().getName() + "*" } - final override string getLabel() { result = getReferentType().getLabel() + "*" } + final override string getLabel() { result = this.getReferentType().getLabel() + "*" } - override string toStringWithTypes() { result = getReferentType().toStringWithTypes() + "*" } + override string toStringWithTypes() { result = this.getReferentType().toStringWithTypes() + "*" } } /** An array type. */ @@ -86,7 +86,7 @@ class ArrayType extends ValueOrRefType, @dotnet_array_type { /** Gets the type of the array element. */ Type getElementType() { none() } - final override string getLabel() { result = getElementType().getLabel() + "[]" } + final override string getLabel() { result = this.getElementType().getLabel() + "[]" } - override string toStringWithTypes() { result = getElementType().toStringWithTypes() + "[]" } + override string toStringWithTypes() { result = this.getElementType().toStringWithTypes() + "[]" } } diff --git a/csharp/ql/lib/semmle/code/dotnet/Variable.qll b/csharp/ql/lib/semmle/code/dotnet/Variable.qll index 9f9a12e7d98..ee9ccebbbe6 100644 --- a/csharp/ql/lib/semmle/code/dotnet/Variable.qll +++ b/csharp/ql/lib/semmle/code/dotnet/Variable.qll @@ -15,10 +15,10 @@ class Field extends Variable, Member, @dotnet_field { } /** A parameter to a .Net callable, property or function pointer type. */ class Parameter extends Variable, @dotnet_parameter { /** Gets the raw position of this parameter, including the `this` parameter at index 0. */ - final int getRawPosition() { this = getDeclaringElement().getRawParameter(result) } + final int getRawPosition() { this = this.getDeclaringElement().getRawParameter(result) } /** Gets the position of this parameter, excluding the `this` parameter. */ - int getPosition() { this = getDeclaringElement().getParameter(result) } + int getPosition() { this = this.getDeclaringElement().getParameter(result) } /** Gets the callable defining this parameter. */ Callable getCallable() { result = this.getDeclaringElement() } diff --git a/csharp/ql/lib/tutorial.qll b/csharp/ql/lib/tutorial.qll new file mode 100644 index 00000000000..8cb1797a532 --- /dev/null +++ b/csharp/ql/lib/tutorial.qll @@ -0,0 +1,1207 @@ +/** + * This library is used in the QL detective tutorials. + * + * Note: Data is usually stored in a separate database and the QL libraries only contain predicates, + * but for this tutorial both the data and the predicates are stored in the library. + */ +class Person extends string { + Person() { + this = "Ronil" or + this = "Dina" or + this = "Ravi" or + this = "Bruce" or + this = "Jo" or + this = "Aida" or + this = "Esme" or + this = "Charlie" or + this = "Fred" or + this = "Meera" or + this = "Maya" or + this = "Chad" or + this = "Tiana" or + this = "Laura" or + this = "George" or + this = "Will" or + this = "Mary" or + this = "Almira" or + this = "Susannah" or + this = "Rhoda" or + this = "Cynthia" or + this = "Eunice" or + this = "Olive" or + this = "Virginia" or + this = "Angeline" or + this = "Helen" or + this = "Cornelia" or + this = "Harriet" or + this = "Mahala" or + this = "Abby" or + this = "Margaret" or + this = "Deb" or + this = "Minerva" or + this = "Severus" or + this = "Lavina" or + this = "Adeline" or + this = "Cath" or + this = "Elisa" or + this = "Lucretia" or + this = "Anne" or + this = "Eleanor" or + this = "Joanna" or + this = "Adam" or + this = "Agnes" or + this = "Rosanna" or + this = "Clara" or + this = "Melissa" or + this = "Amy" or + this = "Isabel" or + this = "Jemima" or + this = "Cordelia" or + this = "Melinda" or + this = "Delila" or + this = "Jeremiah" or + this = "Elijah" or + this = "Hester" or + this = "Walter" or + this = "Oliver" or + this = "Hugh" or + this = "Aaron" or + this = "Reuben" or + this = "Eli" or + this = "Amos" or + this = "Augustus" or + this = "Theodore" or + this = "Ira" or + this = "Timothy" or + this = "Cyrus" or + this = "Horace" or + this = "Simon" or + this = "Asa" or + this = "Frank" or + this = "Nelson" or + this = "Leonard" or + this = "Harrison" or + this = "Anthony" or + this = "Louis" or + this = "Milton" or + this = "Noah" or + this = "Cornelius" or + this = "Abdul" or + this = "Warren" or + this = "Harvey" or + this = "Dennis" or + this = "Wesley" or + this = "Sylvester" or + this = "Gilbert" or + this = "Sullivan" or + this = "Edmund" or + this = "Wilson" or + this = "Perry" or + this = "Matthew" or + this = "Simba" or + this = "Nala" or + this = "Rafiki" or + this = "Shenzi" or + this = "Ernest" or + this = "Gertrude" or + this = "Oscar" or + this = "Lilian" or + this = "Raymond" or + this = "Elgar" or + this = "Elmer" or + this = "Herbert" or + this = "Maude" or + this = "Mae" or + this = "Otto" or + this = "Edwin" or + this = "Ophelia" or + this = "Parsley" or + this = "Sage" or + this = "Rosemary" or + this = "Thyme" or + this = "Garfunkel" or + this = "King Basil" or + this = "Stephen" + } + + /** Gets the hair color of the person. If the person is bald, there is no result. */ + string getHairColor() { + this = "Ronil" and result = "black" + or + this = "Dina" and result = "black" + or + this = "Ravi" and result = "black" + or + this = "Bruce" and result = "brown" + or + this = "Jo" and result = "red" + or + this = "Aida" and result = "blond" + or + this = "Esme" and result = "blond" + or + this = "Fred" and result = "gray" + or + this = "Meera" and result = "brown" + or + this = "Maya" and result = "brown" + or + this = "Chad" and result = "brown" + or + this = "Tiana" and result = "black" + or + this = "Laura" and result = "blond" + or + this = "George" and result = "blond" + or + this = "Will" and result = "blond" + or + this = "Mary" and result = "blond" + or + this = "Almira" and result = "black" + or + this = "Susannah" and result = "blond" + or + this = "Rhoda" and result = "blond" + or + this = "Cynthia" and result = "gray" + or + this = "Eunice" and result = "white" + or + this = "Olive" and result = "brown" + or + this = "Virginia" and result = "brown" + or + this = "Angeline" and result = "red" + or + this = "Helen" and result = "white" + or + this = "Cornelia" and result = "gray" + or + this = "Harriet" and result = "white" + or + this = "Mahala" and result = "black" + or + this = "Abby" and result = "red" + or + this = "Margaret" and result = "brown" + or + this = "Deb" and result = "brown" + or + this = "Minerva" and result = "brown" + or + this = "Severus" and result = "black" + or + this = "Lavina" and result = "brown" + or + this = "Adeline" and result = "brown" + or + this = "Cath" and result = "brown" + or + this = "Elisa" and result = "brown" + or + this = "Lucretia" and result = "gray" + or + this = "Anne" and result = "black" + or + this = "Eleanor" and result = "brown" + or + this = "Joanna" and result = "brown" + or + this = "Adam" and result = "black" + or + this = "Agnes" and result = "black" + or + this = "Rosanna" and result = "gray" + or + this = "Clara" and result = "blond" + or + this = "Melissa" and result = "brown" + or + this = "Amy" and result = "brown" + or + this = "Isabel" and result = "black" + or + this = "Jemima" and result = "red" + or + this = "Cordelia" and result = "red" + or + this = "Melinda" and result = "gray" + or + this = "Delila" and result = "white" + or + this = "Jeremiah" and result = "gray" + or + this = "Hester" and result = "black" + or + this = "Walter" and result = "black" + or + this = "Aaron" and result = "gray" + or + this = "Reuben" and result = "gray" + or + this = "Eli" and result = "gray" + or + this = "Amos" and result = "white" + or + this = "Augustus" and result = "white" + or + this = "Theodore" and result = "white" + or + this = "Timothy" and result = "brown" + or + this = "Cyrus" and result = "brown" + or + this = "Horace" and result = "brown" + or + this = "Simon" and result = "brown" + or + this = "Asa" and result = "brown" + or + this = "Frank" and result = "brown" + or + this = "Nelson" and result = "black" + or + this = "Leonard" and result = "black" + or + this = "Harrison" and result = "black" + or + this = "Anthony" and result = "black" + or + this = "Louis" and result = "black" + or + this = "Milton" and result = "blond" + or + this = "Noah" and result = "blond" + or + this = "Cornelius" and result = "red" + or + this = "Abdul" and result = "brown" + or + this = "Warren" and result = "red" + or + this = "Harvey" and result = "blond" + or + this = "Dennis" and result = "blond" + or + this = "Wesley" and result = "brown" + or + this = "Sylvester" and result = "brown" + or + this = "Gilbert" and result = "brown" + or + this = "Sullivan" and result = "brown" + or + this = "Edmund" and result = "brown" + or + this = "Wilson" and result = "blond" + or + this = "Perry" and result = "black" + or + this = "Simba" and result = "brown" + or + this = "Nala" and result = "brown" + or + this = "Rafiki" and result = "red" + or + this = "Shenzi" and result = "gray" + or + this = "Ernest" and result = "blond" + or + this = "Gertrude" and result = "brown" + or + this = "Oscar" and result = "blond" + or + this = "Lilian" and result = "brown" + or + this = "Raymond" and result = "brown" + or + this = "Elgar" and result = "brown" + or + this = "Elmer" and result = "brown" + or + this = "Herbert" and result = "brown" + or + this = "Maude" and result = "brown" + or + this = "Mae" and result = "brown" + or + this = "Otto" and result = "black" + or + this = "Edwin" and result = "black" + or + this = "Ophelia" and result = "brown" + or + this = "Parsley" and result = "brown" + or + this = "Sage" and result = "brown" + or + this = "Rosemary" and result = "brown" + or + this = "Thyme" and result = "brown" + or + this = "Garfunkel" and result = "brown" + or + this = "King Basil" and result = "brown" + or + this = "Stephen" and result = "black" + or + this = "Stephen" and result = "gray" + } + + /** Gets the age of the person (in years). If the person is deceased, there is no result. */ + int getAge() { + this = "Ronil" and result = 21 + or + this = "Dina" and result = 53 + or + this = "Ravi" and result = 16 + or + this = "Bruce" and result = 35 + or + this = "Jo" and result = 47 + or + this = "Aida" and result = 26 + or + this = "Esme" and result = 25 + or + this = "Charlie" and result = 31 + or + this = "Fred" and result = 68 + or + this = "Meera" and result = 62 + or + this = "Maya" and result = 29 + or + this = "Chad" and result = 49 + or + this = "Tiana" and result = 18 + or + this = "Laura" and result = 2 + or + this = "George" and result = 3 + or + this = "Will" and result = 41 + or + this = "Mary" and result = 51 + or + this = "Almira" and result = 1 + or + this = "Susannah" and result = 97 + or + this = "Rhoda" and result = 39 + or + this = "Cynthia" and result = 89 + or + this = "Eunice" and result = 83 + or + this = "Olive" and result = 25 + or + this = "Virginia" and result = 52 + or + this = "Angeline" and result = 22 + or + this = "Helen" and result = 79 + or + this = "Cornelia" and result = 59 + or + this = "Harriet" and result = 57 + or + this = "Mahala" and result = 61 + or + this = "Abby" and result = 24 + or + this = "Margaret" and result = 59 + or + this = "Deb" and result = 31 + or + this = "Minerva" and result = 72 + or + this = "Severus" and result = 61 + or + this = "Lavina" and result = 33 + or + this = "Adeline" and result = 17 + or + this = "Cath" and result = 22 + or + this = "Elisa" and result = 9 + or + this = "Lucretia" and result = 56 + or + this = "Anne" and result = 11 + or + this = "Eleanor" and result = 80 + or + this = "Joanna" and result = 43 + or + this = "Adam" and result = 37 + or + this = "Agnes" and result = 47 + or + this = "Rosanna" and result = 61 + or + this = "Clara" and result = 31 + or + this = "Melissa" and result = 37 + or + this = "Amy" and result = 12 + or + this = "Isabel" and result = 6 + or + this = "Jemima" and result = 16 + or + this = "Cordelia" and result = 21 + or + this = "Melinda" and result = 55 + or + this = "Delila" and result = 66 + or + this = "Jeremiah" and result = 54 + or + this = "Elijah" and result = 42 + or + this = "Hester" and result = 68 + or + this = "Walter" and result = 66 + or + this = "Oliver" and result = 33 + or + this = "Hugh" and result = 51 + or + this = "Aaron" and result = 49 + or + this = "Reuben" and result = 58 + or + this = "Eli" and result = 70 + or + this = "Amos" and result = 65 + or + this = "Augustus" and result = 56 + or + this = "Theodore" and result = 69 + or + this = "Ira" and result = 1 + or + this = "Timothy" and result = 54 + or + this = "Cyrus" and result = 78 + or + this = "Horace" and result = 34 + or + this = "Simon" and result = 23 + or + this = "Asa" and result = 28 + or + this = "Frank" and result = 59 + or + this = "Nelson" and result = 38 + or + this = "Leonard" and result = 58 + or + this = "Harrison" and result = 7 + or + this = "Anthony" and result = 2 + or + this = "Louis" and result = 34 + or + this = "Milton" and result = 36 + or + this = "Noah" and result = 48 + or + this = "Cornelius" and result = 41 + or + this = "Abdul" and result = 67 + or + this = "Warren" and result = 47 + or + this = "Harvey" and result = 31 + or + this = "Dennis" and result = 39 + or + this = "Wesley" and result = 13 + or + this = "Sylvester" and result = 19 + or + this = "Gilbert" and result = 16 + or + this = "Sullivan" and result = 17 + or + this = "Edmund" and result = 29 + or + this = "Wilson" and result = 27 + or + this = "Perry" and result = 31 + or + this = "Matthew" and result = 55 + or + this = "Simba" and result = 8 + or + this = "Nala" and result = 7 + or + this = "Rafiki" and result = 76 + or + this = "Shenzi" and result = 67 + } + + /** Gets the height of the person (in cm). If the person is deceased, there is no result. */ + float getHeight() { + this = "Ronil" and result = 183.0 + or + this = "Dina" and result = 155.1 + or + this = "Ravi" and result = 175.2 + or + this = "Bruce" and result = 191.3 + or + this = "Jo" and result = 163.4 + or + this = "Aida" and result = 182.6 + or + this = "Esme" and result = 176.9 + or + this = "Charlie" and result = 189.7 + or + this = "Fred" and result = 179.4 + or + this = "Meera" and result = 160.1 + or + this = "Maya" and result = 153.0 + or + this = "Chad" and result = 168.5 + or + this = "Tiana" and result = 149.7 + or + this = "Laura" and result = 87.5 + or + this = "George" and result = 96.4 + or + this = "Will" and result = 167.1 + or + this = "Mary" and result = 159.8 + or + this = "Almira" and result = 62.1 + or + this = "Susannah" and result = 145.8 + or + this = "Rhoda" and result = 180.1 + or + this = "Cynthia" and result = 161.8 + or + this = "Eunice" and result = 153.2 + or + this = "Olive" and result = 179.9 + or + this = "Virginia" and result = 165.1 + or + this = "Angeline" and result = 172.3 + or + this = "Helen" and result = 163.1 + or + this = "Cornelia" and result = 160.8 + or + this = "Harriet" and result = 163.2 + or + this = "Mahala" and result = 157.7 + or + this = "Abby" and result = 174.5 + or + this = "Margaret" and result = 165.6 + or + this = "Deb" and result = 171.6 + or + this = "Minerva" and result = 168.7 + or + this = "Severus" and result = 188.8 + or + this = "Lavina" and result = 155.1 + or + this = "Adeline" and result = 165.5 + or + this = "Cath" and result = 147.8 + or + this = "Elisa" and result = 129.4 + or + this = "Lucretia" and result = 153.6 + or + this = "Anne" and result = 140.4 + or + this = "Eleanor" and result = 151.1 + or + this = "Joanna" and result = 167.2 + or + this = "Adam" and result = 155.5 + or + this = "Agnes" and result = 156.8 + or + this = "Rosanna" and result = 162.4 + or + this = "Clara" and result = 158.6 + or + this = "Melissa" and result = 182.3 + or + this = "Amy" and result = 147.1 + or + this = "Isabel" and result = 121.4 + or + this = "Jemima" and result = 149.8 + or + this = "Cordelia" and result = 151.7 + or + this = "Melinda" and result = 154.4 + or + this = "Delila" and result = 163.4 + or + this = "Jeremiah" and result = 167.5 + or + this = "Elijah" and result = 184.5 + or + this = "Hester" and result = 152.7 + or + this = "Walter" and result = 159.6 + or + this = "Oliver" and result = 192.4 + or + this = "Hugh" and result = 173.1 + or + this = "Aaron" and result = 176.6 + or + this = "Reuben" and result = 169.9 + or + this = "Eli" and result = 180.4 + or + this = "Amos" and result = 167.4 + or + this = "Augustus" and result = 156.5 + or + this = "Theodore" and result = 176.6 + or + this = "Ira" and result = 54.1 + or + this = "Timothy" and result = 172.2 + or + this = "Cyrus" and result = 157.9 + or + this = "Horace" and result = 169.3 + or + this = "Simon" and result = 157.1 + or + this = "Asa" and result = 149.4 + or + this = "Frank" and result = 167.2 + or + this = "Nelson" and result = 173.0 + or + this = "Leonard" and result = 172.0 + or + this = "Harrison" and result = 126.0 + or + this = "Anthony" and result = 98.4 + or + this = "Louis" and result = 186.8 + or + this = "Milton" and result = 157.8 + or + this = "Noah" and result = 190.5 + or + this = "Cornelius" and result = 183.1 + or + this = "Abdul" and result = 182.0 + or + this = "Warren" and result = 175.0 + or + this = "Harvey" and result = 169.3 + or + this = "Dennis" and result = 160.4 + or + this = "Wesley" and result = 139.8 + or + this = "Sylvester" and result = 188.2 + or + this = "Gilbert" and result = 177.6 + or + this = "Sullivan" and result = 168.3 + or + this = "Edmund" and result = 159.2 + or + this = "Wilson" and result = 167.6 + or + this = "Perry" and result = 189.1 + or + this = "Matthew" and result = 167.2 + or + this = "Simba" and result = 140.1 + or + this = "Nala" and result = 138.0 + or + this = "Rafiki" and result = 139.3 + or + this = "Shenzi" and result = 171.1 + } + + /** Gets the location of the person's home ("north", "south", "east", or "west"). If the person is deceased, there is no result. */ + string getLocation() { + this = "Ronil" and result = "north" + or + this = "Dina" and result = "north" + or + this = "Ravi" and result = "north" + or + this = "Bruce" and result = "south" + or + this = "Jo" and result = "west" + or + this = "Aida" and result = "east" + or + this = "Esme" and result = "east" + or + this = "Charlie" and result = "south" + or + this = "Fred" and result = "west" + or + this = "Meera" and result = "south" + or + this = "Maya" and result = "south" + or + this = "Chad" and result = "south" + or + this = "Tiana" and result = "west" + or + this = "Laura" and result = "south" + or + this = "George" and result = "south" + or + this = "Will" and result = "south" + or + this = "Mary" and result = "south" + or + this = "Almira" and result = "south" + or + this = "Susannah" and result = "north" + or + this = "Rhoda" and result = "north" + or + this = "Cynthia" and result = "north" + or + this = "Eunice" and result = "north" + or + this = "Olive" and result = "west" + or + this = "Virginia" and result = "west" + or + this = "Angeline" and result = "west" + or + this = "Helen" and result = "west" + or + this = "Cornelia" and result = "east" + or + this = "Harriet" and result = "east" + or + this = "Mahala" and result = "east" + or + this = "Abby" and result = "east" + or + this = "Margaret" and result = "east" + or + this = "Deb" and result = "east" + or + this = "Minerva" and result = "south" + or + this = "Severus" and result = "north" + or + this = "Lavina" and result = "east" + or + this = "Adeline" and result = "west" + or + this = "Cath" and result = "east" + or + this = "Elisa" and result = "east" + or + this = "Lucretia" and result = "north" + or + this = "Anne" and result = "north" + or + this = "Eleanor" and result = "south" + or + this = "Joanna" and result = "south" + or + this = "Adam" and result = "east" + or + this = "Agnes" and result = "east" + or + this = "Rosanna" and result = "east" + or + this = "Clara" and result = "east" + or + this = "Melissa" and result = "west" + or + this = "Amy" and result = "west" + or + this = "Isabel" and result = "west" + or + this = "Jemima" and result = "west" + or + this = "Cordelia" and result = "west" + or + this = "Melinda" and result = "west" + or + this = "Delila" and result = "south" + or + this = "Jeremiah" and result = "north" + or + this = "Elijah" and result = "north" + or + this = "Hester" and result = "east" + or + this = "Walter" and result = "east" + or + this = "Oliver" and result = "east" + or + this = "Hugh" and result = "south" + or + this = "Aaron" and result = "south" + or + this = "Reuben" and result = "west" + or + this = "Eli" and result = "west" + or + this = "Amos" and result = "east" + or + this = "Augustus" and result = "south" + or + this = "Theodore" and result = "west" + or + this = "Ira" and result = "south" + or + this = "Timothy" and result = "north" + or + this = "Cyrus" and result = "north" + or + this = "Horace" and result = "east" + or + this = "Simon" and result = "east" + or + this = "Asa" and result = "east" + or + this = "Frank" and result = "west" + or + this = "Nelson" and result = "west" + or + this = "Leonard" and result = "west" + or + this = "Harrison" and result = "north" + or + this = "Anthony" and result = "north" + or + this = "Louis" and result = "north" + or + this = "Milton" and result = "south" + or + this = "Noah" and result = "south" + or + this = "Cornelius" and result = "east" + or + this = "Abdul" and result = "east" + or + this = "Warren" and result = "west" + or + this = "Harvey" and result = "west" + or + this = "Dennis" and result = "west" + or + this = "Wesley" and result = "west" + or + this = "Sylvester" and result = "south" + or + this = "Gilbert" and result = "east" + or + this = "Sullivan" and result = "east" + or + this = "Edmund" and result = "north" + or + this = "Wilson" and result = "north" + or + this = "Perry" and result = "west" + or + this = "Matthew" and result = "east" + or + this = "Simba" and result = "south" + or + this = "Nala" and result = "south" + or + this = "Rafiki" and result = "north" + or + this = "Shenzi" and result = "west" + } + + /** Holds if the person is deceased. */ + predicate isDeceased() { + this = "Ernest" or + this = "Gertrude" or + this = "Oscar" or + this = "Lilian" or + this = "Edwin" or + this = "Raymond" or + this = "Elgar" or + this = "Elmer" or + this = "Herbert" or + this = "Maude" or + this = "Mae" or + this = "Otto" or + this = "Ophelia" or + this = "Parsley" or + this = "Sage" or + this = "Rosemary" or + this = "Thyme" or + this = "Garfunkel" or + this = "King Basil" + } + + /** Gets a parent of the person (alive or deceased). */ + Person getAParent() { + this = "Stephen" and result = "Edmund" + or + this = "Edmund" and result = "Augustus" + or + this = "Augustus" and result = "Stephen" + or + this = "Abby" and result = "Cornelia" + or + this = "Abby" and result = "Amos" + or + this = "Abdul" and result = "Susannah" + or + this = "Adam" and result = "Amos" + or + this = "Adeline" and result = "Melinda" + or + this = "Adeline" and result = "Frank" + or + this = "Agnes" and result = "Abdul" + or + this = "Aida" and result = "Agnes" + or + this = "Almira" and result = "Sylvester" + or + this = "Amos" and result = "Eunice" + or + this = "Amy" and result = "Noah" + or + this = "Amy" and result = "Chad" + or + this = "Angeline" and result = "Reuben" + or + this = "Angeline" and result = "Lucretia" + or + this = "Anne" and result = "Rhoda" + or + this = "Anne" and result = "Louis" + or + this = "Anthony" and result = "Lavina" + or + this = "Anthony" and result = "Asa" + or + this = "Asa" and result = "Cornelia" + or + this = "Cath" and result = "Harriet" + or + this = "Charlie" and result = "Matthew" + or + this = "Clara" and result = "Ernest" + or + this = "Cornelia" and result = "Cynthia" + or + this = "Cornelius" and result = "Eli" + or + this = "Deb" and result = "Margaret" + or + this = "Dennis" and result = "Fred" + or + this = "Eli" and result = "Susannah" + or + this = "Elijah" and result = "Delila" + or + this = "Elisa" and result = "Deb" + or + this = "Elisa" and result = "Horace" + or + this = "Esme" and result = "Margaret" + or + this = "Frank" and result = "Eleanor" + or + this = "Frank" and result = "Cyrus" + or + this = "George" and result = "Maya" + or + this = "George" and result = "Wilson" + or + this = "Gilbert" and result = "Cornelius" + or + this = "Harriet" and result = "Cynthia" + or + this = "Harrison" and result = "Louis" + or + this = "Harvey" and result = "Fred" + or + this = "Helen" and result = "Susannah" + or + this = "Hester" and result = "Edwin" + or + this = "Hugh" and result = "Cyrus" + or + this = "Hugh" and result = "Helen" + or + this = "Ira" and result = "Maya" + or + this = "Ira" and result = "Wilson" + or + this = "Isabel" and result = "Perry" + or + this = "Isabel" and result = "Harvey" + or + this = "Jemima" and result = "Melinda" + or + this = "Jemima" and result = "Frank" + or + this = "Ernest" and result = "Lilian" + or + this = "Ernest" and result = "Oscar" + or + this = "Gertrude" and result = "Ophelia" + or + this = "Gertrude" and result = "Raymond" + or + this = "Lilian" and result = "Elgar" + or + this = "Lilian" and result = "Mae" + or + this = "Raymond" and result = "Elgar" + or + this = "Raymond" and result = "Mae" + or + this = "Elmer" and result = "Ophelia" + or + this = "Elmer" and result = "Raymond" + or + this = "Herbert" and result = "Ophelia" + or + this = "Herbert" and result = "Raymond" + or + this = "Maude" and result = "Ophelia" + or + this = "Maude" and result = "Raymond" + or + this = "Otto" and result = "Elgar" + or + this = "Otto" and result = "Mae" + or + this = "Edwin" and result = "Otto" + or + this = "Parsley" and result = "Simon" + or + this = "Parsley" and result = "Garfunkel" + or + this = "Sage" and result = "Simon" + or + this = "Sage" and result = "Garfunkel" + or + this = "Rosemary" and result = "Simon" + or + this = "Rosemary" and result = "Garfunkel" + or + this = "Thyme" and result = "Simon" + or + this = "Thyme" and result = "Garfunkel" + or + this = "King Basil" and result = "Ophelia" + or + this = "King Basil" and result = "Raymond" + or + this = "Jo" and result = "Theodore" + or + this = "Joanna" and result = "Shenzi" + or + this = "Laura" and result = "Maya" + or + this = "Laura" and result = "Wilson" + or + this = "Lavina" and result = "Mahala" + or + this = "Lavina" and result = "Walter" + or + this = "Leonard" and result = "Cyrus" + or + this = "Leonard" and result = "Helen" + or + this = "Lucretia" and result = "Eleanor" + or + this = "Lucretia" and result = "Cyrus" + or + this = "Mahala" and result = "Eunice" + or + this = "Margaret" and result = "Cynthia" + or + this = "Matthew" and result = "Cyrus" + or + this = "Matthew" and result = "Helen" + or + this = "Maya" and result = "Meera" + or + this = "Melinda" and result = "Rafiki" + or + this = "Melissa" and result = "Mahala" + or + this = "Melissa" and result = "Walter" + or + this = "Nala" and result = "Bruce" + or + this = "Nelson" and result = "Mahala" + or + this = "Nelson" and result = "Walter" + or + this = "Noah" and result = "Eli" + or + this = "Olive" and result = "Reuben" + or + this = "Olive" and result = "Lucretia" + or + this = "Oliver" and result = "Matthew" + or + this = "Perry" and result = "Leonard" + or + this = "Ravi" and result = "Dina" + or + this = "Simba" and result = "Will" + or + this = "Simon" and result = "Margaret" + or + this = "Sullivan" and result = "Cornelius" + or + this = "Sylvester" and result = "Timothy" + or + this = "Theodore" and result = "Susannah" + or + this = "Tiana" and result = "Jo" + or + this = "Virginia" and result = "Helen" + or + this = "Warren" and result = "Shenzi" + or + this = "Wesley" and result = "Warren" + or + this = "Wesley" and result = "Jo" + or + this = "Will" and result = "Eli" + } + + /** Holds if the person is allowed in the region. Initially, all villagers are allowed in every region. */ + predicate isAllowedIn(string region) { + region = "north" or + region = "south" or + region = "east" or + region = "west" + } +} + +/** Returns a parent of the person. */ +Person parentOf(Person p) { result = p.getAParent() } diff --git a/csharp/ql/src/AlertSuppression.ql b/csharp/ql/src/AlertSuppression.ql index 1467731809b..3cb6d759b6e 100644 --- a/csharp/ql/src/AlertSuppression.ql +++ b/csharp/ql/src/AlertSuppression.ql @@ -55,7 +55,7 @@ class SuppressionScope extends @commentline { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql b/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql index 0b4f4fd99b8..a0542a94735 100644 --- a/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql +++ b/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql @@ -123,6 +123,8 @@ class ConstantMatchingCondition extends ConstantCondition { se.getCase(i).getPattern() = this.(DiscardExpr) and i > 0 ) + or + this = any(PositionalPatternExpr ppe).getPattern(_) } override string getMessage() { diff --git a/csharp/ql/src/Bad Practices/Magic Constants/MagicConstants.qll b/csharp/ql/src/Bad Practices/Magic Constants/MagicConstants.qll index d604dfaeefe..b65cdcd1961 100644 --- a/csharp/ql/src/Bad Practices/Magic Constants/MagicConstants.qll +++ b/csharp/ql/src/Bad Practices/Magic Constants/MagicConstants.qll @@ -7,179 +7,30 @@ import semmle.code.csharp.frameworks.System */ private predicate trivialPositiveIntValue(string s) { - s = "0" or - s = "1" or - s = "2" or - s = "3" or - s = "4" or - s = "5" or - s = "6" or - s = "7" or - s = "8" or - s = "9" or - s = "10" or - s = "11" or - s = "12" or - s = "13" or - s = "14" or - s = "15" or - s = "16" or - s = "17" or - s = "18" or - s = "19" or - s = "20" or - s = "16" or - s = "32" or - s = "64" or - s = "128" or - s = "256" or - s = "512" or - s = "1024" or - s = "2048" or - s = "4096" or - s = "16384" or - s = "32768" or - s = "65536" or - s = "1048576" or - s = "2147483648" or - s = "4294967296" or - s = "15" or - s = "31" or - s = "63" or - s = "127" or - s = "255" or - s = "511" or - s = "1023" or - s = "2047" or - s = "4095" or - s = "16383" or - s = "32767" or - s = "65535" or - s = "1048577" or - s = "2147483647" or - s = "4294967295" or - s = "0x00000001" or - s = "0x00000002" or - s = "0x00000004" or - s = "0x00000008" or - s = "0x00000010" or - s = "0x00000020" or - s = "0x00000040" or - s = "0x00000080" or - s = "0x00000100" or - s = "0x00000200" or - s = "0x00000400" or - s = "0x00000800" or - s = "0x00001000" or - s = "0x00002000" or - s = "0x00004000" or - s = "0x00008000" or - s = "0x00010000" or - s = "0x00020000" or - s = "0x00040000" or - s = "0x00080000" or - s = "0x00100000" or - s = "0x00200000" or - s = "0x00400000" or - s = "0x00800000" or - s = "0x01000000" or - s = "0x02000000" or - s = "0x04000000" or - s = "0x08000000" or - s = "0x10000000" or - s = "0x20000000" or - s = "0x40000000" or - s = "0x80000000" or - s = "0x00000001" or - s = "0x00000003" or - s = "0x00000007" or - s = "0x0000000f" or - s = "0x0000001f" or - s = "0x0000003f" or - s = "0x0000007f" or - s = "0x000000ff" or - s = "0x000001ff" or - s = "0x000003ff" or - s = "0x000007ff" or - s = "0x00000fff" or - s = "0x00001fff" or - s = "0x00003fff" or - s = "0x00007fff" or - s = "0x0000ffff" or - s = "0x0001ffff" or - s = "0x0003ffff" or - s = "0x0007ffff" or - s = "0x000fffff" or - s = "0x001fffff" or - s = "0x003fffff" or - s = "0x007fffff" or - s = "0x00ffffff" or - s = "0x01ffffff" or - s = "0x03ffffff" or - s = "0x07ffffff" or - s = "0x0fffffff" or - s = "0x1fffffff" or - s = "0x3fffffff" or - s = "0x7fffffff" or - s = "0xffffffff" or - s = "0x0001" or - s = "0x0002" or - s = "0x0004" or - s = "0x0008" or - s = "0x0010" or - s = "0x0020" or - s = "0x0040" or - s = "0x0080" or - s = "0x0100" or - s = "0x0200" or - s = "0x0400" or - s = "0x0800" or - s = "0x1000" or - s = "0x2000" or - s = "0x4000" or - s = "0x8000" or - s = "0x0001" or - s = "0x0003" or - s = "0x0007" or - s = "0x000f" or - s = "0x001f" or - s = "0x003f" or - s = "0x007f" or - s = "0x00ff" or - s = "0x01ff" or - s = "0x03ff" or - s = "0x07ff" or - s = "0x0fff" or - s = "0x1fff" or - s = "0x3fff" or - s = "0x7fff" or - s = "0xffff" or - s = "0x01" or - s = "0x02" or - s = "0x04" or - s = "0x08" or - s = "0x10" or - s = "0x20" or - s = "0x40" or - s = "0x80" or - s = "0x01" or - s = "0x03" or - s = "0x07" or - s = "0x0f" or - s = "0x1f" or - s = "0x3f" or - s = "0x7f" or - s = "0xff" or - s = "0x00" or - s = "10" or - s = "100" or - s = "1000" or - s = "10000" or - s = "100000" or - s = "1000000" or - s = "10000000" or - s = "100000000" or - s = "1000000000" + s = + [ + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", + "17", "18", "19", "20", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", + "16384", "32768", "65536", "1048576", "2147483648", "4294967296", "15", "31", "63", "127", + "255", "511", "1023", "2047", "4095", "16383", "32767", "65535", "1048577", "2147483647", + "4294967295", "0x00000001", "0x00000002", "0x00000004", "0x00000008", "0x00000010", + "0x00000020", "0x00000040", "0x00000080", "0x00000100", "0x00000200", "0x00000400", + "0x00000800", "0x00001000", "0x00002000", "0x00004000", "0x00008000", "0x00010000", + "0x00020000", "0x00040000", "0x00080000", "0x00100000", "0x00200000", "0x00400000", + "0x00800000", "0x01000000", "0x02000000", "0x04000000", "0x08000000", "0x10000000", + "0x20000000", "0x40000000", "0x80000000", "0x00000001", "0x00000003", "0x00000007", + "0x0000000f", "0x0000001f", "0x0000003f", "0x0000007f", "0x000000ff", "0x000001ff", + "0x000003ff", "0x000007ff", "0x00000fff", "0x00001fff", "0x00003fff", "0x00007fff", + "0x0000ffff", "0x0001ffff", "0x0003ffff", "0x0007ffff", "0x000fffff", "0x001fffff", + "0x003fffff", "0x007fffff", "0x00ffffff", "0x01ffffff", "0x03ffffff", "0x07ffffff", + "0x0fffffff", "0x1fffffff", "0x3fffffff", "0x7fffffff", "0xffffffff", "0x0001", "0x0002", + "0x0004", "0x0008", "0x0010", "0x0020", "0x0040", "0x0080", "0x0100", "0x0200", "0x0400", + "0x0800", "0x1000", "0x2000", "0x4000", "0x8000", "0x0001", "0x0003", "0x0007", "0x000f", + "0x001f", "0x003f", "0x007f", "0x00ff", "0x01ff", "0x03ff", "0x07ff", "0x0fff", "0x1fff", + "0x3fff", "0x7fff", "0xffff", "0x01", "0x02", "0x04", "0x08", "0x10", "0x20", "0x40", "0x80", + "0x01", "0x03", "0x07", "0x0f", "0x1f", "0x3f", "0x7f", "0xff", "0x00", "10", "100", "1000", + "10000", "100000", "1000000", "10000000", "100000000", "1000000000" + ] } private predicate trivialIntValue(string s) { @@ -193,15 +44,7 @@ private predicate intTrivial(Literal lit) { } private predicate powerOfTen(float f) { - f = 10 or - f = 100 or - f = 1000 or - f = 10000 or - f = 100000 or - f = 1000000 or - f = 10000000 or - f = 100000000 or - f = 1000000000 + f = [10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000] } private predicate floatTrivial(Literal lit) { diff --git a/csharp/ql/src/Bad Practices/Naming Conventions/DefaultControlNames.ql b/csharp/ql/src/Bad Practices/Naming Conventions/DefaultControlNames.ql index be381328582..2bf51653d99 100644 --- a/csharp/ql/src/Bad Practices/Naming Conventions/DefaultControlNames.ql +++ b/csharp/ql/src/Bad Practices/Naming Conventions/DefaultControlNames.ql @@ -13,16 +13,11 @@ import csharp predicate controlName(string prefix) { - prefix = "[Ll]abel" or - prefix = "[Bb]utton" or - prefix = "[Pp]anel" or - prefix = "[Rr]adio[Bb]utton" or - prefix = "[Pp]rop" or - prefix = "[Ss]atus[Ss]trip" or - prefix = "[Tt]able[Ll]ayout[Dd]esigner" or - prefix = "[Tt]ext[Bb]ox" or - prefix = "[Tt]ool[Ss]trip" or - prefix = "[Pp]icture[Bb]ox" + prefix = + [ + "[Ll]abel", "[Bb]utton", "[Pp]anel", "[Rr]adio[Bb]utton", "[Pp]rop", "[Ss]atus[Ss]trip", + "[Tt]able[Ll]ayout[Dd]esigner", "[Tt]ext[Bb]ox", "[Tt]ool[Ss]trip", "[Pp]icture[Bb]ox" + ] } predicate usedInHumanWrittenCode(Field f) { diff --git a/csharp/ql/src/Bad Practices/Naming Conventions/VariableNameTooShort.ql b/csharp/ql/src/Bad Practices/Naming Conventions/VariableNameTooShort.ql index f161edda6c4..cb778465df8 100644 --- a/csharp/ql/src/Bad Practices/Naming Conventions/VariableNameTooShort.ql +++ b/csharp/ql/src/Bad Practices/Naming Conventions/VariableNameTooShort.ql @@ -34,16 +34,7 @@ select variable, "Variable name '" + name + "' is too short." // Adjustable: acceptable short names // predicate allowedName(string name) { - name = "url" or - name = "cmd" or - name = "UK" or - name = "uri" or - name = "top" or - name = "row" or - name = "pin" or - name = "log" or - name = "key" or - name = "_" + name = ["url", "cmd", "UK", "uri", "top", "row", "pin", "log", "key", "_"] } // diff --git a/csharp/ql/src/Dead Code/DeadStoreOfLocal.ql b/csharp/ql/src/Dead Code/DeadStoreOfLocal.ql index 1064a76c2e0..4612091743f 100644 --- a/csharp/ql/src/Dead Code/DeadStoreOfLocal.ql +++ b/csharp/ql/src/Dead Code/DeadStoreOfLocal.ql @@ -37,21 +37,11 @@ Expr getADelegateExpr(Callable c) { */ predicate nonEscapingCall(Call c) { exists(string name | c.getTarget().hasName(name) | - name = "ForEach" or - name = "Count" or - name = "Any" or - name = "All" or - name = "Average" or - name = "Aggregate" or - name = "First" or - name = "Last" or - name = "FirstOrDefault" or - name = "LastOrDefault" or - name = "LongCount" or - name = "Max" or - name = "Single" or - name = "SingleOrDefault" or - name = "Sum" + name = + [ + "ForEach", "Count", "Any", "All", "Average", "Aggregate", "First", "Last", "FirstOrDefault", + "LastOrDefault", "LongCount", "Max", "Single", "SingleOrDefault", "Sum" + ] ) } @@ -72,7 +62,8 @@ predicate mayEscape(LocalVariable v) { class RelevantDefinition extends AssignableDefinition { RelevantDefinition() { - this instanceof AssignableDefinitions::AssignmentDefinition + this.(AssignableDefinitions::AssignmentDefinition).getAssignment() = + any(Assignment a | not a = any(UsingDeclStmt uds).getAVariableDeclExpr()) or this instanceof AssignableDefinitions::MutationDefinition or @@ -115,12 +106,7 @@ class RelevantDefinition extends AssignableDefinition { private predicate isDefaultLikeInitializer() { this.isInitializer() and exists(Expr e | e = this.getSource().stripCasts() | - exists(string val | val = e.getValue() | - val = "0" or - val = "-1" or - val = "" or - val = "false" - ) + e.getValue() = ["0", "-1", "", "false"] or e instanceof NullLiteral or diff --git a/csharp/ql/src/Diagnostics/DiagnosticExtractionErrors.ql b/csharp/ql/src/Diagnostics/DiagnosticExtractionErrors.ql index 23943d8491f..e9e2a42bfa8 100644 --- a/csharp/ql/src/Diagnostics/DiagnosticExtractionErrors.ql +++ b/csharp/ql/src/Diagnostics/DiagnosticExtractionErrors.ql @@ -21,8 +21,8 @@ abstract private class DiagnosticError extends TDiagnosticError { abstract Location getLocation(); string getLocationMessage() { - if getLocation().getFile().fromSource() - then result = " in " + getLocation().getFile() + if this.getLocation().getFile().fromSource() + then result = " in " + this.getLocation().getFile() else result = "" } } diff --git a/csharp/ql/src/Stubs/Stubs.qll b/csharp/ql/src/Stubs/Stubs.qll index 6f92fb12f55..e0c009b8783 100644 --- a/csharp/ql/src/Stubs/Stubs.qll +++ b/csharp/ql/src/Stubs/Stubs.qll @@ -128,6 +128,7 @@ abstract private class GeneratedType extends Type, GeneratedElement { /** Gets the entire C# stub code for this type. */ pragma[nomagic] final string getStub(Assembly assembly) { + this.isInAssembly(assembly) and if this.isDuplicate(assembly) then result = @@ -161,7 +162,7 @@ abstract private class GeneratedType extends Type, GeneratedElement { if this instanceof Enum then result = "" else - if exists(getAnInterestingBaseType()) + if exists(this.getAnInterestingBaseType()) then result = " : " + @@ -220,15 +221,15 @@ abstract private class GeneratedType extends Type, GeneratedElement { } final Type getAGeneratedType() { - result = getAnInterestingBaseType() + result = this.getAnInterestingBaseType() or - result = getAGeneratedMember().(Callable).getReturnType() + result = this.getAGeneratedMember().(Callable).getReturnType() or - result = getAGeneratedMember().(Callable).getAParameter().getType() + result = this.getAGeneratedMember().(Callable).getAParameter().getType() or - result = getAGeneratedMember().(Property).getType() + result = this.getAGeneratedMember().(Property).getType() or - result = getAGeneratedMember().(Field).getType() + result = this.getAGeneratedMember().(Field).getType() } } @@ -331,7 +332,8 @@ private class GeneratedNamespace extends Namespace, GeneratedElement { final string getStubs(Assembly assembly) { result = - getPreamble() + getTypeStubs(assembly) + getSubNamespaceStubs(assembly) + getPostAmble() + this.getPreamble() + this.getTypeStubs(assembly) + this.getSubNamespaceStubs(assembly) + + this.getPostAmble() } /** Gets the `n`th generated child namespace, indexed from 0. */ @@ -358,7 +360,7 @@ private class GeneratedNamespace extends Namespace, GeneratedElement { this.isInAssembly(assembly) and result = concat(GeneratedNamespace child, int i | - child = getChildNamespace(i) and child.isInAssembly(assembly) + child = this.getChildNamespace(i) and child.isInAssembly(assembly) | child.getStubs(assembly) order by i ) @@ -612,83 +614,18 @@ private string stubImplementation(Virtualizable c) { } private predicate isKeyword(string s) { - s = "abstract" or - s = "as" or - s = "base" or - s = "bool" or - s = "break" or - s = "byte" or - s = "case" or - s = "catch" or - s = "char" or - s = "checked" or - s = "class" or - s = "const" or - s = "continue" or - s = "decimal" or - s = "default" or - s = "delegate" or - s = "do" or - s = "double" or - s = "else" or - s = "enum" or - s = "event" or - s = "explicit" or - s = "extern" or - s = "false" or - s = "finally" or - s = "fixed" or - s = "float" or - s = "for" or - s = "foreach" or - s = "goto" or - s = "if" or - s = "implicit" or - s = "in" or - s = "int" or - s = "interface" or - s = "internal" or - s = "is" or - s = "lock" or - s = "long" or - s = "namespace" or - s = "new" or - s = "null" or - s = "object" or - s = "operator" or - s = "out" or - s = "override" or - s = "params" or - s = "private" or - s = "protected" or - s = "public" or - s = "readonly" or - s = "ref" or - s = "return" or - s = "sbyte" or - s = "sealed" or - s = "short" or - s = "sizeof" or - s = "stackalloc" or - s = "static" or - s = "string" or - s = "struct" or - s = "switch" or - s = "this" or - s = "throw" or - s = "true" or - s = "try" or - s = "typeof" or - s = "uint" or - s = "ulong" or - s = "unchecked" or - s = "unsafe" or - s = "ushort" or - s = "using" or - s = "virtual" or - s = "void" or - s = "volatile" or - s = "while" + s = + [ + "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", + "class", "const", "continue", "decimal", "default", "delegate", "do", "double", "else", + "enum", "event", "explicit", "extern", "false", "finally", "fixed", "float", "for", "foreach", + "goto", "if", "implicit", "in", "int", "interface", "internal", "is", "lock", "long", + "namespace", "new", "null", "object", "operator", "out", "override", "params", "private", + "protected", "public", "readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof", + "stackalloc", "static", "string", "struct", "switch", "this", "throw", "true", "try", + "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using", "virtual", "void", + "volatile", "while" + ] } bindingset[s] diff --git a/csharp/ql/src/definitions.qll b/csharp/ql/src/definitions.qll index ade40435e18..559bc0d3908 100644 --- a/csharp/ql/src/definitions.qll +++ b/csharp/ql/src/definitions.qll @@ -13,7 +13,7 @@ abstract class Use extends @type_mention_parent { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll b/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll index 4b86f9a7cec..bb8630a5e0c 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll @@ -24,7 +24,7 @@ class IRBlockBase extends TIRBlock { final string toString() { result = getFirstInstruction(this).toString() } /** Gets the source location of the first non-`Phi` instruction in this block. */ - final Language::Location getLocation() { result = getFirstInstruction().getLocation() } + final Language::Location getLocation() { result = this.getFirstInstruction().getLocation() } /** * INTERNAL: Do not use. @@ -39,7 +39,7 @@ class IRBlockBase extends TIRBlock { ) and this = rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 | - funcBlock.getEnclosingFunction() = getEnclosingFunction() and + funcBlock.getEnclosingFunction() = this.getEnclosingFunction() and funcBlock.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and // Ensure that the block containing `EnterFunction` always comes first. if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction @@ -59,15 +59,15 @@ class IRBlockBase extends TIRBlock { * Get the `Phi` instructions that appear at the start of this block. */ final PhiInstruction getAPhiInstruction() { - Construction::getPhiInstructionBlockStart(result) = getFirstInstruction() + Construction::getPhiInstructionBlockStart(result) = this.getFirstInstruction() } /** * Gets an instruction in this block. This includes `Phi` instructions. */ final Instruction getAnInstruction() { - result = getInstruction(_) or - result = getAPhiInstruction() + result = this.getInstruction(_) or + result = this.getAPhiInstruction() } /** @@ -78,7 +78,9 @@ class IRBlockBase extends TIRBlock { /** * Gets the last instruction in this block. */ - final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) } + final Instruction getLastInstruction() { + result = this.getInstruction(this.getInstructionCount() - 1) + } /** * Gets the number of non-`Phi` instructions in this block. @@ -149,7 +151,7 @@ class IRBlock extends IRBlockBase { * Block `A` dominates block `B` if any control flow path from the entry block of the function to * block `B` must pass through block `A`. A block always dominates itself. */ - final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } + final predicate dominates(IRBlock block) { this.strictlyDominates(block) or this = block } /** * Gets a block on the dominance frontier of this block. @@ -159,8 +161,8 @@ class IRBlock extends IRBlockBase { */ pragma[noinline] final IRBlock dominanceFrontier() { - dominates(result.getAPredecessor()) and - not strictlyDominates(result) + this.dominates(result.getAPredecessor()) and + not this.strictlyDominates(result) } /** @@ -189,7 +191,7 @@ class IRBlock extends IRBlockBase { * Block `A` post-dominates block `B` if any control flow path from `B` to the exit block of the * function must pass through block `A`. A block always post-dominates itself. */ - final predicate postDominates(IRBlock block) { strictlyPostDominates(block) or this = block } + final predicate postDominates(IRBlock block) { this.strictlyPostDominates(block) or this = block } /** * Gets a block on the post-dominance frontier of this block. @@ -199,16 +201,16 @@ class IRBlock extends IRBlockBase { */ pragma[noinline] final IRBlock postPominanceFrontier() { - postDominates(result.getASuccessor()) and - not strictlyPostDominates(result) + this.postDominates(result.getASuccessor()) and + not this.strictlyPostDominates(result) } /** * Holds if this block is reachable from the entry block of its function. */ final predicate isReachableFromFunctionEntry() { - this = getEnclosingIRFunction().getEntryBlock() or - getAPredecessor().isReachableFromFunctionEntry() + this = this.getEnclosingIRFunction().getEntryBlock() or + this.getAPredecessor().isReachableFromFunctionEntry() } } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll index 2fb3edad602..88a973fc5a8 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll @@ -41,7 +41,7 @@ class Instruction extends Construction::TStageInstruction { } /** Gets a textual representation of this element. */ - final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } + final string toString() { result = this.getOpcode().toString() + ": " + this.getAST().toString() } /** * Gets a string showing the result, opcode, and operands of the instruction, equivalent to what @@ -50,7 +50,8 @@ class Instruction extends Construction::TStageInstruction { * `mu0_28(int) = Store r0_26, r0_27` */ final string getDumpString() { - result = getResultString() + " = " + getOperationString() + " " + getOperandsString() + result = + this.getResultString() + " = " + this.getOperationString() + " " + this.getOperandsString() } private predicate shouldGenerateDumpStrings() { @@ -66,10 +67,13 @@ class Instruction extends Construction::TStageInstruction { * VariableAddress[x] */ final string getOperationString() { - shouldGenerateDumpStrings() and - if exists(getImmediateString()) - then result = getOperationPrefix() + getOpcode().toString() + "[" + getImmediateString() + "]" - else result = getOperationPrefix() + getOpcode().toString() + this.shouldGenerateDumpStrings() and + if exists(this.getImmediateString()) + then + result = + this.getOperationPrefix() + this.getOpcode().toString() + "[" + this.getImmediateString() + + "]" + else result = this.getOperationPrefix() + this.getOpcode().toString() } /** @@ -78,17 +82,17 @@ class Instruction extends Construction::TStageInstruction { string getImmediateString() { none() } private string getOperationPrefix() { - shouldGenerateDumpStrings() and + this.shouldGenerateDumpStrings() and if this instanceof SideEffectInstruction then result = "^" else result = "" } private string getResultPrefix() { - shouldGenerateDumpStrings() and - if getResultIRType() instanceof IRVoidType + this.shouldGenerateDumpStrings() and + if this.getResultIRType() instanceof IRVoidType then result = "v" else - if hasMemoryResult() - then if isResultModeled() then result = "m" else result = "mu" + if this.hasMemoryResult() + then if this.isResultModeled() then result = "m" else result = "mu" else result = "r" } @@ -97,7 +101,7 @@ class Instruction extends Construction::TStageInstruction { * used by debugging and printing code only. */ int getDisplayIndexInBlock() { - shouldGenerateDumpStrings() and + this.shouldGenerateDumpStrings() and exists(IRBlock block | this = block.getInstruction(result) or @@ -111,12 +115,12 @@ class Instruction extends Construction::TStageInstruction { } private int getLineRank() { - shouldGenerateDumpStrings() and + this.shouldGenerateDumpStrings() and this = rank[result](Instruction instr | instr = - getAnInstructionAtLine(getEnclosingIRFunction(), getLocation().getFile(), - getLocation().getStartLine()) + getAnInstructionAtLine(this.getEnclosingIRFunction(), this.getLocation().getFile(), + this.getLocation().getStartLine()) | instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock() ) @@ -130,8 +134,9 @@ class Instruction extends Construction::TStageInstruction { * Example: `r1_1` */ string getResultId() { - shouldGenerateDumpStrings() and - result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank() + this.shouldGenerateDumpStrings() and + result = + this.getResultPrefix() + this.getAST().getLocation().getStartLine() + "_" + this.getLineRank() } /** @@ -142,8 +147,8 @@ class Instruction extends Construction::TStageInstruction { * Example: `r1_1(int*)` */ final string getResultString() { - shouldGenerateDumpStrings() and - result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")" + this.shouldGenerateDumpStrings() and + result = this.getResultId() + "(" + this.getResultLanguageType().getDumpString() + ")" } /** @@ -153,10 +158,10 @@ class Instruction extends Construction::TStageInstruction { * Example: `func:r3_4, this:r3_5` */ string getOperandsString() { - shouldGenerateDumpStrings() and + this.shouldGenerateDumpStrings() and result = concat(Operand operand | - operand = getAnOperand() + operand = this.getAnOperand() | operand.getDumpString(), ", " order by operand.getDumpSortOrder() ) @@ -190,7 +195,7 @@ class Instruction extends Construction::TStageInstruction { * Gets the function that contains this instruction. */ final Language::Function getEnclosingFunction() { - result = getEnclosingIRFunction().getFunction() + result = this.getEnclosingIRFunction().getFunction() } /** @@ -208,7 +213,7 @@ class Instruction extends Construction::TStageInstruction { /** * Gets the location of the source code for this instruction. */ - final Language::Location getLocation() { result = getAST().getLocation() } + final Language::Location getLocation() { result = this.getAST().getLocation() } /** * Gets the `Expr` whose result is computed by this instruction, if any. The `Expr` may be a @@ -243,7 +248,7 @@ class Instruction extends Construction::TStageInstruction { * a result, its result type will be `IRVoidType`. */ cached - final IRType getResultIRType() { result = getResultLanguageType().getIRType() } + final IRType getResultIRType() { result = this.getResultLanguageType().getIRType() } /** * Gets the type of the result produced by this instruction. If the @@ -254,7 +259,7 @@ class Instruction extends Construction::TStageInstruction { */ final Language::Type getResultType() { exists(Language::LanguageType resultType | - resultType = getResultLanguageType() and + resultType = this.getResultLanguageType() and ( resultType.hasUnspecifiedType(result, _) or @@ -283,7 +288,7 @@ class Instruction extends Construction::TStageInstruction { * result of the `Load` instruction is a prvalue of type `int`, representing * the integer value loaded from variable `x`. */ - final predicate isGLValue() { getResultLanguageType().hasType(_, true) } + final predicate isGLValue() { this.getResultLanguageType().hasType(_, true) } /** * Gets the size of the result produced by this instruction, in bytes. If the @@ -292,7 +297,7 @@ class Instruction extends Construction::TStageInstruction { * If `this.isGLValue()` holds for this instruction, the value of * `getResultSize()` will always be the size of a pointer. */ - final int getResultSize() { result = getResultLanguageType().getByteSize() } + final int getResultSize() { result = this.getResultLanguageType().getByteSize() } /** * Gets the opcode that specifies the operation performed by this instruction. @@ -314,14 +319,16 @@ class Instruction extends Construction::TStageInstruction { /** * Holds if this instruction produces a memory result. */ - final predicate hasMemoryResult() { exists(getResultMemoryAccess()) } + final predicate hasMemoryResult() { exists(this.getResultMemoryAccess()) } /** * Gets the kind of memory access performed by this instruction's result. * Holds only for instructions with a memory result. */ pragma[inline] - final MemoryAccessKind getResultMemoryAccess() { result = getOpcode().getWriteMemoryAccess() } + final MemoryAccessKind getResultMemoryAccess() { + result = this.getOpcode().getWriteMemoryAccess() + } /** * Holds if the memory access performed by this instruction's result will not always write to @@ -332,7 +339,7 @@ class Instruction extends Construction::TStageInstruction { * (for example, the global side effects of a function call). */ pragma[inline] - final predicate hasResultMayMemoryAccess() { getOpcode().hasMayWriteMemoryAccess() } + final predicate hasResultMayMemoryAccess() { this.getOpcode().hasMayWriteMemoryAccess() } /** * Gets the operand that holds the memory address to which this instruction stores its @@ -340,7 +347,7 @@ class Instruction extends Construction::TStageInstruction { * is `r1`. */ final AddressOperand getResultAddressOperand() { - getResultMemoryAccess().usesAddressOperand() and + this.getResultMemoryAccess().usesAddressOperand() and result.getUse() = this } @@ -349,7 +356,7 @@ class Instruction extends Construction::TStageInstruction { * result, if any. For example, in `m3 = Store r1, r2`, the result of `getResultAddressOperand()` * is the instruction that defines `r1`. */ - final Instruction getResultAddress() { result = getResultAddressOperand().getDef() } + final Instruction getResultAddress() { result = this.getResultAddressOperand().getDef() } /** * Holds if the result of this instruction is precisely modeled in SSA. Always @@ -368,7 +375,7 @@ class Instruction extends Construction::TStageInstruction { */ final predicate isResultModeled() { // Register results are always in SSA form. - not hasMemoryResult() or + not this.hasMemoryResult() or Construction::hasModeledMemoryResult(this) } @@ -412,7 +419,7 @@ class Instruction extends Construction::TStageInstruction { /** * Gets all direct successors of this instruction. */ - final Instruction getASuccessor() { result = getSuccessor(_) } + final Instruction getASuccessor() { result = this.getSuccessor(_) } /** * Gets a predecessor of this instruction such that the predecessor reaches @@ -423,7 +430,7 @@ class Instruction extends Construction::TStageInstruction { /** * Gets all direct predecessors of this instruction. */ - final Instruction getAPredecessor() { result = getPredecessor(_) } + final Instruction getAPredecessor() { result = this.getPredecessor(_) } } /** @@ -543,7 +550,7 @@ class IndexedInstruction extends Instruction { * at this instruction. This instruction has no predecessors. */ class EnterFunctionInstruction extends Instruction { - EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction } + EnterFunctionInstruction() { this.getOpcode() instanceof Opcode::EnterFunction } } /** @@ -554,7 +561,7 @@ class EnterFunctionInstruction extends Instruction { * struct, or union, see `FieldAddressInstruction`. */ class VariableAddressInstruction extends VariableInstruction { - VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } + VariableAddressInstruction() { this.getOpcode() instanceof Opcode::VariableAddress } } /** @@ -566,7 +573,7 @@ class VariableAddressInstruction extends VariableInstruction { * The result has an `IRFunctionAddress` type. */ class FunctionAddressInstruction extends FunctionInstruction { - FunctionAddressInstruction() { getOpcode() instanceof Opcode::FunctionAddress } + FunctionAddressInstruction() { this.getOpcode() instanceof Opcode::FunctionAddress } } /** @@ -577,7 +584,7 @@ class FunctionAddressInstruction extends FunctionInstruction { * initializes that parameter. */ class InitializeParameterInstruction extends VariableInstruction { - InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter } + InitializeParameterInstruction() { this.getOpcode() instanceof Opcode::InitializeParameter } /** * Gets the parameter initialized by this instruction. @@ -603,7 +610,7 @@ class InitializeParameterInstruction extends VariableInstruction { * initialized elsewhere, would not otherwise have a definition in this function. */ class InitializeNonLocalInstruction extends Instruction { - InitializeNonLocalInstruction() { getOpcode() instanceof Opcode::InitializeNonLocal } + InitializeNonLocalInstruction() { this.getOpcode() instanceof Opcode::InitializeNonLocal } } /** @@ -611,7 +618,7 @@ class InitializeNonLocalInstruction extends Instruction { * with the value of that memory on entry to the function. */ class InitializeIndirectionInstruction extends VariableInstruction { - InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection } + InitializeIndirectionInstruction() { this.getOpcode() instanceof Opcode::InitializeIndirection } /** * Gets the parameter initialized by this instruction. @@ -635,24 +642,24 @@ class InitializeIndirectionInstruction extends VariableInstruction { * An instruction that initializes the `this` pointer parameter of the enclosing function. */ class InitializeThisInstruction extends Instruction { - InitializeThisInstruction() { getOpcode() instanceof Opcode::InitializeThis } + InitializeThisInstruction() { this.getOpcode() instanceof Opcode::InitializeThis } } /** * An instruction that computes the address of a non-static field of an object. */ class FieldAddressInstruction extends FieldInstruction { - FieldAddressInstruction() { getOpcode() instanceof Opcode::FieldAddress } + FieldAddressInstruction() { this.getOpcode() instanceof Opcode::FieldAddress } /** * Gets the operand that provides the address of the object containing the field. */ - final UnaryOperand getObjectAddressOperand() { result = getAnOperand() } + final UnaryOperand getObjectAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the object containing the field. */ - final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } + final Instruction getObjectAddress() { result = this.getObjectAddressOperand().getDef() } } /** @@ -661,17 +668,19 @@ class FieldAddressInstruction extends FieldInstruction { * This instruction is used for element access to C# arrays. */ class ElementsAddressInstruction extends UnaryInstruction { - ElementsAddressInstruction() { getOpcode() instanceof Opcode::ElementsAddress } + ElementsAddressInstruction() { this.getOpcode() instanceof Opcode::ElementsAddress } /** * Gets the operand that provides the address of the array object. */ - final UnaryOperand getArrayObjectAddressOperand() { result = getAnOperand() } + final UnaryOperand getArrayObjectAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the array object. */ - final Instruction getArrayObjectAddress() { result = getArrayObjectAddressOperand().getDef() } + final Instruction getArrayObjectAddress() { + result = this.getArrayObjectAddressOperand().getDef() + } } /** @@ -685,7 +694,7 @@ class ElementsAddressInstruction extends UnaryInstruction { * taken may want to ignore any function that contains an `ErrorInstruction`. */ class ErrorInstruction extends Instruction { - ErrorInstruction() { getOpcode() instanceof Opcode::Error } + ErrorInstruction() { this.getOpcode() instanceof Opcode::Error } } /** @@ -695,7 +704,7 @@ class ErrorInstruction extends Instruction { * an initializer, or whose initializer only partially initializes the variable. */ class UninitializedInstruction extends VariableInstruction { - UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized } + UninitializedInstruction() { this.getOpcode() instanceof Opcode::Uninitialized } /** * Gets the variable that is uninitialized. @@ -710,7 +719,7 @@ class UninitializedInstruction extends VariableInstruction { * least one instruction, even when the AST has no semantic effect. */ class NoOpInstruction extends Instruction { - NoOpInstruction() { getOpcode() instanceof Opcode::NoOp } + NoOpInstruction() { this.getOpcode() instanceof Opcode::NoOp } } /** @@ -732,32 +741,32 @@ class NoOpInstruction extends Instruction { * `void`-returning function. */ class ReturnInstruction extends Instruction { - ReturnInstruction() { getOpcode() instanceof ReturnOpcode } + ReturnInstruction() { this.getOpcode() instanceof ReturnOpcode } } /** * An instruction that returns control to the caller of the function, without returning a value. */ class ReturnVoidInstruction extends ReturnInstruction { - ReturnVoidInstruction() { getOpcode() instanceof Opcode::ReturnVoid } + ReturnVoidInstruction() { this.getOpcode() instanceof Opcode::ReturnVoid } } /** * An instruction that returns control to the caller of the function, including a return value. */ class ReturnValueInstruction extends ReturnInstruction { - ReturnValueInstruction() { getOpcode() instanceof Opcode::ReturnValue } + ReturnValueInstruction() { this.getOpcode() instanceof Opcode::ReturnValue } /** * Gets the operand that provides the value being returned by the function. */ - final LoadOperand getReturnValueOperand() { result = getAnOperand() } + final LoadOperand getReturnValueOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the value being returned by the function, if an * exact definition is available. */ - final Instruction getReturnValue() { result = getReturnValueOperand().getDef() } + final Instruction getReturnValue() { result = this.getReturnValueOperand().getDef() } } /** @@ -770,28 +779,28 @@ class ReturnValueInstruction extends ReturnInstruction { * that the caller initialized the memory pointed to by the parameter before the call. */ class ReturnIndirectionInstruction extends VariableInstruction { - ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } + ReturnIndirectionInstruction() { this.getOpcode() instanceof Opcode::ReturnIndirection } /** * Gets the operand that provides the value of the pointed-to memory. */ - final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + final SideEffectOperand getSideEffectOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the value of the pointed-to memory, if an exact * definition is available. */ - final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + final Instruction getSideEffect() { result = this.getSideEffectOperand().getDef() } /** * Gets the operand that provides the address of the pointed-to memory. */ - final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + final AddressOperand getSourceAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the pointed-to memory. */ - final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } + final Instruction getSourceAddress() { result = this.getSourceAddressOperand().getDef() } /** * Gets the parameter for which this instruction reads the final pointed-to value within the @@ -821,12 +830,12 @@ class ReturnIndirectionInstruction extends VariableInstruction { * * There are several different copy instructions, depending on the source and destination of the * copy operation: - * - `CopyInstruction` - Copies a register operand to a register result. + * - `CopyValueInstruction` - Copies a register operand to a register result. * - `LoadInstruction` - Copies a memory operand to a register result. * - `StoreInstruction` - Copies a register operand to a memory result. */ class CopyInstruction extends Instruction { - CopyInstruction() { getOpcode() instanceof CopyOpcode } + CopyInstruction() { this.getOpcode() instanceof CopyOpcode } /** * Gets the operand that provides the input value of the copy. @@ -837,16 +846,16 @@ class CopyInstruction extends Instruction { * Gets the instruction whose result provides the input value of the copy, if an exact definition * is available. */ - final Instruction getSourceValue() { result = getSourceValueOperand().getDef() } + final Instruction getSourceValue() { result = this.getSourceValueOperand().getDef() } } /** * An instruction that returns a register result containing a copy of its register operand. */ class CopyValueInstruction extends CopyInstruction, UnaryInstruction { - CopyValueInstruction() { getOpcode() instanceof Opcode::CopyValue } + CopyValueInstruction() { this.getOpcode() instanceof Opcode::CopyValue } - final override UnaryOperand getSourceValueOperand() { result = getAnOperand() } + final override UnaryOperand getSourceValueOperand() { result = this.getAnOperand() } } /** @@ -863,47 +872,49 @@ private string getAddressOperandDescription(AddressOperand operand) { * An instruction that returns a register result containing a copy of its memory operand. */ class LoadInstruction extends CopyInstruction { - LoadInstruction() { getOpcode() instanceof Opcode::Load } + LoadInstruction() { this.getOpcode() instanceof Opcode::Load } final override string getImmediateString() { - result = getAddressOperandDescription(getSourceAddressOperand()) + result = getAddressOperandDescription(this.getSourceAddressOperand()) } /** * Gets the operand that provides the address of the value being loaded. */ - final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + final AddressOperand getSourceAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the value being loaded. */ - final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } + final Instruction getSourceAddress() { result = this.getSourceAddressOperand().getDef() } - final override LoadOperand getSourceValueOperand() { result = getAnOperand() } + final override LoadOperand getSourceValueOperand() { result = this.getAnOperand() } } /** * An instruction that returns a memory result containing a copy of its register operand. */ class StoreInstruction extends CopyInstruction { - StoreInstruction() { getOpcode() instanceof Opcode::Store } + StoreInstruction() { this.getOpcode() instanceof Opcode::Store } final override string getImmediateString() { - result = getAddressOperandDescription(getDestinationAddressOperand()) + result = getAddressOperandDescription(this.getDestinationAddressOperand()) } /** * Gets the operand that provides the address of the location to which the value will be stored. */ - final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + final AddressOperand getDestinationAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the location to which the value will * be stored, if an exact definition is available. */ - final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } + final Instruction getDestinationAddress() { + result = this.getDestinationAddressOperand().getDef() + } - final override StoreValueOperand getSourceValueOperand() { result = getAnOperand() } + final override StoreValueOperand getSourceValueOperand() { result = this.getAnOperand() } } /** @@ -911,27 +922,27 @@ class StoreInstruction extends CopyInstruction { * operand. */ class ConditionalBranchInstruction extends Instruction { - ConditionalBranchInstruction() { getOpcode() instanceof Opcode::ConditionalBranch } + ConditionalBranchInstruction() { this.getOpcode() instanceof Opcode::ConditionalBranch } /** * Gets the operand that provides the Boolean condition controlling the branch. */ - final ConditionOperand getConditionOperand() { result = getAnOperand() } + final ConditionOperand getConditionOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the Boolean condition controlling the branch. */ - final Instruction getCondition() { result = getConditionOperand().getDef() } + final Instruction getCondition() { result = this.getConditionOperand().getDef() } /** * Gets the instruction to which control will flow if the condition is true. */ - final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) } + final Instruction getTrueSuccessor() { result = this.getSuccessor(EdgeKind::trueEdge()) } /** * Gets the instruction to which control will flow if the condition is false. */ - final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) } + final Instruction getFalseSuccessor() { result = this.getSuccessor(EdgeKind::falseEdge()) } } /** @@ -943,14 +954,14 @@ class ConditionalBranchInstruction extends Instruction { * successors. */ class ExitFunctionInstruction extends Instruction { - ExitFunctionInstruction() { getOpcode() instanceof Opcode::ExitFunction } + ExitFunctionInstruction() { this.getOpcode() instanceof Opcode::ExitFunction } } /** * An instruction whose result is a constant value. */ class ConstantInstruction extends ConstantValueInstruction { - ConstantInstruction() { getOpcode() instanceof Opcode::Constant } + ConstantInstruction() { this.getOpcode() instanceof Opcode::Constant } } /** @@ -959,7 +970,7 @@ class ConstantInstruction extends ConstantValueInstruction { class IntegerConstantInstruction extends ConstantInstruction { IntegerConstantInstruction() { exists(IRType resultType | - resultType = getResultIRType() and + resultType = this.getResultIRType() and (resultType instanceof IRIntegerType or resultType instanceof IRBooleanType) ) } @@ -969,7 +980,7 @@ class IntegerConstantInstruction extends ConstantInstruction { * An instruction whose result is a constant value of floating-point type. */ class FloatConstantInstruction extends ConstantInstruction { - FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType } + FloatConstantInstruction() { this.getResultIRType() instanceof IRFloatingPointType } } /** @@ -978,7 +989,9 @@ class FloatConstantInstruction extends ConstantInstruction { class StringConstantInstruction extends VariableInstruction { override IRStringLiteral var; - final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) } + final override string getImmediateString() { + result = Language::getStringLiteralText(this.getValue()) + } /** * Gets the string literal whose address is returned by this instruction. @@ -990,37 +1003,37 @@ class StringConstantInstruction extends VariableInstruction { * An instruction whose result is computed from two operands. */ class BinaryInstruction extends Instruction { - BinaryInstruction() { getOpcode() instanceof BinaryOpcode } + BinaryInstruction() { this.getOpcode() instanceof BinaryOpcode } /** * Gets the left operand of this binary instruction. */ - final LeftOperand getLeftOperand() { result = getAnOperand() } + final LeftOperand getLeftOperand() { result = this.getAnOperand() } /** * Gets the right operand of this binary instruction. */ - final RightOperand getRightOperand() { result = getAnOperand() } + final RightOperand getRightOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the value of the left operand of this binary * instruction. */ - final Instruction getLeft() { result = getLeftOperand().getDef() } + final Instruction getLeft() { result = this.getLeftOperand().getDef() } /** * Gets the instruction whose result provides the value of the right operand of this binary * instruction. */ - final Instruction getRight() { result = getRightOperand().getDef() } + final Instruction getRight() { result = this.getRightOperand().getDef() } /** * Holds if this instruction's operands are `op1` and `op2`, in either order. */ final predicate hasOperands(Operand op1, Operand op2) { - op1 = getLeftOperand() and op2 = getRightOperand() + op1 = this.getLeftOperand() and op2 = this.getRightOperand() or - op1 = getRightOperand() and op2 = getLeftOperand() + op1 = this.getRightOperand() and op2 = this.getLeftOperand() } } @@ -1028,7 +1041,7 @@ class BinaryInstruction extends Instruction { * An instruction that computes the result of an arithmetic operation. */ class ArithmeticInstruction extends Instruction { - ArithmeticInstruction() { getOpcode() instanceof ArithmeticOpcode } + ArithmeticInstruction() { this.getOpcode() instanceof ArithmeticOpcode } } /** @@ -1050,7 +1063,7 @@ class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction * performed according to IEEE-754. */ class AddInstruction extends BinaryArithmeticInstruction { - AddInstruction() { getOpcode() instanceof Opcode::Add } + AddInstruction() { this.getOpcode() instanceof Opcode::Add } } /** @@ -1061,7 +1074,7 @@ class AddInstruction extends BinaryArithmeticInstruction { * according to IEEE-754. */ class SubInstruction extends BinaryArithmeticInstruction { - SubInstruction() { getOpcode() instanceof Opcode::Sub } + SubInstruction() { this.getOpcode() instanceof Opcode::Sub } } /** @@ -1072,7 +1085,7 @@ class SubInstruction extends BinaryArithmeticInstruction { * performed according to IEEE-754. */ class MulInstruction extends BinaryArithmeticInstruction { - MulInstruction() { getOpcode() instanceof Opcode::Mul } + MulInstruction() { this.getOpcode() instanceof Opcode::Mul } } /** @@ -1083,7 +1096,7 @@ class MulInstruction extends BinaryArithmeticInstruction { * to IEEE-754. */ class DivInstruction extends BinaryArithmeticInstruction { - DivInstruction() { getOpcode() instanceof Opcode::Div } + DivInstruction() { this.getOpcode() instanceof Opcode::Div } } /** @@ -1093,7 +1106,7 @@ class DivInstruction extends BinaryArithmeticInstruction { * division by zero or integer overflow is undefined. */ class RemInstruction extends BinaryArithmeticInstruction { - RemInstruction() { getOpcode() instanceof Opcode::Rem } + RemInstruction() { this.getOpcode() instanceof Opcode::Rem } } /** @@ -1104,14 +1117,14 @@ class RemInstruction extends BinaryArithmeticInstruction { * is performed according to IEEE-754. */ class NegateInstruction extends UnaryArithmeticInstruction { - NegateInstruction() { getOpcode() instanceof Opcode::Negate } + NegateInstruction() { this.getOpcode() instanceof Opcode::Negate } } /** * An instruction that computes the result of a bitwise operation. */ class BitwiseInstruction extends Instruction { - BitwiseInstruction() { getOpcode() instanceof BitwiseOpcode } + BitwiseInstruction() { this.getOpcode() instanceof BitwiseOpcode } } /** @@ -1130,7 +1143,7 @@ class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } * Both operands must have the same integer type, which will also be the result type. */ class BitAndInstruction extends BinaryBitwiseInstruction { - BitAndInstruction() { getOpcode() instanceof Opcode::BitAnd } + BitAndInstruction() { this.getOpcode() instanceof Opcode::BitAnd } } /** @@ -1139,7 +1152,7 @@ class BitAndInstruction extends BinaryBitwiseInstruction { * Both operands must have the same integer type, which will also be the result type. */ class BitOrInstruction extends BinaryBitwiseInstruction { - BitOrInstruction() { getOpcode() instanceof Opcode::BitOr } + BitOrInstruction() { this.getOpcode() instanceof Opcode::BitOr } } /** @@ -1148,7 +1161,7 @@ class BitOrInstruction extends BinaryBitwiseInstruction { * Both operands must have the same integer type, which will also be the result type. */ class BitXorInstruction extends BinaryBitwiseInstruction { - BitXorInstruction() { getOpcode() instanceof Opcode::BitXor } + BitXorInstruction() { this.getOpcode() instanceof Opcode::BitXor } } /** @@ -1159,7 +1172,7 @@ class BitXorInstruction extends BinaryBitwiseInstruction { * rightmost bits are zero-filled. */ class ShiftLeftInstruction extends BinaryBitwiseInstruction { - ShiftLeftInstruction() { getOpcode() instanceof Opcode::ShiftLeft } + ShiftLeftInstruction() { this.getOpcode() instanceof Opcode::ShiftLeft } } /** @@ -1172,7 +1185,7 @@ class ShiftLeftInstruction extends BinaryBitwiseInstruction { * of the left operand. */ class ShiftRightInstruction extends BinaryBitwiseInstruction { - ShiftRightInstruction() { getOpcode() instanceof Opcode::ShiftRight } + ShiftRightInstruction() { this.getOpcode() instanceof Opcode::ShiftRight } } /** @@ -1183,7 +1196,7 @@ class PointerArithmeticInstruction extends BinaryInstruction { int elementSize; PointerArithmeticInstruction() { - getOpcode() instanceof PointerArithmeticOpcode and + this.getOpcode() instanceof PointerArithmeticOpcode and elementSize = Raw::getInstructionElementSize(this) } @@ -1206,7 +1219,7 @@ class PointerArithmeticInstruction extends BinaryInstruction { * An instruction that adds or subtracts an integer offset from a pointer. */ class PointerOffsetInstruction extends PointerArithmeticInstruction { - PointerOffsetInstruction() { getOpcode() instanceof PointerOffsetOpcode } + PointerOffsetInstruction() { this.getOpcode() instanceof PointerOffsetOpcode } } /** @@ -1217,7 +1230,7 @@ class PointerOffsetInstruction extends PointerArithmeticInstruction { * overflow is undefined. */ class PointerAddInstruction extends PointerOffsetInstruction { - PointerAddInstruction() { getOpcode() instanceof Opcode::PointerAdd } + PointerAddInstruction() { this.getOpcode() instanceof Opcode::PointerAdd } } /** @@ -1228,7 +1241,7 @@ class PointerAddInstruction extends PointerOffsetInstruction { * pointer underflow is undefined. */ class PointerSubInstruction extends PointerOffsetInstruction { - PointerSubInstruction() { getOpcode() instanceof Opcode::PointerSub } + PointerSubInstruction() { this.getOpcode() instanceof Opcode::PointerSub } } /** @@ -1241,31 +1254,31 @@ class PointerSubInstruction extends PointerOffsetInstruction { * undefined. */ class PointerDiffInstruction extends PointerArithmeticInstruction { - PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } + PointerDiffInstruction() { this.getOpcode() instanceof Opcode::PointerDiff } } /** * An instruction whose result is computed from a single operand. */ class UnaryInstruction extends Instruction { - UnaryInstruction() { getOpcode() instanceof UnaryOpcode } + UnaryInstruction() { this.getOpcode() instanceof UnaryOpcode } /** * Gets the sole operand of this instruction. */ - final UnaryOperand getUnaryOperand() { result = getAnOperand() } + final UnaryOperand getUnaryOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the sole operand of this instruction. */ - final Instruction getUnary() { result = getUnaryOperand().getDef() } + final Instruction getUnary() { result = this.getUnaryOperand().getDef() } } /** * An instruction that converts the value of its operand to a value of a different type. */ class ConvertInstruction extends UnaryInstruction { - ConvertInstruction() { getOpcode() instanceof Opcode::Convert } + ConvertInstruction() { this.getOpcode() instanceof Opcode::Convert } } /** @@ -1279,7 +1292,7 @@ class ConvertInstruction extends UnaryInstruction { * `as` expression. */ class CheckedConvertOrNullInstruction extends UnaryInstruction { - CheckedConvertOrNullInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrNull } + CheckedConvertOrNullInstruction() { this.getOpcode() instanceof Opcode::CheckedConvertOrNull } } /** @@ -1293,7 +1306,7 @@ class CheckedConvertOrNullInstruction extends UnaryInstruction { * expression. */ class CheckedConvertOrThrowInstruction extends UnaryInstruction { - CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow } + CheckedConvertOrThrowInstruction() { this.getOpcode() instanceof Opcode::CheckedConvertOrThrow } } /** @@ -1306,7 +1319,7 @@ class CheckedConvertOrThrowInstruction extends UnaryInstruction { * the most-derived object. */ class CompleteObjectAddressInstruction extends UnaryInstruction { - CompleteObjectAddressInstruction() { getOpcode() instanceof Opcode::CompleteObjectAddress } + CompleteObjectAddressInstruction() { this.getOpcode() instanceof Opcode::CompleteObjectAddress } } /** @@ -1351,7 +1364,7 @@ class InheritanceConversionInstruction extends UnaryInstruction { * An instruction that converts from the address of a derived class to the address of a base class. */ class ConvertToBaseInstruction extends InheritanceConversionInstruction { - ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode } + ConvertToBaseInstruction() { this.getOpcode() instanceof ConvertToBaseOpcode } } /** @@ -1361,7 +1374,9 @@ class ConvertToBaseInstruction extends InheritanceConversionInstruction { * If the operand holds a null address, the result is a null address. */ class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { - ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase } + ConvertToNonVirtualBaseInstruction() { + this.getOpcode() instanceof Opcode::ConvertToNonVirtualBase + } } /** @@ -1371,7 +1386,7 @@ class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { * If the operand holds a null address, the result is a null address. */ class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { - ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase } + ConvertToVirtualBaseInstruction() { this.getOpcode() instanceof Opcode::ConvertToVirtualBase } } /** @@ -1381,7 +1396,7 @@ class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { * If the operand holds a null address, the result is a null address. */ class ConvertToDerivedInstruction extends InheritanceConversionInstruction { - ConvertToDerivedInstruction() { getOpcode() instanceof Opcode::ConvertToDerived } + ConvertToDerivedInstruction() { this.getOpcode() instanceof Opcode::ConvertToDerived } } /** @@ -1390,7 +1405,7 @@ class ConvertToDerivedInstruction extends InheritanceConversionInstruction { * The operand must have an integer type, which will also be the result type. */ class BitComplementInstruction extends UnaryBitwiseInstruction { - BitComplementInstruction() { getOpcode() instanceof Opcode::BitComplement } + BitComplementInstruction() { this.getOpcode() instanceof Opcode::BitComplement } } /** @@ -1399,14 +1414,14 @@ class BitComplementInstruction extends UnaryBitwiseInstruction { * The operand must have a Boolean type, which will also be the result type. */ class LogicalNotInstruction extends UnaryInstruction { - LogicalNotInstruction() { getOpcode() instanceof Opcode::LogicalNot } + LogicalNotInstruction() { this.getOpcode() instanceof Opcode::LogicalNot } } /** * An instruction that compares two numeric operands. */ class CompareInstruction extends BinaryInstruction { - CompareInstruction() { getOpcode() instanceof CompareOpcode } + CompareInstruction() { this.getOpcode() instanceof CompareOpcode } } /** @@ -1417,7 +1432,7 @@ class CompareInstruction extends BinaryInstruction { * unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareEQInstruction extends CompareInstruction { - CompareEQInstruction() { getOpcode() instanceof Opcode::CompareEQ } + CompareEQInstruction() { this.getOpcode() instanceof Opcode::CompareEQ } } /** @@ -1428,14 +1443,14 @@ class CompareEQInstruction extends CompareInstruction { * `left == right`. Floating-point comparison is performed according to IEEE-754. */ class CompareNEInstruction extends CompareInstruction { - CompareNEInstruction() { getOpcode() instanceof Opcode::CompareNE } + CompareNEInstruction() { this.getOpcode() instanceof Opcode::CompareNE } } /** * An instruction that does a relative comparison of two values, such as `<` or `>=`. */ class RelationalInstruction extends CompareInstruction { - RelationalInstruction() { getOpcode() instanceof RelationalOpcode } + RelationalInstruction() { this.getOpcode() instanceof RelationalOpcode } /** * Gets the operand on the "greater" (or "greater-or-equal") side @@ -1467,11 +1482,11 @@ class RelationalInstruction extends CompareInstruction { * are unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareLTInstruction extends RelationalInstruction { - CompareLTInstruction() { getOpcode() instanceof Opcode::CompareLT } + CompareLTInstruction() { this.getOpcode() instanceof Opcode::CompareLT } - override Instruction getLesser() { result = getLeft() } + override Instruction getLesser() { result = this.getLeft() } - override Instruction getGreater() { result = getRight() } + override Instruction getGreater() { result = this.getRight() } override predicate isStrict() { any() } } @@ -1484,11 +1499,11 @@ class CompareLTInstruction extends RelationalInstruction { * are unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareGTInstruction extends RelationalInstruction { - CompareGTInstruction() { getOpcode() instanceof Opcode::CompareGT } + CompareGTInstruction() { this.getOpcode() instanceof Opcode::CompareGT } - override Instruction getLesser() { result = getRight() } + override Instruction getLesser() { result = this.getRight() } - override Instruction getGreater() { result = getLeft() } + override Instruction getGreater() { result = this.getLeft() } override predicate isStrict() { any() } } @@ -1502,11 +1517,11 @@ class CompareGTInstruction extends RelationalInstruction { * are unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareLEInstruction extends RelationalInstruction { - CompareLEInstruction() { getOpcode() instanceof Opcode::CompareLE } + CompareLEInstruction() { this.getOpcode() instanceof Opcode::CompareLE } - override Instruction getLesser() { result = getLeft() } + override Instruction getLesser() { result = this.getLeft() } - override Instruction getGreater() { result = getRight() } + override Instruction getGreater() { result = this.getRight() } override predicate isStrict() { none() } } @@ -1520,11 +1535,11 @@ class CompareLEInstruction extends RelationalInstruction { * are unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareGEInstruction extends RelationalInstruction { - CompareGEInstruction() { getOpcode() instanceof Opcode::CompareGE } + CompareGEInstruction() { this.getOpcode() instanceof Opcode::CompareGE } - override Instruction getLesser() { result = getRight() } + override Instruction getLesser() { result = this.getRight() } - override Instruction getGreater() { result = getLeft() } + override Instruction getGreater() { result = this.getLeft() } override predicate isStrict() { none() } } @@ -1543,78 +1558,78 @@ class CompareGEInstruction extends RelationalInstruction { * of any case edge. */ class SwitchInstruction extends Instruction { - SwitchInstruction() { getOpcode() instanceof Opcode::Switch } + SwitchInstruction() { this.getOpcode() instanceof Opcode::Switch } /** Gets the operand that provides the integer value controlling the switch. */ - final ConditionOperand getExpressionOperand() { result = getAnOperand() } + final ConditionOperand getExpressionOperand() { result = this.getAnOperand() } /** Gets the instruction whose result provides the integer value controlling the switch. */ - final Instruction getExpression() { result = getExpressionOperand().getDef() } + final Instruction getExpression() { result = this.getExpressionOperand().getDef() } /** Gets the successor instructions along the case edges of the switch. */ - final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) } + final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = this.getSuccessor(edge)) } /** Gets the successor instruction along the default edge of the switch, if any. */ - final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) } + final Instruction getDefaultSuccessor() { result = this.getSuccessor(EdgeKind::defaultEdge()) } } /** * An instruction that calls a function. */ class CallInstruction extends Instruction { - CallInstruction() { getOpcode() instanceof Opcode::Call } + CallInstruction() { this.getOpcode() instanceof Opcode::Call } final override string getImmediateString() { - result = getStaticCallTarget().toString() + result = this.getStaticCallTarget().toString() or - not exists(getStaticCallTarget()) and result = "?" + not exists(this.getStaticCallTarget()) and result = "?" } /** * Gets the operand the specifies the target function of the call. */ - final CallTargetOperand getCallTargetOperand() { result = getAnOperand() } + final CallTargetOperand getCallTargetOperand() { result = this.getAnOperand() } /** * Gets the `Instruction` that computes the target function of the call. This is usually a * `FunctionAddress` instruction, but can also be an arbitrary instruction that produces a * function pointer. */ - final Instruction getCallTarget() { result = getCallTargetOperand().getDef() } + final Instruction getCallTarget() { result = this.getCallTargetOperand().getDef() } /** * Gets all of the argument operands of the call, including the `this` pointer, if any. */ - final ArgumentOperand getAnArgumentOperand() { result = getAnOperand() } + final ArgumentOperand getAnArgumentOperand() { result = this.getAnOperand() } /** * Gets the `Function` that the call targets, if this is statically known. */ final Language::Function getStaticCallTarget() { - result = getCallTarget().(FunctionAddressInstruction).getFunctionSymbol() + result = this.getCallTarget().(FunctionAddressInstruction).getFunctionSymbol() } /** * Gets all of the arguments of the call, including the `this` pointer, if any. */ - final Instruction getAnArgument() { result = getAnArgumentOperand().getDef() } + final Instruction getAnArgument() { result = this.getAnArgumentOperand().getDef() } /** * Gets the `this` pointer argument operand of the call, if any. */ - final ThisArgumentOperand getThisArgumentOperand() { result = getAnOperand() } + final ThisArgumentOperand getThisArgumentOperand() { result = this.getAnOperand() } /** * Gets the `this` pointer argument of the call, if any. */ - final Instruction getThisArgument() { result = getThisArgumentOperand().getDef() } + final Instruction getThisArgument() { result = this.getThisArgumentOperand().getDef() } /** * Gets the argument operand at the specified index. */ pragma[noinline] final PositionalArgumentOperand getPositionalArgumentOperand(int index) { - result = getAnOperand() and + result = this.getAnOperand() and result.getIndex() = index } @@ -1623,7 +1638,7 @@ class CallInstruction extends Instruction { */ pragma[noinline] final Instruction getPositionalArgument(int index) { - result = getPositionalArgumentOperand(index).getDef() + result = this.getPositionalArgumentOperand(index).getDef() } /** @@ -1631,16 +1646,16 @@ class CallInstruction extends Instruction { */ pragma[noinline] final ArgumentOperand getArgumentOperand(int index) { - index >= 0 and result = getPositionalArgumentOperand(index) + index >= 0 and result = this.getPositionalArgumentOperand(index) or - index = -1 and result = getThisArgumentOperand() + index = -1 and result = this.getThisArgumentOperand() } /** * Gets the argument at the specified index, or `this` if `index` is `-1`. */ pragma[noinline] - final Instruction getArgument(int index) { result = getArgumentOperand(index).getDef() } + final Instruction getArgument(int index) { result = this.getArgumentOperand(index).getDef() } /** * Gets the number of arguments of the call, including the `this` pointer, if any. @@ -1665,7 +1680,7 @@ class CallInstruction extends Instruction { * An instruction representing a side effect of a function call. */ class SideEffectInstruction extends Instruction { - SideEffectInstruction() { getOpcode() instanceof SideEffectOpcode } + SideEffectInstruction() { this.getOpcode() instanceof SideEffectOpcode } /** * Gets the instruction whose execution causes this side effect. @@ -1680,7 +1695,7 @@ class SideEffectInstruction extends Instruction { * accessed by that call. */ class CallSideEffectInstruction extends SideEffectInstruction { - CallSideEffectInstruction() { getOpcode() instanceof Opcode::CallSideEffect } + CallSideEffectInstruction() { this.getOpcode() instanceof Opcode::CallSideEffect } } /** @@ -1691,7 +1706,7 @@ class CallSideEffectInstruction extends SideEffectInstruction { * call target cannot write to escaped memory. */ class CallReadSideEffectInstruction extends SideEffectInstruction { - CallReadSideEffectInstruction() { getOpcode() instanceof Opcode::CallReadSideEffect } + CallReadSideEffectInstruction() { this.getOpcode() instanceof Opcode::CallReadSideEffect } } /** @@ -1699,33 +1714,33 @@ class CallReadSideEffectInstruction extends SideEffectInstruction { * specific parameter. */ class ReadSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { - ReadSideEffectInstruction() { getOpcode() instanceof ReadSideEffectOpcode } + ReadSideEffectInstruction() { this.getOpcode() instanceof ReadSideEffectOpcode } /** Gets the operand for the value that will be read from this instruction, if known. */ - final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + final SideEffectOperand getSideEffectOperand() { result = this.getAnOperand() } /** Gets the value that will be read from this instruction, if known. */ - final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + final Instruction getSideEffect() { result = this.getSideEffectOperand().getDef() } /** Gets the operand for the address from which this instruction may read. */ - final AddressOperand getArgumentOperand() { result = getAnOperand() } + final AddressOperand getArgumentOperand() { result = this.getAnOperand() } /** Gets the address from which this instruction may read. */ - final Instruction getArgumentDef() { result = getArgumentOperand().getDef() } + final Instruction getArgumentDef() { result = this.getArgumentOperand().getDef() } } /** * An instruction representing the read of an indirect parameter within a function call. */ class IndirectReadSideEffectInstruction extends ReadSideEffectInstruction { - IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect } + IndirectReadSideEffectInstruction() { this.getOpcode() instanceof Opcode::IndirectReadSideEffect } } /** * An instruction representing the read of an indirect buffer parameter within a function call. */ class BufferReadSideEffectInstruction extends ReadSideEffectInstruction { - BufferReadSideEffectInstruction() { getOpcode() instanceof Opcode::BufferReadSideEffect } + BufferReadSideEffectInstruction() { this.getOpcode() instanceof Opcode::BufferReadSideEffect } } /** @@ -1733,18 +1748,18 @@ class BufferReadSideEffectInstruction extends ReadSideEffectInstruction { */ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { SizedBufferReadSideEffectInstruction() { - getOpcode() instanceof Opcode::SizedBufferReadSideEffect + this.getOpcode() instanceof Opcode::SizedBufferReadSideEffect } /** * Gets the operand that holds the number of bytes read from the buffer. */ - final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + final BufferSizeOperand getBufferSizeOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the number of bytes read from the buffer. */ - final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } + final Instruction getBufferSize() { result = this.getBufferSizeOperand().getDef() } } /** @@ -1752,17 +1767,17 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { * specific parameter. */ class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { - WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode } + WriteSideEffectInstruction() { this.getOpcode() instanceof WriteSideEffectOpcode } /** * Get the operand that holds the address of the memory to be written. */ - final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + final AddressOperand getDestinationAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the memory to be written. */ - Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } + Instruction getDestinationAddress() { result = this.getDestinationAddressOperand().getDef() } } /** @@ -1770,7 +1785,7 @@ class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstructi */ class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction { IndirectMustWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::IndirectMustWriteSideEffect + this.getOpcode() instanceof Opcode::IndirectMustWriteSideEffect } } @@ -1780,7 +1795,7 @@ class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction */ class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { BufferMustWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::BufferMustWriteSideEffect + this.getOpcode() instanceof Opcode::BufferMustWriteSideEffect } } @@ -1790,18 +1805,18 @@ class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { */ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { SizedBufferMustWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect + this.getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect } /** * Gets the operand that holds the number of bytes written to the buffer. */ - final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + final BufferSizeOperand getBufferSizeOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the number of bytes written to the buffer. */ - final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } + final Instruction getBufferSize() { result = this.getBufferSizeOperand().getDef() } } /** @@ -1812,7 +1827,7 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi */ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { IndirectMayWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::IndirectMayWriteSideEffect + this.getOpcode() instanceof Opcode::IndirectMayWriteSideEffect } } @@ -1822,7 +1837,9 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. */ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { - BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect } + BufferMayWriteSideEffectInstruction() { + this.getOpcode() instanceof Opcode::BufferMayWriteSideEffect + } } /** @@ -1832,18 +1849,18 @@ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { */ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { SizedBufferMayWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect + this.getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect } /** * Gets the operand that holds the number of bytes written to the buffer. */ - final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + final BufferSizeOperand getBufferSizeOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the number of bytes written to the buffer. */ - final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } + final Instruction getBufferSize() { result = this.getBufferSizeOperand().getDef() } } /** @@ -1852,80 +1869,80 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio */ class InitializeDynamicAllocationInstruction extends SideEffectInstruction { InitializeDynamicAllocationInstruction() { - getOpcode() instanceof Opcode::InitializeDynamicAllocation + this.getOpcode() instanceof Opcode::InitializeDynamicAllocation } /** * Gets the operand that represents the address of the allocation this instruction is initializing. */ - final AddressOperand getAllocationAddressOperand() { result = getAnOperand() } + final AddressOperand getAllocationAddressOperand() { result = this.getAnOperand() } /** * Gets the address for the allocation this instruction is initializing. */ - final Instruction getAllocationAddress() { result = getAllocationAddressOperand().getDef() } + final Instruction getAllocationAddress() { result = this.getAllocationAddressOperand().getDef() } } /** * An instruction representing a GNU or MSVC inline assembly statement. */ class InlineAsmInstruction extends Instruction { - InlineAsmInstruction() { getOpcode() instanceof Opcode::InlineAsm } + InlineAsmInstruction() { this.getOpcode() instanceof Opcode::InlineAsm } } /** * An instruction that throws an exception. */ class ThrowInstruction extends Instruction { - ThrowInstruction() { getOpcode() instanceof ThrowOpcode } + ThrowInstruction() { this.getOpcode() instanceof ThrowOpcode } } /** * An instruction that throws a new exception. */ class ThrowValueInstruction extends ThrowInstruction { - ThrowValueInstruction() { getOpcode() instanceof Opcode::ThrowValue } + ThrowValueInstruction() { this.getOpcode() instanceof Opcode::ThrowValue } /** * Gets the address operand of the exception thrown by this instruction. */ - final AddressOperand getExceptionAddressOperand() { result = getAnOperand() } + final AddressOperand getExceptionAddressOperand() { result = this.getAnOperand() } /** * Gets the address of the exception thrown by this instruction. */ - final Instruction getExceptionAddress() { result = getExceptionAddressOperand().getDef() } + final Instruction getExceptionAddress() { result = this.getExceptionAddressOperand().getDef() } /** * Gets the operand for the exception thrown by this instruction. */ - final LoadOperand getExceptionOperand() { result = getAnOperand() } + final LoadOperand getExceptionOperand() { result = this.getAnOperand() } /** * Gets the exception thrown by this instruction. */ - final Instruction getException() { result = getExceptionOperand().getDef() } + final Instruction getException() { result = this.getExceptionOperand().getDef() } } /** * An instruction that re-throws the current exception. */ class ReThrowInstruction extends ThrowInstruction { - ReThrowInstruction() { getOpcode() instanceof Opcode::ReThrow } + ReThrowInstruction() { this.getOpcode() instanceof Opcode::ReThrow } } /** * An instruction that exits the current function by propagating an exception. */ class UnwindInstruction extends Instruction { - UnwindInstruction() { getOpcode() instanceof Opcode::Unwind } + UnwindInstruction() { this.getOpcode() instanceof Opcode::Unwind } } /** * An instruction that starts a `catch` handler. */ class CatchInstruction extends Instruction { - CatchInstruction() { getOpcode() instanceof CatchOpcode } + CatchInstruction() { this.getOpcode() instanceof CatchOpcode } } /** @@ -1935,7 +1952,7 @@ class CatchByTypeInstruction extends CatchInstruction { Language::LanguageType exceptionType; CatchByTypeInstruction() { - getOpcode() instanceof Opcode::CatchByType and + this.getOpcode() instanceof Opcode::CatchByType and exceptionType = Raw::getInstructionExceptionType(this) } @@ -1951,21 +1968,21 @@ class CatchByTypeInstruction extends CatchInstruction { * An instruction that catches any exception. */ class CatchAnyInstruction extends CatchInstruction { - CatchAnyInstruction() { getOpcode() instanceof Opcode::CatchAny } + CatchAnyInstruction() { this.getOpcode() instanceof Opcode::CatchAny } } /** * An instruction that initializes all escaped memory. */ class AliasedDefinitionInstruction extends Instruction { - AliasedDefinitionInstruction() { getOpcode() instanceof Opcode::AliasedDefinition } + AliasedDefinitionInstruction() { this.getOpcode() instanceof Opcode::AliasedDefinition } } /** * An instruction that consumes all escaped memory on exit from the function. */ class AliasedUseInstruction extends Instruction { - AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse } + AliasedUseInstruction() { this.getOpcode() instanceof Opcode::AliasedUse } } /** @@ -1979,7 +1996,7 @@ class AliasedUseInstruction extends Instruction { * runtime. */ class PhiInstruction extends Instruction { - PhiInstruction() { getOpcode() instanceof Opcode::Phi } + PhiInstruction() { this.getOpcode() instanceof Opcode::Phi } /** * Gets all of the instruction's `PhiInputOperand`s, representing the values that flow from each predecessor block. @@ -2047,29 +2064,29 @@ class PhiInstruction extends Instruction { * https://link.springer.com/content/pdf/10.1007%2F3-540-61053-7_66.pdf. */ class ChiInstruction extends Instruction { - ChiInstruction() { getOpcode() instanceof Opcode::Chi } + ChiInstruction() { this.getOpcode() instanceof Opcode::Chi } /** * Gets the operand that represents the previous state of all memory that might be aliased by the * memory write. */ - final ChiTotalOperand getTotalOperand() { result = getAnOperand() } + final ChiTotalOperand getTotalOperand() { result = this.getAnOperand() } /** * Gets the operand that represents the previous state of all memory that might be aliased by the * memory write. */ - final Instruction getTotal() { result = getTotalOperand().getDef() } + final Instruction getTotal() { result = this.getTotalOperand().getDef() } /** * Gets the operand that represents the new value written by the memory write. */ - final ChiPartialOperand getPartialOperand() { result = getAnOperand() } + final ChiPartialOperand getPartialOperand() { result = this.getAnOperand() } /** * Gets the operand that represents the new value written by the memory write. */ - final Instruction getPartial() { result = getPartialOperand().getDef() } + final Instruction getPartial() { result = this.getPartialOperand().getDef() } /** * Gets the bit range `[startBit, endBit)` updated by the partial operand of this `ChiInstruction`, relative to the start address of the total operand. @@ -2093,7 +2110,7 @@ class ChiInstruction extends Instruction { * or `Switch` instruction where that particular edge is infeasible. */ class UnreachedInstruction extends Instruction { - UnreachedInstruction() { getOpcode() instanceof Opcode::Unreached } + UnreachedInstruction() { this.getOpcode() instanceof Opcode::Unreached } } /** @@ -2106,7 +2123,7 @@ class BuiltInOperationInstruction extends Instruction { Language::BuiltInOperation operation; BuiltInOperationInstruction() { - getOpcode() instanceof BuiltInOperationOpcode and + this.getOpcode() instanceof BuiltInOperationOpcode and operation = Raw::getInstructionBuiltInOperation(this) } @@ -2122,9 +2139,9 @@ class BuiltInOperationInstruction extends Instruction { * actual operation is specified by the `getBuiltInOperation()` predicate. */ class BuiltInInstruction extends BuiltInOperationInstruction { - BuiltInInstruction() { getOpcode() instanceof Opcode::BuiltIn } + BuiltInInstruction() { this.getOpcode() instanceof Opcode::BuiltIn } - final override string getImmediateString() { result = getBuiltInOperation().toString() } + final override string getImmediateString() { result = this.getBuiltInOperation().toString() } } /** @@ -2135,7 +2152,7 @@ class BuiltInInstruction extends BuiltInOperationInstruction { * to the `...` parameter. */ class VarArgsStartInstruction extends UnaryInstruction { - VarArgsStartInstruction() { getOpcode() instanceof Opcode::VarArgsStart } + VarArgsStartInstruction() { this.getOpcode() instanceof Opcode::VarArgsStart } } /** @@ -2145,7 +2162,7 @@ class VarArgsStartInstruction extends UnaryInstruction { * a result. */ class VarArgsEndInstruction extends UnaryInstruction { - VarArgsEndInstruction() { getOpcode() instanceof Opcode::VarArgsEnd } + VarArgsEndInstruction() { this.getOpcode() instanceof Opcode::VarArgsEnd } } /** @@ -2155,7 +2172,7 @@ class VarArgsEndInstruction extends UnaryInstruction { * argument. */ class VarArgInstruction extends UnaryInstruction { - VarArgInstruction() { getOpcode() instanceof Opcode::VarArg } + VarArgInstruction() { this.getOpcode() instanceof Opcode::VarArg } } /** @@ -2166,7 +2183,7 @@ class VarArgInstruction extends UnaryInstruction { * argument of the `...` parameter. */ class NextVarArgInstruction extends UnaryInstruction { - NextVarArgInstruction() { getOpcode() instanceof Opcode::NextVarArg } + NextVarArgInstruction() { this.getOpcode() instanceof Opcode::NextVarArg } } /** @@ -2180,5 +2197,5 @@ class NextVarArgInstruction extends UnaryInstruction { * The result is the address of the newly allocated object. */ class NewObjInstruction extends Instruction { - NewObjInstruction() { getOpcode() instanceof Opcode::NewObj } + NewObjInstruction() { this.getOpcode() instanceof Opcode::NewObj } } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll b/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll index d7cf89ca9aa..85d217bd361 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll @@ -46,12 +46,12 @@ class Operand extends TStageOperand { /** * Gets the location of the source code for this operand. */ - final Language::Location getLocation() { result = getUse().getLocation() } + final Language::Location getLocation() { result = this.getUse().getLocation() } /** * Gets the function that contains this operand. */ - final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() } + final IRFunction getEnclosingIRFunction() { result = this.getUse().getEnclosingIRFunction() } /** * Gets the `Instruction` that consumes this operand. @@ -74,7 +74,7 @@ class Operand extends TStageOperand { */ final Instruction getDef() { result = this.getAnyDef() and - getDefinitionOverlap() instanceof MustExactlyOverlap + this.getDefinitionOverlap() instanceof MustExactlyOverlap } /** @@ -82,7 +82,7 @@ class Operand extends TStageOperand { * * Gets the `Instruction` that consumes this operand. */ - deprecated final Instruction getUseInstruction() { result = getUse() } + deprecated final Instruction getUseInstruction() { result = this.getUse() } /** * DEPRECATED: use `getAnyDef` or `getDef`. The exact replacement for this @@ -91,7 +91,7 @@ class Operand extends TStageOperand { * * Gets the `Instruction` whose result is the value of the operand. */ - deprecated final Instruction getDefinitionInstruction() { result = getAnyDef() } + deprecated final Instruction getDefinitionInstruction() { result = this.getAnyDef() } /** * Gets the overlap relationship between the operand's definition and its use. @@ -101,7 +101,9 @@ class Operand extends TStageOperand { /** * Holds if the result of the definition instruction does not exactly overlap this use. */ - final predicate isDefinitionInexact() { not getDefinitionOverlap() instanceof MustExactlyOverlap } + final predicate isDefinitionInexact() { + not this.getDefinitionOverlap() instanceof MustExactlyOverlap + } /** * Gets a prefix to use when dumping the operand in an operand list. @@ -121,7 +123,7 @@ class Operand extends TStageOperand { * For example: `this:r3_5` */ final string getDumpString() { - result = getDumpLabel() + getInexactSpecifier() + getDefinitionId() + result = this.getDumpLabel() + this.getInexactSpecifier() + this.getDefinitionId() } /** @@ -129,9 +131,9 @@ class Operand extends TStageOperand { * definition is not modeled in SSA. */ private string getDefinitionId() { - result = getAnyDef().getResultId() + result = this.getAnyDef().getResultId() or - not exists(getAnyDef()) and result = "m?" + not exists(this.getAnyDef()) and result = "m?" } /** @@ -140,7 +142,7 @@ class Operand extends TStageOperand { * the empty string. */ private string getInexactSpecifier() { - if isDefinitionInexact() then result = "~" else result = "" + if this.isDefinitionInexact() then result = "~" else result = "" } /** @@ -155,7 +157,7 @@ class Operand extends TStageOperand { * the definition type, such as in the case of a partial read or a read from a pointer that * has been cast to a different type. */ - Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() } + Language::LanguageType getLanguageType() { result = this.getAnyDef().getResultLanguageType() } /** * Gets the language-neutral type of the value consumed by this operand. This is usually the same @@ -164,7 +166,7 @@ class Operand extends TStageOperand { * from the definition type, such as in the case of a partial read or a read from a pointer that * has been cast to a different type. */ - final IRType getIRType() { result = getLanguageType().getIRType() } + final IRType getIRType() { result = this.getLanguageType().getIRType() } /** * Gets the type of the value consumed by this operand. This is usually the same as the @@ -173,7 +175,7 @@ class Operand extends TStageOperand { * the definition type, such as in the case of a partial read or a read from a pointer that * has been cast to a different type. */ - final Language::Type getType() { getLanguageType().hasType(result, _) } + final Language::Type getType() { this.getLanguageType().hasType(result, _) } /** * Holds if the value consumed by this operand is a glvalue. If this @@ -182,13 +184,13 @@ class Operand extends TStageOperand { * not hold, the value of the operand represents a value whose type is * given by `getType()`. */ - final predicate isGLValue() { getLanguageType().hasType(_, true) } + final predicate isGLValue() { this.getLanguageType().hasType(_, true) } /** * Gets the size of the value consumed by this operand, in bytes. If the operand does not have * a known constant size, this predicate does not hold. */ - final int getSize() { result = getLanguageType().getByteSize() } + final int getSize() { result = this.getLanguageType().getByteSize() } } /** @@ -205,7 +207,7 @@ class MemoryOperand extends Operand { /** * Gets the kind of memory access performed by the operand. */ - MemoryAccessKind getMemoryAccess() { result = getUse().getOpcode().getReadMemoryAccess() } + MemoryAccessKind getMemoryAccess() { result = this.getUse().getOpcode().getReadMemoryAccess() } /** * Holds if the memory access performed by this operand will not always read from every bit in the @@ -215,7 +217,7 @@ class MemoryOperand extends Operand { * conservative estimate of the memory that might actually be accessed at runtime (for example, * the global side effects of a function call). */ - predicate hasMayReadMemoryAccess() { getUse().getOpcode().hasMayReadMemoryAccess() } + predicate hasMayReadMemoryAccess() { this.getUse().getOpcode().hasMayReadMemoryAccess() } /** * Returns the operand that holds the memory address from which the current operand loads its @@ -223,8 +225,8 @@ class MemoryOperand extends Operand { * is `r1`. */ final AddressOperand getAddressOperand() { - getMemoryAccess().usesAddressOperand() and - result.getUse() = getUse() + this.getMemoryAccess().usesAddressOperand() and + result.getUse() = this.getUse() } } @@ -294,7 +296,7 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOpe result = unique(Instruction defInstr | hasDefinition(defInstr, _)) } - final override Overlap getDefinitionOverlap() { hasDefinition(_, result) } + final override Overlap getDefinitionOverlap() { this.hasDefinition(_, result) } pragma[noinline] private predicate hasDefinition(Instruction defInstr, Overlap overlap) { @@ -449,13 +451,17 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand { final override Overlap getDefinitionOverlap() { result = overlap } - final override int getDumpSortOrder() { result = 11 + getPredecessorBlock().getDisplayIndex() } - - final override string getDumpLabel() { - result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" + final override int getDumpSortOrder() { + result = 11 + this.getPredecessorBlock().getDisplayIndex() } - final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() } + final override string getDumpLabel() { + result = "from " + this.getPredecessorBlock().getDisplayIndex().toString() + ":" + } + + final override string getDumpId() { + result = this.getPredecessorBlock().getDisplayIndex().toString() + } /** * Gets the predecessor block from which this value comes. diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedCondition.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedCondition.qll index a172800b377..99833c70d0b 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedCondition.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedCondition.qll @@ -139,13 +139,13 @@ class TranslatedLogicalOrExpr extends TranslatedBinaryLogicalOperation { override LogicalOrExpr expr; override Instruction getChildTrueSuccessor(ConditionBase child) { - child = getAnOperand() and + child = this.getAnOperand() and result = this.getConditionContext().getChildTrueSuccessor(this) } override Instruction getChildFalseSuccessor(ConditionBase child) { child = this.getLeftOperand() and - result = getRightOperand().getFirstInstruction() + result = this.getRightOperand().getFirstInstruction() or child = this.getRightOperand() and result = this.getConditionContext().getChildFalseSuccessor(this) diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedDeclaration.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedDeclaration.qll index 86cbdbb4360..9b4fbbba723 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedDeclaration.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedDeclaration.qll @@ -48,7 +48,7 @@ class TranslatedLocalVariableDeclaration extends TranslatedLocalDeclaration, override LocalVariable getDeclVar() { result = var } - override Type getVarType() { result = getVariableType(getDeclVar()) } + override Type getVarType() { result = getVariableType(this.getDeclVar()) } override Type getTargetType() { result = getVariableType(var) } @@ -58,7 +58,7 @@ class TranslatedLocalVariableDeclaration extends TranslatedLocalDeclaration, or this.hasUninitializedInstruction() and tag = InitializerStoreTag() ) and - result = getIRUserVariable(getFunction(), getDeclVar()) + result = getIRUserVariable(this.getFunction(), this.getDeclVar()) } override TranslatedInitialization getInitialization() { diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedElement.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedElement.qll index 04e05dc9814..ea1ad7931cb 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedElement.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedElement.qll @@ -456,7 +456,7 @@ abstract class TranslatedElement extends TTranslatedElement { * there is no enclosing `try`. */ Instruction getExceptionSuccessorInstruction() { - result = getParent().getExceptionSuccessorInstruction() + result = this.getParent().getExceptionSuccessorInstruction() } /** @@ -558,7 +558,7 @@ abstract class TranslatedElement extends TTranslatedElement { * Gets the temporary variable generated by this element with tag `tag`. */ final IRTempVariable getTempVariable(TempVariableTag tag) { - result.getAST() = getAST() and + result.getAST() = this.getAST() and result.getTag() = tag and this.hasTempVariable(tag, _) } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedExpr.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedExpr.qll index 72c408a3f2a..362ed3e0d2b 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedExpr.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedExpr.qll @@ -98,7 +98,7 @@ abstract class TranslatedCoreExpr extends TranslatedExpr { } final CSharpType getResultCSharpType() { - if isResultLValue() = true + if this.isResultLValue() = true then result = getTypeForGLValue(expr.getType()) else result = getTypeForPRValue(expr.getType()) } @@ -138,18 +138,18 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext, tag = ConditionValueFalseConstantTag() ) and opcode instanceof Opcode::Constant and - resultType = getResultCSharpType() + resultType = this.getResultCSharpType() or ( tag = ConditionValueTrueStoreTag() or tag = ConditionValueFalseStoreTag() ) and opcode instanceof Opcode::Store and - resultType = getResultCSharpType() + resultType = this.getResultCSharpType() or tag = ConditionValueResultLoadTag() and opcode instanceof Opcode::Load and - resultType = getResultCSharpType() + resultType = this.getResultCSharpType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -258,7 +258,7 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad { override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = LoadTag() and opcode instanceof Opcode::Load and - if producesExprResult() + if this.producesExprResult() then resultType = getTypeForPRValue(expr.getType()) else resultType = getTypeForGLValue(expr.getType()) } @@ -542,7 +542,7 @@ class TranslatedArrayAccess extends TranslatedNonConstantExpr { } final override TranslatedElement getChild(int id) { - id = -1 and result = getBaseOperand() + id = -1 and result = this.getBaseOperand() or result = this.getOffsetOperand(id) } @@ -559,7 +559,7 @@ class TranslatedArrayAccess extends TranslatedNonConstantExpr { or // The successor of the last `PointerAdd` instruction is // the successor of the `TranslatedArrayAccess`. - tag = PointerAddTag(getRank() - 1) and + tag = PointerAddTag(this.getRank() - 1) and result = this.getParent().getChildSuccessor(this) or // The successor of an `ElementsAddress` instruction is @@ -582,27 +582,29 @@ class TranslatedArrayAccess extends TranslatedNonConstantExpr { result = this.getInstruction(PointerAddTag(child.getAST().getIndex())) } - override Instruction getResult() { result = this.getInstruction(PointerAddTag(getRank() - 1)) } + override Instruction getResult() { + result = this.getInstruction(PointerAddTag(this.getRank() - 1)) + } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { exists(int index | - inBounds(index) and + this.inBounds(index) and tag = PointerAddTag(index) and opcode instanceof Opcode::PointerAdd and - resultType = getTypeForPRValue(getArrayOfDim(getRank() - index, expr.getType())) + resultType = getTypeForPRValue(getArrayOfDim(this.getRank() - index, expr.getType())) ) or exists(int index | - inBounds(index) and + this.inBounds(index) and tag = ElementsAddressTag(index) and opcode instanceof Opcode::ElementsAddress and - resultType = getTypeForPRValue(getArrayOfDim(getRank() - index, expr.getType())) + resultType = getTypeForPRValue(getArrayOfDim(this.getRank() - index, expr.getType())) ) } override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { exists(int index | - inBounds(index) and + this.inBounds(index) and tag = PointerAddTag(index) and ( operandTag instanceof LeftOperandTag and @@ -632,7 +634,7 @@ class TranslatedArrayAccess extends TranslatedNonConstantExpr { override int getInstructionElementSize(InstructionTag tag) { exists(int index | - inBounds(index) and + this.inBounds(index) and tag = PointerAddTag(index) and result = Language::getTypeSize(expr.getQualifier().getType().(ArrayType).getElementType()) ) @@ -989,9 +991,9 @@ abstract class TranslatedSingleInstructionExpr extends TranslatedNonConstantExpr abstract Opcode getOpcode(); final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { - opcode = getOpcode() and + opcode = this.getOpcode() and tag = OnlyInstructionTag() and - resultType = getResultCSharpType() + resultType = this.getResultCSharpType() } final override Instruction getResult() { result = this.getInstruction(OnlyInstructionTag()) } @@ -1189,7 +1191,7 @@ class TranslatedBinaryOperation extends TranslatedSingleInstructionExpr { override int getInstructionElementSize(InstructionTag tag) { tag = OnlyInstructionTag() and exists(Opcode opcode | - opcode = getOpcode() and + opcode = this.getOpcode() and ( opcode instanceof Opcode::PointerAdd or opcode instanceof Opcode::PointerSub or @@ -1200,7 +1202,9 @@ class TranslatedBinaryOperation extends TranslatedSingleInstructionExpr { } private TranslatedExpr getPointerOperand() { - if swapOperandsOnOp() then result = this.getRightOperand() else result = this.getLeftOperand() + if this.swapOperandsOnOp() + then result = this.getRightOperand() + else result = this.getLeftOperand() } private predicate swapOperandsOnOp() { @@ -1425,7 +1429,7 @@ class TranslatedAssignOperation extends TranslatedAssignment { resultType = getTypeForPRValue(this.getLeftOperand().getResultType()) or tag = AssignOperationOpTag() and - opcode = getOpcode() and + opcode = this.getOpcode() and resultType = getTypeForPRValue(this.getConvertedLeftOperandType()) or tag = AssignmentStoreTag() and @@ -1452,7 +1456,7 @@ class TranslatedAssignOperation extends TranslatedAssignment { opcode instanceof Opcode::PointerSub ) ) and - result = Language::getTypeSize(getResultType().(PointerType).getReferentType()) + result = Language::getTypeSize(this.getResultType().(PointerType).getReferentType()) } override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { @@ -1799,7 +1803,7 @@ class TranslatedIsExpr extends TranslatedNonConstantExpr { result = this.getInstruction(GeneratedConstantTag()) ) or - hasVar() and + this.hasVar() and tag = GeneratedBranchTag() and operandTag instanceof ConditionOperandTag and result = this.getInstruction(GeneratedNEQTag()) @@ -1848,7 +1852,7 @@ class TranslatedLambdaExpr extends TranslatedNonConstantExpr, InitializationCont } override Instruction getChildSuccessor(TranslatedElement child) { - child = getInitialization() and + child = this.getInitialization() and result = this.getInstruction(LoadTag()) } @@ -1922,7 +1926,7 @@ class TranslatedDelegateCall extends TranslatedNonConstantExpr { override Instruction getChildSuccessor(TranslatedElement child) { child = this.getInvokeCall() and - result = getParent().getChildSuccessor(this) + result = this.getParent().getChildSuccessor(this) } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { @@ -1973,7 +1977,7 @@ abstract class TranslatedCreation extends TranslatedCoreExpr, TTranslatedCreatio else result = this.getInstruction(NewObjTag()) } - override Instruction getReceiver() { result = getInstruction(NewObjTag()) } + override Instruction getReceiver() { result = this.getInstruction(NewObjTag()) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { kind instanceof GotoEdge and @@ -1998,11 +2002,11 @@ abstract class TranslatedCreation extends TranslatedCoreExpr, TTranslatedCreatio child = this.getConstructorCall() and if exists(this.getInitializerExpr()) then result = this.getInitializerExpr().getFirstInstruction() - else result = getLoadOrChildSuccessor() + else result = this.getLoadOrChildSuccessor() ) or child = this.getInitializerExpr() and - result = getLoadOrChildSuccessor() + result = this.getLoadOrChildSuccessor() } private Instruction getLoadOrChildSuccessor() { diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedFunction.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedFunction.qll index 65488a1b95d..94b48b0985d 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedFunction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedFunction.qll @@ -68,20 +68,20 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { or ( tag = AliasedDefinitionTag() and - if exists(getThisType()) + if exists(this.getThisType()) then result = this.getInstruction(InitializeThisTag()) else - if exists(getParameter(0)) + if exists(this.getParameter(0)) then result = this.getParameter(0).getFirstInstruction() else result = this.getBodyOrReturn() ) or ( tag = InitializeThisTag() and - if exists(getParameter(0)) + if exists(this.getParameter(0)) then result = this.getParameter(0).getFirstInstruction() else - if exists(getConstructorInitializer()) + if exists(this.getConstructorInitializer()) then result = this.getConstructorInitializer().getFirstInstruction() else result = this.getBodyOrReturn() ) @@ -106,7 +106,7 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { if exists(callable.getParameter(paramIndex + 1)) then result = this.getParameter(paramIndex + 1).getFirstInstruction() else - if exists(getConstructorInitializer()) + if exists(this.getConstructorInitializer()) then result = this.getConstructorInitializer().getFirstInstruction() else result = this.getBodyOrReturn() ) @@ -136,12 +136,12 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { or tag = InitializeThisTag() and opcode instanceof Opcode::InitializeThis and - resultType = getTypeForGLValue(getThisType()) + resultType = getTypeForGLValue(this.getThisType()) or tag = ReturnValueAddressTag() and opcode instanceof Opcode::VariableAddress and - not getReturnType() instanceof VoidType and - resultType = getTypeForGLValue(getReturnType()) + not this.getReturnType() instanceof VoidType and + resultType = getTypeForGLValue(this.getReturnType()) or ( tag = ReturnTag() and @@ -201,7 +201,7 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { final override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { tag = ReturnValueTempVar() and type = getTypeForPRValue(this.getReturnType()) and - not getReturnType() instanceof VoidType + not this.getReturnType() instanceof VoidType } /** @@ -320,7 +320,7 @@ class TranslatedParameter extends TranslatedElement, TTranslatedParameter { tag = InitializerStoreTag() or tag = InitializerVariableAddressTag() ) and - result = getIRUserVariable(getFunction(), param) + result = getIRUserVariable(this.getFunction(), param) } final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedInitialization.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedInitialization.qll index cbe0e7c1d2a..77e41c15e72 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedInitialization.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedInitialization.qll @@ -139,7 +139,7 @@ class TranslatedDirectInitialization extends TranslatedInitialization { opcode instanceof Opcode::Store and resultType = getTypeForPRValue(this.getContext().getTargetType()) or - needsConversion() and + this.needsConversion() and tag = AssignmentConvertRightTag() and // For now only use `Opcode::Convert` to // crudely represent conversions. Could @@ -153,9 +153,9 @@ class TranslatedDirectInitialization extends TranslatedInitialization { result = this.getParent().getChildSuccessor(this) and kind instanceof GotoEdge or - needsConversion() and + this.needsConversion() and tag = AssignmentConvertRightTag() and - result = getInstruction(InitializerStoreTag()) and + result = this.getInstruction(InitializerStoreTag()) and kind instanceof GotoEdge } @@ -203,7 +203,7 @@ abstract class TranslatedElementInitialization extends TranslatedElement { ArrayInitializer initList; final override string toString() { - result = initList.toString() + "[" + getElementIndex().toString() + "]" + result = initList.toString() + "[" + this.getElementIndex().toString() + "]" } final override Language::AST getAST() { result = initList } @@ -211,54 +211,54 @@ abstract class TranslatedElementInitialization extends TranslatedElement { final override Callable getFunction() { result = initList.getEnclosingCallable() } final override Instruction getFirstInstruction() { - result = this.getInstruction(getElementIndexTag()) + result = this.getInstruction(this.getElementIndexTag()) } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { - tag = getElementIndexTag() and + tag = this.getElementIndexTag() and opcode instanceof Opcode::Constant and resultType = getIntType() or - tag = getElementAddressTag() and + tag = this.getElementAddressTag() and opcode instanceof Opcode::PointerAdd and - resultType = getTypeForGLValue(getElementType()) + resultType = getTypeForGLValue(this.getElementType()) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { - tag = getElementIndexTag() and - result = this.getInstruction(getElementAddressTag()) and + tag = this.getElementIndexTag() and + result = this.getInstruction(this.getElementAddressTag()) and kind instanceof GotoEdge } override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { - tag = getElementAddressTag() and + tag = this.getElementAddressTag() and ( operandTag instanceof LeftOperandTag and result = this.getParent().(InitializationContext).getTargetAddress() or operandTag instanceof RightOperandTag and - result = this.getInstruction(getElementIndexTag()) + result = this.getInstruction(this.getElementIndexTag()) ) } override int getInstructionElementSize(InstructionTag tag) { - tag = getElementAddressTag() and - result = Language::getTypeSize(getElementType()) + tag = this.getElementAddressTag() and + result = Language::getTypeSize(this.getElementType()) } override string getInstructionConstantValue(InstructionTag tag) { - tag = getElementIndexTag() and - result = getElementIndex().toString() + tag = this.getElementIndexTag() and + result = this.getElementIndex().toString() } abstract int getElementIndex(); final InstructionTag getElementAddressTag() { - result = InitializerElementAddressTag(getElementIndex()) + result = InitializerElementAddressTag(this.getElementIndex()) } final InstructionTag getElementIndexTag() { - result = InitializerElementIndexTag(getElementIndex()) + result = InitializerElementIndexTag(this.getElementIndex()) } final ArrayInitializer getInitList() { result = initList } @@ -278,14 +278,16 @@ class TranslatedExplicitElementInitialization extends TranslatedElementInitializ this = TTranslatedExplicitElementInitialization(initList, elementIndex) } - override Instruction getTargetAddress() { result = this.getInstruction(getElementAddressTag()) } + override Instruction getTargetAddress() { + result = this.getInstruction(this.getElementAddressTag()) + } - override Type getTargetType() { result = getElementType() } + override Type getTargetType() { result = this.getElementType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { result = TranslatedElementInitialization.super.getInstructionSuccessor(tag, kind) or - tag = getElementAddressTag() and + tag = this.getElementAddressTag() and result = this.getInitialization().getFirstInstruction() and kind instanceof GotoEdge } @@ -340,7 +342,7 @@ class TranslatedConstructorInitializer extends TranslatedConstructorCallFromCons override string toString() { result = "constructor init: " + call.toString() } override Instruction getFirstInstruction() { - if needsConversion() + if this.needsConversion() then result = this.getInstruction(OnlyInstructionTag()) else result = this.getConstructorCall().getFirstInstruction() } @@ -361,13 +363,13 @@ class TranslatedConstructorInitializer extends TranslatedConstructorCallFromCons override Instruction getReceiver() { if this.needsConversion() then result = this.getInstruction(OnlyInstructionTag()) - else result = getTranslatedFunction(getFunction()).getInitializeThisInstruction() + else result = getTranslatedFunction(this.getFunction()).getInitializeThisInstruction() } override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { tag = OnlyInstructionTag() and operandTag instanceof UnaryOperandTag and - result = getTranslatedFunction(getFunction()).getInitializeThisInstruction() + result = getTranslatedFunction(this.getFunction()).getInitializeThisInstruction() } predicate needsConversion() { diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedStmt.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedStmt.qll index 81de9a6b7c9..2f91484094a 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedStmt.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedStmt.qll @@ -79,7 +79,7 @@ class TranslatedDeclStmt extends TranslatedStmt { override Instruction getChildSuccessor(TranslatedElement child) { exists(int index | child = this.getLocalDeclaration(index) and - if index = (getChildCount() - 1) + if index = (this.getChildCount() - 1) then result = this.getParent().getChildSuccessor(this) else result = this.getLocalDeclaration(index + 1).getFirstInstruction() ) @@ -276,14 +276,14 @@ class TranslatedBlock extends TranslatedStmt { override TranslatedElement getChild(int id) { result = this.getStmt(id) } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { - isEmpty() and + this.isEmpty() and opcode instanceof Opcode::NoOp and tag = OnlyInstructionTag() and resultType = getVoidType() } override Instruction getFirstInstruction() { - if isEmpty() + if this.isEmpty() then result = this.getInstruction(OnlyInstructionTag()) else result = this.getStmt(0).getFirstInstruction() } @@ -303,7 +303,7 @@ class TranslatedBlock extends TranslatedStmt { override Instruction getChildSuccessor(TranslatedElement child) { exists(int index | child = this.getStmt(index) and - if index = (getStmtCount() - 1) + if index = (this.getStmtCount() - 1) then result = this.getParent().getChildSuccessor(this) else result = this.getStmt(index + 1).getFirstInstruction() ) @@ -347,7 +347,7 @@ class TranslatedCatchByTypeClause extends TranslatedClause { } override TranslatedElement getChild(int id) { - id = 0 and result = getParameter() + id = 0 and result = this.getParameter() or result = super.getChild(id) } @@ -355,14 +355,14 @@ class TranslatedCatchByTypeClause extends TranslatedClause { override Instruction getChildSuccessor(TranslatedElement child) { result = super.getChildSuccessor(child) or - child = getParameter() and result = this.getBlock().getFirstInstruction() + child = this.getParameter() and result = this.getBlock().getFirstInstruction() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = CatchTag() and ( kind instanceof GotoEdge and - result = getParameter().getFirstInstruction() + result = this.getParameter().getFirstInstruction() or kind instanceof ExceptionEdge and result = this.getParent().(TranslatedTryStmt).getNextHandler(this) @@ -559,8 +559,8 @@ abstract class TranslatedLoop extends TranslatedStmt, ConditionContext { final TranslatedStmt getBody() { result = getTranslatedStmt(stmt.getBody()) } final Instruction getFirstConditionInstruction() { - if hasCondition() - then result = getCondition().getFirstInstruction() + if this.hasCondition() + then result = this.getCondition().getFirstInstruction() else result = this.getBody().getFirstInstruction() } @@ -611,13 +611,13 @@ class TranslatedForStmt extends TranslatedLoop { override ForStmt stmt; override TranslatedElement getChild(int id) { - initializerIndex(id) and result = this.getDeclAndInit(id) + this.initializerIndex(id) and result = this.getDeclAndInit(id) or - result = this.getUpdate(updateIndex(id)) + result = this.getUpdate(this.updateIndex(id)) or - id = initializersNo() + updatesNo() and result = this.getCondition() + id = this.initializersNo() + this.updatesNo() and result = this.getCondition() or - id = initializersNo() + updatesNo() + 1 and result = this.getBody() + id = this.initializersNo() + this.updatesNo() + 1 and result = this.getBody() } private TranslatedElement getDeclAndInit(int index) { @@ -636,11 +636,11 @@ class TranslatedForStmt extends TranslatedLoop { private int updatesNo() { result = count(stmt.getAnUpdate()) } - private predicate initializerIndex(int index) { index in [0 .. initializersNo() - 1] } + private predicate initializerIndex(int index) { index in [0 .. this.initializersNo() - 1] } private int updateIndex(int index) { - result in [0 .. updatesNo() - 1] and - index = initializersNo() + result + result in [0 .. this.updatesNo() - 1] and + index = this.initializersNo() + result } override Instruction getFirstInstruction() { @@ -652,11 +652,11 @@ class TranslatedForStmt extends TranslatedLoop { override Instruction getChildSuccessor(TranslatedElement child) { exists(int index | child = this.getDeclAndInit(index) and - index < initializersNo() - 1 and + index < this.initializersNo() - 1 and result = this.getDeclAndInit(index + 1).getFirstInstruction() ) or - child = this.getDeclAndInit(initializersNo() - 1) and + child = this.getDeclAndInit(this.initializersNo() - 1) and result = this.getFirstConditionInstruction() or ( @@ -671,7 +671,7 @@ class TranslatedForStmt extends TranslatedLoop { result = this.getUpdate(index + 1).getFirstInstruction() ) or - child = this.getUpdate(updatesNo() - 1) and + child = this.getUpdate(this.updatesNo() - 1) and result = this.getFirstConditionInstruction() } } @@ -693,7 +693,7 @@ abstract class TranslatedSpecificJump extends TranslatedStmt { override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = OnlyInstructionTag() and kind instanceof GotoEdge and - result = getTargetInstruction() + result = this.getTargetInstruction() } override Instruction getChildSuccessor(TranslatedElement child) { none() } @@ -832,7 +832,7 @@ class TranslatedSwitchStmt extends TranslatedStmt { not exists(stmt.getDefaultCase()) and tag = SwitchBranchTag() and kind instanceof DefaultEdge and - result = getParent().getChildSuccessor(this) + result = this.getParent().getChildSuccessor(this) } private EdgeKind getCaseEdge(CaseStmt caseStmt) { @@ -862,19 +862,21 @@ class TranslatedEnumeratorForeach extends TranslatedLoop { override ForeachStmt stmt; override TranslatedElement getChild(int id) { - id = 0 and result = getTempEnumDecl() + id = 0 and result = this.getTempEnumDecl() or - id = 1 and result = getTry() + id = 1 and result = this.getTry() } - override Instruction getFirstInstruction() { result = getTempEnumDecl().getFirstInstruction() } + override Instruction getFirstInstruction() { + result = this.getTempEnumDecl().getFirstInstruction() + } override Instruction getChildSuccessor(TranslatedElement child) { - child = getTempEnumDecl() and - result = getTry().getFirstInstruction() + child = this.getTempEnumDecl() and + result = this.getTry().getFirstInstruction() or - child = getTry() and - result = getParent().getChildSuccessor(this) + child = this.getTry() and + result = this.getParent().getChildSuccessor(this) } private TranslatedElement getTry() { result = ForeachElements::getTry(stmt) } @@ -909,9 +911,9 @@ class TranslatedFixedStmt extends TranslatedStmt { override FixedStmt stmt; override TranslatedElement getChild(int id) { - result = getDecl(id) + result = this.getDecl(id) or - id = noDecls() and result = this.getBody() + id = this.noDecls() and result = this.getBody() } override Instruction getFirstInstruction() { result = this.getDecl(0).getFirstInstruction() } @@ -947,24 +949,26 @@ class TranslatedLockStmt extends TranslatedStmt { override LockStmt stmt; override TranslatedElement getChild(int id) { - id = 0 and result = getLockedVarDecl() + id = 0 and result = this.getLockedVarDecl() or - id = 1 and result = getLockWasTakenDecl() + id = 1 and result = this.getLockWasTakenDecl() or - id = 2 and result = getTry() + id = 2 and result = this.getTry() } - override Instruction getFirstInstruction() { result = getLockedVarDecl().getFirstInstruction() } + override Instruction getFirstInstruction() { + result = this.getLockedVarDecl().getFirstInstruction() + } override Instruction getChildSuccessor(TranslatedElement child) { - child = getLockedVarDecl() and - result = getLockWasTakenDecl().getFirstInstruction() + child = this.getLockedVarDecl() and + result = this.getLockWasTakenDecl().getFirstInstruction() or - child = getLockWasTakenDecl() and - result = getTry().getFirstInstruction() + child = this.getLockWasTakenDecl() and + result = this.getTry().getFirstInstruction() or - child = getTry() and - result = getParent().getChildSuccessor(this) + child = this.getTry() and + result = this.getParent().getChildSuccessor(this) } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { @@ -1017,13 +1021,13 @@ class TranslatedUsingBlockStmt extends TranslatedStmt { override UsingBlockStmt stmt; override TranslatedElement getChild(int id) { - result = getDecl(id) + result = this.getDecl(id) or - id = getNumberOfDecls() and result = this.getBody() + id = this.getNumberOfDecls() and result = this.getBody() } override Instruction getFirstInstruction() { - if getNumberOfDecls() > 0 + if this.getNumberOfDecls() > 0 then result = this.getDecl(0).getFirstInstruction() else result = this.getBody().getFirstInstruction() } @@ -1060,7 +1064,7 @@ class TranslatedUsingBlockStmt extends TranslatedStmt { class TranslatedUsingDeclStmt extends TranslatedStmt { override UsingDeclStmt stmt; - override TranslatedElement getChild(int id) { result = getDecl(id) } + override TranslatedElement getChild(int id) { result = this.getDecl(id) } override Instruction getFirstInstruction() { result = this.getDecl(0).getFirstInstruction() } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll index 4b86f9a7cec..bb8630a5e0c 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll @@ -24,7 +24,7 @@ class IRBlockBase extends TIRBlock { final string toString() { result = getFirstInstruction(this).toString() } /** Gets the source location of the first non-`Phi` instruction in this block. */ - final Language::Location getLocation() { result = getFirstInstruction().getLocation() } + final Language::Location getLocation() { result = this.getFirstInstruction().getLocation() } /** * INTERNAL: Do not use. @@ -39,7 +39,7 @@ class IRBlockBase extends TIRBlock { ) and this = rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 | - funcBlock.getEnclosingFunction() = getEnclosingFunction() and + funcBlock.getEnclosingFunction() = this.getEnclosingFunction() and funcBlock.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and // Ensure that the block containing `EnterFunction` always comes first. if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction @@ -59,15 +59,15 @@ class IRBlockBase extends TIRBlock { * Get the `Phi` instructions that appear at the start of this block. */ final PhiInstruction getAPhiInstruction() { - Construction::getPhiInstructionBlockStart(result) = getFirstInstruction() + Construction::getPhiInstructionBlockStart(result) = this.getFirstInstruction() } /** * Gets an instruction in this block. This includes `Phi` instructions. */ final Instruction getAnInstruction() { - result = getInstruction(_) or - result = getAPhiInstruction() + result = this.getInstruction(_) or + result = this.getAPhiInstruction() } /** @@ -78,7 +78,9 @@ class IRBlockBase extends TIRBlock { /** * Gets the last instruction in this block. */ - final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) } + final Instruction getLastInstruction() { + result = this.getInstruction(this.getInstructionCount() - 1) + } /** * Gets the number of non-`Phi` instructions in this block. @@ -149,7 +151,7 @@ class IRBlock extends IRBlockBase { * Block `A` dominates block `B` if any control flow path from the entry block of the function to * block `B` must pass through block `A`. A block always dominates itself. */ - final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } + final predicate dominates(IRBlock block) { this.strictlyDominates(block) or this = block } /** * Gets a block on the dominance frontier of this block. @@ -159,8 +161,8 @@ class IRBlock extends IRBlockBase { */ pragma[noinline] final IRBlock dominanceFrontier() { - dominates(result.getAPredecessor()) and - not strictlyDominates(result) + this.dominates(result.getAPredecessor()) and + not this.strictlyDominates(result) } /** @@ -189,7 +191,7 @@ class IRBlock extends IRBlockBase { * Block `A` post-dominates block `B` if any control flow path from `B` to the exit block of the * function must pass through block `A`. A block always post-dominates itself. */ - final predicate postDominates(IRBlock block) { strictlyPostDominates(block) or this = block } + final predicate postDominates(IRBlock block) { this.strictlyPostDominates(block) or this = block } /** * Gets a block on the post-dominance frontier of this block. @@ -199,16 +201,16 @@ class IRBlock extends IRBlockBase { */ pragma[noinline] final IRBlock postPominanceFrontier() { - postDominates(result.getASuccessor()) and - not strictlyPostDominates(result) + this.postDominates(result.getASuccessor()) and + not this.strictlyPostDominates(result) } /** * Holds if this block is reachable from the entry block of its function. */ final predicate isReachableFromFunctionEntry() { - this = getEnclosingIRFunction().getEntryBlock() or - getAPredecessor().isReachableFromFunctionEntry() + this = this.getEnclosingIRFunction().getEntryBlock() or + this.getAPredecessor().isReachableFromFunctionEntry() } } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll index 2fb3edad602..88a973fc5a8 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll @@ -41,7 +41,7 @@ class Instruction extends Construction::TStageInstruction { } /** Gets a textual representation of this element. */ - final string toString() { result = getOpcode().toString() + ": " + getAST().toString() } + final string toString() { result = this.getOpcode().toString() + ": " + this.getAST().toString() } /** * Gets a string showing the result, opcode, and operands of the instruction, equivalent to what @@ -50,7 +50,8 @@ class Instruction extends Construction::TStageInstruction { * `mu0_28(int) = Store r0_26, r0_27` */ final string getDumpString() { - result = getResultString() + " = " + getOperationString() + " " + getOperandsString() + result = + this.getResultString() + " = " + this.getOperationString() + " " + this.getOperandsString() } private predicate shouldGenerateDumpStrings() { @@ -66,10 +67,13 @@ class Instruction extends Construction::TStageInstruction { * VariableAddress[x] */ final string getOperationString() { - shouldGenerateDumpStrings() and - if exists(getImmediateString()) - then result = getOperationPrefix() + getOpcode().toString() + "[" + getImmediateString() + "]" - else result = getOperationPrefix() + getOpcode().toString() + this.shouldGenerateDumpStrings() and + if exists(this.getImmediateString()) + then + result = + this.getOperationPrefix() + this.getOpcode().toString() + "[" + this.getImmediateString() + + "]" + else result = this.getOperationPrefix() + this.getOpcode().toString() } /** @@ -78,17 +82,17 @@ class Instruction extends Construction::TStageInstruction { string getImmediateString() { none() } private string getOperationPrefix() { - shouldGenerateDumpStrings() and + this.shouldGenerateDumpStrings() and if this instanceof SideEffectInstruction then result = "^" else result = "" } private string getResultPrefix() { - shouldGenerateDumpStrings() and - if getResultIRType() instanceof IRVoidType + this.shouldGenerateDumpStrings() and + if this.getResultIRType() instanceof IRVoidType then result = "v" else - if hasMemoryResult() - then if isResultModeled() then result = "m" else result = "mu" + if this.hasMemoryResult() + then if this.isResultModeled() then result = "m" else result = "mu" else result = "r" } @@ -97,7 +101,7 @@ class Instruction extends Construction::TStageInstruction { * used by debugging and printing code only. */ int getDisplayIndexInBlock() { - shouldGenerateDumpStrings() and + this.shouldGenerateDumpStrings() and exists(IRBlock block | this = block.getInstruction(result) or @@ -111,12 +115,12 @@ class Instruction extends Construction::TStageInstruction { } private int getLineRank() { - shouldGenerateDumpStrings() and + this.shouldGenerateDumpStrings() and this = rank[result](Instruction instr | instr = - getAnInstructionAtLine(getEnclosingIRFunction(), getLocation().getFile(), - getLocation().getStartLine()) + getAnInstructionAtLine(this.getEnclosingIRFunction(), this.getLocation().getFile(), + this.getLocation().getStartLine()) | instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock() ) @@ -130,8 +134,9 @@ class Instruction extends Construction::TStageInstruction { * Example: `r1_1` */ string getResultId() { - shouldGenerateDumpStrings() and - result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank() + this.shouldGenerateDumpStrings() and + result = + this.getResultPrefix() + this.getAST().getLocation().getStartLine() + "_" + this.getLineRank() } /** @@ -142,8 +147,8 @@ class Instruction extends Construction::TStageInstruction { * Example: `r1_1(int*)` */ final string getResultString() { - shouldGenerateDumpStrings() and - result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")" + this.shouldGenerateDumpStrings() and + result = this.getResultId() + "(" + this.getResultLanguageType().getDumpString() + ")" } /** @@ -153,10 +158,10 @@ class Instruction extends Construction::TStageInstruction { * Example: `func:r3_4, this:r3_5` */ string getOperandsString() { - shouldGenerateDumpStrings() and + this.shouldGenerateDumpStrings() and result = concat(Operand operand | - operand = getAnOperand() + operand = this.getAnOperand() | operand.getDumpString(), ", " order by operand.getDumpSortOrder() ) @@ -190,7 +195,7 @@ class Instruction extends Construction::TStageInstruction { * Gets the function that contains this instruction. */ final Language::Function getEnclosingFunction() { - result = getEnclosingIRFunction().getFunction() + result = this.getEnclosingIRFunction().getFunction() } /** @@ -208,7 +213,7 @@ class Instruction extends Construction::TStageInstruction { /** * Gets the location of the source code for this instruction. */ - final Language::Location getLocation() { result = getAST().getLocation() } + final Language::Location getLocation() { result = this.getAST().getLocation() } /** * Gets the `Expr` whose result is computed by this instruction, if any. The `Expr` may be a @@ -243,7 +248,7 @@ class Instruction extends Construction::TStageInstruction { * a result, its result type will be `IRVoidType`. */ cached - final IRType getResultIRType() { result = getResultLanguageType().getIRType() } + final IRType getResultIRType() { result = this.getResultLanguageType().getIRType() } /** * Gets the type of the result produced by this instruction. If the @@ -254,7 +259,7 @@ class Instruction extends Construction::TStageInstruction { */ final Language::Type getResultType() { exists(Language::LanguageType resultType | - resultType = getResultLanguageType() and + resultType = this.getResultLanguageType() and ( resultType.hasUnspecifiedType(result, _) or @@ -283,7 +288,7 @@ class Instruction extends Construction::TStageInstruction { * result of the `Load` instruction is a prvalue of type `int`, representing * the integer value loaded from variable `x`. */ - final predicate isGLValue() { getResultLanguageType().hasType(_, true) } + final predicate isGLValue() { this.getResultLanguageType().hasType(_, true) } /** * Gets the size of the result produced by this instruction, in bytes. If the @@ -292,7 +297,7 @@ class Instruction extends Construction::TStageInstruction { * If `this.isGLValue()` holds for this instruction, the value of * `getResultSize()` will always be the size of a pointer. */ - final int getResultSize() { result = getResultLanguageType().getByteSize() } + final int getResultSize() { result = this.getResultLanguageType().getByteSize() } /** * Gets the opcode that specifies the operation performed by this instruction. @@ -314,14 +319,16 @@ class Instruction extends Construction::TStageInstruction { /** * Holds if this instruction produces a memory result. */ - final predicate hasMemoryResult() { exists(getResultMemoryAccess()) } + final predicate hasMemoryResult() { exists(this.getResultMemoryAccess()) } /** * Gets the kind of memory access performed by this instruction's result. * Holds only for instructions with a memory result. */ pragma[inline] - final MemoryAccessKind getResultMemoryAccess() { result = getOpcode().getWriteMemoryAccess() } + final MemoryAccessKind getResultMemoryAccess() { + result = this.getOpcode().getWriteMemoryAccess() + } /** * Holds if the memory access performed by this instruction's result will not always write to @@ -332,7 +339,7 @@ class Instruction extends Construction::TStageInstruction { * (for example, the global side effects of a function call). */ pragma[inline] - final predicate hasResultMayMemoryAccess() { getOpcode().hasMayWriteMemoryAccess() } + final predicate hasResultMayMemoryAccess() { this.getOpcode().hasMayWriteMemoryAccess() } /** * Gets the operand that holds the memory address to which this instruction stores its @@ -340,7 +347,7 @@ class Instruction extends Construction::TStageInstruction { * is `r1`. */ final AddressOperand getResultAddressOperand() { - getResultMemoryAccess().usesAddressOperand() and + this.getResultMemoryAccess().usesAddressOperand() and result.getUse() = this } @@ -349,7 +356,7 @@ class Instruction extends Construction::TStageInstruction { * result, if any. For example, in `m3 = Store r1, r2`, the result of `getResultAddressOperand()` * is the instruction that defines `r1`. */ - final Instruction getResultAddress() { result = getResultAddressOperand().getDef() } + final Instruction getResultAddress() { result = this.getResultAddressOperand().getDef() } /** * Holds if the result of this instruction is precisely modeled in SSA. Always @@ -368,7 +375,7 @@ class Instruction extends Construction::TStageInstruction { */ final predicate isResultModeled() { // Register results are always in SSA form. - not hasMemoryResult() or + not this.hasMemoryResult() or Construction::hasModeledMemoryResult(this) } @@ -412,7 +419,7 @@ class Instruction extends Construction::TStageInstruction { /** * Gets all direct successors of this instruction. */ - final Instruction getASuccessor() { result = getSuccessor(_) } + final Instruction getASuccessor() { result = this.getSuccessor(_) } /** * Gets a predecessor of this instruction such that the predecessor reaches @@ -423,7 +430,7 @@ class Instruction extends Construction::TStageInstruction { /** * Gets all direct predecessors of this instruction. */ - final Instruction getAPredecessor() { result = getPredecessor(_) } + final Instruction getAPredecessor() { result = this.getPredecessor(_) } } /** @@ -543,7 +550,7 @@ class IndexedInstruction extends Instruction { * at this instruction. This instruction has no predecessors. */ class EnterFunctionInstruction extends Instruction { - EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction } + EnterFunctionInstruction() { this.getOpcode() instanceof Opcode::EnterFunction } } /** @@ -554,7 +561,7 @@ class EnterFunctionInstruction extends Instruction { * struct, or union, see `FieldAddressInstruction`. */ class VariableAddressInstruction extends VariableInstruction { - VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress } + VariableAddressInstruction() { this.getOpcode() instanceof Opcode::VariableAddress } } /** @@ -566,7 +573,7 @@ class VariableAddressInstruction extends VariableInstruction { * The result has an `IRFunctionAddress` type. */ class FunctionAddressInstruction extends FunctionInstruction { - FunctionAddressInstruction() { getOpcode() instanceof Opcode::FunctionAddress } + FunctionAddressInstruction() { this.getOpcode() instanceof Opcode::FunctionAddress } } /** @@ -577,7 +584,7 @@ class FunctionAddressInstruction extends FunctionInstruction { * initializes that parameter. */ class InitializeParameterInstruction extends VariableInstruction { - InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter } + InitializeParameterInstruction() { this.getOpcode() instanceof Opcode::InitializeParameter } /** * Gets the parameter initialized by this instruction. @@ -603,7 +610,7 @@ class InitializeParameterInstruction extends VariableInstruction { * initialized elsewhere, would not otherwise have a definition in this function. */ class InitializeNonLocalInstruction extends Instruction { - InitializeNonLocalInstruction() { getOpcode() instanceof Opcode::InitializeNonLocal } + InitializeNonLocalInstruction() { this.getOpcode() instanceof Opcode::InitializeNonLocal } } /** @@ -611,7 +618,7 @@ class InitializeNonLocalInstruction extends Instruction { * with the value of that memory on entry to the function. */ class InitializeIndirectionInstruction extends VariableInstruction { - InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection } + InitializeIndirectionInstruction() { this.getOpcode() instanceof Opcode::InitializeIndirection } /** * Gets the parameter initialized by this instruction. @@ -635,24 +642,24 @@ class InitializeIndirectionInstruction extends VariableInstruction { * An instruction that initializes the `this` pointer parameter of the enclosing function. */ class InitializeThisInstruction extends Instruction { - InitializeThisInstruction() { getOpcode() instanceof Opcode::InitializeThis } + InitializeThisInstruction() { this.getOpcode() instanceof Opcode::InitializeThis } } /** * An instruction that computes the address of a non-static field of an object. */ class FieldAddressInstruction extends FieldInstruction { - FieldAddressInstruction() { getOpcode() instanceof Opcode::FieldAddress } + FieldAddressInstruction() { this.getOpcode() instanceof Opcode::FieldAddress } /** * Gets the operand that provides the address of the object containing the field. */ - final UnaryOperand getObjectAddressOperand() { result = getAnOperand() } + final UnaryOperand getObjectAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the object containing the field. */ - final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() } + final Instruction getObjectAddress() { result = this.getObjectAddressOperand().getDef() } } /** @@ -661,17 +668,19 @@ class FieldAddressInstruction extends FieldInstruction { * This instruction is used for element access to C# arrays. */ class ElementsAddressInstruction extends UnaryInstruction { - ElementsAddressInstruction() { getOpcode() instanceof Opcode::ElementsAddress } + ElementsAddressInstruction() { this.getOpcode() instanceof Opcode::ElementsAddress } /** * Gets the operand that provides the address of the array object. */ - final UnaryOperand getArrayObjectAddressOperand() { result = getAnOperand() } + final UnaryOperand getArrayObjectAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the array object. */ - final Instruction getArrayObjectAddress() { result = getArrayObjectAddressOperand().getDef() } + final Instruction getArrayObjectAddress() { + result = this.getArrayObjectAddressOperand().getDef() + } } /** @@ -685,7 +694,7 @@ class ElementsAddressInstruction extends UnaryInstruction { * taken may want to ignore any function that contains an `ErrorInstruction`. */ class ErrorInstruction extends Instruction { - ErrorInstruction() { getOpcode() instanceof Opcode::Error } + ErrorInstruction() { this.getOpcode() instanceof Opcode::Error } } /** @@ -695,7 +704,7 @@ class ErrorInstruction extends Instruction { * an initializer, or whose initializer only partially initializes the variable. */ class UninitializedInstruction extends VariableInstruction { - UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized } + UninitializedInstruction() { this.getOpcode() instanceof Opcode::Uninitialized } /** * Gets the variable that is uninitialized. @@ -710,7 +719,7 @@ class UninitializedInstruction extends VariableInstruction { * least one instruction, even when the AST has no semantic effect. */ class NoOpInstruction extends Instruction { - NoOpInstruction() { getOpcode() instanceof Opcode::NoOp } + NoOpInstruction() { this.getOpcode() instanceof Opcode::NoOp } } /** @@ -732,32 +741,32 @@ class NoOpInstruction extends Instruction { * `void`-returning function. */ class ReturnInstruction extends Instruction { - ReturnInstruction() { getOpcode() instanceof ReturnOpcode } + ReturnInstruction() { this.getOpcode() instanceof ReturnOpcode } } /** * An instruction that returns control to the caller of the function, without returning a value. */ class ReturnVoidInstruction extends ReturnInstruction { - ReturnVoidInstruction() { getOpcode() instanceof Opcode::ReturnVoid } + ReturnVoidInstruction() { this.getOpcode() instanceof Opcode::ReturnVoid } } /** * An instruction that returns control to the caller of the function, including a return value. */ class ReturnValueInstruction extends ReturnInstruction { - ReturnValueInstruction() { getOpcode() instanceof Opcode::ReturnValue } + ReturnValueInstruction() { this.getOpcode() instanceof Opcode::ReturnValue } /** * Gets the operand that provides the value being returned by the function. */ - final LoadOperand getReturnValueOperand() { result = getAnOperand() } + final LoadOperand getReturnValueOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the value being returned by the function, if an * exact definition is available. */ - final Instruction getReturnValue() { result = getReturnValueOperand().getDef() } + final Instruction getReturnValue() { result = this.getReturnValueOperand().getDef() } } /** @@ -770,28 +779,28 @@ class ReturnValueInstruction extends ReturnInstruction { * that the caller initialized the memory pointed to by the parameter before the call. */ class ReturnIndirectionInstruction extends VariableInstruction { - ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } + ReturnIndirectionInstruction() { this.getOpcode() instanceof Opcode::ReturnIndirection } /** * Gets the operand that provides the value of the pointed-to memory. */ - final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + final SideEffectOperand getSideEffectOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the value of the pointed-to memory, if an exact * definition is available. */ - final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + final Instruction getSideEffect() { result = this.getSideEffectOperand().getDef() } /** * Gets the operand that provides the address of the pointed-to memory. */ - final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + final AddressOperand getSourceAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the pointed-to memory. */ - final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } + final Instruction getSourceAddress() { result = this.getSourceAddressOperand().getDef() } /** * Gets the parameter for which this instruction reads the final pointed-to value within the @@ -821,12 +830,12 @@ class ReturnIndirectionInstruction extends VariableInstruction { * * There are several different copy instructions, depending on the source and destination of the * copy operation: - * - `CopyInstruction` - Copies a register operand to a register result. + * - `CopyValueInstruction` - Copies a register operand to a register result. * - `LoadInstruction` - Copies a memory operand to a register result. * - `StoreInstruction` - Copies a register operand to a memory result. */ class CopyInstruction extends Instruction { - CopyInstruction() { getOpcode() instanceof CopyOpcode } + CopyInstruction() { this.getOpcode() instanceof CopyOpcode } /** * Gets the operand that provides the input value of the copy. @@ -837,16 +846,16 @@ class CopyInstruction extends Instruction { * Gets the instruction whose result provides the input value of the copy, if an exact definition * is available. */ - final Instruction getSourceValue() { result = getSourceValueOperand().getDef() } + final Instruction getSourceValue() { result = this.getSourceValueOperand().getDef() } } /** * An instruction that returns a register result containing a copy of its register operand. */ class CopyValueInstruction extends CopyInstruction, UnaryInstruction { - CopyValueInstruction() { getOpcode() instanceof Opcode::CopyValue } + CopyValueInstruction() { this.getOpcode() instanceof Opcode::CopyValue } - final override UnaryOperand getSourceValueOperand() { result = getAnOperand() } + final override UnaryOperand getSourceValueOperand() { result = this.getAnOperand() } } /** @@ -863,47 +872,49 @@ private string getAddressOperandDescription(AddressOperand operand) { * An instruction that returns a register result containing a copy of its memory operand. */ class LoadInstruction extends CopyInstruction { - LoadInstruction() { getOpcode() instanceof Opcode::Load } + LoadInstruction() { this.getOpcode() instanceof Opcode::Load } final override string getImmediateString() { - result = getAddressOperandDescription(getSourceAddressOperand()) + result = getAddressOperandDescription(this.getSourceAddressOperand()) } /** * Gets the operand that provides the address of the value being loaded. */ - final AddressOperand getSourceAddressOperand() { result = getAnOperand() } + final AddressOperand getSourceAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the value being loaded. */ - final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } + final Instruction getSourceAddress() { result = this.getSourceAddressOperand().getDef() } - final override LoadOperand getSourceValueOperand() { result = getAnOperand() } + final override LoadOperand getSourceValueOperand() { result = this.getAnOperand() } } /** * An instruction that returns a memory result containing a copy of its register operand. */ class StoreInstruction extends CopyInstruction { - StoreInstruction() { getOpcode() instanceof Opcode::Store } + StoreInstruction() { this.getOpcode() instanceof Opcode::Store } final override string getImmediateString() { - result = getAddressOperandDescription(getDestinationAddressOperand()) + result = getAddressOperandDescription(this.getDestinationAddressOperand()) } /** * Gets the operand that provides the address of the location to which the value will be stored. */ - final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + final AddressOperand getDestinationAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the location to which the value will * be stored, if an exact definition is available. */ - final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } + final Instruction getDestinationAddress() { + result = this.getDestinationAddressOperand().getDef() + } - final override StoreValueOperand getSourceValueOperand() { result = getAnOperand() } + final override StoreValueOperand getSourceValueOperand() { result = this.getAnOperand() } } /** @@ -911,27 +922,27 @@ class StoreInstruction extends CopyInstruction { * operand. */ class ConditionalBranchInstruction extends Instruction { - ConditionalBranchInstruction() { getOpcode() instanceof Opcode::ConditionalBranch } + ConditionalBranchInstruction() { this.getOpcode() instanceof Opcode::ConditionalBranch } /** * Gets the operand that provides the Boolean condition controlling the branch. */ - final ConditionOperand getConditionOperand() { result = getAnOperand() } + final ConditionOperand getConditionOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the Boolean condition controlling the branch. */ - final Instruction getCondition() { result = getConditionOperand().getDef() } + final Instruction getCondition() { result = this.getConditionOperand().getDef() } /** * Gets the instruction to which control will flow if the condition is true. */ - final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) } + final Instruction getTrueSuccessor() { result = this.getSuccessor(EdgeKind::trueEdge()) } /** * Gets the instruction to which control will flow if the condition is false. */ - final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) } + final Instruction getFalseSuccessor() { result = this.getSuccessor(EdgeKind::falseEdge()) } } /** @@ -943,14 +954,14 @@ class ConditionalBranchInstruction extends Instruction { * successors. */ class ExitFunctionInstruction extends Instruction { - ExitFunctionInstruction() { getOpcode() instanceof Opcode::ExitFunction } + ExitFunctionInstruction() { this.getOpcode() instanceof Opcode::ExitFunction } } /** * An instruction whose result is a constant value. */ class ConstantInstruction extends ConstantValueInstruction { - ConstantInstruction() { getOpcode() instanceof Opcode::Constant } + ConstantInstruction() { this.getOpcode() instanceof Opcode::Constant } } /** @@ -959,7 +970,7 @@ class ConstantInstruction extends ConstantValueInstruction { class IntegerConstantInstruction extends ConstantInstruction { IntegerConstantInstruction() { exists(IRType resultType | - resultType = getResultIRType() and + resultType = this.getResultIRType() and (resultType instanceof IRIntegerType or resultType instanceof IRBooleanType) ) } @@ -969,7 +980,7 @@ class IntegerConstantInstruction extends ConstantInstruction { * An instruction whose result is a constant value of floating-point type. */ class FloatConstantInstruction extends ConstantInstruction { - FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType } + FloatConstantInstruction() { this.getResultIRType() instanceof IRFloatingPointType } } /** @@ -978,7 +989,9 @@ class FloatConstantInstruction extends ConstantInstruction { class StringConstantInstruction extends VariableInstruction { override IRStringLiteral var; - final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) } + final override string getImmediateString() { + result = Language::getStringLiteralText(this.getValue()) + } /** * Gets the string literal whose address is returned by this instruction. @@ -990,37 +1003,37 @@ class StringConstantInstruction extends VariableInstruction { * An instruction whose result is computed from two operands. */ class BinaryInstruction extends Instruction { - BinaryInstruction() { getOpcode() instanceof BinaryOpcode } + BinaryInstruction() { this.getOpcode() instanceof BinaryOpcode } /** * Gets the left operand of this binary instruction. */ - final LeftOperand getLeftOperand() { result = getAnOperand() } + final LeftOperand getLeftOperand() { result = this.getAnOperand() } /** * Gets the right operand of this binary instruction. */ - final RightOperand getRightOperand() { result = getAnOperand() } + final RightOperand getRightOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the value of the left operand of this binary * instruction. */ - final Instruction getLeft() { result = getLeftOperand().getDef() } + final Instruction getLeft() { result = this.getLeftOperand().getDef() } /** * Gets the instruction whose result provides the value of the right operand of this binary * instruction. */ - final Instruction getRight() { result = getRightOperand().getDef() } + final Instruction getRight() { result = this.getRightOperand().getDef() } /** * Holds if this instruction's operands are `op1` and `op2`, in either order. */ final predicate hasOperands(Operand op1, Operand op2) { - op1 = getLeftOperand() and op2 = getRightOperand() + op1 = this.getLeftOperand() and op2 = this.getRightOperand() or - op1 = getRightOperand() and op2 = getLeftOperand() + op1 = this.getRightOperand() and op2 = this.getLeftOperand() } } @@ -1028,7 +1041,7 @@ class BinaryInstruction extends Instruction { * An instruction that computes the result of an arithmetic operation. */ class ArithmeticInstruction extends Instruction { - ArithmeticInstruction() { getOpcode() instanceof ArithmeticOpcode } + ArithmeticInstruction() { this.getOpcode() instanceof ArithmeticOpcode } } /** @@ -1050,7 +1063,7 @@ class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction * performed according to IEEE-754. */ class AddInstruction extends BinaryArithmeticInstruction { - AddInstruction() { getOpcode() instanceof Opcode::Add } + AddInstruction() { this.getOpcode() instanceof Opcode::Add } } /** @@ -1061,7 +1074,7 @@ class AddInstruction extends BinaryArithmeticInstruction { * according to IEEE-754. */ class SubInstruction extends BinaryArithmeticInstruction { - SubInstruction() { getOpcode() instanceof Opcode::Sub } + SubInstruction() { this.getOpcode() instanceof Opcode::Sub } } /** @@ -1072,7 +1085,7 @@ class SubInstruction extends BinaryArithmeticInstruction { * performed according to IEEE-754. */ class MulInstruction extends BinaryArithmeticInstruction { - MulInstruction() { getOpcode() instanceof Opcode::Mul } + MulInstruction() { this.getOpcode() instanceof Opcode::Mul } } /** @@ -1083,7 +1096,7 @@ class MulInstruction extends BinaryArithmeticInstruction { * to IEEE-754. */ class DivInstruction extends BinaryArithmeticInstruction { - DivInstruction() { getOpcode() instanceof Opcode::Div } + DivInstruction() { this.getOpcode() instanceof Opcode::Div } } /** @@ -1093,7 +1106,7 @@ class DivInstruction extends BinaryArithmeticInstruction { * division by zero or integer overflow is undefined. */ class RemInstruction extends BinaryArithmeticInstruction { - RemInstruction() { getOpcode() instanceof Opcode::Rem } + RemInstruction() { this.getOpcode() instanceof Opcode::Rem } } /** @@ -1104,14 +1117,14 @@ class RemInstruction extends BinaryArithmeticInstruction { * is performed according to IEEE-754. */ class NegateInstruction extends UnaryArithmeticInstruction { - NegateInstruction() { getOpcode() instanceof Opcode::Negate } + NegateInstruction() { this.getOpcode() instanceof Opcode::Negate } } /** * An instruction that computes the result of a bitwise operation. */ class BitwiseInstruction extends Instruction { - BitwiseInstruction() { getOpcode() instanceof BitwiseOpcode } + BitwiseInstruction() { this.getOpcode() instanceof BitwiseOpcode } } /** @@ -1130,7 +1143,7 @@ class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { } * Both operands must have the same integer type, which will also be the result type. */ class BitAndInstruction extends BinaryBitwiseInstruction { - BitAndInstruction() { getOpcode() instanceof Opcode::BitAnd } + BitAndInstruction() { this.getOpcode() instanceof Opcode::BitAnd } } /** @@ -1139,7 +1152,7 @@ class BitAndInstruction extends BinaryBitwiseInstruction { * Both operands must have the same integer type, which will also be the result type. */ class BitOrInstruction extends BinaryBitwiseInstruction { - BitOrInstruction() { getOpcode() instanceof Opcode::BitOr } + BitOrInstruction() { this.getOpcode() instanceof Opcode::BitOr } } /** @@ -1148,7 +1161,7 @@ class BitOrInstruction extends BinaryBitwiseInstruction { * Both operands must have the same integer type, which will also be the result type. */ class BitXorInstruction extends BinaryBitwiseInstruction { - BitXorInstruction() { getOpcode() instanceof Opcode::BitXor } + BitXorInstruction() { this.getOpcode() instanceof Opcode::BitXor } } /** @@ -1159,7 +1172,7 @@ class BitXorInstruction extends BinaryBitwiseInstruction { * rightmost bits are zero-filled. */ class ShiftLeftInstruction extends BinaryBitwiseInstruction { - ShiftLeftInstruction() { getOpcode() instanceof Opcode::ShiftLeft } + ShiftLeftInstruction() { this.getOpcode() instanceof Opcode::ShiftLeft } } /** @@ -1172,7 +1185,7 @@ class ShiftLeftInstruction extends BinaryBitwiseInstruction { * of the left operand. */ class ShiftRightInstruction extends BinaryBitwiseInstruction { - ShiftRightInstruction() { getOpcode() instanceof Opcode::ShiftRight } + ShiftRightInstruction() { this.getOpcode() instanceof Opcode::ShiftRight } } /** @@ -1183,7 +1196,7 @@ class PointerArithmeticInstruction extends BinaryInstruction { int elementSize; PointerArithmeticInstruction() { - getOpcode() instanceof PointerArithmeticOpcode and + this.getOpcode() instanceof PointerArithmeticOpcode and elementSize = Raw::getInstructionElementSize(this) } @@ -1206,7 +1219,7 @@ class PointerArithmeticInstruction extends BinaryInstruction { * An instruction that adds or subtracts an integer offset from a pointer. */ class PointerOffsetInstruction extends PointerArithmeticInstruction { - PointerOffsetInstruction() { getOpcode() instanceof PointerOffsetOpcode } + PointerOffsetInstruction() { this.getOpcode() instanceof PointerOffsetOpcode } } /** @@ -1217,7 +1230,7 @@ class PointerOffsetInstruction extends PointerArithmeticInstruction { * overflow is undefined. */ class PointerAddInstruction extends PointerOffsetInstruction { - PointerAddInstruction() { getOpcode() instanceof Opcode::PointerAdd } + PointerAddInstruction() { this.getOpcode() instanceof Opcode::PointerAdd } } /** @@ -1228,7 +1241,7 @@ class PointerAddInstruction extends PointerOffsetInstruction { * pointer underflow is undefined. */ class PointerSubInstruction extends PointerOffsetInstruction { - PointerSubInstruction() { getOpcode() instanceof Opcode::PointerSub } + PointerSubInstruction() { this.getOpcode() instanceof Opcode::PointerSub } } /** @@ -1241,31 +1254,31 @@ class PointerSubInstruction extends PointerOffsetInstruction { * undefined. */ class PointerDiffInstruction extends PointerArithmeticInstruction { - PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff } + PointerDiffInstruction() { this.getOpcode() instanceof Opcode::PointerDiff } } /** * An instruction whose result is computed from a single operand. */ class UnaryInstruction extends Instruction { - UnaryInstruction() { getOpcode() instanceof UnaryOpcode } + UnaryInstruction() { this.getOpcode() instanceof UnaryOpcode } /** * Gets the sole operand of this instruction. */ - final UnaryOperand getUnaryOperand() { result = getAnOperand() } + final UnaryOperand getUnaryOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the sole operand of this instruction. */ - final Instruction getUnary() { result = getUnaryOperand().getDef() } + final Instruction getUnary() { result = this.getUnaryOperand().getDef() } } /** * An instruction that converts the value of its operand to a value of a different type. */ class ConvertInstruction extends UnaryInstruction { - ConvertInstruction() { getOpcode() instanceof Opcode::Convert } + ConvertInstruction() { this.getOpcode() instanceof Opcode::Convert } } /** @@ -1279,7 +1292,7 @@ class ConvertInstruction extends UnaryInstruction { * `as` expression. */ class CheckedConvertOrNullInstruction extends UnaryInstruction { - CheckedConvertOrNullInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrNull } + CheckedConvertOrNullInstruction() { this.getOpcode() instanceof Opcode::CheckedConvertOrNull } } /** @@ -1293,7 +1306,7 @@ class CheckedConvertOrNullInstruction extends UnaryInstruction { * expression. */ class CheckedConvertOrThrowInstruction extends UnaryInstruction { - CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow } + CheckedConvertOrThrowInstruction() { this.getOpcode() instanceof Opcode::CheckedConvertOrThrow } } /** @@ -1306,7 +1319,7 @@ class CheckedConvertOrThrowInstruction extends UnaryInstruction { * the most-derived object. */ class CompleteObjectAddressInstruction extends UnaryInstruction { - CompleteObjectAddressInstruction() { getOpcode() instanceof Opcode::CompleteObjectAddress } + CompleteObjectAddressInstruction() { this.getOpcode() instanceof Opcode::CompleteObjectAddress } } /** @@ -1351,7 +1364,7 @@ class InheritanceConversionInstruction extends UnaryInstruction { * An instruction that converts from the address of a derived class to the address of a base class. */ class ConvertToBaseInstruction extends InheritanceConversionInstruction { - ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode } + ConvertToBaseInstruction() { this.getOpcode() instanceof ConvertToBaseOpcode } } /** @@ -1361,7 +1374,9 @@ class ConvertToBaseInstruction extends InheritanceConversionInstruction { * If the operand holds a null address, the result is a null address. */ class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { - ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase } + ConvertToNonVirtualBaseInstruction() { + this.getOpcode() instanceof Opcode::ConvertToNonVirtualBase + } } /** @@ -1371,7 +1386,7 @@ class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction { * If the operand holds a null address, the result is a null address. */ class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { - ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase } + ConvertToVirtualBaseInstruction() { this.getOpcode() instanceof Opcode::ConvertToVirtualBase } } /** @@ -1381,7 +1396,7 @@ class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction { * If the operand holds a null address, the result is a null address. */ class ConvertToDerivedInstruction extends InheritanceConversionInstruction { - ConvertToDerivedInstruction() { getOpcode() instanceof Opcode::ConvertToDerived } + ConvertToDerivedInstruction() { this.getOpcode() instanceof Opcode::ConvertToDerived } } /** @@ -1390,7 +1405,7 @@ class ConvertToDerivedInstruction extends InheritanceConversionInstruction { * The operand must have an integer type, which will also be the result type. */ class BitComplementInstruction extends UnaryBitwiseInstruction { - BitComplementInstruction() { getOpcode() instanceof Opcode::BitComplement } + BitComplementInstruction() { this.getOpcode() instanceof Opcode::BitComplement } } /** @@ -1399,14 +1414,14 @@ class BitComplementInstruction extends UnaryBitwiseInstruction { * The operand must have a Boolean type, which will also be the result type. */ class LogicalNotInstruction extends UnaryInstruction { - LogicalNotInstruction() { getOpcode() instanceof Opcode::LogicalNot } + LogicalNotInstruction() { this.getOpcode() instanceof Opcode::LogicalNot } } /** * An instruction that compares two numeric operands. */ class CompareInstruction extends BinaryInstruction { - CompareInstruction() { getOpcode() instanceof CompareOpcode } + CompareInstruction() { this.getOpcode() instanceof CompareOpcode } } /** @@ -1417,7 +1432,7 @@ class CompareInstruction extends BinaryInstruction { * unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareEQInstruction extends CompareInstruction { - CompareEQInstruction() { getOpcode() instanceof Opcode::CompareEQ } + CompareEQInstruction() { this.getOpcode() instanceof Opcode::CompareEQ } } /** @@ -1428,14 +1443,14 @@ class CompareEQInstruction extends CompareInstruction { * `left == right`. Floating-point comparison is performed according to IEEE-754. */ class CompareNEInstruction extends CompareInstruction { - CompareNEInstruction() { getOpcode() instanceof Opcode::CompareNE } + CompareNEInstruction() { this.getOpcode() instanceof Opcode::CompareNE } } /** * An instruction that does a relative comparison of two values, such as `<` or `>=`. */ class RelationalInstruction extends CompareInstruction { - RelationalInstruction() { getOpcode() instanceof RelationalOpcode } + RelationalInstruction() { this.getOpcode() instanceof RelationalOpcode } /** * Gets the operand on the "greater" (or "greater-or-equal") side @@ -1467,11 +1482,11 @@ class RelationalInstruction extends CompareInstruction { * are unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareLTInstruction extends RelationalInstruction { - CompareLTInstruction() { getOpcode() instanceof Opcode::CompareLT } + CompareLTInstruction() { this.getOpcode() instanceof Opcode::CompareLT } - override Instruction getLesser() { result = getLeft() } + override Instruction getLesser() { result = this.getLeft() } - override Instruction getGreater() { result = getRight() } + override Instruction getGreater() { result = this.getRight() } override predicate isStrict() { any() } } @@ -1484,11 +1499,11 @@ class CompareLTInstruction extends RelationalInstruction { * are unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareGTInstruction extends RelationalInstruction { - CompareGTInstruction() { getOpcode() instanceof Opcode::CompareGT } + CompareGTInstruction() { this.getOpcode() instanceof Opcode::CompareGT } - override Instruction getLesser() { result = getRight() } + override Instruction getLesser() { result = this.getRight() } - override Instruction getGreater() { result = getLeft() } + override Instruction getGreater() { result = this.getLeft() } override predicate isStrict() { any() } } @@ -1502,11 +1517,11 @@ class CompareGTInstruction extends RelationalInstruction { * are unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareLEInstruction extends RelationalInstruction { - CompareLEInstruction() { getOpcode() instanceof Opcode::CompareLE } + CompareLEInstruction() { this.getOpcode() instanceof Opcode::CompareLE } - override Instruction getLesser() { result = getLeft() } + override Instruction getLesser() { result = this.getLeft() } - override Instruction getGreater() { result = getRight() } + override Instruction getGreater() { result = this.getRight() } override predicate isStrict() { none() } } @@ -1520,11 +1535,11 @@ class CompareLEInstruction extends RelationalInstruction { * are unordered. Floating-point comparison is performed according to IEEE-754. */ class CompareGEInstruction extends RelationalInstruction { - CompareGEInstruction() { getOpcode() instanceof Opcode::CompareGE } + CompareGEInstruction() { this.getOpcode() instanceof Opcode::CompareGE } - override Instruction getLesser() { result = getRight() } + override Instruction getLesser() { result = this.getRight() } - override Instruction getGreater() { result = getLeft() } + override Instruction getGreater() { result = this.getLeft() } override predicate isStrict() { none() } } @@ -1543,78 +1558,78 @@ class CompareGEInstruction extends RelationalInstruction { * of any case edge. */ class SwitchInstruction extends Instruction { - SwitchInstruction() { getOpcode() instanceof Opcode::Switch } + SwitchInstruction() { this.getOpcode() instanceof Opcode::Switch } /** Gets the operand that provides the integer value controlling the switch. */ - final ConditionOperand getExpressionOperand() { result = getAnOperand() } + final ConditionOperand getExpressionOperand() { result = this.getAnOperand() } /** Gets the instruction whose result provides the integer value controlling the switch. */ - final Instruction getExpression() { result = getExpressionOperand().getDef() } + final Instruction getExpression() { result = this.getExpressionOperand().getDef() } /** Gets the successor instructions along the case edges of the switch. */ - final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) } + final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = this.getSuccessor(edge)) } /** Gets the successor instruction along the default edge of the switch, if any. */ - final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) } + final Instruction getDefaultSuccessor() { result = this.getSuccessor(EdgeKind::defaultEdge()) } } /** * An instruction that calls a function. */ class CallInstruction extends Instruction { - CallInstruction() { getOpcode() instanceof Opcode::Call } + CallInstruction() { this.getOpcode() instanceof Opcode::Call } final override string getImmediateString() { - result = getStaticCallTarget().toString() + result = this.getStaticCallTarget().toString() or - not exists(getStaticCallTarget()) and result = "?" + not exists(this.getStaticCallTarget()) and result = "?" } /** * Gets the operand the specifies the target function of the call. */ - final CallTargetOperand getCallTargetOperand() { result = getAnOperand() } + final CallTargetOperand getCallTargetOperand() { result = this.getAnOperand() } /** * Gets the `Instruction` that computes the target function of the call. This is usually a * `FunctionAddress` instruction, but can also be an arbitrary instruction that produces a * function pointer. */ - final Instruction getCallTarget() { result = getCallTargetOperand().getDef() } + final Instruction getCallTarget() { result = this.getCallTargetOperand().getDef() } /** * Gets all of the argument operands of the call, including the `this` pointer, if any. */ - final ArgumentOperand getAnArgumentOperand() { result = getAnOperand() } + final ArgumentOperand getAnArgumentOperand() { result = this.getAnOperand() } /** * Gets the `Function` that the call targets, if this is statically known. */ final Language::Function getStaticCallTarget() { - result = getCallTarget().(FunctionAddressInstruction).getFunctionSymbol() + result = this.getCallTarget().(FunctionAddressInstruction).getFunctionSymbol() } /** * Gets all of the arguments of the call, including the `this` pointer, if any. */ - final Instruction getAnArgument() { result = getAnArgumentOperand().getDef() } + final Instruction getAnArgument() { result = this.getAnArgumentOperand().getDef() } /** * Gets the `this` pointer argument operand of the call, if any. */ - final ThisArgumentOperand getThisArgumentOperand() { result = getAnOperand() } + final ThisArgumentOperand getThisArgumentOperand() { result = this.getAnOperand() } /** * Gets the `this` pointer argument of the call, if any. */ - final Instruction getThisArgument() { result = getThisArgumentOperand().getDef() } + final Instruction getThisArgument() { result = this.getThisArgumentOperand().getDef() } /** * Gets the argument operand at the specified index. */ pragma[noinline] final PositionalArgumentOperand getPositionalArgumentOperand(int index) { - result = getAnOperand() and + result = this.getAnOperand() and result.getIndex() = index } @@ -1623,7 +1638,7 @@ class CallInstruction extends Instruction { */ pragma[noinline] final Instruction getPositionalArgument(int index) { - result = getPositionalArgumentOperand(index).getDef() + result = this.getPositionalArgumentOperand(index).getDef() } /** @@ -1631,16 +1646,16 @@ class CallInstruction extends Instruction { */ pragma[noinline] final ArgumentOperand getArgumentOperand(int index) { - index >= 0 and result = getPositionalArgumentOperand(index) + index >= 0 and result = this.getPositionalArgumentOperand(index) or - index = -1 and result = getThisArgumentOperand() + index = -1 and result = this.getThisArgumentOperand() } /** * Gets the argument at the specified index, or `this` if `index` is `-1`. */ pragma[noinline] - final Instruction getArgument(int index) { result = getArgumentOperand(index).getDef() } + final Instruction getArgument(int index) { result = this.getArgumentOperand(index).getDef() } /** * Gets the number of arguments of the call, including the `this` pointer, if any. @@ -1665,7 +1680,7 @@ class CallInstruction extends Instruction { * An instruction representing a side effect of a function call. */ class SideEffectInstruction extends Instruction { - SideEffectInstruction() { getOpcode() instanceof SideEffectOpcode } + SideEffectInstruction() { this.getOpcode() instanceof SideEffectOpcode } /** * Gets the instruction whose execution causes this side effect. @@ -1680,7 +1695,7 @@ class SideEffectInstruction extends Instruction { * accessed by that call. */ class CallSideEffectInstruction extends SideEffectInstruction { - CallSideEffectInstruction() { getOpcode() instanceof Opcode::CallSideEffect } + CallSideEffectInstruction() { this.getOpcode() instanceof Opcode::CallSideEffect } } /** @@ -1691,7 +1706,7 @@ class CallSideEffectInstruction extends SideEffectInstruction { * call target cannot write to escaped memory. */ class CallReadSideEffectInstruction extends SideEffectInstruction { - CallReadSideEffectInstruction() { getOpcode() instanceof Opcode::CallReadSideEffect } + CallReadSideEffectInstruction() { this.getOpcode() instanceof Opcode::CallReadSideEffect } } /** @@ -1699,33 +1714,33 @@ class CallReadSideEffectInstruction extends SideEffectInstruction { * specific parameter. */ class ReadSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { - ReadSideEffectInstruction() { getOpcode() instanceof ReadSideEffectOpcode } + ReadSideEffectInstruction() { this.getOpcode() instanceof ReadSideEffectOpcode } /** Gets the operand for the value that will be read from this instruction, if known. */ - final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } + final SideEffectOperand getSideEffectOperand() { result = this.getAnOperand() } /** Gets the value that will be read from this instruction, if known. */ - final Instruction getSideEffect() { result = getSideEffectOperand().getDef() } + final Instruction getSideEffect() { result = this.getSideEffectOperand().getDef() } /** Gets the operand for the address from which this instruction may read. */ - final AddressOperand getArgumentOperand() { result = getAnOperand() } + final AddressOperand getArgumentOperand() { result = this.getAnOperand() } /** Gets the address from which this instruction may read. */ - final Instruction getArgumentDef() { result = getArgumentOperand().getDef() } + final Instruction getArgumentDef() { result = this.getArgumentOperand().getDef() } } /** * An instruction representing the read of an indirect parameter within a function call. */ class IndirectReadSideEffectInstruction extends ReadSideEffectInstruction { - IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect } + IndirectReadSideEffectInstruction() { this.getOpcode() instanceof Opcode::IndirectReadSideEffect } } /** * An instruction representing the read of an indirect buffer parameter within a function call. */ class BufferReadSideEffectInstruction extends ReadSideEffectInstruction { - BufferReadSideEffectInstruction() { getOpcode() instanceof Opcode::BufferReadSideEffect } + BufferReadSideEffectInstruction() { this.getOpcode() instanceof Opcode::BufferReadSideEffect } } /** @@ -1733,18 +1748,18 @@ class BufferReadSideEffectInstruction extends ReadSideEffectInstruction { */ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { SizedBufferReadSideEffectInstruction() { - getOpcode() instanceof Opcode::SizedBufferReadSideEffect + this.getOpcode() instanceof Opcode::SizedBufferReadSideEffect } /** * Gets the operand that holds the number of bytes read from the buffer. */ - final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + final BufferSizeOperand getBufferSizeOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the number of bytes read from the buffer. */ - final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } + final Instruction getBufferSize() { result = this.getBufferSizeOperand().getDef() } } /** @@ -1752,17 +1767,17 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction { * specific parameter. */ class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstruction { - WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode } + WriteSideEffectInstruction() { this.getOpcode() instanceof WriteSideEffectOpcode } /** * Get the operand that holds the address of the memory to be written. */ - final AddressOperand getDestinationAddressOperand() { result = getAnOperand() } + final AddressOperand getDestinationAddressOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the address of the memory to be written. */ - Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() } + Instruction getDestinationAddress() { result = this.getDestinationAddressOperand().getDef() } } /** @@ -1770,7 +1785,7 @@ class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstructi */ class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction { IndirectMustWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::IndirectMustWriteSideEffect + this.getOpcode() instanceof Opcode::IndirectMustWriteSideEffect } } @@ -1780,7 +1795,7 @@ class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction */ class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { BufferMustWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::BufferMustWriteSideEffect + this.getOpcode() instanceof Opcode::BufferMustWriteSideEffect } } @@ -1790,18 +1805,18 @@ class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { */ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction { SizedBufferMustWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect + this.getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect } /** * Gets the operand that holds the number of bytes written to the buffer. */ - final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + final BufferSizeOperand getBufferSizeOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the number of bytes written to the buffer. */ - final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } + final Instruction getBufferSize() { result = this.getBufferSizeOperand().getDef() } } /** @@ -1812,7 +1827,7 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi */ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { IndirectMayWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::IndirectMayWriteSideEffect + this.getOpcode() instanceof Opcode::IndirectMayWriteSideEffect } } @@ -1822,7 +1837,9 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction { * Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten. */ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { - BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect } + BufferMayWriteSideEffectInstruction() { + this.getOpcode() instanceof Opcode::BufferMayWriteSideEffect + } } /** @@ -1832,18 +1849,18 @@ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { */ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction { SizedBufferMayWriteSideEffectInstruction() { - getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect + this.getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect } /** * Gets the operand that holds the number of bytes written to the buffer. */ - final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() } + final BufferSizeOperand getBufferSizeOperand() { result = this.getAnOperand() } /** * Gets the instruction whose result provides the number of bytes written to the buffer. */ - final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() } + final Instruction getBufferSize() { result = this.getBufferSizeOperand().getDef() } } /** @@ -1852,80 +1869,80 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio */ class InitializeDynamicAllocationInstruction extends SideEffectInstruction { InitializeDynamicAllocationInstruction() { - getOpcode() instanceof Opcode::InitializeDynamicAllocation + this.getOpcode() instanceof Opcode::InitializeDynamicAllocation } /** * Gets the operand that represents the address of the allocation this instruction is initializing. */ - final AddressOperand getAllocationAddressOperand() { result = getAnOperand() } + final AddressOperand getAllocationAddressOperand() { result = this.getAnOperand() } /** * Gets the address for the allocation this instruction is initializing. */ - final Instruction getAllocationAddress() { result = getAllocationAddressOperand().getDef() } + final Instruction getAllocationAddress() { result = this.getAllocationAddressOperand().getDef() } } /** * An instruction representing a GNU or MSVC inline assembly statement. */ class InlineAsmInstruction extends Instruction { - InlineAsmInstruction() { getOpcode() instanceof Opcode::InlineAsm } + InlineAsmInstruction() { this.getOpcode() instanceof Opcode::InlineAsm } } /** * An instruction that throws an exception. */ class ThrowInstruction extends Instruction { - ThrowInstruction() { getOpcode() instanceof ThrowOpcode } + ThrowInstruction() { this.getOpcode() instanceof ThrowOpcode } } /** * An instruction that throws a new exception. */ class ThrowValueInstruction extends ThrowInstruction { - ThrowValueInstruction() { getOpcode() instanceof Opcode::ThrowValue } + ThrowValueInstruction() { this.getOpcode() instanceof Opcode::ThrowValue } /** * Gets the address operand of the exception thrown by this instruction. */ - final AddressOperand getExceptionAddressOperand() { result = getAnOperand() } + final AddressOperand getExceptionAddressOperand() { result = this.getAnOperand() } /** * Gets the address of the exception thrown by this instruction. */ - final Instruction getExceptionAddress() { result = getExceptionAddressOperand().getDef() } + final Instruction getExceptionAddress() { result = this.getExceptionAddressOperand().getDef() } /** * Gets the operand for the exception thrown by this instruction. */ - final LoadOperand getExceptionOperand() { result = getAnOperand() } + final LoadOperand getExceptionOperand() { result = this.getAnOperand() } /** * Gets the exception thrown by this instruction. */ - final Instruction getException() { result = getExceptionOperand().getDef() } + final Instruction getException() { result = this.getExceptionOperand().getDef() } } /** * An instruction that re-throws the current exception. */ class ReThrowInstruction extends ThrowInstruction { - ReThrowInstruction() { getOpcode() instanceof Opcode::ReThrow } + ReThrowInstruction() { this.getOpcode() instanceof Opcode::ReThrow } } /** * An instruction that exits the current function by propagating an exception. */ class UnwindInstruction extends Instruction { - UnwindInstruction() { getOpcode() instanceof Opcode::Unwind } + UnwindInstruction() { this.getOpcode() instanceof Opcode::Unwind } } /** * An instruction that starts a `catch` handler. */ class CatchInstruction extends Instruction { - CatchInstruction() { getOpcode() instanceof CatchOpcode } + CatchInstruction() { this.getOpcode() instanceof CatchOpcode } } /** @@ -1935,7 +1952,7 @@ class CatchByTypeInstruction extends CatchInstruction { Language::LanguageType exceptionType; CatchByTypeInstruction() { - getOpcode() instanceof Opcode::CatchByType and + this.getOpcode() instanceof Opcode::CatchByType and exceptionType = Raw::getInstructionExceptionType(this) } @@ -1951,21 +1968,21 @@ class CatchByTypeInstruction extends CatchInstruction { * An instruction that catches any exception. */ class CatchAnyInstruction extends CatchInstruction { - CatchAnyInstruction() { getOpcode() instanceof Opcode::CatchAny } + CatchAnyInstruction() { this.getOpcode() instanceof Opcode::CatchAny } } /** * An instruction that initializes all escaped memory. */ class AliasedDefinitionInstruction extends Instruction { - AliasedDefinitionInstruction() { getOpcode() instanceof Opcode::AliasedDefinition } + AliasedDefinitionInstruction() { this.getOpcode() instanceof Opcode::AliasedDefinition } } /** * An instruction that consumes all escaped memory on exit from the function. */ class AliasedUseInstruction extends Instruction { - AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse } + AliasedUseInstruction() { this.getOpcode() instanceof Opcode::AliasedUse } } /** @@ -1979,7 +1996,7 @@ class AliasedUseInstruction extends Instruction { * runtime. */ class PhiInstruction extends Instruction { - PhiInstruction() { getOpcode() instanceof Opcode::Phi } + PhiInstruction() { this.getOpcode() instanceof Opcode::Phi } /** * Gets all of the instruction's `PhiInputOperand`s, representing the values that flow from each predecessor block. @@ -2047,29 +2064,29 @@ class PhiInstruction extends Instruction { * https://link.springer.com/content/pdf/10.1007%2F3-540-61053-7_66.pdf. */ class ChiInstruction extends Instruction { - ChiInstruction() { getOpcode() instanceof Opcode::Chi } + ChiInstruction() { this.getOpcode() instanceof Opcode::Chi } /** * Gets the operand that represents the previous state of all memory that might be aliased by the * memory write. */ - final ChiTotalOperand getTotalOperand() { result = getAnOperand() } + final ChiTotalOperand getTotalOperand() { result = this.getAnOperand() } /** * Gets the operand that represents the previous state of all memory that might be aliased by the * memory write. */ - final Instruction getTotal() { result = getTotalOperand().getDef() } + final Instruction getTotal() { result = this.getTotalOperand().getDef() } /** * Gets the operand that represents the new value written by the memory write. */ - final ChiPartialOperand getPartialOperand() { result = getAnOperand() } + final ChiPartialOperand getPartialOperand() { result = this.getAnOperand() } /** * Gets the operand that represents the new value written by the memory write. */ - final Instruction getPartial() { result = getPartialOperand().getDef() } + final Instruction getPartial() { result = this.getPartialOperand().getDef() } /** * Gets the bit range `[startBit, endBit)` updated by the partial operand of this `ChiInstruction`, relative to the start address of the total operand. @@ -2093,7 +2110,7 @@ class ChiInstruction extends Instruction { * or `Switch` instruction where that particular edge is infeasible. */ class UnreachedInstruction extends Instruction { - UnreachedInstruction() { getOpcode() instanceof Opcode::Unreached } + UnreachedInstruction() { this.getOpcode() instanceof Opcode::Unreached } } /** @@ -2106,7 +2123,7 @@ class BuiltInOperationInstruction extends Instruction { Language::BuiltInOperation operation; BuiltInOperationInstruction() { - getOpcode() instanceof BuiltInOperationOpcode and + this.getOpcode() instanceof BuiltInOperationOpcode and operation = Raw::getInstructionBuiltInOperation(this) } @@ -2122,9 +2139,9 @@ class BuiltInOperationInstruction extends Instruction { * actual operation is specified by the `getBuiltInOperation()` predicate. */ class BuiltInInstruction extends BuiltInOperationInstruction { - BuiltInInstruction() { getOpcode() instanceof Opcode::BuiltIn } + BuiltInInstruction() { this.getOpcode() instanceof Opcode::BuiltIn } - final override string getImmediateString() { result = getBuiltInOperation().toString() } + final override string getImmediateString() { result = this.getBuiltInOperation().toString() } } /** @@ -2135,7 +2152,7 @@ class BuiltInInstruction extends BuiltInOperationInstruction { * to the `...` parameter. */ class VarArgsStartInstruction extends UnaryInstruction { - VarArgsStartInstruction() { getOpcode() instanceof Opcode::VarArgsStart } + VarArgsStartInstruction() { this.getOpcode() instanceof Opcode::VarArgsStart } } /** @@ -2145,7 +2162,7 @@ class VarArgsStartInstruction extends UnaryInstruction { * a result. */ class VarArgsEndInstruction extends UnaryInstruction { - VarArgsEndInstruction() { getOpcode() instanceof Opcode::VarArgsEnd } + VarArgsEndInstruction() { this.getOpcode() instanceof Opcode::VarArgsEnd } } /** @@ -2155,7 +2172,7 @@ class VarArgsEndInstruction extends UnaryInstruction { * argument. */ class VarArgInstruction extends UnaryInstruction { - VarArgInstruction() { getOpcode() instanceof Opcode::VarArg } + VarArgInstruction() { this.getOpcode() instanceof Opcode::VarArg } } /** @@ -2166,7 +2183,7 @@ class VarArgInstruction extends UnaryInstruction { * argument of the `...` parameter. */ class NextVarArgInstruction extends UnaryInstruction { - NextVarArgInstruction() { getOpcode() instanceof Opcode::NextVarArg } + NextVarArgInstruction() { this.getOpcode() instanceof Opcode::NextVarArg } } /** @@ -2180,5 +2197,5 @@ class NextVarArgInstruction extends UnaryInstruction { * The result is the address of the newly allocated object. */ class NewObjInstruction extends Instruction { - NewObjInstruction() { getOpcode() instanceof Opcode::NewObj } + NewObjInstruction() { this.getOpcode() instanceof Opcode::NewObj } } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll index d7cf89ca9aa..85d217bd361 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll @@ -46,12 +46,12 @@ class Operand extends TStageOperand { /** * Gets the location of the source code for this operand. */ - final Language::Location getLocation() { result = getUse().getLocation() } + final Language::Location getLocation() { result = this.getUse().getLocation() } /** * Gets the function that contains this operand. */ - final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() } + final IRFunction getEnclosingIRFunction() { result = this.getUse().getEnclosingIRFunction() } /** * Gets the `Instruction` that consumes this operand. @@ -74,7 +74,7 @@ class Operand extends TStageOperand { */ final Instruction getDef() { result = this.getAnyDef() and - getDefinitionOverlap() instanceof MustExactlyOverlap + this.getDefinitionOverlap() instanceof MustExactlyOverlap } /** @@ -82,7 +82,7 @@ class Operand extends TStageOperand { * * Gets the `Instruction` that consumes this operand. */ - deprecated final Instruction getUseInstruction() { result = getUse() } + deprecated final Instruction getUseInstruction() { result = this.getUse() } /** * DEPRECATED: use `getAnyDef` or `getDef`. The exact replacement for this @@ -91,7 +91,7 @@ class Operand extends TStageOperand { * * Gets the `Instruction` whose result is the value of the operand. */ - deprecated final Instruction getDefinitionInstruction() { result = getAnyDef() } + deprecated final Instruction getDefinitionInstruction() { result = this.getAnyDef() } /** * Gets the overlap relationship between the operand's definition and its use. @@ -101,7 +101,9 @@ class Operand extends TStageOperand { /** * Holds if the result of the definition instruction does not exactly overlap this use. */ - final predicate isDefinitionInexact() { not getDefinitionOverlap() instanceof MustExactlyOverlap } + final predicate isDefinitionInexact() { + not this.getDefinitionOverlap() instanceof MustExactlyOverlap + } /** * Gets a prefix to use when dumping the operand in an operand list. @@ -121,7 +123,7 @@ class Operand extends TStageOperand { * For example: `this:r3_5` */ final string getDumpString() { - result = getDumpLabel() + getInexactSpecifier() + getDefinitionId() + result = this.getDumpLabel() + this.getInexactSpecifier() + this.getDefinitionId() } /** @@ -129,9 +131,9 @@ class Operand extends TStageOperand { * definition is not modeled in SSA. */ private string getDefinitionId() { - result = getAnyDef().getResultId() + result = this.getAnyDef().getResultId() or - not exists(getAnyDef()) and result = "m?" + not exists(this.getAnyDef()) and result = "m?" } /** @@ -140,7 +142,7 @@ class Operand extends TStageOperand { * the empty string. */ private string getInexactSpecifier() { - if isDefinitionInexact() then result = "~" else result = "" + if this.isDefinitionInexact() then result = "~" else result = "" } /** @@ -155,7 +157,7 @@ class Operand extends TStageOperand { * the definition type, such as in the case of a partial read or a read from a pointer that * has been cast to a different type. */ - Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() } + Language::LanguageType getLanguageType() { result = this.getAnyDef().getResultLanguageType() } /** * Gets the language-neutral type of the value consumed by this operand. This is usually the same @@ -164,7 +166,7 @@ class Operand extends TStageOperand { * from the definition type, such as in the case of a partial read or a read from a pointer that * has been cast to a different type. */ - final IRType getIRType() { result = getLanguageType().getIRType() } + final IRType getIRType() { result = this.getLanguageType().getIRType() } /** * Gets the type of the value consumed by this operand. This is usually the same as the @@ -173,7 +175,7 @@ class Operand extends TStageOperand { * the definition type, such as in the case of a partial read or a read from a pointer that * has been cast to a different type. */ - final Language::Type getType() { getLanguageType().hasType(result, _) } + final Language::Type getType() { this.getLanguageType().hasType(result, _) } /** * Holds if the value consumed by this operand is a glvalue. If this @@ -182,13 +184,13 @@ class Operand extends TStageOperand { * not hold, the value of the operand represents a value whose type is * given by `getType()`. */ - final predicate isGLValue() { getLanguageType().hasType(_, true) } + final predicate isGLValue() { this.getLanguageType().hasType(_, true) } /** * Gets the size of the value consumed by this operand, in bytes. If the operand does not have * a known constant size, this predicate does not hold. */ - final int getSize() { result = getLanguageType().getByteSize() } + final int getSize() { result = this.getLanguageType().getByteSize() } } /** @@ -205,7 +207,7 @@ class MemoryOperand extends Operand { /** * Gets the kind of memory access performed by the operand. */ - MemoryAccessKind getMemoryAccess() { result = getUse().getOpcode().getReadMemoryAccess() } + MemoryAccessKind getMemoryAccess() { result = this.getUse().getOpcode().getReadMemoryAccess() } /** * Holds if the memory access performed by this operand will not always read from every bit in the @@ -215,7 +217,7 @@ class MemoryOperand extends Operand { * conservative estimate of the memory that might actually be accessed at runtime (for example, * the global side effects of a function call). */ - predicate hasMayReadMemoryAccess() { getUse().getOpcode().hasMayReadMemoryAccess() } + predicate hasMayReadMemoryAccess() { this.getUse().getOpcode().hasMayReadMemoryAccess() } /** * Returns the operand that holds the memory address from which the current operand loads its @@ -223,8 +225,8 @@ class MemoryOperand extends Operand { * is `r1`. */ final AddressOperand getAddressOperand() { - getMemoryAccess().usesAddressOperand() and - result.getUse() = getUse() + this.getMemoryAccess().usesAddressOperand() and + result.getUse() = this.getUse() } } @@ -294,7 +296,7 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOpe result = unique(Instruction defInstr | hasDefinition(defInstr, _)) } - final override Overlap getDefinitionOverlap() { hasDefinition(_, result) } + final override Overlap getDefinitionOverlap() { this.hasDefinition(_, result) } pragma[noinline] private predicate hasDefinition(Instruction defInstr, Overlap overlap) { @@ -449,13 +451,17 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand { final override Overlap getDefinitionOverlap() { result = overlap } - final override int getDumpSortOrder() { result = 11 + getPredecessorBlock().getDisplayIndex() } - - final override string getDumpLabel() { - result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" + final override int getDumpSortOrder() { + result = 11 + this.getPredecessorBlock().getDisplayIndex() } - final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() } + final override string getDumpLabel() { + result = "from " + this.getPredecessorBlock().getDisplayIndex().toString() + ":" + } + + final override string getDumpId() { + result = this.getPredecessorBlock().getDisplayIndex().toString() + } /** * Gets the predecessor block from which this value comes. diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll index e0bf271dcc7..b0eb5ec98cb 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll @@ -36,7 +36,7 @@ module AliasModels { * Holds if this is the input value of the parameter with index `index`. * DEPRECATED: Use `isParameter(index)` instead. */ - deprecated final predicate isInParameter(ParameterIndex index) { isParameter(index) } + deprecated final predicate isInParameter(ParameterIndex index) { this.isParameter(index) } /** * Holds if this is the input value pointed to by a pointer parameter to a function, or the input @@ -63,7 +63,7 @@ module AliasModels { * DEPRECATED: Use `isParameterDeref(index)` instead. */ deprecated final predicate isInParameterPointer(ParameterIndex index) { - isParameterDeref(index) + this.isParameterDeref(index) } /** @@ -86,7 +86,7 @@ module AliasModels { * function. * DEPRECATED: Use `isQualifierObject()` instead. */ - deprecated final predicate isInQualifier() { isQualifierObject() } + deprecated final predicate isInQualifier() { this.isQualifierObject() } /** * Holds if this is the input value of the `this` pointer of an instance member function. @@ -184,7 +184,7 @@ module AliasModels { * DEPRECATED: Use `isParameterDeref(index)` instead. */ deprecated final predicate isOutParameterPointer(ParameterIndex index) { - isParameterDeref(index) + this.isParameterDeref(index) } /** @@ -207,7 +207,7 @@ module AliasModels { * function. * DEPRECATED: Use `isQualifierObject()` instead. */ - deprecated final predicate isOutQualifier() { isQualifierObject() } + deprecated final predicate isOutQualifier() { this.isQualifierObject() } /** * Holds if this is the value returned by a function. @@ -232,7 +232,7 @@ module AliasModels { * Holds if this is the value returned by a function. * DEPRECATED: Use `isReturnValue()` instead. */ - deprecated final predicate isOutReturnValue() { isReturnValue() } + deprecated final predicate isOutReturnValue() { this.isReturnValue() } /** * Holds if this is the output value pointed to by the return value of a function, if the function @@ -260,7 +260,7 @@ module AliasModels { * function returns a reference. * DEPRECATED: Use `isReturnValueDeref()` instead. */ - deprecated final predicate isOutReturnPointer() { isReturnValueDeref() } + deprecated final predicate isOutReturnPointer() { this.isReturnValueDeref() } /** * Holds if `i >= 0` and `isParameterDeref(i)` holds for this is the value, or diff --git a/csharp/ql/src/experimental/ir/internal/IRGuards.qll b/csharp/ql/src/experimental/ir/internal/IRGuards.qll index d01dcbed1e1..40780a3920e 100644 --- a/csharp/ql/src/experimental/ir/internal/IRGuards.qll +++ b/csharp/ql/src/experimental/ir/internal/IRGuards.qll @@ -147,7 +147,7 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition { override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) { exists(boolean testIsTrue | - comparesLt(left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue) + this.comparesLt(left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue) ) } @@ -161,7 +161,7 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition { override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) { exists(boolean testIsTrue | - comparesEq(left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue) + this.comparesEq(left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue) ) } } @@ -326,9 +326,9 @@ class IRGuardCondition extends Instruction { cached predicate controlsEdge(IRBlock pred, IRBlock succ, boolean testIsTrue) { pred.getASuccessor() = succ and - controls(pred, testIsTrue) + this.controls(pred, testIsTrue) or - hasBranchEdge(succ, testIsTrue) and + this.hasBranchEdge(succ, testIsTrue) and branch.getCondition() = this and branch.getBlock() = pred } diff --git a/csharp/ql/src/experimental/ir/internal/IntegerInterval.qll b/csharp/ql/src/experimental/ir/internal/IntegerInterval.qll index cd12b9b627a..4f8f4b4e672 100644 --- a/csharp/ql/src/experimental/ir/internal/IntegerInterval.qll +++ b/csharp/ql/src/experimental/ir/internal/IntegerInterval.qll @@ -18,10 +18,11 @@ Overlap getOverlap(IntValue defStart, IntValue defEnd, IntValue useStart, IntVal else if isLE(defStart, useStart) and isGE(defEnd, useEnd) then result instanceof MustTotallyOverlap - else - if isLE(defEnd, useStart) or isGE(defStart, useEnd) - then none() - else result instanceof MayPartiallyOverlap + else ( + not isLE(defEnd, useStart) and + not isGE(defStart, useEnd) and + result instanceof MayPartiallyOverlap + ) } /** diff --git a/csharp/ql/src/experimental/ir/rangeanalysis/RangeUtils.qll b/csharp/ql/src/experimental/ir/rangeanalysis/RangeUtils.qll index 4a7f1d69840..b7fdfc3546f 100644 --- a/csharp/ql/src/experimental/ir/rangeanalysis/RangeUtils.qll +++ b/csharp/ql/src/experimental/ir/rangeanalysis/RangeUtils.qll @@ -52,10 +52,7 @@ IntValue getArrayDim(Variable arr) { arr.getInitializer() = ac and if exists(ac.getLengthArgument(0)) then result = ac.getLengthArgument(0).getValue().toInt() - else - if exists(ac.getInitializer()) - then result = ac.getInitializer().getNumberOfElements() - else none() + else result = ac.getInitializer().getNumberOfElements() ) } diff --git a/csharp/ql/src/qlpack.yml b/csharp/ql/src/qlpack.yml index fc9ac2f6895..7c00298a638 100644 --- a/csharp/ql/src/qlpack.yml +++ b/csharp/ql/src/qlpack.yml @@ -2,6 +2,7 @@ name: codeql/csharp-queries version: 0.0.2 suites: codeql-suites extractor: csharp +defaultSuiteFile: codeql-suites/csharp-code-scanning.qls dependencies: codeql/csharp-all: "*" codeql/suite-helpers: "*" diff --git a/csharp/ql/test/TestUtilities/InlineExpectationsTest.qll b/csharp/ql/test/TestUtilities/InlineExpectationsTest.qll new file mode 100644 index 00000000000..52a790cca28 --- /dev/null +++ b/csharp/ql/test/TestUtilities/InlineExpectationsTest.qll @@ -0,0 +1,346 @@ +/** + * Provides a library for writing QL tests whose success or failure is based on expected results + * embedded in the test source code as comments, rather than the contents of an `.expected` file + * (in that the `.expected` file should always be empty). + * + * To add this framework to a new language: + * - Add a file `InlineExpectationsTestPrivate.qll` that defines a `ExpectationComment` class. This class + * must support a `getContents` method that returns the contents of the given comment, _excluding_ + * the comment indicator itself. It should also define `toString` and `getLocation` as usual. + * + * To create a new inline expectations test: + * - Declare a class that extends `InlineExpectationsTest`. In the characteristic predicate of the + * new class, bind `this` to a unique string (usually the name of the test). + * - Override the `hasActualResult()` predicate to produce the actual results of the query. For each + * result, specify a `Location`, a text description of the element for which the result was + * reported, a short string to serve as the tag to identify expected results for this test, and the + * expected value of the result. + * - Override `getARelevantTag()` to return the set of tags that can be produced by + * `hasActualResult()`. Often this is just a single tag. + * + * Example: + * ```ql + * class ConstantValueTest extends InlineExpectationsTest { + * ConstantValueTest() { this = "ConstantValueTest" } + * + * override string getARelevantTag() { + * // We only use one tag for this test. + * result = "const" + * } + * + * override predicate hasActualResult( + * Location location, string element, string tag, string value + * ) { + * exists(Expr e | + * tag = "const" and // The tag for this test. + * value = e.getValue() and // The expected value. Will only hold for constant expressions. + * location = e.getLocation() and // The location of the result to be reported. + * element = e.toString() // The display text for the result. + * ) + * } + * } + * ``` + * + * There is no need to write a `select` clause or query predicate. All of the differences between + * expected results and actual results will be reported in the `failures()` query predicate. + * + * To annotate the test source code with an expected result, place a comment starting with a `$` on the + * same line as the expected result, with text of the following format as the body of the comment: + * + * `tag=expected-value` + * + * Where `tag` is the value of the `tag` parameter from `hasActualResult()`, and `expected-value` is + * the value of the `value` parameter from `hasActualResult()`. The `=expected-value` portion may be + * omitted, in which case `expected-value` is treated as the empty string. Multiple expectations may + * be placed in the same comment. Any actual result that + * appears on a line that does not contain a matching expected result comment will be reported with + * a message of the form "Unexpected result: tag=value". Any expected result comment for which there + * is no matching actual result will be reported with a message of the form + * "Missing result: tag=expected-value". + * + * Example: + * ```cpp + * int i = x + 5; // $ const=5 + * int j = y + (7 - 3) // $ const=7 const=3 const=4 // The result of the subtraction is a constant. + * ``` + * + * For tests that contain known missing and spurious results, it is possible to further + * annotate that a particular expected result is known to be spurious, or that a particular + * missing result is known to be missing: + * + * `$ SPURIOUS: tag=expected-value` // Spurious result + * `$ MISSING: tag=expected-value` // Missing result + * + * A spurious expectation is treated as any other expected result, except that if there is no + * matching actual result, the message will be of the form "Fixed spurious result: tag=value". A + * missing expectation is treated as if there were no expected result, except that if a + * matching expected result is found, the message will be of the form + * "Fixed missing result: tag=value". + * + * A single line can contain all the expected, spurious and missing results of that line. For instance: + * `$ tag1=value1 SPURIOUS: tag2=value2 MISSING: tag3=value3`. + * + * If the same result value is expected for two or more tags on the same line, there is a shorthand + * notation available: + * + * `tag1,tag2=expected-value` + * + * is equivalent to: + * + * `tag1=expected-value tag2=expected-value` + */ + +private import InlineExpectationsTestPrivate + +/** + * Base class for tests with inline expectations. The test extends this class to provide the actual + * results of the query, which are then compared with the expected results in comments to produce a + * list of failure messages that point out where the actual results differ from the expected + * results. + */ +abstract class InlineExpectationsTest extends string { + bindingset[this] + InlineExpectationsTest() { any() } + + /** + * Returns all tags that can be generated by this test. Most tests will only ever produce a single + * tag. Any expected result comments for a tag that is not returned by the `getARelevantTag()` + * predicate for an active test will be ignored. This makes it possible to write multiple tests in + * different `.ql` files that all query the same source code. + */ + abstract string getARelevantTag(); + + /** + * Returns the actual results of the query that is being tested. Each result consist of the + * following values: + * - `location` - The source code location of the result. Any expected result comment must appear + * on the start line of this location. + * - `element` - Display text for the element on which the result is reported. + * - `tag` - The tag that marks this result as coming from this test. This must be one of the tags + * returned by `getARelevantTag()`. + * - `value` - The value of the result, which will be matched against the value associated with + * `tag` in any expected result comment on that line. + */ + abstract predicate hasActualResult(Location location, string element, string tag, string value); + + final predicate hasFailureMessage(FailureLocatable element, string message) { + exists(ActualResult actualResult | + actualResult.getTest() = this and + element = actualResult and + ( + exists(FalseNegativeExpectation falseNegative | + falseNegative.matchesActualResult(actualResult) and + message = "Fixed missing result:" + falseNegative.getExpectationText() + ) + or + not exists(ValidExpectation expectation | expectation.matchesActualResult(actualResult)) and + message = "Unexpected result: " + actualResult.getExpectationText() + ) + ) + or + exists(ValidExpectation expectation | + not exists(ActualResult actualResult | expectation.matchesActualResult(actualResult)) and + expectation.getTag() = getARelevantTag() and + element = expectation and + ( + expectation instanceof GoodExpectation and + message = "Missing result:" + expectation.getExpectationText() + or + expectation instanceof FalsePositiveExpectation and + message = "Fixed spurious result:" + expectation.getExpectationText() + ) + ) + or + exists(InvalidExpectation expectation | + element = expectation and + message = "Invalid expectation syntax: " + expectation.getExpectation() + ) + } +} + +/** + * RegEx pattern to match a comment containing one or more expected results. The comment must have + * `$` as its first non-whitespace character. Any subsequent character + * is treated as part of the expected results, except that the comment may contain a `//` sequence + * to treat the remainder of the line as a regular (non-interpreted) comment. + */ +private string expectationCommentPattern() { result = "\\s*\\$((?:[^/]|/[^/])*)(?://.*)?" } + +/** + * The possible columns in an expectation comment. The `TDefaultColumn` branch represents the first + * column in a comment. This column is not precedeeded by a name. `TNamedColumn(name)` represents a + * column containing expected results preceeded by the string `name:`. + */ +private newtype TColumn = + TDefaultColumn() or + TNamedColumn(string name) { name = ["MISSING", "SPURIOUS"] } + +bindingset[start, content] +private int getEndOfColumnPosition(int start, string content) { + result = + min(string name, int cand | + exists(TNamedColumn(name)) and + cand = content.indexOf(name + ":") and + cand >= start + | + cand + ) + or + not exists(string name | + exists(TNamedColumn(name)) and + content.indexOf(name + ":") >= start + ) and + result = content.length() +} + +private predicate getAnExpectation( + ExpectationComment comment, TColumn column, string expectation, string tags, string value +) { + exists(string content | + content = comment.getContents().regexpCapture(expectationCommentPattern(), 1) and + ( + column = TDefaultColumn() and + exists(int end | + end = getEndOfColumnPosition(0, content) and + expectation = content.prefix(end).regexpFind(expectationPattern(), _, _).trim() + ) + or + exists(string name, int start, int end | + column = TNamedColumn(name) and + start = content.indexOf(name + ":") + name.length() + 1 and + end = getEndOfColumnPosition(start, content) and + expectation = content.substring(start, end).regexpFind(expectationPattern(), _, _).trim() + ) + ) + ) and + tags = expectation.regexpCapture(expectationPattern(), 1) and + if exists(expectation.regexpCapture(expectationPattern(), 2)) + then value = expectation.regexpCapture(expectationPattern(), 2) + else value = "" +} + +private string getColumnString(TColumn column) { + column = TDefaultColumn() and result = "" + or + column = TNamedColumn(result) +} + +/** + * RegEx pattern to match a single expected result, not including the leading `$`. It consists of one or + * more comma-separated tags containing only letters, digits, `-` and `_` (note that the first character + * must not be a digit), optionally followed by `=` and the expected value. + */ +private string expectationPattern() { + exists(string tag, string tags, string value | + tag = "[A-Za-z-_][A-Za-z-_0-9]*" and + tags = "((?:" + tag + ")(?:\\s*,\\s*" + tag + ")*)" and + // In Python, we allow both `"` and `'` for strings, as well as the prefixes `bru`. + // For example, `b"foo"`. + value = "((?:[bru]*\"[^\"]*\"|[bru]*'[^']*'|\\S+)*)" and + result = tags + "(?:=" + value + ")?" + ) +} + +private newtype TFailureLocatable = + TActualResult( + InlineExpectationsTest test, Location location, string element, string tag, string value + ) { + test.hasActualResult(location, element, tag, value) + } or + TValidExpectation(ExpectationComment comment, string tag, string value, string knownFailure) { + exists(TColumn column, string tags | + getAnExpectation(comment, column, _, tags, value) and + tag = tags.splitAt(",") and + knownFailure = getColumnString(column) + ) + } or + TInvalidExpectation(ExpectationComment comment, string expectation) { + getAnExpectation(comment, _, expectation, _, _) and + not expectation.regexpMatch(expectationPattern()) + } + +class FailureLocatable extends TFailureLocatable { + string toString() { none() } + + Location getLocation() { none() } + + final string getExpectationText() { result = getTag() + "=" + getValue() } + + string getTag() { none() } + + string getValue() { none() } +} + +class ActualResult extends FailureLocatable, TActualResult { + InlineExpectationsTest test; + Location location; + string element; + string tag; + string value; + + ActualResult() { this = TActualResult(test, location, element, tag, value) } + + override string toString() { result = element } + + override Location getLocation() { result = location } + + InlineExpectationsTest getTest() { result = test } + + override string getTag() { result = tag } + + override string getValue() { result = value } +} + +abstract private class Expectation extends FailureLocatable { + ExpectationComment comment; + + override string toString() { result = comment.toString() } + + override Location getLocation() { result = comment.getLocation() } +} + +private class ValidExpectation extends Expectation, TValidExpectation { + string tag; + string value; + string knownFailure; + + ValidExpectation() { this = TValidExpectation(comment, tag, value, knownFailure) } + + override string getTag() { result = tag } + + override string getValue() { result = value } + + string getKnownFailure() { result = knownFailure } + + predicate matchesActualResult(ActualResult actualResult) { + getLocation().getStartLine() = actualResult.getLocation().getStartLine() and + getLocation().getFile() = actualResult.getLocation().getFile() and + getTag() = actualResult.getTag() and + getValue() = actualResult.getValue() + } +} + +/* Note: These next three classes correspond to all the possible values of type `TColumn`. */ +class GoodExpectation extends ValidExpectation { + GoodExpectation() { getKnownFailure() = "" } +} + +class FalsePositiveExpectation extends ValidExpectation { + FalsePositiveExpectation() { getKnownFailure() = "SPURIOUS" } +} + +class FalseNegativeExpectation extends ValidExpectation { + FalseNegativeExpectation() { getKnownFailure() = "MISSING" } +} + +class InvalidExpectation extends Expectation, TInvalidExpectation { + string expectation; + + InvalidExpectation() { this = TInvalidExpectation(comment, expectation) } + + string getExpectation() { result = expectation } +} + +query predicate failures(FailureLocatable element, string message) { + exists(InlineExpectationsTest test | test.hasFailureMessage(element, message)) +} diff --git a/csharp/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll b/csharp/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll new file mode 100644 index 00000000000..3df85cc0fd6 --- /dev/null +++ b/csharp/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll @@ -0,0 +1,10 @@ +import csharp +import semmle.code.csharp.Comments + +/** + * A class representing line comments in C# used by the InlineExpectations core code + */ +class ExpectationComment extends SinglelineComment { + /** Gets the contents of the given comment, _without_ the preceding comment marker (`//`). */ + string getContents() { result = this.getText() } +} diff --git a/csharp/ql/test/TestUtilities/InlineFlowTest.qll b/csharp/ql/test/TestUtilities/InlineFlowTest.qll new file mode 100644 index 00000000000..6bd44e81df3 --- /dev/null +++ b/csharp/ql/test/TestUtilities/InlineFlowTest.qll @@ -0,0 +1,110 @@ +/** + * Provides a simple base test for flow-related tests using inline expectations. + * + * Example for a test.ql: + * ```ql + * import csharp + * import DataFlow::PathGraph + * import TestUtilities.InlineFlowTest + * + * from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf + * where conf.hasFlowPath(source, sink) + * select sink, source, sink, "$@", source, source.toString() + * ``` + * + * To declare expecations, you can use the $hasTaintFlow or $hasValueFlow comments within the test source files. + * Example of the corresponding test file, e.g. Test.cs + * ```csharp + * public class Test + * { + * object Source() { return null; } + * string Taint() { return null; } + * void Sink(object o) { } + * + * public void test() + * { + * var s = Source(1); + * Sink(s); // $ hasValueFlow=1 + * var t = "foo" + Taint(2); + * Sink(t); // $ hasTaintFlow=2 + * } + * } + * ``` + * + * If you're not interested in a specific flow type, you can disable either value or taint flow expectations as follows: + * ```ql + * class HasFlowTest extends InlineFlowTest { + * override DataFlow::Configuration getTaintFlowConfig() { none() } + * + * override DataFlow::Configuration getValueFlowConfig() { none() } + * } + * ``` + * + * If you need more fine-grained tuning, consider implementing a test using `InlineExpectationsTest`. + */ + +import csharp +import TestUtilities.InlineExpectationsTest + +private predicate defaultSource(DataFlow::Node src) { + src.asExpr().(MethodCall).getTarget().getUndecoratedName() = ["Source", "Taint"] +} + +private predicate defaultSink(DataFlow::Node sink) { + exists(MethodCall mc | mc.getTarget().hasUndecoratedName("Sink") | + sink.asExpr() = mc.getAnArgument() + ) +} + +class DefaultValueFlowConf extends DataFlow::Configuration { + DefaultValueFlowConf() { this = "qltest:defaultValueFlowConf" } + + override predicate isSource(DataFlow::Node n) { defaultSource(n) } + + override predicate isSink(DataFlow::Node n) { defaultSink(n) } + + override int fieldFlowBranchLimit() { result = 1000 } +} + +class DefaultTaintFlowConf extends TaintTracking::Configuration { + DefaultTaintFlowConf() { this = "qltest:defaultTaintFlowConf" } + + override predicate isSource(DataFlow::Node n) { defaultSource(n) } + + override predicate isSink(DataFlow::Node n) { defaultSink(n) } + + override int fieldFlowBranchLimit() { result = 1000 } +} + +private string getSourceArgString(DataFlow::Node src) { + defaultSource(src) and + src.asExpr().(MethodCall).getAnArgument().getValue() = result +} + +class InlineFlowTest extends InlineExpectationsTest { + InlineFlowTest() { this = "HasFlowTest" } + + override string getARelevantTag() { result = ["hasValueFlow", "hasTaintFlow"] } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "hasValueFlow" and + exists(DataFlow::Node src, DataFlow::Node sink | getValueFlowConfig().hasFlow(src, sink) | + sink.getLocation() = location and + element = sink.toString() and + if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = "" + ) + or + tag = "hasTaintFlow" and + exists(DataFlow::Node src, DataFlow::Node sink | + getTaintFlowConfig().hasFlow(src, sink) and not getValueFlowConfig().hasFlow(src, sink) + | + sink.getLocation() = location and + element = sink.toString() and + if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = "" + ) + } + + DataFlow::Configuration getValueFlowConfig() { result = any(DefaultValueFlowConf config) } + + DataFlow::Configuration getTaintFlowConfig() { result = any(DefaultTaintFlowConf config) } +} diff --git a/csharp/ql/test/library-tests/assemblies/CompareVersions.ql b/csharp/ql/test/library-tests/assemblies/CompareVersions.ql index cb4b6745258..ec1b13dcbe6 100644 --- a/csharp/ql/test/library-tests/assemblies/CompareVersions.ql +++ b/csharp/ql/test/library-tests/assemblies/CompareVersions.ql @@ -1,16 +1,8 @@ import csharp Version getAVersion() { - result = "1.2" or - result = "1.2.0" or - result = "1.2.0.0" or - result = "1.3" or - result = "1.3.1" or - result = "1.3.1.2" or - result = "1.3.1.3" or - result = "1.3.2" or - result = "1.4" or - result = "2.3.1" + result = + ["1.2", "1.2.0", "1.2.0.0", "1.3", "1.3.1", "1.3.1.2", "1.3.1.3", "1.3.2", "1.4", "2.3.1"] } from Version v1, Version v2 diff --git a/csharp/ql/test/library-tests/assemblies/ValidVersions.ql b/csharp/ql/test/library-tests/assemblies/ValidVersions.ql index da5b749200c..3e66e165461 100644 --- a/csharp/ql/test/library-tests/assemblies/ValidVersions.ql +++ b/csharp/ql/test/library-tests/assemblies/ValidVersions.ql @@ -1,14 +1,6 @@ import csharp from Version version -where - version = "1.2.3.4" or - version = "2.3.24" or - version = "1.2" or - version = "xxx" or - version = "1.x" or - version = "1" or - version = "" or - version = "1234.56" +where version = ["1.2.3.4", "2.3.24", "1.2", "xxx", "1.x", "1", "", "1234.56"] select version, version.getMajor(), version.getMajorRevision(), version.getMinor(), version.getMinorRevision() diff --git a/csharp/ql/test/library-tests/assignables/AssignableDefinition.ql b/csharp/ql/test/library-tests/assignables/AssignableDefinition.ql index 6f24ecaa0e9..ad5facdab45 100644 --- a/csharp/ql/test/library-tests/assignables/AssignableDefinition.ql +++ b/csharp/ql/test/library-tests/assignables/AssignableDefinition.ql @@ -6,14 +6,14 @@ newtype TTargetAccessOption = class TargetAccessOption extends TTargetAccessOption { string toString() { - result = som().toString() + result = this.som().toString() or - exists(non()) and result = "" + exists(this.non()) and result = "" } Location getLocation() { - result = som().getLocation() or - result = non().getLocation() + result = this.som().getLocation() or + result = this.non().getLocation() } private AssignableAccess som() { this = TTargetAccessSome(result) } @@ -31,14 +31,14 @@ newtype TSourceOption = class SourceOption extends TSourceOption { string toString() { - result = som().toString() + result = this.som().toString() or - exists(non()) and result = "" + exists(this.non()) and result = "" } Location getLocation() { - result = som().getLocation() or - result = non().getLocation() + result = this.som().getLocation() or + result = this.non().getLocation() } private Expr som() { this = TSourceSome(result) } diff --git a/csharp/ql/test/library-tests/cil/consistency/Handles.ql b/csharp/ql/test/library-tests/cil/consistency/Handles.ql index a64ea94eb98..e67e4b29416 100644 --- a/csharp/ql/test/library-tests/cil/consistency/Handles.ql +++ b/csharp/ql/test/library-tests/cil/consistency/Handles.ql @@ -5,7 +5,7 @@ import dotnet class MetadataEntity extends DotNet::NamedElement, @metadata_entity { int getHandle() { metadata_handle(this, _, result) } - predicate hasHandle() { exists(getHandle()) } + predicate hasHandle() { exists(this.getHandle()) } Assembly getAssembly() { metadata_handle(this, result, _) } } diff --git a/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.cs b/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.cs index 3b8a9ba7c7f..4125866a549 100644 --- a/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.cs +++ b/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.cs @@ -92,6 +92,19 @@ namespace My.Qltest Sink(i); } + void M15() + { + var d1 = new D(); + d1.Field = new object(); + var d2 = new D(); + Apply2(d => + { + Sink(d); + }, d1, d2); + Sink(d1.Field); + Sink(d2.Field2); // SPURIOUS FLOW + } + object StepArgRes(object x) { return null; } void StepArgArg(object @in, object @out) { } @@ -103,6 +116,7 @@ namespace My.Qltest void StepQualArg(object @out) { } object Field; + object Field2; object StepFieldGetter() => throw null; @@ -122,6 +136,8 @@ namespace My.Qltest static S[] Map(S[] elements, Func f) => throw null; + static void Apply2(Action f, S s1, S s2) => throw null; + static void Parse(string s, out int i) => throw null; static void Sink(object o) { } diff --git a/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.expected b/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.expected index 62af20beb18..5a1c6b31a97 100644 --- a/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.expected @@ -24,12 +24,12 @@ edges | ExternalFlow.cs:54:36:54:47 | object creation of type Object : Object | ExternalFlow.cs:54:13:54:16 | [post] this access [element] : Object | | ExternalFlow.cs:55:18:55:21 | this access [element] : Object | ExternalFlow.cs:55:18:55:41 | call to method StepElementGetter | | ExternalFlow.cs:60:35:60:35 | o : Object | ExternalFlow.cs:60:47:60:47 | access to parameter o | -| ExternalFlow.cs:60:64:60:75 | object creation of type Object : Object | ExternalFlow.cs:121:46:121:46 | s : Object | +| ExternalFlow.cs:60:64:60:75 | object creation of type Object : Object | ExternalFlow.cs:135:46:135:46 | s : Object | | ExternalFlow.cs:65:21:65:60 | call to method Apply : Object | ExternalFlow.cs:66:18:66:18 | access to local variable o | | ExternalFlow.cs:65:45:65:56 | object creation of type Object : Object | ExternalFlow.cs:65:21:65:60 | call to method Apply : Object | | ExternalFlow.cs:71:30:71:45 | { ..., ... } [element] : Object | ExternalFlow.cs:72:17:72:20 | access to local variable objs [element] : Object | | ExternalFlow.cs:71:32:71:43 | object creation of type Object : Object | ExternalFlow.cs:71:30:71:45 | { ..., ... } [element] : Object | -| ExternalFlow.cs:72:17:72:20 | access to local variable objs [element] : Object | ExternalFlow.cs:123:34:123:41 | elements [element] : Object | +| ExternalFlow.cs:72:17:72:20 | access to local variable objs [element] : Object | ExternalFlow.cs:137:34:137:41 | elements [element] : Object | | ExternalFlow.cs:72:23:72:23 | o : Object | ExternalFlow.cs:72:35:72:35 | access to parameter o | | ExternalFlow.cs:77:24:77:58 | call to method Map [element] : Object | ExternalFlow.cs:78:18:78:21 | access to local variable objs [element] : Object | | ExternalFlow.cs:77:46:77:57 | object creation of type Object : Object | ExternalFlow.cs:77:24:77:58 | call to method Map [element] : Object | @@ -43,8 +43,29 @@ edges | ExternalFlow.cs:90:21:90:34 | object creation of type String : String | ExternalFlow.cs:91:19:91:19 | access to local variable s : String | | ExternalFlow.cs:91:19:91:19 | access to local variable s : String | ExternalFlow.cs:91:30:91:30 | SSA def(i) : Int32 | | ExternalFlow.cs:91:30:91:30 | SSA def(i) : Int32 | ExternalFlow.cs:92:18:92:18 | (...) ... | -| ExternalFlow.cs:121:46:121:46 | s : Object | ExternalFlow.cs:60:35:60:35 | o : Object | -| ExternalFlow.cs:123:34:123:41 | elements [element] : Object | ExternalFlow.cs:72:23:72:23 | o : Object | +| ExternalFlow.cs:98:13:98:14 | [post] access to local variable d1 [field Field] : Object | ExternalFlow.cs:103:16:103:17 | access to local variable d1 [field Field] : Object | +| ExternalFlow.cs:98:13:98:14 | [post] access to local variable d1 [field Field] : Object | ExternalFlow.cs:104:18:104:19 | access to local variable d1 [field Field] : Object | +| ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | ExternalFlow.cs:98:13:98:14 | [post] access to local variable d1 [field Field] : Object | +| ExternalFlow.cs:100:20:100:20 | d : Object | ExternalFlow.cs:102:22:102:22 | access to parameter d | +| ExternalFlow.cs:103:16:103:17 | access to local variable d1 [field Field] : Object | ExternalFlow.cs:103:20:103:21 | [post] access to local variable d2 [field Field2] : Object | +| ExternalFlow.cs:103:16:103:17 | access to local variable d1 [field Field] : Object | ExternalFlow.cs:139:46:139:47 | s1 [field Field] : Object | +| ExternalFlow.cs:103:20:103:21 | [post] access to local variable d2 [field Field2] : Object | ExternalFlow.cs:105:18:105:19 | access to local variable d2 [field Field2] : Object | +| ExternalFlow.cs:104:18:104:19 | access to local variable d1 [field Field] : Object | ExternalFlow.cs:104:18:104:25 | access to field Field | +| ExternalFlow.cs:105:18:105:19 | access to local variable d2 [field Field2] : Object | ExternalFlow.cs:105:18:105:26 | access to field Field2 | +| ExternalFlow.cs:135:46:135:46 | s : Object | ExternalFlow.cs:60:35:60:35 | o : Object | +| ExternalFlow.cs:137:34:137:41 | elements [element] : Object | ExternalFlow.cs:72:23:72:23 | o : Object | +| ExternalFlow.cs:137:34:137:41 | elements [element] : Object | ExternalFlow.cs:72:23:72:23 | o : Object | +| ExternalFlow.cs:137:34:137:41 | elements [element] : Object | ExternalFlow.cs:137:34:137:41 | elements [element] : Object | +| ExternalFlow.cs:137:34:137:41 | elements [element] : Object | ExternalFlow.cs:137:34:137:41 | elements [element] : Object | +| ExternalFlow.cs:139:46:139:47 | s1 [field Field] : Object | ExternalFlow.cs:100:20:100:20 | d : Object | +| ExternalFlow.cs:139:46:139:47 | s1 [field Field] : Object | ExternalFlow.cs:100:20:100:20 | d : Object | +| ExternalFlow.cs:139:46:139:47 | s1 [field Field] : Object | ExternalFlow.cs:139:46:139:47 | s1 [field Field] : Object | +| ExternalFlow.cs:139:46:139:47 | s1 [field Field] : Object | ExternalFlow.cs:139:46:139:47 | s1 [field Field] : Object | +| ExternalFlow.cs:139:46:139:47 | s1 [field Field] : Object | ExternalFlow.cs:139:52:139:53 | s2 [field Field2] : Object | +| ExternalFlow.cs:139:46:139:47 | s1 [field Field] : Object | ExternalFlow.cs:139:52:139:53 | s2 [field Field2] : Object | +| ExternalFlow.cs:139:52:139:53 | s2 [field Field2] : Object | ExternalFlow.cs:100:20:100:20 | d : Object | +| ExternalFlow.cs:139:52:139:53 | s2 [field Field2] : Object | ExternalFlow.cs:139:46:139:47 | s1 [field Field] : Object | +| ExternalFlow.cs:139:52:139:53 | s2 [field Field2] : Object | ExternalFlow.cs:139:52:139:53 | s2 [field Field2] : Object | nodes | ExternalFlow.cs:9:27:9:38 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | | ExternalFlow.cs:10:18:10:33 | call to method StepArgRes | semmle.label | call to method StepArgRes | @@ -104,8 +125,22 @@ nodes | ExternalFlow.cs:91:19:91:19 | access to local variable s : String | semmle.label | access to local variable s : String | | ExternalFlow.cs:91:30:91:30 | SSA def(i) : Int32 | semmle.label | SSA def(i) : Int32 | | ExternalFlow.cs:92:18:92:18 | (...) ... | semmle.label | (...) ... | -| ExternalFlow.cs:121:46:121:46 | s : Object | semmle.label | s : Object | -| ExternalFlow.cs:123:34:123:41 | elements [element] : Object | semmle.label | elements [element] : Object | +| ExternalFlow.cs:98:13:98:14 | [post] access to local variable d1 [field Field] : Object | semmle.label | [post] access to local variable d1 [field Field] : Object | +| ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| ExternalFlow.cs:100:20:100:20 | d : Object | semmle.label | d : Object | +| ExternalFlow.cs:102:22:102:22 | access to parameter d | semmle.label | access to parameter d | +| ExternalFlow.cs:103:16:103:17 | access to local variable d1 [field Field] : Object | semmle.label | access to local variable d1 [field Field] : Object | +| ExternalFlow.cs:103:20:103:21 | [post] access to local variable d2 [field Field2] : Object | semmle.label | [post] access to local variable d2 [field Field2] : Object | +| ExternalFlow.cs:104:18:104:19 | access to local variable d1 [field Field] : Object | semmle.label | access to local variable d1 [field Field] : Object | +| ExternalFlow.cs:104:18:104:25 | access to field Field | semmle.label | access to field Field | +| ExternalFlow.cs:105:18:105:19 | access to local variable d2 [field Field2] : Object | semmle.label | access to local variable d2 [field Field2] : Object | +| ExternalFlow.cs:105:18:105:26 | access to field Field2 | semmle.label | access to field Field2 | +| ExternalFlow.cs:135:46:135:46 | s : Object | semmle.label | s : Object | +| ExternalFlow.cs:137:34:137:41 | elements [element] : Object | semmle.label | elements [element] : Object | +| ExternalFlow.cs:137:34:137:41 | elements [element] : Object | semmle.label | elements [element] : Object | +| ExternalFlow.cs:139:46:139:47 | s1 [field Field] : Object | semmle.label | s1 [field Field] : Object | +| ExternalFlow.cs:139:46:139:47 | s1 [field Field] : Object | semmle.label | s1 [field Field] : Object | +| ExternalFlow.cs:139:52:139:53 | s2 [field Field2] : Object | semmle.label | s2 [field Field2] : Object | subpaths invalidModelRow #select @@ -124,3 +159,6 @@ invalidModelRow | ExternalFlow.cs:78:18:78:24 | (...) ... | ExternalFlow.cs:77:46:77:57 | object creation of type Object : Object | ExternalFlow.cs:78:18:78:24 | (...) ... | $@ | ExternalFlow.cs:77:46:77:57 | object creation of type Object : Object | object creation of type Object : Object | | ExternalFlow.cs:85:18:85:25 | access to array element | ExternalFlow.cs:83:32:83:43 | object creation of type Object : Object | ExternalFlow.cs:85:18:85:25 | access to array element | $@ | ExternalFlow.cs:83:32:83:43 | object creation of type Object : Object | object creation of type Object : Object | | ExternalFlow.cs:92:18:92:18 | (...) ... | ExternalFlow.cs:90:21:90:34 | object creation of type String : String | ExternalFlow.cs:92:18:92:18 | (...) ... | $@ | ExternalFlow.cs:90:21:90:34 | object creation of type String : String | object creation of type String : String | +| ExternalFlow.cs:102:22:102:22 | access to parameter d | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | ExternalFlow.cs:102:22:102:22 | access to parameter d | $@ | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | object creation of type Object : Object | +| ExternalFlow.cs:104:18:104:25 | access to field Field | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | ExternalFlow.cs:104:18:104:25 | access to field Field | $@ | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | object creation of type Object : Object | +| ExternalFlow.cs:105:18:105:26 | access to field Field2 | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | ExternalFlow.cs:105:18:105:26 | access to field Field2 | $@ | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | object creation of type Object : Object | diff --git a/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.ql b/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.ql index d22956a1b0e..f164967c770 100644 --- a/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.ql +++ b/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.ql @@ -23,6 +23,8 @@ class SummaryModelTest extends SummaryModelCsv { "My.Qltest;D;false;StepElementSetter;(System.Object);;Argument[0];Element of Argument[-1];value", "My.Qltest;D;false;Apply<,>;(System.Func,S);;Argument[1];Parameter[0] of Argument[0];value", "My.Qltest;D;false;Apply<,>;(System.Func,S);;ReturnValue of Argument[0];ReturnValue;value", + "My.Qltest;D;false;Apply2<>;(System.Action,S,S);;Field[My.Qltest.D.Field] of Argument[1];Parameter[0] of Argument[0];value", + "My.Qltest;D;false;Apply2<>;(System.Action,S,S);;Field[My.Qltest.D.Field2] of Argument[2];Parameter[0] of Argument[0];value", "My.Qltest;D;false;Map<,>;(S[],System.Func);;Element of Argument[0];Parameter[0] of Argument[1];value", "My.Qltest;D;false;Map<,>;(S[],System.Func);;ReturnValue of Argument[1];Element of ReturnValue;value", "My.Qltest;D;false;Parse;(System.String,System.Int32);;Argument[0];Argument[1];taint" diff --git a/csharp/ql/test/library-tests/dataflow/external-models/steps.expected b/csharp/ql/test/library-tests/dataflow/external-models/steps.expected index f73872ed9be..84e3026c74f 100644 --- a/csharp/ql/test/library-tests/dataflow/external-models/steps.expected +++ b/csharp/ql/test/library-tests/dataflow/external-models/steps.expected @@ -8,6 +8,8 @@ summaryThroughStep | Steps.cs:22:13:22:16 | this access | Steps.cs:22:13:22:30 | call to method StepQualRes | false | | Steps.cs:23:13:23:25 | this access | Steps.cs:23:13:23:25 | call to method StepQualRes | false | | Steps.cs:26:13:26:31 | this access | Steps.cs:26:25:26:30 | [post] access to local variable argOut | false | +| Steps.cs:30:13:30:16 | this access | Steps.cs:30:13:30:16 | [post] this access | true | +| Steps.cs:34:13:34:16 | this access | Steps.cs:34:13:34:16 | [post] this access | true | | Steps.cs:41:29:41:29 | 0 | Steps.cs:41:13:41:30 | call to method StepGeneric | true | | Steps.cs:42:30:42:34 | false | Steps.cs:42:13:42:35 | call to method StepGeneric2 | true | | Steps.cs:44:36:44:43 | "string" | Steps.cs:44:13:44:44 | call to method StepOverride | true | @@ -19,3 +21,6 @@ summarySetterStep | Steps.cs:30:34:30:34 | 0 | Steps.cs:30:13:30:16 | [post] this access | Steps.cs:57:13:57:17 | field Field | | Steps.cs:34:37:34:37 | 0 | Steps.cs:34:13:34:16 | [post] this access | Steps.cs:63:13:63:20 | property Property | | Steps.cs:38:36:38:36 | 0 | Steps.cs:38:13:38:16 | [post] this access | file://:0:0:0:0 | element | +clearsContent +| Steps.cs:61:14:61:28 | StepFieldSetter | Steps.cs:57:13:57:17 | field Field | -1 | +| Steps.cs:67:14:67:31 | StepPropertySetter | Steps.cs:63:13:63:20 | property Property | -1 | diff --git a/csharp/ql/test/library-tests/dataflow/external-models/steps.ql b/csharp/ql/test/library-tests/dataflow/external-models/steps.ql index b5643d27ca5..cab4c292a40 100644 --- a/csharp/ql/test/library-tests/dataflow/external-models/steps.ql +++ b/csharp/ql/test/library-tests/dataflow/external-models/steps.ql @@ -1,6 +1,7 @@ import csharp import DataFlow import semmle.code.csharp.dataflow.ExternalFlow +import semmle.code.csharp.dataflow.FlowSummary import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl import CsvValidation @@ -40,3 +41,8 @@ query predicate summaryGetterStep(DataFlow::Node arg, DataFlow::Node out, Conten query predicate summarySetterStep(DataFlow::Node arg, DataFlow::Node out, Content c) { FlowSummaryImpl::Private::Steps::summarySetterStep(arg, c, out) } + +query predicate clearsContent(SummarizedCallable c, DataFlow::Content k, int i) { + c.clearsContent(i, k) and + c.fromSource() +} diff --git a/csharp/ql/test/library-tests/dataflow/fields/A.cs b/csharp/ql/test/library-tests/dataflow/fields/A.cs index d56ab3adc1e..663bbb1dab6 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/A.cs +++ b/csharp/ql/test/library-tests/dataflow/fields/A.cs @@ -2,35 +2,35 @@ public class A { public void M1() { - var c = new C(); + var c = Source(1); var b = B.Make(c); - Sink(b.c); // flow + Sink(b.c); // $ hasValueFlow=1 } public void M2() { var b = new B(); - b.Set(new C1()); - Sink(b.Get()); // flow - Sink((new B(new C())).Get()); // flow + b.Set(Source(2.1)); + Sink(b.Get()); // $ hasValueFlow=2.1 + Sink((new B(Source(2.2))).Get()); // $ hasValueFlow=2.2 } public void M3() { var b1 = new B(); B b2; - b2 = SetOnB(b1, new C2()); - Sink(b1.c); // no flow - Sink(b2.c); // flow + b2 = SetOnB(b1, Source(3)); + Sink(b1.c); + Sink(b2.c); // $ hasValueFlow=3 } public void M4() { var b1 = new B(); B b2; - b2 = SetOnBWrap(b1, new C2()); - Sink(b1.c); // no flow - Sink(b2.c); // flow + b2 = SetOnBWrap(b1, Source(4)); + Sink(b1.c); + Sink(b2.c); // $ hasValueFlow=4 } public B SetOnBWrap(B b1, C c) @@ -52,7 +52,7 @@ public class A public void M5() { - var a = new A(); + var a = Source(5); C1 c1 = new C1(); c1.a = a; M6(c1); @@ -61,7 +61,7 @@ public class A { if (c is C1) { - Sink(((C1)c).a); // flow + Sink(((C1)c).a); // $ hasValueFlow=5 } C cc; if (c is C2) @@ -80,13 +80,13 @@ public class A public void M7(B b) { - b.Set(new C()); + b.Set(Source(7)); } public void M8() { var b = new B(); M7(b); - Sink(b.c); // flow + Sink(b.c); // $ hasValueFlow=7 } public class D @@ -94,33 +94,33 @@ public class A public B b; public D(B b, bool x) { - b.c = new C(); - this.b = x ? b : new B(); + b.c = Source(9.1); + this.b = x ? b : Source(9.2); } } public void M9() { - var b = new B(); + var b = Source(9.3); var d = new D(b, R()); - Sink(d.b); // flow x2 - Sink(d.b.c); // flow - Sink(b.c); // flow + Sink(d.b); // $ hasValueFlow=9.2 $ hasValueFlow=9.3 + Sink(d.b.c); // $ hasValueFlow=9.1 + Sink(b.c); // $ hasValueFlow=9.1 } public void M10() { - var b = new B(); + var b = Source(10); var l1 = new MyList(b, new MyList(null, null)); var l2 = new MyList(null, l1); var l3 = new MyList(null, l2); Sink(l3.head); // no flow, b is nested beneath at least one .next Sink(l3.next.head); // flow, the precise nesting depth isn't tracked - Sink(l3.next.next.head); // flow + Sink(l3.next.next.head); // $ hasValueFlow=10 Sink(l3.next.next.next.head); // no flow for (var l = l3; l != null; l = l.next) { - Sink(l.head); // flow + Sink(l.head); // $ hasValueFlow=10 } } @@ -160,4 +160,6 @@ public class A this.next = next; } } + + static T Source(object source) => throw null; } diff --git a/csharp/ql/test/library-tests/dataflow/fields/B.cs b/csharp/ql/test/library-tests/dataflow/fields/B.cs index f25b8959d22..84c87e7298f 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/B.cs +++ b/csharp/ql/test/library-tests/dataflow/fields/B.cs @@ -2,20 +2,20 @@ public class B { public void M1() { - var e = new Elem(); + var e = Source(1); var b1 = new Box1(e, null); var b2 = new Box2(b1); - Sink(b2.box1.elem1); // flow + Sink(b2.box1.elem1); // $ hasValueFlow=1 Sink(b2.box1.elem2); // no flow } public void M2() { - var e = new Elem(); + var e = Source(2); var b1 = new Box1(null, e); var b2 = new Box2(b1); Sink(b2.box1.elem1); // no flow - Sink(b2.box1.elem2); // flow + Sink(b2.box1.elem2); // $ hasValueFlow=2 } public static void Sink(object o) { } @@ -41,4 +41,6 @@ public class B this.box1 = b1; } } + + static T Source(object source) => throw null; } diff --git a/csharp/ql/test/library-tests/dataflow/fields/C.cs b/csharp/ql/test/library-tests/dataflow/fields/C.cs index da7b5f14fee..9ebe9ec27ed 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/C.cs +++ b/csharp/ql/test/library-tests/dataflow/fields/C.cs @@ -1,11 +1,11 @@ public class C { - private Elem s1 = new Elem(); - private readonly Elem s2 = new Elem(); + private Elem s1 = Source(1); + private readonly Elem s2 = Source(2); private Elem s3; - private static Elem s4 = new Elem(); - private Elem s5 { get; set; } = new Elem(); - private Elem s6 { get => new Elem(); set { } } + private static Elem s4 = Source(4); + private Elem s5 { get; set; } = Source(5); + private Elem s6 { get => Source(6); set { } } void M1() { @@ -15,20 +15,22 @@ public class C private C() { - this.s3 = new Elem(); + this.s3 = Source(3); } public void M2() { - Sink(s1); - Sink(s2); - Sink(s3); - Sink(s4); - Sink(s5); - Sink(s6); + Sink(s1); // $ hasValueFlow=1 + Sink(s2); // $ hasValueFlow=2 + Sink(s3); // $ hasValueFlow=3 + Sink(s4); // $ hasValueFlow=4 + Sink(s5); // $ hasValueFlow=5 + Sink(s6); // $ hasValueFlow=6 } public static void Sink(object o) { } + static T Source(object source) => throw null; + public class Elem { } } diff --git a/csharp/ql/test/library-tests/dataflow/fields/D.cs b/csharp/ql/test/library-tests/dataflow/fields/D.cs index 505f56bd388..7a3fe985137 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/D.cs +++ b/csharp/ql/test/library-tests/dataflow/fields/D.cs @@ -26,26 +26,28 @@ public class D private void M() { - var o = new object(); + var o = Source(1); var d = Create(o, null, null); - Sink(d.AutoProp); // flow + Sink(d.AutoProp); // $ hasValueFlow=1 Sink(d.TrivialProp); // no flow Sink(d.trivialPropField); // no flow Sink(d.ComplexProp); // no flow - d = Create(null, o, null); + d = Create(null, Source(2), null); Sink(d.AutoProp); // no flow - Sink(d.TrivialProp); // flow - Sink(d.trivialPropField); // flow - Sink(d.ComplexProp); // flow + Sink(d.TrivialProp); // $ hasValueFlow=2 + Sink(d.trivialPropField); // $ hasValueFlow=2 + Sink(d.ComplexProp); // $ hasValueFlow=2 - d = Create(null, null, o); + d = Create(null, null, Source(3)); Sink(d.AutoProp); // no flow - Sink(d.TrivialProp); // flow - Sink(d.trivialPropField); // flow - Sink(d.ComplexProp); // flow + Sink(d.TrivialProp); // $ hasValueFlow=3 + Sink(d.trivialPropField); // $ hasValueFlow=3 + Sink(d.ComplexProp); // $ hasValueFlow=3 } public static void Sink(object o) { } + + static T Source(object source) => throw null; } diff --git a/csharp/ql/test/library-tests/dataflow/fields/E.cs b/csharp/ql/test/library-tests/dataflow/fields/E.cs index 45ee1994256..975952c8f64 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/E.cs +++ b/csharp/ql/test/library-tests/dataflow/fields/E.cs @@ -19,9 +19,9 @@ public class E private void M() { - var o = new object(); + var o = Source(1); var s = CreateS(o); - Sink(s.Field); // flow + Sink(s.Field); // $ hasValueFlow=1 s = new S(); NotASetter(s, o); @@ -29,4 +29,6 @@ public class E } public static void Sink(object o) { } + + static T Source(object source) => throw null; } diff --git a/csharp/ql/test/library-tests/dataflow/fields/F.cs b/csharp/ql/test/library-tests/dataflow/fields/F.cs index 81076f72a93..2e7cd9b6e2c 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/F.cs +++ b/csharp/ql/test/library-tests/dataflow/fields/F.cs @@ -7,23 +7,25 @@ public class F private void M() { - var o = new object(); + var o = Source(1); var f = Create(o, null); - Sink(f.Field1); // flow + Sink(f.Field1); // $ hasValueFlow=1 Sink(f.Field2); // no flow - f = Create(null, o); + f = Create(null, Source(2)); Sink(f.Field1); // no flow - Sink(f.Field2); // flow + Sink(f.Field2); // $ hasValueFlow=2 - f = new F() { Field1 = o }; - Sink(f.Field1); // flow + f = new F() { Field1 = Source(3) }; + Sink(f.Field1); // $ hasValueFlow=3 Sink(f.Field2); // no flow - f = new F() { Field2 = o }; + f = new F() { Field2 = Source(4) }; Sink(f.Field1); // no flow - Sink(f.Field2); // flow + Sink(f.Field2); // $ hasValueFlow=4 } public static void Sink(object o) { } + + static T Source(object source) => throw null; } diff --git a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected index e8a74f347dc..14e2c3d27b7 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected @@ -1,946 +1,1821 @@ +failures edges -| A.cs:5:17:5:23 | object creation of type C : C | A.cs:6:24:6:24 | access to local variable c : C | +| A.cs:5:17:5:28 | call to method Source : C | A.cs:6:24:6:24 | access to local variable c : C | +| A.cs:5:17:5:28 | call to method Source : C | A.cs:6:24:6:24 | access to local variable c : C | +| A.cs:6:17:6:25 | call to method Make [field c] : C | A.cs:7:14:7:14 | access to local variable b [field c] : C | | A.cs:6:17:6:25 | call to method Make [field c] : C | A.cs:7:14:7:14 | access to local variable b [field c] : C | | A.cs:6:24:6:24 | access to local variable c : C | A.cs:6:17:6:25 | call to method Make [field c] : C | +| A.cs:6:24:6:24 | access to local variable c : C | A.cs:6:17:6:25 | call to method Make [field c] : C | +| A.cs:6:24:6:24 | access to local variable c : C | A.cs:147:32:147:32 | c : C | | A.cs:6:24:6:24 | access to local variable c : C | A.cs:147:32:147:32 | c : C | | A.cs:7:14:7:14 | access to local variable b [field c] : C | A.cs:7:14:7:16 | access to field c | +| A.cs:7:14:7:14 | access to local variable b [field c] : C | A.cs:7:14:7:16 | access to field c | | A.cs:13:9:13:9 | [post] access to local variable b [field c] : C1 | A.cs:14:14:14:14 | access to local variable b [field c] : C1 | -| A.cs:13:15:13:22 | object creation of type C1 : C1 | A.cs:13:9:13:9 | [post] access to local variable b [field c] : C1 | -| A.cs:13:15:13:22 | object creation of type C1 : C1 | A.cs:145:27:145:27 | c : C1 | +| A.cs:13:9:13:9 | [post] access to local variable b [field c] : C1 | A.cs:14:14:14:14 | access to local variable b [field c] : C1 | +| A.cs:13:15:13:29 | call to method Source : C1 | A.cs:13:9:13:9 | [post] access to local variable b [field c] : C1 | +| A.cs:13:15:13:29 | call to method Source : C1 | A.cs:13:9:13:9 | [post] access to local variable b [field c] : C1 | +| A.cs:13:15:13:29 | call to method Source : C1 | A.cs:145:27:145:27 | c : C1 | +| A.cs:13:15:13:29 | call to method Source : C1 | A.cs:145:27:145:27 | c : C1 | +| A.cs:14:14:14:14 | access to local variable b [field c] : C1 | A.cs:14:14:14:20 | call to method Get | | A.cs:14:14:14:14 | access to local variable b [field c] : C1 | A.cs:14:14:14:20 | call to method Get | | A.cs:14:14:14:14 | access to local variable b [field c] : C1 | A.cs:146:18:146:20 | this [field c] : C1 | -| A.cs:15:15:15:28 | object creation of type B [field c] : C | A.cs:15:14:15:35 | call to method Get | -| A.cs:15:15:15:28 | object creation of type B [field c] : C | A.cs:146:18:146:20 | this [field c] : C | -| A.cs:15:21:15:27 | object creation of type C : C | A.cs:15:15:15:28 | object creation of type B [field c] : C | -| A.cs:15:21:15:27 | object creation of type C : C | A.cs:141:20:141:20 | c : C | -| A.cs:22:14:22:33 | call to method SetOnB [field c] : C2 | A.cs:24:14:24:15 | access to local variable b2 [field c] : C2 | -| A.cs:22:25:22:32 | object creation of type C2 : C2 | A.cs:22:14:22:33 | call to method SetOnB [field c] : C2 | -| A.cs:22:25:22:32 | object creation of type C2 : C2 | A.cs:42:29:42:29 | c : C2 | +| A.cs:14:14:14:14 | access to local variable b [field c] : C1 | A.cs:146:18:146:20 | this [field c] : C1 | +| A.cs:15:15:15:35 | object creation of type B [field c] : C | A.cs:15:14:15:42 | call to method Get | +| A.cs:15:15:15:35 | object creation of type B [field c] : C | A.cs:15:14:15:42 | call to method Get | +| A.cs:15:15:15:35 | object creation of type B [field c] : C | A.cs:146:18:146:20 | this [field c] : C | +| A.cs:15:15:15:35 | object creation of type B [field c] : C | A.cs:146:18:146:20 | this [field c] : C | +| A.cs:15:21:15:34 | call to method Source : C | A.cs:15:15:15:35 | object creation of type B [field c] : C | +| A.cs:15:21:15:34 | call to method Source : C | A.cs:15:15:15:35 | object creation of type B [field c] : C | +| A.cs:15:21:15:34 | call to method Source : C | A.cs:141:20:141:20 | c : C | +| A.cs:15:21:15:34 | call to method Source : C | A.cs:141:20:141:20 | c : C | +| A.cs:22:14:22:38 | call to method SetOnB [field c] : C2 | A.cs:24:14:24:15 | access to local variable b2 [field c] : C2 | +| A.cs:22:14:22:38 | call to method SetOnB [field c] : C2 | A.cs:24:14:24:15 | access to local variable b2 [field c] : C2 | +| A.cs:22:25:22:37 | call to method Source : C2 | A.cs:22:14:22:38 | call to method SetOnB [field c] : C2 | +| A.cs:22:25:22:37 | call to method Source : C2 | A.cs:22:14:22:38 | call to method SetOnB [field c] : C2 | +| A.cs:22:25:22:37 | call to method Source : C2 | A.cs:42:29:42:29 | c : C2 | +| A.cs:22:25:22:37 | call to method Source : C2 | A.cs:42:29:42:29 | c : C2 | | A.cs:24:14:24:15 | access to local variable b2 [field c] : C2 | A.cs:24:14:24:17 | access to field c | -| A.cs:31:14:31:37 | call to method SetOnBWrap [field c] : C2 | A.cs:33:14:33:15 | access to local variable b2 [field c] : C2 | -| A.cs:31:29:31:36 | object creation of type C2 : C2 | A.cs:31:14:31:37 | call to method SetOnBWrap [field c] : C2 | -| A.cs:31:29:31:36 | object creation of type C2 : C2 | A.cs:36:33:36:33 | c : C2 | +| A.cs:24:14:24:15 | access to local variable b2 [field c] : C2 | A.cs:24:14:24:17 | access to field c | +| A.cs:31:14:31:42 | call to method SetOnBWrap [field c] : C2 | A.cs:33:14:33:15 | access to local variable b2 [field c] : C2 | +| A.cs:31:14:31:42 | call to method SetOnBWrap [field c] : C2 | A.cs:33:14:33:15 | access to local variable b2 [field c] : C2 | +| A.cs:31:29:31:41 | call to method Source : C2 | A.cs:31:14:31:42 | call to method SetOnBWrap [field c] : C2 | +| A.cs:31:29:31:41 | call to method Source : C2 | A.cs:31:14:31:42 | call to method SetOnBWrap [field c] : C2 | +| A.cs:31:29:31:41 | call to method Source : C2 | A.cs:36:33:36:33 | c : C2 | +| A.cs:31:29:31:41 | call to method Source : C2 | A.cs:36:33:36:33 | c : C2 | +| A.cs:33:14:33:15 | access to local variable b2 [field c] : C2 | A.cs:33:14:33:17 | access to field c | | A.cs:33:14:33:15 | access to local variable b2 [field c] : C2 | A.cs:33:14:33:17 | access to field c | | A.cs:36:33:36:33 | c : C2 | A.cs:38:29:38:29 | access to parameter c : C2 | +| A.cs:36:33:36:33 | c : C2 | A.cs:38:29:38:29 | access to parameter c : C2 | +| A.cs:38:18:38:30 | call to method SetOnB [field c] : C2 | A.cs:39:16:39:28 | ... ? ... : ... [field c] : C2 | | A.cs:38:18:38:30 | call to method SetOnB [field c] : C2 | A.cs:39:16:39:28 | ... ? ... : ... [field c] : C2 | | A.cs:38:29:38:29 | access to parameter c : C2 | A.cs:38:18:38:30 | call to method SetOnB [field c] : C2 | +| A.cs:38:29:38:29 | access to parameter c : C2 | A.cs:38:18:38:30 | call to method SetOnB [field c] : C2 | +| A.cs:38:29:38:29 | access to parameter c : C2 | A.cs:42:29:42:29 | c : C2 | | A.cs:38:29:38:29 | access to parameter c : C2 | A.cs:42:29:42:29 | c : C2 | | A.cs:42:29:42:29 | c : C2 | A.cs:47:20:47:20 | access to parameter c : C2 | +| A.cs:42:29:42:29 | c : C2 | A.cs:47:20:47:20 | access to parameter c : C2 | +| A.cs:47:13:47:14 | [post] access to local variable b2 [field c] : C2 | A.cs:48:20:48:21 | access to local variable b2 [field c] : C2 | | A.cs:47:13:47:14 | [post] access to local variable b2 [field c] : C2 | A.cs:48:20:48:21 | access to local variable b2 [field c] : C2 | | A.cs:47:20:47:20 | access to parameter c : C2 | A.cs:47:13:47:14 | [post] access to local variable b2 [field c] : C2 | +| A.cs:47:20:47:20 | access to parameter c : C2 | A.cs:47:13:47:14 | [post] access to local variable b2 [field c] : C2 | | A.cs:47:20:47:20 | access to parameter c : C2 | A.cs:145:27:145:27 | c : C2 | -| A.cs:55:17:55:23 | object creation of type A : A | A.cs:57:16:57:16 | access to local variable a : A | +| A.cs:47:20:47:20 | access to parameter c : C2 | A.cs:145:27:145:27 | c : C2 | +| A.cs:55:17:55:28 | call to method Source : A | A.cs:57:16:57:16 | access to local variable a : A | +| A.cs:55:17:55:28 | call to method Source : A | A.cs:57:16:57:16 | access to local variable a : A | +| A.cs:57:9:57:10 | [post] access to local variable c1 [field a] : A | A.cs:58:12:58:13 | access to local variable c1 [field a] : A | | A.cs:57:9:57:10 | [post] access to local variable c1 [field a] : A | A.cs:58:12:58:13 | access to local variable c1 [field a] : A | | A.cs:57:16:57:16 | access to local variable a : A | A.cs:57:9:57:10 | [post] access to local variable c1 [field a] : A | +| A.cs:57:16:57:16 | access to local variable a : A | A.cs:57:9:57:10 | [post] access to local variable c1 [field a] : A | +| A.cs:58:12:58:13 | access to local variable c1 [field a] : A | A.cs:60:22:60:22 | c [field a] : A | | A.cs:58:12:58:13 | access to local variable c1 [field a] : A | A.cs:60:22:60:22 | c [field a] : A | | A.cs:60:22:60:22 | c [field a] : A | A.cs:64:19:64:23 | (...) ... [field a] : A | +| A.cs:60:22:60:22 | c [field a] : A | A.cs:64:19:64:23 | (...) ... [field a] : A | +| A.cs:64:19:64:23 | (...) ... [field a] : A | A.cs:64:18:64:26 | access to field a | | A.cs:64:19:64:23 | (...) ... [field a] : A | A.cs:64:18:64:26 | access to field a | | A.cs:83:9:83:9 | [post] access to parameter b [field c] : C | A.cs:88:12:88:12 | [post] access to local variable b [field c] : C | -| A.cs:83:15:83:21 | object creation of type C : C | A.cs:83:9:83:9 | [post] access to parameter b [field c] : C | -| A.cs:83:15:83:21 | object creation of type C : C | A.cs:145:27:145:27 | c : C | +| A.cs:83:9:83:9 | [post] access to parameter b [field c] : C | A.cs:88:12:88:12 | [post] access to local variable b [field c] : C | +| A.cs:83:15:83:26 | call to method Source : C | A.cs:83:9:83:9 | [post] access to parameter b [field c] : C | +| A.cs:83:15:83:26 | call to method Source : C | A.cs:83:9:83:9 | [post] access to parameter b [field c] : C | +| A.cs:83:15:83:26 | call to method Source : C | A.cs:145:27:145:27 | c : C | +| A.cs:83:15:83:26 | call to method Source : C | A.cs:145:27:145:27 | c : C | +| A.cs:88:12:88:12 | [post] access to local variable b [field c] : C | A.cs:89:14:89:14 | access to local variable b [field c] : C | | A.cs:88:12:88:12 | [post] access to local variable b [field c] : C | A.cs:89:14:89:14 | access to local variable b [field c] : C | | A.cs:89:14:89:14 | access to local variable b [field c] : C | A.cs:89:14:89:16 | access to field c | +| A.cs:89:14:89:14 | access to local variable b [field c] : C | A.cs:89:14:89:16 | access to field c | | A.cs:95:20:95:20 | b : B | A.cs:97:13:97:13 | access to parameter b : B | -| A.cs:97:13:97:13 | [post] access to parameter b [field c] : C | A.cs:98:22:98:36 | ... ? ... : ... [field c] : C | +| A.cs:95:20:95:20 | b : B | A.cs:97:13:97:13 | access to parameter b : B | +| A.cs:97:13:97:13 | [post] access to parameter b [field c] : C | A.cs:98:22:98:43 | ... ? ... : ... [field c] : C | +| A.cs:97:13:97:13 | [post] access to parameter b [field c] : C | A.cs:98:22:98:43 | ... ? ... : ... [field c] : C | | A.cs:97:13:97:13 | [post] access to parameter b [field c] : C | A.cs:105:23:105:23 | [post] access to local variable b [field c] : C | -| A.cs:97:13:97:13 | access to parameter b : B | A.cs:98:22:98:36 | ... ? ... : ... : B | -| A.cs:97:19:97:25 | object creation of type C : C | A.cs:97:13:97:13 | [post] access to parameter b [field c] : C | +| A.cs:97:13:97:13 | [post] access to parameter b [field c] : C | A.cs:105:23:105:23 | [post] access to local variable b [field c] : C | +| A.cs:97:13:97:13 | access to parameter b : B | A.cs:98:22:98:43 | ... ? ... : ... : B | +| A.cs:97:13:97:13 | access to parameter b : B | A.cs:98:22:98:43 | ... ? ... : ... : B | +| A.cs:97:19:97:32 | call to method Source : C | A.cs:97:13:97:13 | [post] access to parameter b [field c] : C | +| A.cs:97:19:97:32 | call to method Source : C | A.cs:97:13:97:13 | [post] access to parameter b [field c] : C | +| A.cs:98:13:98:16 | [post] this access [field b, field c] : C | A.cs:105:17:105:29 | object creation of type D [field b, field c] : C | | A.cs:98:13:98:16 | [post] this access [field b, field c] : C | A.cs:105:17:105:29 | object creation of type D [field b, field c] : C | | A.cs:98:13:98:16 | [post] this access [field b] : B | A.cs:105:17:105:29 | object creation of type D [field b] : B | -| A.cs:98:22:98:36 | ... ? ... : ... : B | A.cs:98:13:98:16 | [post] this access [field b] : B | -| A.cs:98:22:98:36 | ... ? ... : ... : B | A.cs:98:13:98:16 | [post] this access [field b] : B | -| A.cs:98:22:98:36 | ... ? ... : ... [field c] : C | A.cs:98:13:98:16 | [post] this access [field b, field c] : C | -| A.cs:98:30:98:36 | object creation of type B : B | A.cs:98:22:98:36 | ... ? ... : ... : B | -| A.cs:104:17:104:23 | object creation of type B : B | A.cs:105:23:105:23 | access to local variable b : B | +| A.cs:98:13:98:16 | [post] this access [field b] : B | A.cs:105:17:105:29 | object creation of type D [field b] : B | +| A.cs:98:22:98:43 | ... ? ... : ... : B | A.cs:98:13:98:16 | [post] this access [field b] : B | +| A.cs:98:22:98:43 | ... ? ... : ... : B | A.cs:98:13:98:16 | [post] this access [field b] : B | +| A.cs:98:22:98:43 | ... ? ... : ... : B | A.cs:98:13:98:16 | [post] this access [field b] : B | +| A.cs:98:22:98:43 | ... ? ... : ... : B | A.cs:98:13:98:16 | [post] this access [field b] : B | +| A.cs:98:22:98:43 | ... ? ... : ... [field c] : C | A.cs:98:13:98:16 | [post] this access [field b, field c] : C | +| A.cs:98:22:98:43 | ... ? ... : ... [field c] : C | A.cs:98:13:98:16 | [post] this access [field b, field c] : C | +| A.cs:98:30:98:43 | call to method Source : B | A.cs:98:22:98:43 | ... ? ... : ... : B | +| A.cs:98:30:98:43 | call to method Source : B | A.cs:98:22:98:43 | ... ? ... : ... : B | +| A.cs:104:17:104:30 | call to method Source : B | A.cs:105:23:105:23 | access to local variable b : B | +| A.cs:104:17:104:30 | call to method Source : B | A.cs:105:23:105:23 | access to local variable b : B | +| A.cs:105:17:105:29 | object creation of type D [field b, field c] : C | A.cs:107:14:107:14 | access to local variable d [field b, field c] : C | | A.cs:105:17:105:29 | object creation of type D [field b, field c] : C | A.cs:107:14:107:14 | access to local variable d [field b, field c] : C | | A.cs:105:17:105:29 | object creation of type D [field b] : B | A.cs:106:14:106:14 | access to local variable d [field b] : B | +| A.cs:105:17:105:29 | object creation of type D [field b] : B | A.cs:106:14:106:14 | access to local variable d [field b] : B | +| A.cs:105:23:105:23 | [post] access to local variable b [field c] : C | A.cs:108:14:108:14 | access to local variable b [field c] : C | | A.cs:105:23:105:23 | [post] access to local variable b [field c] : C | A.cs:108:14:108:14 | access to local variable b [field c] : C | | A.cs:105:23:105:23 | access to local variable b : B | A.cs:95:20:95:20 | b : B | +| A.cs:105:23:105:23 | access to local variable b : B | A.cs:95:20:95:20 | b : B | +| A.cs:105:23:105:23 | access to local variable b : B | A.cs:105:17:105:29 | object creation of type D [field b] : B | | A.cs:105:23:105:23 | access to local variable b : B | A.cs:105:17:105:29 | object creation of type D [field b] : B | | A.cs:106:14:106:14 | access to local variable d [field b] : B | A.cs:106:14:106:16 | access to field b | +| A.cs:106:14:106:14 | access to local variable d [field b] : B | A.cs:106:14:106:16 | access to field b | +| A.cs:107:14:107:14 | access to local variable d [field b, field c] : C | A.cs:107:14:107:16 | access to field b [field c] : C | | A.cs:107:14:107:14 | access to local variable d [field b, field c] : C | A.cs:107:14:107:16 | access to field b [field c] : C | | A.cs:107:14:107:16 | access to field b [field c] : C | A.cs:107:14:107:18 | access to field c | +| A.cs:107:14:107:16 | access to field b [field c] : C | A.cs:107:14:107:18 | access to field c | | A.cs:108:14:108:14 | access to local variable b [field c] : C | A.cs:108:14:108:16 | access to field c | -| A.cs:113:17:113:23 | object creation of type B : B | A.cs:114:29:114:29 | access to local variable b : B | +| A.cs:108:14:108:14 | access to local variable b [field c] : C | A.cs:108:14:108:16 | access to field c | +| A.cs:113:17:113:29 | call to method Source : B | A.cs:114:29:114:29 | access to local variable b : B | +| A.cs:113:17:113:29 | call to method Source : B | A.cs:114:29:114:29 | access to local variable b : B | +| A.cs:114:18:114:54 | object creation of type MyList [field head] : B | A.cs:115:35:115:36 | access to local variable l1 [field head] : B | | A.cs:114:18:114:54 | object creation of type MyList [field head] : B | A.cs:115:35:115:36 | access to local variable l1 [field head] : B | | A.cs:114:29:114:29 | access to local variable b : B | A.cs:114:18:114:54 | object creation of type MyList [field head] : B | +| A.cs:114:29:114:29 | access to local variable b : B | A.cs:114:18:114:54 | object creation of type MyList [field head] : B | +| A.cs:114:29:114:29 | access to local variable b : B | A.cs:157:25:157:28 | head : B | | A.cs:114:29:114:29 | access to local variable b : B | A.cs:157:25:157:28 | head : B | | A.cs:115:18:115:37 | object creation of type MyList [field next, field head] : B | A.cs:116:35:116:36 | access to local variable l2 [field next, field head] : B | +| A.cs:115:18:115:37 | object creation of type MyList [field next, field head] : B | A.cs:116:35:116:36 | access to local variable l2 [field next, field head] : B | +| A.cs:115:35:115:36 | access to local variable l1 [field head] : B | A.cs:115:18:115:37 | object creation of type MyList [field next, field head] : B | | A.cs:115:35:115:36 | access to local variable l1 [field head] : B | A.cs:115:18:115:37 | object creation of type MyList [field next, field head] : B | | A.cs:115:35:115:36 | access to local variable l1 [field head] : B | A.cs:157:38:157:41 | next [field head] : B | +| A.cs:115:35:115:36 | access to local variable l1 [field head] : B | A.cs:157:38:157:41 | next [field head] : B | +| A.cs:116:18:116:37 | object creation of type MyList [field next, field next, field head] : B | A.cs:119:14:119:15 | access to local variable l3 [field next, field next, field head] : B | | A.cs:116:18:116:37 | object creation of type MyList [field next, field next, field head] : B | A.cs:119:14:119:15 | access to local variable l3 [field next, field next, field head] : B | | A.cs:116:18:116:37 | object creation of type MyList [field next, field next, field head] : B | A.cs:121:41:121:41 | access to local variable l [field next, field next, field head] : B | +| A.cs:116:18:116:37 | object creation of type MyList [field next, field next, field head] : B | A.cs:121:41:121:41 | access to local variable l [field next, field next, field head] : B | +| A.cs:116:35:116:36 | access to local variable l2 [field next, field head] : B | A.cs:116:18:116:37 | object creation of type MyList [field next, field next, field head] : B | | A.cs:116:35:116:36 | access to local variable l2 [field next, field head] : B | A.cs:116:18:116:37 | object creation of type MyList [field next, field next, field head] : B | | A.cs:116:35:116:36 | access to local variable l2 [field next, field head] : B | A.cs:157:38:157:41 | next [field next, field head] : B | +| A.cs:116:35:116:36 | access to local variable l2 [field next, field head] : B | A.cs:157:38:157:41 | next [field next, field head] : B | +| A.cs:119:14:119:15 | access to local variable l3 [field next, field next, field head] : B | A.cs:119:14:119:20 | access to field next [field next, field head] : B | | A.cs:119:14:119:15 | access to local variable l3 [field next, field next, field head] : B | A.cs:119:14:119:20 | access to field next [field next, field head] : B | | A.cs:119:14:119:20 | access to field next [field next, field head] : B | A.cs:119:14:119:25 | access to field next [field head] : B | +| A.cs:119:14:119:20 | access to field next [field next, field head] : B | A.cs:119:14:119:25 | access to field next [field head] : B | +| A.cs:119:14:119:25 | access to field next [field head] : B | A.cs:119:14:119:30 | access to field head | | A.cs:119:14:119:25 | access to field next [field head] : B | A.cs:119:14:119:30 | access to field head | | A.cs:121:41:121:41 | access to local variable l [field next, field head] : B | A.cs:121:41:121:46 | access to field next [field head] : B | +| A.cs:121:41:121:41 | access to local variable l [field next, field head] : B | A.cs:121:41:121:46 | access to field next [field head] : B | +| A.cs:121:41:121:41 | access to local variable l [field next, field next, field head] : B | A.cs:121:41:121:46 | access to field next [field next, field head] : B | | A.cs:121:41:121:41 | access to local variable l [field next, field next, field head] : B | A.cs:121:41:121:46 | access to field next [field next, field head] : B | | A.cs:121:41:121:46 | access to field next [field head] : B | A.cs:123:18:123:18 | access to local variable l [field head] : B | +| A.cs:121:41:121:46 | access to field next [field head] : B | A.cs:123:18:123:18 | access to local variable l [field head] : B | +| A.cs:121:41:121:46 | access to field next [field next, field head] : B | A.cs:121:41:121:41 | access to local variable l [field next, field head] : B | | A.cs:121:41:121:46 | access to field next [field next, field head] : B | A.cs:121:41:121:41 | access to local variable l [field next, field head] : B | | A.cs:123:18:123:18 | access to local variable l [field head] : B | A.cs:123:18:123:23 | access to field head | +| A.cs:123:18:123:18 | access to local variable l [field head] : B | A.cs:123:18:123:23 | access to field head | +| A.cs:141:20:141:20 | c : C | A.cs:143:22:143:22 | access to parameter c : C | | A.cs:141:20:141:20 | c : C | A.cs:143:22:143:22 | access to parameter c : C | | A.cs:143:22:143:22 | access to parameter c : C | A.cs:143:13:143:16 | [post] this access [field c] : C | +| A.cs:143:22:143:22 | access to parameter c : C | A.cs:143:13:143:16 | [post] this access [field c] : C | +| A.cs:145:27:145:27 | c : C | A.cs:145:41:145:41 | access to parameter c : C | | A.cs:145:27:145:27 | c : C | A.cs:145:41:145:41 | access to parameter c : C | | A.cs:145:27:145:27 | c : C1 | A.cs:145:41:145:41 | access to parameter c : C1 | +| A.cs:145:27:145:27 | c : C1 | A.cs:145:41:145:41 | access to parameter c : C1 | +| A.cs:145:27:145:27 | c : C2 | A.cs:145:41:145:41 | access to parameter c : C2 | | A.cs:145:27:145:27 | c : C2 | A.cs:145:41:145:41 | access to parameter c : C2 | | A.cs:145:41:145:41 | access to parameter c : C | A.cs:145:32:145:35 | [post] this access [field c] : C | +| A.cs:145:41:145:41 | access to parameter c : C | A.cs:145:32:145:35 | [post] this access [field c] : C | +| A.cs:145:41:145:41 | access to parameter c : C1 | A.cs:145:32:145:35 | [post] this access [field c] : C1 | | A.cs:145:41:145:41 | access to parameter c : C1 | A.cs:145:32:145:35 | [post] this access [field c] : C1 | | A.cs:145:41:145:41 | access to parameter c : C2 | A.cs:145:32:145:35 | [post] this access [field c] : C2 | +| A.cs:145:41:145:41 | access to parameter c : C2 | A.cs:145:32:145:35 | [post] this access [field c] : C2 | +| A.cs:146:18:146:20 | this [field c] : C | A.cs:146:33:146:36 | this access [field c] : C | | A.cs:146:18:146:20 | this [field c] : C | A.cs:146:33:146:36 | this access [field c] : C | | A.cs:146:18:146:20 | this [field c] : C1 | A.cs:146:33:146:36 | this access [field c] : C1 | +| A.cs:146:18:146:20 | this [field c] : C1 | A.cs:146:33:146:36 | this access [field c] : C1 | +| A.cs:146:33:146:36 | this access [field c] : C | A.cs:146:33:146:38 | access to field c : C | | A.cs:146:33:146:36 | this access [field c] : C | A.cs:146:33:146:38 | access to field c : C | | A.cs:146:33:146:36 | this access [field c] : C1 | A.cs:146:33:146:38 | access to field c : C1 | +| A.cs:146:33:146:36 | this access [field c] : C1 | A.cs:146:33:146:38 | access to field c : C1 | +| A.cs:147:32:147:32 | c : C | A.cs:149:26:149:26 | access to parameter c : C | | A.cs:147:32:147:32 | c : C | A.cs:149:26:149:26 | access to parameter c : C | | A.cs:149:26:149:26 | access to parameter c : C | A.cs:141:20:141:20 | c : C | +| A.cs:149:26:149:26 | access to parameter c : C | A.cs:141:20:141:20 | c : C | +| A.cs:149:26:149:26 | access to parameter c : C | A.cs:149:20:149:27 | object creation of type B [field c] : C | | A.cs:149:26:149:26 | access to parameter c : C | A.cs:149:20:149:27 | object creation of type B [field c] : C | | A.cs:157:25:157:28 | head : B | A.cs:159:25:159:28 | access to parameter head : B | +| A.cs:157:25:157:28 | head : B | A.cs:159:25:159:28 | access to parameter head : B | +| A.cs:157:38:157:41 | next [field head] : B | A.cs:160:25:160:28 | access to parameter next [field head] : B | | A.cs:157:38:157:41 | next [field head] : B | A.cs:160:25:160:28 | access to parameter next [field head] : B | | A.cs:157:38:157:41 | next [field next, field head] : B | A.cs:160:25:160:28 | access to parameter next [field next, field head] : B | +| A.cs:157:38:157:41 | next [field next, field head] : B | A.cs:160:25:160:28 | access to parameter next [field next, field head] : B | +| A.cs:159:25:159:28 | access to parameter head : B | A.cs:159:13:159:16 | [post] this access [field head] : B | | A.cs:159:25:159:28 | access to parameter head : B | A.cs:159:13:159:16 | [post] this access [field head] : B | | A.cs:160:25:160:28 | access to parameter next [field head] : B | A.cs:160:13:160:16 | [post] this access [field next, field head] : B | +| A.cs:160:25:160:28 | access to parameter next [field head] : B | A.cs:160:13:160:16 | [post] this access [field next, field head] : B | | A.cs:160:25:160:28 | access to parameter next [field next, field head] : B | A.cs:160:13:160:16 | [post] this access [field next, field next, field head] : B | -| B.cs:5:17:5:26 | object creation of type Elem : Elem | B.cs:6:27:6:27 | access to local variable e : Elem | +| A.cs:160:25:160:28 | access to parameter next [field next, field head] : B | A.cs:160:13:160:16 | [post] this access [field next, field next, field head] : B | +| B.cs:5:17:5:31 | call to method Source : Elem | B.cs:6:27:6:27 | access to local variable e : Elem | +| B.cs:5:17:5:31 | call to method Source : Elem | B.cs:6:27:6:27 | access to local variable e : Elem | +| B.cs:6:18:6:34 | object creation of type Box1 [field elem1] : Elem | B.cs:7:27:7:28 | access to local variable b1 [field elem1] : Elem | | B.cs:6:18:6:34 | object creation of type Box1 [field elem1] : Elem | B.cs:7:27:7:28 | access to local variable b1 [field elem1] : Elem | | B.cs:6:27:6:27 | access to local variable e : Elem | B.cs:6:18:6:34 | object creation of type Box1 [field elem1] : Elem | +| B.cs:6:27:6:27 | access to local variable e : Elem | B.cs:6:18:6:34 | object creation of type Box1 [field elem1] : Elem | +| B.cs:6:27:6:27 | access to local variable e : Elem | B.cs:29:26:29:27 | e1 : Elem | | B.cs:6:27:6:27 | access to local variable e : Elem | B.cs:29:26:29:27 | e1 : Elem | | B.cs:7:18:7:29 | object creation of type Box2 [field box1, field elem1] : Elem | B.cs:8:14:8:15 | access to local variable b2 [field box1, field elem1] : Elem | +| B.cs:7:18:7:29 | object creation of type Box2 [field box1, field elem1] : Elem | B.cs:8:14:8:15 | access to local variable b2 [field box1, field elem1] : Elem | +| B.cs:7:27:7:28 | access to local variable b1 [field elem1] : Elem | B.cs:7:18:7:29 | object creation of type Box2 [field box1, field elem1] : Elem | | B.cs:7:27:7:28 | access to local variable b1 [field elem1] : Elem | B.cs:7:18:7:29 | object creation of type Box2 [field box1, field elem1] : Elem | | B.cs:7:27:7:28 | access to local variable b1 [field elem1] : Elem | B.cs:39:26:39:27 | b1 [field elem1] : Elem | +| B.cs:7:27:7:28 | access to local variable b1 [field elem1] : Elem | B.cs:39:26:39:27 | b1 [field elem1] : Elem | +| B.cs:8:14:8:15 | access to local variable b2 [field box1, field elem1] : Elem | B.cs:8:14:8:20 | access to field box1 [field elem1] : Elem | | B.cs:8:14:8:15 | access to local variable b2 [field box1, field elem1] : Elem | B.cs:8:14:8:20 | access to field box1 [field elem1] : Elem | | B.cs:8:14:8:20 | access to field box1 [field elem1] : Elem | B.cs:8:14:8:26 | access to field elem1 | -| B.cs:14:17:14:26 | object creation of type Elem : Elem | B.cs:15:33:15:33 | access to local variable e : Elem | +| B.cs:8:14:8:20 | access to field box1 [field elem1] : Elem | B.cs:8:14:8:26 | access to field elem1 | +| B.cs:14:17:14:31 | call to method Source : Elem | B.cs:15:33:15:33 | access to local variable e : Elem | +| B.cs:14:17:14:31 | call to method Source : Elem | B.cs:15:33:15:33 | access to local variable e : Elem | +| B.cs:15:18:15:34 | object creation of type Box1 [field elem2] : Elem | B.cs:16:27:16:28 | access to local variable b1 [field elem2] : Elem | | B.cs:15:18:15:34 | object creation of type Box1 [field elem2] : Elem | B.cs:16:27:16:28 | access to local variable b1 [field elem2] : Elem | | B.cs:15:33:15:33 | access to local variable e : Elem | B.cs:15:18:15:34 | object creation of type Box1 [field elem2] : Elem | +| B.cs:15:33:15:33 | access to local variable e : Elem | B.cs:15:18:15:34 | object creation of type Box1 [field elem2] : Elem | +| B.cs:15:33:15:33 | access to local variable e : Elem | B.cs:29:35:29:36 | e2 : Elem | | B.cs:15:33:15:33 | access to local variable e : Elem | B.cs:29:35:29:36 | e2 : Elem | | B.cs:16:18:16:29 | object creation of type Box2 [field box1, field elem2] : Elem | B.cs:18:14:18:15 | access to local variable b2 [field box1, field elem2] : Elem | +| B.cs:16:18:16:29 | object creation of type Box2 [field box1, field elem2] : Elem | B.cs:18:14:18:15 | access to local variable b2 [field box1, field elem2] : Elem | +| B.cs:16:27:16:28 | access to local variable b1 [field elem2] : Elem | B.cs:16:18:16:29 | object creation of type Box2 [field box1, field elem2] : Elem | | B.cs:16:27:16:28 | access to local variable b1 [field elem2] : Elem | B.cs:16:18:16:29 | object creation of type Box2 [field box1, field elem2] : Elem | | B.cs:16:27:16:28 | access to local variable b1 [field elem2] : Elem | B.cs:39:26:39:27 | b1 [field elem2] : Elem | +| B.cs:16:27:16:28 | access to local variable b1 [field elem2] : Elem | B.cs:39:26:39:27 | b1 [field elem2] : Elem | +| B.cs:18:14:18:15 | access to local variable b2 [field box1, field elem2] : Elem | B.cs:18:14:18:20 | access to field box1 [field elem2] : Elem | | B.cs:18:14:18:15 | access to local variable b2 [field box1, field elem2] : Elem | B.cs:18:14:18:20 | access to field box1 [field elem2] : Elem | | B.cs:18:14:18:20 | access to field box1 [field elem2] : Elem | B.cs:18:14:18:26 | access to field elem2 | +| B.cs:18:14:18:20 | access to field box1 [field elem2] : Elem | B.cs:18:14:18:26 | access to field elem2 | +| B.cs:29:26:29:27 | e1 : Elem | B.cs:31:26:31:27 | access to parameter e1 : Elem | | B.cs:29:26:29:27 | e1 : Elem | B.cs:31:26:31:27 | access to parameter e1 : Elem | | B.cs:29:35:29:36 | e2 : Elem | B.cs:32:26:32:27 | access to parameter e2 : Elem | +| B.cs:29:35:29:36 | e2 : Elem | B.cs:32:26:32:27 | access to parameter e2 : Elem | +| B.cs:31:26:31:27 | access to parameter e1 : Elem | B.cs:31:13:31:16 | [post] this access [field elem1] : Elem | | B.cs:31:26:31:27 | access to parameter e1 : Elem | B.cs:31:13:31:16 | [post] this access [field elem1] : Elem | | B.cs:32:26:32:27 | access to parameter e2 : Elem | B.cs:32:13:32:16 | [post] this access [field elem2] : Elem | +| B.cs:32:26:32:27 | access to parameter e2 : Elem | B.cs:32:13:32:16 | [post] this access [field elem2] : Elem | +| B.cs:39:26:39:27 | b1 [field elem1] : Elem | B.cs:41:25:41:26 | access to parameter b1 [field elem1] : Elem | | B.cs:39:26:39:27 | b1 [field elem1] : Elem | B.cs:41:25:41:26 | access to parameter b1 [field elem1] : Elem | | B.cs:39:26:39:27 | b1 [field elem2] : Elem | B.cs:41:25:41:26 | access to parameter b1 [field elem2] : Elem | +| B.cs:39:26:39:27 | b1 [field elem2] : Elem | B.cs:41:25:41:26 | access to parameter b1 [field elem2] : Elem | +| B.cs:41:25:41:26 | access to parameter b1 [field elem1] : Elem | B.cs:41:13:41:16 | [post] this access [field box1, field elem1] : Elem | | B.cs:41:25:41:26 | access to parameter b1 [field elem1] : Elem | B.cs:41:13:41:16 | [post] this access [field box1, field elem1] : Elem | | B.cs:41:25:41:26 | access to parameter b1 [field elem2] : Elem | B.cs:41:13:41:16 | [post] this access [field box1, field elem2] : Elem | +| B.cs:41:25:41:26 | access to parameter b1 [field elem2] : Elem | B.cs:41:13:41:16 | [post] this access [field box1, field elem2] : Elem | | C.cs:3:18:3:19 | [post] this access [field s1] : Elem | C.cs:12:15:12:21 | object creation of type C [field s1] : Elem | -| C.cs:3:23:3:32 | object creation of type Elem : Elem | C.cs:3:18:3:19 | [post] this access [field s1] : Elem | +| C.cs:3:18:3:19 | [post] this access [field s1] : Elem | C.cs:12:15:12:21 | object creation of type C [field s1] : Elem | +| C.cs:3:23:3:37 | call to method Source : Elem | C.cs:3:18:3:19 | [post] this access [field s1] : Elem | +| C.cs:3:23:3:37 | call to method Source : Elem | C.cs:3:18:3:19 | [post] this access [field s1] : Elem | | C.cs:4:27:4:28 | [post] this access [field s2] : Elem | C.cs:12:15:12:21 | object creation of type C [field s2] : Elem | -| C.cs:4:32:4:41 | object creation of type Elem : Elem | C.cs:4:27:4:28 | [post] this access [field s2] : Elem | -| C.cs:6:30:6:39 | object creation of type Elem : Elem | C.cs:26:14:26:15 | access to field s4 | +| C.cs:4:27:4:28 | [post] this access [field s2] : Elem | C.cs:12:15:12:21 | object creation of type C [field s2] : Elem | +| C.cs:4:32:4:46 | call to method Source : Elem | C.cs:4:27:4:28 | [post] this access [field s2] : Elem | +| C.cs:4:32:4:46 | call to method Source : Elem | C.cs:4:27:4:28 | [post] this access [field s2] : Elem | +| C.cs:6:30:6:44 | call to method Source : Elem | C.cs:26:14:26:15 | access to field s4 | +| C.cs:6:30:6:44 | call to method Source : Elem | C.cs:26:14:26:15 | access to field s4 | | C.cs:7:18:7:19 | [post] this access [property s5] : Elem | C.cs:12:15:12:21 | object creation of type C [property s5] : Elem | -| C.cs:7:37:7:46 | object creation of type Elem : Elem | C.cs:7:18:7:19 | [post] this access [property s5] : Elem | -| C.cs:8:30:8:39 | object creation of type Elem : Elem | C.cs:28:14:28:15 | access to property s6 | +| C.cs:7:18:7:19 | [post] this access [property s5] : Elem | C.cs:12:15:12:21 | object creation of type C [property s5] : Elem | +| C.cs:7:37:7:51 | call to method Source : Elem | C.cs:7:18:7:19 | [post] this access [property s5] : Elem | +| C.cs:7:37:7:51 | call to method Source : Elem | C.cs:7:18:7:19 | [post] this access [property s5] : Elem | +| C.cs:8:30:8:44 | call to method Source : Elem | C.cs:28:14:28:15 | access to property s6 | +| C.cs:8:30:8:44 | call to method Source : Elem | C.cs:28:14:28:15 | access to property s6 | +| C.cs:12:15:12:21 | object creation of type C [field s1] : Elem | C.cs:13:9:13:9 | access to local variable c [field s1] : Elem | | C.cs:12:15:12:21 | object creation of type C [field s1] : Elem | C.cs:13:9:13:9 | access to local variable c [field s1] : Elem | | C.cs:12:15:12:21 | object creation of type C [field s2] : Elem | C.cs:13:9:13:9 | access to local variable c [field s2] : Elem | +| C.cs:12:15:12:21 | object creation of type C [field s2] : Elem | C.cs:13:9:13:9 | access to local variable c [field s2] : Elem | +| C.cs:12:15:12:21 | object creation of type C [field s3] : Elem | C.cs:13:9:13:9 | access to local variable c [field s3] : Elem | | C.cs:12:15:12:21 | object creation of type C [field s3] : Elem | C.cs:13:9:13:9 | access to local variable c [field s3] : Elem | | C.cs:12:15:12:21 | object creation of type C [property s5] : Elem | C.cs:13:9:13:9 | access to local variable c [property s5] : Elem | +| C.cs:12:15:12:21 | object creation of type C [property s5] : Elem | C.cs:13:9:13:9 | access to local variable c [property s5] : Elem | +| C.cs:13:9:13:9 | access to local variable c [field s1] : Elem | C.cs:21:17:21:18 | this [field s1] : Elem | | C.cs:13:9:13:9 | access to local variable c [field s1] : Elem | C.cs:21:17:21:18 | this [field s1] : Elem | | C.cs:13:9:13:9 | access to local variable c [field s2] : Elem | C.cs:21:17:21:18 | this [field s2] : Elem | +| C.cs:13:9:13:9 | access to local variable c [field s2] : Elem | C.cs:21:17:21:18 | this [field s2] : Elem | +| C.cs:13:9:13:9 | access to local variable c [field s3] : Elem | C.cs:21:17:21:18 | this [field s3] : Elem | | C.cs:13:9:13:9 | access to local variable c [field s3] : Elem | C.cs:21:17:21:18 | this [field s3] : Elem | | C.cs:13:9:13:9 | access to local variable c [property s5] : Elem | C.cs:21:17:21:18 | this [property s5] : Elem | +| C.cs:13:9:13:9 | access to local variable c [property s5] : Elem | C.cs:21:17:21:18 | this [property s5] : Elem | | C.cs:18:9:18:12 | [post] this access [field s3] : Elem | C.cs:12:15:12:21 | object creation of type C [field s3] : Elem | -| C.cs:18:19:18:28 | object creation of type Elem : Elem | C.cs:18:9:18:12 | [post] this access [field s3] : Elem | +| C.cs:18:9:18:12 | [post] this access [field s3] : Elem | C.cs:12:15:12:21 | object creation of type C [field s3] : Elem | +| C.cs:18:19:18:33 | call to method Source : Elem | C.cs:18:9:18:12 | [post] this access [field s3] : Elem | +| C.cs:18:19:18:33 | call to method Source : Elem | C.cs:18:9:18:12 | [post] this access [field s3] : Elem | +| C.cs:21:17:21:18 | this [field s1] : Elem | C.cs:23:14:23:15 | this access [field s1] : Elem | | C.cs:21:17:21:18 | this [field s1] : Elem | C.cs:23:14:23:15 | this access [field s1] : Elem | | C.cs:21:17:21:18 | this [field s2] : Elem | C.cs:24:14:24:15 | this access [field s2] : Elem | +| C.cs:21:17:21:18 | this [field s2] : Elem | C.cs:24:14:24:15 | this access [field s2] : Elem | +| C.cs:21:17:21:18 | this [field s3] : Elem | C.cs:25:14:25:15 | this access [field s3] : Elem | | C.cs:21:17:21:18 | this [field s3] : Elem | C.cs:25:14:25:15 | this access [field s3] : Elem | | C.cs:21:17:21:18 | this [property s5] : Elem | C.cs:27:14:27:15 | this access [property s5] : Elem | +| C.cs:21:17:21:18 | this [property s5] : Elem | C.cs:27:14:27:15 | this access [property s5] : Elem | +| C.cs:23:14:23:15 | this access [field s1] : Elem | C.cs:23:14:23:15 | access to field s1 | | C.cs:23:14:23:15 | this access [field s1] : Elem | C.cs:23:14:23:15 | access to field s1 | | C.cs:24:14:24:15 | this access [field s2] : Elem | C.cs:24:14:24:15 | access to field s2 | +| C.cs:24:14:24:15 | this access [field s2] : Elem | C.cs:24:14:24:15 | access to field s2 | +| C.cs:25:14:25:15 | this access [field s3] : Elem | C.cs:25:14:25:15 | access to field s3 | | C.cs:25:14:25:15 | this access [field s3] : Elem | C.cs:25:14:25:15 | access to field s3 | | C.cs:27:14:27:15 | this access [property s5] : Elem | C.cs:27:14:27:15 | access to property s5 | +| C.cs:27:14:27:15 | this access [property s5] : Elem | C.cs:27:14:27:15 | access to property s5 | +| D.cs:8:9:8:11 | this [field trivialPropField] : Object | D.cs:8:22:8:25 | this access [field trivialPropField] : Object | | D.cs:8:9:8:11 | this [field trivialPropField] : Object | D.cs:8:22:8:25 | this access [field trivialPropField] : Object | | D.cs:8:22:8:25 | this access [field trivialPropField] : Object | D.cs:8:22:8:42 | access to field trivialPropField : Object | +| D.cs:8:22:8:25 | this access [field trivialPropField] : Object | D.cs:8:22:8:42 | access to field trivialPropField : Object | +| D.cs:9:9:9:11 | value : Object | D.cs:9:39:9:43 | access to parameter value : Object | | D.cs:9:9:9:11 | value : Object | D.cs:9:39:9:43 | access to parameter value : Object | | D.cs:9:39:9:43 | access to parameter value : Object | D.cs:9:15:9:18 | [post] this access [field trivialPropField] : Object | +| D.cs:9:39:9:43 | access to parameter value : Object | D.cs:9:15:9:18 | [post] this access [field trivialPropField] : Object | +| D.cs:14:9:14:11 | this [field trivialPropField] : Object | D.cs:14:22:14:25 | this access [field trivialPropField] : Object | | D.cs:14:9:14:11 | this [field trivialPropField] : Object | D.cs:14:22:14:25 | this access [field trivialPropField] : Object | | D.cs:14:22:14:25 | this access [field trivialPropField] : Object | D.cs:14:22:14:42 | access to field trivialPropField : Object | +| D.cs:14:22:14:25 | this access [field trivialPropField] : Object | D.cs:14:22:14:42 | access to field trivialPropField : Object | +| D.cs:15:9:15:11 | value : Object | D.cs:15:34:15:38 | access to parameter value : Object | | D.cs:15:9:15:11 | value : Object | D.cs:15:34:15:38 | access to parameter value : Object | | D.cs:15:34:15:38 | access to parameter value : Object | D.cs:9:9:9:11 | value : Object | +| D.cs:15:34:15:38 | access to parameter value : Object | D.cs:9:9:9:11 | value : Object | +| D.cs:15:34:15:38 | access to parameter value : Object | D.cs:15:15:15:18 | [post] this access [field trivialPropField] : Object | | D.cs:15:34:15:38 | access to parameter value : Object | D.cs:15:15:15:18 | [post] this access [field trivialPropField] : Object | | D.cs:18:28:18:29 | o1 : Object | D.cs:21:24:21:25 | access to parameter o1 : Object | +| D.cs:18:28:18:29 | o1 : Object | D.cs:21:24:21:25 | access to parameter o1 : Object | +| D.cs:18:39:18:40 | o2 : Object | D.cs:22:27:22:28 | access to parameter o2 : Object | | D.cs:18:39:18:40 | o2 : Object | D.cs:22:27:22:28 | access to parameter o2 : Object | | D.cs:18:50:18:51 | o3 : Object | D.cs:23:27:23:28 | access to parameter o3 : Object | +| D.cs:18:50:18:51 | o3 : Object | D.cs:23:27:23:28 | access to parameter o3 : Object | +| D.cs:21:9:21:11 | [post] access to local variable ret [property AutoProp] : Object | D.cs:24:16:24:18 | access to local variable ret [property AutoProp] : Object | | D.cs:21:9:21:11 | [post] access to local variable ret [property AutoProp] : Object | D.cs:24:16:24:18 | access to local variable ret [property AutoProp] : Object | | D.cs:21:24:21:25 | access to parameter o1 : Object | D.cs:21:9:21:11 | [post] access to local variable ret [property AutoProp] : Object | +| D.cs:21:24:21:25 | access to parameter o1 : Object | D.cs:21:9:21:11 | [post] access to local variable ret [property AutoProp] : Object | +| D.cs:22:9:22:11 | [post] access to local variable ret [field trivialPropField] : Object | D.cs:24:16:24:18 | access to local variable ret [field trivialPropField] : Object | | D.cs:22:9:22:11 | [post] access to local variable ret [field trivialPropField] : Object | D.cs:24:16:24:18 | access to local variable ret [field trivialPropField] : Object | | D.cs:22:27:22:28 | access to parameter o2 : Object | D.cs:9:9:9:11 | value : Object | +| D.cs:22:27:22:28 | access to parameter o2 : Object | D.cs:9:9:9:11 | value : Object | +| D.cs:22:27:22:28 | access to parameter o2 : Object | D.cs:22:9:22:11 | [post] access to local variable ret [field trivialPropField] : Object | | D.cs:22:27:22:28 | access to parameter o2 : Object | D.cs:22:9:22:11 | [post] access to local variable ret [field trivialPropField] : Object | | D.cs:23:9:23:11 | [post] access to local variable ret [field trivialPropField] : Object | D.cs:24:16:24:18 | access to local variable ret [field trivialPropField] : Object | +| D.cs:23:9:23:11 | [post] access to local variable ret [field trivialPropField] : Object | D.cs:24:16:24:18 | access to local variable ret [field trivialPropField] : Object | +| D.cs:23:27:23:28 | access to parameter o3 : Object | D.cs:15:9:15:11 | value : Object | | D.cs:23:27:23:28 | access to parameter o3 : Object | D.cs:15:9:15:11 | value : Object | | D.cs:23:27:23:28 | access to parameter o3 : Object | D.cs:23:9:23:11 | [post] access to local variable ret [field trivialPropField] : Object | -| D.cs:29:17:29:28 | object creation of type Object : Object | D.cs:31:24:31:24 | access to local variable o : Object | -| D.cs:29:17:29:28 | object creation of type Object : Object | D.cs:37:26:37:26 | access to local variable o : Object | -| D.cs:29:17:29:28 | object creation of type Object : Object | D.cs:43:32:43:32 | access to local variable o : Object | +| D.cs:23:27:23:28 | access to parameter o3 : Object | D.cs:23:9:23:11 | [post] access to local variable ret [field trivialPropField] : Object | +| D.cs:29:17:29:33 | call to method Source : Object | D.cs:31:24:31:24 | access to local variable o : Object | +| D.cs:29:17:29:33 | call to method Source : Object | D.cs:31:24:31:24 | access to local variable o : Object | +| D.cs:31:17:31:37 | call to method Create [property AutoProp] : Object | D.cs:32:14:32:14 | access to local variable d [property AutoProp] : Object | | D.cs:31:17:31:37 | call to method Create [property AutoProp] : Object | D.cs:32:14:32:14 | access to local variable d [property AutoProp] : Object | | D.cs:31:24:31:24 | access to local variable o : Object | D.cs:18:28:18:29 | o1 : Object | +| D.cs:31:24:31:24 | access to local variable o : Object | D.cs:18:28:18:29 | o1 : Object | +| D.cs:31:24:31:24 | access to local variable o : Object | D.cs:31:17:31:37 | call to method Create [property AutoProp] : Object | | D.cs:31:24:31:24 | access to local variable o : Object | D.cs:31:17:31:37 | call to method Create [property AutoProp] : Object | | D.cs:32:14:32:14 | access to local variable d [property AutoProp] : Object | D.cs:32:14:32:23 | access to property AutoProp | -| D.cs:37:13:37:33 | call to method Create [field trivialPropField] : Object | D.cs:39:14:39:14 | access to local variable d [field trivialPropField] : Object | -| D.cs:37:13:37:33 | call to method Create [field trivialPropField] : Object | D.cs:40:14:40:14 | access to local variable d [field trivialPropField] : Object | -| D.cs:37:13:37:33 | call to method Create [field trivialPropField] : Object | D.cs:41:14:41:14 | access to local variable d [field trivialPropField] : Object | -| D.cs:37:26:37:26 | access to local variable o : Object | D.cs:18:39:18:40 | o2 : Object | -| D.cs:37:26:37:26 | access to local variable o : Object | D.cs:37:13:37:33 | call to method Create [field trivialPropField] : Object | +| D.cs:32:14:32:14 | access to local variable d [property AutoProp] : Object | D.cs:32:14:32:23 | access to property AutoProp | +| D.cs:37:13:37:49 | call to method Create [field trivialPropField] : Object | D.cs:39:14:39:14 | access to local variable d [field trivialPropField] : Object | +| D.cs:37:13:37:49 | call to method Create [field trivialPropField] : Object | D.cs:39:14:39:14 | access to local variable d [field trivialPropField] : Object | +| D.cs:37:13:37:49 | call to method Create [field trivialPropField] : Object | D.cs:40:14:40:14 | access to local variable d [field trivialPropField] : Object | +| D.cs:37:13:37:49 | call to method Create [field trivialPropField] : Object | D.cs:40:14:40:14 | access to local variable d [field trivialPropField] : Object | +| D.cs:37:13:37:49 | call to method Create [field trivialPropField] : Object | D.cs:41:14:41:14 | access to local variable d [field trivialPropField] : Object | +| D.cs:37:13:37:49 | call to method Create [field trivialPropField] : Object | D.cs:41:14:41:14 | access to local variable d [field trivialPropField] : Object | +| D.cs:37:26:37:42 | call to method Source : Object | D.cs:18:39:18:40 | o2 : Object | +| D.cs:37:26:37:42 | call to method Source : Object | D.cs:18:39:18:40 | o2 : Object | +| D.cs:37:26:37:42 | call to method Source : Object | D.cs:37:13:37:49 | call to method Create [field trivialPropField] : Object | +| D.cs:37:26:37:42 | call to method Source : Object | D.cs:37:13:37:49 | call to method Create [field trivialPropField] : Object | +| D.cs:39:14:39:14 | access to local variable d [field trivialPropField] : Object | D.cs:8:9:8:11 | this [field trivialPropField] : Object | | D.cs:39:14:39:14 | access to local variable d [field trivialPropField] : Object | D.cs:8:9:8:11 | this [field trivialPropField] : Object | | D.cs:39:14:39:14 | access to local variable d [field trivialPropField] : Object | D.cs:39:14:39:26 | access to property TrivialProp | +| D.cs:39:14:39:14 | access to local variable d [field trivialPropField] : Object | D.cs:39:14:39:26 | access to property TrivialProp | +| D.cs:40:14:40:14 | access to local variable d [field trivialPropField] : Object | D.cs:40:14:40:31 | access to field trivialPropField | | D.cs:40:14:40:14 | access to local variable d [field trivialPropField] : Object | D.cs:40:14:40:31 | access to field trivialPropField | | D.cs:41:14:41:14 | access to local variable d [field trivialPropField] : Object | D.cs:14:9:14:11 | this [field trivialPropField] : Object | +| D.cs:41:14:41:14 | access to local variable d [field trivialPropField] : Object | D.cs:14:9:14:11 | this [field trivialPropField] : Object | | D.cs:41:14:41:14 | access to local variable d [field trivialPropField] : Object | D.cs:41:14:41:26 | access to property ComplexProp | -| D.cs:43:13:43:33 | call to method Create [field trivialPropField] : Object | D.cs:45:14:45:14 | access to local variable d [field trivialPropField] : Object | -| D.cs:43:13:43:33 | call to method Create [field trivialPropField] : Object | D.cs:46:14:46:14 | access to local variable d [field trivialPropField] : Object | -| D.cs:43:13:43:33 | call to method Create [field trivialPropField] : Object | D.cs:47:14:47:14 | access to local variable d [field trivialPropField] : Object | -| D.cs:43:32:43:32 | access to local variable o : Object | D.cs:18:50:18:51 | o3 : Object | -| D.cs:43:32:43:32 | access to local variable o : Object | D.cs:43:13:43:33 | call to method Create [field trivialPropField] : Object | +| D.cs:41:14:41:14 | access to local variable d [field trivialPropField] : Object | D.cs:41:14:41:26 | access to property ComplexProp | +| D.cs:43:13:43:49 | call to method Create [field trivialPropField] : Object | D.cs:45:14:45:14 | access to local variable d [field trivialPropField] : Object | +| D.cs:43:13:43:49 | call to method Create [field trivialPropField] : Object | D.cs:45:14:45:14 | access to local variable d [field trivialPropField] : Object | +| D.cs:43:13:43:49 | call to method Create [field trivialPropField] : Object | D.cs:46:14:46:14 | access to local variable d [field trivialPropField] : Object | +| D.cs:43:13:43:49 | call to method Create [field trivialPropField] : Object | D.cs:46:14:46:14 | access to local variable d [field trivialPropField] : Object | +| D.cs:43:13:43:49 | call to method Create [field trivialPropField] : Object | D.cs:47:14:47:14 | access to local variable d [field trivialPropField] : Object | +| D.cs:43:13:43:49 | call to method Create [field trivialPropField] : Object | D.cs:47:14:47:14 | access to local variable d [field trivialPropField] : Object | +| D.cs:43:32:43:48 | call to method Source : Object | D.cs:18:50:18:51 | o3 : Object | +| D.cs:43:32:43:48 | call to method Source : Object | D.cs:18:50:18:51 | o3 : Object | +| D.cs:43:32:43:48 | call to method Source : Object | D.cs:43:13:43:49 | call to method Create [field trivialPropField] : Object | +| D.cs:43:32:43:48 | call to method Source : Object | D.cs:43:13:43:49 | call to method Create [field trivialPropField] : Object | +| D.cs:45:14:45:14 | access to local variable d [field trivialPropField] : Object | D.cs:8:9:8:11 | this [field trivialPropField] : Object | | D.cs:45:14:45:14 | access to local variable d [field trivialPropField] : Object | D.cs:8:9:8:11 | this [field trivialPropField] : Object | | D.cs:45:14:45:14 | access to local variable d [field trivialPropField] : Object | D.cs:45:14:45:26 | access to property TrivialProp | +| D.cs:45:14:45:14 | access to local variable d [field trivialPropField] : Object | D.cs:45:14:45:26 | access to property TrivialProp | +| D.cs:46:14:46:14 | access to local variable d [field trivialPropField] : Object | D.cs:46:14:46:31 | access to field trivialPropField | | D.cs:46:14:46:14 | access to local variable d [field trivialPropField] : Object | D.cs:46:14:46:31 | access to field trivialPropField | | D.cs:47:14:47:14 | access to local variable d [field trivialPropField] : Object | D.cs:14:9:14:11 | this [field trivialPropField] : Object | +| D.cs:47:14:47:14 | access to local variable d [field trivialPropField] : Object | D.cs:14:9:14:11 | this [field trivialPropField] : Object | +| D.cs:47:14:47:14 | access to local variable d [field trivialPropField] : Object | D.cs:47:14:47:26 | access to property ComplexProp | | D.cs:47:14:47:14 | access to local variable d [field trivialPropField] : Object | D.cs:47:14:47:26 | access to property ComplexProp | | E.cs:8:29:8:29 | o : Object | E.cs:11:21:11:21 | access to parameter o : Object | +| E.cs:8:29:8:29 | o : Object | E.cs:11:21:11:21 | access to parameter o : Object | +| E.cs:11:9:11:11 | [post] access to local variable ret [field Field] : Object | E.cs:12:16:12:18 | access to local variable ret [field Field] : Object | | E.cs:11:9:11:11 | [post] access to local variable ret [field Field] : Object | E.cs:12:16:12:18 | access to local variable ret [field Field] : Object | | E.cs:11:21:11:21 | access to parameter o : Object | E.cs:11:9:11:11 | [post] access to local variable ret [field Field] : Object | -| E.cs:22:17:22:28 | object creation of type Object : Object | E.cs:23:25:23:25 | access to local variable o : Object | +| E.cs:11:21:11:21 | access to parameter o : Object | E.cs:11:9:11:11 | [post] access to local variable ret [field Field] : Object | +| E.cs:22:17:22:33 | call to method Source : Object | E.cs:23:25:23:25 | access to local variable o : Object | +| E.cs:22:17:22:33 | call to method Source : Object | E.cs:23:25:23:25 | access to local variable o : Object | +| E.cs:23:17:23:26 | call to method CreateS [field Field] : Object | E.cs:24:14:24:14 | access to local variable s [field Field] : Object | | E.cs:23:17:23:26 | call to method CreateS [field Field] : Object | E.cs:24:14:24:14 | access to local variable s [field Field] : Object | | E.cs:23:25:23:25 | access to local variable o : Object | E.cs:8:29:8:29 | o : Object | +| E.cs:23:25:23:25 | access to local variable o : Object | E.cs:8:29:8:29 | o : Object | +| E.cs:23:25:23:25 | access to local variable o : Object | E.cs:23:17:23:26 | call to method CreateS [field Field] : Object | | E.cs:23:25:23:25 | access to local variable o : Object | E.cs:23:17:23:26 | call to method CreateS [field Field] : Object | | E.cs:24:14:24:14 | access to local variable s [field Field] : Object | E.cs:24:14:24:20 | access to field Field | +| E.cs:24:14:24:14 | access to local variable s [field Field] : Object | E.cs:24:14:24:20 | access to field Field | +| F.cs:6:28:6:29 | o1 : Object | F.cs:6:65:6:66 | access to parameter o1 : Object | | F.cs:6:28:6:29 | o1 : Object | F.cs:6:65:6:66 | access to parameter o1 : Object | | F.cs:6:39:6:40 | o2 : Object | F.cs:6:78:6:79 | access to parameter o2 : Object | +| F.cs:6:39:6:40 | o2 : Object | F.cs:6:78:6:79 | access to parameter o2 : Object | +| F.cs:6:54:6:81 | { ..., ... } [field Field1] : Object | F.cs:6:46:6:81 | object creation of type F [field Field1] : Object | | F.cs:6:54:6:81 | { ..., ... } [field Field1] : Object | F.cs:6:46:6:81 | object creation of type F [field Field1] : Object | | F.cs:6:54:6:81 | { ..., ... } [field Field2] : Object | F.cs:6:46:6:81 | object creation of type F [field Field2] : Object | +| F.cs:6:54:6:81 | { ..., ... } [field Field2] : Object | F.cs:6:46:6:81 | object creation of type F [field Field2] : Object | +| F.cs:6:65:6:66 | access to parameter o1 : Object | F.cs:6:54:6:81 | { ..., ... } [field Field1] : Object | | F.cs:6:65:6:66 | access to parameter o1 : Object | F.cs:6:54:6:81 | { ..., ... } [field Field1] : Object | | F.cs:6:78:6:79 | access to parameter o2 : Object | F.cs:6:54:6:81 | { ..., ... } [field Field2] : Object | -| F.cs:10:17:10:28 | object creation of type Object : Object | F.cs:11:24:11:24 | access to local variable o : Object | -| F.cs:10:17:10:28 | object creation of type Object : Object | F.cs:15:26:15:26 | access to local variable o : Object | -| F.cs:10:17:10:28 | object creation of type Object : Object | F.cs:19:32:19:32 | access to local variable o : Object | -| F.cs:10:17:10:28 | object creation of type Object : Object | F.cs:23:32:23:32 | access to local variable o : Object | +| F.cs:6:78:6:79 | access to parameter o2 : Object | F.cs:6:54:6:81 | { ..., ... } [field Field2] : Object | +| F.cs:10:17:10:33 | call to method Source : Object | F.cs:11:24:11:24 | access to local variable o : Object | +| F.cs:10:17:10:33 | call to method Source : Object | F.cs:11:24:11:24 | access to local variable o : Object | +| F.cs:11:17:11:31 | call to method Create [field Field1] : Object | F.cs:12:14:12:14 | access to local variable f [field Field1] : Object | | F.cs:11:17:11:31 | call to method Create [field Field1] : Object | F.cs:12:14:12:14 | access to local variable f [field Field1] : Object | | F.cs:11:24:11:24 | access to local variable o : Object | F.cs:6:28:6:29 | o1 : Object | +| F.cs:11:24:11:24 | access to local variable o : Object | F.cs:6:28:6:29 | o1 : Object | +| F.cs:11:24:11:24 | access to local variable o : Object | F.cs:11:17:11:31 | call to method Create [field Field1] : Object | | F.cs:11:24:11:24 | access to local variable o : Object | F.cs:11:17:11:31 | call to method Create [field Field1] : Object | | F.cs:12:14:12:14 | access to local variable f [field Field1] : Object | F.cs:12:14:12:21 | access to field Field1 | -| F.cs:15:13:15:27 | call to method Create [field Field2] : Object | F.cs:17:14:17:14 | access to local variable f [field Field2] : Object | -| F.cs:15:26:15:26 | access to local variable o : Object | F.cs:6:39:6:40 | o2 : Object | -| F.cs:15:26:15:26 | access to local variable o : Object | F.cs:15:13:15:27 | call to method Create [field Field2] : Object | +| F.cs:12:14:12:14 | access to local variable f [field Field1] : Object | F.cs:12:14:12:21 | access to field Field1 | +| F.cs:15:13:15:43 | call to method Create [field Field2] : Object | F.cs:17:14:17:14 | access to local variable f [field Field2] : Object | +| F.cs:15:13:15:43 | call to method Create [field Field2] : Object | F.cs:17:14:17:14 | access to local variable f [field Field2] : Object | +| F.cs:15:26:15:42 | call to method Source : Object | F.cs:6:39:6:40 | o2 : Object | +| F.cs:15:26:15:42 | call to method Source : Object | F.cs:6:39:6:40 | o2 : Object | +| F.cs:15:26:15:42 | call to method Source : Object | F.cs:15:13:15:43 | call to method Create [field Field2] : Object | +| F.cs:15:26:15:42 | call to method Source : Object | F.cs:15:13:15:43 | call to method Create [field Field2] : Object | | F.cs:17:14:17:14 | access to local variable f [field Field2] : Object | F.cs:17:14:17:21 | access to field Field2 | -| F.cs:19:21:19:34 | { ..., ... } [field Field1] : Object | F.cs:20:14:20:14 | access to local variable f [field Field1] : Object | -| F.cs:19:32:19:32 | access to local variable o : Object | F.cs:19:21:19:34 | { ..., ... } [field Field1] : Object | +| F.cs:17:14:17:14 | access to local variable f [field Field2] : Object | F.cs:17:14:17:21 | access to field Field2 | +| F.cs:19:21:19:50 | { ..., ... } [field Field1] : Object | F.cs:20:14:20:14 | access to local variable f [field Field1] : Object | +| F.cs:19:21:19:50 | { ..., ... } [field Field1] : Object | F.cs:20:14:20:14 | access to local variable f [field Field1] : Object | +| F.cs:19:32:19:48 | call to method Source : Object | F.cs:19:21:19:50 | { ..., ... } [field Field1] : Object | +| F.cs:19:32:19:48 | call to method Source : Object | F.cs:19:21:19:50 | { ..., ... } [field Field1] : Object | | F.cs:20:14:20:14 | access to local variable f [field Field1] : Object | F.cs:20:14:20:21 | access to field Field1 | -| F.cs:23:21:23:34 | { ..., ... } [field Field2] : Object | F.cs:25:14:25:14 | access to local variable f [field Field2] : Object | -| F.cs:23:32:23:32 | access to local variable o : Object | F.cs:23:21:23:34 | { ..., ... } [field Field2] : Object | +| F.cs:20:14:20:14 | access to local variable f [field Field1] : Object | F.cs:20:14:20:21 | access to field Field1 | +| F.cs:23:21:23:50 | { ..., ... } [field Field2] : Object | F.cs:25:14:25:14 | access to local variable f [field Field2] : Object | +| F.cs:23:21:23:50 | { ..., ... } [field Field2] : Object | F.cs:25:14:25:14 | access to local variable f [field Field2] : Object | +| F.cs:23:32:23:48 | call to method Source : Object | F.cs:23:21:23:50 | { ..., ... } [field Field2] : Object | +| F.cs:23:32:23:48 | call to method Source : Object | F.cs:23:21:23:50 | { ..., ... } [field Field2] : Object | | F.cs:25:14:25:14 | access to local variable f [field Field2] : Object | F.cs:25:14:25:21 | access to field Field2 | -| G.cs:7:18:7:27 | object creation of type Elem : Elem | G.cs:9:23:9:23 | access to local variable e : Elem | +| F.cs:25:14:25:14 | access to local variable f [field Field2] : Object | F.cs:25:14:25:21 | access to field Field2 | +| G.cs:7:18:7:32 | call to method Source : Elem | G.cs:9:23:9:23 | access to local variable e : Elem | +| G.cs:7:18:7:32 | call to method Source : Elem | G.cs:9:23:9:23 | access to local variable e : Elem | +| G.cs:9:9:9:9 | [post] access to local variable b [field Box1, field Elem] : Elem | G.cs:10:18:10:18 | access to local variable b [field Box1, field Elem] : Elem | | G.cs:9:9:9:9 | [post] access to local variable b [field Box1, field Elem] : Elem | G.cs:10:18:10:18 | access to local variable b [field Box1, field Elem] : Elem | | G.cs:9:9:9:14 | [post] access to field Box1 [field Elem] : Elem | G.cs:9:9:9:9 | [post] access to local variable b [field Box1, field Elem] : Elem | +| G.cs:9:9:9:14 | [post] access to field Box1 [field Elem] : Elem | G.cs:9:9:9:9 | [post] access to local variable b [field Box1, field Elem] : Elem | +| G.cs:9:23:9:23 | access to local variable e : Elem | G.cs:9:9:9:14 | [post] access to field Box1 [field Elem] : Elem | | G.cs:9:23:9:23 | access to local variable e : Elem | G.cs:9:9:9:14 | [post] access to field Box1 [field Elem] : Elem | | G.cs:10:18:10:18 | access to local variable b [field Box1, field Elem] : Elem | G.cs:37:38:37:39 | b2 [field Box1, field Elem] : Elem | -| G.cs:15:18:15:27 | object creation of type Elem : Elem | G.cs:17:24:17:24 | access to local variable e : Elem | +| G.cs:10:18:10:18 | access to local variable b [field Box1, field Elem] : Elem | G.cs:37:38:37:39 | b2 [field Box1, field Elem] : Elem | +| G.cs:15:18:15:32 | call to method Source : Elem | G.cs:17:24:17:24 | access to local variable e : Elem | +| G.cs:15:18:15:32 | call to method Source : Elem | G.cs:17:24:17:24 | access to local variable e : Elem | +| G.cs:17:9:17:9 | [post] access to local variable b [field Box1, field Elem] : Elem | G.cs:18:18:18:18 | access to local variable b [field Box1, field Elem] : Elem | | G.cs:17:9:17:9 | [post] access to local variable b [field Box1, field Elem] : Elem | G.cs:18:18:18:18 | access to local variable b [field Box1, field Elem] : Elem | | G.cs:17:9:17:14 | [post] access to field Box1 [field Elem] : Elem | G.cs:17:9:17:9 | [post] access to local variable b [field Box1, field Elem] : Elem | +| G.cs:17:9:17:14 | [post] access to field Box1 [field Elem] : Elem | G.cs:17:9:17:9 | [post] access to local variable b [field Box1, field Elem] : Elem | +| G.cs:17:24:17:24 | access to local variable e : Elem | G.cs:17:9:17:14 | [post] access to field Box1 [field Elem] : Elem | | G.cs:17:24:17:24 | access to local variable e : Elem | G.cs:17:9:17:14 | [post] access to field Box1 [field Elem] : Elem | | G.cs:17:24:17:24 | access to local variable e : Elem | G.cs:64:34:64:34 | e : Elem | +| G.cs:17:24:17:24 | access to local variable e : Elem | G.cs:64:34:64:34 | e : Elem | | G.cs:18:18:18:18 | access to local variable b [field Box1, field Elem] : Elem | G.cs:37:38:37:39 | b2 [field Box1, field Elem] : Elem | -| G.cs:23:18:23:27 | object creation of type Elem : Elem | G.cs:25:28:25:28 | access to local variable e : Elem | +| G.cs:18:18:18:18 | access to local variable b [field Box1, field Elem] : Elem | G.cs:37:38:37:39 | b2 [field Box1, field Elem] : Elem | +| G.cs:23:18:23:32 | call to method Source : Elem | G.cs:25:28:25:28 | access to local variable e : Elem | +| G.cs:23:18:23:32 | call to method Source : Elem | G.cs:25:28:25:28 | access to local variable e : Elem | +| G.cs:25:9:25:9 | [post] access to local variable b [field Box1, field Elem] : Elem | G.cs:26:18:26:18 | access to local variable b [field Box1, field Elem] : Elem | | G.cs:25:9:25:9 | [post] access to local variable b [field Box1, field Elem] : Elem | G.cs:26:18:26:18 | access to local variable b [field Box1, field Elem] : Elem | | G.cs:25:9:25:19 | [post] call to method GetBox1 [field Elem] : Elem | G.cs:25:9:25:9 | [post] access to local variable b [field Box1, field Elem] : Elem | +| G.cs:25:9:25:19 | [post] call to method GetBox1 [field Elem] : Elem | G.cs:25:9:25:9 | [post] access to local variable b [field Box1, field Elem] : Elem | +| G.cs:25:28:25:28 | access to local variable e : Elem | G.cs:25:9:25:19 | [post] call to method GetBox1 [field Elem] : Elem | | G.cs:25:28:25:28 | access to local variable e : Elem | G.cs:25:9:25:19 | [post] call to method GetBox1 [field Elem] : Elem | | G.cs:26:18:26:18 | access to local variable b [field Box1, field Elem] : Elem | G.cs:37:38:37:39 | b2 [field Box1, field Elem] : Elem | -| G.cs:31:18:31:27 | object creation of type Elem : Elem | G.cs:33:29:33:29 | access to local variable e : Elem | +| G.cs:26:18:26:18 | access to local variable b [field Box1, field Elem] : Elem | G.cs:37:38:37:39 | b2 [field Box1, field Elem] : Elem | +| G.cs:31:18:31:32 | call to method Source : Elem | G.cs:33:29:33:29 | access to local variable e : Elem | +| G.cs:31:18:31:32 | call to method Source : Elem | G.cs:33:29:33:29 | access to local variable e : Elem | +| G.cs:33:9:33:9 | [post] access to local variable b [field Box1, field Elem] : Elem | G.cs:34:18:34:18 | access to local variable b [field Box1, field Elem] : Elem | | G.cs:33:9:33:9 | [post] access to local variable b [field Box1, field Elem] : Elem | G.cs:34:18:34:18 | access to local variable b [field Box1, field Elem] : Elem | | G.cs:33:9:33:19 | [post] call to method GetBox1 [field Elem] : Elem | G.cs:33:9:33:9 | [post] access to local variable b [field Box1, field Elem] : Elem | +| G.cs:33:9:33:19 | [post] call to method GetBox1 [field Elem] : Elem | G.cs:33:9:33:9 | [post] access to local variable b [field Box1, field Elem] : Elem | +| G.cs:33:29:33:29 | access to local variable e : Elem | G.cs:33:9:33:19 | [post] call to method GetBox1 [field Elem] : Elem | | G.cs:33:29:33:29 | access to local variable e : Elem | G.cs:33:9:33:19 | [post] call to method GetBox1 [field Elem] : Elem | | G.cs:33:29:33:29 | access to local variable e : Elem | G.cs:64:34:64:34 | e : Elem | +| G.cs:33:29:33:29 | access to local variable e : Elem | G.cs:64:34:64:34 | e : Elem | +| G.cs:34:18:34:18 | access to local variable b [field Box1, field Elem] : Elem | G.cs:37:38:37:39 | b2 [field Box1, field Elem] : Elem | | G.cs:34:18:34:18 | access to local variable b [field Box1, field Elem] : Elem | G.cs:37:38:37:39 | b2 [field Box1, field Elem] : Elem | | G.cs:37:38:37:39 | b2 [field Box1, field Elem] : Elem | G.cs:39:14:39:15 | access to parameter b2 [field Box1, field Elem] : Elem | +| G.cs:37:38:37:39 | b2 [field Box1, field Elem] : Elem | G.cs:39:14:39:15 | access to parameter b2 [field Box1, field Elem] : Elem | +| G.cs:39:14:39:15 | access to parameter b2 [field Box1, field Elem] : Elem | G.cs:39:14:39:25 | call to method GetBox1 [field Elem] : Elem | | G.cs:39:14:39:15 | access to parameter b2 [field Box1, field Elem] : Elem | G.cs:39:14:39:25 | call to method GetBox1 [field Elem] : Elem | | G.cs:39:14:39:15 | access to parameter b2 [field Box1, field Elem] : Elem | G.cs:71:21:71:27 | this [field Box1, field Elem] : Elem | +| G.cs:39:14:39:15 | access to parameter b2 [field Box1, field Elem] : Elem | G.cs:71:21:71:27 | this [field Box1, field Elem] : Elem | +| G.cs:39:14:39:25 | call to method GetBox1 [field Elem] : Elem | G.cs:39:14:39:35 | call to method GetElem | | G.cs:39:14:39:25 | call to method GetBox1 [field Elem] : Elem | G.cs:39:14:39:35 | call to method GetElem | | G.cs:39:14:39:25 | call to method GetBox1 [field Elem] : Elem | G.cs:63:21:63:27 | this [field Elem] : Elem | -| G.cs:44:18:44:27 | object creation of type Elem : Elem | G.cs:46:30:46:30 | access to local variable e : Elem | +| G.cs:39:14:39:25 | call to method GetBox1 [field Elem] : Elem | G.cs:63:21:63:27 | this [field Elem] : Elem | +| G.cs:44:18:44:32 | call to method Source : Elem | G.cs:46:30:46:30 | access to local variable e : Elem | +| G.cs:44:18:44:32 | call to method Source : Elem | G.cs:46:30:46:30 | access to local variable e : Elem | +| G.cs:46:9:46:16 | [post] access to field boxfield [field Box1, field Elem] : Elem | G.cs:46:9:46:16 | [post] this access [field boxfield, field Box1, field Elem] : Elem | | G.cs:46:9:46:16 | [post] access to field boxfield [field Box1, field Elem] : Elem | G.cs:46:9:46:16 | [post] this access [field boxfield, field Box1, field Elem] : Elem | | G.cs:46:9:46:16 | [post] this access [field boxfield, field Box1, field Elem] : Elem | G.cs:47:9:47:13 | this access [field boxfield, field Box1, field Elem] : Elem | +| G.cs:46:9:46:16 | [post] this access [field boxfield, field Box1, field Elem] : Elem | G.cs:47:9:47:13 | this access [field boxfield, field Box1, field Elem] : Elem | +| G.cs:46:9:46:21 | [post] access to field Box1 [field Elem] : Elem | G.cs:46:9:46:16 | [post] access to field boxfield [field Box1, field Elem] : Elem | | G.cs:46:9:46:21 | [post] access to field Box1 [field Elem] : Elem | G.cs:46:9:46:16 | [post] access to field boxfield [field Box1, field Elem] : Elem | | G.cs:46:30:46:30 | access to local variable e : Elem | G.cs:46:9:46:21 | [post] access to field Box1 [field Elem] : Elem | +| G.cs:46:30:46:30 | access to local variable e : Elem | G.cs:46:9:46:21 | [post] access to field Box1 [field Elem] : Elem | +| G.cs:47:9:47:13 | this access [field boxfield, field Box1, field Elem] : Elem | G.cs:50:18:50:20 | this [field boxfield, field Box1, field Elem] : Elem | | G.cs:47:9:47:13 | this access [field boxfield, field Box1, field Elem] : Elem | G.cs:50:18:50:20 | this [field boxfield, field Box1, field Elem] : Elem | | G.cs:50:18:50:20 | this [field boxfield, field Box1, field Elem] : Elem | G.cs:52:14:52:21 | this access [field boxfield, field Box1, field Elem] : Elem | +| G.cs:50:18:50:20 | this [field boxfield, field Box1, field Elem] : Elem | G.cs:52:14:52:21 | this access [field boxfield, field Box1, field Elem] : Elem | +| G.cs:52:14:52:21 | access to field boxfield [field Box1, field Elem] : Elem | G.cs:52:14:52:26 | access to field Box1 [field Elem] : Elem | | G.cs:52:14:52:21 | access to field boxfield [field Box1, field Elem] : Elem | G.cs:52:14:52:26 | access to field Box1 [field Elem] : Elem | | G.cs:52:14:52:21 | this access [field boxfield, field Box1, field Elem] : Elem | G.cs:52:14:52:21 | access to field boxfield [field Box1, field Elem] : Elem | +| G.cs:52:14:52:21 | this access [field boxfield, field Box1, field Elem] : Elem | G.cs:52:14:52:21 | access to field boxfield [field Box1, field Elem] : Elem | +| G.cs:52:14:52:26 | access to field Box1 [field Elem] : Elem | G.cs:52:14:52:31 | access to field Elem | | G.cs:52:14:52:26 | access to field Box1 [field Elem] : Elem | G.cs:52:14:52:31 | access to field Elem | | G.cs:63:21:63:27 | this [field Elem] : Elem | G.cs:63:34:63:37 | this access [field Elem] : Elem | +| G.cs:63:21:63:27 | this [field Elem] : Elem | G.cs:63:34:63:37 | this access [field Elem] : Elem | +| G.cs:63:34:63:37 | this access [field Elem] : Elem | G.cs:63:34:63:37 | access to field Elem : Elem | | G.cs:63:34:63:37 | this access [field Elem] : Elem | G.cs:63:34:63:37 | access to field Elem : Elem | | G.cs:64:34:64:34 | e : Elem | G.cs:64:46:64:46 | access to parameter e : Elem | +| G.cs:64:34:64:34 | e : Elem | G.cs:64:46:64:46 | access to parameter e : Elem | +| G.cs:64:46:64:46 | access to parameter e : Elem | G.cs:64:39:64:42 | [post] this access [field Elem] : Elem | | G.cs:64:46:64:46 | access to parameter e : Elem | G.cs:64:39:64:42 | [post] this access [field Elem] : Elem | | G.cs:71:21:71:27 | this [field Box1, field Elem] : Elem | G.cs:71:34:71:37 | this access [field Box1, field Elem] : Elem | +| G.cs:71:21:71:27 | this [field Box1, field Elem] : Elem | G.cs:71:34:71:37 | this access [field Box1, field Elem] : Elem | +| G.cs:71:34:71:37 | this access [field Box1, field Elem] : Elem | G.cs:71:34:71:37 | access to field Box1 [field Elem] : Elem | | G.cs:71:34:71:37 | this access [field Box1, field Elem] : Elem | G.cs:71:34:71:37 | access to field Box1 [field Elem] : Elem | | H.cs:13:15:13:15 | a [field FieldA] : Object | H.cs:16:22:16:22 | access to parameter a [field FieldA] : Object | +| H.cs:13:15:13:15 | a [field FieldA] : Object | H.cs:16:22:16:22 | access to parameter a [field FieldA] : Object | +| H.cs:16:9:16:11 | [post] access to local variable ret [field FieldA] : Object | H.cs:17:16:17:18 | access to local variable ret [field FieldA] : Object | | H.cs:16:9:16:11 | [post] access to local variable ret [field FieldA] : Object | H.cs:17:16:17:18 | access to local variable ret [field FieldA] : Object | | H.cs:16:22:16:22 | access to parameter a [field FieldA] : Object | H.cs:16:22:16:29 | access to field FieldA : Object | +| H.cs:16:22:16:22 | access to parameter a [field FieldA] : Object | H.cs:16:22:16:29 | access to field FieldA : Object | +| H.cs:16:22:16:29 | access to field FieldA : Object | H.cs:16:9:16:11 | [post] access to local variable ret [field FieldA] : Object | | H.cs:16:22:16:29 | access to field FieldA : Object | H.cs:16:9:16:11 | [post] access to local variable ret [field FieldA] : Object | | H.cs:23:9:23:9 | [post] access to local variable a [field FieldA] : Object | H.cs:24:27:24:27 | access to local variable a [field FieldA] : Object | -| H.cs:23:20:23:31 | object creation of type Object : Object | H.cs:23:9:23:9 | [post] access to local variable a [field FieldA] : Object | +| H.cs:23:9:23:9 | [post] access to local variable a [field FieldA] : Object | H.cs:24:27:24:27 | access to local variable a [field FieldA] : Object | +| H.cs:23:20:23:36 | call to method Source : Object | H.cs:23:9:23:9 | [post] access to local variable a [field FieldA] : Object | +| H.cs:23:20:23:36 | call to method Source : Object | H.cs:23:9:23:9 | [post] access to local variable a [field FieldA] : Object | +| H.cs:24:21:24:28 | call to method Clone [field FieldA] : Object | H.cs:25:14:25:18 | access to local variable clone [field FieldA] : Object | | H.cs:24:21:24:28 | call to method Clone [field FieldA] : Object | H.cs:25:14:25:18 | access to local variable clone [field FieldA] : Object | | H.cs:24:27:24:27 | access to local variable a [field FieldA] : Object | H.cs:13:15:13:15 | a [field FieldA] : Object | +| H.cs:24:27:24:27 | access to local variable a [field FieldA] : Object | H.cs:13:15:13:15 | a [field FieldA] : Object | +| H.cs:24:27:24:27 | access to local variable a [field FieldA] : Object | H.cs:24:21:24:28 | call to method Clone [field FieldA] : Object | | H.cs:24:27:24:27 | access to local variable a [field FieldA] : Object | H.cs:24:21:24:28 | call to method Clone [field FieldA] : Object | | H.cs:25:14:25:18 | access to local variable clone [field FieldA] : Object | H.cs:25:14:25:25 | access to field FieldA | +| H.cs:25:14:25:18 | access to local variable clone [field FieldA] : Object | H.cs:25:14:25:25 | access to field FieldA | +| H.cs:33:19:33:19 | a [field FieldA] : A | H.cs:36:20:36:20 | access to parameter a [field FieldA] : A | | H.cs:33:19:33:19 | a [field FieldA] : A | H.cs:36:20:36:20 | access to parameter a [field FieldA] : A | | H.cs:33:19:33:19 | a [field FieldA] : Object | H.cs:36:20:36:20 | access to parameter a [field FieldA] : Object | +| H.cs:33:19:33:19 | a [field FieldA] : Object | H.cs:36:20:36:20 | access to parameter a [field FieldA] : Object | +| H.cs:36:9:36:9 | [post] access to local variable b [field FieldB] : A | H.cs:37:16:37:16 | access to local variable b [field FieldB] : A | | H.cs:36:9:36:9 | [post] access to local variable b [field FieldB] : A | H.cs:37:16:37:16 | access to local variable b [field FieldB] : A | | H.cs:36:9:36:9 | [post] access to local variable b [field FieldB] : Object | H.cs:37:16:37:16 | access to local variable b [field FieldB] : Object | +| H.cs:36:9:36:9 | [post] access to local variable b [field FieldB] : Object | H.cs:37:16:37:16 | access to local variable b [field FieldB] : Object | +| H.cs:36:20:36:20 | access to parameter a [field FieldA] : A | H.cs:36:20:36:27 | access to field FieldA : A | | H.cs:36:20:36:20 | access to parameter a [field FieldA] : A | H.cs:36:20:36:27 | access to field FieldA : A | | H.cs:36:20:36:20 | access to parameter a [field FieldA] : Object | H.cs:36:20:36:27 | access to field FieldA : Object | +| H.cs:36:20:36:20 | access to parameter a [field FieldA] : Object | H.cs:36:20:36:27 | access to field FieldA : Object | +| H.cs:36:20:36:27 | access to field FieldA : A | H.cs:36:9:36:9 | [post] access to local variable b [field FieldB] : A | | H.cs:36:20:36:27 | access to field FieldA : A | H.cs:36:9:36:9 | [post] access to local variable b [field FieldB] : A | | H.cs:36:20:36:27 | access to field FieldA : Object | H.cs:36:9:36:9 | [post] access to local variable b [field FieldB] : Object | +| H.cs:36:20:36:27 | access to field FieldA : Object | H.cs:36:9:36:9 | [post] access to local variable b [field FieldB] : Object | | H.cs:43:9:43:9 | [post] access to local variable a [field FieldA] : Object | H.cs:44:27:44:27 | access to local variable a [field FieldA] : Object | -| H.cs:43:20:43:31 | object creation of type Object : Object | H.cs:43:9:43:9 | [post] access to local variable a [field FieldA] : Object | +| H.cs:43:9:43:9 | [post] access to local variable a [field FieldA] : Object | H.cs:44:27:44:27 | access to local variable a [field FieldA] : Object | +| H.cs:43:20:43:36 | call to method Source : Object | H.cs:43:9:43:9 | [post] access to local variable a [field FieldA] : Object | +| H.cs:43:20:43:36 | call to method Source : Object | H.cs:43:9:43:9 | [post] access to local variable a [field FieldA] : Object | +| H.cs:44:17:44:28 | call to method Transform [field FieldB] : Object | H.cs:45:14:45:14 | access to local variable b [field FieldB] : Object | | H.cs:44:17:44:28 | call to method Transform [field FieldB] : Object | H.cs:45:14:45:14 | access to local variable b [field FieldB] : Object | | H.cs:44:27:44:27 | access to local variable a [field FieldA] : Object | H.cs:33:19:33:19 | a [field FieldA] : Object | +| H.cs:44:27:44:27 | access to local variable a [field FieldA] : Object | H.cs:33:19:33:19 | a [field FieldA] : Object | +| H.cs:44:27:44:27 | access to local variable a [field FieldA] : Object | H.cs:44:17:44:28 | call to method Transform [field FieldB] : Object | | H.cs:44:27:44:27 | access to local variable a [field FieldA] : Object | H.cs:44:17:44:28 | call to method Transform [field FieldB] : Object | | H.cs:45:14:45:14 | access to local variable b [field FieldB] : Object | H.cs:45:14:45:21 | access to field FieldB | +| H.cs:45:14:45:14 | access to local variable b [field FieldB] : Object | H.cs:45:14:45:21 | access to field FieldB | +| H.cs:53:25:53:25 | a [field FieldA] : Object | H.cs:55:21:55:21 | access to parameter a [field FieldA] : Object | | H.cs:53:25:53:25 | a [field FieldA] : Object | H.cs:55:21:55:21 | access to parameter a [field FieldA] : Object | | H.cs:55:21:55:21 | access to parameter a [field FieldA] : Object | H.cs:55:21:55:28 | access to field FieldA : Object | +| H.cs:55:21:55:21 | access to parameter a [field FieldA] : Object | H.cs:55:21:55:28 | access to field FieldA : Object | +| H.cs:55:21:55:28 | access to field FieldA : Object | H.cs:55:9:55:10 | [post] access to parameter b1 [field FieldB] : Object | | H.cs:55:21:55:28 | access to field FieldA : Object | H.cs:55:9:55:10 | [post] access to parameter b1 [field FieldB] : Object | | H.cs:63:9:63:9 | [post] access to local variable a [field FieldA] : Object | H.cs:64:22:64:22 | access to local variable a [field FieldA] : Object | -| H.cs:63:20:63:31 | object creation of type Object : Object | H.cs:63:9:63:9 | [post] access to local variable a [field FieldA] : Object | +| H.cs:63:9:63:9 | [post] access to local variable a [field FieldA] : Object | H.cs:64:22:64:22 | access to local variable a [field FieldA] : Object | +| H.cs:63:20:63:36 | call to method Source : Object | H.cs:63:9:63:9 | [post] access to local variable a [field FieldA] : Object | +| H.cs:63:20:63:36 | call to method Source : Object | H.cs:63:9:63:9 | [post] access to local variable a [field FieldA] : Object | +| H.cs:64:22:64:22 | access to local variable a [field FieldA] : Object | H.cs:53:25:53:25 | a [field FieldA] : Object | | H.cs:64:22:64:22 | access to local variable a [field FieldA] : Object | H.cs:53:25:53:25 | a [field FieldA] : Object | | H.cs:64:22:64:22 | access to local variable a [field FieldA] : Object | H.cs:64:25:64:26 | [post] access to local variable b1 [field FieldB] : Object | +| H.cs:64:22:64:22 | access to local variable a [field FieldA] : Object | H.cs:64:25:64:26 | [post] access to local variable b1 [field FieldB] : Object | +| H.cs:64:25:64:26 | [post] access to local variable b1 [field FieldB] : Object | H.cs:65:14:65:15 | access to local variable b1 [field FieldB] : Object | | H.cs:64:25:64:26 | [post] access to local variable b1 [field FieldB] : Object | H.cs:65:14:65:15 | access to local variable b1 [field FieldB] : Object | | H.cs:65:14:65:15 | access to local variable b1 [field FieldB] : Object | H.cs:65:14:65:22 | access to field FieldB | +| H.cs:65:14:65:15 | access to local variable b1 [field FieldB] : Object | H.cs:65:14:65:22 | access to field FieldB | +| H.cs:77:30:77:30 | o : Object | H.cs:79:20:79:20 | access to parameter o : Object | | H.cs:77:30:77:30 | o : Object | H.cs:79:20:79:20 | access to parameter o : Object | | H.cs:79:9:79:9 | [post] access to parameter a [field FieldA] : Object | H.cs:80:22:80:22 | access to parameter a [field FieldA] : Object | +| H.cs:79:9:79:9 | [post] access to parameter a [field FieldA] : Object | H.cs:80:22:80:22 | access to parameter a [field FieldA] : Object | +| H.cs:79:20:79:20 | access to parameter o : Object | H.cs:79:9:79:9 | [post] access to parameter a [field FieldA] : Object | | H.cs:79:20:79:20 | access to parameter o : Object | H.cs:79:9:79:9 | [post] access to parameter a [field FieldA] : Object | | H.cs:80:22:80:22 | access to parameter a [field FieldA] : Object | H.cs:53:25:53:25 | a [field FieldA] : Object | +| H.cs:80:22:80:22 | access to parameter a [field FieldA] : Object | H.cs:53:25:53:25 | a [field FieldA] : Object | +| H.cs:80:22:80:22 | access to parameter a [field FieldA] : Object | H.cs:80:25:80:26 | [post] access to parameter b1 [field FieldB] : Object | | H.cs:80:22:80:22 | access to parameter a [field FieldA] : Object | H.cs:80:25:80:26 | [post] access to parameter b1 [field FieldB] : Object | | H.cs:88:17:88:17 | [post] access to local variable a [field FieldA] : Object | H.cs:89:14:89:14 | access to local variable a [field FieldA] : Object | -| H.cs:88:20:88:31 | object creation of type Object : Object | H.cs:77:30:77:30 | o : Object | -| H.cs:88:20:88:31 | object creation of type Object : Object | H.cs:88:17:88:17 | [post] access to local variable a [field FieldA] : Object | -| H.cs:88:20:88:31 | object creation of type Object : Object | H.cs:88:34:88:35 | [post] access to local variable b1 [field FieldB] : Object | -| H.cs:88:34:88:35 | [post] access to local variable b1 [field FieldB] : Object | H.cs:90:14:90:15 | access to local variable b1 [field FieldB] : Object | +| H.cs:88:17:88:17 | [post] access to local variable a [field FieldA] : Object | H.cs:89:14:89:14 | access to local variable a [field FieldA] : Object | +| H.cs:88:20:88:36 | call to method Source : Object | H.cs:77:30:77:30 | o : Object | +| H.cs:88:20:88:36 | call to method Source : Object | H.cs:77:30:77:30 | o : Object | +| H.cs:88:20:88:36 | call to method Source : Object | H.cs:88:17:88:17 | [post] access to local variable a [field FieldA] : Object | +| H.cs:88:20:88:36 | call to method Source : Object | H.cs:88:17:88:17 | [post] access to local variable a [field FieldA] : Object | +| H.cs:88:20:88:36 | call to method Source : Object | H.cs:88:39:88:40 | [post] access to local variable b1 [field FieldB] : Object | +| H.cs:88:20:88:36 | call to method Source : Object | H.cs:88:39:88:40 | [post] access to local variable b1 [field FieldB] : Object | +| H.cs:88:39:88:40 | [post] access to local variable b1 [field FieldB] : Object | H.cs:90:14:90:15 | access to local variable b1 [field FieldB] : Object | +| H.cs:88:39:88:40 | [post] access to local variable b1 [field FieldB] : Object | H.cs:90:14:90:15 | access to local variable b1 [field FieldB] : Object | +| H.cs:89:14:89:14 | access to local variable a [field FieldA] : Object | H.cs:89:14:89:21 | access to field FieldA | | H.cs:89:14:89:14 | access to local variable a [field FieldA] : Object | H.cs:89:14:89:21 | access to field FieldA | | H.cs:90:14:90:15 | access to local variable b1 [field FieldB] : Object | H.cs:90:14:90:22 | access to field FieldB | +| H.cs:90:14:90:15 | access to local variable b1 [field FieldB] : Object | H.cs:90:14:90:22 | access to field FieldB | +| H.cs:102:23:102:23 | a [field FieldA] : Object | H.cs:105:23:105:23 | access to parameter a [field FieldA] : Object | | H.cs:102:23:102:23 | a [field FieldA] : Object | H.cs:105:23:105:23 | access to parameter a [field FieldA] : Object | | H.cs:105:9:105:12 | [post] access to local variable temp [field FieldB, field FieldA] : Object | H.cs:106:29:106:32 | access to local variable temp [field FieldB, field FieldA] : Object | +| H.cs:105:9:105:12 | [post] access to local variable temp [field FieldB, field FieldA] : Object | H.cs:106:29:106:32 | access to local variable temp [field FieldB, field FieldA] : Object | +| H.cs:105:23:105:23 | access to parameter a [field FieldA] : Object | H.cs:105:9:105:12 | [post] access to local variable temp [field FieldB, field FieldA] : Object | | H.cs:105:23:105:23 | access to parameter a [field FieldA] : Object | H.cs:105:9:105:12 | [post] access to local variable temp [field FieldB, field FieldA] : Object | | H.cs:106:26:106:39 | (...) ... [field FieldA] : Object | H.cs:33:19:33:19 | a [field FieldA] : Object | +| H.cs:106:26:106:39 | (...) ... [field FieldA] : Object | H.cs:33:19:33:19 | a [field FieldA] : Object | +| H.cs:106:26:106:39 | (...) ... [field FieldA] : Object | H.cs:106:16:106:40 | call to method Transform [field FieldB] : Object | | H.cs:106:26:106:39 | (...) ... [field FieldA] : Object | H.cs:106:16:106:40 | call to method Transform [field FieldB] : Object | | H.cs:106:29:106:32 | access to local variable temp [field FieldB, field FieldA] : Object | H.cs:106:29:106:39 | access to field FieldB [field FieldA] : Object | +| H.cs:106:29:106:32 | access to local variable temp [field FieldB, field FieldA] : Object | H.cs:106:29:106:39 | access to field FieldB [field FieldA] : Object | +| H.cs:106:29:106:39 | access to field FieldB [field FieldA] : Object | H.cs:106:26:106:39 | (...) ... [field FieldA] : Object | | H.cs:106:29:106:39 | access to field FieldB [field FieldA] : Object | H.cs:106:26:106:39 | (...) ... [field FieldA] : Object | | H.cs:112:9:112:9 | [post] access to local variable a [field FieldA] : Object | H.cs:113:31:113:31 | access to local variable a [field FieldA] : Object | -| H.cs:112:20:112:31 | object creation of type Object : Object | H.cs:112:9:112:9 | [post] access to local variable a [field FieldA] : Object | +| H.cs:112:9:112:9 | [post] access to local variable a [field FieldA] : Object | H.cs:113:31:113:31 | access to local variable a [field FieldA] : Object | +| H.cs:112:20:112:36 | call to method Source : Object | H.cs:112:9:112:9 | [post] access to local variable a [field FieldA] : Object | +| H.cs:112:20:112:36 | call to method Source : Object | H.cs:112:9:112:9 | [post] access to local variable a [field FieldA] : Object | +| H.cs:113:17:113:32 | call to method TransformWrap [field FieldB] : Object | H.cs:114:14:114:14 | access to local variable b [field FieldB] : Object | | H.cs:113:17:113:32 | call to method TransformWrap [field FieldB] : Object | H.cs:114:14:114:14 | access to local variable b [field FieldB] : Object | | H.cs:113:31:113:31 | access to local variable a [field FieldA] : Object | H.cs:102:23:102:23 | a [field FieldA] : Object | +| H.cs:113:31:113:31 | access to local variable a [field FieldA] : Object | H.cs:102:23:102:23 | a [field FieldA] : Object | +| H.cs:113:31:113:31 | access to local variable a [field FieldA] : Object | H.cs:113:17:113:32 | call to method TransformWrap [field FieldB] : Object | | H.cs:113:31:113:31 | access to local variable a [field FieldA] : Object | H.cs:113:17:113:32 | call to method TransformWrap [field FieldB] : Object | | H.cs:114:14:114:14 | access to local variable b [field FieldB] : Object | H.cs:114:14:114:21 | access to field FieldB | +| H.cs:114:14:114:14 | access to local variable b [field FieldB] : Object | H.cs:114:14:114:21 | access to field FieldB | +| H.cs:122:18:122:18 | a [field FieldA] : Object | H.cs:124:26:124:26 | access to parameter a [field FieldA] : Object | | H.cs:122:18:122:18 | a [field FieldA] : Object | H.cs:124:26:124:26 | access to parameter a [field FieldA] : Object | | H.cs:124:16:124:27 | call to method Transform [field FieldB] : Object | H.cs:124:16:124:34 | access to field FieldB : Object | +| H.cs:124:16:124:27 | call to method Transform [field FieldB] : Object | H.cs:124:16:124:34 | access to field FieldB : Object | +| H.cs:124:26:124:26 | access to parameter a [field FieldA] : Object | H.cs:33:19:33:19 | a [field FieldA] : Object | | H.cs:124:26:124:26 | access to parameter a [field FieldA] : Object | H.cs:33:19:33:19 | a [field FieldA] : Object | | H.cs:124:26:124:26 | access to parameter a [field FieldA] : Object | H.cs:124:16:124:27 | call to method Transform [field FieldB] : Object | +| H.cs:124:26:124:26 | access to parameter a [field FieldA] : Object | H.cs:124:16:124:27 | call to method Transform [field FieldB] : Object | | H.cs:130:9:130:9 | [post] access to local variable a [field FieldA] : Object | H.cs:131:18:131:18 | access to local variable a [field FieldA] : Object | -| H.cs:130:20:130:31 | object creation of type Object : Object | H.cs:130:9:130:9 | [post] access to local variable a [field FieldA] : Object | +| H.cs:130:9:130:9 | [post] access to local variable a [field FieldA] : Object | H.cs:131:18:131:18 | access to local variable a [field FieldA] : Object | +| H.cs:130:20:130:36 | call to method Source : Object | H.cs:130:9:130:9 | [post] access to local variable a [field FieldA] : Object | +| H.cs:130:20:130:36 | call to method Source : Object | H.cs:130:9:130:9 | [post] access to local variable a [field FieldA] : Object | +| H.cs:131:18:131:18 | access to local variable a [field FieldA] : Object | H.cs:122:18:122:18 | a [field FieldA] : Object | | H.cs:131:18:131:18 | access to local variable a [field FieldA] : Object | H.cs:122:18:122:18 | a [field FieldA] : Object | | H.cs:131:18:131:18 | access to local variable a [field FieldA] : Object | H.cs:131:14:131:19 | call to method Get | +| H.cs:131:18:131:18 | access to local variable a [field FieldA] : Object | H.cs:131:14:131:19 | call to method Get | +| H.cs:138:27:138:27 | o : A | H.cs:141:20:141:25 | ... as ... : A | | H.cs:138:27:138:27 | o : A | H.cs:141:20:141:25 | ... as ... : A | | H.cs:141:9:141:9 | [post] access to local variable a [field FieldA] : A | H.cs:142:26:142:26 | access to local variable a [field FieldA] : A | +| H.cs:141:9:141:9 | [post] access to local variable a [field FieldA] : A | H.cs:142:26:142:26 | access to local variable a [field FieldA] : A | +| H.cs:141:20:141:25 | ... as ... : A | H.cs:141:9:141:9 | [post] access to local variable a [field FieldA] : A | | H.cs:141:20:141:25 | ... as ... : A | H.cs:141:9:141:9 | [post] access to local variable a [field FieldA] : A | | H.cs:142:16:142:27 | call to method Transform [field FieldB] : A | H.cs:142:16:142:34 | access to field FieldB : A | +| H.cs:142:16:142:27 | call to method Transform [field FieldB] : A | H.cs:142:16:142:34 | access to field FieldB : A | +| H.cs:142:26:142:26 | access to local variable a [field FieldA] : A | H.cs:33:19:33:19 | a [field FieldA] : A | | H.cs:142:26:142:26 | access to local variable a [field FieldA] : A | H.cs:33:19:33:19 | a [field FieldA] : A | | H.cs:142:26:142:26 | access to local variable a [field FieldA] : A | H.cs:142:16:142:27 | call to method Transform [field FieldB] : A | -| H.cs:147:17:147:32 | call to method Through : A | H.cs:148:14:148:14 | access to local variable a | -| H.cs:147:25:147:31 | object creation of type A : A | H.cs:138:27:138:27 | o : A | -| H.cs:147:25:147:31 | object creation of type A : A | H.cs:147:17:147:32 | call to method Through : A | +| H.cs:142:26:142:26 | access to local variable a [field FieldA] : A | H.cs:142:16:142:27 | call to method Transform [field FieldB] : A | +| H.cs:147:17:147:39 | call to method Through : A | H.cs:148:14:148:14 | access to local variable a | +| H.cs:147:17:147:39 | call to method Through : A | H.cs:148:14:148:14 | access to local variable a | +| H.cs:147:25:147:38 | call to method Source : A | H.cs:138:27:138:27 | o : A | +| H.cs:147:25:147:38 | call to method Source : A | H.cs:138:27:138:27 | o : A | +| H.cs:147:25:147:38 | call to method Source : A | H.cs:147:17:147:39 | call to method Through : A | +| H.cs:147:25:147:38 | call to method Source : A | H.cs:147:17:147:39 | call to method Through : A | | H.cs:153:32:153:32 | o : Object | H.cs:156:20:156:20 | access to parameter o : Object | -| H.cs:155:17:155:23 | object creation of type B : B | H.cs:156:9:156:9 | access to local variable b : B | +| H.cs:153:32:153:32 | o : Object | H.cs:156:20:156:20 | access to parameter o : Object | +| H.cs:155:17:155:30 | call to method Source : B | H.cs:156:9:156:9 | access to local variable b : B | +| H.cs:155:17:155:30 | call to method Source : B | H.cs:156:9:156:9 | access to local variable b : B | +| H.cs:156:9:156:9 | [post] access to local variable b [field FieldB] : Object | H.cs:157:20:157:20 | access to local variable b [field FieldB] : Object | | H.cs:156:9:156:9 | [post] access to local variable b [field FieldB] : Object | H.cs:157:20:157:20 | access to local variable b [field FieldB] : Object | | H.cs:156:9:156:9 | access to local variable b : B | H.cs:157:20:157:20 | access to local variable b : B | +| H.cs:156:9:156:9 | access to local variable b : B | H.cs:157:20:157:20 | access to local variable b : B | +| H.cs:156:20:156:20 | access to parameter o : Object | H.cs:156:9:156:9 | [post] access to local variable b [field FieldB] : Object | | H.cs:156:20:156:20 | access to parameter o : Object | H.cs:156:9:156:9 | [post] access to local variable b [field FieldB] : Object | | H.cs:157:9:157:9 | [post] access to parameter a [field FieldA] : B | H.cs:164:19:164:19 | [post] access to local variable a [field FieldA] : B | +| H.cs:157:9:157:9 | [post] access to parameter a [field FieldA] : B | H.cs:164:19:164:19 | [post] access to local variable a [field FieldA] : B | +| H.cs:157:20:157:20 | access to local variable b : B | H.cs:157:9:157:9 | [post] access to parameter a [field FieldA] : B | | H.cs:157:20:157:20 | access to local variable b : B | H.cs:157:9:157:9 | [post] access to parameter a [field FieldA] : B | | H.cs:157:20:157:20 | access to local variable b [field FieldB] : Object | H.cs:157:9:157:9 | [post] access to parameter a [field FieldA, field FieldB] : Object | -| H.cs:163:17:163:28 | object creation of type Object : Object | H.cs:164:22:164:22 | access to local variable o : Object | -| H.cs:164:19:164:19 | [post] access to local variable a [field FieldA, field FieldB] : Object | H.cs:165:21:165:21 | access to local variable a [field FieldA, field FieldB] : Object | -| H.cs:164:19:164:19 | [post] access to local variable a [field FieldA] : B | H.cs:165:21:165:21 | access to local variable a [field FieldA] : B | +| H.cs:157:20:157:20 | access to local variable b [field FieldB] : Object | H.cs:157:9:157:9 | [post] access to parameter a [field FieldA, field FieldB] : Object | +| H.cs:163:17:163:35 | call to method Source : Object | H.cs:164:22:164:22 | access to local variable o : Object | +| H.cs:163:17:163:35 | call to method Source : Object | H.cs:164:22:164:22 | access to local variable o : Object | +| H.cs:164:19:164:19 | [post] access to local variable a [field FieldA, field FieldB] : Object | H.cs:165:20:165:20 | access to local variable a [field FieldA, field FieldB] : Object | +| H.cs:164:19:164:19 | [post] access to local variable a [field FieldA, field FieldB] : Object | H.cs:165:20:165:20 | access to local variable a [field FieldA, field FieldB] : Object | +| H.cs:164:19:164:19 | [post] access to local variable a [field FieldA] : B | H.cs:165:20:165:20 | access to local variable a [field FieldA] : B | +| H.cs:164:19:164:19 | [post] access to local variable a [field FieldA] : B | H.cs:165:20:165:20 | access to local variable a [field FieldA] : B | +| H.cs:164:22:164:22 | access to local variable o : Object | H.cs:153:32:153:32 | o : Object | | H.cs:164:22:164:22 | access to local variable o : Object | H.cs:153:32:153:32 | o : Object | | H.cs:164:22:164:22 | access to local variable o : Object | H.cs:164:19:164:19 | [post] access to local variable a [field FieldA, field FieldB] : Object | -| H.cs:165:17:165:28 | (...) ... : B | H.cs:166:14:166:14 | access to local variable b | -| H.cs:165:17:165:28 | (...) ... [field FieldB] : Object | H.cs:167:14:167:14 | access to local variable b [field FieldB] : Object | -| H.cs:165:21:165:21 | access to local variable a [field FieldA, field FieldB] : Object | H.cs:165:21:165:28 | access to field FieldA [field FieldB] : Object | -| H.cs:165:21:165:21 | access to local variable a [field FieldA] : B | H.cs:165:21:165:28 | access to field FieldA : B | -| H.cs:165:21:165:28 | access to field FieldA : B | H.cs:165:17:165:28 | (...) ... : B | -| H.cs:165:21:165:28 | access to field FieldA [field FieldB] : Object | H.cs:165:17:165:28 | (...) ... [field FieldB] : Object | +| H.cs:164:22:164:22 | access to local variable o : Object | H.cs:164:19:164:19 | [post] access to local variable a [field FieldA, field FieldB] : Object | +| H.cs:165:17:165:27 | (...) ... : B | H.cs:166:14:166:14 | access to local variable b | +| H.cs:165:17:165:27 | (...) ... : B | H.cs:166:14:166:14 | access to local variable b | +| H.cs:165:17:165:27 | (...) ... [field FieldB] : Object | H.cs:167:14:167:14 | access to local variable b [field FieldB] : Object | +| H.cs:165:17:165:27 | (...) ... [field FieldB] : Object | H.cs:167:14:167:14 | access to local variable b [field FieldB] : Object | +| H.cs:165:20:165:20 | access to local variable a [field FieldA, field FieldB] : Object | H.cs:165:20:165:27 | access to field FieldA [field FieldB] : Object | +| H.cs:165:20:165:20 | access to local variable a [field FieldA, field FieldB] : Object | H.cs:165:20:165:27 | access to field FieldA [field FieldB] : Object | +| H.cs:165:20:165:20 | access to local variable a [field FieldA] : B | H.cs:165:20:165:27 | access to field FieldA : B | +| H.cs:165:20:165:20 | access to local variable a [field FieldA] : B | H.cs:165:20:165:27 | access to field FieldA : B | +| H.cs:165:20:165:27 | access to field FieldA : B | H.cs:165:17:165:27 | (...) ... : B | +| H.cs:165:20:165:27 | access to field FieldA : B | H.cs:165:17:165:27 | (...) ... : B | +| H.cs:165:20:165:27 | access to field FieldA [field FieldB] : Object | H.cs:165:17:165:27 | (...) ... [field FieldB] : Object | +| H.cs:165:20:165:27 | access to field FieldA [field FieldB] : Object | H.cs:165:17:165:27 | (...) ... [field FieldB] : Object | +| H.cs:167:14:167:14 | access to local variable b [field FieldB] : Object | H.cs:167:14:167:21 | access to field FieldB | | H.cs:167:14:167:14 | access to local variable b [field FieldB] : Object | H.cs:167:14:167:21 | access to field FieldB | | I.cs:7:9:7:14 | [post] this access [field Field1] : Object | I.cs:21:13:21:19 | object creation of type I [field Field1] : Object | +| I.cs:7:9:7:14 | [post] this access [field Field1] : Object | I.cs:21:13:21:19 | object creation of type I [field Field1] : Object | | I.cs:7:9:7:14 | [post] this access [field Field1] : Object | I.cs:26:13:26:37 | [pre-initializer] object creation of type I [field Field1] : Object | -| I.cs:7:18:7:29 | object creation of type Object : Object | I.cs:7:9:7:14 | [post] this access [field Field1] : Object | -| I.cs:13:17:13:28 | object creation of type Object : Object | I.cs:15:20:15:20 | access to local variable o : Object | +| I.cs:7:9:7:14 | [post] this access [field Field1] : Object | I.cs:26:13:26:37 | [pre-initializer] object creation of type I [field Field1] : Object | +| I.cs:7:18:7:34 | call to method Source : Object | I.cs:7:9:7:14 | [post] this access [field Field1] : Object | +| I.cs:7:18:7:34 | call to method Source : Object | I.cs:7:9:7:14 | [post] this access [field Field1] : Object | +| I.cs:13:17:13:33 | call to method Source : Object | I.cs:15:20:15:20 | access to local variable o : Object | +| I.cs:13:17:13:33 | call to method Source : Object | I.cs:15:20:15:20 | access to local variable o : Object | +| I.cs:15:9:15:9 | [post] access to local variable i [field Field1] : Object | I.cs:16:9:16:9 | access to local variable i [field Field1] : Object | | I.cs:15:9:15:9 | [post] access to local variable i [field Field1] : Object | I.cs:16:9:16:9 | access to local variable i [field Field1] : Object | | I.cs:15:20:15:20 | access to local variable o : Object | I.cs:15:9:15:9 | [post] access to local variable i [field Field1] : Object | +| I.cs:15:20:15:20 | access to local variable o : Object | I.cs:15:9:15:9 | [post] access to local variable i [field Field1] : Object | +| I.cs:16:9:16:9 | access to local variable i [field Field1] : Object | I.cs:17:9:17:9 | access to local variable i [field Field1] : Object | | I.cs:16:9:16:9 | access to local variable i [field Field1] : Object | I.cs:17:9:17:9 | access to local variable i [field Field1] : Object | | I.cs:17:9:17:9 | access to local variable i [field Field1] : Object | I.cs:18:14:18:14 | access to local variable i [field Field1] : Object | +| I.cs:17:9:17:9 | access to local variable i [field Field1] : Object | I.cs:18:14:18:14 | access to local variable i [field Field1] : Object | +| I.cs:18:14:18:14 | access to local variable i [field Field1] : Object | I.cs:18:14:18:21 | access to field Field1 | | I.cs:18:14:18:14 | access to local variable i [field Field1] : Object | I.cs:18:14:18:21 | access to field Field1 | | I.cs:21:13:21:19 | object creation of type I [field Field1] : Object | I.cs:22:9:22:9 | access to local variable i [field Field1] : Object | +| I.cs:21:13:21:19 | object creation of type I [field Field1] : Object | I.cs:22:9:22:9 | access to local variable i [field Field1] : Object | +| I.cs:22:9:22:9 | access to local variable i [field Field1] : Object | I.cs:23:14:23:14 | access to local variable i [field Field1] : Object | | I.cs:22:9:22:9 | access to local variable i [field Field1] : Object | I.cs:23:14:23:14 | access to local variable i [field Field1] : Object | | I.cs:23:14:23:14 | access to local variable i [field Field1] : Object | I.cs:23:14:23:21 | access to field Field1 | +| I.cs:23:14:23:14 | access to local variable i [field Field1] : Object | I.cs:23:14:23:21 | access to field Field1 | +| I.cs:26:13:26:37 | [pre-initializer] object creation of type I [field Field1] : Object | I.cs:27:14:27:14 | access to local variable i [field Field1] : Object | | I.cs:26:13:26:37 | [pre-initializer] object creation of type I [field Field1] : Object | I.cs:27:14:27:14 | access to local variable i [field Field1] : Object | | I.cs:27:14:27:14 | access to local variable i [field Field1] : Object | I.cs:27:14:27:21 | access to field Field1 | -| I.cs:31:13:31:24 | object creation of type Object : Object | I.cs:32:20:32:20 | access to local variable o : Object | +| I.cs:27:14:27:14 | access to local variable i [field Field1] : Object | I.cs:27:14:27:21 | access to field Field1 | +| I.cs:31:13:31:29 | call to method Source : Object | I.cs:32:20:32:20 | access to local variable o : Object | +| I.cs:31:13:31:29 | call to method Source : Object | I.cs:32:20:32:20 | access to local variable o : Object | +| I.cs:32:9:32:9 | [post] access to local variable i [field Field1] : Object | I.cs:33:9:33:9 | access to local variable i [field Field1] : Object | | I.cs:32:9:32:9 | [post] access to local variable i [field Field1] : Object | I.cs:33:9:33:9 | access to local variable i [field Field1] : Object | | I.cs:32:20:32:20 | access to local variable o : Object | I.cs:32:9:32:9 | [post] access to local variable i [field Field1] : Object | +| I.cs:32:20:32:20 | access to local variable o : Object | I.cs:32:9:32:9 | [post] access to local variable i [field Field1] : Object | +| I.cs:33:9:33:9 | access to local variable i [field Field1] : Object | I.cs:34:12:34:12 | access to local variable i [field Field1] : Object | | I.cs:33:9:33:9 | access to local variable i [field Field1] : Object | I.cs:34:12:34:12 | access to local variable i [field Field1] : Object | | I.cs:34:12:34:12 | access to local variable i [field Field1] : Object | I.cs:37:23:37:23 | i [field Field1] : Object | +| I.cs:34:12:34:12 | access to local variable i [field Field1] : Object | I.cs:37:23:37:23 | i [field Field1] : Object | +| I.cs:37:23:37:23 | i [field Field1] : Object | I.cs:39:9:39:9 | access to parameter i [field Field1] : Object | | I.cs:37:23:37:23 | i [field Field1] : Object | I.cs:39:9:39:9 | access to parameter i [field Field1] : Object | | I.cs:39:9:39:9 | access to parameter i [field Field1] : Object | I.cs:40:14:40:14 | access to parameter i [field Field1] : Object | +| I.cs:39:9:39:9 | access to parameter i [field Field1] : Object | I.cs:40:14:40:14 | access to parameter i [field Field1] : Object | | I.cs:40:14:40:14 | access to parameter i [field Field1] : Object | I.cs:40:14:40:21 | access to field Field1 | -| J.cs:12:17:12:28 | object creation of type Object : Object | J.cs:13:29:13:29 | access to local variable o : Object | -| J.cs:12:17:12:28 | object creation of type Object : Object | J.cs:21:36:21:36 | access to local variable o : Object | +| I.cs:40:14:40:14 | access to parameter i [field Field1] : Object | I.cs:40:14:40:21 | access to field Field1 | +| J.cs:12:17:12:33 | call to method Source : Object | J.cs:13:29:13:29 | access to local variable o : Object | +| J.cs:12:17:12:33 | call to method Source : Object | J.cs:13:29:13:29 | access to local variable o : Object | +| J.cs:13:18:13:36 | object creation of type Record [property Prop1] : Object | J.cs:14:14:14:15 | access to local variable r1 [property Prop1] : Object | | J.cs:13:18:13:36 | object creation of type Record [property Prop1] : Object | J.cs:14:14:14:15 | access to local variable r1 [property Prop1] : Object | | J.cs:13:18:13:36 | object creation of type Record [property Prop1] : Object | J.cs:18:14:18:15 | access to local variable r2 [property Prop1] : Object | +| J.cs:13:18:13:36 | object creation of type Record [property Prop1] : Object | J.cs:18:14:18:15 | access to local variable r2 [property Prop1] : Object | +| J.cs:13:18:13:36 | object creation of type Record [property Prop1] : Object | J.cs:22:14:22:15 | access to local variable r3 [property Prop1] : Object | | J.cs:13:18:13:36 | object creation of type Record [property Prop1] : Object | J.cs:22:14:22:15 | access to local variable r3 [property Prop1] : Object | | J.cs:13:29:13:29 | access to local variable o : Object | J.cs:13:18:13:36 | object creation of type Record [property Prop1] : Object | +| J.cs:13:29:13:29 | access to local variable o : Object | J.cs:13:18:13:36 | object creation of type Record [property Prop1] : Object | +| J.cs:14:14:14:15 | access to local variable r1 [property Prop1] : Object | J.cs:14:14:14:21 | access to property Prop1 | | J.cs:14:14:14:15 | access to local variable r1 [property Prop1] : Object | J.cs:14:14:14:21 | access to property Prop1 | | J.cs:18:14:18:15 | access to local variable r2 [property Prop1] : Object | J.cs:18:14:18:21 | access to property Prop1 | -| J.cs:21:18:21:38 | ... with { ... } [property Prop2] : Object | J.cs:23:14:23:15 | access to local variable r3 [property Prop2] : Object | -| J.cs:21:36:21:36 | access to local variable o : Object | J.cs:21:18:21:38 | ... with { ... } [property Prop2] : Object | +| J.cs:18:14:18:15 | access to local variable r2 [property Prop1] : Object | J.cs:18:14:18:21 | access to property Prop1 | +| J.cs:21:18:21:54 | ... with { ... } [property Prop2] : Object | J.cs:23:14:23:15 | access to local variable r3 [property Prop2] : Object | +| J.cs:21:18:21:54 | ... with { ... } [property Prop2] : Object | J.cs:23:14:23:15 | access to local variable r3 [property Prop2] : Object | +| J.cs:21:36:21:52 | call to method Source : Object | J.cs:21:18:21:54 | ... with { ... } [property Prop2] : Object | +| J.cs:21:36:21:52 | call to method Source : Object | J.cs:21:18:21:54 | ... with { ... } [property Prop2] : Object | +| J.cs:22:14:22:15 | access to local variable r3 [property Prop1] : Object | J.cs:22:14:22:21 | access to property Prop1 | | J.cs:22:14:22:15 | access to local variable r3 [property Prop1] : Object | J.cs:22:14:22:21 | access to property Prop1 | | J.cs:23:14:23:15 | access to local variable r3 [property Prop2] : Object | J.cs:23:14:23:21 | access to property Prop2 | +| J.cs:23:14:23:15 | access to local variable r3 [property Prop2] : Object | J.cs:23:14:23:21 | access to property Prop2 | nodes -| A.cs:5:17:5:23 | object creation of type C : C | semmle.label | object creation of type C : C | +| A.cs:5:17:5:28 | call to method Source : C | semmle.label | call to method Source : C | +| A.cs:5:17:5:28 | call to method Source : C | semmle.label | call to method Source : C | +| A.cs:6:17:6:25 | call to method Make [field c] : C | semmle.label | call to method Make [field c] : C | | A.cs:6:17:6:25 | call to method Make [field c] : C | semmle.label | call to method Make [field c] : C | | A.cs:6:24:6:24 | access to local variable c : C | semmle.label | access to local variable c : C | +| A.cs:6:24:6:24 | access to local variable c : C | semmle.label | access to local variable c : C | +| A.cs:7:14:7:14 | access to local variable b [field c] : C | semmle.label | access to local variable b [field c] : C | | A.cs:7:14:7:14 | access to local variable b [field c] : C | semmle.label | access to local variable b [field c] : C | | A.cs:7:14:7:16 | access to field c | semmle.label | access to field c | +| A.cs:7:14:7:16 | access to field c | semmle.label | access to field c | | A.cs:13:9:13:9 | [post] access to local variable b [field c] : C1 | semmle.label | [post] access to local variable b [field c] : C1 | -| A.cs:13:15:13:22 | object creation of type C1 : C1 | semmle.label | object creation of type C1 : C1 | +| A.cs:13:9:13:9 | [post] access to local variable b [field c] : C1 | semmle.label | [post] access to local variable b [field c] : C1 | +| A.cs:13:15:13:29 | call to method Source : C1 | semmle.label | call to method Source : C1 | +| A.cs:13:15:13:29 | call to method Source : C1 | semmle.label | call to method Source : C1 | +| A.cs:14:14:14:14 | access to local variable b [field c] : C1 | semmle.label | access to local variable b [field c] : C1 | | A.cs:14:14:14:14 | access to local variable b [field c] : C1 | semmle.label | access to local variable b [field c] : C1 | | A.cs:14:14:14:20 | call to method Get | semmle.label | call to method Get | -| A.cs:15:14:15:35 | call to method Get | semmle.label | call to method Get | -| A.cs:15:15:15:28 | object creation of type B [field c] : C | semmle.label | object creation of type B [field c] : C | -| A.cs:15:21:15:27 | object creation of type C : C | semmle.label | object creation of type C : C | -| A.cs:22:14:22:33 | call to method SetOnB [field c] : C2 | semmle.label | call to method SetOnB [field c] : C2 | -| A.cs:22:25:22:32 | object creation of type C2 : C2 | semmle.label | object creation of type C2 : C2 | +| A.cs:14:14:14:20 | call to method Get | semmle.label | call to method Get | +| A.cs:15:14:15:42 | call to method Get | semmle.label | call to method Get | +| A.cs:15:14:15:42 | call to method Get | semmle.label | call to method Get | +| A.cs:15:15:15:35 | object creation of type B [field c] : C | semmle.label | object creation of type B [field c] : C | +| A.cs:15:15:15:35 | object creation of type B [field c] : C | semmle.label | object creation of type B [field c] : C | +| A.cs:15:21:15:34 | call to method Source : C | semmle.label | call to method Source : C | +| A.cs:15:21:15:34 | call to method Source : C | semmle.label | call to method Source : C | +| A.cs:22:14:22:38 | call to method SetOnB [field c] : C2 | semmle.label | call to method SetOnB [field c] : C2 | +| A.cs:22:14:22:38 | call to method SetOnB [field c] : C2 | semmle.label | call to method SetOnB [field c] : C2 | +| A.cs:22:25:22:37 | call to method Source : C2 | semmle.label | call to method Source : C2 | +| A.cs:22:25:22:37 | call to method Source : C2 | semmle.label | call to method Source : C2 | +| A.cs:24:14:24:15 | access to local variable b2 [field c] : C2 | semmle.label | access to local variable b2 [field c] : C2 | | A.cs:24:14:24:15 | access to local variable b2 [field c] : C2 | semmle.label | access to local variable b2 [field c] : C2 | | A.cs:24:14:24:17 | access to field c | semmle.label | access to field c | -| A.cs:31:14:31:37 | call to method SetOnBWrap [field c] : C2 | semmle.label | call to method SetOnBWrap [field c] : C2 | -| A.cs:31:29:31:36 | object creation of type C2 : C2 | semmle.label | object creation of type C2 : C2 | +| A.cs:24:14:24:17 | access to field c | semmle.label | access to field c | +| A.cs:31:14:31:42 | call to method SetOnBWrap [field c] : C2 | semmle.label | call to method SetOnBWrap [field c] : C2 | +| A.cs:31:14:31:42 | call to method SetOnBWrap [field c] : C2 | semmle.label | call to method SetOnBWrap [field c] : C2 | +| A.cs:31:29:31:41 | call to method Source : C2 | semmle.label | call to method Source : C2 | +| A.cs:31:29:31:41 | call to method Source : C2 | semmle.label | call to method Source : C2 | +| A.cs:33:14:33:15 | access to local variable b2 [field c] : C2 | semmle.label | access to local variable b2 [field c] : C2 | | A.cs:33:14:33:15 | access to local variable b2 [field c] : C2 | semmle.label | access to local variable b2 [field c] : C2 | | A.cs:33:14:33:17 | access to field c | semmle.label | access to field c | +| A.cs:33:14:33:17 | access to field c | semmle.label | access to field c | +| A.cs:36:33:36:33 | c : C2 | semmle.label | c : C2 | | A.cs:36:33:36:33 | c : C2 | semmle.label | c : C2 | | A.cs:38:18:38:30 | call to method SetOnB [field c] : C2 | semmle.label | call to method SetOnB [field c] : C2 | +| A.cs:38:18:38:30 | call to method SetOnB [field c] : C2 | semmle.label | call to method SetOnB [field c] : C2 | +| A.cs:38:29:38:29 | access to parameter c : C2 | semmle.label | access to parameter c : C2 | | A.cs:38:29:38:29 | access to parameter c : C2 | semmle.label | access to parameter c : C2 | | A.cs:39:16:39:28 | ... ? ... : ... [field c] : C2 | semmle.label | ... ? ... : ... [field c] : C2 | +| A.cs:39:16:39:28 | ... ? ... : ... [field c] : C2 | semmle.label | ... ? ... : ... [field c] : C2 | +| A.cs:42:29:42:29 | c : C2 | semmle.label | c : C2 | | A.cs:42:29:42:29 | c : C2 | semmle.label | c : C2 | | A.cs:47:13:47:14 | [post] access to local variable b2 [field c] : C2 | semmle.label | [post] access to local variable b2 [field c] : C2 | +| A.cs:47:13:47:14 | [post] access to local variable b2 [field c] : C2 | semmle.label | [post] access to local variable b2 [field c] : C2 | +| A.cs:47:20:47:20 | access to parameter c : C2 | semmle.label | access to parameter c : C2 | | A.cs:47:20:47:20 | access to parameter c : C2 | semmle.label | access to parameter c : C2 | | A.cs:48:20:48:21 | access to local variable b2 [field c] : C2 | semmle.label | access to local variable b2 [field c] : C2 | -| A.cs:55:17:55:23 | object creation of type A : A | semmle.label | object creation of type A : A | +| A.cs:48:20:48:21 | access to local variable b2 [field c] : C2 | semmle.label | access to local variable b2 [field c] : C2 | +| A.cs:55:17:55:28 | call to method Source : A | semmle.label | call to method Source : A | +| A.cs:55:17:55:28 | call to method Source : A | semmle.label | call to method Source : A | +| A.cs:57:9:57:10 | [post] access to local variable c1 [field a] : A | semmle.label | [post] access to local variable c1 [field a] : A | | A.cs:57:9:57:10 | [post] access to local variable c1 [field a] : A | semmle.label | [post] access to local variable c1 [field a] : A | | A.cs:57:16:57:16 | access to local variable a : A | semmle.label | access to local variable a : A | +| A.cs:57:16:57:16 | access to local variable a : A | semmle.label | access to local variable a : A | +| A.cs:58:12:58:13 | access to local variable c1 [field a] : A | semmle.label | access to local variable c1 [field a] : A | | A.cs:58:12:58:13 | access to local variable c1 [field a] : A | semmle.label | access to local variable c1 [field a] : A | | A.cs:60:22:60:22 | c [field a] : A | semmle.label | c [field a] : A | +| A.cs:60:22:60:22 | c [field a] : A | semmle.label | c [field a] : A | +| A.cs:64:18:64:26 | access to field a | semmle.label | access to field a | | A.cs:64:18:64:26 | access to field a | semmle.label | access to field a | | A.cs:64:19:64:23 | (...) ... [field a] : A | semmle.label | (...) ... [field a] : A | +| A.cs:64:19:64:23 | (...) ... [field a] : A | semmle.label | (...) ... [field a] : A | | A.cs:83:9:83:9 | [post] access to parameter b [field c] : C | semmle.label | [post] access to parameter b [field c] : C | -| A.cs:83:15:83:21 | object creation of type C : C | semmle.label | object creation of type C : C | +| A.cs:83:9:83:9 | [post] access to parameter b [field c] : C | semmle.label | [post] access to parameter b [field c] : C | +| A.cs:83:15:83:26 | call to method Source : C | semmle.label | call to method Source : C | +| A.cs:83:15:83:26 | call to method Source : C | semmle.label | call to method Source : C | +| A.cs:88:12:88:12 | [post] access to local variable b [field c] : C | semmle.label | [post] access to local variable b [field c] : C | | A.cs:88:12:88:12 | [post] access to local variable b [field c] : C | semmle.label | [post] access to local variable b [field c] : C | | A.cs:89:14:89:14 | access to local variable b [field c] : C | semmle.label | access to local variable b [field c] : C | +| A.cs:89:14:89:14 | access to local variable b [field c] : C | semmle.label | access to local variable b [field c] : C | +| A.cs:89:14:89:16 | access to field c | semmle.label | access to field c | | A.cs:89:14:89:16 | access to field c | semmle.label | access to field c | | A.cs:95:20:95:20 | b : B | semmle.label | b : B | +| A.cs:95:20:95:20 | b : B | semmle.label | b : B | +| A.cs:97:13:97:13 | [post] access to parameter b [field c] : C | semmle.label | [post] access to parameter b [field c] : C | | A.cs:97:13:97:13 | [post] access to parameter b [field c] : C | semmle.label | [post] access to parameter b [field c] : C | | A.cs:97:13:97:13 | access to parameter b : B | semmle.label | access to parameter b : B | -| A.cs:97:19:97:25 | object creation of type C : C | semmle.label | object creation of type C : C | +| A.cs:97:13:97:13 | access to parameter b : B | semmle.label | access to parameter b : B | +| A.cs:97:19:97:32 | call to method Source : C | semmle.label | call to method Source : C | +| A.cs:97:19:97:32 | call to method Source : C | semmle.label | call to method Source : C | +| A.cs:98:13:98:16 | [post] this access [field b, field c] : C | semmle.label | [post] this access [field b, field c] : C | | A.cs:98:13:98:16 | [post] this access [field b, field c] : C | semmle.label | [post] this access [field b, field c] : C | | A.cs:98:13:98:16 | [post] this access [field b] : B | semmle.label | [post] this access [field b] : B | | A.cs:98:13:98:16 | [post] this access [field b] : B | semmle.label | [post] this access [field b] : B | -| A.cs:98:22:98:36 | ... ? ... : ... : B | semmle.label | ... ? ... : ... : B | -| A.cs:98:22:98:36 | ... ? ... : ... : B | semmle.label | ... ? ... : ... : B | -| A.cs:98:22:98:36 | ... ? ... : ... [field c] : C | semmle.label | ... ? ... : ... [field c] : C | -| A.cs:98:30:98:36 | object creation of type B : B | semmle.label | object creation of type B : B | -| A.cs:104:17:104:23 | object creation of type B : B | semmle.label | object creation of type B : B | +| A.cs:98:13:98:16 | [post] this access [field b] : B | semmle.label | [post] this access [field b] : B | +| A.cs:98:13:98:16 | [post] this access [field b] : B | semmle.label | [post] this access [field b] : B | +| A.cs:98:22:98:43 | ... ? ... : ... : B | semmle.label | ... ? ... : ... : B | +| A.cs:98:22:98:43 | ... ? ... : ... : B | semmle.label | ... ? ... : ... : B | +| A.cs:98:22:98:43 | ... ? ... : ... : B | semmle.label | ... ? ... : ... : B | +| A.cs:98:22:98:43 | ... ? ... : ... : B | semmle.label | ... ? ... : ... : B | +| A.cs:98:22:98:43 | ... ? ... : ... [field c] : C | semmle.label | ... ? ... : ... [field c] : C | +| A.cs:98:22:98:43 | ... ? ... : ... [field c] : C | semmle.label | ... ? ... : ... [field c] : C | +| A.cs:98:30:98:43 | call to method Source : B | semmle.label | call to method Source : B | +| A.cs:98:30:98:43 | call to method Source : B | semmle.label | call to method Source : B | +| A.cs:104:17:104:30 | call to method Source : B | semmle.label | call to method Source : B | +| A.cs:104:17:104:30 | call to method Source : B | semmle.label | call to method Source : B | +| A.cs:105:17:105:29 | object creation of type D [field b, field c] : C | semmle.label | object creation of type D [field b, field c] : C | | A.cs:105:17:105:29 | object creation of type D [field b, field c] : C | semmle.label | object creation of type D [field b, field c] : C | | A.cs:105:17:105:29 | object creation of type D [field b] : B | semmle.label | object creation of type D [field b] : B | +| A.cs:105:17:105:29 | object creation of type D [field b] : B | semmle.label | object creation of type D [field b] : B | +| A.cs:105:23:105:23 | [post] access to local variable b [field c] : C | semmle.label | [post] access to local variable b [field c] : C | | A.cs:105:23:105:23 | [post] access to local variable b [field c] : C | semmle.label | [post] access to local variable b [field c] : C | | A.cs:105:23:105:23 | access to local variable b : B | semmle.label | access to local variable b : B | +| A.cs:105:23:105:23 | access to local variable b : B | semmle.label | access to local variable b : B | +| A.cs:106:14:106:14 | access to local variable d [field b] : B | semmle.label | access to local variable d [field b] : B | | A.cs:106:14:106:14 | access to local variable d [field b] : B | semmle.label | access to local variable d [field b] : B | | A.cs:106:14:106:16 | access to field b | semmle.label | access to field b | +| A.cs:106:14:106:16 | access to field b | semmle.label | access to field b | +| A.cs:107:14:107:14 | access to local variable d [field b, field c] : C | semmle.label | access to local variable d [field b, field c] : C | | A.cs:107:14:107:14 | access to local variable d [field b, field c] : C | semmle.label | access to local variable d [field b, field c] : C | | A.cs:107:14:107:16 | access to field b [field c] : C | semmle.label | access to field b [field c] : C | +| A.cs:107:14:107:16 | access to field b [field c] : C | semmle.label | access to field b [field c] : C | +| A.cs:107:14:107:18 | access to field c | semmle.label | access to field c | | A.cs:107:14:107:18 | access to field c | semmle.label | access to field c | | A.cs:108:14:108:14 | access to local variable b [field c] : C | semmle.label | access to local variable b [field c] : C | +| A.cs:108:14:108:14 | access to local variable b [field c] : C | semmle.label | access to local variable b [field c] : C | | A.cs:108:14:108:16 | access to field c | semmle.label | access to field c | -| A.cs:113:17:113:23 | object creation of type B : B | semmle.label | object creation of type B : B | +| A.cs:108:14:108:16 | access to field c | semmle.label | access to field c | +| A.cs:113:17:113:29 | call to method Source : B | semmle.label | call to method Source : B | +| A.cs:113:17:113:29 | call to method Source : B | semmle.label | call to method Source : B | +| A.cs:114:18:114:54 | object creation of type MyList [field head] : B | semmle.label | object creation of type MyList [field head] : B | | A.cs:114:18:114:54 | object creation of type MyList [field head] : B | semmle.label | object creation of type MyList [field head] : B | | A.cs:114:29:114:29 | access to local variable b : B | semmle.label | access to local variable b : B | +| A.cs:114:29:114:29 | access to local variable b : B | semmle.label | access to local variable b : B | +| A.cs:115:18:115:37 | object creation of type MyList [field next, field head] : B | semmle.label | object creation of type MyList [field next, field head] : B | | A.cs:115:18:115:37 | object creation of type MyList [field next, field head] : B | semmle.label | object creation of type MyList [field next, field head] : B | | A.cs:115:35:115:36 | access to local variable l1 [field head] : B | semmle.label | access to local variable l1 [field head] : B | +| A.cs:115:35:115:36 | access to local variable l1 [field head] : B | semmle.label | access to local variable l1 [field head] : B | +| A.cs:116:18:116:37 | object creation of type MyList [field next, field next, field head] : B | semmle.label | object creation of type MyList [field next, field next, field head] : B | | A.cs:116:18:116:37 | object creation of type MyList [field next, field next, field head] : B | semmle.label | object creation of type MyList [field next, field next, field head] : B | | A.cs:116:35:116:36 | access to local variable l2 [field next, field head] : B | semmle.label | access to local variable l2 [field next, field head] : B | +| A.cs:116:35:116:36 | access to local variable l2 [field next, field head] : B | semmle.label | access to local variable l2 [field next, field head] : B | +| A.cs:119:14:119:15 | access to local variable l3 [field next, field next, field head] : B | semmle.label | access to local variable l3 [field next, field next, field head] : B | | A.cs:119:14:119:15 | access to local variable l3 [field next, field next, field head] : B | semmle.label | access to local variable l3 [field next, field next, field head] : B | | A.cs:119:14:119:20 | access to field next [field next, field head] : B | semmle.label | access to field next [field next, field head] : B | +| A.cs:119:14:119:20 | access to field next [field next, field head] : B | semmle.label | access to field next [field next, field head] : B | +| A.cs:119:14:119:25 | access to field next [field head] : B | semmle.label | access to field next [field head] : B | | A.cs:119:14:119:25 | access to field next [field head] : B | semmle.label | access to field next [field head] : B | | A.cs:119:14:119:30 | access to field head | semmle.label | access to field head | +| A.cs:119:14:119:30 | access to field head | semmle.label | access to field head | +| A.cs:121:41:121:41 | access to local variable l [field next, field head] : B | semmle.label | access to local variable l [field next, field head] : B | | A.cs:121:41:121:41 | access to local variable l [field next, field head] : B | semmle.label | access to local variable l [field next, field head] : B | | A.cs:121:41:121:41 | access to local variable l [field next, field next, field head] : B | semmle.label | access to local variable l [field next, field next, field head] : B | +| A.cs:121:41:121:41 | access to local variable l [field next, field next, field head] : B | semmle.label | access to local variable l [field next, field next, field head] : B | +| A.cs:121:41:121:46 | access to field next [field head] : B | semmle.label | access to field next [field head] : B | | A.cs:121:41:121:46 | access to field next [field head] : B | semmle.label | access to field next [field head] : B | | A.cs:121:41:121:46 | access to field next [field next, field head] : B | semmle.label | access to field next [field next, field head] : B | +| A.cs:121:41:121:46 | access to field next [field next, field head] : B | semmle.label | access to field next [field next, field head] : B | +| A.cs:123:18:123:18 | access to local variable l [field head] : B | semmle.label | access to local variable l [field head] : B | | A.cs:123:18:123:18 | access to local variable l [field head] : B | semmle.label | access to local variable l [field head] : B | | A.cs:123:18:123:23 | access to field head | semmle.label | access to field head | +| A.cs:123:18:123:23 | access to field head | semmle.label | access to field head | +| A.cs:141:20:141:20 | c : C | semmle.label | c : C | | A.cs:141:20:141:20 | c : C | semmle.label | c : C | | A.cs:143:13:143:16 | [post] this access [field c] : C | semmle.label | [post] this access [field c] : C | +| A.cs:143:13:143:16 | [post] this access [field c] : C | semmle.label | [post] this access [field c] : C | +| A.cs:143:22:143:22 | access to parameter c : C | semmle.label | access to parameter c : C | | A.cs:143:22:143:22 | access to parameter c : C | semmle.label | access to parameter c : C | | A.cs:145:27:145:27 | c : C | semmle.label | c : C | +| A.cs:145:27:145:27 | c : C | semmle.label | c : C | +| A.cs:145:27:145:27 | c : C1 | semmle.label | c : C1 | | A.cs:145:27:145:27 | c : C1 | semmle.label | c : C1 | | A.cs:145:27:145:27 | c : C2 | semmle.label | c : C2 | +| A.cs:145:27:145:27 | c : C2 | semmle.label | c : C2 | +| A.cs:145:32:145:35 | [post] this access [field c] : C | semmle.label | [post] this access [field c] : C | | A.cs:145:32:145:35 | [post] this access [field c] : C | semmle.label | [post] this access [field c] : C | | A.cs:145:32:145:35 | [post] this access [field c] : C1 | semmle.label | [post] this access [field c] : C1 | +| A.cs:145:32:145:35 | [post] this access [field c] : C1 | semmle.label | [post] this access [field c] : C1 | +| A.cs:145:32:145:35 | [post] this access [field c] : C2 | semmle.label | [post] this access [field c] : C2 | | A.cs:145:32:145:35 | [post] this access [field c] : C2 | semmle.label | [post] this access [field c] : C2 | | A.cs:145:41:145:41 | access to parameter c : C | semmle.label | access to parameter c : C | +| A.cs:145:41:145:41 | access to parameter c : C | semmle.label | access to parameter c : C | +| A.cs:145:41:145:41 | access to parameter c : C1 | semmle.label | access to parameter c : C1 | | A.cs:145:41:145:41 | access to parameter c : C1 | semmle.label | access to parameter c : C1 | | A.cs:145:41:145:41 | access to parameter c : C2 | semmle.label | access to parameter c : C2 | +| A.cs:145:41:145:41 | access to parameter c : C2 | semmle.label | access to parameter c : C2 | +| A.cs:146:18:146:20 | this [field c] : C | semmle.label | this [field c] : C | | A.cs:146:18:146:20 | this [field c] : C | semmle.label | this [field c] : C | | A.cs:146:18:146:20 | this [field c] : C1 | semmle.label | this [field c] : C1 | +| A.cs:146:18:146:20 | this [field c] : C1 | semmle.label | this [field c] : C1 | +| A.cs:146:33:146:36 | this access [field c] : C | semmle.label | this access [field c] : C | | A.cs:146:33:146:36 | this access [field c] : C | semmle.label | this access [field c] : C | | A.cs:146:33:146:36 | this access [field c] : C1 | semmle.label | this access [field c] : C1 | +| A.cs:146:33:146:36 | this access [field c] : C1 | semmle.label | this access [field c] : C1 | +| A.cs:146:33:146:38 | access to field c : C | semmle.label | access to field c : C | | A.cs:146:33:146:38 | access to field c : C | semmle.label | access to field c : C | | A.cs:146:33:146:38 | access to field c : C1 | semmle.label | access to field c : C1 | +| A.cs:146:33:146:38 | access to field c : C1 | semmle.label | access to field c : C1 | +| A.cs:147:32:147:32 | c : C | semmle.label | c : C | | A.cs:147:32:147:32 | c : C | semmle.label | c : C | | A.cs:149:20:149:27 | object creation of type B [field c] : C | semmle.label | object creation of type B [field c] : C | +| A.cs:149:20:149:27 | object creation of type B [field c] : C | semmle.label | object creation of type B [field c] : C | +| A.cs:149:26:149:26 | access to parameter c : C | semmle.label | access to parameter c : C | | A.cs:149:26:149:26 | access to parameter c : C | semmle.label | access to parameter c : C | | A.cs:157:25:157:28 | head : B | semmle.label | head : B | +| A.cs:157:25:157:28 | head : B | semmle.label | head : B | +| A.cs:157:38:157:41 | next [field head] : B | semmle.label | next [field head] : B | | A.cs:157:38:157:41 | next [field head] : B | semmle.label | next [field head] : B | | A.cs:157:38:157:41 | next [field next, field head] : B | semmle.label | next [field next, field head] : B | +| A.cs:157:38:157:41 | next [field next, field head] : B | semmle.label | next [field next, field head] : B | +| A.cs:159:13:159:16 | [post] this access [field head] : B | semmle.label | [post] this access [field head] : B | | A.cs:159:13:159:16 | [post] this access [field head] : B | semmle.label | [post] this access [field head] : B | | A.cs:159:25:159:28 | access to parameter head : B | semmle.label | access to parameter head : B | +| A.cs:159:25:159:28 | access to parameter head : B | semmle.label | access to parameter head : B | +| A.cs:160:13:160:16 | [post] this access [field next, field head] : B | semmle.label | [post] this access [field next, field head] : B | | A.cs:160:13:160:16 | [post] this access [field next, field head] : B | semmle.label | [post] this access [field next, field head] : B | | A.cs:160:13:160:16 | [post] this access [field next, field next, field head] : B | semmle.label | [post] this access [field next, field next, field head] : B | +| A.cs:160:13:160:16 | [post] this access [field next, field next, field head] : B | semmle.label | [post] this access [field next, field next, field head] : B | +| A.cs:160:25:160:28 | access to parameter next [field head] : B | semmle.label | access to parameter next [field head] : B | | A.cs:160:25:160:28 | access to parameter next [field head] : B | semmle.label | access to parameter next [field head] : B | | A.cs:160:25:160:28 | access to parameter next [field next, field head] : B | semmle.label | access to parameter next [field next, field head] : B | -| B.cs:5:17:5:26 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | +| A.cs:160:25:160:28 | access to parameter next [field next, field head] : B | semmle.label | access to parameter next [field next, field head] : B | +| B.cs:5:17:5:31 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| B.cs:5:17:5:31 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| B.cs:6:18:6:34 | object creation of type Box1 [field elem1] : Elem | semmle.label | object creation of type Box1 [field elem1] : Elem | | B.cs:6:18:6:34 | object creation of type Box1 [field elem1] : Elem | semmle.label | object creation of type Box1 [field elem1] : Elem | | B.cs:6:27:6:27 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | +| B.cs:6:27:6:27 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | +| B.cs:7:18:7:29 | object creation of type Box2 [field box1, field elem1] : Elem | semmle.label | object creation of type Box2 [field box1, field elem1] : Elem | | B.cs:7:18:7:29 | object creation of type Box2 [field box1, field elem1] : Elem | semmle.label | object creation of type Box2 [field box1, field elem1] : Elem | | B.cs:7:27:7:28 | access to local variable b1 [field elem1] : Elem | semmle.label | access to local variable b1 [field elem1] : Elem | +| B.cs:7:27:7:28 | access to local variable b1 [field elem1] : Elem | semmle.label | access to local variable b1 [field elem1] : Elem | +| B.cs:8:14:8:15 | access to local variable b2 [field box1, field elem1] : Elem | semmle.label | access to local variable b2 [field box1, field elem1] : Elem | | B.cs:8:14:8:15 | access to local variable b2 [field box1, field elem1] : Elem | semmle.label | access to local variable b2 [field box1, field elem1] : Elem | | B.cs:8:14:8:20 | access to field box1 [field elem1] : Elem | semmle.label | access to field box1 [field elem1] : Elem | +| B.cs:8:14:8:20 | access to field box1 [field elem1] : Elem | semmle.label | access to field box1 [field elem1] : Elem | | B.cs:8:14:8:26 | access to field elem1 | semmle.label | access to field elem1 | -| B.cs:14:17:14:26 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | +| B.cs:8:14:8:26 | access to field elem1 | semmle.label | access to field elem1 | +| B.cs:14:17:14:31 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| B.cs:14:17:14:31 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| B.cs:15:18:15:34 | object creation of type Box1 [field elem2] : Elem | semmle.label | object creation of type Box1 [field elem2] : Elem | | B.cs:15:18:15:34 | object creation of type Box1 [field elem2] : Elem | semmle.label | object creation of type Box1 [field elem2] : Elem | | B.cs:15:33:15:33 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | +| B.cs:15:33:15:33 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | +| B.cs:16:18:16:29 | object creation of type Box2 [field box1, field elem2] : Elem | semmle.label | object creation of type Box2 [field box1, field elem2] : Elem | | B.cs:16:18:16:29 | object creation of type Box2 [field box1, field elem2] : Elem | semmle.label | object creation of type Box2 [field box1, field elem2] : Elem | | B.cs:16:27:16:28 | access to local variable b1 [field elem2] : Elem | semmle.label | access to local variable b1 [field elem2] : Elem | +| B.cs:16:27:16:28 | access to local variable b1 [field elem2] : Elem | semmle.label | access to local variable b1 [field elem2] : Elem | +| B.cs:18:14:18:15 | access to local variable b2 [field box1, field elem2] : Elem | semmle.label | access to local variable b2 [field box1, field elem2] : Elem | | B.cs:18:14:18:15 | access to local variable b2 [field box1, field elem2] : Elem | semmle.label | access to local variable b2 [field box1, field elem2] : Elem | | B.cs:18:14:18:20 | access to field box1 [field elem2] : Elem | semmle.label | access to field box1 [field elem2] : Elem | +| B.cs:18:14:18:20 | access to field box1 [field elem2] : Elem | semmle.label | access to field box1 [field elem2] : Elem | +| B.cs:18:14:18:26 | access to field elem2 | semmle.label | access to field elem2 | | B.cs:18:14:18:26 | access to field elem2 | semmle.label | access to field elem2 | | B.cs:29:26:29:27 | e1 : Elem | semmle.label | e1 : Elem | +| B.cs:29:26:29:27 | e1 : Elem | semmle.label | e1 : Elem | +| B.cs:29:35:29:36 | e2 : Elem | semmle.label | e2 : Elem | | B.cs:29:35:29:36 | e2 : Elem | semmle.label | e2 : Elem | | B.cs:31:13:31:16 | [post] this access [field elem1] : Elem | semmle.label | [post] this access [field elem1] : Elem | +| B.cs:31:13:31:16 | [post] this access [field elem1] : Elem | semmle.label | [post] this access [field elem1] : Elem | +| B.cs:31:26:31:27 | access to parameter e1 : Elem | semmle.label | access to parameter e1 : Elem | | B.cs:31:26:31:27 | access to parameter e1 : Elem | semmle.label | access to parameter e1 : Elem | | B.cs:32:13:32:16 | [post] this access [field elem2] : Elem | semmle.label | [post] this access [field elem2] : Elem | +| B.cs:32:13:32:16 | [post] this access [field elem2] : Elem | semmle.label | [post] this access [field elem2] : Elem | +| B.cs:32:26:32:27 | access to parameter e2 : Elem | semmle.label | access to parameter e2 : Elem | | B.cs:32:26:32:27 | access to parameter e2 : Elem | semmle.label | access to parameter e2 : Elem | | B.cs:39:26:39:27 | b1 [field elem1] : Elem | semmle.label | b1 [field elem1] : Elem | +| B.cs:39:26:39:27 | b1 [field elem1] : Elem | semmle.label | b1 [field elem1] : Elem | +| B.cs:39:26:39:27 | b1 [field elem2] : Elem | semmle.label | b1 [field elem2] : Elem | | B.cs:39:26:39:27 | b1 [field elem2] : Elem | semmle.label | b1 [field elem2] : Elem | | B.cs:41:13:41:16 | [post] this access [field box1, field elem1] : Elem | semmle.label | [post] this access [field box1, field elem1] : Elem | +| B.cs:41:13:41:16 | [post] this access [field box1, field elem1] : Elem | semmle.label | [post] this access [field box1, field elem1] : Elem | +| B.cs:41:13:41:16 | [post] this access [field box1, field elem2] : Elem | semmle.label | [post] this access [field box1, field elem2] : Elem | | B.cs:41:13:41:16 | [post] this access [field box1, field elem2] : Elem | semmle.label | [post] this access [field box1, field elem2] : Elem | | B.cs:41:25:41:26 | access to parameter b1 [field elem1] : Elem | semmle.label | access to parameter b1 [field elem1] : Elem | +| B.cs:41:25:41:26 | access to parameter b1 [field elem1] : Elem | semmle.label | access to parameter b1 [field elem1] : Elem | +| B.cs:41:25:41:26 | access to parameter b1 [field elem2] : Elem | semmle.label | access to parameter b1 [field elem2] : Elem | | B.cs:41:25:41:26 | access to parameter b1 [field elem2] : Elem | semmle.label | access to parameter b1 [field elem2] : Elem | | C.cs:3:18:3:19 | [post] this access [field s1] : Elem | semmle.label | [post] this access [field s1] : Elem | -| C.cs:3:23:3:32 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | +| C.cs:3:18:3:19 | [post] this access [field s1] : Elem | semmle.label | [post] this access [field s1] : Elem | +| C.cs:3:23:3:37 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| C.cs:3:23:3:37 | call to method Source : Elem | semmle.label | call to method Source : Elem | | C.cs:4:27:4:28 | [post] this access [field s2] : Elem | semmle.label | [post] this access [field s2] : Elem | -| C.cs:4:32:4:41 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | -| C.cs:6:30:6:39 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | +| C.cs:4:27:4:28 | [post] this access [field s2] : Elem | semmle.label | [post] this access [field s2] : Elem | +| C.cs:4:32:4:46 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| C.cs:4:32:4:46 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| C.cs:6:30:6:44 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| C.cs:6:30:6:44 | call to method Source : Elem | semmle.label | call to method Source : Elem | | C.cs:7:18:7:19 | [post] this access [property s5] : Elem | semmle.label | [post] this access [property s5] : Elem | -| C.cs:7:37:7:46 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | -| C.cs:8:30:8:39 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | +| C.cs:7:18:7:19 | [post] this access [property s5] : Elem | semmle.label | [post] this access [property s5] : Elem | +| C.cs:7:37:7:51 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| C.cs:7:37:7:51 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| C.cs:8:30:8:44 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| C.cs:8:30:8:44 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| C.cs:12:15:12:21 | object creation of type C [field s1] : Elem | semmle.label | object creation of type C [field s1] : Elem | | C.cs:12:15:12:21 | object creation of type C [field s1] : Elem | semmle.label | object creation of type C [field s1] : Elem | | C.cs:12:15:12:21 | object creation of type C [field s2] : Elem | semmle.label | object creation of type C [field s2] : Elem | +| C.cs:12:15:12:21 | object creation of type C [field s2] : Elem | semmle.label | object creation of type C [field s2] : Elem | +| C.cs:12:15:12:21 | object creation of type C [field s3] : Elem | semmle.label | object creation of type C [field s3] : Elem | | C.cs:12:15:12:21 | object creation of type C [field s3] : Elem | semmle.label | object creation of type C [field s3] : Elem | | C.cs:12:15:12:21 | object creation of type C [property s5] : Elem | semmle.label | object creation of type C [property s5] : Elem | +| C.cs:12:15:12:21 | object creation of type C [property s5] : Elem | semmle.label | object creation of type C [property s5] : Elem | +| C.cs:13:9:13:9 | access to local variable c [field s1] : Elem | semmle.label | access to local variable c [field s1] : Elem | | C.cs:13:9:13:9 | access to local variable c [field s1] : Elem | semmle.label | access to local variable c [field s1] : Elem | | C.cs:13:9:13:9 | access to local variable c [field s2] : Elem | semmle.label | access to local variable c [field s2] : Elem | +| C.cs:13:9:13:9 | access to local variable c [field s2] : Elem | semmle.label | access to local variable c [field s2] : Elem | +| C.cs:13:9:13:9 | access to local variable c [field s3] : Elem | semmle.label | access to local variable c [field s3] : Elem | | C.cs:13:9:13:9 | access to local variable c [field s3] : Elem | semmle.label | access to local variable c [field s3] : Elem | | C.cs:13:9:13:9 | access to local variable c [property s5] : Elem | semmle.label | access to local variable c [property s5] : Elem | +| C.cs:13:9:13:9 | access to local variable c [property s5] : Elem | semmle.label | access to local variable c [property s5] : Elem | | C.cs:18:9:18:12 | [post] this access [field s3] : Elem | semmle.label | [post] this access [field s3] : Elem | -| C.cs:18:19:18:28 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | +| C.cs:18:9:18:12 | [post] this access [field s3] : Elem | semmle.label | [post] this access [field s3] : Elem | +| C.cs:18:19:18:33 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| C.cs:18:19:18:33 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| C.cs:21:17:21:18 | this [field s1] : Elem | semmle.label | this [field s1] : Elem | | C.cs:21:17:21:18 | this [field s1] : Elem | semmle.label | this [field s1] : Elem | | C.cs:21:17:21:18 | this [field s2] : Elem | semmle.label | this [field s2] : Elem | +| C.cs:21:17:21:18 | this [field s2] : Elem | semmle.label | this [field s2] : Elem | +| C.cs:21:17:21:18 | this [field s3] : Elem | semmle.label | this [field s3] : Elem | | C.cs:21:17:21:18 | this [field s3] : Elem | semmle.label | this [field s3] : Elem | | C.cs:21:17:21:18 | this [property s5] : Elem | semmle.label | this [property s5] : Elem | +| C.cs:21:17:21:18 | this [property s5] : Elem | semmle.label | this [property s5] : Elem | +| C.cs:23:14:23:15 | access to field s1 | semmle.label | access to field s1 | | C.cs:23:14:23:15 | access to field s1 | semmle.label | access to field s1 | | C.cs:23:14:23:15 | this access [field s1] : Elem | semmle.label | this access [field s1] : Elem | +| C.cs:23:14:23:15 | this access [field s1] : Elem | semmle.label | this access [field s1] : Elem | +| C.cs:24:14:24:15 | access to field s2 | semmle.label | access to field s2 | | C.cs:24:14:24:15 | access to field s2 | semmle.label | access to field s2 | | C.cs:24:14:24:15 | this access [field s2] : Elem | semmle.label | this access [field s2] : Elem | +| C.cs:24:14:24:15 | this access [field s2] : Elem | semmle.label | this access [field s2] : Elem | +| C.cs:25:14:25:15 | access to field s3 | semmle.label | access to field s3 | | C.cs:25:14:25:15 | access to field s3 | semmle.label | access to field s3 | | C.cs:25:14:25:15 | this access [field s3] : Elem | semmle.label | this access [field s3] : Elem | +| C.cs:25:14:25:15 | this access [field s3] : Elem | semmle.label | this access [field s3] : Elem | +| C.cs:26:14:26:15 | access to field s4 | semmle.label | access to field s4 | | C.cs:26:14:26:15 | access to field s4 | semmle.label | access to field s4 | | C.cs:27:14:27:15 | access to property s5 | semmle.label | access to property s5 | +| C.cs:27:14:27:15 | access to property s5 | semmle.label | access to property s5 | +| C.cs:27:14:27:15 | this access [property s5] : Elem | semmle.label | this access [property s5] : Elem | | C.cs:27:14:27:15 | this access [property s5] : Elem | semmle.label | this access [property s5] : Elem | | C.cs:28:14:28:15 | access to property s6 | semmle.label | access to property s6 | +| C.cs:28:14:28:15 | access to property s6 | semmle.label | access to property s6 | +| D.cs:8:9:8:11 | this [field trivialPropField] : Object | semmle.label | this [field trivialPropField] : Object | | D.cs:8:9:8:11 | this [field trivialPropField] : Object | semmle.label | this [field trivialPropField] : Object | | D.cs:8:22:8:25 | this access [field trivialPropField] : Object | semmle.label | this access [field trivialPropField] : Object | +| D.cs:8:22:8:25 | this access [field trivialPropField] : Object | semmle.label | this access [field trivialPropField] : Object | +| D.cs:8:22:8:42 | access to field trivialPropField : Object | semmle.label | access to field trivialPropField : Object | | D.cs:8:22:8:42 | access to field trivialPropField : Object | semmle.label | access to field trivialPropField : Object | | D.cs:9:9:9:11 | value : Object | semmle.label | value : Object | +| D.cs:9:9:9:11 | value : Object | semmle.label | value : Object | +| D.cs:9:15:9:18 | [post] this access [field trivialPropField] : Object | semmle.label | [post] this access [field trivialPropField] : Object | | D.cs:9:15:9:18 | [post] this access [field trivialPropField] : Object | semmle.label | [post] this access [field trivialPropField] : Object | | D.cs:9:39:9:43 | access to parameter value : Object | semmle.label | access to parameter value : Object | +| D.cs:9:39:9:43 | access to parameter value : Object | semmle.label | access to parameter value : Object | +| D.cs:14:9:14:11 | this [field trivialPropField] : Object | semmle.label | this [field trivialPropField] : Object | | D.cs:14:9:14:11 | this [field trivialPropField] : Object | semmle.label | this [field trivialPropField] : Object | | D.cs:14:22:14:25 | this access [field trivialPropField] : Object | semmle.label | this access [field trivialPropField] : Object | +| D.cs:14:22:14:25 | this access [field trivialPropField] : Object | semmle.label | this access [field trivialPropField] : Object | +| D.cs:14:22:14:42 | access to field trivialPropField : Object | semmle.label | access to field trivialPropField : Object | | D.cs:14:22:14:42 | access to field trivialPropField : Object | semmle.label | access to field trivialPropField : Object | | D.cs:15:9:15:11 | value : Object | semmle.label | value : Object | +| D.cs:15:9:15:11 | value : Object | semmle.label | value : Object | +| D.cs:15:15:15:18 | [post] this access [field trivialPropField] : Object | semmle.label | [post] this access [field trivialPropField] : Object | | D.cs:15:15:15:18 | [post] this access [field trivialPropField] : Object | semmle.label | [post] this access [field trivialPropField] : Object | | D.cs:15:34:15:38 | access to parameter value : Object | semmle.label | access to parameter value : Object | +| D.cs:15:34:15:38 | access to parameter value : Object | semmle.label | access to parameter value : Object | +| D.cs:18:28:18:29 | o1 : Object | semmle.label | o1 : Object | | D.cs:18:28:18:29 | o1 : Object | semmle.label | o1 : Object | | D.cs:18:39:18:40 | o2 : Object | semmle.label | o2 : Object | +| D.cs:18:39:18:40 | o2 : Object | semmle.label | o2 : Object | +| D.cs:18:50:18:51 | o3 : Object | semmle.label | o3 : Object | | D.cs:18:50:18:51 | o3 : Object | semmle.label | o3 : Object | | D.cs:21:9:21:11 | [post] access to local variable ret [property AutoProp] : Object | semmle.label | [post] access to local variable ret [property AutoProp] : Object | +| D.cs:21:9:21:11 | [post] access to local variable ret [property AutoProp] : Object | semmle.label | [post] access to local variable ret [property AutoProp] : Object | +| D.cs:21:24:21:25 | access to parameter o1 : Object | semmle.label | access to parameter o1 : Object | | D.cs:21:24:21:25 | access to parameter o1 : Object | semmle.label | access to parameter o1 : Object | | D.cs:22:9:22:11 | [post] access to local variable ret [field trivialPropField] : Object | semmle.label | [post] access to local variable ret [field trivialPropField] : Object | +| D.cs:22:9:22:11 | [post] access to local variable ret [field trivialPropField] : Object | semmle.label | [post] access to local variable ret [field trivialPropField] : Object | +| D.cs:22:27:22:28 | access to parameter o2 : Object | semmle.label | access to parameter o2 : Object | | D.cs:22:27:22:28 | access to parameter o2 : Object | semmle.label | access to parameter o2 : Object | | D.cs:23:9:23:11 | [post] access to local variable ret [field trivialPropField] : Object | semmle.label | [post] access to local variable ret [field trivialPropField] : Object | +| D.cs:23:9:23:11 | [post] access to local variable ret [field trivialPropField] : Object | semmle.label | [post] access to local variable ret [field trivialPropField] : Object | +| D.cs:23:27:23:28 | access to parameter o3 : Object | semmle.label | access to parameter o3 : Object | | D.cs:23:27:23:28 | access to parameter o3 : Object | semmle.label | access to parameter o3 : Object | | D.cs:24:16:24:18 | access to local variable ret [field trivialPropField] : Object | semmle.label | access to local variable ret [field trivialPropField] : Object | | D.cs:24:16:24:18 | access to local variable ret [field trivialPropField] : Object | semmle.label | access to local variable ret [field trivialPropField] : Object | +| D.cs:24:16:24:18 | access to local variable ret [field trivialPropField] : Object | semmle.label | access to local variable ret [field trivialPropField] : Object | +| D.cs:24:16:24:18 | access to local variable ret [field trivialPropField] : Object | semmle.label | access to local variable ret [field trivialPropField] : Object | | D.cs:24:16:24:18 | access to local variable ret [property AutoProp] : Object | semmle.label | access to local variable ret [property AutoProp] : Object | -| D.cs:29:17:29:28 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| D.cs:24:16:24:18 | access to local variable ret [property AutoProp] : Object | semmle.label | access to local variable ret [property AutoProp] : Object | +| D.cs:29:17:29:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| D.cs:29:17:29:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| D.cs:31:17:31:37 | call to method Create [property AutoProp] : Object | semmle.label | call to method Create [property AutoProp] : Object | | D.cs:31:17:31:37 | call to method Create [property AutoProp] : Object | semmle.label | call to method Create [property AutoProp] : Object | | D.cs:31:24:31:24 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| D.cs:31:24:31:24 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| D.cs:32:14:32:14 | access to local variable d [property AutoProp] : Object | semmle.label | access to local variable d [property AutoProp] : Object | | D.cs:32:14:32:14 | access to local variable d [property AutoProp] : Object | semmle.label | access to local variable d [property AutoProp] : Object | | D.cs:32:14:32:23 | access to property AutoProp | semmle.label | access to property AutoProp | -| D.cs:37:13:37:33 | call to method Create [field trivialPropField] : Object | semmle.label | call to method Create [field trivialPropField] : Object | -| D.cs:37:26:37:26 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| D.cs:32:14:32:23 | access to property AutoProp | semmle.label | access to property AutoProp | +| D.cs:37:13:37:49 | call to method Create [field trivialPropField] : Object | semmle.label | call to method Create [field trivialPropField] : Object | +| D.cs:37:13:37:49 | call to method Create [field trivialPropField] : Object | semmle.label | call to method Create [field trivialPropField] : Object | +| D.cs:37:26:37:42 | call to method Source : Object | semmle.label | call to method Source : Object | +| D.cs:37:26:37:42 | call to method Source : Object | semmle.label | call to method Source : Object | +| D.cs:39:14:39:14 | access to local variable d [field trivialPropField] : Object | semmle.label | access to local variable d [field trivialPropField] : Object | | D.cs:39:14:39:14 | access to local variable d [field trivialPropField] : Object | semmle.label | access to local variable d [field trivialPropField] : Object | | D.cs:39:14:39:26 | access to property TrivialProp | semmle.label | access to property TrivialProp | +| D.cs:39:14:39:26 | access to property TrivialProp | semmle.label | access to property TrivialProp | +| D.cs:40:14:40:14 | access to local variable d [field trivialPropField] : Object | semmle.label | access to local variable d [field trivialPropField] : Object | | D.cs:40:14:40:14 | access to local variable d [field trivialPropField] : Object | semmle.label | access to local variable d [field trivialPropField] : Object | | D.cs:40:14:40:31 | access to field trivialPropField | semmle.label | access to field trivialPropField | +| D.cs:40:14:40:31 | access to field trivialPropField | semmle.label | access to field trivialPropField | +| D.cs:41:14:41:14 | access to local variable d [field trivialPropField] : Object | semmle.label | access to local variable d [field trivialPropField] : Object | | D.cs:41:14:41:14 | access to local variable d [field trivialPropField] : Object | semmle.label | access to local variable d [field trivialPropField] : Object | | D.cs:41:14:41:26 | access to property ComplexProp | semmle.label | access to property ComplexProp | -| D.cs:43:13:43:33 | call to method Create [field trivialPropField] : Object | semmle.label | call to method Create [field trivialPropField] : Object | -| D.cs:43:32:43:32 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| D.cs:41:14:41:26 | access to property ComplexProp | semmle.label | access to property ComplexProp | +| D.cs:43:13:43:49 | call to method Create [field trivialPropField] : Object | semmle.label | call to method Create [field trivialPropField] : Object | +| D.cs:43:13:43:49 | call to method Create [field trivialPropField] : Object | semmle.label | call to method Create [field trivialPropField] : Object | +| D.cs:43:32:43:48 | call to method Source : Object | semmle.label | call to method Source : Object | +| D.cs:43:32:43:48 | call to method Source : Object | semmle.label | call to method Source : Object | +| D.cs:45:14:45:14 | access to local variable d [field trivialPropField] : Object | semmle.label | access to local variable d [field trivialPropField] : Object | | D.cs:45:14:45:14 | access to local variable d [field trivialPropField] : Object | semmle.label | access to local variable d [field trivialPropField] : Object | | D.cs:45:14:45:26 | access to property TrivialProp | semmle.label | access to property TrivialProp | +| D.cs:45:14:45:26 | access to property TrivialProp | semmle.label | access to property TrivialProp | +| D.cs:46:14:46:14 | access to local variable d [field trivialPropField] : Object | semmle.label | access to local variable d [field trivialPropField] : Object | | D.cs:46:14:46:14 | access to local variable d [field trivialPropField] : Object | semmle.label | access to local variable d [field trivialPropField] : Object | | D.cs:46:14:46:31 | access to field trivialPropField | semmle.label | access to field trivialPropField | +| D.cs:46:14:46:31 | access to field trivialPropField | semmle.label | access to field trivialPropField | +| D.cs:47:14:47:14 | access to local variable d [field trivialPropField] : Object | semmle.label | access to local variable d [field trivialPropField] : Object | | D.cs:47:14:47:14 | access to local variable d [field trivialPropField] : Object | semmle.label | access to local variable d [field trivialPropField] : Object | | D.cs:47:14:47:26 | access to property ComplexProp | semmle.label | access to property ComplexProp | +| D.cs:47:14:47:26 | access to property ComplexProp | semmle.label | access to property ComplexProp | +| E.cs:8:29:8:29 | o : Object | semmle.label | o : Object | | E.cs:8:29:8:29 | o : Object | semmle.label | o : Object | | E.cs:11:9:11:11 | [post] access to local variable ret [field Field] : Object | semmle.label | [post] access to local variable ret [field Field] : Object | +| E.cs:11:9:11:11 | [post] access to local variable ret [field Field] : Object | semmle.label | [post] access to local variable ret [field Field] : Object | +| E.cs:11:21:11:21 | access to parameter o : Object | semmle.label | access to parameter o : Object | | E.cs:11:21:11:21 | access to parameter o : Object | semmle.label | access to parameter o : Object | | E.cs:12:16:12:18 | access to local variable ret [field Field] : Object | semmle.label | access to local variable ret [field Field] : Object | -| E.cs:22:17:22:28 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| E.cs:12:16:12:18 | access to local variable ret [field Field] : Object | semmle.label | access to local variable ret [field Field] : Object | +| E.cs:22:17:22:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| E.cs:22:17:22:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| E.cs:23:17:23:26 | call to method CreateS [field Field] : Object | semmle.label | call to method CreateS [field Field] : Object | | E.cs:23:17:23:26 | call to method CreateS [field Field] : Object | semmle.label | call to method CreateS [field Field] : Object | | E.cs:23:25:23:25 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| E.cs:23:25:23:25 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| E.cs:24:14:24:14 | access to local variable s [field Field] : Object | semmle.label | access to local variable s [field Field] : Object | | E.cs:24:14:24:14 | access to local variable s [field Field] : Object | semmle.label | access to local variable s [field Field] : Object | | E.cs:24:14:24:20 | access to field Field | semmle.label | access to field Field | +| E.cs:24:14:24:20 | access to field Field | semmle.label | access to field Field | +| F.cs:6:28:6:29 | o1 : Object | semmle.label | o1 : Object | | F.cs:6:28:6:29 | o1 : Object | semmle.label | o1 : Object | | F.cs:6:39:6:40 | o2 : Object | semmle.label | o2 : Object | +| F.cs:6:39:6:40 | o2 : Object | semmle.label | o2 : Object | +| F.cs:6:46:6:81 | object creation of type F [field Field1] : Object | semmle.label | object creation of type F [field Field1] : Object | | F.cs:6:46:6:81 | object creation of type F [field Field1] : Object | semmle.label | object creation of type F [field Field1] : Object | | F.cs:6:46:6:81 | object creation of type F [field Field2] : Object | semmle.label | object creation of type F [field Field2] : Object | +| F.cs:6:46:6:81 | object creation of type F [field Field2] : Object | semmle.label | object creation of type F [field Field2] : Object | +| F.cs:6:54:6:81 | { ..., ... } [field Field1] : Object | semmle.label | { ..., ... } [field Field1] : Object | | F.cs:6:54:6:81 | { ..., ... } [field Field1] : Object | semmle.label | { ..., ... } [field Field1] : Object | | F.cs:6:54:6:81 | { ..., ... } [field Field2] : Object | semmle.label | { ..., ... } [field Field2] : Object | +| F.cs:6:54:6:81 | { ..., ... } [field Field2] : Object | semmle.label | { ..., ... } [field Field2] : Object | +| F.cs:6:65:6:66 | access to parameter o1 : Object | semmle.label | access to parameter o1 : Object | | F.cs:6:65:6:66 | access to parameter o1 : Object | semmle.label | access to parameter o1 : Object | | F.cs:6:78:6:79 | access to parameter o2 : Object | semmle.label | access to parameter o2 : Object | -| F.cs:10:17:10:28 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| F.cs:6:78:6:79 | access to parameter o2 : Object | semmle.label | access to parameter o2 : Object | +| F.cs:10:17:10:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| F.cs:10:17:10:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| F.cs:11:17:11:31 | call to method Create [field Field1] : Object | semmle.label | call to method Create [field Field1] : Object | | F.cs:11:17:11:31 | call to method Create [field Field1] : Object | semmle.label | call to method Create [field Field1] : Object | | F.cs:11:24:11:24 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| F.cs:11:24:11:24 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| F.cs:12:14:12:14 | access to local variable f [field Field1] : Object | semmle.label | access to local variable f [field Field1] : Object | | F.cs:12:14:12:14 | access to local variable f [field Field1] : Object | semmle.label | access to local variable f [field Field1] : Object | | F.cs:12:14:12:21 | access to field Field1 | semmle.label | access to field Field1 | -| F.cs:15:13:15:27 | call to method Create [field Field2] : Object | semmle.label | call to method Create [field Field2] : Object | -| F.cs:15:26:15:26 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| F.cs:12:14:12:21 | access to field Field1 | semmle.label | access to field Field1 | +| F.cs:15:13:15:43 | call to method Create [field Field2] : Object | semmle.label | call to method Create [field Field2] : Object | +| F.cs:15:13:15:43 | call to method Create [field Field2] : Object | semmle.label | call to method Create [field Field2] : Object | +| F.cs:15:26:15:42 | call to method Source : Object | semmle.label | call to method Source : Object | +| F.cs:15:26:15:42 | call to method Source : Object | semmle.label | call to method Source : Object | +| F.cs:17:14:17:14 | access to local variable f [field Field2] : Object | semmle.label | access to local variable f [field Field2] : Object | | F.cs:17:14:17:14 | access to local variable f [field Field2] : Object | semmle.label | access to local variable f [field Field2] : Object | | F.cs:17:14:17:21 | access to field Field2 | semmle.label | access to field Field2 | -| F.cs:19:21:19:34 | { ..., ... } [field Field1] : Object | semmle.label | { ..., ... } [field Field1] : Object | -| F.cs:19:32:19:32 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| F.cs:17:14:17:21 | access to field Field2 | semmle.label | access to field Field2 | +| F.cs:19:21:19:50 | { ..., ... } [field Field1] : Object | semmle.label | { ..., ... } [field Field1] : Object | +| F.cs:19:21:19:50 | { ..., ... } [field Field1] : Object | semmle.label | { ..., ... } [field Field1] : Object | +| F.cs:19:32:19:48 | call to method Source : Object | semmle.label | call to method Source : Object | +| F.cs:19:32:19:48 | call to method Source : Object | semmle.label | call to method Source : Object | +| F.cs:20:14:20:14 | access to local variable f [field Field1] : Object | semmle.label | access to local variable f [field Field1] : Object | | F.cs:20:14:20:14 | access to local variable f [field Field1] : Object | semmle.label | access to local variable f [field Field1] : Object | | F.cs:20:14:20:21 | access to field Field1 | semmle.label | access to field Field1 | -| F.cs:23:21:23:34 | { ..., ... } [field Field2] : Object | semmle.label | { ..., ... } [field Field2] : Object | -| F.cs:23:32:23:32 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| F.cs:20:14:20:21 | access to field Field1 | semmle.label | access to field Field1 | +| F.cs:23:21:23:50 | { ..., ... } [field Field2] : Object | semmle.label | { ..., ... } [field Field2] : Object | +| F.cs:23:21:23:50 | { ..., ... } [field Field2] : Object | semmle.label | { ..., ... } [field Field2] : Object | +| F.cs:23:32:23:48 | call to method Source : Object | semmle.label | call to method Source : Object | +| F.cs:23:32:23:48 | call to method Source : Object | semmle.label | call to method Source : Object | +| F.cs:25:14:25:14 | access to local variable f [field Field2] : Object | semmle.label | access to local variable f [field Field2] : Object | | F.cs:25:14:25:14 | access to local variable f [field Field2] : Object | semmle.label | access to local variable f [field Field2] : Object | | F.cs:25:14:25:21 | access to field Field2 | semmle.label | access to field Field2 | -| G.cs:7:18:7:27 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | +| F.cs:25:14:25:21 | access to field Field2 | semmle.label | access to field Field2 | +| G.cs:7:18:7:32 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| G.cs:7:18:7:32 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| G.cs:9:9:9:9 | [post] access to local variable b [field Box1, field Elem] : Elem | semmle.label | [post] access to local variable b [field Box1, field Elem] : Elem | | G.cs:9:9:9:9 | [post] access to local variable b [field Box1, field Elem] : Elem | semmle.label | [post] access to local variable b [field Box1, field Elem] : Elem | | G.cs:9:9:9:14 | [post] access to field Box1 [field Elem] : Elem | semmle.label | [post] access to field Box1 [field Elem] : Elem | +| G.cs:9:9:9:14 | [post] access to field Box1 [field Elem] : Elem | semmle.label | [post] access to field Box1 [field Elem] : Elem | +| G.cs:9:23:9:23 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | | G.cs:9:23:9:23 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | | G.cs:10:18:10:18 | access to local variable b [field Box1, field Elem] : Elem | semmle.label | access to local variable b [field Box1, field Elem] : Elem | -| G.cs:15:18:15:27 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | +| G.cs:10:18:10:18 | access to local variable b [field Box1, field Elem] : Elem | semmle.label | access to local variable b [field Box1, field Elem] : Elem | +| G.cs:15:18:15:32 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| G.cs:15:18:15:32 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| G.cs:17:9:17:9 | [post] access to local variable b [field Box1, field Elem] : Elem | semmle.label | [post] access to local variable b [field Box1, field Elem] : Elem | | G.cs:17:9:17:9 | [post] access to local variable b [field Box1, field Elem] : Elem | semmle.label | [post] access to local variable b [field Box1, field Elem] : Elem | | G.cs:17:9:17:14 | [post] access to field Box1 [field Elem] : Elem | semmle.label | [post] access to field Box1 [field Elem] : Elem | +| G.cs:17:9:17:14 | [post] access to field Box1 [field Elem] : Elem | semmle.label | [post] access to field Box1 [field Elem] : Elem | +| G.cs:17:24:17:24 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | | G.cs:17:24:17:24 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | | G.cs:18:18:18:18 | access to local variable b [field Box1, field Elem] : Elem | semmle.label | access to local variable b [field Box1, field Elem] : Elem | -| G.cs:23:18:23:27 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | +| G.cs:18:18:18:18 | access to local variable b [field Box1, field Elem] : Elem | semmle.label | access to local variable b [field Box1, field Elem] : Elem | +| G.cs:23:18:23:32 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| G.cs:23:18:23:32 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| G.cs:25:9:25:9 | [post] access to local variable b [field Box1, field Elem] : Elem | semmle.label | [post] access to local variable b [field Box1, field Elem] : Elem | | G.cs:25:9:25:9 | [post] access to local variable b [field Box1, field Elem] : Elem | semmle.label | [post] access to local variable b [field Box1, field Elem] : Elem | | G.cs:25:9:25:19 | [post] call to method GetBox1 [field Elem] : Elem | semmle.label | [post] call to method GetBox1 [field Elem] : Elem | +| G.cs:25:9:25:19 | [post] call to method GetBox1 [field Elem] : Elem | semmle.label | [post] call to method GetBox1 [field Elem] : Elem | +| G.cs:25:28:25:28 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | | G.cs:25:28:25:28 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | | G.cs:26:18:26:18 | access to local variable b [field Box1, field Elem] : Elem | semmle.label | access to local variable b [field Box1, field Elem] : Elem | -| G.cs:31:18:31:27 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | +| G.cs:26:18:26:18 | access to local variable b [field Box1, field Elem] : Elem | semmle.label | access to local variable b [field Box1, field Elem] : Elem | +| G.cs:31:18:31:32 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| G.cs:31:18:31:32 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| G.cs:33:9:33:9 | [post] access to local variable b [field Box1, field Elem] : Elem | semmle.label | [post] access to local variable b [field Box1, field Elem] : Elem | | G.cs:33:9:33:9 | [post] access to local variable b [field Box1, field Elem] : Elem | semmle.label | [post] access to local variable b [field Box1, field Elem] : Elem | | G.cs:33:9:33:19 | [post] call to method GetBox1 [field Elem] : Elem | semmle.label | [post] call to method GetBox1 [field Elem] : Elem | +| G.cs:33:9:33:19 | [post] call to method GetBox1 [field Elem] : Elem | semmle.label | [post] call to method GetBox1 [field Elem] : Elem | +| G.cs:33:29:33:29 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | | G.cs:33:29:33:29 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | | G.cs:34:18:34:18 | access to local variable b [field Box1, field Elem] : Elem | semmle.label | access to local variable b [field Box1, field Elem] : Elem | +| G.cs:34:18:34:18 | access to local variable b [field Box1, field Elem] : Elem | semmle.label | access to local variable b [field Box1, field Elem] : Elem | +| G.cs:37:38:37:39 | b2 [field Box1, field Elem] : Elem | semmle.label | b2 [field Box1, field Elem] : Elem | | G.cs:37:38:37:39 | b2 [field Box1, field Elem] : Elem | semmle.label | b2 [field Box1, field Elem] : Elem | | G.cs:39:14:39:15 | access to parameter b2 [field Box1, field Elem] : Elem | semmle.label | access to parameter b2 [field Box1, field Elem] : Elem | +| G.cs:39:14:39:15 | access to parameter b2 [field Box1, field Elem] : Elem | semmle.label | access to parameter b2 [field Box1, field Elem] : Elem | +| G.cs:39:14:39:25 | call to method GetBox1 [field Elem] : Elem | semmle.label | call to method GetBox1 [field Elem] : Elem | | G.cs:39:14:39:25 | call to method GetBox1 [field Elem] : Elem | semmle.label | call to method GetBox1 [field Elem] : Elem | | G.cs:39:14:39:35 | call to method GetElem | semmle.label | call to method GetElem | -| G.cs:44:18:44:27 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | +| G.cs:39:14:39:35 | call to method GetElem | semmle.label | call to method GetElem | +| G.cs:44:18:44:32 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| G.cs:44:18:44:32 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| G.cs:46:9:46:16 | [post] access to field boxfield [field Box1, field Elem] : Elem | semmle.label | [post] access to field boxfield [field Box1, field Elem] : Elem | | G.cs:46:9:46:16 | [post] access to field boxfield [field Box1, field Elem] : Elem | semmle.label | [post] access to field boxfield [field Box1, field Elem] : Elem | | G.cs:46:9:46:16 | [post] this access [field boxfield, field Box1, field Elem] : Elem | semmle.label | [post] this access [field boxfield, field Box1, field Elem] : Elem | +| G.cs:46:9:46:16 | [post] this access [field boxfield, field Box1, field Elem] : Elem | semmle.label | [post] this access [field boxfield, field Box1, field Elem] : Elem | +| G.cs:46:9:46:21 | [post] access to field Box1 [field Elem] : Elem | semmle.label | [post] access to field Box1 [field Elem] : Elem | | G.cs:46:9:46:21 | [post] access to field Box1 [field Elem] : Elem | semmle.label | [post] access to field Box1 [field Elem] : Elem | | G.cs:46:30:46:30 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | +| G.cs:46:30:46:30 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | +| G.cs:47:9:47:13 | this access [field boxfield, field Box1, field Elem] : Elem | semmle.label | this access [field boxfield, field Box1, field Elem] : Elem | | G.cs:47:9:47:13 | this access [field boxfield, field Box1, field Elem] : Elem | semmle.label | this access [field boxfield, field Box1, field Elem] : Elem | | G.cs:50:18:50:20 | this [field boxfield, field Box1, field Elem] : Elem | semmle.label | this [field boxfield, field Box1, field Elem] : Elem | +| G.cs:50:18:50:20 | this [field boxfield, field Box1, field Elem] : Elem | semmle.label | this [field boxfield, field Box1, field Elem] : Elem | +| G.cs:52:14:52:21 | access to field boxfield [field Box1, field Elem] : Elem | semmle.label | access to field boxfield [field Box1, field Elem] : Elem | | G.cs:52:14:52:21 | access to field boxfield [field Box1, field Elem] : Elem | semmle.label | access to field boxfield [field Box1, field Elem] : Elem | | G.cs:52:14:52:21 | this access [field boxfield, field Box1, field Elem] : Elem | semmle.label | this access [field boxfield, field Box1, field Elem] : Elem | +| G.cs:52:14:52:21 | this access [field boxfield, field Box1, field Elem] : Elem | semmle.label | this access [field boxfield, field Box1, field Elem] : Elem | +| G.cs:52:14:52:26 | access to field Box1 [field Elem] : Elem | semmle.label | access to field Box1 [field Elem] : Elem | | G.cs:52:14:52:26 | access to field Box1 [field Elem] : Elem | semmle.label | access to field Box1 [field Elem] : Elem | | G.cs:52:14:52:31 | access to field Elem | semmle.label | access to field Elem | +| G.cs:52:14:52:31 | access to field Elem | semmle.label | access to field Elem | +| G.cs:63:21:63:27 | this [field Elem] : Elem | semmle.label | this [field Elem] : Elem | | G.cs:63:21:63:27 | this [field Elem] : Elem | semmle.label | this [field Elem] : Elem | | G.cs:63:34:63:37 | access to field Elem : Elem | semmle.label | access to field Elem : Elem | +| G.cs:63:34:63:37 | access to field Elem : Elem | semmle.label | access to field Elem : Elem | +| G.cs:63:34:63:37 | this access [field Elem] : Elem | semmle.label | this access [field Elem] : Elem | | G.cs:63:34:63:37 | this access [field Elem] : Elem | semmle.label | this access [field Elem] : Elem | | G.cs:64:34:64:34 | e : Elem | semmle.label | e : Elem | +| G.cs:64:34:64:34 | e : Elem | semmle.label | e : Elem | +| G.cs:64:39:64:42 | [post] this access [field Elem] : Elem | semmle.label | [post] this access [field Elem] : Elem | | G.cs:64:39:64:42 | [post] this access [field Elem] : Elem | semmle.label | [post] this access [field Elem] : Elem | | G.cs:64:46:64:46 | access to parameter e : Elem | semmle.label | access to parameter e : Elem | +| G.cs:64:46:64:46 | access to parameter e : Elem | semmle.label | access to parameter e : Elem | +| G.cs:71:21:71:27 | this [field Box1, field Elem] : Elem | semmle.label | this [field Box1, field Elem] : Elem | | G.cs:71:21:71:27 | this [field Box1, field Elem] : Elem | semmle.label | this [field Box1, field Elem] : Elem | | G.cs:71:34:71:37 | access to field Box1 [field Elem] : Elem | semmle.label | access to field Box1 [field Elem] : Elem | +| G.cs:71:34:71:37 | access to field Box1 [field Elem] : Elem | semmle.label | access to field Box1 [field Elem] : Elem | +| G.cs:71:34:71:37 | this access [field Box1, field Elem] : Elem | semmle.label | this access [field Box1, field Elem] : Elem | | G.cs:71:34:71:37 | this access [field Box1, field Elem] : Elem | semmle.label | this access [field Box1, field Elem] : Elem | | H.cs:13:15:13:15 | a [field FieldA] : Object | semmle.label | a [field FieldA] : Object | +| H.cs:13:15:13:15 | a [field FieldA] : Object | semmle.label | a [field FieldA] : Object | +| H.cs:16:9:16:11 | [post] access to local variable ret [field FieldA] : Object | semmle.label | [post] access to local variable ret [field FieldA] : Object | | H.cs:16:9:16:11 | [post] access to local variable ret [field FieldA] : Object | semmle.label | [post] access to local variable ret [field FieldA] : Object | | H.cs:16:22:16:22 | access to parameter a [field FieldA] : Object | semmle.label | access to parameter a [field FieldA] : Object | +| H.cs:16:22:16:22 | access to parameter a [field FieldA] : Object | semmle.label | access to parameter a [field FieldA] : Object | +| H.cs:16:22:16:29 | access to field FieldA : Object | semmle.label | access to field FieldA : Object | | H.cs:16:22:16:29 | access to field FieldA : Object | semmle.label | access to field FieldA : Object | | H.cs:17:16:17:18 | access to local variable ret [field FieldA] : Object | semmle.label | access to local variable ret [field FieldA] : Object | +| H.cs:17:16:17:18 | access to local variable ret [field FieldA] : Object | semmle.label | access to local variable ret [field FieldA] : Object | | H.cs:23:9:23:9 | [post] access to local variable a [field FieldA] : Object | semmle.label | [post] access to local variable a [field FieldA] : Object | -| H.cs:23:20:23:31 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| H.cs:23:9:23:9 | [post] access to local variable a [field FieldA] : Object | semmle.label | [post] access to local variable a [field FieldA] : Object | +| H.cs:23:20:23:36 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:23:20:23:36 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:24:21:24:28 | call to method Clone [field FieldA] : Object | semmle.label | call to method Clone [field FieldA] : Object | | H.cs:24:21:24:28 | call to method Clone [field FieldA] : Object | semmle.label | call to method Clone [field FieldA] : Object | | H.cs:24:27:24:27 | access to local variable a [field FieldA] : Object | semmle.label | access to local variable a [field FieldA] : Object | +| H.cs:24:27:24:27 | access to local variable a [field FieldA] : Object | semmle.label | access to local variable a [field FieldA] : Object | +| H.cs:25:14:25:18 | access to local variable clone [field FieldA] : Object | semmle.label | access to local variable clone [field FieldA] : Object | | H.cs:25:14:25:18 | access to local variable clone [field FieldA] : Object | semmle.label | access to local variable clone [field FieldA] : Object | | H.cs:25:14:25:25 | access to field FieldA | semmle.label | access to field FieldA | +| H.cs:25:14:25:25 | access to field FieldA | semmle.label | access to field FieldA | +| H.cs:33:19:33:19 | a [field FieldA] : A | semmle.label | a [field FieldA] : A | | H.cs:33:19:33:19 | a [field FieldA] : A | semmle.label | a [field FieldA] : A | | H.cs:33:19:33:19 | a [field FieldA] : Object | semmle.label | a [field FieldA] : Object | +| H.cs:33:19:33:19 | a [field FieldA] : Object | semmle.label | a [field FieldA] : Object | +| H.cs:36:9:36:9 | [post] access to local variable b [field FieldB] : A | semmle.label | [post] access to local variable b [field FieldB] : A | | H.cs:36:9:36:9 | [post] access to local variable b [field FieldB] : A | semmle.label | [post] access to local variable b [field FieldB] : A | | H.cs:36:9:36:9 | [post] access to local variable b [field FieldB] : Object | semmle.label | [post] access to local variable b [field FieldB] : Object | +| H.cs:36:9:36:9 | [post] access to local variable b [field FieldB] : Object | semmle.label | [post] access to local variable b [field FieldB] : Object | +| H.cs:36:20:36:20 | access to parameter a [field FieldA] : A | semmle.label | access to parameter a [field FieldA] : A | | H.cs:36:20:36:20 | access to parameter a [field FieldA] : A | semmle.label | access to parameter a [field FieldA] : A | | H.cs:36:20:36:20 | access to parameter a [field FieldA] : Object | semmle.label | access to parameter a [field FieldA] : Object | +| H.cs:36:20:36:20 | access to parameter a [field FieldA] : Object | semmle.label | access to parameter a [field FieldA] : Object | +| H.cs:36:20:36:27 | access to field FieldA : A | semmle.label | access to field FieldA : A | | H.cs:36:20:36:27 | access to field FieldA : A | semmle.label | access to field FieldA : A | | H.cs:36:20:36:27 | access to field FieldA : Object | semmle.label | access to field FieldA : Object | +| H.cs:36:20:36:27 | access to field FieldA : Object | semmle.label | access to field FieldA : Object | +| H.cs:37:16:37:16 | access to local variable b [field FieldB] : A | semmle.label | access to local variable b [field FieldB] : A | | H.cs:37:16:37:16 | access to local variable b [field FieldB] : A | semmle.label | access to local variable b [field FieldB] : A | | H.cs:37:16:37:16 | access to local variable b [field FieldB] : Object | semmle.label | access to local variable b [field FieldB] : Object | +| H.cs:37:16:37:16 | access to local variable b [field FieldB] : Object | semmle.label | access to local variable b [field FieldB] : Object | | H.cs:43:9:43:9 | [post] access to local variable a [field FieldA] : Object | semmle.label | [post] access to local variable a [field FieldA] : Object | -| H.cs:43:20:43:31 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| H.cs:43:9:43:9 | [post] access to local variable a [field FieldA] : Object | semmle.label | [post] access to local variable a [field FieldA] : Object | +| H.cs:43:20:43:36 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:43:20:43:36 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:44:17:44:28 | call to method Transform [field FieldB] : Object | semmle.label | call to method Transform [field FieldB] : Object | | H.cs:44:17:44:28 | call to method Transform [field FieldB] : Object | semmle.label | call to method Transform [field FieldB] : Object | | H.cs:44:27:44:27 | access to local variable a [field FieldA] : Object | semmle.label | access to local variable a [field FieldA] : Object | +| H.cs:44:27:44:27 | access to local variable a [field FieldA] : Object | semmle.label | access to local variable a [field FieldA] : Object | +| H.cs:45:14:45:14 | access to local variable b [field FieldB] : Object | semmle.label | access to local variable b [field FieldB] : Object | | H.cs:45:14:45:14 | access to local variable b [field FieldB] : Object | semmle.label | access to local variable b [field FieldB] : Object | | H.cs:45:14:45:21 | access to field FieldB | semmle.label | access to field FieldB | +| H.cs:45:14:45:21 | access to field FieldB | semmle.label | access to field FieldB | +| H.cs:53:25:53:25 | a [field FieldA] : Object | semmle.label | a [field FieldA] : Object | | H.cs:53:25:53:25 | a [field FieldA] : Object | semmle.label | a [field FieldA] : Object | | H.cs:55:9:55:10 | [post] access to parameter b1 [field FieldB] : Object | semmle.label | [post] access to parameter b1 [field FieldB] : Object | +| H.cs:55:9:55:10 | [post] access to parameter b1 [field FieldB] : Object | semmle.label | [post] access to parameter b1 [field FieldB] : Object | +| H.cs:55:21:55:21 | access to parameter a [field FieldA] : Object | semmle.label | access to parameter a [field FieldA] : Object | | H.cs:55:21:55:21 | access to parameter a [field FieldA] : Object | semmle.label | access to parameter a [field FieldA] : Object | | H.cs:55:21:55:28 | access to field FieldA : Object | semmle.label | access to field FieldA : Object | +| H.cs:55:21:55:28 | access to field FieldA : Object | semmle.label | access to field FieldA : Object | | H.cs:63:9:63:9 | [post] access to local variable a [field FieldA] : Object | semmle.label | [post] access to local variable a [field FieldA] : Object | -| H.cs:63:20:63:31 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| H.cs:63:9:63:9 | [post] access to local variable a [field FieldA] : Object | semmle.label | [post] access to local variable a [field FieldA] : Object | +| H.cs:63:20:63:36 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:63:20:63:36 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:64:22:64:22 | access to local variable a [field FieldA] : Object | semmle.label | access to local variable a [field FieldA] : Object | | H.cs:64:22:64:22 | access to local variable a [field FieldA] : Object | semmle.label | access to local variable a [field FieldA] : Object | | H.cs:64:25:64:26 | [post] access to local variable b1 [field FieldB] : Object | semmle.label | [post] access to local variable b1 [field FieldB] : Object | +| H.cs:64:25:64:26 | [post] access to local variable b1 [field FieldB] : Object | semmle.label | [post] access to local variable b1 [field FieldB] : Object | +| H.cs:65:14:65:15 | access to local variable b1 [field FieldB] : Object | semmle.label | access to local variable b1 [field FieldB] : Object | | H.cs:65:14:65:15 | access to local variable b1 [field FieldB] : Object | semmle.label | access to local variable b1 [field FieldB] : Object | | H.cs:65:14:65:22 | access to field FieldB | semmle.label | access to field FieldB | +| H.cs:65:14:65:22 | access to field FieldB | semmle.label | access to field FieldB | +| H.cs:77:30:77:30 | o : Object | semmle.label | o : Object | | H.cs:77:30:77:30 | o : Object | semmle.label | o : Object | | H.cs:79:9:79:9 | [post] access to parameter a [field FieldA] : Object | semmle.label | [post] access to parameter a [field FieldA] : Object | +| H.cs:79:9:79:9 | [post] access to parameter a [field FieldA] : Object | semmle.label | [post] access to parameter a [field FieldA] : Object | +| H.cs:79:20:79:20 | access to parameter o : Object | semmle.label | access to parameter o : Object | | H.cs:79:20:79:20 | access to parameter o : Object | semmle.label | access to parameter o : Object | | H.cs:80:22:80:22 | access to parameter a [field FieldA] : Object | semmle.label | access to parameter a [field FieldA] : Object | +| H.cs:80:22:80:22 | access to parameter a [field FieldA] : Object | semmle.label | access to parameter a [field FieldA] : Object | +| H.cs:80:25:80:26 | [post] access to parameter b1 [field FieldB] : Object | semmle.label | [post] access to parameter b1 [field FieldB] : Object | | H.cs:80:25:80:26 | [post] access to parameter b1 [field FieldB] : Object | semmle.label | [post] access to parameter b1 [field FieldB] : Object | | H.cs:88:17:88:17 | [post] access to local variable a [field FieldA] : Object | semmle.label | [post] access to local variable a [field FieldA] : Object | -| H.cs:88:20:88:31 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | -| H.cs:88:34:88:35 | [post] access to local variable b1 [field FieldB] : Object | semmle.label | [post] access to local variable b1 [field FieldB] : Object | +| H.cs:88:17:88:17 | [post] access to local variable a [field FieldA] : Object | semmle.label | [post] access to local variable a [field FieldA] : Object | +| H.cs:88:20:88:36 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:88:20:88:36 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:88:39:88:40 | [post] access to local variable b1 [field FieldB] : Object | semmle.label | [post] access to local variable b1 [field FieldB] : Object | +| H.cs:88:39:88:40 | [post] access to local variable b1 [field FieldB] : Object | semmle.label | [post] access to local variable b1 [field FieldB] : Object | +| H.cs:89:14:89:14 | access to local variable a [field FieldA] : Object | semmle.label | access to local variable a [field FieldA] : Object | | H.cs:89:14:89:14 | access to local variable a [field FieldA] : Object | semmle.label | access to local variable a [field FieldA] : Object | | H.cs:89:14:89:21 | access to field FieldA | semmle.label | access to field FieldA | +| H.cs:89:14:89:21 | access to field FieldA | semmle.label | access to field FieldA | +| H.cs:90:14:90:15 | access to local variable b1 [field FieldB] : Object | semmle.label | access to local variable b1 [field FieldB] : Object | | H.cs:90:14:90:15 | access to local variable b1 [field FieldB] : Object | semmle.label | access to local variable b1 [field FieldB] : Object | | H.cs:90:14:90:22 | access to field FieldB | semmle.label | access to field FieldB | +| H.cs:90:14:90:22 | access to field FieldB | semmle.label | access to field FieldB | +| H.cs:102:23:102:23 | a [field FieldA] : Object | semmle.label | a [field FieldA] : Object | | H.cs:102:23:102:23 | a [field FieldA] : Object | semmle.label | a [field FieldA] : Object | | H.cs:105:9:105:12 | [post] access to local variable temp [field FieldB, field FieldA] : Object | semmle.label | [post] access to local variable temp [field FieldB, field FieldA] : Object | +| H.cs:105:9:105:12 | [post] access to local variable temp [field FieldB, field FieldA] : Object | semmle.label | [post] access to local variable temp [field FieldB, field FieldA] : Object | +| H.cs:105:23:105:23 | access to parameter a [field FieldA] : Object | semmle.label | access to parameter a [field FieldA] : Object | | H.cs:105:23:105:23 | access to parameter a [field FieldA] : Object | semmle.label | access to parameter a [field FieldA] : Object | | H.cs:106:16:106:40 | call to method Transform [field FieldB] : Object | semmle.label | call to method Transform [field FieldB] : Object | +| H.cs:106:16:106:40 | call to method Transform [field FieldB] : Object | semmle.label | call to method Transform [field FieldB] : Object | +| H.cs:106:26:106:39 | (...) ... [field FieldA] : Object | semmle.label | (...) ... [field FieldA] : Object | | H.cs:106:26:106:39 | (...) ... [field FieldA] : Object | semmle.label | (...) ... [field FieldA] : Object | | H.cs:106:29:106:32 | access to local variable temp [field FieldB, field FieldA] : Object | semmle.label | access to local variable temp [field FieldB, field FieldA] : Object | +| H.cs:106:29:106:32 | access to local variable temp [field FieldB, field FieldA] : Object | semmle.label | access to local variable temp [field FieldB, field FieldA] : Object | +| H.cs:106:29:106:39 | access to field FieldB [field FieldA] : Object | semmle.label | access to field FieldB [field FieldA] : Object | | H.cs:106:29:106:39 | access to field FieldB [field FieldA] : Object | semmle.label | access to field FieldB [field FieldA] : Object | | H.cs:112:9:112:9 | [post] access to local variable a [field FieldA] : Object | semmle.label | [post] access to local variable a [field FieldA] : Object | -| H.cs:112:20:112:31 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| H.cs:112:9:112:9 | [post] access to local variable a [field FieldA] : Object | semmle.label | [post] access to local variable a [field FieldA] : Object | +| H.cs:112:20:112:36 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:112:20:112:36 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:113:17:113:32 | call to method TransformWrap [field FieldB] : Object | semmle.label | call to method TransformWrap [field FieldB] : Object | | H.cs:113:17:113:32 | call to method TransformWrap [field FieldB] : Object | semmle.label | call to method TransformWrap [field FieldB] : Object | | H.cs:113:31:113:31 | access to local variable a [field FieldA] : Object | semmle.label | access to local variable a [field FieldA] : Object | +| H.cs:113:31:113:31 | access to local variable a [field FieldA] : Object | semmle.label | access to local variable a [field FieldA] : Object | +| H.cs:114:14:114:14 | access to local variable b [field FieldB] : Object | semmle.label | access to local variable b [field FieldB] : Object | | H.cs:114:14:114:14 | access to local variable b [field FieldB] : Object | semmle.label | access to local variable b [field FieldB] : Object | | H.cs:114:14:114:21 | access to field FieldB | semmle.label | access to field FieldB | +| H.cs:114:14:114:21 | access to field FieldB | semmle.label | access to field FieldB | +| H.cs:122:18:122:18 | a [field FieldA] : Object | semmle.label | a [field FieldA] : Object | | H.cs:122:18:122:18 | a [field FieldA] : Object | semmle.label | a [field FieldA] : Object | | H.cs:124:16:124:27 | call to method Transform [field FieldB] : Object | semmle.label | call to method Transform [field FieldB] : Object | +| H.cs:124:16:124:27 | call to method Transform [field FieldB] : Object | semmle.label | call to method Transform [field FieldB] : Object | +| H.cs:124:16:124:34 | access to field FieldB : Object | semmle.label | access to field FieldB : Object | | H.cs:124:16:124:34 | access to field FieldB : Object | semmle.label | access to field FieldB : Object | | H.cs:124:26:124:26 | access to parameter a [field FieldA] : Object | semmle.label | access to parameter a [field FieldA] : Object | +| H.cs:124:26:124:26 | access to parameter a [field FieldA] : Object | semmle.label | access to parameter a [field FieldA] : Object | | H.cs:130:9:130:9 | [post] access to local variable a [field FieldA] : Object | semmle.label | [post] access to local variable a [field FieldA] : Object | -| H.cs:130:20:130:31 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| H.cs:130:9:130:9 | [post] access to local variable a [field FieldA] : Object | semmle.label | [post] access to local variable a [field FieldA] : Object | +| H.cs:130:20:130:36 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:130:20:130:36 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:131:14:131:19 | call to method Get | semmle.label | call to method Get | | H.cs:131:14:131:19 | call to method Get | semmle.label | call to method Get | | H.cs:131:18:131:18 | access to local variable a [field FieldA] : Object | semmle.label | access to local variable a [field FieldA] : Object | +| H.cs:131:18:131:18 | access to local variable a [field FieldA] : Object | semmle.label | access to local variable a [field FieldA] : Object | +| H.cs:138:27:138:27 | o : A | semmle.label | o : A | | H.cs:138:27:138:27 | o : A | semmle.label | o : A | | H.cs:141:9:141:9 | [post] access to local variable a [field FieldA] : A | semmle.label | [post] access to local variable a [field FieldA] : A | +| H.cs:141:9:141:9 | [post] access to local variable a [field FieldA] : A | semmle.label | [post] access to local variable a [field FieldA] : A | +| H.cs:141:20:141:25 | ... as ... : A | semmle.label | ... as ... : A | | H.cs:141:20:141:25 | ... as ... : A | semmle.label | ... as ... : A | | H.cs:142:16:142:27 | call to method Transform [field FieldB] : A | semmle.label | call to method Transform [field FieldB] : A | +| H.cs:142:16:142:27 | call to method Transform [field FieldB] : A | semmle.label | call to method Transform [field FieldB] : A | +| H.cs:142:16:142:34 | access to field FieldB : A | semmle.label | access to field FieldB : A | | H.cs:142:16:142:34 | access to field FieldB : A | semmle.label | access to field FieldB : A | | H.cs:142:26:142:26 | access to local variable a [field FieldA] : A | semmle.label | access to local variable a [field FieldA] : A | -| H.cs:147:17:147:32 | call to method Through : A | semmle.label | call to method Through : A | -| H.cs:147:25:147:31 | object creation of type A : A | semmle.label | object creation of type A : A | +| H.cs:142:26:142:26 | access to local variable a [field FieldA] : A | semmle.label | access to local variable a [field FieldA] : A | +| H.cs:147:17:147:39 | call to method Through : A | semmle.label | call to method Through : A | +| H.cs:147:17:147:39 | call to method Through : A | semmle.label | call to method Through : A | +| H.cs:147:25:147:38 | call to method Source : A | semmle.label | call to method Source : A | +| H.cs:147:25:147:38 | call to method Source : A | semmle.label | call to method Source : A | +| H.cs:148:14:148:14 | access to local variable a | semmle.label | access to local variable a | | H.cs:148:14:148:14 | access to local variable a | semmle.label | access to local variable a | | H.cs:153:32:153:32 | o : Object | semmle.label | o : Object | -| H.cs:155:17:155:23 | object creation of type B : B | semmle.label | object creation of type B : B | +| H.cs:153:32:153:32 | o : Object | semmle.label | o : Object | +| H.cs:155:17:155:30 | call to method Source : B | semmle.label | call to method Source : B | +| H.cs:155:17:155:30 | call to method Source : B | semmle.label | call to method Source : B | +| H.cs:156:9:156:9 | [post] access to local variable b [field FieldB] : Object | semmle.label | [post] access to local variable b [field FieldB] : Object | | H.cs:156:9:156:9 | [post] access to local variable b [field FieldB] : Object | semmle.label | [post] access to local variable b [field FieldB] : Object | | H.cs:156:9:156:9 | access to local variable b : B | semmle.label | access to local variable b : B | +| H.cs:156:9:156:9 | access to local variable b : B | semmle.label | access to local variable b : B | +| H.cs:156:20:156:20 | access to parameter o : Object | semmle.label | access to parameter o : Object | | H.cs:156:20:156:20 | access to parameter o : Object | semmle.label | access to parameter o : Object | | H.cs:157:9:157:9 | [post] access to parameter a [field FieldA, field FieldB] : Object | semmle.label | [post] access to parameter a [field FieldA, field FieldB] : Object | +| H.cs:157:9:157:9 | [post] access to parameter a [field FieldA, field FieldB] : Object | semmle.label | [post] access to parameter a [field FieldA, field FieldB] : Object | +| H.cs:157:9:157:9 | [post] access to parameter a [field FieldA] : B | semmle.label | [post] access to parameter a [field FieldA] : B | | H.cs:157:9:157:9 | [post] access to parameter a [field FieldA] : B | semmle.label | [post] access to parameter a [field FieldA] : B | | H.cs:157:20:157:20 | access to local variable b : B | semmle.label | access to local variable b : B | +| H.cs:157:20:157:20 | access to local variable b : B | semmle.label | access to local variable b : B | | H.cs:157:20:157:20 | access to local variable b [field FieldB] : Object | semmle.label | access to local variable b [field FieldB] : Object | -| H.cs:163:17:163:28 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| H.cs:157:20:157:20 | access to local variable b [field FieldB] : Object | semmle.label | access to local variable b [field FieldB] : Object | +| H.cs:163:17:163:35 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:163:17:163:35 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:164:19:164:19 | [post] access to local variable a [field FieldA, field FieldB] : Object | semmle.label | [post] access to local variable a [field FieldA, field FieldB] : Object | | H.cs:164:19:164:19 | [post] access to local variable a [field FieldA, field FieldB] : Object | semmle.label | [post] access to local variable a [field FieldA, field FieldB] : Object | | H.cs:164:19:164:19 | [post] access to local variable a [field FieldA] : B | semmle.label | [post] access to local variable a [field FieldA] : B | +| H.cs:164:19:164:19 | [post] access to local variable a [field FieldA] : B | semmle.label | [post] access to local variable a [field FieldA] : B | | H.cs:164:22:164:22 | access to local variable o : Object | semmle.label | access to local variable o : Object | -| H.cs:165:17:165:28 | (...) ... : B | semmle.label | (...) ... : B | -| H.cs:165:17:165:28 | (...) ... [field FieldB] : Object | semmle.label | (...) ... [field FieldB] : Object | -| H.cs:165:21:165:21 | access to local variable a [field FieldA, field FieldB] : Object | semmle.label | access to local variable a [field FieldA, field FieldB] : Object | -| H.cs:165:21:165:21 | access to local variable a [field FieldA] : B | semmle.label | access to local variable a [field FieldA] : B | -| H.cs:165:21:165:28 | access to field FieldA : B | semmle.label | access to field FieldA : B | -| H.cs:165:21:165:28 | access to field FieldA [field FieldB] : Object | semmle.label | access to field FieldA [field FieldB] : Object | +| H.cs:164:22:164:22 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| H.cs:165:17:165:27 | (...) ... : B | semmle.label | (...) ... : B | +| H.cs:165:17:165:27 | (...) ... : B | semmle.label | (...) ... : B | +| H.cs:165:17:165:27 | (...) ... [field FieldB] : Object | semmle.label | (...) ... [field FieldB] : Object | +| H.cs:165:17:165:27 | (...) ... [field FieldB] : Object | semmle.label | (...) ... [field FieldB] : Object | +| H.cs:165:20:165:20 | access to local variable a [field FieldA, field FieldB] : Object | semmle.label | access to local variable a [field FieldA, field FieldB] : Object | +| H.cs:165:20:165:20 | access to local variable a [field FieldA, field FieldB] : Object | semmle.label | access to local variable a [field FieldA, field FieldB] : Object | +| H.cs:165:20:165:20 | access to local variable a [field FieldA] : B | semmle.label | access to local variable a [field FieldA] : B | +| H.cs:165:20:165:20 | access to local variable a [field FieldA] : B | semmle.label | access to local variable a [field FieldA] : B | +| H.cs:165:20:165:27 | access to field FieldA : B | semmle.label | access to field FieldA : B | +| H.cs:165:20:165:27 | access to field FieldA : B | semmle.label | access to field FieldA : B | +| H.cs:165:20:165:27 | access to field FieldA [field FieldB] : Object | semmle.label | access to field FieldA [field FieldB] : Object | +| H.cs:165:20:165:27 | access to field FieldA [field FieldB] : Object | semmle.label | access to field FieldA [field FieldB] : Object | +| H.cs:166:14:166:14 | access to local variable b | semmle.label | access to local variable b | | H.cs:166:14:166:14 | access to local variable b | semmle.label | access to local variable b | | H.cs:167:14:167:14 | access to local variable b [field FieldB] : Object | semmle.label | access to local variable b [field FieldB] : Object | +| H.cs:167:14:167:14 | access to local variable b [field FieldB] : Object | semmle.label | access to local variable b [field FieldB] : Object | +| H.cs:167:14:167:21 | access to field FieldB | semmle.label | access to field FieldB | | H.cs:167:14:167:21 | access to field FieldB | semmle.label | access to field FieldB | | I.cs:7:9:7:14 | [post] this access [field Field1] : Object | semmle.label | [post] this access [field Field1] : Object | -| I.cs:7:18:7:29 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | -| I.cs:13:17:13:28 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| I.cs:7:9:7:14 | [post] this access [field Field1] : Object | semmle.label | [post] this access [field Field1] : Object | +| I.cs:7:18:7:34 | call to method Source : Object | semmle.label | call to method Source : Object | +| I.cs:7:18:7:34 | call to method Source : Object | semmle.label | call to method Source : Object | +| I.cs:13:17:13:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| I.cs:13:17:13:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| I.cs:15:9:15:9 | [post] access to local variable i [field Field1] : Object | semmle.label | [post] access to local variable i [field Field1] : Object | | I.cs:15:9:15:9 | [post] access to local variable i [field Field1] : Object | semmle.label | [post] access to local variable i [field Field1] : Object | | I.cs:15:20:15:20 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| I.cs:15:20:15:20 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| I.cs:16:9:16:9 | access to local variable i [field Field1] : Object | semmle.label | access to local variable i [field Field1] : Object | | I.cs:16:9:16:9 | access to local variable i [field Field1] : Object | semmle.label | access to local variable i [field Field1] : Object | | I.cs:17:9:17:9 | access to local variable i [field Field1] : Object | semmle.label | access to local variable i [field Field1] : Object | +| I.cs:17:9:17:9 | access to local variable i [field Field1] : Object | semmle.label | access to local variable i [field Field1] : Object | +| I.cs:18:14:18:14 | access to local variable i [field Field1] : Object | semmle.label | access to local variable i [field Field1] : Object | | I.cs:18:14:18:14 | access to local variable i [field Field1] : Object | semmle.label | access to local variable i [field Field1] : Object | | I.cs:18:14:18:21 | access to field Field1 | semmle.label | access to field Field1 | +| I.cs:18:14:18:21 | access to field Field1 | semmle.label | access to field Field1 | +| I.cs:21:13:21:19 | object creation of type I [field Field1] : Object | semmle.label | object creation of type I [field Field1] : Object | | I.cs:21:13:21:19 | object creation of type I [field Field1] : Object | semmle.label | object creation of type I [field Field1] : Object | | I.cs:22:9:22:9 | access to local variable i [field Field1] : Object | semmle.label | access to local variable i [field Field1] : Object | +| I.cs:22:9:22:9 | access to local variable i [field Field1] : Object | semmle.label | access to local variable i [field Field1] : Object | +| I.cs:23:14:23:14 | access to local variable i [field Field1] : Object | semmle.label | access to local variable i [field Field1] : Object | | I.cs:23:14:23:14 | access to local variable i [field Field1] : Object | semmle.label | access to local variable i [field Field1] : Object | | I.cs:23:14:23:21 | access to field Field1 | semmle.label | access to field Field1 | +| I.cs:23:14:23:21 | access to field Field1 | semmle.label | access to field Field1 | +| I.cs:26:13:26:37 | [pre-initializer] object creation of type I [field Field1] : Object | semmle.label | [pre-initializer] object creation of type I [field Field1] : Object | | I.cs:26:13:26:37 | [pre-initializer] object creation of type I [field Field1] : Object | semmle.label | [pre-initializer] object creation of type I [field Field1] : Object | | I.cs:27:14:27:14 | access to local variable i [field Field1] : Object | semmle.label | access to local variable i [field Field1] : Object | +| I.cs:27:14:27:14 | access to local variable i [field Field1] : Object | semmle.label | access to local variable i [field Field1] : Object | | I.cs:27:14:27:21 | access to field Field1 | semmle.label | access to field Field1 | -| I.cs:31:13:31:24 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| I.cs:27:14:27:21 | access to field Field1 | semmle.label | access to field Field1 | +| I.cs:31:13:31:29 | call to method Source : Object | semmle.label | call to method Source : Object | +| I.cs:31:13:31:29 | call to method Source : Object | semmle.label | call to method Source : Object | +| I.cs:32:9:32:9 | [post] access to local variable i [field Field1] : Object | semmle.label | [post] access to local variable i [field Field1] : Object | | I.cs:32:9:32:9 | [post] access to local variable i [field Field1] : Object | semmle.label | [post] access to local variable i [field Field1] : Object | | I.cs:32:20:32:20 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| I.cs:32:20:32:20 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| I.cs:33:9:33:9 | access to local variable i [field Field1] : Object | semmle.label | access to local variable i [field Field1] : Object | | I.cs:33:9:33:9 | access to local variable i [field Field1] : Object | semmle.label | access to local variable i [field Field1] : Object | | I.cs:34:12:34:12 | access to local variable i [field Field1] : Object | semmle.label | access to local variable i [field Field1] : Object | +| I.cs:34:12:34:12 | access to local variable i [field Field1] : Object | semmle.label | access to local variable i [field Field1] : Object | +| I.cs:37:23:37:23 | i [field Field1] : Object | semmle.label | i [field Field1] : Object | | I.cs:37:23:37:23 | i [field Field1] : Object | semmle.label | i [field Field1] : Object | | I.cs:39:9:39:9 | access to parameter i [field Field1] : Object | semmle.label | access to parameter i [field Field1] : Object | +| I.cs:39:9:39:9 | access to parameter i [field Field1] : Object | semmle.label | access to parameter i [field Field1] : Object | +| I.cs:40:14:40:14 | access to parameter i [field Field1] : Object | semmle.label | access to parameter i [field Field1] : Object | | I.cs:40:14:40:14 | access to parameter i [field Field1] : Object | semmle.label | access to parameter i [field Field1] : Object | | I.cs:40:14:40:21 | access to field Field1 | semmle.label | access to field Field1 | -| J.cs:12:17:12:28 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| I.cs:40:14:40:21 | access to field Field1 | semmle.label | access to field Field1 | +| J.cs:12:17:12:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| J.cs:12:17:12:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| J.cs:13:18:13:36 | object creation of type Record [property Prop1] : Object | semmle.label | object creation of type Record [property Prop1] : Object | | J.cs:13:18:13:36 | object creation of type Record [property Prop1] : Object | semmle.label | object creation of type Record [property Prop1] : Object | | J.cs:13:29:13:29 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| J.cs:13:29:13:29 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| J.cs:14:14:14:15 | access to local variable r1 [property Prop1] : Object | semmle.label | access to local variable r1 [property Prop1] : Object | | J.cs:14:14:14:15 | access to local variable r1 [property Prop1] : Object | semmle.label | access to local variable r1 [property Prop1] : Object | | J.cs:14:14:14:21 | access to property Prop1 | semmle.label | access to property Prop1 | +| J.cs:14:14:14:21 | access to property Prop1 | semmle.label | access to property Prop1 | +| J.cs:18:14:18:15 | access to local variable r2 [property Prop1] : Object | semmle.label | access to local variable r2 [property Prop1] : Object | | J.cs:18:14:18:15 | access to local variable r2 [property Prop1] : Object | semmle.label | access to local variable r2 [property Prop1] : Object | | J.cs:18:14:18:21 | access to property Prop1 | semmle.label | access to property Prop1 | -| J.cs:21:18:21:38 | ... with { ... } [property Prop2] : Object | semmle.label | ... with { ... } [property Prop2] : Object | -| J.cs:21:36:21:36 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| J.cs:18:14:18:21 | access to property Prop1 | semmle.label | access to property Prop1 | +| J.cs:21:18:21:54 | ... with { ... } [property Prop2] : Object | semmle.label | ... with { ... } [property Prop2] : Object | +| J.cs:21:18:21:54 | ... with { ... } [property Prop2] : Object | semmle.label | ... with { ... } [property Prop2] : Object | +| J.cs:21:36:21:52 | call to method Source : Object | semmle.label | call to method Source : Object | +| J.cs:21:36:21:52 | call to method Source : Object | semmle.label | call to method Source : Object | +| J.cs:22:14:22:15 | access to local variable r3 [property Prop1] : Object | semmle.label | access to local variable r3 [property Prop1] : Object | | J.cs:22:14:22:15 | access to local variable r3 [property Prop1] : Object | semmle.label | access to local variable r3 [property Prop1] : Object | | J.cs:22:14:22:21 | access to property Prop1 | semmle.label | access to property Prop1 | +| J.cs:22:14:22:21 | access to property Prop1 | semmle.label | access to property Prop1 | | J.cs:23:14:23:15 | access to local variable r3 [property Prop2] : Object | semmle.label | access to local variable r3 [property Prop2] : Object | +| J.cs:23:14:23:15 | access to local variable r3 [property Prop2] : Object | semmle.label | access to local variable r3 [property Prop2] : Object | +| J.cs:23:14:23:21 | access to property Prop2 | semmle.label | access to property Prop2 | | J.cs:23:14:23:21 | access to property Prop2 | semmle.label | access to property Prop2 | subpaths | A.cs:6:24:6:24 | access to local variable c : C | A.cs:147:32:147:32 | c : C | A.cs:149:20:149:27 | object creation of type B [field c] : C | A.cs:6:17:6:25 | call to method Make [field c] : C | -| A.cs:13:15:13:22 | object creation of type C1 : C1 | A.cs:145:27:145:27 | c : C1 | A.cs:145:32:145:35 | [post] this access [field c] : C1 | A.cs:13:9:13:9 | [post] access to local variable b [field c] : C1 | +| A.cs:6:24:6:24 | access to local variable c : C | A.cs:147:32:147:32 | c : C | A.cs:149:20:149:27 | object creation of type B [field c] : C | A.cs:6:17:6:25 | call to method Make [field c] : C | +| A.cs:13:15:13:29 | call to method Source : C1 | A.cs:145:27:145:27 | c : C1 | A.cs:145:32:145:35 | [post] this access [field c] : C1 | A.cs:13:9:13:9 | [post] access to local variable b [field c] : C1 | +| A.cs:13:15:13:29 | call to method Source : C1 | A.cs:145:27:145:27 | c : C1 | A.cs:145:32:145:35 | [post] this access [field c] : C1 | A.cs:13:9:13:9 | [post] access to local variable b [field c] : C1 | | A.cs:14:14:14:14 | access to local variable b [field c] : C1 | A.cs:146:18:146:20 | this [field c] : C1 | A.cs:146:33:146:38 | access to field c : C1 | A.cs:14:14:14:20 | call to method Get : C1 | -| A.cs:15:15:15:28 | object creation of type B [field c] : C | A.cs:146:18:146:20 | this [field c] : C | A.cs:146:33:146:38 | access to field c : C | A.cs:15:14:15:35 | call to method Get : C | -| A.cs:15:21:15:27 | object creation of type C : C | A.cs:141:20:141:20 | c : C | A.cs:143:13:143:16 | [post] this access [field c] : C | A.cs:15:15:15:28 | object creation of type B [field c] : C | -| A.cs:22:25:22:32 | object creation of type C2 : C2 | A.cs:42:29:42:29 | c : C2 | A.cs:48:20:48:21 | access to local variable b2 [field c] : C2 | A.cs:22:14:22:33 | call to method SetOnB [field c] : C2 | -| A.cs:31:29:31:36 | object creation of type C2 : C2 | A.cs:36:33:36:33 | c : C2 | A.cs:39:16:39:28 | ... ? ... : ... [field c] : C2 | A.cs:31:14:31:37 | call to method SetOnBWrap [field c] : C2 | +| A.cs:14:14:14:14 | access to local variable b [field c] : C1 | A.cs:146:18:146:20 | this [field c] : C1 | A.cs:146:33:146:38 | access to field c : C1 | A.cs:14:14:14:20 | call to method Get : C1 | +| A.cs:15:15:15:35 | object creation of type B [field c] : C | A.cs:146:18:146:20 | this [field c] : C | A.cs:146:33:146:38 | access to field c : C | A.cs:15:14:15:42 | call to method Get : C | +| A.cs:15:15:15:35 | object creation of type B [field c] : C | A.cs:146:18:146:20 | this [field c] : C | A.cs:146:33:146:38 | access to field c : C | A.cs:15:14:15:42 | call to method Get : C | +| A.cs:15:21:15:34 | call to method Source : C | A.cs:141:20:141:20 | c : C | A.cs:143:13:143:16 | [post] this access [field c] : C | A.cs:15:15:15:35 | object creation of type B [field c] : C | +| A.cs:15:21:15:34 | call to method Source : C | A.cs:141:20:141:20 | c : C | A.cs:143:13:143:16 | [post] this access [field c] : C | A.cs:15:15:15:35 | object creation of type B [field c] : C | +| A.cs:22:25:22:37 | call to method Source : C2 | A.cs:42:29:42:29 | c : C2 | A.cs:48:20:48:21 | access to local variable b2 [field c] : C2 | A.cs:22:14:22:38 | call to method SetOnB [field c] : C2 | +| A.cs:22:25:22:37 | call to method Source : C2 | A.cs:42:29:42:29 | c : C2 | A.cs:48:20:48:21 | access to local variable b2 [field c] : C2 | A.cs:22:14:22:38 | call to method SetOnB [field c] : C2 | +| A.cs:31:29:31:41 | call to method Source : C2 | A.cs:36:33:36:33 | c : C2 | A.cs:39:16:39:28 | ... ? ... : ... [field c] : C2 | A.cs:31:14:31:42 | call to method SetOnBWrap [field c] : C2 | +| A.cs:31:29:31:41 | call to method Source : C2 | A.cs:36:33:36:33 | c : C2 | A.cs:39:16:39:28 | ... ? ... : ... [field c] : C2 | A.cs:31:14:31:42 | call to method SetOnBWrap [field c] : C2 | +| A.cs:38:29:38:29 | access to parameter c : C2 | A.cs:42:29:42:29 | c : C2 | A.cs:48:20:48:21 | access to local variable b2 [field c] : C2 | A.cs:38:18:38:30 | call to method SetOnB [field c] : C2 | | A.cs:38:29:38:29 | access to parameter c : C2 | A.cs:42:29:42:29 | c : C2 | A.cs:48:20:48:21 | access to local variable b2 [field c] : C2 | A.cs:38:18:38:30 | call to method SetOnB [field c] : C2 | | A.cs:47:20:47:20 | access to parameter c : C2 | A.cs:145:27:145:27 | c : C2 | A.cs:145:32:145:35 | [post] this access [field c] : C2 | A.cs:47:13:47:14 | [post] access to local variable b2 [field c] : C2 | -| A.cs:83:15:83:21 | object creation of type C : C | A.cs:145:27:145:27 | c : C | A.cs:145:32:145:35 | [post] this access [field c] : C | A.cs:83:9:83:9 | [post] access to parameter b [field c] : C | +| A.cs:47:20:47:20 | access to parameter c : C2 | A.cs:145:27:145:27 | c : C2 | A.cs:145:32:145:35 | [post] this access [field c] : C2 | A.cs:47:13:47:14 | [post] access to local variable b2 [field c] : C2 | +| A.cs:83:15:83:26 | call to method Source : C | A.cs:145:27:145:27 | c : C | A.cs:145:32:145:35 | [post] this access [field c] : C | A.cs:83:9:83:9 | [post] access to parameter b [field c] : C | +| A.cs:83:15:83:26 | call to method Source : C | A.cs:145:27:145:27 | c : C | A.cs:145:32:145:35 | [post] this access [field c] : C | A.cs:83:9:83:9 | [post] access to parameter b [field c] : C | +| A.cs:105:23:105:23 | access to local variable b : B | A.cs:95:20:95:20 | b : B | A.cs:98:13:98:16 | [post] this access [field b] : B | A.cs:105:17:105:29 | object creation of type D [field b] : B | | A.cs:105:23:105:23 | access to local variable b : B | A.cs:95:20:95:20 | b : B | A.cs:98:13:98:16 | [post] this access [field b] : B | A.cs:105:17:105:29 | object creation of type D [field b] : B | | A.cs:114:29:114:29 | access to local variable b : B | A.cs:157:25:157:28 | head : B | A.cs:159:13:159:16 | [post] this access [field head] : B | A.cs:114:18:114:54 | object creation of type MyList [field head] : B | +| A.cs:114:29:114:29 | access to local variable b : B | A.cs:157:25:157:28 | head : B | A.cs:159:13:159:16 | [post] this access [field head] : B | A.cs:114:18:114:54 | object creation of type MyList [field head] : B | +| A.cs:115:35:115:36 | access to local variable l1 [field head] : B | A.cs:157:38:157:41 | next [field head] : B | A.cs:160:13:160:16 | [post] this access [field next, field head] : B | A.cs:115:18:115:37 | object creation of type MyList [field next, field head] : B | | A.cs:115:35:115:36 | access to local variable l1 [field head] : B | A.cs:157:38:157:41 | next [field head] : B | A.cs:160:13:160:16 | [post] this access [field next, field head] : B | A.cs:115:18:115:37 | object creation of type MyList [field next, field head] : B | | A.cs:116:35:116:36 | access to local variable l2 [field next, field head] : B | A.cs:157:38:157:41 | next [field next, field head] : B | A.cs:160:13:160:16 | [post] this access [field next, field next, field head] : B | A.cs:116:18:116:37 | object creation of type MyList [field next, field next, field head] : B | +| A.cs:116:35:116:36 | access to local variable l2 [field next, field head] : B | A.cs:157:38:157:41 | next [field next, field head] : B | A.cs:160:13:160:16 | [post] this access [field next, field next, field head] : B | A.cs:116:18:116:37 | object creation of type MyList [field next, field next, field head] : B | +| A.cs:149:26:149:26 | access to parameter c : C | A.cs:141:20:141:20 | c : C | A.cs:143:13:143:16 | [post] this access [field c] : C | A.cs:149:20:149:27 | object creation of type B [field c] : C | | A.cs:149:26:149:26 | access to parameter c : C | A.cs:141:20:141:20 | c : C | A.cs:143:13:143:16 | [post] this access [field c] : C | A.cs:149:20:149:27 | object creation of type B [field c] : C | | B.cs:6:27:6:27 | access to local variable e : Elem | B.cs:29:26:29:27 | e1 : Elem | B.cs:31:13:31:16 | [post] this access [field elem1] : Elem | B.cs:6:18:6:34 | object creation of type Box1 [field elem1] : Elem | +| B.cs:6:27:6:27 | access to local variable e : Elem | B.cs:29:26:29:27 | e1 : Elem | B.cs:31:13:31:16 | [post] this access [field elem1] : Elem | B.cs:6:18:6:34 | object creation of type Box1 [field elem1] : Elem | +| B.cs:7:27:7:28 | access to local variable b1 [field elem1] : Elem | B.cs:39:26:39:27 | b1 [field elem1] : Elem | B.cs:41:13:41:16 | [post] this access [field box1, field elem1] : Elem | B.cs:7:18:7:29 | object creation of type Box2 [field box1, field elem1] : Elem | | B.cs:7:27:7:28 | access to local variable b1 [field elem1] : Elem | B.cs:39:26:39:27 | b1 [field elem1] : Elem | B.cs:41:13:41:16 | [post] this access [field box1, field elem1] : Elem | B.cs:7:18:7:29 | object creation of type Box2 [field box1, field elem1] : Elem | | B.cs:15:33:15:33 | access to local variable e : Elem | B.cs:29:35:29:36 | e2 : Elem | B.cs:32:13:32:16 | [post] this access [field elem2] : Elem | B.cs:15:18:15:34 | object creation of type Box1 [field elem2] : Elem | +| B.cs:15:33:15:33 | access to local variable e : Elem | B.cs:29:35:29:36 | e2 : Elem | B.cs:32:13:32:16 | [post] this access [field elem2] : Elem | B.cs:15:18:15:34 | object creation of type Box1 [field elem2] : Elem | +| B.cs:16:27:16:28 | access to local variable b1 [field elem2] : Elem | B.cs:39:26:39:27 | b1 [field elem2] : Elem | B.cs:41:13:41:16 | [post] this access [field box1, field elem2] : Elem | B.cs:16:18:16:29 | object creation of type Box2 [field box1, field elem2] : Elem | | B.cs:16:27:16:28 | access to local variable b1 [field elem2] : Elem | B.cs:39:26:39:27 | b1 [field elem2] : Elem | B.cs:41:13:41:16 | [post] this access [field box1, field elem2] : Elem | B.cs:16:18:16:29 | object creation of type Box2 [field box1, field elem2] : Elem | | D.cs:15:34:15:38 | access to parameter value : Object | D.cs:9:9:9:11 | value : Object | D.cs:9:15:9:18 | [post] this access [field trivialPropField] : Object | D.cs:15:15:15:18 | [post] this access [field trivialPropField] : Object | +| D.cs:15:34:15:38 | access to parameter value : Object | D.cs:9:9:9:11 | value : Object | D.cs:9:15:9:18 | [post] this access [field trivialPropField] : Object | D.cs:15:15:15:18 | [post] this access [field trivialPropField] : Object | +| D.cs:22:27:22:28 | access to parameter o2 : Object | D.cs:9:9:9:11 | value : Object | D.cs:9:15:9:18 | [post] this access [field trivialPropField] : Object | D.cs:22:9:22:11 | [post] access to local variable ret [field trivialPropField] : Object | | D.cs:22:27:22:28 | access to parameter o2 : Object | D.cs:9:9:9:11 | value : Object | D.cs:9:15:9:18 | [post] this access [field trivialPropField] : Object | D.cs:22:9:22:11 | [post] access to local variable ret [field trivialPropField] : Object | | D.cs:23:27:23:28 | access to parameter o3 : Object | D.cs:15:9:15:11 | value : Object | D.cs:15:15:15:18 | [post] this access [field trivialPropField] : Object | D.cs:23:9:23:11 | [post] access to local variable ret [field trivialPropField] : Object | +| D.cs:23:27:23:28 | access to parameter o3 : Object | D.cs:15:9:15:11 | value : Object | D.cs:15:15:15:18 | [post] this access [field trivialPropField] : Object | D.cs:23:9:23:11 | [post] access to local variable ret [field trivialPropField] : Object | | D.cs:31:24:31:24 | access to local variable o : Object | D.cs:18:28:18:29 | o1 : Object | D.cs:24:16:24:18 | access to local variable ret [property AutoProp] : Object | D.cs:31:17:31:37 | call to method Create [property AutoProp] : Object | -| D.cs:37:26:37:26 | access to local variable o : Object | D.cs:18:39:18:40 | o2 : Object | D.cs:24:16:24:18 | access to local variable ret [field trivialPropField] : Object | D.cs:37:13:37:33 | call to method Create [field trivialPropField] : Object | +| D.cs:31:24:31:24 | access to local variable o : Object | D.cs:18:28:18:29 | o1 : Object | D.cs:24:16:24:18 | access to local variable ret [property AutoProp] : Object | D.cs:31:17:31:37 | call to method Create [property AutoProp] : Object | +| D.cs:37:26:37:42 | call to method Source : Object | D.cs:18:39:18:40 | o2 : Object | D.cs:24:16:24:18 | access to local variable ret [field trivialPropField] : Object | D.cs:37:13:37:49 | call to method Create [field trivialPropField] : Object | +| D.cs:37:26:37:42 | call to method Source : Object | D.cs:18:39:18:40 | o2 : Object | D.cs:24:16:24:18 | access to local variable ret [field trivialPropField] : Object | D.cs:37:13:37:49 | call to method Create [field trivialPropField] : Object | +| D.cs:39:14:39:14 | access to local variable d [field trivialPropField] : Object | D.cs:8:9:8:11 | this [field trivialPropField] : Object | D.cs:8:22:8:42 | access to field trivialPropField : Object | D.cs:39:14:39:26 | access to property TrivialProp : Object | | D.cs:39:14:39:14 | access to local variable d [field trivialPropField] : Object | D.cs:8:9:8:11 | this [field trivialPropField] : Object | D.cs:8:22:8:42 | access to field trivialPropField : Object | D.cs:39:14:39:26 | access to property TrivialProp : Object | | D.cs:41:14:41:14 | access to local variable d [field trivialPropField] : Object | D.cs:14:9:14:11 | this [field trivialPropField] : Object | D.cs:14:22:14:42 | access to field trivialPropField : Object | D.cs:41:14:41:26 | access to property ComplexProp : Object | -| D.cs:43:32:43:32 | access to local variable o : Object | D.cs:18:50:18:51 | o3 : Object | D.cs:24:16:24:18 | access to local variable ret [field trivialPropField] : Object | D.cs:43:13:43:33 | call to method Create [field trivialPropField] : Object | +| D.cs:41:14:41:14 | access to local variable d [field trivialPropField] : Object | D.cs:14:9:14:11 | this [field trivialPropField] : Object | D.cs:14:22:14:42 | access to field trivialPropField : Object | D.cs:41:14:41:26 | access to property ComplexProp : Object | +| D.cs:43:32:43:48 | call to method Source : Object | D.cs:18:50:18:51 | o3 : Object | D.cs:24:16:24:18 | access to local variable ret [field trivialPropField] : Object | D.cs:43:13:43:49 | call to method Create [field trivialPropField] : Object | +| D.cs:43:32:43:48 | call to method Source : Object | D.cs:18:50:18:51 | o3 : Object | D.cs:24:16:24:18 | access to local variable ret [field trivialPropField] : Object | D.cs:43:13:43:49 | call to method Create [field trivialPropField] : Object | +| D.cs:45:14:45:14 | access to local variable d [field trivialPropField] : Object | D.cs:8:9:8:11 | this [field trivialPropField] : Object | D.cs:8:22:8:42 | access to field trivialPropField : Object | D.cs:45:14:45:26 | access to property TrivialProp : Object | | D.cs:45:14:45:14 | access to local variable d [field trivialPropField] : Object | D.cs:8:9:8:11 | this [field trivialPropField] : Object | D.cs:8:22:8:42 | access to field trivialPropField : Object | D.cs:45:14:45:26 | access to property TrivialProp : Object | | D.cs:47:14:47:14 | access to local variable d [field trivialPropField] : Object | D.cs:14:9:14:11 | this [field trivialPropField] : Object | D.cs:14:22:14:42 | access to field trivialPropField : Object | D.cs:47:14:47:26 | access to property ComplexProp : Object | +| D.cs:47:14:47:14 | access to local variable d [field trivialPropField] : Object | D.cs:14:9:14:11 | this [field trivialPropField] : Object | D.cs:14:22:14:42 | access to field trivialPropField : Object | D.cs:47:14:47:26 | access to property ComplexProp : Object | +| E.cs:23:25:23:25 | access to local variable o : Object | E.cs:8:29:8:29 | o : Object | E.cs:12:16:12:18 | access to local variable ret [field Field] : Object | E.cs:23:17:23:26 | call to method CreateS [field Field] : Object | | E.cs:23:25:23:25 | access to local variable o : Object | E.cs:8:29:8:29 | o : Object | E.cs:12:16:12:18 | access to local variable ret [field Field] : Object | E.cs:23:17:23:26 | call to method CreateS [field Field] : Object | | F.cs:11:24:11:24 | access to local variable o : Object | F.cs:6:28:6:29 | o1 : Object | F.cs:6:46:6:81 | object creation of type F [field Field1] : Object | F.cs:11:17:11:31 | call to method Create [field Field1] : Object | -| F.cs:15:26:15:26 | access to local variable o : Object | F.cs:6:39:6:40 | o2 : Object | F.cs:6:46:6:81 | object creation of type F [field Field2] : Object | F.cs:15:13:15:27 | call to method Create [field Field2] : Object | +| F.cs:11:24:11:24 | access to local variable o : Object | F.cs:6:28:6:29 | o1 : Object | F.cs:6:46:6:81 | object creation of type F [field Field1] : Object | F.cs:11:17:11:31 | call to method Create [field Field1] : Object | +| F.cs:15:26:15:42 | call to method Source : Object | F.cs:6:39:6:40 | o2 : Object | F.cs:6:46:6:81 | object creation of type F [field Field2] : Object | F.cs:15:13:15:43 | call to method Create [field Field2] : Object | +| F.cs:15:26:15:42 | call to method Source : Object | F.cs:6:39:6:40 | o2 : Object | F.cs:6:46:6:81 | object creation of type F [field Field2] : Object | F.cs:15:13:15:43 | call to method Create [field Field2] : Object | +| G.cs:17:24:17:24 | access to local variable e : Elem | G.cs:64:34:64:34 | e : Elem | G.cs:64:39:64:42 | [post] this access [field Elem] : Elem | G.cs:17:9:17:14 | [post] access to field Box1 [field Elem] : Elem | | G.cs:17:24:17:24 | access to local variable e : Elem | G.cs:64:34:64:34 | e : Elem | G.cs:64:39:64:42 | [post] this access [field Elem] : Elem | G.cs:17:9:17:14 | [post] access to field Box1 [field Elem] : Elem | | G.cs:33:29:33:29 | access to local variable e : Elem | G.cs:64:34:64:34 | e : Elem | G.cs:64:39:64:42 | [post] this access [field Elem] : Elem | G.cs:33:9:33:19 | [post] call to method GetBox1 [field Elem] : Elem | +| G.cs:33:29:33:29 | access to local variable e : Elem | G.cs:64:34:64:34 | e : Elem | G.cs:64:39:64:42 | [post] this access [field Elem] : Elem | G.cs:33:9:33:19 | [post] call to method GetBox1 [field Elem] : Elem | +| G.cs:39:14:39:15 | access to parameter b2 [field Box1, field Elem] : Elem | G.cs:71:21:71:27 | this [field Box1, field Elem] : Elem | G.cs:71:34:71:37 | access to field Box1 [field Elem] : Elem | G.cs:39:14:39:25 | call to method GetBox1 [field Elem] : Elem | | G.cs:39:14:39:15 | access to parameter b2 [field Box1, field Elem] : Elem | G.cs:71:21:71:27 | this [field Box1, field Elem] : Elem | G.cs:71:34:71:37 | access to field Box1 [field Elem] : Elem | G.cs:39:14:39:25 | call to method GetBox1 [field Elem] : Elem | | G.cs:39:14:39:25 | call to method GetBox1 [field Elem] : Elem | G.cs:63:21:63:27 | this [field Elem] : Elem | G.cs:63:34:63:37 | access to field Elem : Elem | G.cs:39:14:39:35 | call to method GetElem : Elem | +| G.cs:39:14:39:25 | call to method GetBox1 [field Elem] : Elem | G.cs:63:21:63:27 | this [field Elem] : Elem | G.cs:63:34:63:37 | access to field Elem : Elem | G.cs:39:14:39:35 | call to method GetElem : Elem | +| H.cs:24:27:24:27 | access to local variable a [field FieldA] : Object | H.cs:13:15:13:15 | a [field FieldA] : Object | H.cs:17:16:17:18 | access to local variable ret [field FieldA] : Object | H.cs:24:21:24:28 | call to method Clone [field FieldA] : Object | | H.cs:24:27:24:27 | access to local variable a [field FieldA] : Object | H.cs:13:15:13:15 | a [field FieldA] : Object | H.cs:17:16:17:18 | access to local variable ret [field FieldA] : Object | H.cs:24:21:24:28 | call to method Clone [field FieldA] : Object | | H.cs:44:27:44:27 | access to local variable a [field FieldA] : Object | H.cs:33:19:33:19 | a [field FieldA] : Object | H.cs:37:16:37:16 | access to local variable b [field FieldB] : Object | H.cs:44:17:44:28 | call to method Transform [field FieldB] : Object | +| H.cs:44:27:44:27 | access to local variable a [field FieldA] : Object | H.cs:33:19:33:19 | a [field FieldA] : Object | H.cs:37:16:37:16 | access to local variable b [field FieldB] : Object | H.cs:44:17:44:28 | call to method Transform [field FieldB] : Object | +| H.cs:64:22:64:22 | access to local variable a [field FieldA] : Object | H.cs:53:25:53:25 | a [field FieldA] : Object | H.cs:55:9:55:10 | [post] access to parameter b1 [field FieldB] : Object | H.cs:64:25:64:26 | [post] access to local variable b1 [field FieldB] : Object | | H.cs:64:22:64:22 | access to local variable a [field FieldA] : Object | H.cs:53:25:53:25 | a [field FieldA] : Object | H.cs:55:9:55:10 | [post] access to parameter b1 [field FieldB] : Object | H.cs:64:25:64:26 | [post] access to local variable b1 [field FieldB] : Object | | H.cs:80:22:80:22 | access to parameter a [field FieldA] : Object | H.cs:53:25:53:25 | a [field FieldA] : Object | H.cs:55:9:55:10 | [post] access to parameter b1 [field FieldB] : Object | H.cs:80:25:80:26 | [post] access to parameter b1 [field FieldB] : Object | -| H.cs:88:20:88:31 | object creation of type Object : Object | H.cs:77:30:77:30 | o : Object | H.cs:79:9:79:9 | [post] access to parameter a [field FieldA] : Object | H.cs:88:17:88:17 | [post] access to local variable a [field FieldA] : Object | -| H.cs:88:20:88:31 | object creation of type Object : Object | H.cs:77:30:77:30 | o : Object | H.cs:80:25:80:26 | [post] access to parameter b1 [field FieldB] : Object | H.cs:88:34:88:35 | [post] access to local variable b1 [field FieldB] : Object | +| H.cs:80:22:80:22 | access to parameter a [field FieldA] : Object | H.cs:53:25:53:25 | a [field FieldA] : Object | H.cs:55:9:55:10 | [post] access to parameter b1 [field FieldB] : Object | H.cs:80:25:80:26 | [post] access to parameter b1 [field FieldB] : Object | +| H.cs:88:20:88:36 | call to method Source : Object | H.cs:77:30:77:30 | o : Object | H.cs:79:9:79:9 | [post] access to parameter a [field FieldA] : Object | H.cs:88:17:88:17 | [post] access to local variable a [field FieldA] : Object | +| H.cs:88:20:88:36 | call to method Source : Object | H.cs:77:30:77:30 | o : Object | H.cs:79:9:79:9 | [post] access to parameter a [field FieldA] : Object | H.cs:88:17:88:17 | [post] access to local variable a [field FieldA] : Object | +| H.cs:88:20:88:36 | call to method Source : Object | H.cs:77:30:77:30 | o : Object | H.cs:80:25:80:26 | [post] access to parameter b1 [field FieldB] : Object | H.cs:88:39:88:40 | [post] access to local variable b1 [field FieldB] : Object | +| H.cs:88:20:88:36 | call to method Source : Object | H.cs:77:30:77:30 | o : Object | H.cs:80:25:80:26 | [post] access to parameter b1 [field FieldB] : Object | H.cs:88:39:88:40 | [post] access to local variable b1 [field FieldB] : Object | +| H.cs:106:26:106:39 | (...) ... [field FieldA] : Object | H.cs:33:19:33:19 | a [field FieldA] : Object | H.cs:37:16:37:16 | access to local variable b [field FieldB] : Object | H.cs:106:16:106:40 | call to method Transform [field FieldB] : Object | | H.cs:106:26:106:39 | (...) ... [field FieldA] : Object | H.cs:33:19:33:19 | a [field FieldA] : Object | H.cs:37:16:37:16 | access to local variable b [field FieldB] : Object | H.cs:106:16:106:40 | call to method Transform [field FieldB] : Object | | H.cs:113:31:113:31 | access to local variable a [field FieldA] : Object | H.cs:102:23:102:23 | a [field FieldA] : Object | H.cs:106:16:106:40 | call to method Transform [field FieldB] : Object | H.cs:113:17:113:32 | call to method TransformWrap [field FieldB] : Object | +| H.cs:113:31:113:31 | access to local variable a [field FieldA] : Object | H.cs:102:23:102:23 | a [field FieldA] : Object | H.cs:106:16:106:40 | call to method Transform [field FieldB] : Object | H.cs:113:17:113:32 | call to method TransformWrap [field FieldB] : Object | +| H.cs:124:26:124:26 | access to parameter a [field FieldA] : Object | H.cs:33:19:33:19 | a [field FieldA] : Object | H.cs:37:16:37:16 | access to local variable b [field FieldB] : Object | H.cs:124:16:124:27 | call to method Transform [field FieldB] : Object | | H.cs:124:26:124:26 | access to parameter a [field FieldA] : Object | H.cs:33:19:33:19 | a [field FieldA] : Object | H.cs:37:16:37:16 | access to local variable b [field FieldB] : Object | H.cs:124:16:124:27 | call to method Transform [field FieldB] : Object | | H.cs:131:18:131:18 | access to local variable a [field FieldA] : Object | H.cs:122:18:122:18 | a [field FieldA] : Object | H.cs:124:16:124:34 | access to field FieldB : Object | H.cs:131:14:131:19 | call to method Get : Object | +| H.cs:131:18:131:18 | access to local variable a [field FieldA] : Object | H.cs:122:18:122:18 | a [field FieldA] : Object | H.cs:124:16:124:34 | access to field FieldB : Object | H.cs:131:14:131:19 | call to method Get : Object | | H.cs:142:26:142:26 | access to local variable a [field FieldA] : A | H.cs:33:19:33:19 | a [field FieldA] : A | H.cs:37:16:37:16 | access to local variable b [field FieldB] : A | H.cs:142:16:142:27 | call to method Transform [field FieldB] : A | -| H.cs:147:25:147:31 | object creation of type A : A | H.cs:138:27:138:27 | o : A | H.cs:142:16:142:34 | access to field FieldB : A | H.cs:147:17:147:32 | call to method Through : A | +| H.cs:142:26:142:26 | access to local variable a [field FieldA] : A | H.cs:33:19:33:19 | a [field FieldA] : A | H.cs:37:16:37:16 | access to local variable b [field FieldB] : A | H.cs:142:16:142:27 | call to method Transform [field FieldB] : A | +| H.cs:147:25:147:38 | call to method Source : A | H.cs:138:27:138:27 | o : A | H.cs:142:16:142:34 | access to field FieldB : A | H.cs:147:17:147:39 | call to method Through : A | +| H.cs:147:25:147:38 | call to method Source : A | H.cs:138:27:138:27 | o : A | H.cs:142:16:142:34 | access to field FieldB : A | H.cs:147:17:147:39 | call to method Through : A | +| H.cs:164:22:164:22 | access to local variable o : Object | H.cs:153:32:153:32 | o : Object | H.cs:157:9:157:9 | [post] access to parameter a [field FieldA, field FieldB] : Object | H.cs:164:19:164:19 | [post] access to local variable a [field FieldA, field FieldB] : Object | | H.cs:164:22:164:22 | access to local variable o : Object | H.cs:153:32:153:32 | o : Object | H.cs:157:9:157:9 | [post] access to parameter a [field FieldA, field FieldB] : Object | H.cs:164:19:164:19 | [post] access to local variable a [field FieldA, field FieldB] : Object | #select -| A.cs:7:14:7:16 | access to field c | A.cs:5:17:5:23 | object creation of type C : C | A.cs:7:14:7:16 | access to field c | $@ | A.cs:5:17:5:23 | object creation of type C : C | object creation of type C : C | -| A.cs:14:14:14:20 | call to method Get | A.cs:13:15:13:22 | object creation of type C1 : C1 | A.cs:14:14:14:20 | call to method Get | $@ | A.cs:13:15:13:22 | object creation of type C1 : C1 | object creation of type C1 : C1 | -| A.cs:15:14:15:35 | call to method Get | A.cs:15:21:15:27 | object creation of type C : C | A.cs:15:14:15:35 | call to method Get | $@ | A.cs:15:21:15:27 | object creation of type C : C | object creation of type C : C | -| A.cs:24:14:24:17 | access to field c | A.cs:22:25:22:32 | object creation of type C2 : C2 | A.cs:24:14:24:17 | access to field c | $@ | A.cs:22:25:22:32 | object creation of type C2 : C2 | object creation of type C2 : C2 | -| A.cs:33:14:33:17 | access to field c | A.cs:31:29:31:36 | object creation of type C2 : C2 | A.cs:33:14:33:17 | access to field c | $@ | A.cs:31:29:31:36 | object creation of type C2 : C2 | object creation of type C2 : C2 | -| A.cs:64:18:64:26 | access to field a | A.cs:55:17:55:23 | object creation of type A : A | A.cs:64:18:64:26 | access to field a | $@ | A.cs:55:17:55:23 | object creation of type A : A | object creation of type A : A | -| A.cs:89:14:89:16 | access to field c | A.cs:83:15:83:21 | object creation of type C : C | A.cs:89:14:89:16 | access to field c | $@ | A.cs:83:15:83:21 | object creation of type C : C | object creation of type C : C | -| A.cs:106:14:106:16 | access to field b | A.cs:98:30:98:36 | object creation of type B : B | A.cs:106:14:106:16 | access to field b | $@ | A.cs:98:30:98:36 | object creation of type B : B | object creation of type B : B | -| A.cs:106:14:106:16 | access to field b | A.cs:104:17:104:23 | object creation of type B : B | A.cs:106:14:106:16 | access to field b | $@ | A.cs:104:17:104:23 | object creation of type B : B | object creation of type B : B | -| A.cs:107:14:107:18 | access to field c | A.cs:97:19:97:25 | object creation of type C : C | A.cs:107:14:107:18 | access to field c | $@ | A.cs:97:19:97:25 | object creation of type C : C | object creation of type C : C | -| A.cs:108:14:108:16 | access to field c | A.cs:97:19:97:25 | object creation of type C : C | A.cs:108:14:108:16 | access to field c | $@ | A.cs:97:19:97:25 | object creation of type C : C | object creation of type C : C | -| A.cs:119:14:119:30 | access to field head | A.cs:113:17:113:23 | object creation of type B : B | A.cs:119:14:119:30 | access to field head | $@ | A.cs:113:17:113:23 | object creation of type B : B | object creation of type B : B | -| A.cs:123:18:123:23 | access to field head | A.cs:113:17:113:23 | object creation of type B : B | A.cs:123:18:123:23 | access to field head | $@ | A.cs:113:17:113:23 | object creation of type B : B | object creation of type B : B | -| B.cs:8:14:8:26 | access to field elem1 | B.cs:5:17:5:26 | object creation of type Elem : Elem | B.cs:8:14:8:26 | access to field elem1 | $@ | B.cs:5:17:5:26 | object creation of type Elem : Elem | object creation of type Elem : Elem | -| B.cs:18:14:18:26 | access to field elem2 | B.cs:14:17:14:26 | object creation of type Elem : Elem | B.cs:18:14:18:26 | access to field elem2 | $@ | B.cs:14:17:14:26 | object creation of type Elem : Elem | object creation of type Elem : Elem | -| C.cs:23:14:23:15 | access to field s1 | C.cs:3:23:3:32 | object creation of type Elem : Elem | C.cs:23:14:23:15 | access to field s1 | $@ | C.cs:3:23:3:32 | object creation of type Elem : Elem | object creation of type Elem : Elem | -| C.cs:24:14:24:15 | access to field s2 | C.cs:4:32:4:41 | object creation of type Elem : Elem | C.cs:24:14:24:15 | access to field s2 | $@ | C.cs:4:32:4:41 | object creation of type Elem : Elem | object creation of type Elem : Elem | -| C.cs:25:14:25:15 | access to field s3 | C.cs:18:19:18:28 | object creation of type Elem : Elem | C.cs:25:14:25:15 | access to field s3 | $@ | C.cs:18:19:18:28 | object creation of type Elem : Elem | object creation of type Elem : Elem | -| C.cs:26:14:26:15 | access to field s4 | C.cs:6:30:6:39 | object creation of type Elem : Elem | C.cs:26:14:26:15 | access to field s4 | $@ | C.cs:6:30:6:39 | object creation of type Elem : Elem | object creation of type Elem : Elem | -| C.cs:27:14:27:15 | access to property s5 | C.cs:7:37:7:46 | object creation of type Elem : Elem | C.cs:27:14:27:15 | access to property s5 | $@ | C.cs:7:37:7:46 | object creation of type Elem : Elem | object creation of type Elem : Elem | -| C.cs:28:14:28:15 | access to property s6 | C.cs:8:30:8:39 | object creation of type Elem : Elem | C.cs:28:14:28:15 | access to property s6 | $@ | C.cs:8:30:8:39 | object creation of type Elem : Elem | object creation of type Elem : Elem | -| D.cs:32:14:32:23 | access to property AutoProp | D.cs:29:17:29:28 | object creation of type Object : Object | D.cs:32:14:32:23 | access to property AutoProp | $@ | D.cs:29:17:29:28 | object creation of type Object : Object | object creation of type Object : Object | -| D.cs:39:14:39:26 | access to property TrivialProp | D.cs:29:17:29:28 | object creation of type Object : Object | D.cs:39:14:39:26 | access to property TrivialProp | $@ | D.cs:29:17:29:28 | object creation of type Object : Object | object creation of type Object : Object | -| D.cs:40:14:40:31 | access to field trivialPropField | D.cs:29:17:29:28 | object creation of type Object : Object | D.cs:40:14:40:31 | access to field trivialPropField | $@ | D.cs:29:17:29:28 | object creation of type Object : Object | object creation of type Object : Object | -| D.cs:41:14:41:26 | access to property ComplexProp | D.cs:29:17:29:28 | object creation of type Object : Object | D.cs:41:14:41:26 | access to property ComplexProp | $@ | D.cs:29:17:29:28 | object creation of type Object : Object | object creation of type Object : Object | -| D.cs:45:14:45:26 | access to property TrivialProp | D.cs:29:17:29:28 | object creation of type Object : Object | D.cs:45:14:45:26 | access to property TrivialProp | $@ | D.cs:29:17:29:28 | object creation of type Object : Object | object creation of type Object : Object | -| D.cs:46:14:46:31 | access to field trivialPropField | D.cs:29:17:29:28 | object creation of type Object : Object | D.cs:46:14:46:31 | access to field trivialPropField | $@ | D.cs:29:17:29:28 | object creation of type Object : Object | object creation of type Object : Object | -| D.cs:47:14:47:26 | access to property ComplexProp | D.cs:29:17:29:28 | object creation of type Object : Object | D.cs:47:14:47:26 | access to property ComplexProp | $@ | D.cs:29:17:29:28 | object creation of type Object : Object | object creation of type Object : Object | -| E.cs:24:14:24:20 | access to field Field | E.cs:22:17:22:28 | object creation of type Object : Object | E.cs:24:14:24:20 | access to field Field | $@ | E.cs:22:17:22:28 | object creation of type Object : Object | object creation of type Object : Object | -| F.cs:12:14:12:21 | access to field Field1 | F.cs:10:17:10:28 | object creation of type Object : Object | F.cs:12:14:12:21 | access to field Field1 | $@ | F.cs:10:17:10:28 | object creation of type Object : Object | object creation of type Object : Object | -| F.cs:17:14:17:21 | access to field Field2 | F.cs:10:17:10:28 | object creation of type Object : Object | F.cs:17:14:17:21 | access to field Field2 | $@ | F.cs:10:17:10:28 | object creation of type Object : Object | object creation of type Object : Object | -| F.cs:20:14:20:21 | access to field Field1 | F.cs:10:17:10:28 | object creation of type Object : Object | F.cs:20:14:20:21 | access to field Field1 | $@ | F.cs:10:17:10:28 | object creation of type Object : Object | object creation of type Object : Object | -| F.cs:25:14:25:21 | access to field Field2 | F.cs:10:17:10:28 | object creation of type Object : Object | F.cs:25:14:25:21 | access to field Field2 | $@ | F.cs:10:17:10:28 | object creation of type Object : Object | object creation of type Object : Object | -| G.cs:39:14:39:35 | call to method GetElem | G.cs:7:18:7:27 | object creation of type Elem : Elem | G.cs:39:14:39:35 | call to method GetElem | $@ | G.cs:7:18:7:27 | object creation of type Elem : Elem | object creation of type Elem : Elem | -| G.cs:39:14:39:35 | call to method GetElem | G.cs:15:18:15:27 | object creation of type Elem : Elem | G.cs:39:14:39:35 | call to method GetElem | $@ | G.cs:15:18:15:27 | object creation of type Elem : Elem | object creation of type Elem : Elem | -| G.cs:39:14:39:35 | call to method GetElem | G.cs:23:18:23:27 | object creation of type Elem : Elem | G.cs:39:14:39:35 | call to method GetElem | $@ | G.cs:23:18:23:27 | object creation of type Elem : Elem | object creation of type Elem : Elem | -| G.cs:39:14:39:35 | call to method GetElem | G.cs:31:18:31:27 | object creation of type Elem : Elem | G.cs:39:14:39:35 | call to method GetElem | $@ | G.cs:31:18:31:27 | object creation of type Elem : Elem | object creation of type Elem : Elem | -| G.cs:52:14:52:31 | access to field Elem | G.cs:44:18:44:27 | object creation of type Elem : Elem | G.cs:52:14:52:31 | access to field Elem | $@ | G.cs:44:18:44:27 | object creation of type Elem : Elem | object creation of type Elem : Elem | -| H.cs:25:14:25:25 | access to field FieldA | H.cs:23:20:23:31 | object creation of type Object : Object | H.cs:25:14:25:25 | access to field FieldA | $@ | H.cs:23:20:23:31 | object creation of type Object : Object | object creation of type Object : Object | -| H.cs:45:14:45:21 | access to field FieldB | H.cs:43:20:43:31 | object creation of type Object : Object | H.cs:45:14:45:21 | access to field FieldB | $@ | H.cs:43:20:43:31 | object creation of type Object : Object | object creation of type Object : Object | -| H.cs:65:14:65:22 | access to field FieldB | H.cs:63:20:63:31 | object creation of type Object : Object | H.cs:65:14:65:22 | access to field FieldB | $@ | H.cs:63:20:63:31 | object creation of type Object : Object | object creation of type Object : Object | -| H.cs:89:14:89:21 | access to field FieldA | H.cs:88:20:88:31 | object creation of type Object : Object | H.cs:89:14:89:21 | access to field FieldA | $@ | H.cs:88:20:88:31 | object creation of type Object : Object | object creation of type Object : Object | -| H.cs:90:14:90:22 | access to field FieldB | H.cs:88:20:88:31 | object creation of type Object : Object | H.cs:90:14:90:22 | access to field FieldB | $@ | H.cs:88:20:88:31 | object creation of type Object : Object | object creation of type Object : Object | -| H.cs:114:14:114:21 | access to field FieldB | H.cs:112:20:112:31 | object creation of type Object : Object | H.cs:114:14:114:21 | access to field FieldB | $@ | H.cs:112:20:112:31 | object creation of type Object : Object | object creation of type Object : Object | -| H.cs:131:14:131:19 | call to method Get | H.cs:130:20:130:31 | object creation of type Object : Object | H.cs:131:14:131:19 | call to method Get | $@ | H.cs:130:20:130:31 | object creation of type Object : Object | object creation of type Object : Object | -| H.cs:148:14:148:14 | access to local variable a | H.cs:147:25:147:31 | object creation of type A : A | H.cs:148:14:148:14 | access to local variable a | $@ | H.cs:147:25:147:31 | object creation of type A : A | object creation of type A : A | -| H.cs:166:14:166:14 | access to local variable b | H.cs:155:17:155:23 | object creation of type B : B | H.cs:166:14:166:14 | access to local variable b | $@ | H.cs:155:17:155:23 | object creation of type B : B | object creation of type B : B | -| H.cs:167:14:167:21 | access to field FieldB | H.cs:163:17:163:28 | object creation of type Object : Object | H.cs:167:14:167:21 | access to field FieldB | $@ | H.cs:163:17:163:28 | object creation of type Object : Object | object creation of type Object : Object | -| I.cs:18:14:18:21 | access to field Field1 | I.cs:13:17:13:28 | object creation of type Object : Object | I.cs:18:14:18:21 | access to field Field1 | $@ | I.cs:13:17:13:28 | object creation of type Object : Object | object creation of type Object : Object | -| I.cs:23:14:23:21 | access to field Field1 | I.cs:7:18:7:29 | object creation of type Object : Object | I.cs:23:14:23:21 | access to field Field1 | $@ | I.cs:7:18:7:29 | object creation of type Object : Object | object creation of type Object : Object | -| I.cs:27:14:27:21 | access to field Field1 | I.cs:7:18:7:29 | object creation of type Object : Object | I.cs:27:14:27:21 | access to field Field1 | $@ | I.cs:7:18:7:29 | object creation of type Object : Object | object creation of type Object : Object | -| I.cs:40:14:40:21 | access to field Field1 | I.cs:31:13:31:24 | object creation of type Object : Object | I.cs:40:14:40:21 | access to field Field1 | $@ | I.cs:31:13:31:24 | object creation of type Object : Object | object creation of type Object : Object | -| J.cs:14:14:14:21 | access to property Prop1 | J.cs:12:17:12:28 | object creation of type Object : Object | J.cs:14:14:14:21 | access to property Prop1 | $@ | J.cs:12:17:12:28 | object creation of type Object : Object | object creation of type Object : Object | -| J.cs:18:14:18:21 | access to property Prop1 | J.cs:12:17:12:28 | object creation of type Object : Object | J.cs:18:14:18:21 | access to property Prop1 | $@ | J.cs:12:17:12:28 | object creation of type Object : Object | object creation of type Object : Object | -| J.cs:22:14:22:21 | access to property Prop1 | J.cs:12:17:12:28 | object creation of type Object : Object | J.cs:22:14:22:21 | access to property Prop1 | $@ | J.cs:12:17:12:28 | object creation of type Object : Object | object creation of type Object : Object | -| J.cs:23:14:23:21 | access to property Prop2 | J.cs:12:17:12:28 | object creation of type Object : Object | J.cs:23:14:23:21 | access to property Prop2 | $@ | J.cs:12:17:12:28 | object creation of type Object : Object | object creation of type Object : Object | +| A.cs:7:14:7:16 | access to field c | A.cs:5:17:5:28 | call to method Source : C | A.cs:7:14:7:16 | access to field c | $@ | A.cs:5:17:5:28 | call to method Source : C | call to method Source : C | +| A.cs:14:14:14:20 | call to method Get | A.cs:13:15:13:29 | call to method Source : C1 | A.cs:14:14:14:20 | call to method Get | $@ | A.cs:13:15:13:29 | call to method Source : C1 | call to method Source : C1 | +| A.cs:15:14:15:42 | call to method Get | A.cs:15:21:15:34 | call to method Source : C | A.cs:15:14:15:42 | call to method Get | $@ | A.cs:15:21:15:34 | call to method Source : C | call to method Source : C | +| A.cs:24:14:24:17 | access to field c | A.cs:22:25:22:37 | call to method Source : C2 | A.cs:24:14:24:17 | access to field c | $@ | A.cs:22:25:22:37 | call to method Source : C2 | call to method Source : C2 | +| A.cs:33:14:33:17 | access to field c | A.cs:31:29:31:41 | call to method Source : C2 | A.cs:33:14:33:17 | access to field c | $@ | A.cs:31:29:31:41 | call to method Source : C2 | call to method Source : C2 | +| A.cs:64:18:64:26 | access to field a | A.cs:55:17:55:28 | call to method Source : A | A.cs:64:18:64:26 | access to field a | $@ | A.cs:55:17:55:28 | call to method Source : A | call to method Source : A | +| A.cs:89:14:89:16 | access to field c | A.cs:83:15:83:26 | call to method Source : C | A.cs:89:14:89:16 | access to field c | $@ | A.cs:83:15:83:26 | call to method Source : C | call to method Source : C | +| A.cs:106:14:106:16 | access to field b | A.cs:98:30:98:43 | call to method Source : B | A.cs:106:14:106:16 | access to field b | $@ | A.cs:98:30:98:43 | call to method Source : B | call to method Source : B | +| A.cs:106:14:106:16 | access to field b | A.cs:104:17:104:30 | call to method Source : B | A.cs:106:14:106:16 | access to field b | $@ | A.cs:104:17:104:30 | call to method Source : B | call to method Source : B | +| A.cs:107:14:107:18 | access to field c | A.cs:97:19:97:32 | call to method Source : C | A.cs:107:14:107:18 | access to field c | $@ | A.cs:97:19:97:32 | call to method Source : C | call to method Source : C | +| A.cs:108:14:108:16 | access to field c | A.cs:97:19:97:32 | call to method Source : C | A.cs:108:14:108:16 | access to field c | $@ | A.cs:97:19:97:32 | call to method Source : C | call to method Source : C | +| A.cs:119:14:119:30 | access to field head | A.cs:113:17:113:29 | call to method Source : B | A.cs:119:14:119:30 | access to field head | $@ | A.cs:113:17:113:29 | call to method Source : B | call to method Source : B | +| A.cs:123:18:123:23 | access to field head | A.cs:113:17:113:29 | call to method Source : B | A.cs:123:18:123:23 | access to field head | $@ | A.cs:113:17:113:29 | call to method Source : B | call to method Source : B | +| B.cs:8:14:8:26 | access to field elem1 | B.cs:5:17:5:31 | call to method Source : Elem | B.cs:8:14:8:26 | access to field elem1 | $@ | B.cs:5:17:5:31 | call to method Source : Elem | call to method Source : Elem | +| B.cs:18:14:18:26 | access to field elem2 | B.cs:14:17:14:31 | call to method Source : Elem | B.cs:18:14:18:26 | access to field elem2 | $@ | B.cs:14:17:14:31 | call to method Source : Elem | call to method Source : Elem | +| C.cs:23:14:23:15 | access to field s1 | C.cs:3:23:3:37 | call to method Source : Elem | C.cs:23:14:23:15 | access to field s1 | $@ | C.cs:3:23:3:37 | call to method Source : Elem | call to method Source : Elem | +| C.cs:24:14:24:15 | access to field s2 | C.cs:4:32:4:46 | call to method Source : Elem | C.cs:24:14:24:15 | access to field s2 | $@ | C.cs:4:32:4:46 | call to method Source : Elem | call to method Source : Elem | +| C.cs:25:14:25:15 | access to field s3 | C.cs:18:19:18:33 | call to method Source : Elem | C.cs:25:14:25:15 | access to field s3 | $@ | C.cs:18:19:18:33 | call to method Source : Elem | call to method Source : Elem | +| C.cs:26:14:26:15 | access to field s4 | C.cs:6:30:6:44 | call to method Source : Elem | C.cs:26:14:26:15 | access to field s4 | $@ | C.cs:6:30:6:44 | call to method Source : Elem | call to method Source : Elem | +| C.cs:27:14:27:15 | access to property s5 | C.cs:7:37:7:51 | call to method Source : Elem | C.cs:27:14:27:15 | access to property s5 | $@ | C.cs:7:37:7:51 | call to method Source : Elem | call to method Source : Elem | +| C.cs:28:14:28:15 | access to property s6 | C.cs:8:30:8:44 | call to method Source : Elem | C.cs:28:14:28:15 | access to property s6 | $@ | C.cs:8:30:8:44 | call to method Source : Elem | call to method Source : Elem | +| D.cs:32:14:32:23 | access to property AutoProp | D.cs:29:17:29:33 | call to method Source : Object | D.cs:32:14:32:23 | access to property AutoProp | $@ | D.cs:29:17:29:33 | call to method Source : Object | call to method Source : Object | +| D.cs:39:14:39:26 | access to property TrivialProp | D.cs:37:26:37:42 | call to method Source : Object | D.cs:39:14:39:26 | access to property TrivialProp | $@ | D.cs:37:26:37:42 | call to method Source : Object | call to method Source : Object | +| D.cs:40:14:40:31 | access to field trivialPropField | D.cs:37:26:37:42 | call to method Source : Object | D.cs:40:14:40:31 | access to field trivialPropField | $@ | D.cs:37:26:37:42 | call to method Source : Object | call to method Source : Object | +| D.cs:41:14:41:26 | access to property ComplexProp | D.cs:37:26:37:42 | call to method Source : Object | D.cs:41:14:41:26 | access to property ComplexProp | $@ | D.cs:37:26:37:42 | call to method Source : Object | call to method Source : Object | +| D.cs:45:14:45:26 | access to property TrivialProp | D.cs:43:32:43:48 | call to method Source : Object | D.cs:45:14:45:26 | access to property TrivialProp | $@ | D.cs:43:32:43:48 | call to method Source : Object | call to method Source : Object | +| D.cs:46:14:46:31 | access to field trivialPropField | D.cs:43:32:43:48 | call to method Source : Object | D.cs:46:14:46:31 | access to field trivialPropField | $@ | D.cs:43:32:43:48 | call to method Source : Object | call to method Source : Object | +| D.cs:47:14:47:26 | access to property ComplexProp | D.cs:43:32:43:48 | call to method Source : Object | D.cs:47:14:47:26 | access to property ComplexProp | $@ | D.cs:43:32:43:48 | call to method Source : Object | call to method Source : Object | +| E.cs:24:14:24:20 | access to field Field | E.cs:22:17:22:33 | call to method Source : Object | E.cs:24:14:24:20 | access to field Field | $@ | E.cs:22:17:22:33 | call to method Source : Object | call to method Source : Object | +| F.cs:12:14:12:21 | access to field Field1 | F.cs:10:17:10:33 | call to method Source : Object | F.cs:12:14:12:21 | access to field Field1 | $@ | F.cs:10:17:10:33 | call to method Source : Object | call to method Source : Object | +| F.cs:17:14:17:21 | access to field Field2 | F.cs:15:26:15:42 | call to method Source : Object | F.cs:17:14:17:21 | access to field Field2 | $@ | F.cs:15:26:15:42 | call to method Source : Object | call to method Source : Object | +| F.cs:20:14:20:21 | access to field Field1 | F.cs:19:32:19:48 | call to method Source : Object | F.cs:20:14:20:21 | access to field Field1 | $@ | F.cs:19:32:19:48 | call to method Source : Object | call to method Source : Object | +| F.cs:25:14:25:21 | access to field Field2 | F.cs:23:32:23:48 | call to method Source : Object | F.cs:25:14:25:21 | access to field Field2 | $@ | F.cs:23:32:23:48 | call to method Source : Object | call to method Source : Object | +| G.cs:39:14:39:35 | call to method GetElem | G.cs:7:18:7:32 | call to method Source : Elem | G.cs:39:14:39:35 | call to method GetElem | $@ | G.cs:7:18:7:32 | call to method Source : Elem | call to method Source : Elem | +| G.cs:39:14:39:35 | call to method GetElem | G.cs:15:18:15:32 | call to method Source : Elem | G.cs:39:14:39:35 | call to method GetElem | $@ | G.cs:15:18:15:32 | call to method Source : Elem | call to method Source : Elem | +| G.cs:39:14:39:35 | call to method GetElem | G.cs:23:18:23:32 | call to method Source : Elem | G.cs:39:14:39:35 | call to method GetElem | $@ | G.cs:23:18:23:32 | call to method Source : Elem | call to method Source : Elem | +| G.cs:39:14:39:35 | call to method GetElem | G.cs:31:18:31:32 | call to method Source : Elem | G.cs:39:14:39:35 | call to method GetElem | $@ | G.cs:31:18:31:32 | call to method Source : Elem | call to method Source : Elem | +| G.cs:52:14:52:31 | access to field Elem | G.cs:44:18:44:32 | call to method Source : Elem | G.cs:52:14:52:31 | access to field Elem | $@ | G.cs:44:18:44:32 | call to method Source : Elem | call to method Source : Elem | +| H.cs:25:14:25:25 | access to field FieldA | H.cs:23:20:23:36 | call to method Source : Object | H.cs:25:14:25:25 | access to field FieldA | $@ | H.cs:23:20:23:36 | call to method Source : Object | call to method Source : Object | +| H.cs:45:14:45:21 | access to field FieldB | H.cs:43:20:43:36 | call to method Source : Object | H.cs:45:14:45:21 | access to field FieldB | $@ | H.cs:43:20:43:36 | call to method Source : Object | call to method Source : Object | +| H.cs:65:14:65:22 | access to field FieldB | H.cs:63:20:63:36 | call to method Source : Object | H.cs:65:14:65:22 | access to field FieldB | $@ | H.cs:63:20:63:36 | call to method Source : Object | call to method Source : Object | +| H.cs:89:14:89:21 | access to field FieldA | H.cs:88:20:88:36 | call to method Source : Object | H.cs:89:14:89:21 | access to field FieldA | $@ | H.cs:88:20:88:36 | call to method Source : Object | call to method Source : Object | +| H.cs:90:14:90:22 | access to field FieldB | H.cs:88:20:88:36 | call to method Source : Object | H.cs:90:14:90:22 | access to field FieldB | $@ | H.cs:88:20:88:36 | call to method Source : Object | call to method Source : Object | +| H.cs:114:14:114:21 | access to field FieldB | H.cs:112:20:112:36 | call to method Source : Object | H.cs:114:14:114:21 | access to field FieldB | $@ | H.cs:112:20:112:36 | call to method Source : Object | call to method Source : Object | +| H.cs:131:14:131:19 | call to method Get | H.cs:130:20:130:36 | call to method Source : Object | H.cs:131:14:131:19 | call to method Get | $@ | H.cs:130:20:130:36 | call to method Source : Object | call to method Source : Object | +| H.cs:148:14:148:14 | access to local variable a | H.cs:147:25:147:38 | call to method Source : A | H.cs:148:14:148:14 | access to local variable a | $@ | H.cs:147:25:147:38 | call to method Source : A | call to method Source : A | +| H.cs:166:14:166:14 | access to local variable b | H.cs:155:17:155:30 | call to method Source : B | H.cs:166:14:166:14 | access to local variable b | $@ | H.cs:155:17:155:30 | call to method Source : B | call to method Source : B | +| H.cs:167:14:167:21 | access to field FieldB | H.cs:163:17:163:35 | call to method Source : Object | H.cs:167:14:167:21 | access to field FieldB | $@ | H.cs:163:17:163:35 | call to method Source : Object | call to method Source : Object | +| I.cs:18:14:18:21 | access to field Field1 | I.cs:13:17:13:33 | call to method Source : Object | I.cs:18:14:18:21 | access to field Field1 | $@ | I.cs:13:17:13:33 | call to method Source : Object | call to method Source : Object | +| I.cs:23:14:23:21 | access to field Field1 | I.cs:7:18:7:34 | call to method Source : Object | I.cs:23:14:23:21 | access to field Field1 | $@ | I.cs:7:18:7:34 | call to method Source : Object | call to method Source : Object | +| I.cs:27:14:27:21 | access to field Field1 | I.cs:7:18:7:34 | call to method Source : Object | I.cs:27:14:27:21 | access to field Field1 | $@ | I.cs:7:18:7:34 | call to method Source : Object | call to method Source : Object | +| I.cs:40:14:40:21 | access to field Field1 | I.cs:31:13:31:29 | call to method Source : Object | I.cs:40:14:40:21 | access to field Field1 | $@ | I.cs:31:13:31:29 | call to method Source : Object | call to method Source : Object | +| J.cs:14:14:14:21 | access to property Prop1 | J.cs:12:17:12:33 | call to method Source : Object | J.cs:14:14:14:21 | access to property Prop1 | $@ | J.cs:12:17:12:33 | call to method Source : Object | call to method Source : Object | +| J.cs:18:14:18:21 | access to property Prop1 | J.cs:12:17:12:33 | call to method Source : Object | J.cs:18:14:18:21 | access to property Prop1 | $@ | J.cs:12:17:12:33 | call to method Source : Object | call to method Source : Object | +| J.cs:22:14:22:21 | access to property Prop1 | J.cs:12:17:12:33 | call to method Source : Object | J.cs:22:14:22:21 | access to property Prop1 | $@ | J.cs:12:17:12:33 | call to method Source : Object | call to method Source : Object | +| J.cs:23:14:23:21 | access to property Prop2 | J.cs:21:36:21:52 | call to method Source : Object | J.cs:23:14:23:21 | access to property Prop2 | $@ | J.cs:21:36:21:52 | call to method Source : Object | call to method Source : Object | diff --git a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.ql b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.ql index 9d1a7f807fd..55578cf970c 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.ql +++ b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.ql @@ -4,20 +4,8 @@ import csharp import DataFlow::PathGraph +import TestUtilities.InlineFlowTest -class Conf extends DataFlow::Configuration { - Conf() { this = "FieldFlowConf" } - - override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof ObjectCreation } - - override predicate isSink(DataFlow::Node sink) { - exists(MethodCall mc | - mc.getTarget().hasName("Sink") and - mc.getAnArgument() = sink.asExpr() - ) - } -} - -from DataFlow::PathNode source, DataFlow::PathNode sink, Conf conf +from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf where conf.hasFlowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/csharp/ql/test/library-tests/dataflow/fields/G.cs b/csharp/ql/test/library-tests/dataflow/fields/G.cs index 5f35ae57293..4a11c67c225 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/G.cs +++ b/csharp/ql/test/library-tests/dataflow/fields/G.cs @@ -4,7 +4,7 @@ public class G public void M1() { - Elem e = new Elem(); + Elem e = Source(1); Box2 b = new Box2(new Box1(null)); b.Box1.Elem = e; SinkWrap(b); @@ -12,7 +12,7 @@ public class G public void M2() { - Elem e = new Elem(); + Elem e = Source(2); Box2 b = new Box2(new Box1(null)); b.Box1.SetElem(e); SinkWrap(b); @@ -20,7 +20,7 @@ public class G public void M3() { - Elem e = new Elem(); + Elem e = Source(3); Box2 b = new Box2(new Box1(null)); b.GetBox1().Elem = e; SinkWrap(b); @@ -28,7 +28,7 @@ public class G public void M4() { - Elem e = new Elem(); + Elem e = Source(4); Box2 b = new Box2(new Box1(null)); b.GetBox1().SetElem(e); SinkWrap(b); @@ -36,12 +36,12 @@ public class G public static void SinkWrap(Box2 b2) { - Sink(b2.GetBox1().GetElem()); + Sink(b2.GetBox1().GetElem()); // $ hasValueFlow=1 $ hasValueFlow=2 $ hasValueFlow=3 $ hasValueFlow=4 } public void M5a() { - Elem e = new Elem(); + Elem e = Source(5); boxfield = new Box2(new Box1(null)); boxfield.Box1.Elem = e; M5b(); @@ -49,7 +49,7 @@ public class G private void M5b() { - Sink(boxfield.Box1.Elem); + Sink(boxfield.Box1.Elem); // $ hasValueFlow=5 } public static void Sink(object o) { } @@ -71,4 +71,6 @@ public class G public Box1 GetBox1() => Box1; public void SetBox1(Box1 b) { Box1 = b; } } + + static T Source(object source) => throw null; } \ No newline at end of file diff --git a/csharp/ql/test/library-tests/dataflow/fields/H.cs b/csharp/ql/test/library-tests/dataflow/fields/H.cs index 1d214fc89da..079ca2295e8 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/H.cs +++ b/csharp/ql/test/library-tests/dataflow/fields/H.cs @@ -20,10 +20,10 @@ public class H void M1(object o) { var a = new A(); - a.FieldA = new object(); + a.FieldA = Source(1); var clone = Clone(a); - Sink(clone.FieldA); // flow - + Sink(clone.FieldA); // $ hasValueFlow=1 + a = new A(); a.FieldA = o; clone = Clone(a); @@ -40,9 +40,9 @@ public class H void M2(object o) { var a = new A(); - a.FieldA = new object(); + a.FieldA = Source(2); var b = Transform(a); - Sink(b.FieldB); // flow + Sink(b.FieldB); // $ hasValueFlow=2 a = new A(); a.FieldA = o; @@ -60,9 +60,9 @@ public class H var a = new A(); var b1 = new B(); var b2 = new B(); - a.FieldA = new object(); + a.FieldA = Source(3); TransformArg(a, b1, b2); - Sink(b1.FieldB); // flow + Sink(b1.FieldB); // $ hasValueFlow=3 Sink(b2.FieldB); // no flow a = new A(); @@ -85,9 +85,9 @@ public class H var a = new A(); var b1 = new B(); var b2 = new B(); - SetArgs(a, new object(), b1, b2); - Sink(a.FieldA); // flow - Sink(b1.FieldB); // flow + SetArgs(a, Source(4), b1, b2); + Sink(a.FieldA); // $ hasValueFlow=4 + Sink(b1.FieldB); // $ hasValueFlow=4 Sink(b2.FieldB); // no flow a = new A(); @@ -109,9 +109,9 @@ public class H void M5(object o) { var a = new A(); - a.FieldA = new object(); + a.FieldA = Source(5); var b = TransformWrap(a); - Sink(b.FieldB); // flow + Sink(b.FieldB); // $ hasValueFlow=5 a = new A(); a.FieldA = o; @@ -127,8 +127,8 @@ public class H void M6(object o) { var a = new A(); - a.FieldA = new object(); - Sink(Get(a)); // flow + a.FieldA = Source(6); + Sink(Get(a)); // $ hasValueFlow=6 a = new A(); a.FieldA = o; @@ -144,15 +144,15 @@ public class H void M7() { - var a = Through(new A()); - Sink(a); // flow - var b = Through(new B()); + var a = Through(Source(7.1)); + Sink(a); // $ hasValueFlow=7.1 + var b = Through(Source(7.2)); Sink(b); // no flow } void SetNested(A a, object o) { - var b = new B(); + var b = Source(8.1); b.FieldB = o; a.FieldA = b; } @@ -160,12 +160,14 @@ public class H void M8() { var a = new A(); - var o = new object(); + var o = Source(8.2); SetNested(a, o); - var b = (B) a.FieldA; - Sink(b); // flow (from `new B()` inside `SetNested`) - Sink(b.FieldB); // flow + var b = (B)a.FieldA; + Sink(b); // $ hasValueFlow=8.1 + Sink(b.FieldB); // $ hasValueFlow=8.2 } public static void Sink(object o) { } + + static T Source(object source) => throw null; } \ No newline at end of file diff --git a/csharp/ql/test/library-tests/dataflow/fields/I.cs b/csharp/ql/test/library-tests/dataflow/fields/I.cs index 99aed3b0df7..41ae9cd364c 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/I.cs +++ b/csharp/ql/test/library-tests/dataflow/fields/I.cs @@ -4,31 +4,31 @@ public class I object Field2; public I() { - Field1 = new object(); - Field2 = new object(); + Field1 = Source(1); + Field2 = Source(2); } private void M() { - var o = new object(); + var o = Source(3); var i = new I(); i.Field1 = o; i.Field2 = o; i.Field2 = null; - Sink(i.Field1); // flow + Sink(i.Field1); // $ hasValueFlow=3 Sink(i.Field2); // no flow i = new I(); i.Field2 = null; - Sink(i.Field1); // flow + Sink(i.Field1); // $ hasValueFlow=1 Sink(i.Field2); // no flow i = new I() { Field2 = null }; - Sink(i.Field1); // flow + Sink(i.Field1); // $ hasValueFlow=1 Sink(i.Field2); // no flow i = new I(); - o = new object(); + o = Source(4); i.Field1 = o; i.Field2 = o; M2(i); @@ -37,10 +37,12 @@ public class I private void M2(I i) { i.Field2 = null; - Sink(i.Field1); // flow + Sink(i.Field1); // $ hasValueFlow=4 Sink(i.Field2); // no flow } public static void Sink(object o) { } + + static T Source(object source) => throw null; } diff --git a/csharp/ql/test/library-tests/dataflow/fields/J.cs b/csharp/ql/test/library-tests/dataflow/fields/J.cs index 4244a514281..b53cdb2e97e 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/J.cs +++ b/csharp/ql/test/library-tests/dataflow/fields/J.cs @@ -9,18 +9,18 @@ public class J { private void M1() { - var o = new object(); + var o = Source(1); var r1 = new Record(o, null); - Sink(r1.Prop1); // flow + Sink(r1.Prop1); // $ hasValueFlow=1 Sink(r1.Prop2); // no flow var r2 = r1 with { }; - Sink(r2.Prop1); // flow + Sink(r2.Prop1); // $ hasValueFlow=1 Sink(r2.Prop2); // no flow - var r3 = r1 with { Prop2 = o }; - Sink(r3.Prop1); // flow - Sink(r3.Prop2); // flow + var r3 = r1 with { Prop2 = Source(2) }; + Sink(r3.Prop1); // $ hasValueFlow=1 + Sink(r3.Prop2); // $ hasValueFlow=2 var r4 = r1 with { Prop1 = null }; Sink(r4.Prop1); // no flow @@ -28,4 +28,6 @@ public class J } public static void Sink(object o) { } + + static T Source(object source) => throw null; } diff --git a/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantCondition.cs b/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantCondition.cs index dd72191a29a..9e7386149a4 100644 --- a/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantCondition.cs +++ b/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantCondition.cs @@ -48,8 +48,8 @@ class ConstantNullness j = (int?)i ?? 1; // BAD s = ""?.CommaJoinWith(s); // BAD s = s ?? ""; // GOOD - s = (i==0 ? s : null) ?? s; // GOOD - var k = (i==0 ? s : null)?.Length; // GOOD + s = (i == 0 ? s : null) ?? s; // GOOD + var k = (i == 0 ? s : null)?.Length; // GOOD } } @@ -59,12 +59,12 @@ class ConstantMatching { switch (1 + 2) { - case 2 : // BAD - break; - case 3 : // BAD - break; - case int _ : // GOOD - break; + case 2: // BAD + break; + case 3: // BAD + break; + case int _: // GOOD + break; } } @@ -72,10 +72,10 @@ class ConstantMatching { switch ((object)s) { - case int _ : // BAD - break; - case "" : // GOOD - break; + case int _: // BAD + break; + case "": // GOOD + break; } } @@ -83,8 +83,8 @@ class ConstantMatching { switch (o) { - case IList _ : // GOOD - break; + case IList _: // GOOD + break; } } @@ -105,7 +105,8 @@ class ConstantMatching }; } - void M6(bool b1, bool b2) { + void M6(bool b1, bool b2) + { if (!b1) return; if (!b2) @@ -113,6 +114,16 @@ class ConstantMatching if (b1 && b2) // BAD return; } + + string M7(object o) + { + return o switch + { + (string s, _) => s, // GOOD + (_, string s) => s, // GOOD + _ => "" // GOOD + }; + } } class Assertions diff --git a/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantCondition.expected b/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantCondition.expected index f7ce6c1824a..17a51125c4c 100644 --- a/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantCondition.expected +++ b/csharp/ql/test/query-tests/Bad Practices/Control-Flow/ConstantCondition/ConstantCondition.expected @@ -8,8 +8,8 @@ | ConstantCondition.cs:64:18:64:18 | 3 | Pattern always matches. | | ConstantCondition.cs:75:18:75:20 | access to type Int32 | Pattern never matches. | | ConstantCondition.cs:95:13:95:13 | _ | Pattern always matches. | -| ConstantCondition.cs:113:13:113:14 | access to parameter b1 | Condition always evaluates to 'true'. | -| ConstantCondition.cs:113:19:113:20 | access to parameter b2 | Condition always evaluates to 'true'. | +| ConstantCondition.cs:114:13:114:14 | access to parameter b1 | Condition always evaluates to 'true'. | +| ConstantCondition.cs:114:19:114:20 | access to parameter b2 | Condition always evaluates to 'true'. | | ConstantConditionBad.cs:5:16:5:20 | ... > ... | Condition always evaluates to 'false'. | | ConstantConditionalExpressionCondition.cs:11:22:11:34 | ... == ... | Condition always evaluates to 'true'. | | ConstantConditionalExpressionCondition.cs:12:21:12:25 | false | Condition always evaluates to 'false'. | diff --git a/csharp/ql/test/query-tests/Dead Code/DeadStoreOfLocal/DeadStoreOfLocal.cs b/csharp/ql/test/query-tests/Dead Code/DeadStoreOfLocal/DeadStoreOfLocal.cs index 289ffed7953..941981f6d6c 100644 --- a/csharp/ql/test/query-tests/Dead Code/DeadStoreOfLocal/DeadStoreOfLocal.cs +++ b/csharp/ql/test/query-tests/Dead Code/DeadStoreOfLocal/DeadStoreOfLocal.cs @@ -461,3 +461,16 @@ public static class AnonymousVariable return count; } } + +public static class Using +{ + public static void M() + { + using var x = new System.IO.FileStream("", System.IO.FileMode.Open); // GOOD + using var _ = new System.IO.FileStream("", System.IO.FileMode.Open); // GOOD + + using (var y = new System.IO.FileStream("", System.IO.FileMode.Open)) // BAD + { + } + } +} \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Dead Code/DeadStoreOfLocal/DeadStoreOfLocal.expected b/csharp/ql/test/query-tests/Dead Code/DeadStoreOfLocal/DeadStoreOfLocal.expected index ab6a47ca9ed..6f718c49407 100644 --- a/csharp/ql/test/query-tests/Dead Code/DeadStoreOfLocal/DeadStoreOfLocal.expected +++ b/csharp/ql/test/query-tests/Dead Code/DeadStoreOfLocal/DeadStoreOfLocal.expected @@ -14,6 +14,7 @@ | DeadStoreOfLocal.cs:331:9:331:32 | ... = ... | This assignment to $@ is useless, since its value is never read. | DeadStoreOfLocal.cs:327:23:327:23 | b | b | | DeadStoreOfLocal.cs:372:13:372:20 | String s = ... | This assignment to $@ is useless, since its value is never read. | DeadStoreOfLocal.cs:372:13:372:13 | s | s | | DeadStoreOfLocal.cs:398:13:398:21 | ... = ... | This assignment to $@ is useless, since its value is never read. | DeadStoreOfLocal.cs:396:13:396:13 | s | s | +| DeadStoreOfLocal.cs:472:20:472:76 | FileStream y = ... | This assignment to $@ is useless, since its value is never read. | DeadStoreOfLocal.cs:472:20:472:20 | y | y | | DeadStoreOfLocalBad.cs:7:13:7:48 | Boolean success = ... | This assignment to $@ is useless, since its value is never read. | DeadStoreOfLocalBad.cs:7:13:7:19 | success | success | | DeadStoreOfLocalBad.cs:23:32:23:32 | FormatException e | This assignment to $@ is useless, since its value is never read. | DeadStoreOfLocalBad.cs:23:32:23:32 | e | e | | DeadStoreOfLocalBad.cs:32:22:32:22 | String s | This assignment to $@ is useless, since its value is never read. | DeadStoreOfLocalBad.cs:32:22:32:22 | s | s | diff --git a/csharp/upgrades/qlpack.yml b/csharp/upgrades/qlpack.yml index 9c3e8f6520a..1fb67a8785a 100644 --- a/csharp/upgrades/qlpack.yml +++ b/csharp/upgrades/qlpack.yml @@ -1,3 +1,4 @@ name: codeql/csharp-upgrades upgrades: . version: 0.0.2 +library: true diff --git a/docs/codeql/codeql-cli/about-ql-packs.rst b/docs/codeql/codeql-cli/about-ql-packs.rst index dc47f2a023b..fb5a1d8a8a0 100644 --- a/docs/codeql/codeql-cli/about-ql-packs.rst +++ b/docs/codeql/codeql-cli/about-ql-packs.rst @@ -7,7 +7,7 @@ QL packs are used to organize the files used in CodeQL analysis. They contain queries, library files, query suites, and important metadata. The `CodeQL repository `__ contains QL packs for -C/C++, C#, Java, JavaScript, and Python. The `CodeQL for Go +C/C++, C#, Java, JavaScript, Python, and Ruby. The `CodeQL for Go `__ repository contains a QL pack for Go analysis. You can also make custom QL packs to contain your own queries and libraries. diff --git a/docs/codeql/codeql-cli/creating-codeql-databases.rst b/docs/codeql/codeql-cli/creating-codeql-databases.rst index 16dadf16411..9fa137599b7 100644 --- a/docs/codeql/codeql-cli/creating-codeql-databases.rst +++ b/docs/codeql/codeql-cli/creating-codeql-databases.rst @@ -88,15 +88,15 @@ Creating databases for non-compiled languages --------------------------------------------- The CodeQL CLI includes extractors to create databases for non-compiled -languages---specifically, JavaScript (and TypeScript) and Python. These -extractors are automatically invoked when you specify JavaScript or Python as +languages---specifically, JavaScript (and TypeScript), Python, and Ruby. These +extractors are automatically invoked when you specify JavaScript, Python, or Ruby as the ``--language`` option when executing ``database create``. When creating databases for these languages you must ensure that all additional dependencies are available. .. pull-quote:: Important - When you run ``database create`` for JavaScript, TypeScript, and Python, you should not + When you run ``database create`` for JavaScript, TypeScript, Python, and Ruby, you should not specify a ``--command`` option. Otherwise this overrides the normal extractor invocation, which will create an empty database. If you create databases for multiple languages and one of them is a compiled language, @@ -116,6 +116,8 @@ Here, we have specified a ``--source-root`` path, which is the location where database creation is executed, but is not necessarily the checkout root of the codebase. +By default, files in ``node_modules`` and ``bower_components`` directories are not extracted. + Python ~~~~~~ @@ -127,14 +129,25 @@ When creating databases for Python you must ensure: packages that the codebase depends on. - You have installed the `virtualenv `__ pip module. -In the command line you must specify ``--language=python``. For example +In the command line you must specify ``--language=python``. For example:: :: codeql database create --language=python /python-database -executes the ``database create`` subcommand from the code's checkout root, +This executes the ``database create`` subcommand from the code's checkout root, generating a new Python database at ``/python-database``. +Ruby +~~~~ + +Creating databases for Ruby requires no additional dependencies. +In the command line you must specify ``--language=ruby``. For example:: + + codeql database create --language=ruby --source-root /ruby-database + +Here, we have specified a ``--source-root`` path, which is the location where +database creation is executed, but is not necessarily the checkout root of the +codebase. Creating databases for compiled languages ----------------------------------------- @@ -221,6 +234,26 @@ commands that you can specify for compiled languages. codeql database create java-database --language=java --command='ant -f build.xml' +- Project built using Bazel:: + + # Navigate to the Bazel workspace. + + # Before building, remove cached objects + # and stop all running Bazel server processes. + bazel clean --expunge + + # Build using the following Bazel flags, to help CodeQL detect the build: + # `--spawn_strategy=local`: build locally, instead of using a distributed build + # `--nouse_action_cache`: turn off build caching, which might prevent recompilation of source code + # `--noremote_accept_cached`, `--noremote_upload_local_results`: avoid using a remote cache + codeql database create new-database --language= \ + --command='bazel build --spawn_strategy=local --nouse_action_cache --noremote_accept_cached --noremote_upload_local_results //path/to/package:target' + + # After building, stop all running Bazel server processes. + # This ensures future build commands start in a clean Bazel server process + # without CodeQL attached. + bazel shutdown + - Project built using a custom build script:: codeql database create new-database --language= --command='./scripts/build.sh' diff --git a/docs/codeql/codeql-cli/getting-started-with-the-codeql-cli.rst b/docs/codeql/codeql-cli/getting-started-with-the-codeql-cli.rst index 1144c1c3e6c..a8ef822a628 100644 --- a/docs/codeql/codeql-cli/getting-started-with-the-codeql-cli.rst +++ b/docs/codeql/codeql-cli/getting-started-with-the-codeql-cli.rst @@ -100,7 +100,7 @@ further options on the command line. The `CodeQL repository `__ contains the queries and libraries required for CodeQL analysis of C/C++, C#, Java, -JavaScript/TypeScript, and Python. +JavaScript/TypeScript, Python, and Ruby. Clone a copy of this repository into ``codeql-home``. By default, the root of the cloned repository will be called ``codeql``. diff --git a/docs/codeql/codeql-for-visual-studio-code/setting-up-codeql-in-visual-studio-code.rst b/docs/codeql/codeql-for-visual-studio-code/setting-up-codeql-in-visual-studio-code.rst index 0075826d421..a43c69edecb 100644 --- a/docs/codeql/codeql-for-visual-studio-code/setting-up-codeql-in-visual-studio-code.rst +++ b/docs/codeql/codeql-for-visual-studio-code/setting-up-codeql-in-visual-studio-code.rst @@ -78,7 +78,7 @@ Using the starter workspace ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The starter workspace is a Git repository. It contains: -* The `repository of CodeQL libraries and queries `__ for C/C++, C#, Java, JavaScript, and Python. This is included as a submodule, so it can be updated without affecting your custom queries. +* The `repository of CodeQL libraries and queries `__ for C/C++, C#, Java, JavaScript, Python, and Ruby. This is included as a submodule, so it can be updated without affecting your custom queries. * The `repository of CodeQL libraries and queries `__ for Go. This is also included as a submodule. * A series of folders named ``codeql-custom-queries-``. These are ready for you to start developing your own custom queries for each language, using the standard libraries. There are some example queries to get you started. diff --git a/docs/codeql/codeql-language-guides/basic-query-for-ruby-code.rst b/docs/codeql/codeql-language-guides/basic-query-for-ruby-code.rst new file mode 100644 index 00000000000..c820bf007df --- /dev/null +++ b/docs/codeql/codeql-language-guides/basic-query-for-ruby-code.rst @@ -0,0 +1,144 @@ +.. _basic-query-for-ruby-code: + +Basic query for Ruby code +========================= + +Learn to write and run a simple CodeQL query. + +About the query +--------------- + +The query we're going to run performs a basic search of the code for ``if`` expressions that are redundant, in the sense that they have an empty ``then`` branch. For example, code such as: + +.. code-block:: ruby + + if error + # Handle the error + +Running the query +----------------- + +#. In the main search box on LGTM.com, search for the project you want to query. For tips, see `Searching `__. + +#. Click the project in the search results. + +#. Click **Query this project**. + + This opens the query console. (For information about using this, see `Using the query console `__.) + + .. pull-quote:: + + Note + + Alternatively, you can go straight to the query console by clicking **Query console** (at the top of any page), selecting **Ruby** from the **Language** drop-down list, then choosing one or more projects to query from those displayed in the **Project** drop-down list. + +#. Copy the following query into the text box in the query console: + + .. code-block:: ql + + import ruby + + from IfExpr ifexpr + where + not exists(ifexpr.getThen()) + select ifexpr, "This 'if' expression is redundant." + + LGTM checks whether your query compiles and, if all is well, the **Run** button changes to green to indicate that you can go ahead and run the query. + +#. Click **Run**. + + The name of the project you are querying, and the ID of the most recently analyzed commit to the project, are listed below the query box. To the right of this is an icon that indicates the progress of the query operation: + + .. image:: ../images/query-progress.png + :align: center + + .. pull-quote:: + + Note + + Your query is always run against the most recently analyzed commit to the selected project. + + The query will take a few moments to return results. When the query completes, the results are displayed below the project name. The query results are listed in two columns, corresponding to the two expressions in the ``select`` clause of the query. The first column corresponds to the expression ``ifexpr`` and is linked to the location in the source code of the project where ``ifexpr`` occurs. The second column is the alert message. + + ➤ `Example query results `__ + + .. pull-quote:: + + Note + + An ellipsis (…) at the bottom of the table indicates that the entire list is not displayed—click it to show more results. + +#. If any matching code is found, click a link in the ``ifexpr`` column to view the ``if`` statement in the code viewer. + + The matching ``if`` expression is highlighted with a yellow background in the code viewer. If any code in the file also matches a query from the standard query library for that language, you will see a red alert message at the appropriate point within the code. + +About the query structure +~~~~~~~~~~~~~~~~~~~~~~~~~ + +After the initial ``import`` statement, this simple query comprises three parts that serve similar purposes to the FROM, WHERE, and SELECT parts of an SQL query. + ++---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+ +| Query part | Purpose | Details | ++===============================================================+===================================================================================================================+========================================================================================================================+ +| ``import ruby`` | Imports the standard CodeQL libraries for Ruby. | Every query begins with one or more ``import`` statements. | ++---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+ +| ``from IfExpr ifexpr`` | Defines the variables for the query. | We use: an ``IfExpr`` variable for ``if`` expressions. | +| | Declarations are of the form: | | +| | `` `` | | ++---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+ +| ``where not exists(ifexpr.getThen())`` | Defines a condition on the variables. | ``ifexpr.getThen()``: gets the ``then`` branch of the ``if`` expression. | +| | | | +| | | ``exists(...)``: requires that there is a matching element, in this case a ``then`` branch. | ++---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+ +| ``select ifexpr, "This 'if' expression is redundant."`` | Defines what to report for each match. | Reports the resulting ``if`` expression with a string that explains the problem. | +| | | | +| | ``select`` statements for queries that are used to find instances of poor coding practice are always in the form: | | +| | ``select , ""`` | | ++---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+ + +Extend the query +---------------- + +Query writing is an inherently iterative process. You write a simple query and then, when you run it, you discover examples that you had not previously considered, or opportunities for improvement. + +Remove false positive results +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Browsing the results of our basic query shows that it could be improved. Among the results you are likely to find examples of ``if`` statements with an ``else`` branch, where an empty ``then`` branch does serve a purpose. For example: + +.. code-block:: ruby + + if option == "-verbose" + # nothing to do - handled earlier + else + error "unrecognized option" + +In this case, identifying the ``if`` statement with the empty ``then`` branch as redundant is a false positive. One solution to this is to modify the query to select ``if`` statements where both the ``then`` and ``else`` branches are missing. + +To exclude ``if`` statements that have an ``else`` branch: + +#. Add the following to the where clause: + + .. code-block:: ql + + and not exists(ifstmt.getElse()) + + The ``where`` clause is now: + + .. code-block:: ql + + where + not exists(ifexpr.getThen()) and + not exists(ifexpr.getElse()) + +#. Click **Run**. + + There are now fewer results because ``if`` expressions with an ``else`` branch are no longer included. + +➤ `See this in the query console `__ + +Further reading +--------------- + +.. include:: ../reusables/ruby-further-reading.rst +.. include:: ../reusables/codeql-ref-tools-further-reading.rst diff --git a/docs/codeql/codeql-language-guides/codeql-for-ruby.rst b/docs/codeql/codeql-language-guides/codeql-for-ruby.rst new file mode 100644 index 00000000000..bfb29a012ef --- /dev/null +++ b/docs/codeql/codeql-language-guides/codeql-for-ruby.rst @@ -0,0 +1,18 @@ +.. _codeql-for-ruby: + +CodeQL for Ruby +=============== + +Experiment and learn how to write effective and efficient queries for CodeQL databases generated from Ruby codebases. + +.. toctree:: + :hidden: + + basic-query-for-ruby-code + codeql-library-for-ruby + +- :doc:`Basic query for Ruby code `: Learn to write and run a simple CodeQL query using LGTM. + +- :doc:`CodeQL library for Ruby `: When you're analyzing a Ruby program, you can make use of the large collection of classes in the CodeQL library for Ruby. + +.. include:: ../reusables/ruby-beta-note.rst diff --git a/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst new file mode 100644 index 00000000000..ede3182fd56 --- /dev/null +++ b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst @@ -0,0 +1,622 @@ +.. codeql-library-for-ruby: + +CodeQL library for Ruby +======================= + +When you're analyzing a Ruby program, you can make use of the large collection of classes in the CodeQL library for Ruby. + +Overview +-------- + +CodeQL ships with an extensive library for analyzing Ruby code. The classes in this library present +the data from a CodeQL database in an object-oriented form and provide abstractions and predicates +to help you with common analysis tasks. + +The library is implemented as a set of CodeQL modules, that is, files with the extension ``.qll``. The +module `ruby.qll `__ imports most other standard library modules, so you can include the complete +library by beginning your query with: + +.. code-block:: ql + + import ruby + +The CodeQL libraries model various aspects of Ruby code, depending on the type of query you want to write. +For example the abstract syntax tree (AST) library is used for locating program elements, to match syntactic +elements in the source code. This can be used to find values, patterns and structures. + +The control flow graph (CFG) is imported using + +.. code-block:: ql + + import codeql.ruby.CFG + +The CFG models the control flow between statements and expressions, for example whether one expression can +flow to another expression, or whether an expression "dominates" another one, meaning that all paths to an +expression must flow through another expression first. + +The data flow library is imported using + +.. code-block:: ql + + import codeql.ruby.DataFlow + +Data flow tracks the flow of data through the program, including through function calls (interprocedural data flow). +Data flow is particularly useful for security queries, where untrusted data flows to vulnerable parts of the program +to exploit it. Related to data flow, is the taint-tracking library, which finds how data can *influence* other values +in a program, even when it is not copied exactly. + +The API graphs library is used to locate methods in libraries. This is particuarly useful when locating +particular functions or parameters that could be used as a source or sink of data in a security query. + +To summarize, the main Ruby modules are: + +.. list-table:: Main Ruby modules + :header-rows: 1 + + * - Import + - Description + * - ``ruby`` + - The standard Ruby library + * - ``codeql.ruby.AST`` + - The abstract syntax tree library (also imported by `ruby.qll`) + * - ``codeql.ruby.ApiGraphs`` + - The API graphs library + * - ``codeql.ruby.CFG`` + - The control flow graph library + * - ``codeql.ruby.DataFlow`` + - The data flow library + * - ``codeql.ruby.TaintTracking`` + - The taint tracking library + +The CodeQL examples in this article are only excerpts and are not meant to represent complete queries. + +Abstract syntax +--------------- + +The abstract syntax tree (AST) represents the elements of the source code organized into a tree. The `AST viewer `__ +in Visual Studio Code shows the AST nodes, including the relevant CodeQL classes and predicates. + +All CodeQL AST classes inherit from the `AstNode` class, which provides the following member predicates +to all AST classes: + +.. list-table:: Main predicates in ``AstNode`` + :header-rows: 1 + + * - Predicate + - Description + * - ``getEnclosingModule()`` + - Gets the enclosing module, if any. + * - ``getEnclosingMethod()`` + - Gets the enclosing method, if any. + * - ``getLocation()`` + - Gets the location of this node. + * - ``getAChild()`` + - Gets a child node of this node. + * - ``getParent()`` + - Gets the parent of this `AstNode`, if this node is not a root node. + * - ``getDesugared`` + - Gets the desugared version of this AST node, if any. + * - ``isSynthesized()`` + - Holds if this node was synthesized to represent an implicit AST node not + present in the source code. + +Modules +~~~~~~~ + +Modules represent the main structural elements of Ruby programs, and include modules (``Module``), +namespaces (``Namespace``) and classes (``ClassDeclaration``). + +.. list-table:: Callable classes + :header-rows: 1 + + * - CodeQL class + - Description and selected predicates + * - ``Module`` + - A representation of a runtime `module` or `class` value. + + - `getADeclaration()` - Gets a declaration + - `getSuperClass()` - Gets the super class of this module, if any. + - `getAPrependedModule()` - Gets a prepended module. + - `getAnIncludedModule()` - Gets an included module. + * - ``Namespace`` + - A class or module definition. + + - `getName()` - Gets the name of the module/class. + - `getAMethod()`, `getMethod(name)` - Gets a method in this namespace. + - `getAClass()`, `getClass(name)` - Gets a class in this namespace. + - `getAModule()`, `getModule(name)` - Gets a module in this namespace. + * - ``ClassDeclaration`` + - A class definition. + * - ``SingletonClass`` + - A definition of a singleton class on an object. + * - ``ModuleDeclaration`` + - A module definition. + * - ``Toplevel`` + - The node representing the entire Ruby source file. + +The following example lists all methods in the class `ApiController`: + +.. code-block:: ql + + import ruby + + from ClassDeclaration m + where m.getName() = "ApiController" + select m, m.getAMethod() + +Callables +~~~~~~~~~ + +`Callables` are elements that can be called, including methods and blocks. + +.. list-table:: Callable classes + :header-rows: 1 + + * - CodeQL class + - Description and main predicates + * - ``Callable`` + - A callable. + + - `getAParameter()` - gets a parameter of this callable. + - `getParameter(n)` - gets the nth parameter of this callable. + * - ``Private`` + - A call to ``private``. + * - ``Method`` + - A method. + + - `getName()` - gets the name of this method + * - ``SingletonMethod`` + - A singleton method. + * - ``Lambda`` + - A lambda (anonymous method). + * - ``Block`` + - A block. + * - ``DoBlock`` + - A block enclosed within `do` and `end`. + * - ``BraceBlock`` + - A block defined using curly braces. + +*Parameters* are the values that are passed into callables. Unlike other CodeQL language models, +parameters in Ruby are not variables themselves, but can introduce variables into the +callable. The variables of a parameter are given by the `getAVariable()` predicate. + +.. list-table:: Parameter classes + :header-rows: 1 + + * - CodeQL class + - Description and main predicates + * - ``Parameter`` + - A parameter. + + - `getCallable()` - Gets the callable that this parameter belongs to. + - `getPosition()` - Gets the zero-based position of this parameter. + - `getAVariable()`, `getVariable(name)` - Gets a variable introduced by this parameter. + * - ``PatternParameter`` + - A parameter defined using a pattern. + * - ``TuplePatternParameter`` + - A parameter defined using a tuple pattern. + * - ``NamedParameter`` + - A named parameter. + + - `getName()`, `hasName(name)` - Gets the name of this parameter. + - `getAnAccess()` - Gets an access to this parameter. + - `getDefiningAccess()` - Gets the access that defines the underlying local variable. + * - ``SimpleParameter`` + - A simple (normal) parameter. + * - ``BlockParameter`` + - A parameter that is a block. + * - ``HashSplatParameter`` + - A hash-splat (or double-splat) parameter. + * - ``KeywordParameter`` + - A keyword parameter, including a default value if the parameter is optional. + + - `getDefaultValue()` - Gets the default value, i.e. the value assigned to the parameter when one is not provided by the caller. + * - ``OptionalParameter`` + - An optional parameter. + + - `getDefaultValue()` - Gets the default value, i.e. the value assigned to the parameter when one is not provided by the caller. + * - ``SplatParameter`` + - A splat parameter. + + +Example + +.. code-block:: ql + + import ruby + + from Method m + where m.getName() = "show" + select m.getParameter(0) + +Statements +~~~~~~~~~~ + +Statements are the elements of code blocks. Statements that produce a value are called *expressions* +and have CodeQL class `Expr`. The remaining statement types (that do not produce values) are listed below. + +.. list-table:: Statement classes + :header-rows: 1 + + * - CodeQL class + - Description and main predicates + * - ``Stmt`` + - The base class for all statements. + + - `getAControlFlowNode()` - Gets a control-flow node for this statement, if any. + - `getEnclosingCallable()` - Gets the enclosing callable, if any. + * - ``EmptyStmt`` + - An empty statement. + * - ``BeginExpr`` + - A `begin` statement. + * - ``BeginBlock`` + - A `BEGIN` block. + * - ``EndBlock`` + - An `END` block. + * - ``UndefStmt`` + - An `undef` statement. + * - ``AliasStmt`` + - An `alias` statement. + * - ``ReturningStmt`` + - A statement that may return a value: `return`, `break` and `next`. + * - ``ReturnStmt`` + - A `return` statement. + * - ``BreakStmt`` + - A `break` statement. + * - ``NextStmt`` + - A `next` statement. + * - ``RedoStmt`` + - A `redo` statement. + * - ``RetryStmt`` + - A `retry` statement. + +The following example finds all literals that are returned by a `return` statement. + +.. code-block:: ql + + import ruby + + from ReturnStmt return, Literal lit + where lit.getParent() = return + select lit, "Returning a literal " + lit.getValueText() + +Expressions +~~~~~~~~~~~ + +Expressions are types of statement that evaluate to a value. The CodeQL class `Expr` is the base class of all expression types. + +.. list-table:: Expressions + :header-rows: 1 + + * - CodeQL class + - Description and main predicates + * - ``Expr`` + - An expression. + + This is the root class for all expressions. + + - `getValueText()` - Gets the textual (constant) value of this expression, if any. + * - ``Self`` + - A reference to the current object. + * - ``Pair`` + - A pair expression. + * - ``RescueClause`` + - A `rescue` clause. + * - ``RescueModifierExpr`` + - An expression with a `rescue` modifier. + * - ``StringConcatenation`` + - A concatenation of string literals. + + - `getConcatenatedValueText()` - Gets the result of concatenating all the string literals, if and only if they do not contain any interpolations. + +.. list-table:: Statement sequences + :header-rows: 1 + + * - CodeQL class + - Description + * - ``StmtSequence`` + - A sequence of expressions. + + - `getAStmt()`, `getStmt(n)` - Gets a statement in this sequence. + - `isEmpty()` - Holds if this sequence has no statements. + - `getNumberOfStatements()` - Gets the number of statements in this sequence. + * - ``BodyStmt`` + - A sequence of statements representing the body of a method, class, module, or do-block. + + - `getARescue()`, `getRescue(n)` - Gets a rescue clause in this block. + - `getElse()` - Gets the `else` clause in this block, if any. + - `getEnsure()` - Gets the `ensure` clause in this block, if any. + * - ``ParenthesizedExpr`` + - A parenthesized expression sequence, typically containing a single expression. + + +Literals are expressions that evaluate directly to the given value. The CodeQL Ruby library models all types of +Ruby literal. + +.. list-table:: Literals + :header-rows: 1 + + * - CodeQL class + - Description + * - ``Literal`` + - A literal. This is the base class for all literals. + + - `getValueText()` - Gets the source text for this literal, if this is a simple literal. + * - ``NumericLiteral`` + - A numerical literal. The literal types are ``IntegerLiteral``, ``FloatLiteral``, ``RationalLiteral``, and ``ComplexLiteral``. + * - ``NilLiteral`` + - A `nil` literal. + * - ``BooleanLiteral`` + - A Boolean value. The classes ``TrueLiteral`` and ``FalseLiteral`` match `true` and `false` respectively. + * - ``StringComponent`` + - A component of a string. Either a ``StringTextComponent``, ``StringEscapeSequenceComponent``, or ``StringInterpolationComponent``. + * - ``RegExpLiteral`` + - A regular expression literal. + * - ``SymbolLiteral`` + - A symbol literal. + * - ``SubshellLiteral`` + - A subshell literal. + * - ``CharacterLiteral`` + - A character literal. + * - ``ArrayLiteral`` + - An array literal. + * - ``HashLiteral`` + - A hash literal. + * - ``RangeLiteral`` + - A range literal. + * - ``MethodName`` + - A method name literal. + +The following example defines a string literal class containing the text "username": + +.. code-block:: ql + + class UsernameLiteral extends Literal + { + UsernameLiteral() { this.getValueText().toLowerCase().matches("%username%") } + } + + +*Operations* are types of expression that typically perform some sort of calculation. Most operations are ``MethodCalls`` because often +there is an underlying call to the operation. + +.. list-table:: Operations + :header-rows: 1 + + * - CodeQL class + - Description + * - ``Operation`` + - An operation. + * - ``UnaryOperation`` + - A unary operation. + + Types of unary operation include ``UnaryLogicalOperation``, ``NotExpr``, ``UnaryPlusExpr``, ``UnaryMinusExpr``, ``SplatExpr``, + ``HashSplatExpr``, ``UnaryBitwiseOperation``, and ``ComplementExpr``. + * - ``DefinedExpr`` + - A call to the special `defined?` operator + * - ``BinaryOperation`` + - A binary operation, that includes many other operation categories such as ``BinaryArithmeticOperation``, ``BinaryBitwiseOperation``, ``ComparisonOperation``, ``SpaceshipExpr``, and ``Assignment``. + * - ``BinaryArithmeticOperation`` + - A binary arithmetic operation. Includes: ``AddExpr``, ``SubExpr``, ``MulExpr``, ``DivExpr``, ``ModuloExpr``, and ``ExponentExpr``. + * - ``BinaryLogicalOperation`` + - A binary logical operation. Includes: ``LogicalAndExpr`` and ``LogicalOrExpr``. + * - ``BinaryBitwiseOperation`` + - A binary bitwise operation. Includes: ``LShiftExpr``, ``RShiftExpr``, ``BitwiseAndExpr``, ``BitwiseOrExpr``, and ``BitwiseXorExpr``. + * - ``ComparisonOperation`` + - A comparison operation, including the classes ``EqualityOperation``, ``EqExpr``, ``NEExpr``, ``CaseEqExpr``, ``RelationalOperation``, ``GTExpr``, ``GEExpr``, ``LTExpr``, and ``LEExpr``. + * - ``RegExpMatchExpr`` + - A regexp match expression. + * - ``NoRegExpMatchExpr`` + - A regexp-doesn't-match expression. + * - ``Assignment`` + - An assignment. Assignments are simple assignments (``AssignExpr``), or assignment operations (``AssignOperation``). + + The assignment arithmetic operations (``AssignArithmeticOperation``) are ``AssignAddExpr``, ``AssignSubExpr``, ``AssignMulExpr``, ``AssignDivExpr``, ``AssignModuloExpr``, and ``AssignExponentExpr``. + + The assignment logical operations (``AssignLogicalOperation``) are ``AssignLogicalAndExpr`` and ``AssignLogicalOrExpr``. + + The assignment bitwise operations (``AssignBitwiseOperation``) are ``AssignLShiftExpr``, ``AssignRShiftExpr``, ``AssignBitwiseAndExpr``, ``AssignBitwiseOrExpr``, and ``AssignBitwiseXorExpr``. + +The following example finds "chained assignments" (of the form ``A=B=C``): + +.. code-block:: ql + + import ruby + + from Assignment op + where op.getRightOperand() instanceof Assignment + select op, "This is a chained assignment." + +Calls pass control to another function, include explicit method calls (``MethodCall``), but also include other types of call such as `super` calls or `yield` calls. + +.. list-table:: Calls + :header-rows: 1 + + * - CodeQL class + - Description and main predicates + * - ``Call`` + - A call. + + - `getArgument(n)`, `getAnArgument()`, `getKeywordArgument(keyword)` - Gets an argument of this call. + - `getATarget()` - Gets a potential target of this call, if any. + * - ``MethodCall`` + - A method call. + + - `getReceiver()` - Gets the receiver of this call, if any. This is the object being invoked. + - `getMethodName()` - Gets the name of the method being called. + - `getBlock()` - Gets the block of this method call, if any. + * - ``SetterMethodCall`` + - A call to a setter method. + * - ``ElementReference`` + - An element reference; a call to the `[]` method. + * - ``YieldCall`` + - A call to `yield`. + * - ``SuperCall`` + - A call to `super`. + * - ``BlockArgument`` + - A block argument in a method call. + +The following example finds all method calls to a method called `delete`. + +.. code-block:: ql + + import ruby + + from MethodCall call + where call.getMethodName() = "delete" + select call, "Call to 'delete'." + +Control expressions are expressions used for control flow. They are classed as expressions because they can produce a value. + +.. list-table:: Control expressions + :header-rows: 1 + + * - CodeQL class + - Description and main predicates + * - ``ControlExpr`` + - A control expression, such as a `case`, `if`, `unless`, ternary-if (`?:`), `while`, `until` (including expression-modifier variants), and `for`. + * - ``ConditionalExpr`` + - A conditional expression. + + - `getCondition()` - Gets the condition expression. + * - ``IfExpr`` + - An `if` or `elsif` expression. + + - `getThen()` - Gets the `then` branch. + - `getElse()` - Gets the `elseif` or `else` branch. + * - ``UnlessExpr`` + - An `unless` expression. + * - ``IfModifierExpr`` + - An expression modified using `if`. + * - ``UnlessModifierExpr`` + - An expression modified using `unless`. + * - ``TernaryIfExpr`` + - A conditional expression using the ternary (`?:`) operator. + * - ``CaseExpr`` + - A `case` expression. + * - ``WhenExpr`` + - A `when` branch of a `case` expression. + * - ``Loop`` + - A loop. That is, a `for` loop, a `while` or `until` loop, or their expression-modifier variants. + * - ``ConditionalLoop`` + - A loop using a condition expression. That is, a `while` or `until` loop, or their expression-modifier variants. + + - `getCondition()` - Gets the condition expression of this loop. + * - ``WhileExpr`` + - A `while` loop. + * - ``UntilExpr`` + - An `until` loop. + * - ``WhileModifierExpr`` + - An expression looped using the `while` modifier. + * - ``UntilModifierExpr`` + - An expression looped using the `until` modifier. + * - ``ForExpr`` + - A `for` loop. + +The following example finds `if`-expressions that are missing a `then` branch. + +.. code-block:: ql + + import ruby + + from IfExpr expr + where not exists(expr.getThen()) + select expr, "This if-expression is redundant." + +Variables +~~~~~~~~~ + +*Variables* are names that hold values in a Ruby program. If you want to query *any* type +of variable, then use the ``Variable`` class, otherwise use one of the subclasses +``LocalVariable``, ``InstanceVariable``, ``ClassVariable`` or ``GlobalVariable``. + +Local variables have the scope of a single function or block, instance variables have the +scope of an object (like member variables), *class* variables have the scope of a class and are +shared between all instances of that class (like static variables), and *global* variables +have the scope of the entire program. + +.. list-table:: Variable classes + :header-rows: 1 + + * - CodeQL class + - Description and main predicates + * - ``Variable`` + - A variable declared in a scope. + + - `getName()`, `hasName(name)` - Gets the name of this variable. + - `getDeclaringScope()` - Gets the scope this variable is declared in. + - `getAnAccess()` - Gets an access to this variable. + * - ``LocalVariable`` + - A local variable. + * - ``InstanceVariable`` + - An instance variable. + * - ``ClassVariable`` + - A class variable. + * - ``GlobalVariable`` + - A global variable. + +The following example finds all class variables in the class `StaticController`: + +.. code-block:: ql + + import ruby + + from ClassDeclaration cd, ClassVariable v + where + v.getDeclaringScope() = cd and + cd.getName() = "StaticController" + select v, "This is a static variable in 'StaticController'." + +Variable accesses are the uses of a variable in the source code. Note that variables, and *uses* of variables are different concepts. +Variables are modelled using the ``Variable`` class, whereas uses of the variable are modelled using the ``VariableAccess`` class. +``Variable.getAnAccess()`` gets the accesses of a variable. + +Variable accesses come in two types: *reads* of the variable (a ``ReadAccess``), and *writes* to the variable (a ``WriteAccess``). +Accesses are a type of expression, so extend the ``Expr`` class. + +.. list-table:: Variable access classes + :header-rows: 1 + + * - CodeQL class + - Description and main predicates + * - ``VariableAccess`` + - An access to a variable. + + - `getVariable()` - Gets the variable that is accessed. + * - ``VariableReadAccess`` + - An access to a variable where the value is read. + * - ``VariableWriteAccess`` + - An access to a variable where the value is updated. + * - ``LocalVariableAccess`` + - An access to a local variable. + * - ``LocalVariableWriteAccess`` + - An access to a local variable where the value is updated. + * - ``LocalVariableReadAccess`` + - An access to a local variable where the value is read. + * - ``GlobalVariableAccess`` + - An access to a global variable where the value is updated. + * - ``InstanceVariableAccess`` + - An access to a global variable where the value is read. + * - ``InstanceVariableReadAccess`` + - An access to an instance variable. + * - ``InstanceVariableWriteAccess`` + - An access to an instance variable where the value is updated. + * - ``ClassVariableAccess`` + - An access to a class variable. + * - ``ClassVariableWriteAccess`` + - An access to a class variable where the value is updated. + * - ``ClassVariableReadAccess`` + - An access to a class variable where the value is read. + +The following example finds writes to class variables in the class `StaticController`: + +.. code-block:: ql + + import ruby + + from ClassVariableWriteAccess write, ClassDeclaration cd, ClassVariable v + where + v.getDeclaringScope() = cd and + cd.getName() = "StaticController" and + write.getVariable() = v + select write, "'StaticController' class variable is written here." \ No newline at end of file diff --git a/docs/codeql/codeql-language-guides/index.rst b/docs/codeql/codeql-language-guides/index.rst index 440b706a02a..79f3f79ac54 100644 --- a/docs/codeql/codeql-language-guides/index.rst +++ b/docs/codeql/codeql-language-guides/index.rst @@ -13,4 +13,4 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat codeql-for-java codeql-for-javascript codeql-for-python - \ No newline at end of file + codeql-for-ruby diff --git a/docs/codeql/ql-language-reference/ql-language-specification.rst b/docs/codeql/ql-language-reference/ql-language-specification.rst index 919a075e9aa..26795bd844c 100644 --- a/docs/codeql/ql-language-reference/ql-language-specification.rst +++ b/docs/codeql/ql-language-reference/ql-language-specification.rst @@ -1333,7 +1333,7 @@ The values of a set literal expression are all the values of all the contained e Set literals are supported from release 2.1.0 of the CodeQL CLI, and release 1.24 of LGTM Enterprise. -Since release 2.6.3 of the CodeQL CLI, and release 1.28 of LGTM Enterprise, a trailing comma is allowed in a set literal. +Since release 2.7.1 of the CodeQL CLI, and release 1.28 of LGTM Enterprise, a trailing comma is allowed in a set literal. Disambiguation of expressions ----------------------------- diff --git a/docs/codeql/query-help/codeql-cwe-coverage.rst b/docs/codeql/query-help/codeql-cwe-coverage.rst index b333053a1c8..30e7b569184 100644 --- a/docs/codeql/query-help/codeql-cwe-coverage.rst +++ b/docs/codeql/query-help/codeql-cwe-coverage.rst @@ -33,3 +33,6 @@ Note that the CWE coverage includes both "`supported queries ` - :doc:`CodeQL query help for JavaScript ` - :doc:`CodeQL query help for Python ` - +- :doc:`CodeQL query help for Ruby ` .. pull-quote:: Information @@ -23,6 +23,8 @@ View the query help for the queries included in the ``code-scanning``, ``securit For a full list of the CWEs covered by these queries, see ":doc:`CodeQL CWE coverage `." +.. include:: ../reusables/ruby-beta-note.rst + .. toctree:: :hidden: :titlesonly: @@ -33,5 +35,6 @@ For a full list of the CWEs covered by these queries, see ":doc:`CodeQL CWE cove java javascript python + ruby codeql-cwe-coverage diff --git a/docs/codeql/query-help/ruby-cwe.md b/docs/codeql/query-help/ruby-cwe.md new file mode 100644 index 00000000000..61f49069fce --- /dev/null +++ b/docs/codeql/query-help/ruby-cwe.md @@ -0,0 +1,8 @@ +# CWE coverage for Ruby + +An overview of CWE coverage for Ruby in the latest release of CodeQL. + +## Overview + + + diff --git a/docs/codeql/query-help/ruby.rst b/docs/codeql/query-help/ruby.rst new file mode 100644 index 00000000000..3ce1304ba76 --- /dev/null +++ b/docs/codeql/query-help/ruby.rst @@ -0,0 +1,8 @@ +CodeQL query help for Ruby +============================ + +.. include:: ../reusables/query-help-overview.rst + +For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. + +.. include:: toc-ruby.rst diff --git a/docs/codeql/reusables/beta-note-package-management.rst b/docs/codeql/reusables/beta-note-package-management.rst index 025e89489ab..a4fd362a70c 100644 --- a/docs/codeql/reusables/beta-note-package-management.rst +++ b/docs/codeql/reusables/beta-note-package-management.rst @@ -2,4 +2,4 @@ Note - The CodeQL package management functionality, including CodeQL packs, is currently available as a beta release and is subject to change. During the beta release, CodeQL packs are available only using GitHub Packages - the GitHub Container registry. To use this beta functionality, install the beta release of the CodeQL CLI bundle from: https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.6.0-beta.1. \ No newline at end of file + The CodeQL package management functionality, including CodeQL packs, is currently available as a beta release and is subject to change. During the beta release, CodeQL packs are available only using GitHub Packages - the GitHub Container registry. To use this beta functionality, install version 2.6.0 or higher of the CodeQL CLI bundle from: https://github.com/github/codeql-action/releases. \ No newline at end of file diff --git a/docs/codeql/reusables/extractors.rst b/docs/codeql/reusables/extractors.rst index 9076f7a768d..a3a4952811d 100644 --- a/docs/codeql/reusables/extractors.rst +++ b/docs/codeql/reusables/extractors.rst @@ -15,4 +15,6 @@ * - JavaScript/TypeScript - ``javascript`` * - Python - - ``python`` \ No newline at end of file + - ``python`` + * - Ruby + - ``ruby`` \ No newline at end of file diff --git a/docs/codeql/reusables/ruby-beta-note.rst b/docs/codeql/reusables/ruby-beta-note.rst new file mode 100644 index 00000000000..761381777c0 --- /dev/null +++ b/docs/codeql/reusables/ruby-beta-note.rst @@ -0,0 +1,4 @@ + .. pull-quote:: Note + + CodeQL analysis for Ruby is currently in beta. During the beta, analysis of Ruby code, + and the accompanying documentation, will not be as comprehensive as for other languages. diff --git a/docs/codeql/reusables/ruby-further-reading.rst b/docs/codeql/reusables/ruby-further-reading.rst new file mode 100644 index 00000000000..2f12e7dfb74 --- /dev/null +++ b/docs/codeql/reusables/ruby-further-reading.rst @@ -0,0 +1,3 @@ +- `CodeQL queries for Ruby `__ +- `Example queries for Ruby `__ +- `CodeQL library reference for Ruby `__ diff --git a/docs/codeql/support/reusables/frameworks.rst b/docs/codeql/support/reusables/frameworks.rst index 0846786db63..827b6fbb5dd 100644 --- a/docs/codeql/support/reusables/frameworks.rst +++ b/docs/codeql/support/reusables/frameworks.rst @@ -158,8 +158,10 @@ Python built-in support Flask, Web framework Tornado, Web framework Twisted, Web framework - PyYAML, Serialization + starlette, Asynchronous Server Gateway Interface (ASGI) dill, Serialization + PyYAML, Serialization + ruamel.yaml, Serialization simplejson, Serialization ujson, Serialization fabric, Utility library diff --git a/docs/codeql/support/reusables/versions-compilers.rst b/docs/codeql/support/reusables/versions-compilers.rst index 42c830ea665..62678e16f05 100644 --- a/docs/codeql/support/reusables/versions-compilers.rst +++ b/docs/codeql/support/reusables/versions-compilers.rst @@ -22,7 +22,8 @@ Eclipse compiler for Java (ECJ) [5]_",``.java`` JavaScript,ECMAScript 2021 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhm``, ``.xhtml``, ``.vue``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [6]_" Python,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9",Not applicable,``.py`` - TypeScript [7]_,"2.6-4.4",Standard TypeScript compiler,"``.ts``, ``.tsx``" + Ruby [7]_,"up to 3.02",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``" + TypeScript [8]_,"2.6-4.4",Standard TypeScript compiler,"``.ts``, ``.tsx``" .. container:: footnote-group @@ -32,4 +33,5 @@ .. [4] Builds that execute on Java 7 to 16 can be analyzed. The analysis understands Java 16 standard language features. .. [5] ECJ is supported when the build invokes it via the Maven Compiler plugin or the Takari Lifecycle plugin. .. [6] JSX and Flow code, YAML, JSON, HTML, and XML files may also be analyzed with JavaScript files. - .. [7] TypeScript analysis is performed by running the JavaScript extractor with TypeScript enabled. This is the default for LGTM. + .. [7] Requires glibc 2.17. + .. [8] TypeScript analysis is performed by running the JavaScript extractor with TypeScript enabled. This is the default for LGTM. diff --git a/docs/codeql/writing-codeql-queries/creating-path-queries.rst b/docs/codeql/writing-codeql-queries/creating-path-queries.rst index 60723f488e1..4eec766d488 100644 --- a/docs/codeql/writing-codeql-queries/creating-path-queries.rst +++ b/docs/codeql/writing-codeql-queries/creating-path-queries.rst @@ -116,7 +116,7 @@ Declaring sources and sinks You must provide information about the ``source`` and ``sink`` in your path query. These are objects that correspond to the nodes of the paths that you are exploring. The name and the type of the ``source`` and the ``sink`` must be declared in the ``from`` statement of the query, and the types must be compatible with the nodes of the graph computed by the ``edges`` predicate. -If you are querying C/C++, C#, Java, or JavaScript code (and you have used ``import DataFlow::PathGraph`` in your query), the definitions of the ``source`` and ``sink`` are accessed via the ``Configuration`` class in the data flow library. You should declare all three of these objects in the ``from`` statement. +If you are querying C/C++, C#, Java, JavaScript, Python, or Ruby code (and you have used ``import DataFlow::PathGraph`` in your query), the definitions of the ``source`` and ``sink`` are accessed via the ``Configuration`` class in the data flow library. You should declare all three of these objects in the ``from`` statement. For example: .. code-block:: ql diff --git a/java/change-notes/2021-05-11-ratpack-support.md b/java/change-notes/2021-05-11-ratpack-support.md new file mode 100644 index 00000000000..f490643d62d --- /dev/null +++ b/java/change-notes/2021-05-11-ratpack-support.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Add support for [Ratpack](https://ratpack.io/) HTTP framework. diff --git a/java/change-notes/2021-05-12-hardcoded-azure-credentials-in-api-call.md b/java/change-notes/2021-05-12-hardcoded-azure-credentials-in-api-call.md new file mode 100644 index 00000000000..a5b55ba1b6d --- /dev/null +++ b/java/change-notes/2021-05-12-hardcoded-azure-credentials-in-api-call.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* The query "Hard-coded credential in API call" (`java/hardcoded-credential-api-call`) + now recognizes hard-coded authentication credentials passed to the Azure SDK for Java. diff --git a/java/change-notes/2021-05-24-hardcoded-shiro-key-in-api-call.md b/java/change-notes/2021-05-24-hardcoded-shiro-key-in-api-call.md new file mode 100644 index 00000000000..bb9872d650c --- /dev/null +++ b/java/change-notes/2021-05-24-hardcoded-shiro-key-in-api-call.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* The query "Hard-coded credential in API call" (`java/hardcoded-credential-api-call`) can now detect a hard-coded Apache Shiro cipher key. +* The query "Hard-coded credential in API call" (`java/hardcoded-credential-api-call`) now detects hard-coded credentials that are Base64 encoded or decoded before use. diff --git a/java/change-notes/2021-06-18-insecure-java-mail-query.md b/java/change-notes/2021-06-18-insecure-java-mail-query.md new file mode 100644 index 00000000000..e2778ec1b02 --- /dev/null +++ b/java/change-notes/2021-06-18-insecure-java-mail-query.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The query "Insecure JavaMail SSL Configuration" (`java/insecure-smtp-ssl`) has been promoted from experimental to the main query pack. Its results will now appear by default. This query was originally [submitted as an experimental query by @luchua-bc](https://github.com/github/codeql/pull/3491). diff --git a/java/change-notes/2021-08-10-gson-unsafe-deserialization.md b/java/change-notes/2021-08-10-gson-unsafe-deserialization.md new file mode 100644 index 00000000000..5e0c3f13966 --- /dev/null +++ b/java/change-notes/2021-08-10-gson-unsafe-deserialization.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The "Deserialization of user-controlled data" (`java/unsafe-deserialization`) query now recognizes deserialization using the `Gson` library. diff --git a/java/change-notes/2021-09-03-android-sensitive-broadcast.md b/java/change-notes/2021-09-03-android-sensitive-broadcast.md new file mode 100644 index 00000000000..283ff591e57 --- /dev/null +++ b/java/change-notes/2021-09-03-android-sensitive-broadcast.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The query "Leaking sensitive information through an implicit Intent" (`java/android/sensitive-communication`) has been promoted from experimental to the main query pack. Its results will now appear by default. The query was originally [submitted as an experimental query by @luchua-bc.](https://github.com/github/codeql/pull/4512) \ No newline at end of file diff --git a/java/change-notes/2021-10-07-java-util-stream.md b/java/change-notes/2021-10-07-java-util-stream.md new file mode 100644 index 00000000000..76ed485c279 --- /dev/null +++ b/java/change-notes/2021-10-07-java-util-stream.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Added data flow models for `java.util.stream.Stream`. diff --git a/java/change-notes/2021-10-20-more-specific-types.md b/java/change-notes/2021-10-20-more-specific-types.md new file mode 100644 index 00000000000..9b53ebfeef3 --- /dev/null +++ b/java/change-notes/2021-10-20-more-specific-types.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Some uses of `RefType` in the support for 'generics' and 'imports' now use the more specific `ClassOrInterface` instead diff --git a/java/documentation/library-coverage/coverage.csv b/java/documentation/library-coverage/coverage.csv index b0fa5f000d6..2bf5799b9f6 100644 --- a/java/documentation/library-coverage/coverage.csv +++ b/java/documentation/library-coverage/coverage.csv @@ -1,89 +1,101 @@ -package,sink,source,summary,sink:bean-validation,sink:create-file,sink:groovy,sink:header-splitting,sink:information-leak,sink:jexl,sink:jndi-injection,sink:ldap,sink:mvel,sink:ognl-injection,sink:open-url,sink:set-hostname-verifier,sink:sql,sink:url-open-stream,sink:url-redirect,sink:xpath,sink:xslt,sink:xss,source:remote,summary:taint,summary:value -android.content,8,,4,,,,,,,,,,,,,8,,,,,,,4, -android.database,59,,30,,,,,,,,,,,,,59,,,,,,,30, -android.net,,,60,,,,,,,,,,,,,,,,,,,,45,15 -android.util,,16,,,,,,,,,,,,,,,,,,,,16,, -android.webkit,3,2,,,,,,,,,,,,,,,,,,,3,2,, -com.esotericsoftware.kryo.io,,,1,,,,,,,,,,,,,,,,,,,,1, -com.esotericsoftware.kryo5.io,,,1,,,,,,,,,,,,,,,,,,,,1, -com.fasterxml.jackson.core,,,1,,,,,,,,,,,,,,,,,,,,1, -com.fasterxml.jackson.databind,,,5,,,,,,,,,,,,,,,,,,,,5, -com.google.common.base,,,85,,,,,,,,,,,,,,,,,,,,62,23 -com.google.common.cache,,,17,,,,,,,,,,,,,,,,,,,,,17 -com.google.common.collect,,,553,,,,,,,,,,,,,,,,,,,,2,551 -com.google.common.io,6,,73,,,,,,,,,,,,,,6,,,,,,72,1 -com.opensymphony.xwork2.ognl,3,,,,,,,,,,,,3,,,,,,,,,,, -com.unboundid.ldap.sdk,17,,,,,,,,,,17,,,,,,,,,,,,, -flexjson,,,1,,,,,,,,,,,,,,,,,,,,,1 -groovy.lang,26,,,,,26,,,,,,,,,,,,,,,,,, -groovy.util,5,,,,,5,,,,,,,,,,,,,,,,,, -jakarta.faces.context,2,7,,,,,,,,,,,,,,,,,,,2,7,, -jakarta.json,,,123,,,,,,,,,,,,,,,,,,,,100,23 -jakarta.ws.rs.client,1,,,,,,,,,,,,,1,,,,,,,,,, -jakarta.ws.rs.container,,9,,,,,,,,,,,,,,,,,,,,9,, -jakarta.ws.rs.core,2,,149,,,,,,,,,,,,,,,2,,,,,94,55 -java.beans,,,1,,,,,,,,,,,,,,,,,,,,1, -java.io,3,,27,,3,,,,,,,,,,,,,,,,,,26,1 -java.lang,,,47,,,,,,,,,,,,,,,,,,,,41,6 -java.net,10,3,7,,,,,,,,,,,10,,,,,,,,3,7, -java.nio,10,,4,,10,,,,,,,,,,,,,,,,,,4, -java.sql,7,,,,,,,,,,,,,,,7,,,,,,,, -java.util,,,337,,,,,,,,,,,,,,,,,,,,15,322 -javax.faces.context,2,7,,,,,,,,,,,,,,,,,,,2,7,, -javax.json,,,123,,,,,,,,,,,,,,,,,,,,100,23 -javax.management.remote,2,,,,,,,,,2,,,,,,,,,,,,,, -javax.naming,7,,,,,,,,,6,1,,,,,,,,,,,,, -javax.net.ssl,2,,,,,,,,,,,,,,2,,,,,,,,, -javax.script,1,,,,,,,,,,,1,,,,,,,,,,,, -javax.servlet,4,21,2,,,,3,1,,,,,,,,,,,,,,21,2, -javax.validation,1,1,,1,,,,,,,,,,,,,,,,,,1,, -javax.ws.rs.client,1,,,,,,,,,,,,,1,,,,,,,,,, -javax.ws.rs.container,,9,,,,,,,,,,,,,,,,,,,,9,, -javax.ws.rs.core,3,,149,,,,1,,,,,,,,,,,2,,,,,94,55 -javax.xml.transform,1,,6,,,,,,,,,,,,,,,,,1,,,6, -javax.xml.xpath,3,,,,,,,,,,,,,,,,,,3,,,,, -jodd.json,,,10,,,,,,,,,,,,,,,,,,,,,10 -net.sf.saxon.s9api,5,,,,,,,,,,,,,,,,,,,5,,,, -ognl,6,,,,,,,,,,,,6,,,,,,,,,,, -org.apache.commons.codec,,,6,,,,,,,,,,,,,,,,,,,,6, -org.apache.commons.collections,,,394,,,,,,,,,,,,,,,,,,,,9,385 -org.apache.commons.collections4,,,394,,,,,,,,,,,,,,,,,,,,9,385 -org.apache.commons.io,,,22,,,,,,,,,,,,,,,,,,,,22, -org.apache.commons.jexl2,15,,,,,,,,15,,,,,,,,,,,,,,, -org.apache.commons.jexl3,15,,,,,,,,15,,,,,,,,,,,,,,, -org.apache.commons.lang3,,,423,,,,,,,,,,,,,,,,,,,,292,131 -org.apache.commons.ognl,6,,,,,,,,,,,,6,,,,,,,,,,, -org.apache.commons.text,,,272,,,,,,,,,,,,,,,,,,,,220,52 -org.apache.directory.ldap.client.api,1,,,,,,,,,,1,,,,,,,,,,,,, -org.apache.hc.core5.function,,,1,,,,,,,,,,,,,,,,,,,,1, -org.apache.hc.core5.http,1,2,39,,,,,,,,,,,,,,,,,,1,2,39, -org.apache.hc.core5.net,,,2,,,,,,,,,,,,,,,,,,,,2, -org.apache.hc.core5.util,,,24,,,,,,,,,,,,,,,,,,,,18,6 -org.apache.http,27,3,70,,,,,,,,,,,25,,,,,,,2,3,62,8 -org.apache.ibatis.jdbc,6,,,,,,,,,,,,,,,6,,,,,,,, -org.apache.shiro.jndi,1,,,,,,,,,1,,,,,,,,,,,,,, -org.codehaus.groovy.control,1,,,,,1,,,,,,,,,,,,,,,,,, -org.dom4j,20,,,,,,,,,,,,,,,,,,20,,,,, -org.hibernate,7,,,,,,,,,,,,,,,7,,,,,,,, -org.jooq,1,,,,,,,,,,,,,,,1,,,,,,,, -org.json,,,236,,,,,,,,,,,,,,,,,,,,198,38 -org.mvel2,16,,,,,,,,,,,16,,,,,,,,,,,, -org.springframework.beans,,,26,,,,,,,,,,,,,,,,,,,,,26 -org.springframework.cache,,,13,,,,,,,,,,,,,,,,,,,,,13 -org.springframework.http,14,,70,,,,,,,,,,,14,,,,,,,,,60,10 -org.springframework.jdbc.core,10,,,,,,,,,,,,,,,10,,,,,,,, -org.springframework.jdbc.object,9,,,,,,,,,,,,,,,9,,,,,,,, -org.springframework.jndi,1,,,,,,,,,1,,,,,,,,,,,,,, -org.springframework.ldap,42,,,,,,,,,28,14,,,,,,,,,,,,, -org.springframework.security.web.savedrequest,,6,,,,,,,,,,,,,,,,,,,,6,, -org.springframework.ui,,,32,,,,,,,,,,,,,,,,,,,,,32 -org.springframework.util,,,139,,,,,,,,,,,,,,,,,,,,87,52 -org.springframework.validation,,,13,,,,,,,,,,,,,,,,,,,,13, -org.springframework.web.client,13,3,,,,,,,,,,,,13,,,,,,,,3,, -org.springframework.web.context.request,,8,,,,,,,,,,,,,,,,,,,,8,, -org.springframework.web.multipart,,12,13,,,,,,,,,,,,,,,,,,,12,13, -org.springframework.web.reactive.function.client,2,,,,,,,,,,,,,2,,,,,,,,,, -org.springframework.web.util,,,163,,,,,,,,,,,,,,,,,,,,138,25 -org.xml.sax,,,1,,,,,,,,,,,,,,,,,,,,1, -org.xmlpull.v1,,3,,,,,,,,,,,,,,,,,,,,3,, -play.mvc,,4,,,,,,,,,,,,,,,,,,,,4,, +package,sink,source,summary,sink:bean-validation,sink:create-file,sink:groovy,sink:header-splitting,sink:information-leak,sink:jexl,sink:jndi-injection,sink:ldap,sink:mvel,sink:ognl-injection,sink:open-url,sink:set-hostname-verifier,sink:sql,sink:url-open-stream,sink:url-redirect,sink:xpath,sink:xslt,sink:xss,source:contentprovider,source:remote,summary:taint,summary:value +android.content,8,27,73,,,,,,,,,,,,,8,,,,,,27,,8,65 +android.database,59,,30,,,,,,,,,,,,,59,,,,,,,,30, +android.net,,,60,,,,,,,,,,,,,,,,,,,,,45,15 +android.os,,,122,,,,,,,,,,,,,,,,,,,,,41,81 +android.util,,16,,,,,,,,,,,,,,,,,,,,,16,, +android.webkit,3,2,,,,,,,,,,,,,,,,,,,3,,2,, +cn.hutool.core.codec,,,1,,,,,,,,,,,,,,,,,,,,,1, +com.esotericsoftware.kryo.io,,,1,,,,,,,,,,,,,,,,,,,,,1, +com.esotericsoftware.kryo5.io,,,1,,,,,,,,,,,,,,,,,,,,,1, +com.fasterxml.jackson.core,,,1,,,,,,,,,,,,,,,,,,,,,1, +com.fasterxml.jackson.databind,,,6,,,,,,,,,,,,,,,,,,,,,6, +com.google.common.base,,,85,,,,,,,,,,,,,,,,,,,,,62,23 +com.google.common.cache,,,17,,,,,,,,,,,,,,,,,,,,,,17 +com.google.common.collect,,,553,,,,,,,,,,,,,,,,,,,,,2,551 +com.google.common.io,6,,73,,,,,,,,,,,,,,6,,,,,,,72,1 +com.opensymphony.xwork2.ognl,3,,,,,,,,,,,,3,,,,,,,,,,,, +com.unboundid.ldap.sdk,17,,,,,,,,,,17,,,,,,,,,,,,,, +flexjson,,,1,,,,,,,,,,,,,,,,,,,,,,1 +groovy.lang,26,,,,,26,,,,,,,,,,,,,,,,,,, +groovy.util,5,,,,,5,,,,,,,,,,,,,,,,,,, +jakarta.faces.context,2,7,,,,,,,,,,,,,,,,,,,2,,7,, +jakarta.json,,,123,,,,,,,,,,,,,,,,,,,,,100,23 +jakarta.ws.rs.client,1,,,,,,,,,,,,,1,,,,,,,,,,, +jakarta.ws.rs.container,,9,,,,,,,,,,,,,,,,,,,,,9,, +jakarta.ws.rs.core,2,,149,,,,,,,,,,,,,,,2,,,,,,94,55 +java.beans,,,1,,,,,,,,,,,,,,,,,,,,,1, +java.io,3,,27,,3,,,,,,,,,,,,,,,,,,,26,1 +java.lang,,,51,,,,,,,,,,,,,,,,,,,,,41,10 +java.net,10,3,7,,,,,,,,,,,10,,,,,,,,,3,7, +java.nio,10,,4,,10,,,,,,,,,,,,,,,,,,,4, +java.sql,7,,,,,,,,,,,,,,,7,,,,,,,,, +java.util,,,420,,,,,,,,,,,,,,,,,,,,,15,405 +javax.faces.context,2,7,,,,,,,,,,,,,,,,,,,2,,7,, +javax.json,,,123,,,,,,,,,,,,,,,,,,,,,100,23 +javax.management.remote,2,,,,,,,,,2,,,,,,,,,,,,,,, +javax.naming,7,,,,,,,,,6,1,,,,,,,,,,,,,, +javax.net.ssl,2,,,,,,,,,,,,,,2,,,,,,,,,, +javax.script,1,,,,,,,,,,,1,,,,,,,,,,,,, +javax.servlet,4,21,2,,,,3,1,,,,,,,,,,,,,,,21,2, +javax.validation,1,1,,1,,,,,,,,,,,,,,,,,,,1,, +javax.ws.rs.client,1,,,,,,,,,,,,,1,,,,,,,,,,, +javax.ws.rs.container,,9,,,,,,,,,,,,,,,,,,,,,9,, +javax.ws.rs.core,3,,149,,,,1,,,,,,,,,,,2,,,,,,94,55 +javax.xml.transform,1,,6,,,,,,,,,,,,,,,,,1,,,,6, +javax.xml.xpath,3,,,,,,,,,,,,,,,,,,3,,,,,, +jodd.json,,,10,,,,,,,,,,,,,,,,,,,,,,10 +net.sf.saxon.s9api,5,,,,,,,,,,,,,,,,,,,5,,,,, +ognl,6,,,,,,,,,,,,6,,,,,,,,,,,, +org.apache.commons.codec,,,6,,,,,,,,,,,,,,,,,,,,,6, +org.apache.commons.collections,,,800,,,,,,,,,,,,,,,,,,,,,17,783 +org.apache.commons.collections4,,,800,,,,,,,,,,,,,,,,,,,,,17,783 +org.apache.commons.io,,,22,,,,,,,,,,,,,,,,,,,,,22, +org.apache.commons.jexl2,15,,,,,,,,15,,,,,,,,,,,,,,,, +org.apache.commons.jexl3,15,,,,,,,,15,,,,,,,,,,,,,,,, +org.apache.commons.lang3,,,423,,,,,,,,,,,,,,,,,,,,,292,131 +org.apache.commons.ognl,6,,,,,,,,,,,,6,,,,,,,,,,,, +org.apache.commons.text,,,272,,,,,,,,,,,,,,,,,,,,,220,52 +org.apache.directory.ldap.client.api,1,,,,,,,,,,1,,,,,,,,,,,,,, +org.apache.hc.core5.function,,,1,,,,,,,,,,,,,,,,,,,,,1, +org.apache.hc.core5.http,1,2,39,,,,,,,,,,,,,,,,,,1,,2,39, +org.apache.hc.core5.net,,,2,,,,,,,,,,,,,,,,,,,,,2, +org.apache.hc.core5.util,,,24,,,,,,,,,,,,,,,,,,,,,18,6 +org.apache.http,27,3,70,,,,,,,,,,,25,,,,,,,2,,3,62,8 +org.apache.ibatis.jdbc,6,,,,,,,,,,,,,,,6,,,,,,,,, +org.apache.shiro.codec,,,1,,,,,,,,,,,,,,,,,,,,,1, +org.apache.shiro.jndi,1,,,,,,,,,1,,,,,,,,,,,,,,, +org.codehaus.groovy.control,1,,,,,1,,,,,,,,,,,,,,,,,,, +org.dom4j,20,,,,,,,,,,,,,,,,,,20,,,,,, +org.hibernate,7,,,,,,,,,,,,,,,7,,,,,,,,, +org.jooq,1,,,,,,,,,,,,,,,1,,,,,,,,, +org.json,,,236,,,,,,,,,,,,,,,,,,,,,198,38 +org.mvel2,16,,,,,,,,,,,16,,,,,,,,,,,,, +org.springframework.beans,,,26,,,,,,,,,,,,,,,,,,,,,,26 +org.springframework.cache,,,13,,,,,,,,,,,,,,,,,,,,,,13 +org.springframework.http,14,,70,,,,,,,,,,,14,,,,,,,,,,60,10 +org.springframework.jdbc.core,10,,,,,,,,,,,,,,,10,,,,,,,,, +org.springframework.jdbc.object,9,,,,,,,,,,,,,,,9,,,,,,,,, +org.springframework.jndi,1,,,,,,,,,1,,,,,,,,,,,,,,, +org.springframework.ldap,42,,,,,,,,,28,14,,,,,,,,,,,,,, +org.springframework.security.web.savedrequest,,6,,,,,,,,,,,,,,,,,,,,,6,, +org.springframework.ui,,,32,,,,,,,,,,,,,,,,,,,,,,32 +org.springframework.util,,,139,,,,,,,,,,,,,,,,,,,,,87,52 +org.springframework.validation,,,13,,,,,,,,,,,,,,,,,,,,,13, +org.springframework.web.client,13,3,,,,,,,,,,,,13,,,,,,,,,3,, +org.springframework.web.context.request,,8,,,,,,,,,,,,,,,,,,,,,8,, +org.springframework.web.multipart,,12,13,,,,,,,,,,,,,,,,,,,,12,13, +org.springframework.web.reactive.function.client,2,,,,,,,,,,,,,2,,,,,,,,,,, +org.springframework.web.util,,,163,,,,,,,,,,,,,,,,,,,,,138,25 +org.xml.sax,,,1,,,,,,,,,,,,,,,,,,,,,1, +org.xmlpull.v1,,3,,,,,,,,,,,,,,,,,,,,,3,, +play.mvc,,4,,,,,,,,,,,,,,,,,,,,,4,, +ratpack.core.form,,,3,,,,,,,,,,,,,,,,,,,,,3, +ratpack.core.handling,,6,4,,,,,,,,,,,,,,,,,,,,6,4, +ratpack.core.http,,10,10,,,,,,,,,,,,,,,,,,,,10,10, +ratpack.exec,,,26,,,,,,,,,,,,,,,,,,,,,,26 +ratpack.form,,,3,,,,,,,,,,,,,,,,,,,,,3, +ratpack.func,,,5,,,,,,,,,,,,,,,,,,,,,,5 +ratpack.handling,,6,4,,,,,,,,,,,,,,,,,,,,6,4, +ratpack.http,,10,10,,,,,,,,,,,,,,,,,,,,10,10, +ratpack.util,,,5,,,,,,,,,,,,,,,,,,,,,,5 diff --git a/java/documentation/library-coverage/coverage.rst b/java/documentation/library-coverage/coverage.rst index 6782fd3e515..08fab8d5e36 100644 --- a/java/documentation/library-coverage/coverage.rst +++ b/java/documentation/library-coverage/coverage.rst @@ -7,17 +7,17 @@ Java framework & library support :widths: auto Framework / library,Package,Flow sources,Taint & value steps,Sinks (total),`CWE‑022` :sub:`Path injection`,`CWE‑036` :sub:`Path traversal`,`CWE‑079` :sub:`Cross-site scripting`,`CWE‑089` :sub:`SQL injection`,`CWE‑090` :sub:`LDAP injection`,`CWE‑094` :sub:`Code injection`,`CWE‑319` :sub:`Cleartext transmission` - Android,``android.*``,18,94,70,,,3,67,,, - `Apache Commons Collections `_,"``org.apache.commons.collections``, ``org.apache.commons.collections4``",,788,,,,,,,, + Android,``android.*``,45,285,70,,,3,67,,, + `Apache Commons Collections `_,"``org.apache.commons.collections``, ``org.apache.commons.collections4``",,1600,,,,,,,, `Apache Commons IO `_,``org.apache.commons.io``,,22,,,,,,,, `Apache Commons Lang `_,``org.apache.commons.lang3``,,423,,,,,,,, `Apache Commons Text `_,``org.apache.commons.text``,,272,,,,,,,, `Apache HttpComponents `_,"``org.apache.hc.core5.*``, ``org.apache.http``",5,136,28,,,3,,,,25 `Google Guava `_,``com.google.common.*``,,728,6,,6,,,,, `JSON-java `_,``org.json``,,236,,,,,,,, - Java Standard Library,``java.*``,3,423,30,13,,,7,,,10 + Java Standard Library,``java.*``,3,510,30,13,,,7,,,10 Java extensions,"``javax.*``, ``jakarta.*``",54,552,32,,,4,,1,1,2 `Spring `_,``org.springframework.*``,29,469,91,,,,19,14,,29 - Others,"``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.opensymphony.xwork2.ognl``, ``com.unboundid.ldap.sdk``, ``flexjson``, ``groovy.lang``, ``groovy.util``, ``jodd.json``, ``net.sf.saxon.s9api``, ``ognl``, ``org.apache.commons.codec``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.ibatis.jdbc``, ``org.apache.shiro.jndi``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.hibernate``, ``org.jooq``, ``org.mvel2``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``",7,26,151,,,,14,18,, - Totals,,116,4169,408,13,6,10,107,33,1,66 + Others,"``cn.hutool.core.codec``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.opensymphony.xwork2.ognl``, ``com.unboundid.ldap.sdk``, ``flexjson``, ``groovy.lang``, ``groovy.util``, ``jodd.json``, ``net.sf.saxon.s9api``, ``ognl``, ``org.apache.commons.codec``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.ibatis.jdbc``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.hibernate``, ``org.jooq``, ``org.mvel2``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``",39,99,151,,,,14,18,, + Totals,,175,5332,408,13,6,10,107,33,1,66 diff --git a/java/ql/examples/qlpack.yml b/java/ql/examples/qlpack.yml index 37542a63150..8033757261f 100644 --- a/java/ql/examples/qlpack.yml +++ b/java/ql/examples/qlpack.yml @@ -1,4 +1,4 @@ -name: codeql-java-examples +name: codeql/java-examples version: 0.0.2 dependencies: codeql/java-all: "*" diff --git a/java/ql/lib/config/semmlecode.dbscheme b/java/ql/lib/config/semmlecode.dbscheme index 017ac1ed2df..89a76edebff 100755 --- a/java/ql/lib/config/semmlecode.dbscheme +++ b/java/ql/lib/config/semmlecode.dbscheme @@ -393,7 +393,7 @@ typeVars( string nodeName: string ref, int pos: int ref, int kind: int ref, // deprecated - int parentid: @typeorcallable ref + int parentid: @classorinterfaceorcallable ref ); wildcards( @@ -414,7 +414,7 @@ typeBounds( typeArgs( int argumentid: @reftype ref, int pos: int ref, - int parentid: @typeorcallable ref + int parentid: @classorinterfaceorcallable ref ); isParameterized( @@ -487,7 +487,7 @@ hasModifier( imports( unique int id: @import, - int holder: @typeorpackage ref, + int holder: @classorinterfaceorpackage ref, string name: string ref, int kind: int ref ); @@ -857,10 +857,9 @@ javadocText( @javadocParent = @javadoc | @javadocTag; @javadocElement = @javadocTag | @javadocText; -@typeorpackage = @type | @package; - -@typeorcallable = @type | @callable; @classorinterface = @interface | @class; +@classorinterfaceorpackage = @classorinterface | @package; +@classorinterfaceorcallable = @classorinterface | @callable; @boundedtype = @typevariable | @wildcard; @reftype = @classorinterface | @array | @boundedtype; @classorarray = @class | @array; diff --git a/java/ql/lib/external/ExternalArtifact.qll b/java/ql/lib/external/ExternalArtifact.qll index 5359b99c7c8..2e782a6a4da 100644 --- a/java/ql/lib/external/ExternalArtifact.qll +++ b/java/ql/lib/external/ExternalArtifact.qll @@ -3,24 +3,25 @@ import java class ExternalData extends @externalDataElement { string getDataPath() { externalData(this, result, _, _) } - string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") } + string getQueryPath() { result = this.getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") } int getNumFields() { result = 1 + max(int i | externalData(this, _, i, _) | i) } string getField(int index) { externalData(this, _, index, result) } - int getFieldAsInt(int index) { result = getField(index).toInt() } + int getFieldAsInt(int index) { result = this.getField(index).toInt() } - float getFieldAsFloat(int index) { result = getField(index).toFloat() } + float getFieldAsFloat(int index) { result = this.getField(index).toFloat() } - date getFieldAsDate(int index) { result = getField(index).toDate() } + date getFieldAsDate(int index) { result = this.getField(index).toDate() } - string toString() { result = getQueryPath() + ": " + buildTupleString(0) } + string toString() { result = this.getQueryPath() + ": " + this.buildTupleString(0) } private string buildTupleString(int start) { - start = getNumFields() - 1 and result = getField(start) + start = this.getNumFields() - 1 and result = this.getField(start) or - start < getNumFields() - 1 and result = getField(start) + "," + buildTupleString(start + 1) + start < this.getNumFields() - 1 and + result = this.getField(start) + "," + this.buildTupleString(start + 1) } } @@ -33,7 +34,7 @@ class DefectExternalData extends ExternalData { this.getNumFields() = 2 } - string getURL() { result = getField(0) } + string getURL() { result = this.getField(0) } - string getMessage() { result = getField(1) } + string getMessage() { result = this.getField(1) } } diff --git a/java/ql/lib/semmle/code/FileSystem.qll b/java/ql/lib/semmle/code/FileSystem.qll index 89891acf622..cace20f63e1 100755 --- a/java/ql/lib/semmle/code/FileSystem.qll +++ b/java/ql/lib/semmle/code/FileSystem.qll @@ -33,7 +33,7 @@ class Container extends @container, Top { /** * Gets a URL representing the location of this container. * - * For more information see [Providing URLs](https://help.semmle.com/QL/learn-ql/ql/locations.html#providing-urls). + * For more information see [Providing URLs](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/#providing-urls). */ abstract string getURL(); @@ -47,7 +47,7 @@ class Container extends @container, Top { */ string getRelativePath() { exists(string absPath, string pref | - absPath = getAbsolutePath() and sourceLocationPrefix(pref) + absPath = this.getAbsolutePath() and sourceLocationPrefix(pref) | absPath = pref and result = "" or @@ -74,7 +74,7 @@ class Container extends @container, Top { * */ string getBaseName() { - result = getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1) + result = this.getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1) } /** @@ -100,7 +100,9 @@ class Container extends @container, Top { * "/tmp/x.tar.gz""gz" * */ - string getExtension() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) } + string getExtension() { + result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) + } /** * Gets the stem of this container, that is, the prefix of its base name up to @@ -119,7 +121,9 @@ class Container extends @container, Top { * "/tmp/x.tar.gz""x.tar" * */ - string getStem() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) } + string getStem() { + result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) + } /** Gets the parent container of this file or folder, if any. */ Container getParentContainer() { containerparent(result, this) } @@ -128,20 +132,20 @@ class Container extends @container, Top { Container getAChildContainer() { this = result.getParentContainer() } /** Gets a file in this container. */ - File getAFile() { result = getAChildContainer() } + File getAFile() { result = this.getAChildContainer() } /** Gets the file in this container that has the given `baseName`, if any. */ File getFile(string baseName) { - result = getAFile() and + result = this.getAFile() and result.getBaseName() = baseName } /** Gets a sub-folder in this container. */ - Folder getAFolder() { result = getAChildContainer() } + Folder getAFolder() { result = this.getAChildContainer() } /** Gets the sub-folder in this container that has the given `baseName`, if any. */ Folder getFolder(string baseName) { - result = getAFolder() and + result = this.getAFolder() and result.getBaseName() = baseName } @@ -152,7 +156,7 @@ class Container extends @container, Top { * to provide a different result. To get the absolute path of any `Container`, call * `Container.getAbsolutePath()` directly. */ - override string toString() { result = getAbsolutePath() } + override string toString() { result = this.getAbsolutePath() } } /** A folder. */ @@ -160,7 +164,7 @@ class Folder extends Container, @folder { override string getAbsolutePath() { folders(this, result) } /** Gets the URL of this folder. */ - override string getURL() { result = "folder://" + getAbsolutePath() } + override string getURL() { result = "folder://" + this.getAbsolutePath() } override string getAPrimaryQlClass() { result = "Folder" } } @@ -183,7 +187,7 @@ class File extends Container, @file { * A Java archive file with a ".jar" extension. */ class JarFile extends File { - JarFile() { getExtension() = "jar" } + JarFile() { this.getExtension() = "jar" } /** * Gets the main attribute with the specified `key` @@ -195,13 +199,17 @@ class JarFile extends File { * Gets the "Specification-Version" main attribute * from this JAR file's manifest. */ - string getSpecificationVersion() { result = getManifestMainAttribute("Specification-Version") } + string getSpecificationVersion() { + result = this.getManifestMainAttribute("Specification-Version") + } /** * Gets the "Implementation-Version" main attribute * from this JAR file's manifest. */ - string getImplementationVersion() { result = getManifestMainAttribute("Implementation-Version") } + string getImplementationVersion() { + result = this.getManifestMainAttribute("Implementation-Version") + } /** * Gets the per-entry attribute for the specified `entry` and `key` diff --git a/java/ql/lib/semmle/code/Location.qll b/java/ql/lib/semmle/code/Location.qll index b1275f44351..d90a189acb7 100755 --- a/java/ql/lib/semmle/code/Location.qll +++ b/java/ql/lib/semmle/code/Location.qll @@ -58,15 +58,15 @@ class Top extends @top { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { - hasLocationInfoAux(filepath, startline, startcolumn, endline, endcolumn) + this.hasLocationInfoAux(filepath, startline, startcolumn, endline, endcolumn) or exists(string outFilepath, int outStartline, int outEndline | - hasLocationInfoAux(outFilepath, outStartline, _, outEndline, _) and + this.hasLocationInfoAux(outFilepath, outStartline, _, outEndline, _) and hasSmapLocationInfo(filepath, startline, startcolumn, endline, endcolumn, outFilepath, outStartline, outEndline) ) @@ -103,7 +103,7 @@ class Top extends @top { /** * Gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs. */ - final string getPrimaryQlClasses() { result = concat(getAPrimaryQlClass(), ",") } + final string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") } /** * Gets the name of a primary CodeQL class to which this element belongs. @@ -180,7 +180,7 @@ class Location extends @location { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/java/ql/lib/semmle/code/SMAP.qll b/java/ql/lib/semmle/code/SMAP.qll index 006f5ad5e38..575d54f92de 100644 --- a/java/ql/lib/semmle/code/SMAP.qll +++ b/java/ql/lib/semmle/code/SMAP.qll @@ -52,6 +52,6 @@ predicate hasSmapLocationInfo( smap(inputFile, isl, outputFile, osl) and smap(inputFile, iel - 1, outputFile, oel) and isc = 1 and - iec = 0 + iec = 1 ) } diff --git a/java/ql/lib/semmle/code/java/Annotation.qll b/java/ql/lib/semmle/code/java/Annotation.qll index 2ee7c6403b4..342a2bd6e0d 100755 --- a/java/ql/lib/semmle/code/java/Annotation.qll +++ b/java/ql/lib/semmle/code/java/Annotation.qll @@ -51,7 +51,7 @@ class Annotation extends @annotation, Expr { Expr getValue(string name) { filteredAnnotValue(this, this.getAnnotationElement(name), result) } /** Gets the element being annotated. */ - Element getTarget() { result = getAnnotatedElement() } + Element getTarget() { result = this.getAnnotatedElement() } override string toString() { result = this.getType().getName() } @@ -67,8 +67,8 @@ class Annotation extends @annotation, Expr { * expression defined for the value. */ Expr getAValue(string name) { - getType().getAnnotationElement(name).getType() instanceof Array and - exists(Expr value | value = getValue(name) | + this.getType().getAnnotationElement(name).getType() instanceof Array and + exists(Expr value | value = this.getValue(name) | if value instanceof ArrayInit then result = value.(ArrayInit).getAnInit() else result = value ) } @@ -104,7 +104,7 @@ class Annotatable extends Element { /** Holds if this element has the specified annotation. */ predicate hasAnnotation(string package, string name) { - exists(AnnotationType at | at = getAnAnnotation().getType() | + exists(AnnotationType at | at = this.getAnAnnotation().getType() | at.nestedName() = name and at.getPackage().getName() = package ) } @@ -118,7 +118,7 @@ class Annotatable extends Element { * annotation attached to it for the specified `category`. */ predicate suppressesWarningsAbout(string category) { - category = getAnAnnotation().(SuppressWarningsAnnotation).getASuppressedWarning() + category = this.getAnAnnotation().(SuppressWarningsAnnotation).getASuppressedWarning() or this.(Member).getDeclaringType().suppressesWarningsAbout(category) or diff --git a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll index ff60abb4e73..8e5325902dd 100644 --- a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll +++ b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll @@ -528,13 +528,13 @@ private module ControlFlowGraphImpl { /** Gets the first child node, if any. */ ControlFlowNode firstChild() { - result = getChildNode(-1) + result = this.getChildNode(-1) or - result = getChildNode(0) and not exists(getChildNode(-1)) + result = this.getChildNode(0) and not exists(this.getChildNode(-1)) } /** Holds if this CFG node has any child nodes. */ - predicate isLeafNode() { not exists(getChildNode(_)) } + predicate isLeafNode() { not exists(this.getChildNode(_)) } /** Holds if this node can finish with a `normalCompletion`. */ predicate mayCompleteNormally() { @@ -1222,10 +1222,10 @@ class ConditionNode extends ControlFlowNode { ControlFlowNode getABranchSuccessor(boolean branch) { result = branchSuccessor(this, branch) } /** Gets a true-successor of the `ConditionNode`. */ - ControlFlowNode getATrueSuccessor() { result = getABranchSuccessor(true) } + ControlFlowNode getATrueSuccessor() { result = this.getABranchSuccessor(true) } /** Gets a false-successor of the `ConditionNode`. */ - ControlFlowNode getAFalseSuccessor() { result = getABranchSuccessor(false) } + ControlFlowNode getAFalseSuccessor() { result = this.getABranchSuccessor(false) } /** Gets the condition of this `ConditionNode`. This is equal to the node itself. */ Expr getCondition() { result = this } diff --git a/java/ql/lib/semmle/code/java/Conversions.qll b/java/ql/lib/semmle/code/java/Conversions.qll index 9d55f1297fc..b7cd80c4906 100644 --- a/java/ql/lib/semmle/code/java/Conversions.qll +++ b/java/ql/lib/semmle/code/java/Conversions.qll @@ -27,7 +27,7 @@ abstract class ConversionSite extends Expr { /** * Whether this conversion site actually induces a conversion. */ - predicate isTrivial() { getConversionTarget() = getConversionSource() } + predicate isTrivial() { this.getConversionTarget() = this.getConversionSource() } /** * Whether this conversion is implicit. diff --git a/java/ql/lib/semmle/code/java/Element.qll b/java/ql/lib/semmle/code/java/Element.qll index 12a08f8eb9d..14e48fc0d40 100755 --- a/java/ql/lib/semmle/code/java/Element.qll +++ b/java/ql/lib/semmle/code/java/Element.qll @@ -34,10 +34,10 @@ class Element extends @element, Top { * Elements pertaining to source files may include generated elements * not visible in source code, such as implicit default constructors. */ - predicate fromSource() { getCompilationUnit().getExtension() = "java" } + predicate fromSource() { this.getCompilationUnit().getExtension() = "java" } /** Gets the compilation unit that this element belongs to. */ - CompilationUnit getCompilationUnit() { result = getFile() } + CompilationUnit getCompilationUnit() { result = this.getFile() } /** Cast this element to a `Documentable`. */ Documentable getDoc() { result = this } diff --git a/java/ql/lib/semmle/code/java/Expr.qll b/java/ql/lib/semmle/code/java/Expr.qll index 3e5d54e6ac6..6a329d62a04 100755 --- a/java/ql/lib/semmle/code/java/Expr.qll +++ b/java/ql/lib/semmle/code/java/Expr.qll @@ -86,13 +86,15 @@ class Expr extends ExprParent, @expr { * explicit constructor invocation statement. */ - getEnclosingCallable().isStatic() + this.getEnclosingCallable().isStatic() or - getParent+() instanceof ThisConstructorInvocationStmt + this.getParent+() instanceof ThisConstructorInvocationStmt or - getParent+() instanceof SuperConstructorInvocationStmt + this.getParent+() instanceof SuperConstructorInvocationStmt or - exists(LambdaExpr lam | lam.asMethod() = getEnclosingCallable() and lam.isInStaticContext()) + exists(LambdaExpr lam | + lam.asMethod() = this.getEnclosingCallable() and lam.isInStaticContext() + ) } /** Holds if this expression is parenthesized. */ @@ -116,7 +118,7 @@ private predicate primitiveOrString(Type t) { */ class CompileTimeConstantExpr extends Expr { CompileTimeConstantExpr() { - primitiveOrString(getType()) and + primitiveOrString(this.getType()) and ( // Literals of primitive type and literals of type `String`. this instanceof Literal @@ -425,9 +427,9 @@ class ArrayCreationExpr extends Expr, @arraycreationexpr { * Gets the size of the first dimension, if it can be statically determined. */ int getFirstDimensionSize() { - if exists(getInit()) - then result = getInit().getSize() - else result = getDimension(0).(CompileTimeConstantExpr).getIntValue() + if exists(this.getInit()) + then result = this.getInit().getSize() + else result = this.getDimension(0).(CompileTimeConstantExpr).getIntValue() } /** Gets a printable representation of this expression. */ @@ -463,7 +465,7 @@ class ArrayInit extends Expr, @arrayinit { * Gets the number of expressions in this initializer, that is, the size the * created array will have. */ - int getSize() { result = count(getAnInit()) } + int getSize() { result = count(this.getAnInit()) } /** Gets a printable representation of this expression. */ override string toString() { result = "{...}" } @@ -632,9 +634,9 @@ class Literal extends Expr, @literal { class BooleanLiteral extends Literal, @booleanliteral { /** Gets the boolean representation of this literal. */ boolean getBooleanValue() { - result = true and getValue() = "true" + result = true and this.getValue() = "true" or - result = false and getValue() = "false" + result = false and this.getValue() = "false" } override string getAPrimaryQlClass() { result = "BooleanLiteral" } @@ -657,7 +659,7 @@ class BooleanLiteral extends Literal, @booleanliteral { */ class IntegerLiteral extends Literal, @integerliteral { /** Gets the int representation of this literal. */ - int getIntValue() { result = getValue().toInt() } + int getIntValue() { result = this.getValue().toInt() } override string getAPrimaryQlClass() { result = "IntegerLiteral" } } @@ -693,7 +695,7 @@ class FloatingPointLiteral extends Literal, @floatingpointliteral { * Gets the value of this literal as CodeQL 64-bit `float`. The value will * be parsed as Java 32-bit `float` and then converted to a CodeQL `float`. */ - float getFloatValue() { result = getValue().toFloat() } + float getFloatValue() { result = this.getValue().toFloat() } override string getAPrimaryQlClass() { result = "FloatingPointLiteral" } } @@ -709,7 +711,7 @@ class DoubleLiteral extends Literal, @doubleliteral { * Gets the value of this literal as CodeQL 64-bit `float`. The result will * have the same effective value as the Java `double` literal. */ - float getDoubleValue() { result = getValue().toFloat() } + float getDoubleValue() { result = this.getValue().toFloat() } override string getAPrimaryQlClass() { result = "DoubleLiteral" } } @@ -719,21 +721,30 @@ class CharacterLiteral extends Literal, @characterliteral { override string getAPrimaryQlClass() { result = "CharacterLiteral" } } -/** A string literal. For example, `"hello world"`. */ +/** + * A string literal or text block (Java 15 feature). For example, `"hello world"` + * or + * ```java + * """ + * Text with "quotes" + * """ + * ``` + */ class StringLiteral extends Literal, @stringliteral { /** * Gets the literal string without the quotes. */ - string getRepresentedString() { result = getValue() } + string getRepresentedString() { result = this.getValue() } /** Holds if this string literal is a text block (`""" ... """`). */ - predicate isTextBlock() { getLiteral().matches("\"\"\"%") } + predicate isTextBlock() { this.getLiteral().matches("\"\"\"%") } override string getAPrimaryQlClass() { result = "StringLiteral" } } /** The null literal, written `null`. */ class NullLiteral extends Literal, @nullliteral { + // Override these predicates because the inherited ones have no result override string getLiteral() { result = "null" } override string getValue() { result = "null" } @@ -1175,7 +1186,7 @@ class LambdaExpr extends FunctionalExpr, @lambdaexpr { * Gets the implicit method corresponding to this lambda expression. * The parameters of the lambda expression are the parameters of this method. */ - override Method asMethod() { result = getAnonymousClass().getAMethod() } + override Method asMethod() { result = this.getAnonymousClass().getAMethod() } /** Holds if the body of this lambda is an expression. */ predicate hasExprBody() { lambdaKind(this, 0) } @@ -1185,11 +1196,11 @@ class LambdaExpr extends FunctionalExpr, @lambdaexpr { /** Gets the body of this lambda expression, if it is an expression. */ Expr getExprBody() { - hasExprBody() and result = asMethod().getBody().getAChild().(ReturnStmt).getResult() + this.hasExprBody() and result = this.asMethod().getBody().getAChild().(ReturnStmt).getResult() } /** Gets the body of this lambda expression, if it is a statement. */ - BlockStmt getStmtBody() { hasStmtBody() and result = asMethod().getBody() } + BlockStmt getStmtBody() { this.hasStmtBody() and result = this.asMethod().getBody() } /** Gets a printable representation of this expression. */ override string toString() { result = "...->..." } @@ -1214,7 +1225,29 @@ class MemberRefExpr extends FunctionalExpr, @memberref { * (if the reference is to a constructor) or an array creation expression (if the reference * is to an array constructor). */ - override Method asMethod() { result = getAnonymousClass().getAMethod() } + override Method asMethod() { result = this.getAnonymousClass().getAMethod() } + + /** + * Gets the receiver type whose member this expression refers to. The result might not be + * the type which actually declares the member. For example, for the member reference `ArrayList::toString`, + * this predicate has the result `java.util.ArrayList`, the type explicitly referred to, while + * `getReferencedCallable` will have `java.util.AbstractCollection.toString` as result, which `ArrayList` inherits. + */ + RefType getReceiverType() { + exists(Stmt stmt, Expr resultExpr | + stmt = asMethod().getBody().(SingletonBlock).getStmt() and + ( + resultExpr = stmt.(ReturnStmt).getResult() + or + // Note: Currently never an ExprStmt, but might change once https://github.com/github/codeql/issues/3605 is fixed + resultExpr = stmt.(ExprStmt).getExpr() + ) + | + result = resultExpr.(MethodAccess).getReceiverType() or + result = resultExpr.(ClassInstanceExpr).getConstructedType() or + result = resultExpr.(ArrayCreationExpr).getType() + ) + } /** * Gets the method or constructor referenced by this member reference expression. @@ -1265,16 +1298,16 @@ class ConditionalExpr extends Expr, @conditionalexpr { * it is `getFalseExpr()`. */ Expr getBranchExpr(boolean branch) { - branch = true and result = getTrueExpr() + branch = true and result = this.getTrueExpr() or - branch = false and result = getFalseExpr() + branch = false and result = this.getFalseExpr() } /** * Gets the expressions that is evaluated by one of the branches (`true` * or `false` branch) of this conditional expression. */ - Expr getABranchExpr() { result = getBranchExpr(_) } + Expr getABranchExpr() { result = this.getBranchExpr(_) } /** Gets a printable representation of this expression. */ override string toString() { result = "...?...:..." } @@ -1299,7 +1332,7 @@ class SwitchExpr extends Expr, StmtParent, @switchexpr { * Gets a case of this `switch` expression, * which may be either a normal `case` or a `default`. */ - SwitchCase getACase() { result = getAConstCase() or result = getDefaultCase() } + SwitchCase getACase() { result = this.getAConstCase() or result = this.getDefaultCase() } /** Gets a (non-default) `case` of this `switch` expression. */ ConstCase getAConstCase() { result.getParent() = this } @@ -1312,7 +1345,7 @@ class SwitchExpr extends Expr, StmtParent, @switchexpr { /** Gets a result expression of this `switch` expression. */ Expr getAResult() { - result = getACase().getRuleExpression() + result = this.getACase().getRuleExpression() or exists(YieldStmt yield | yield.(JumpStmt).getTarget() = this and result = yield.getValue()) } @@ -1327,8 +1360,8 @@ class SwitchExpr extends Expr, StmtParent, @switchexpr { class InstanceOfExpr extends Expr, @instanceofexpr { /** Gets the expression on the left-hand side of the `instanceof` operator. */ Expr getExpr() { - if isPattern() - then result = getLocalVariableDeclExpr().getInit() + if this.isPattern() + then result = this.getLocalVariableDeclExpr().getInit() else result.isNthChildOf(this, 0) } @@ -1337,7 +1370,7 @@ class InstanceOfExpr extends Expr, @instanceofexpr { * * Holds if this `instanceof` expression uses pattern matching. */ - predicate isPattern() { exists(getLocalVariableDeclExpr()) } + predicate isPattern() { exists(this.getLocalVariableDeclExpr()) } /** * PREVIEW FEATURE in Java 14. Subject to removal in a future release. @@ -1350,7 +1383,7 @@ class InstanceOfExpr extends Expr, @instanceofexpr { Expr getTypeName() { result.isNthChildOf(this, 1) } /** Gets the type this `instanceof` expression checks for. */ - RefType getCheckedType() { result = getTypeName().getType() } + RefType getCheckedType() { result = this.getTypeName().getType() } /** Gets a printable representation of this expression. */ override string toString() { result = "...instanceof..." } @@ -1448,7 +1481,7 @@ class TypeLiteral extends Expr, @typeliteral { * Gets the type this type literal refers to. For example for `String.class` the * result is the type representing `String`. */ - Type getReferencedType() { result = getTypeName().getType() } + Type getReferencedType() { result = this.getTypeName().getType() } /** Gets a printable representation of this expression. */ override string toString() { result = this.getTypeName().toString() + ".class" } @@ -1473,15 +1506,15 @@ abstract class InstanceAccess extends Expr { * This never holds for accesses in lambda expressions as they cannot access * their own instance directly. */ - predicate isOwnInstanceAccess() { not isEnclosingInstanceAccess(_) } + predicate isOwnInstanceAccess() { not this.isEnclosingInstanceAccess(_) } /** Holds if this instance access is to an enclosing instance of type `t`. */ predicate isEnclosingInstanceAccess(RefType t) { - t = getQualifier().getType().(RefType).getSourceDeclaration() and - t != getEnclosingCallable().getDeclaringType() + t = this.getQualifier().getType().(RefType).getSourceDeclaration() and + t != this.getEnclosingCallable().getDeclaringType() or - not exists(getQualifier()) and - exists(LambdaExpr lam | lam.asMethod() = getEnclosingCallable() | + not exists(this.getQualifier()) and + exists(LambdaExpr lam | lam.asMethod() = this.getEnclosingCallable() | t = lam.getAnonymousClass().getEnclosingType() ) } @@ -1529,7 +1562,7 @@ class VarAccess extends Expr, @varaccess { Expr getQualifier() { result.getParent() = this } /** Holds if this variable access has a qualifier. */ - predicate hasQualifier() { exists(getQualifier()) } + predicate hasQualifier() { exists(this.getQualifier()) } /** Gets the variable accessed by this variable access. */ Variable getVariable() { variableBinding(this, result) } @@ -1571,11 +1604,11 @@ class VarAccess extends Expr, @varaccess { */ predicate isLocal() { // The access has no qualifier, or... - not hasQualifier() + not this.hasQualifier() or // the qualifier is either `this` or `A.this`, where `A` is the enclosing type, or // the qualifier is either `super` or `A.super`, where `A` is the enclosing type. - getQualifier().(InstanceAccess).isOwnInstanceAccess() + this.getQualifier().(InstanceAccess).isOwnInstanceAccess() } override string getAPrimaryQlClass() { result = "VarAccess" } @@ -1617,7 +1650,7 @@ class MethodAccess extends Expr, Call, @methodaccess { override Expr getQualifier() { result.isNthChildOf(this, -1) } /** Holds if this method access has a qualifier. */ - predicate hasQualifier() { exists(getQualifier()) } + predicate hasQualifier() { exists(this.getQualifier()) } /** Gets an argument supplied to the method that is invoked using this method access. */ override Expr getAnArgument() { result.getIndex() >= 0 and result.getParent() = this } @@ -1654,9 +1687,9 @@ class MethodAccess extends Expr, Call, @methodaccess { * the enclosing type if there is no qualifier. */ RefType getReceiverType() { - result = getQualifier().getType() + result = this.getQualifier().getType() or - not hasQualifier() and result = getEnclosingCallable().getDeclaringType() + not this.hasQualifier() and result = this.getEnclosingCallable().getDeclaringType() } /** @@ -1832,7 +1865,7 @@ class Call extends ExprParent, @caller { Callable getCallee() { callableBinding(this, result) } /** Gets the callable invoking this call. */ - Callable getCaller() { result = getEnclosingCallable() } + Callable getCaller() { result = this.getEnclosingCallable() } } /** A polymorphic call to an instance method. */ @@ -2033,14 +2066,14 @@ class Argument extends Expr { } /** Holds if this argument is part of an implicit varargs array. */ - predicate isVararg() { isNthVararg(_) } + predicate isVararg() { this.isNthVararg(_) } /** * Holds if this argument is part of an implicit varargs array at the * given array index. */ predicate isNthVararg(int arrayindex) { - not isExplicitVarargsArray() and + not this.isExplicitVarargsArray() and exists(Callable tgt | call.getCallee() = tgt and tgt.isVarargs() and diff --git a/java/ql/lib/semmle/code/java/Generics.qll b/java/ql/lib/semmle/code/java/Generics.qll index a15c47b1f8f..e1bf32d475b 100755 --- a/java/ql/lib/semmle/code/java/Generics.qll +++ b/java/ql/lib/semmle/code/java/Generics.qll @@ -38,7 +38,7 @@ import Type * * For example, `X` in `class X { }`. */ -class GenericType extends RefType { +class GenericType extends ClassOrInterface { GenericType() { typeVars(_, _, _, _, this) } /** @@ -69,12 +69,12 @@ class GenericType extends RefType { /** * Gets a type parameter of this generic type. */ - TypeVariable getATypeParameter() { result = getTypeParameter(_) } + TypeVariable getATypeParameter() { result = this.getTypeParameter(_) } /** * Gets the number of type parameters of this generic type. */ - int getNumberOfTypeParameters() { result = strictcount(getATypeParameter()) } + int getNumberOfTypeParameters() { result = strictcount(this.getATypeParameter()) } override string getAPrimaryQlClass() { result = "GenericType" } } @@ -107,7 +107,7 @@ abstract class BoundedType extends RefType, @boundedtype { TypeBound getATypeBound() { result.getBoundedType() = this } /** Gets the first type bound for this type, if any. */ - TypeBound getFirstTypeBound() { result = getATypeBound() and result.getPosition() = 0 } + TypeBound getFirstTypeBound() { result = this.getATypeBound() and result.getPosition() = 0 } /** * Gets an upper type bound of this type, or `Object` @@ -123,9 +123,9 @@ abstract class BoundedType extends RefType, @boundedtype { /** Gets a transitive upper bound for this type that is not itself a bounded type. */ RefType getAnUltimateUpperBoundType() { - result = getUpperBoundType() and not result instanceof BoundedType + result = this.getUpperBoundType() and not result instanceof BoundedType or - result = getUpperBoundType().(BoundedType).getAnUltimateUpperBoundType() + result = this.getUpperBoundType().(BoundedType).getAnUltimateUpperBoundType() } override string getAPrimaryQlClass() { result = "BoundedType" } @@ -139,7 +139,7 @@ abstract class BoundedType extends RefType, @boundedtype { */ class TypeVariable extends BoundedType, @typevariable { /** Gets the generic type that is parameterized by this type parameter, if any. */ - RefType getGenericType() { typeVars(this, _, _, _, result) } + GenericType getGenericType() { typeVars(this, _, _, _, result) } /** Gets the generic callable that is parameterized by this type parameter, if any. */ GenericCallable getGenericCallable() { typeVars(this, _, _, _, result) } @@ -168,8 +168,8 @@ class TypeVariable extends BoundedType, @typevariable { /** Gets the lexically enclosing package of this type parameter, if any. */ override Package getPackage() { - result = getGenericType().getPackage() or - result = getGenericCallable().getDeclaringType().getPackage() + result = this.getGenericType().getPackage() or + result = this.getGenericCallable().getDeclaringType().getPackage() } /** Finds a type that was supplied for this parameter. */ @@ -190,9 +190,9 @@ class TypeVariable extends BoundedType, @typevariable { /** Finds a non-typevariable type that was transitively supplied for this parameter. */ RefType getAnUltimatelySuppliedType() { - result = getASuppliedType() and not result instanceof TypeVariable + result = this.getASuppliedType() and not result instanceof TypeVariable or - result = getASuppliedType().(TypeVariable).getAnUltimatelySuppliedType() + result = this.getASuppliedType().(TypeVariable).getAnUltimatelySuppliedType() } override string getAPrimaryQlClass() { result = "TypeVariable" } @@ -261,7 +261,7 @@ class Wildcard extends BoundedType, @wildcard { * Holds if this is the unconstrained wildcard `?`. */ predicate isUnconstrained() { - not hasLowerBound() and + not this.hasLowerBound() and wildcards(this, "?", _) } @@ -321,7 +321,7 @@ class TypeBound extends @typebound { * For example, `List` is a parameterization of * the generic type `List`, where `E` is a type parameter. */ -class ParameterizedType extends RefType { +class ParameterizedType extends ClassOrInterface { ParameterizedType() { typeArgs(_, _, this) or typeVars(_, _, _, _, this) @@ -367,7 +367,9 @@ class ParameterizedType extends RefType { } /** Holds if this type originates from source code. */ - override predicate fromSource() { typeVars(_, _, _, _, this) and RefType.super.fromSource() } + override predicate fromSource() { + typeVars(_, _, _, _, this) and ClassOrInterface.super.fromSource() + } override string getAPrimaryQlClass() { result = "ParameterizedType" } } @@ -451,12 +453,12 @@ class GenericCallable extends Callable { /** * Gets a type parameter of this generic callable. */ - TypeVariable getATypeParameter() { result = getTypeParameter(_) } + TypeVariable getATypeParameter() { result = this.getTypeParameter(_) } /** * Gets the number of type parameters of this generic callable. */ - int getNumberOfTypeParameters() { result = strictcount(getATypeParameter()) } + int getNumberOfTypeParameters() { result = strictcount(this.getATypeParameter()) } } /** @@ -484,10 +486,10 @@ class GenericCall extends Call { /** Gets a type argument of the call for the given `TypeVariable`. */ RefType getATypeArgument(TypeVariable v) { - result = getAnExplicitTypeArgument(v) + result = this.getAnExplicitTypeArgument(v) or - not exists(getAnExplicitTypeArgument(v)) and - result = getAnInferredTypeArgument(v) + not exists(this.getAnExplicitTypeArgument(v)) and + result = this.getAnInferredTypeArgument(v) } } diff --git a/java/ql/lib/semmle/code/java/Import.qll b/java/ql/lib/semmle/code/java/Import.qll index fbcfea4f905..75b9d157d25 100755 --- a/java/ql/lib/semmle/code/java/Import.qll +++ b/java/ql/lib/semmle/code/java/Import.qll @@ -25,7 +25,7 @@ class ImportType extends Import { ImportType() { imports(this, _, _, 1) } /** Gets the imported type. */ - RefType getImportedType() { imports(this, result, _, _) } + ClassOrInterface getImportedType() { imports(this, result, _, _) } override string toString() { result = "import " + this.getImportedType().toString() } @@ -44,7 +44,7 @@ class ImportOnDemandFromType extends Import { ImportOnDemandFromType() { imports(this, _, _, 2) } /** Gets the type from which accessible nested types are imported. */ - RefType getTypeHoldingImport() { imports(this, result, _, _) } + ClassOrInterface getTypeHoldingImport() { imports(this, result, _, _) } /** Gets an imported type. */ NestedType getAnImport() { result.getEnclosingType() = this.getTypeHoldingImport() } @@ -87,7 +87,7 @@ class ImportStaticOnDemand extends Import { ImportStaticOnDemand() { imports(this, _, _, 4) } /** Gets the type from which accessible static members are imported. */ - RefType getTypeHoldingImport() { imports(this, result, _, _) } + ClassOrInterface getTypeHoldingImport() { imports(this, result, _, _) } /** Gets an imported type. */ NestedType getATypeImport() { result.getEnclosingType() = this.getTypeHoldingImport() } @@ -118,7 +118,7 @@ class ImportStaticTypeMember extends Import { ImportStaticTypeMember() { imports(this, _, _, 5) } /** Gets the type from which static members with a given name are imported. */ - RefType getTypeHoldingImport() { imports(this, result, _, _) } + ClassOrInterface getTypeHoldingImport() { imports(this, result, _, _) } /** Gets the name of the imported member(s). */ override string getName() { imports(this, _, result, _) } diff --git a/java/ql/lib/semmle/code/java/JDK.qll b/java/ql/lib/semmle/code/java/JDK.qll index 1e6d730943a..e497740a489 100644 --- a/java/ql/lib/semmle/code/java/JDK.qll +++ b/java/ql/lib/semmle/code/java/JDK.qll @@ -19,12 +19,12 @@ class TypeCloneable extends Interface { /** The class `java.lang.ProcessBuilder`. */ class TypeProcessBuilder extends Class { - TypeProcessBuilder() { hasQualifiedName("java.lang", "ProcessBuilder") } + TypeProcessBuilder() { this.hasQualifiedName("java.lang", "ProcessBuilder") } } /** The class `java.lang.Runtime`. */ class TypeRuntime extends Class { - TypeRuntime() { hasQualifiedName("java.lang", "Runtime") } + TypeRuntime() { this.hasQualifiedName("java.lang", "Runtime") } } /** The class `java.lang.String`. */ @@ -143,17 +143,22 @@ class ImmutableType extends Type { // --- Java IO --- /** The interface `java.io.Serializable`. */ class TypeSerializable extends Interface { - TypeSerializable() { hasQualifiedName("java.io", "Serializable") } + TypeSerializable() { this.hasQualifiedName("java.io", "Serializable") } } /** The interface `java.io.ObjectOutput`. */ class TypeObjectOutput extends Interface { - TypeObjectOutput() { hasQualifiedName("java.io", "ObjectOutput") } + TypeObjectOutput() { this.hasQualifiedName("java.io", "ObjectOutput") } } /** The type `java.io.ObjectOutputStream`. */ class TypeObjectOutputStream extends RefType { - TypeObjectOutputStream() { hasQualifiedName("java.io", "ObjectOutputStream") } + TypeObjectOutputStream() { this.hasQualifiedName("java.io", "ObjectOutputStream") } +} + +/** The type `java.io.ObjectInputStream`. */ +class TypeObjectInputStream extends RefType { + TypeObjectInputStream() { this.hasQualifiedName("java.io", "ObjectInputStream") } } /** The class `java.nio.file.Paths`. */ @@ -191,8 +196,8 @@ class ProcessBuilderConstructor extends Constructor, ExecCallable { */ class MethodProcessBuilderCommand extends Method, ExecCallable { MethodProcessBuilderCommand() { - hasName("command") and - getDeclaringType() instanceof TypeProcessBuilder + this.hasName("command") and + this.getDeclaringType() instanceof TypeProcessBuilder } override int getAnExecutedArgument() { result = 0 } @@ -203,8 +208,8 @@ class MethodProcessBuilderCommand extends Method, ExecCallable { */ class MethodRuntimeExec extends Method, ExecCallable { MethodRuntimeExec() { - hasName("exec") and - getDeclaringType() instanceof TypeRuntime + this.hasName("exec") and + this.getDeclaringType() instanceof TypeRuntime } override int getAnExecutedArgument() { result = 0 } @@ -215,8 +220,8 @@ class MethodRuntimeExec extends Method, ExecCallable { */ class MethodSystemGetenv extends Method { MethodSystemGetenv() { - hasName("getenv") and - getDeclaringType() instanceof TypeSystem + this.hasName("getenv") and + this.getDeclaringType() instanceof TypeSystem } } @@ -225,8 +230,8 @@ class MethodSystemGetenv extends Method { */ class MethodSystemGetProperty extends Method { MethodSystemGetProperty() { - hasName("getProperty") and - getDeclaringType() instanceof TypeSystem + this.hasName("getProperty") and + this.getDeclaringType() instanceof TypeSystem } } @@ -234,7 +239,7 @@ class MethodSystemGetProperty extends Method { * An access to a method named `getProperty` on class `java.lang.System`. */ class MethodAccessSystemGetProperty extends MethodAccess { - MethodAccessSystemGetProperty() { getMethod() instanceof MethodSystemGetProperty } + MethodAccessSystemGetProperty() { this.getMethod() instanceof MethodSystemGetProperty } /** * Holds if this call has a compile-time constant first argument with the value `propertyName`. @@ -250,8 +255,11 @@ class MethodAccessSystemGetProperty extends MethodAccess { */ class MethodExit extends Method { MethodExit() { - hasName("exit") and - (getDeclaringType() instanceof TypeRuntime or getDeclaringType() instanceof TypeSystem) + this.hasName("exit") and + ( + this.getDeclaringType() instanceof TypeRuntime or + this.getDeclaringType() instanceof TypeSystem + ) } } @@ -261,10 +269,10 @@ class MethodExit extends Method { */ class WriteObjectMethod extends Method { WriteObjectMethod() { - hasName("writeObject") and + this.hasName("writeObject") and ( - getDeclaringType() instanceof TypeObjectOutputStream or - getDeclaringType() instanceof TypeObjectOutput + this.getDeclaringType() instanceof TypeObjectOutputStream or + this.getDeclaringType() instanceof TypeObjectOutput ) } } @@ -275,7 +283,7 @@ class WriteObjectMethod extends Method { */ class ReadObjectMethod extends Method { ReadObjectMethod() { - this.getDeclaringType().hasQualifiedName("java.io", "ObjectInputStream") and + this.getDeclaringType() instanceof TypeObjectInputStream and ( this.hasName("readObject") or this.hasName("readObjectOverride") or @@ -288,16 +296,16 @@ class ReadObjectMethod extends Method { /** The method `Class.getName()`. */ class ClassNameMethod extends Method { ClassNameMethod() { - hasName("getName") and - getDeclaringType() instanceof TypeClass + this.hasName("getName") and + this.getDeclaringType() instanceof TypeClass } } /** The method `Class.getSimpleName()`. */ class ClassSimpleNameMethod extends Method { ClassSimpleNameMethod() { - hasName("getSimpleName") and - getDeclaringType() instanceof TypeClass + this.hasName("getSimpleName") and + this.getDeclaringType() instanceof TypeClass } } @@ -329,24 +337,24 @@ class MethodMathMax extends Method { /** The field `System.in`. */ class SystemIn extends Field { SystemIn() { - hasName("in") and - getDeclaringType() instanceof TypeSystem + this.hasName("in") and + this.getDeclaringType() instanceof TypeSystem } } /** The field `System.out`. */ class SystemOut extends Field { SystemOut() { - hasName("out") and - getDeclaringType() instanceof TypeSystem + this.hasName("out") and + this.getDeclaringType() instanceof TypeSystem } } /** The field `System.err`. */ class SystemErr extends Field { SystemErr() { - hasName("err") and - getDeclaringType() instanceof TypeSystem + this.hasName("err") and + this.getDeclaringType() instanceof TypeSystem } } diff --git a/java/ql/lib/semmle/code/java/JDKAnnotations.qll b/java/ql/lib/semmle/code/java/JDKAnnotations.qll index 49776a570f2..0b56599caa2 100644 --- a/java/ql/lib/semmle/code/java/JDKAnnotations.qll +++ b/java/ql/lib/semmle/code/java/JDKAnnotations.qll @@ -25,7 +25,9 @@ class SuppressWarningsAnnotation extends Annotation { } /** Gets the name of a warning suppressed by this annotation. */ - string getASuppressedWarning() { result = getASuppressedWarningLiteral().getRepresentedString() } + string getASuppressedWarning() { + result = this.getASuppressedWarningLiteral().getRepresentedString() + } } /** A `@Target` annotation. */ diff --git a/java/ql/lib/semmle/code/java/JMX.qll b/java/ql/lib/semmle/code/java/JMX.qll index 77194d24767..16c8736059f 100644 --- a/java/ql/lib/semmle/code/java/JMX.qll +++ b/java/ql/lib/semmle/code/java/JMX.qll @@ -26,27 +26,27 @@ class MXBean extends ManagedBean { */ class RegisteredManagedBeanImpl extends Class { RegisteredManagedBeanImpl() { - getAnAncestor() instanceof ManagedBean and + this.getAnAncestor() instanceof ManagedBean and exists(JMXRegistrationCall registerCall | registerCall.getObjectArgument().getType() = this) } /** * Gets a managed bean that this registered bean class implements. */ - ManagedBean getAnImplementedManagedBean() { result = getAnAncestor() } + ManagedBean getAnImplementedManagedBean() { result = this.getAnAncestor() } } /** * A call that registers an object with the `MBeanServer`, directly or indirectly. */ class JMXRegistrationCall extends MethodAccess { - JMXRegistrationCall() { getCallee() instanceof JMXRegistrationMethod } + JMXRegistrationCall() { this.getCallee() instanceof JMXRegistrationMethod } /** * Gets the argument that represents the object in the registration call. */ Expr getObjectArgument() { - result = getArgument(getCallee().(JMXRegistrationMethod).getObjectPosition()) + result = this.getArgument(this.getCallee().(JMXRegistrationMethod).getObjectPosition()) } } @@ -59,15 +59,15 @@ class JMXRegistrationCall extends MethodAccess { class JMXRegistrationMethod extends Method { JMXRegistrationMethod() { // A direct registration with the `MBeanServer`. - getDeclaringType().hasQualifiedName("javax.management", "MBeanServer") and - getName() = "registerMBean" + this.getDeclaringType().hasQualifiedName("javax.management", "MBeanServer") and + this.getName() = "registerMBean" or // The `MBeanServer` is often wrapped by an application specific management class, so identify // methods that wrap a call to another `JMXRegistrationMethod`. exists(JMXRegistrationCall c | // This must be a call to another JMX registration method, where the object argument is an access // of one of the parameters of this method. - c.getObjectArgument().(VarAccess).getVariable() = getAParameter() + c.getObjectArgument().(VarAccess).getVariable() = this.getAParameter() ) } @@ -76,13 +76,13 @@ class JMXRegistrationMethod extends Method { */ int getObjectPosition() { // Passed as the first argument to `registerMBean`. - getDeclaringType().hasQualifiedName("javax.management", "MBeanServer") and - getName() = "registerMBean" and + this.getDeclaringType().hasQualifiedName("javax.management", "MBeanServer") and + this.getName() = "registerMBean" and result = 0 or // Identify the position in this method where the object parameter should be passed. exists(JMXRegistrationCall c | - c.getObjectArgument().(VarAccess).getVariable() = getParameter(result) + c.getObjectArgument().(VarAccess).getVariable() = this.getParameter(result) ) } } diff --git a/java/ql/lib/semmle/code/java/Javadoc.qll b/java/ql/lib/semmle/code/java/Javadoc.qll index 61d978fbd35..8f7b1dbf580 100755 --- a/java/ql/lib/semmle/code/java/Javadoc.qll +++ b/java/ql/lib/semmle/code/java/Javadoc.qll @@ -14,7 +14,7 @@ class JavadocParent extends @javadocParent, Top { JavadocElement getChild(int index) { result = this.getAChild() and result.getIndex() = index } /** Gets the number of documentation elements attached to this parent. */ - int getNumChild() { result = count(getAChild()) } + int getNumChild() { result = count(this.getAChild()) } /** Gets a documentation element with the specified Javadoc tag name. */ JavadocTag getATag(string name) { result = this.getAChild() and result.getTagName() = name } @@ -33,7 +33,9 @@ class Javadoc extends JavadocParent, @javadoc { /** Gets the value of the `@author` tag, if any. */ string getAuthor() { result = this.getATag("@author").getChild(0).toString() } - override string toString() { result = toStringPrefix() + getChild(0) + toStringPostfix() } + override string toString() { + result = this.toStringPrefix() + this.getChild(0) + this.toStringPostfix() + } private string toStringPrefix() { if isEolComment(this) @@ -47,7 +49,7 @@ class Javadoc extends JavadocParent, @javadoc { if isEolComment(this) then result = "" else ( - if strictcount(getAChild()) = 1 then result = " */" else result = " ... */" + if strictcount(this.getAChild()) = 1 then result = " */" else result = " ... */" ) } @@ -119,10 +121,10 @@ class ThrowsTag extends JavadocTag { /** A Javadoc `@see` tag. */ class SeeTag extends JavadocTag { - SeeTag() { getTagName() = "@see" } + SeeTag() { this.getTagName() = "@see" } /** Gets the name of the entity referred to. */ - string getReference() { result = getChild(0).toString() } + string getReference() { result = this.getChild(0).toString() } } /** A Javadoc `@author` tag. */ diff --git a/java/ql/lib/semmle/code/java/Maps.qll b/java/ql/lib/semmle/code/java/Maps.qll index c86cb0ef47a..784db84fb98 100644 --- a/java/ql/lib/semmle/code/java/Maps.qll +++ b/java/ql/lib/semmle/code/java/Maps.qll @@ -76,11 +76,11 @@ class FreshMap extends ClassInstanceExpr { * A call to `Map.put(key, value)`. */ class MapPutCall extends MethodAccess { - MapPutCall() { getCallee().(MapMethod).hasName("put") } + MapPutCall() { this.getCallee().(MapMethod).hasName("put") } /** Gets the key argument of this call. */ - Expr getKey() { result = getArgument(0) } + Expr getKey() { result = this.getArgument(0) } /** Gets the value argument of this call. */ - Expr getValue() { result = getArgument(1) } + Expr getValue() { result = this.getArgument(1) } } diff --git a/java/ql/lib/semmle/code/java/Member.qll b/java/ql/lib/semmle/code/java/Member.qll index da136c577f8..fee8d3f24ee 100755 --- a/java/ql/lib/semmle/code/java/Member.qll +++ b/java/ql/lib/semmle/code/java/Member.qll @@ -21,7 +21,7 @@ class Member extends Element, Annotatable, Modifiable, @member { RefType getDeclaringType() { declaresMember(result, this) } /** Gets the qualified name of this member. */ - string getQualifiedName() { result = getDeclaringType().getName() + "." + getName() } + string getQualifiedName() { result = this.getDeclaringType().getName() + "." + this.getName() } /** * Holds if this member has the specified name and is declared in the @@ -33,9 +33,9 @@ class Member extends Element, Annotatable, Modifiable, @member { /** Holds if this member is package protected, that is, neither public nor private nor protected. */ predicate isPackageProtected() { - not isPrivate() and - not isProtected() and - not isPublic() + not this.isPrivate() and + not this.isProtected() and + not this.isPublic() } /** @@ -78,7 +78,7 @@ class Callable extends StmtParent, Member, @callable { */ string getMethodDescriptor() { exists(string return | return = this.getReturnType().getTypeDescriptor() | - result = "(" + descriptorUpTo(this.getNumberOfParameters()) + ")" + return + result = "(" + this.descriptorUpTo(this.getNumberOfParameters()) + ")" + return ) } @@ -86,19 +86,19 @@ class Callable extends StmtParent, Member, @callable { n = 0 and result = "" or exists(Parameter p | p = this.getParameter(n - 1) | - result = descriptorUpTo(n - 1) + p.getType().getTypeDescriptor() + result = this.descriptorUpTo(n - 1) + p.getType().getTypeDescriptor() ) } /** Holds if this callable calls `target`. */ - predicate calls(Callable target) { exists(getACallSite(target)) } + predicate calls(Callable target) { exists(this.getACallSite(target)) } /** * Holds if this callable calls `target` * using a `super(...)` constructor call. */ predicate callsSuperConstructor(Constructor target) { - getACallSite(target) instanceof SuperConstructorInvocationStmt + this.getACallSite(target) instanceof SuperConstructorInvocationStmt } /** @@ -106,14 +106,14 @@ class Callable extends StmtParent, Member, @callable { * using a `this(...)` constructor call. */ predicate callsThis(Constructor target) { - getACallSite(target) instanceof ThisConstructorInvocationStmt + this.getACallSite(target) instanceof ThisConstructorInvocationStmt } /** * Holds if this callable calls `target` * using a `super` method call. */ - predicate callsSuper(Method target) { getACallSite(target) instanceof SuperMethodAccess } + predicate callsSuper(Method target) { this.getACallSite(target) instanceof SuperMethodAccess } /** * Holds if this callable calls `c` using @@ -165,13 +165,13 @@ class Callable extends StmtParent, Member, @callable { Field getAnAccessedField() { this.accesses(result) } /** Gets the type of a formal parameter of this callable. */ - Type getAParamType() { result = getParameterType(_) } + Type getAParamType() { result = this.getParameterType(_) } /** Holds if this callable does not have any formal parameters. */ - predicate hasNoParameters() { not exists(getAParameter()) } + predicate hasNoParameters() { not exists(this.getAParameter()) } /** Gets the number of formal parameters of this callable. */ - int getNumberOfParameters() { result = count(getAParameter()) } + int getNumberOfParameters() { result = count(this.getAParameter()) } /** Gets a formal parameter of this callable. */ Parameter getAParameter() { result.getCallable() = this } @@ -205,7 +205,7 @@ class Callable extends StmtParent, Member, @callable { */ pragma[nomagic] string paramsString() { - exists(int n | n = getNumberOfParameters() | + exists(int n | n = this.getNumberOfParameters() | n = 0 and result = "()" or n > 0 and result = "(" + this.paramUpTo(n - 1) + ")" @@ -217,9 +217,9 @@ class Callable extends StmtParent, Member, @callable { * from left to right, up to (and including) the `n`-th parameter. */ private string paramUpTo(int n) { - n = 0 and result = getParameterType(0).toString() + n = 0 and result = this.getParameterType(0).toString() or - n > 0 and result = paramUpTo(n - 1) + ", " + getParameterType(n) + n > 0 and result = this.paramUpTo(n - 1) + ", " + this.getParameterType(n) } /** @@ -234,7 +234,7 @@ class Callable extends StmtParent, Member, @callable { Exception getAnException() { exceptions(result, _, this) } /** Gets an exception type that occurs in the `throws` clause of this callable. */ - RefType getAThrownExceptionType() { result = getAnException().getType() } + RefType getAThrownExceptionType() { result = this.getAnException().getType() } /** Gets a call site that references this callable. */ Call getAReference() { result.getCallee() = this } @@ -392,7 +392,7 @@ class Method extends Callable, @method { or // JLS 9.4: Every method declaration in the body of an interface without an // access modifier is implicitly public. - getDeclaringType() instanceof Interface and + this.getDeclaringType() instanceof Interface and not this.isPrivate() or exists(FunctionalExpr func | func.asMethod() = this) @@ -413,7 +413,7 @@ class Method extends Callable, @method { Callable.super.isStrictfp() or // JLS 8.1.1.3, JLS 9.1.1.2 - getDeclaringType().isStrictfp() + this.getDeclaringType().isStrictfp() } /** @@ -421,8 +421,8 @@ class Method extends Callable, @method { * nor an initializer method, and hence could be inherited. */ predicate isInheritable() { - not isPrivate() and - not (isStatic() and getDeclaringType() instanceof Interface) and + not this.isPrivate() and + not (this.isStatic() and this.getDeclaringType() instanceof Interface) and not this instanceof InitializerMethod } @@ -430,13 +430,13 @@ class Method extends Callable, @method { * Holds if this method is neither private nor static, and hence * uses dynamic dispatch. */ - predicate isVirtual() { not isPrivate() and not isStatic() } + predicate isVirtual() { not this.isPrivate() and not this.isStatic() } /** Holds if this method can be overridden. */ predicate isOverridable() { - isVirtual() and - not isFinal() and - not getDeclaringType().isFinal() + this.isVirtual() and + not this.isFinal() and + not this.getDeclaringType().isFinal() } override string getAPrimaryQlClass() { result = "Method" } @@ -549,7 +549,7 @@ abstract class InitializerMethod extends Method { } * field initializations and static initializer blocks. */ class StaticInitializer extends InitializerMethod { - StaticInitializer() { hasName("") } + StaticInitializer() { this.hasName("") } } /** @@ -629,7 +629,7 @@ class Field extends Member, ExprParent, @field, Variable { or // JLS 9.3: Every field declaration in the body of an interface is // implicitly public, static, and final - getDeclaringType() instanceof Interface + this.getDeclaringType() instanceof Interface } override predicate isStatic() { diff --git a/java/ql/lib/semmle/code/java/Modifier.qll b/java/ql/lib/semmle/code/java/Modifier.qll index 39cbe5a3c29..11317ef8537 100755 --- a/java/ql/lib/semmle/code/java/Modifier.qll +++ b/java/ql/lib/semmle/code/java/Modifier.qll @@ -25,7 +25,7 @@ abstract class Modifiable extends Element { * abstract, so `isAbstract()` will hold for them even if `hasModifier("abstract")` * does not. */ - predicate hasModifier(string m) { modifiers(getAModifier(), m) } + predicate hasModifier(string m) { modifiers(this.getAModifier(), m) } /** Holds if this element has no modifier. */ predicate hasNoModifier() { not hasModifier(this, _) } @@ -34,31 +34,31 @@ abstract class Modifiable extends Element { Modifier getAModifier() { this = result.getElement() } /** Holds if this element has an `abstract` modifier or is implicitly abstract. */ - predicate isAbstract() { hasModifier("abstract") } + predicate isAbstract() { this.hasModifier("abstract") } /** Holds if this element has a `static` modifier or is implicitly static. */ - predicate isStatic() { hasModifier("static") } + predicate isStatic() { this.hasModifier("static") } /** Holds if this element has a `final` modifier or is implicitly final. */ - predicate isFinal() { hasModifier("final") } + predicate isFinal() { this.hasModifier("final") } /** Holds if this element has a `public` modifier or is implicitly public. */ - predicate isPublic() { hasModifier("public") } + predicate isPublic() { this.hasModifier("public") } /** Holds if this element has a `protected` modifier. */ - predicate isProtected() { hasModifier("protected") } + predicate isProtected() { this.hasModifier("protected") } /** Holds if this element has a `private` modifier or is implicitly private. */ - predicate isPrivate() { hasModifier("private") } + predicate isPrivate() { this.hasModifier("private") } /** Holds if this element has a `volatile` modifier. */ - predicate isVolatile() { hasModifier("volatile") } + predicate isVolatile() { this.hasModifier("volatile") } /** Holds if this element has a `synchronized` modifier. */ - predicate isSynchronized() { hasModifier("synchronized") } + predicate isSynchronized() { this.hasModifier("synchronized") } /** Holds if this element has a `native` modifier. */ - predicate isNative() { hasModifier("native") } + predicate isNative() { this.hasModifier("native") } /** Holds if this element has a `default` modifier. */ predicate isDefault() { this.hasModifier("default") } diff --git a/java/ql/lib/semmle/code/java/PrettyPrintAst.qll b/java/ql/lib/semmle/code/java/PrettyPrintAst.qll index 45e683a2466..6cb5769184a 100644 --- a/java/ql/lib/semmle/code/java/PrettyPrintAst.qll +++ b/java/ql/lib/semmle/code/java/PrettyPrintAst.qll @@ -169,27 +169,27 @@ private class PpArrayCreationExpr extends PpAst, ArrayCreationExpr { override string getPart(int i) { i = 0 and result = "new " or - i = 1 and result = baseType() + i = 1 and result = this.baseType() or - i = 2 + 3 * dimensionIndex() and result = "[" + i = 2 + 3 * this.dimensionIndex() and result = "[" or - i = 4 + 3 * dimensionIndex() and result = "]" + i = 4 + 3 * this.dimensionIndex() and result = "]" or - i = 4 + 3 * exprDims() + [1 .. nonExprDims()] and result = "[]" + i = 4 + 3 * this.exprDims() + [1 .. this.nonExprDims()] and result = "[]" } private string baseType() { result = this.getType().(Array).getElementType().toString() } private int dimensionIndex() { exists(this.getDimension(result)) } - private int exprDims() { result = max(int j | j = 0 or j = 1 + dimensionIndex()) } + private int exprDims() { result = max(int j | j = 0 or j = 1 + this.dimensionIndex()) } - private int nonExprDims() { result = this.getType().(Array).getDimension() - exprDims() } + private int nonExprDims() { result = this.getType().(Array).getDimension() - this.exprDims() } override PpAst getChild(int i) { exists(int j | result = this.getDimension(j) and i = 3 + 3 * j) or - i = 5 + 3 * exprDims() + nonExprDims() and result = this.getInit() + i = 5 + 3 * this.exprDims() + this.nonExprDims() and result = this.getInit() } } @@ -539,27 +539,27 @@ private class PpForStmt extends PpAst, ForStmt { or exists(int j | j > 0 and exists(this.getInit(j)) and i = 2 + 2 * j and result = ", ") or - i = 1 + lastInitIndex() and result = "; " + i = 1 + this.lastInitIndex() and result = "; " or - i = 3 + lastInitIndex() and result = "; " + i = 3 + this.lastInitIndex() and result = "; " or exists(int j | - j > 0 and exists(this.getUpdate(j)) and i = 3 + lastInitIndex() + 2 * j and result = ", " + j > 0 and exists(this.getUpdate(j)) and i = 3 + this.lastInitIndex() + 2 * j and result = ", " ) or - i = 1 + lastUpdateIndex() and result = ")" + i = 1 + this.lastUpdateIndex() and result = ")" or - i = 2 + lastUpdateIndex() and result = " " and this.getStmt() instanceof BlockStmt + i = 2 + this.lastUpdateIndex() and result = " " and this.getStmt() instanceof BlockStmt } private int lastInitIndex() { result = 3 + 2 * max(int j | exists(this.getInit(j))) } private int lastUpdateIndex() { - result = 4 + lastInitIndex() + 2 * max(int j | exists(this.getUpdate(j))) + result = 4 + this.lastInitIndex() + 2 * max(int j | exists(this.getUpdate(j))) } override predicate newline(int i) { - i = 2 + lastUpdateIndex() and not this.getStmt() instanceof BlockStmt + i = 2 + this.lastUpdateIndex() and not this.getStmt() instanceof BlockStmt } override PpAst getChild(int i) { @@ -567,15 +567,15 @@ private class PpForStmt extends PpAst, ForStmt { or exists(int j | result = this.getInit(j) and i = 3 + 2 * j) or - i = 2 + lastInitIndex() and result = this.getCondition() + i = 2 + this.lastInitIndex() and result = this.getCondition() or - exists(int j | result = this.getUpdate(j) and i = 4 + lastInitIndex() + 2 * j) + exists(int j | result = this.getUpdate(j) and i = 4 + this.lastInitIndex() + 2 * j) or - i = 3 + lastUpdateIndex() and result = this.getStmt() + i = 3 + this.lastUpdateIndex() and result = this.getStmt() } override predicate indents(int i) { - i = 3 + lastUpdateIndex() and not this.getStmt() instanceof BlockStmt + i = 3 + this.lastUpdateIndex() and not this.getStmt() instanceof BlockStmt } } @@ -654,9 +654,9 @@ private class PpTryStmt extends PpAst, TryStmt { or exists(int j | exists(this.getResourceExpr(j)) and i = 3 + 2 * j and result = ";") or - i = 2 + lastResourceIndex() and result = ") " and exists(this.getAResource()) + i = 2 + this.lastResourceIndex() and result = ") " and exists(this.getAResource()) or - i = 1 + lastCatchIndex() and result = " finally " and exists(this.getFinally()) + i = 1 + this.lastCatchIndex() and result = " finally " and exists(this.getFinally()) } private int lastResourceIndex() { @@ -664,17 +664,17 @@ private class PpTryStmt extends PpAst, TryStmt { } private int lastCatchIndex() { - result = 4 + lastResourceIndex() + max(int j | exists(this.getCatchClause(j)) or j = 0) + result = 4 + this.lastResourceIndex() + max(int j | exists(this.getCatchClause(j)) or j = 0) } override PpAst getChild(int i) { exists(int j | i = 2 + 2 * j and result = this.getResource(j)) or - i = 3 + lastResourceIndex() and result = this.getBlock() + i = 3 + this.lastResourceIndex() and result = this.getBlock() or - exists(int j | i = 4 + lastResourceIndex() + j and result = this.getCatchClause(j)) + exists(int j | i = 4 + this.lastResourceIndex() + j and result = this.getCatchClause(j)) or - i = 2 + lastCatchIndex() and result = this.getFinally() + i = 2 + this.lastCatchIndex() and result = this.getFinally() } } @@ -728,11 +728,11 @@ private class PpSwitchCase extends PpAst, SwitchCase { or exists(int j | i = 2 * j and j != 0 and result = ", " and exists(this.(ConstCase).getValue(j))) or - i = 1 + lastConstCaseValueIndex() and result = ":" and not this.isRule() + i = 1 + this.lastConstCaseValueIndex() and result = ":" and not this.isRule() or - i = 1 + lastConstCaseValueIndex() and result = " -> " and this.isRule() + i = 1 + this.lastConstCaseValueIndex() and result = " -> " and this.isRule() or - i = 3 + lastConstCaseValueIndex() and result = ";" and exists(this.getRuleExpression()) + i = 3 + this.lastConstCaseValueIndex() and result = ";" and exists(this.getRuleExpression()) } private int lastConstCaseValueIndex() { @@ -742,9 +742,9 @@ private class PpSwitchCase extends PpAst, SwitchCase { override PpAst getChild(int i) { exists(int j | i = 1 + 2 * j and result = this.(ConstCase).getValue(j)) or - i = 2 + lastConstCaseValueIndex() and result = this.getRuleExpression() + i = 2 + this.lastConstCaseValueIndex() and result = this.getRuleExpression() or - i = 2 + lastConstCaseValueIndex() and result = this.getRuleStatement() + i = 2 + this.lastConstCaseValueIndex() and result = this.getRuleStatement() } } diff --git a/java/ql/lib/semmle/code/java/PrintAst.qll b/java/ql/lib/semmle/code/java/PrintAst.qll index d22065177bc..0d785e9ee28 100644 --- a/java/ql/lib/semmle/code/java/PrintAst.qll +++ b/java/ql/lib/semmle/code/java/PrintAst.qll @@ -151,7 +151,7 @@ class PrintAstNode extends TPrintAstNode { /** * Gets a child of this node. */ - final PrintAstNode getAChild() { result = getChild(_) } + final PrintAstNode getAChild() { result = this.getChild(_) } /** * Gets the parent of this node, if any. @@ -169,7 +169,7 @@ class PrintAstNode extends TPrintAstNode { */ string getProperty(string key) { key = "semmle.label" and - result = toString() + result = this.toString() } /** @@ -178,7 +178,7 @@ class PrintAstNode extends TPrintAstNode { * this. */ string getChildEdgeLabel(int childIndex) { - exists(getChild(childIndex)) and + exists(this.getChild(childIndex)) and result = childIndex.toString() } } @@ -259,7 +259,7 @@ final class AnnotationPartNode extends ExprStmtNode { override ElementNode getChild(int childIndex) { result.getElement() = rank[childIndex](Element ch, string file, int line, int column | - ch = getAnAnnotationChild() and locationSortKeys(ch, file, line, column) + ch = this.getAnAnnotationChild() and locationSortKeys(ch, file, line, column) | ch order by file, line, column ) @@ -352,7 +352,7 @@ private class SingleLocalVarDeclParent extends ExprOrStmt { LocalVariableDeclExpr getVariable() { result.getParent() = this } /** Gets the type access of the variable */ - Expr getTypeAccess() { result = getVariable().getTypeAccess() } + Expr getTypeAccess() { result = this.getVariable().getTypeAccess() } } /** @@ -460,7 +460,7 @@ final class ClassInterfaceNode extends ElementNode { childIndex >= 0 and result.(ElementNode).getElement() = rank[childIndex](Element e, string file, int line, int column | - e = getADeclaration() and locationSortKeys(e, file, line, column) + e = this.getADeclaration() and locationSortKeys(e, file, line, column) | e order by file, line, column ) @@ -507,7 +507,7 @@ final class CompilationUnitNode extends ElementNode { childIndex >= 0 and result.(ElementNode).getElement() = rank[childIndex](Element e, string file, int line, int column | - e = getADeclaration() and locationSortKeys(e, file, line, column) + e = this.getADeclaration() and locationSortKeys(e, file, line, column) | e order by file, line, column ) diff --git a/java/ql/lib/semmle/code/java/Reflection.qll b/java/ql/lib/semmle/code/java/Reflection.qll index ac6046824f6..71864c5cfe9 100644 --- a/java/ql/lib/semmle/code/java/Reflection.qll +++ b/java/ql/lib/semmle/code/java/Reflection.qll @@ -55,7 +55,7 @@ abstract private class ReflectiveClassIdentifier extends Expr { private class ReflectiveClassIdentifierLiteral extends ReflectiveClassIdentifier, TypeLiteral { override RefType getReflectivelyIdentifiedClass() { - result = getReferencedType().(RefType).getSourceDeclaration() + result = this.getReferencedType().(RefType).getSourceDeclaration() } } @@ -65,21 +65,21 @@ private class ReflectiveClassIdentifierLiteral extends ReflectiveClassIdentifier class ReflectiveClassIdentifierMethodAccess extends ReflectiveClassIdentifier, MethodAccess { ReflectiveClassIdentifierMethodAccess() { // A call to `Class.forName(...)`, from which we can infer `T` in the returned type `Class`. - getCallee().getDeclaringType() instanceof TypeClass and getCallee().hasName("forName") + this.getCallee().getDeclaringType() instanceof TypeClass and this.getCallee().hasName("forName") or // A call to `ClassLoader.loadClass(...)`, from which we can infer `T` in the returned type `Class`. - getCallee().getDeclaringType().hasQualifiedName("java.lang", "ClassLoader") and - getCallee().hasName("loadClass") + this.getCallee().getDeclaringType().hasQualifiedName("java.lang", "ClassLoader") and + this.getCallee().hasName("loadClass") } /** * If the argument to this call is a `StringLiteral`, then return that string. */ - string getTypeName() { result = getArgument(0).(StringLiteral).getRepresentedString() } + string getTypeName() { result = this.getArgument(0).(StringLiteral).getRepresentedString() } override RefType getReflectivelyIdentifiedClass() { // We only handle cases where the class is specified as a string literal to this call. - result.getQualifiedName() = getTypeName() + result.getQualifiedName() = this.getTypeName() } } @@ -214,10 +214,10 @@ private predicate expectsEnclosingInstance(RefType r) { class NewInstance extends MethodAccess { NewInstance() { ( - getCallee().getDeclaringType() instanceof TypeClass or - getCallee().getDeclaringType() instanceof TypeConstructor + this.getCallee().getDeclaringType() instanceof TypeClass or + this.getCallee().getDeclaringType() instanceof TypeConstructor ) and - getCallee().hasName("newInstance") + this.getCallee().hasName("newInstance") } /** @@ -225,26 +225,26 @@ class NewInstance extends MethodAccess { * called. */ Constructor getInferredConstructor() { - result = getInferredConstructedType().getAConstructor() and - if getCallee().getDeclaringType() instanceof TypeClass + result = this.getInferredConstructedType().getAConstructor() and + if this.getCallee().getDeclaringType() instanceof TypeClass then result.getNumberOfParameters() = 0 else - if getNumArgument() = 1 and getArgument(0).getType() instanceof Array + if this.getNumArgument() = 1 and this.getArgument(0).getType() instanceof Array then // This is a var-args array argument. If array argument is initialized inline, then identify // the number of arguments specified in the array. - if exists(getArgument(0).(ArrayCreationExpr).getInit()) + if exists(this.getArgument(0).(ArrayCreationExpr).getInit()) then // Count the number of elements in the initializer, and find the matching constructors. - matchConstructorArguments(result, - count(getArgument(0).(ArrayCreationExpr).getInit().getAnInit())) + this.matchConstructorArguments(result, + count(this.getArgument(0).(ArrayCreationExpr).getInit().getAnInit())) else // Could be any of the constructors on this class. any() else // No var-args in play, just use the number of arguments to the `newInstance(..)` to determine // which constructors may be called. - matchConstructorArguments(result, getNumArgument()) + this.matchConstructorArguments(result, this.getNumArgument()) } /** @@ -273,13 +273,13 @@ class NewInstance extends MethodAccess { not result instanceof TypeVariable and ( // If this is called on a `Class` instance, return the inferred type `T`. - result = inferClassParameterType(getQualifier()) + result = inferClassParameterType(this.getQualifier()) or // If this is called on a `Constructor` instance, return the inferred type `T`. - result = inferConstructorParameterType(getQualifier()) + result = inferConstructorParameterType(this.getQualifier()) or // If the result of this is cast to a particular type, then use that type. - result = getCastInferredConstructedTypes() + result = this.getCastInferredConstructedTypes() ) } @@ -313,7 +313,7 @@ class ClassMethodAccess extends MethodAccess { // `TypeVariable`s do not have methods themselves. not result instanceof TypeVariable and // If this is called on a `Class` instance, return the inferred type `T`. - result = inferClassParameterType(getQualifier()) + result = inferClassParameterType(this.getQualifier()) } } @@ -354,13 +354,13 @@ class ReflectiveMethodAccess extends ClassMethodAccess { if this.getCallee().hasName("getDeclaredMethod") then // The method must be declared on the type itself. - result.getDeclaringType() = getInferredClassType() + result.getDeclaringType() = this.getInferredClassType() else // The method may be declared on an inferred type or a super-type. - getInferredClassType().inherits(result) + this.getInferredClassType().inherits(result) ) and // Only consider instances where the method name is provided as a `StringLiteral`. - result.hasName(getArgument(0).(StringLiteral).getRepresentedString()) + result.hasName(this.getArgument(0).(StringLiteral).getRepresentedString()) } } @@ -373,7 +373,9 @@ class ReflectiveAnnotationAccess extends ClassMethodAccess { /** * Gets a possible annotation type for this reflective annotation access. */ - AnnotationType getAPossibleAnnotationType() { result = inferClassParameterType(getArgument(0)) } + AnnotationType getAPossibleAnnotationType() { + result = inferClassParameterType(this.getArgument(0)) + } } /** @@ -391,13 +393,13 @@ class ReflectiveFieldAccess extends ClassMethodAccess { if this.getCallee().hasName("getDeclaredField") then // Declared fields must be on the type itself. - result.getDeclaringType() = getInferredClassType() + result.getDeclaringType() = this.getInferredClassType() else ( // This field must be public, and be inherited by one of the inferred class types. result.isPublic() and - getInferredClassType().inherits(result) + this.getInferredClassType().inherits(result) ) ) and - result.hasName(getArgument(0).(StringLiteral).getRepresentedString()) + result.hasName(this.getArgument(0).(StringLiteral).getRepresentedString()) } } diff --git a/java/ql/lib/semmle/code/java/Statement.qll b/java/ql/lib/semmle/code/java/Statement.qll index a5f9eb81080..c3b4deef6a3 100755 --- a/java/ql/lib/semmle/code/java/Statement.qll +++ b/java/ql/lib/semmle/code/java/Statement.qll @@ -71,7 +71,7 @@ class BlockStmt extends Stmt, @block { int getNumStmt() { result = count(this.getAStmt()) } /** Gets the last statement in this block. */ - Stmt getLastStmt() { result = getStmt(getNumStmt() - 1) } + Stmt getLastStmt() { result = this.getStmt(this.getNumStmt() - 1) } override string pp() { result = "{ ... }" } @@ -93,7 +93,7 @@ class SingletonBlock extends BlockStmt { SingletonBlock() { this.getNumStmt() = 1 } /** Gets the single statement in this block. */ - Stmt getStmt() { result = getStmt(0) } + Stmt getStmt() { result = this.getStmt(0) } } /** @@ -125,7 +125,7 @@ class IfStmt extends ConditionalStmt, @ifstmt { * Gets the statement that is executed whenever the condition * of this branch statement evaluates to `true`. */ - deprecated override Stmt getTrueSuccessor() { result = getThen() } + deprecated override Stmt getTrueSuccessor() { result = this.getThen() } /** Gets the `else` branch of this `if` statement. */ Stmt getElse() { result.isNthChildOf(this, 2) } @@ -155,7 +155,7 @@ class ForStmt extends ConditionalStmt, @forstmt { /** Gets the initializer expression of the loop at the specified (zero-based) position. */ Expr getInit(int index) { - result = getAnInit() and + result = this.getAnInit() and index = -1 - result.getIndex() } @@ -167,7 +167,7 @@ class ForStmt extends ConditionalStmt, @forstmt { /** Gets the update expression of this loop at the specified (zero-based) position. */ Expr getUpdate(int index) { - result = getAnUpdate() and + result = this.getAnUpdate() and index = result.getIndex() - 3 } @@ -178,7 +178,7 @@ class ForStmt extends ConditionalStmt, @forstmt { * Gets the statement that is executed whenever the condition * of this branch statement evaluates to true. */ - deprecated override Stmt getTrueSuccessor() { result = getStmt() } + deprecated override Stmt getTrueSuccessor() { result = this.getStmt() } /** * Gets a variable that is used as an iteration variable: it is defined, @@ -193,12 +193,12 @@ class ForStmt extends ConditionalStmt, @forstmt { */ Variable getAnIterationVariable() { // Check that the variable is assigned to, incremented or decremented in the update expression, and... - exists(Expr update | update = getAnUpdate().getAChildExpr*() | + exists(Expr update | update = this.getAnUpdate().getAChildExpr*() | update.(UnaryAssignExpr).getExpr() = result.getAnAccess() or update = result.getAnAssignedValue() ) and // ...that it is checked or used in the condition. - getCondition().getAChildExpr*() = result.getAnAccess() + this.getCondition().getAChildExpr*() = result.getAnAccess() } override string pp() { result = "for (...;...;...) " + this.getStmt().pp() } @@ -242,7 +242,7 @@ class WhileStmt extends ConditionalStmt, @whilestmt { * Gets the statement that is executed whenever the condition * of this branch statement evaluates to true. */ - deprecated override Stmt getTrueSuccessor() { result = getStmt() } + deprecated override Stmt getTrueSuccessor() { result = this.getStmt() } override string pp() { result = "while (...) " + this.getStmt().pp() } @@ -265,7 +265,7 @@ class DoStmt extends ConditionalStmt, @dostmt { * Gets the statement that is executed whenever the condition * of this branch statement evaluates to `true`. */ - deprecated override Stmt getTrueSuccessor() { result = getStmt() } + deprecated override Stmt getTrueSuccessor() { result = this.getStmt() } override string pp() { result = "do " + this.getStmt().pp() + " while (...)" } @@ -343,17 +343,17 @@ class TryStmt extends Stmt, @trystmt { } /** Gets a resource in this `try` statement, if any. */ - ExprParent getAResource() { result = getAResourceDecl() or result = getAResourceExpr() } + ExprParent getAResource() { result = this.getAResourceDecl() or result = this.getAResourceExpr() } /** Gets the resource at the specified position in this `try` statement. */ ExprParent getResource(int index) { - result = getResourceDecl(index) or result = getResourceExpr(index) + result = this.getResourceDecl(index) or result = this.getResourceExpr(index) } /** Gets a resource variable, if any, either from a resource variable declaration or resource expression. */ Variable getAResourceVariable() { - result = getAResourceDecl().getAVariable().getVariable() or - result = getAResourceExpr().getVariable() + result = this.getAResourceDecl().getAVariable().getVariable() or + result = this.getAResourceExpr().getVariable() } override string pp() { result = "try " + this.getBlock().pp() + " catch (...)" } @@ -381,7 +381,7 @@ class CatchClause extends Stmt, @catchclause { /** Gets a type caught by this `catch` clause. */ RefType getACaughtType() { - exists(Expr ta | ta = getVariable().getTypeAccess() | + exists(Expr ta | ta = this.getVariable().getTypeAccess() | result = ta.(TypeAccess).getType() or result = ta.(UnionTypeAccess).getAnAlternative().getType() ) @@ -411,7 +411,7 @@ class SwitchStmt extends Stmt, @switchstmt { * Gets a case of this `switch` statement, * which may be either a normal `case` or a `default`. */ - SwitchCase getACase() { result = getAConstCase() or result = getDefaultCase() } + SwitchCase getACase() { result = this.getAConstCase() or result = this.getDefaultCase() } /** Gets a (non-default) `case` of this `switch` statement. */ ConstCase getAConstCase() { result.getParent() = this } @@ -550,7 +550,7 @@ class ThrowStmt extends Stmt, @throwstmt { override string getHalsteadID() { result = "ThrowStmt" } /** Gets the type of the expression thrown by this `throw` statement. */ - RefType getThrownExceptionType() { result = getExpr().getType() } + RefType getThrownExceptionType() { result = this.getExpr().getType() } /** * Gets the `catch` clause that catches the exception @@ -559,14 +559,14 @@ class ThrowStmt extends Stmt, @throwstmt { * provided such a `catch` exists. */ CatchClause getLexicalCatchIfAny() { - exists(TryStmt try | try = findEnclosing() and result = catchClauseForThis(try)) + exists(TryStmt try | try = this.findEnclosing() and result = this.catchClauseForThis(try)) } private Stmt findEnclosing() { - result = getEnclosingStmt() + result = this.getEnclosingStmt() or exists(Stmt mid | - mid = findEnclosing() and + mid = this.findEnclosing() and not exists(this.catchClauseForThis(mid.(TryStmt))) and result = mid.getEnclosingStmt() ) @@ -575,7 +575,7 @@ class ThrowStmt extends Stmt, @throwstmt { private CatchClause catchClauseForThis(TryStmt try) { result = try.getACatchClause() and result.getEnclosingCallable() = this.getEnclosingCallable() and - getExpr().getType().(RefType).hasSupertype*(result.getVariable().getType().(RefType)) and + this.getExpr().getType().(RefType).hasSupertype*(result.getVariable().getType().(RefType)) and not this.getEnclosingStmt+() = result } @@ -599,7 +599,7 @@ class JumpStmt extends Stmt { namestrings(result.getLabel(), _, this) } - private Stmt getLabelTarget() { result = getTargetLabel().getStmt() } + private Stmt getLabelTarget() { result = this.getTargetLabel().getStmt() } private Stmt getAPotentialTarget() { this.getEnclosingStmt+() = result and @@ -613,20 +613,20 @@ class JumpStmt extends Stmt { private SwitchExpr getSwitchExprTarget() { result = this.(YieldStmt).getParent+() } private StmtParent getEnclosingTarget() { - result = getSwitchExprTarget() + result = this.getSwitchExprTarget() or - not exists(getSwitchExprTarget()) and - result = getAPotentialTarget() and - not exists(Stmt other | other = getAPotentialTarget() | other.getEnclosingStmt+() = result) + not exists(this.getSwitchExprTarget()) and + result = this.getAPotentialTarget() and + not exists(Stmt other | other = this.getAPotentialTarget() | other.getEnclosingStmt+() = result) } /** * Gets the statement or `switch` expression that this `break`, `yield` or `continue` jumps to. */ StmtParent getTarget() { - result = getLabelTarget() + result = this.getLabelTarget() or - not exists(getLabelTarget()) and result = getEnclosingTarget() + not exists(this.getLabelTarget()) and result = this.getEnclosingTarget() } } @@ -714,9 +714,9 @@ class ExprStmt extends Stmt, @exprstmt { /** Holds if this statement represents a field declaration with an initializer. */ predicate isFieldDecl() { - getEnclosingCallable() instanceof InitializerMethod and + this.getEnclosingCallable() instanceof InitializerMethod and exists(FieldDeclaration fd, Location fdl, Location sl | - fdl = fd.getLocation() and sl = getLocation() + fdl = fd.getLocation() and sl = this.getLocation() | fdl.getFile() = sl.getFile() and fdl.getStartLine() = sl.getStartLine() and @@ -775,7 +775,7 @@ class LocalVariableDeclStmt extends Stmt, @localvariabledeclstmt { } /** Gets an index of a variable declared in this local variable declaration statement. */ - int getAVariableIndex() { exists(getVariable(result)) } + int getAVariableIndex() { exists(this.getVariable(result)) } override string pp() { result = "var ...;" } diff --git a/java/ql/lib/semmle/code/java/StringFormat.qll b/java/ql/lib/semmle/code/java/StringFormat.qll index cc37ee8212a..c6f9a7814db 100644 --- a/java/ql/lib/semmle/code/java/StringFormat.qll +++ b/java/ql/lib/semmle/code/java/StringFormat.qll @@ -152,15 +152,15 @@ class FormattingCall extends Call { private Expr getLastArg() { exists(Expr last | last = this.getArgument(this.getNumArgument() - 1) | if this.hasExplicitVarargsArray() - then result = last.(ArrayCreationExpr).getInit().getInit(getVarargsCount() - 1) + then result = last.(ArrayCreationExpr).getInit().getInit(this.getVarargsCount() - 1) else result = last ) } /** Holds if this uses the "logger ({})" format syntax and the last argument is a `Throwable`. */ predicate hasTrailingThrowableArgument() { - getSyntax() = TFmtLogger() and - getLastArg().getType().(RefType).getASourceSupertype*() instanceof TypeThrowable + this.getSyntax() = TFmtLogger() and + this.getLastArg().getType().(RefType).getASourceSupertype*() instanceof TypeThrowable } /** Gets the argument to this call in the position of the format string */ @@ -171,7 +171,7 @@ class FormattingCall extends Call { exists(int i | result = this.getArgument(i) and i > this.getFormatStringIndex() and - not hasExplicitVarargsArray() + not this.hasExplicitVarargsArray() ) } @@ -433,15 +433,15 @@ private class PrintfFormatString extends FormatString { override int getMaxFmtSpecIndex() { result = max(int ix | - ix = fmtSpecRefersToSpecificIndex(_) or - ix = count(int i | fmtSpecRefersToSequentialIndex(i)) + ix = this.fmtSpecRefersToSpecificIndex(_) or + ix = count(int i | this.fmtSpecRefersToSequentialIndex(i)) ) } override int getASkippedFmtSpecIndex() { - result in [1 .. getMaxFmtSpecIndex()] and - result > count(int i | fmtSpecRefersToSequentialIndex(i)) and - not result = fmtSpecRefersToSpecificIndex(_) + result in [1 .. this.getMaxFmtSpecIndex()] and + result > count(int i | this.fmtSpecRefersToSequentialIndex(i)) and + not result = this.fmtSpecRefersToSpecificIndex(_) } private int getFmtSpecRank(int specOffset) { @@ -449,14 +449,14 @@ private class PrintfFormatString extends FormatString { } override int getAnArgUsageOffset(int argNo) { - argNo = fmtSpecRefersToSpecificIndex(result) + argNo = this.fmtSpecRefersToSpecificIndex(result) or - result = rank[argNo](int i | fmtSpecRefersToSequentialIndex(i)) + result = rank[argNo](int i | this.fmtSpecRefersToSequentialIndex(i)) or - fmtSpecRefersToPrevious(result) and + this.fmtSpecRefersToPrevious(result) and exists(int previousOffset | - getFmtSpecRank(previousOffset) = getFmtSpecRank(result) - 1 and - previousOffset = getAnArgUsageOffset(argNo) + this.getFmtSpecRank(previousOffset) = this.getFmtSpecRank(result) - 1 and + previousOffset = this.getAnArgUsageOffset(argNo) ) } } @@ -479,10 +479,12 @@ private class LoggerFormatString extends FormatString { private predicate fmtPlaceholder(int i) { this.charAt(i) = "{" and this.charAt(i + 1) = "}" and - not true = isUnescapedBackslash(i - 1) + not true = this.isUnescapedBackslash(i - 1) } - override int getMaxFmtSpecIndex() { result = count(int i | fmtPlaceholder(i)) } + override int getMaxFmtSpecIndex() { result = count(int i | this.fmtPlaceholder(i)) } - override int getAnArgUsageOffset(int argNo) { result = rank[argNo](int i | fmtPlaceholder(i)) } + override int getAnArgUsageOffset(int argNo) { + result = rank[argNo](int i | this.fmtPlaceholder(i)) + } } diff --git a/java/ql/lib/semmle/code/java/Type.qll b/java/ql/lib/semmle/code/java/Type.qll index 5395e855701..1d0658595b2 100755 --- a/java/ql/lib/semmle/code/java/Type.qll +++ b/java/ql/lib/semmle/code/java/Type.qll @@ -379,7 +379,7 @@ class RefType extends Type, Annotatable, Modifiable, @reftype { } /** Holds if this type declares any members. */ - predicate hasMember() { exists(getAMember()) } + predicate hasMember() { exists(this.getAMember()) } /** Gets a member declared in this type. */ Member getAMember() { this = result.getDeclaringType() } @@ -545,8 +545,10 @@ class RefType extends Type, Annotatable, Modifiable, @reftype { * `java.lang.Thread$State`. */ string getQualifiedName() { - exists(string pkgName | pkgName = getPackage().getName() | - if pkgName = "" then result = nestedName() else result = pkgName + "." + nestedName() + exists(string pkgName | pkgName = this.getPackage().getName() | + if pkgName = "" + then result = this.nestedName() + else result = pkgName + "." + this.nestedName() ) } @@ -656,7 +658,7 @@ class IntersectionType extends RefType, @class { /** Gets a textual representation of this type that includes all the intersected types. */ string getLongName() { - result = superType().toString() + concat(" & " + superInterface().toString()) + result = this.superType().toString() + concat(" & " + this.superInterface().toString()) } /** Gets the first bound of this intersection type. */ @@ -690,7 +692,8 @@ class AnonymousClass extends NestedClass { override string getTypeDescriptor() { exists(RefType parent | parent = this.getEnclosingType() | exists(int num | - num = 1 + count(AnonymousClass other | other.rankInParent(parent) < rankInParent(parent)) + num = + 1 + count(AnonymousClass other | other.rankInParent(parent) < this.rankInParent(parent)) | exists(string parentWithSemi | parentWithSemi = parent.getTypeDescriptor() | result = parentWithSemi.prefix(parentWithSemi.length() - 1) + "$" + num + ";" @@ -760,8 +763,8 @@ class NestedType extends RefType { /** Gets the nesting depth of this nested type. Top-level types have nesting depth 0. */ int getNestingDepth() { - if getEnclosingType() instanceof NestedType - then result = getEnclosingType().(NestedType).getNestingDepth() + 1 + if this.getEnclosingType() instanceof NestedType + then result = this.getEnclosingType().(NestedType).getNestingDepth() + 1 else result = 1 } @@ -776,26 +779,18 @@ class NestedType extends RefType { super.isStrictfp() or // JLS 8.1.1.3, JLS 9.1.1.2 - getEnclosingType().isStrictfp() + this.getEnclosingType().isStrictfp() } - /** - * Holds if this nested type is static. - * - * A nested type is static either if it is explicitly declared as such - * using the modifier `static`, or if it is implicitly static - * because one of the following holds: - * - * - it is a member type of an interface, - * - it is a member interface, or - * - it is a nested enum type. - * - * See JLS v8, section 8.5.1 (Static Member Type Declarations), - * section 8.9 (Enums) and section 9.5 (Member Type Declarations). - */ override predicate isStatic() { super.isStatic() or + /* + * Note: The following is most likely redundant because `isStatic()` of the superclass + * holds for implicitly static types, but keep the special casing below for now to be + * on the safe side + */ + // JLS 8.5.1: A member interface is implicitly static. this instanceof Interface or @@ -868,9 +863,9 @@ class ClassOrInterface extends RefType, @classorinterface { /** Holds if this class or interface is package protected, that is, neither public nor private nor protected. */ predicate isPackageProtected() { - not isPrivate() and - not isProtected() and - not isPublic() + not this.isPrivate() and + not this.isProtected() and + not this.isPublic() } } @@ -956,12 +951,12 @@ class PrimitiveType extends Type, @primitive { * require an explicit cast. */ Literal getADefaultValue() { - getName() = "boolean" and result.getLiteral() = "false" + this.getName() = "boolean" and result.getLiteral() = "false" or - getName() = "char" and + this.getName() = "char" and (result.getLiteral() = "'\\0'" or result.getLiteral() = "'\\u0000'") or - getName().regexpMatch("(float|double|int|short|byte|long)") and + this.getName().regexpMatch("(float|double|int|short|byte|long)") and result.getLiteral().regexpMatch("0(\\.0)?+[lLfFdD]?+") } @@ -1055,7 +1050,7 @@ class EnumType extends Class { override predicate isFinal() { // JLS 8.9: An enum declaration is implicitly `final` unless it contains // at least one enum constant that has a class body. - not getAnEnumConstant().getAnAssignedValue().getType() instanceof AnonymousClass + not this.getAnEnumConstant().getAnAssignedValue().getType() instanceof AnonymousClass } } diff --git a/java/ql/lib/semmle/code/java/UnitTests.qll b/java/ql/lib/semmle/code/java/UnitTests.qll index 1adc88d35f7..6115094e5d7 100644 --- a/java/ql/lib/semmle/code/java/UnitTests.qll +++ b/java/ql/lib/semmle/code/java/UnitTests.qll @@ -38,11 +38,12 @@ class TearDownMethod extends Method { private class TestRelatedAnnotation extends Annotation { TestRelatedAnnotation() { - this.getType().getPackage().hasName("org.testng.annotations") or - this.getType().getPackage().hasName("org.junit") or - this.getType().getPackage().hasName("org.junit.runner") or - this.getType().getPackage().hasName("org.junit.jupiter.api") or - this.getType().getPackage().hasName("org.junit.jupiter.params") + this.getType() + .getPackage() + .hasName([ + "org.testng.annotations", "org.junit", "org.junit.runner", "org.junit.jupiter.api", + "org.junit.jupiter.params" + ]) } } @@ -115,7 +116,7 @@ class JUnitJupiterTestMethod extends Method { * A JUnit `@Ignore` annotation. */ class JUnitIgnoreAnnotation extends Annotation { - JUnitIgnoreAnnotation() { getType().hasQualifiedName("org.junit", "Ignore") } + JUnitIgnoreAnnotation() { this.getType().hasQualifiedName("org.junit", "Ignore") } } /** @@ -124,7 +125,7 @@ class JUnitIgnoreAnnotation extends Annotation { */ class JUnitIgnoredMethod extends Method { JUnitIgnoredMethod() { - getAnAnnotation() instanceof JUnitIgnoreAnnotation + this.getAnAnnotation() instanceof JUnitIgnoreAnnotation or exists(Class c | c = this.getDeclaringType() | c.getAnAnnotation() instanceof JUnitIgnoreAnnotation @@ -136,14 +137,14 @@ class JUnitIgnoredMethod extends Method { * An annotation in TestNG. */ class TestNGAnnotation extends Annotation { - TestNGAnnotation() { getType().getPackage().hasName("org.testng.annotations") } + TestNGAnnotation() { this.getType().getPackage().hasName("org.testng.annotations") } } /** * An annotation of type `org.test.ng.annotations.Test`. */ class TestNGTestAnnotation extends TestNGAnnotation { - TestNGTestAnnotation() { getType().hasName("Test") } + TestNGTestAnnotation() { this.getType().hasName("Test") } } /** @@ -158,13 +159,13 @@ class TestNGTestMethod extends Method { */ TestNGDataProviderMethod getADataProvider() { exists(TestNGTestAnnotation testAnnotation | - testAnnotation = getAnAnnotation() and + testAnnotation = this.getAnAnnotation() and // The data provider must have the same name as the referenced data provider result.getDataProviderName() = testAnnotation.getValue("dataProvider").(StringLiteral).getRepresentedString() | // Either the data provider should be on the current class, or a supertype - getDeclaringType().getAnAncestor() = result.getDeclaringType() + this.getDeclaringType().getAnAncestor() = result.getDeclaringType() or // Or the data provider class should be declared result.getDeclaringType() = @@ -190,14 +191,14 @@ class TestMethod extends Method { * A TestNG annotation used to mark a method that runs "before". */ class TestNGBeforeAnnotation extends TestNGAnnotation { - TestNGBeforeAnnotation() { getType().getName().matches("Before%") } + TestNGBeforeAnnotation() { this.getType().getName().matches("Before%") } } /** * A TestNG annotation used to mark a method that runs "after". */ class TestNGAfterAnnotation extends TestNGAnnotation { - TestNGAfterAnnotation() { getType().getName().matches("After%") } + TestNGAfterAnnotation() { this.getType().getName().matches("After%") } } /** @@ -205,7 +206,7 @@ class TestNGAfterAnnotation extends TestNGAnnotation { * them as data provider methods for TestNG. */ class TestNGDataProviderAnnotation extends TestNGAnnotation { - TestNGDataProviderAnnotation() { getType().hasName("DataProvider") } + TestNGDataProviderAnnotation() { this.getType().hasName("DataProvider") } } /** @@ -213,7 +214,7 @@ class TestNGDataProviderAnnotation extends TestNGAnnotation { * them as factory methods for TestNG. */ class TestNGFactoryAnnotation extends TestNGAnnotation { - TestNGFactoryAnnotation() { getType().hasName("Factory") } + TestNGFactoryAnnotation() { this.getType().hasName("Factory") } } /** @@ -221,13 +222,13 @@ class TestNGFactoryAnnotation extends TestNGAnnotation { * which listeners apply to them. */ class TestNGListenersAnnotation extends TestNGAnnotation { - TestNGListenersAnnotation() { getType().hasName("Listeners") } + TestNGListenersAnnotation() { this.getType().hasName("Listeners") } /** * Gets a listener defined in this annotation. */ TestNGListenerImpl getAListener() { - result = getAValue("value").(TypeLiteral).getReferencedType() + result = this.getAValue("value").(TypeLiteral).getReferencedType() } } @@ -235,7 +236,7 @@ class TestNGListenersAnnotation extends TestNGAnnotation { * A concrete implementation class of one or more of the TestNG listener interfaces. */ class TestNGListenerImpl extends Class { - TestNGListenerImpl() { getAnAncestor().hasQualifiedName("org.testng", "ITestNGListener") } + TestNGListenerImpl() { this.getAnAncestor().hasQualifiedName("org.testng", "ITestNGListener") } } /** @@ -246,14 +247,14 @@ class TestNGListenerImpl extends Class { * an instance of a particular value when running a test method. */ class TestNGDataProviderMethod extends Method { - TestNGDataProviderMethod() { getAnAnnotation() instanceof TestNGDataProviderAnnotation } + TestNGDataProviderMethod() { this.getAnAnnotation() instanceof TestNGDataProviderAnnotation } /** * Gets the name associated with this data provider. */ string getDataProviderName() { result = - getAnAnnotation() + this.getAnAnnotation() .(TestNGDataProviderAnnotation) .getValue("name") .(StringLiteral) @@ -268,7 +269,7 @@ class TestNGDataProviderMethod extends Method { * This factory callable is used to generate instances of parameterized test classes. */ class TestNGFactoryCallable extends Callable { - TestNGFactoryCallable() { getAnAnnotation() instanceof TestNGFactoryAnnotation } + TestNGFactoryCallable() { this.getAnAnnotation() instanceof TestNGFactoryAnnotation } } /** @@ -276,7 +277,7 @@ class TestNGFactoryCallable extends Callable { */ class ParameterizedJUnitTest extends Class { ParameterizedJUnitTest() { - getAnAnnotation() + this.getAnAnnotation() .(RunWithAnnotation) .getRunner() .(Class) @@ -289,7 +290,7 @@ class ParameterizedJUnitTest extends Class { */ class JUnitCategoryAnnotation extends Annotation { JUnitCategoryAnnotation() { - getType().hasQualifiedName("org.junit.experimental.categories", "Category") + this.getType().hasQualifiedName("org.junit.experimental.categories", "Category") } /** @@ -297,7 +298,7 @@ class JUnitCategoryAnnotation extends Annotation { */ Type getACategory() { exists(TypeLiteral literal, Expr value | - value = getValue("value") and + value = this.getValue("value") and ( literal = value or literal = value.(ArrayCreationExpr).getInit().getAnInit() @@ -313,7 +314,7 @@ class JUnitCategoryAnnotation extends Annotation { */ class JUnitTheoryTest extends Class { JUnitTheoryTest() { - getAnAnnotation() + this.getAnAnnotation() .(RunWithAnnotation) .getRunner() .(Class) diff --git a/java/ql/lib/semmle/code/java/Variable.qll b/java/ql/lib/semmle/code/java/Variable.qll index 439ee5d3f6b..530ddd4eae7 100755 --- a/java/ql/lib/semmle/code/java/Variable.qll +++ b/java/ql/lib/semmle/code/java/Variable.qll @@ -47,12 +47,12 @@ class LocalVariableDecl extends @localvar, LocalScopeVariable { override Callable getCallable() { result = this.getParent().getEnclosingCallable() } /** Gets the callable in which this declaration occurs. */ - Callable getEnclosingCallable() { result = getCallable() } + Callable getEnclosingCallable() { result = this.getCallable() } override string toString() { result = this.getType().getName() + " " + this.getName() } /** Gets the initializer expression of this local variable declaration. */ - override Expr getInitializer() { result = getDeclExpr().getInit() } + override Expr getInitializer() { result = this.getDeclExpr().getInit() } override string getAPrimaryQlClass() { result = "LocalVariableDecl" } } @@ -63,7 +63,7 @@ class Parameter extends Element, @param, LocalScopeVariable { override Type getType() { params(this, result, _, _, _) } /** Holds if the parameter is never assigned a value in the body of the callable. */ - predicate isEffectivelyFinal() { not exists(getAnAssignedValue()) } + predicate isEffectivelyFinal() { not exists(this.getAnAssignedValue()) } /** Gets the (zero-based) index of this formal parameter. */ int getPosition() { params(this, _, result, _, _) } @@ -87,8 +87,8 @@ class Parameter extends Element, @param, LocalScopeVariable { * Varargs parameters will have no results for this method. */ Expr getAnArgument() { - not isVarargs() and - result = getACallArgument(getPosition()) + not this.isVarargs() and + result = this.getACallArgument(this.getPosition()) } pragma[noinline] diff --git a/java/ql/lib/semmle/code/java/arithmetic/Overflow.qll b/java/ql/lib/semmle/code/java/arithmetic/Overflow.qll index 7227c6da398..4e9eb75ec13 100644 --- a/java/ql/lib/semmle/code/java/arithmetic/Overflow.qll +++ b/java/ql/lib/semmle/code/java/arithmetic/Overflow.qll @@ -2,9 +2,9 @@ import java /** A subclass of `PrimitiveType` with width-based ordering methods. */ class OrdPrimitiveType extends PrimitiveType { - predicate widerThan(OrdPrimitiveType that) { getWidthRank() > that.getWidthRank() } + predicate widerThan(OrdPrimitiveType that) { this.getWidthRank() > that.getWidthRank() } - predicate widerThanOrEqualTo(OrdPrimitiveType that) { getWidthRank() >= that.getWidthRank() } + predicate widerThanOrEqualTo(OrdPrimitiveType that) { this.getWidthRank() >= that.getWidthRank() } OrdPrimitiveType maxType(OrdPrimitiveType that) { this.widerThan(that) and result = this diff --git a/java/ql/lib/semmle/code/java/controlflow/BasicBlocks.qll b/java/ql/lib/semmle/code/java/controlflow/BasicBlocks.qll index c0b227ba9ba..5f1ed3438b5 100644 --- a/java/ql/lib/semmle/code/java/controlflow/BasicBlocks.qll +++ b/java/ql/lib/semmle/code/java/controlflow/BasicBlocks.qll @@ -25,13 +25,13 @@ class BasicBlock extends ControlFlowNode { /** Gets an immediate successor of this basic block. */ cached - BasicBlock getABBSuccessor() { result = getLastNode().getASuccessor() } + BasicBlock getABBSuccessor() { result = this.getLastNode().getASuccessor() } /** Gets an immediate predecessor of this basic block. */ BasicBlock getABBPredecessor() { result.getABBSuccessor() = this } /** Gets a control-flow node contained in this basic block. */ - ControlFlowNode getANode() { result = getNode(_) } + ControlFlowNode getANode() { result = this.getNode(_) } /** Gets the control-flow node at a specific (zero-indexed) position in this basic block. */ cached @@ -39,7 +39,7 @@ class BasicBlock extends ControlFlowNode { result = this and pos = 0 or exists(ControlFlowNode mid, int mid_pos | pos = mid_pos + 1 | - getNode(mid_pos) = mid and + this.getNode(mid_pos) = mid and mid.getASuccessor() = result and not result instanceof BasicBlock ) @@ -49,11 +49,11 @@ class BasicBlock extends ControlFlowNode { ControlFlowNode getFirstNode() { result = this } /** Gets the last control-flow node in this basic block. */ - ControlFlowNode getLastNode() { result = getNode(length() - 1) } + ControlFlowNode getLastNode() { result = this.getNode(this.length() - 1) } /** Gets the number of control-flow nodes contained in this basic block. */ cached - int length() { result = strictcount(getANode()) } + int length() { result = strictcount(this.getANode()) } /** Holds if this basic block strictly dominates `node`. */ predicate bbStrictlyDominates(BasicBlock node) { bbStrictlyDominates(this, node) } diff --git a/java/ql/lib/semmle/code/java/controlflow/UnreachableBlocks.qll b/java/ql/lib/semmle/code/java/controlflow/UnreachableBlocks.qll index 5ce27fda434..8e9d90769f3 100644 --- a/java/ql/lib/semmle/code/java/controlflow/UnreachableBlocks.qll +++ b/java/ql/lib/semmle/code/java/controlflow/UnreachableBlocks.qll @@ -12,13 +12,13 @@ import semmle.code.java.controlflow.Guards */ class ConstantField extends Field { ConstantField() { - getType() instanceof ImmutableType and + this.getType() instanceof ImmutableType and // Assigned once - count(getAnAssignedValue()) = 1 and + count(this.getAnAssignedValue()) = 1 and // And that assignment is either in the appropriate initializer, or, for instance fields on // classes with one constructor, in the constructor. - forall(FieldWrite fa | fa = getAnAccess() | - if isStatic() + forall(FieldWrite fa | fa = this.getAnAccess() | + if this.isStatic() then fa.getEnclosingCallable() instanceof StaticInitializer else ( // Defined in the instance initializer. @@ -26,7 +26,7 @@ class ConstantField extends Field { or // It can be defined in the constructor if there is only one constructor. fa.getEnclosingCallable() instanceof Constructor and - count(getDeclaringType().getAConstructor()) = 1 + count(this.getDeclaringType().getAConstructor()) = 1 ) ) } @@ -36,7 +36,7 @@ class ConstantField extends Field { * * Note: although this value is constant, we may not be able to statically determine the value. */ - ConstantExpr getConstantValue() { result = getAnAssignedValue() } + ConstantExpr getConstantValue() { result = this.getAnAssignedValue() } } /** @@ -162,18 +162,18 @@ class ConstSwitchStmt extends SwitchStmt { /** Gets the `ConstCase` that matches, if any. */ ConstCase getMatchingConstCase() { - result = getAConstCase() and + result = this.getAConstCase() and // Only handle the int case for now - result.getValue().(ConstantExpr).getIntValue() = getExpr().(ConstantExpr).getIntValue() + result.getValue().(ConstantExpr).getIntValue() = this.getExpr().(ConstantExpr).getIntValue() } /** Gets the matching case, if it can be deduced. */ SwitchCase getMatchingCase() { // Must be a value we can deduce - exists(getExpr().(ConstantExpr).getIntValue()) and - if exists(getMatchingConstCase()) - then result = getMatchingConstCase() - else result = getDefaultCase() + exists(this.getExpr().(ConstantExpr).getIntValue()) and + if exists(this.getMatchingConstCase()) + then result = this.getMatchingConstCase() + else result = this.getDefaultCase() } /** @@ -184,8 +184,8 @@ class ConstSwitchStmt extends SwitchStmt { SwitchCase getAFailingCase() { exists(SwitchCase matchingCase | // We must have found the matching case, otherwise we can't deduce which cases are not matched - matchingCase = getMatchingCase() and - result = getACase() and + matchingCase = this.getMatchingCase() and + result = this.getACase() and result != matchingCase ) } @@ -208,7 +208,7 @@ class UnreachableBasicBlock extends BasicBlock { or // This block is not reachable in the CFG, and is not a callable, a body of a callable, an // expression in an annotation, an expression in an assert statement, or a catch clause. - forall(BasicBlock bb | bb = getABBPredecessor() | bb instanceof UnreachableBasicBlock) and + forall(BasicBlock bb | bb = this.getABBPredecessor() | bb instanceof UnreachableBasicBlock) and not exists(Callable c | c.getBody() = this) and not this instanceof Callable and not exists(Annotation a | a.getAChildExpr*() = this) and @@ -231,12 +231,12 @@ class UnreachableBasicBlock extends BasicBlock { * An unreachable expression is an expression contained in an `UnreachableBasicBlock`. */ class UnreachableExpr extends Expr { - UnreachableExpr() { getBasicBlock() instanceof UnreachableBasicBlock } + UnreachableExpr() { this.getBasicBlock() instanceof UnreachableBasicBlock } } /** * An unreachable statement is a statement contained in an `UnreachableBasicBlock`. */ class UnreachableStmt extends Stmt { - UnreachableStmt() { getBasicBlock() instanceof UnreachableBasicBlock } + UnreachableStmt() { this.getBasicBlock() instanceof UnreachableBasicBlock } } diff --git a/java/ql/lib/semmle/code/java/controlflow/unreachableblocks/ExcludeDebuggingProfilingLogging.qll b/java/ql/lib/semmle/code/java/controlflow/unreachableblocks/ExcludeDebuggingProfilingLogging.qll index a9f75924c55..3494e813c74 100644 --- a/java/ql/lib/semmle/code/java/controlflow/unreachableblocks/ExcludeDebuggingProfilingLogging.qll +++ b/java/ql/lib/semmle/code/java/controlflow/unreachableblocks/ExcludeDebuggingProfilingLogging.qll @@ -17,16 +17,11 @@ import semmle.code.java.controlflow.UnreachableBlocks class ExcludeDebuggingProfilingLogging extends ExcludedConstantField { ExcludeDebuggingProfilingLogging() { exists(string validFieldName | - validFieldName = "debug" or - validFieldName = "profiling" or - validFieldName = "profile" or - validFieldName = "time" or - validFieldName = "verbose" or - validFieldName = "report" or - validFieldName = "dbg" or - validFieldName = "timing" or - validFieldName = "assert" or - validFieldName = "log" + validFieldName = + [ + "debug", "profiling", "profile", "time", "verbose", "report", "dbg", "timing", "assert", + "log" + ] | getName().regexpMatch(".*(?i)" + validFieldName + ".*") ) and diff --git a/java/ql/lib/semmle/code/java/dataflow/Bound.qll b/java/ql/lib/semmle/code/java/dataflow/Bound.qll index b129203db70..55a8b1f4c3f 100644 --- a/java/ql/lib/semmle/code/java/dataflow/Bound.qll +++ b/java/ql/lib/semmle/code/java/dataflow/Bound.qll @@ -30,7 +30,7 @@ abstract class Bound extends TBound { * The location spans column `sc` of line `sl` to * column `ec` of line `el` in file `path`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0 diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll index 7f46f2d79b6..2f18c89db9b 100644 --- a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll @@ -77,6 +77,9 @@ private import FlowSummary */ private module Frameworks { private import internal.ContainerFlow + private import semmle.code.java.frameworks.android.Android + private import semmle.code.java.frameworks.android.Intent + private import semmle.code.java.frameworks.android.SQLite private import semmle.code.java.frameworks.android.XssSinks private import semmle.code.java.frameworks.ApacheHttp private import semmle.code.java.frameworks.apache.Collections @@ -91,7 +94,10 @@ private module Frameworks { private import semmle.code.java.frameworks.JsonJava private import semmle.code.java.frameworks.Objects private import semmle.code.java.frameworks.Optional + private import semmle.code.java.frameworks.Stream private import semmle.code.java.frameworks.Strings + private import semmle.code.java.frameworks.ratpack.Ratpack + private import semmle.code.java.frameworks.ratpack.RatpackExec private import semmle.code.java.frameworks.spring.SpringCache private import semmle.code.java.frameworks.spring.SpringHttp private import semmle.code.java.frameworks.spring.SpringUtil @@ -111,8 +117,6 @@ private module Frameworks { private import semmle.code.java.security.OgnlInjection private import semmle.code.java.security.XPath private import semmle.code.java.security.XsltInjection - private import semmle.code.java.frameworks.android.Android - private import semmle.code.java.frameworks.android.SQLite private import semmle.code.java.frameworks.Jdbc private import semmle.code.java.frameworks.SpringJdbc private import semmle.code.java.frameworks.MyBatis @@ -309,6 +313,8 @@ private predicate summaryModelCsv(string row) { "java.util;Base64$Decoder;false;decode;(ByteBuffer);;Argument[0];ReturnValue;taint", "java.util;Base64$Decoder;false;decode;(String);;Argument[0];ReturnValue;taint", "java.util;Base64$Decoder;false;wrap;(InputStream);;Argument[0];ReturnValue;taint", + "cn.hutool.core.codec;Base64;true;decode;;;Argument[0];ReturnValue;taint", + "org.apache.shiro.codec;Base64;false;decode;(String);;Argument[0];ReturnValue;taint", "org.apache.commons.codec;Encoder;true;encode;(Object);;Argument[0];ReturnValue;taint", "org.apache.commons.codec;Decoder;true;decode;(Object);;Argument[0];ReturnValue;taint", "org.apache.commons.codec;BinaryEncoder;true;encode;(byte[]);;Argument[0];ReturnValue;taint", @@ -571,7 +577,7 @@ module CsvValidation { not (part = "Argument" and pred = "sink") and not parseArg(part, _) or - specSplit(input, part, _) and + part = specLast(input) and parseParam(part, _) ) and msg = "Unrecognized input specification \"" + part + "\" in " + pred + " model." @@ -639,6 +645,7 @@ private string paramsStringPart(Callable c, int i) { * Returns the empty string if the callable has no parameters. * Parameter types are represented by their type erasure. */ +cached string paramsString(Callable c) { result = concat(int i | | paramsStringPart(c, i) order by i) } private Element interpretElement0( diff --git a/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll b/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll index a8f15a103c8..d8d02f995a6 100644 --- a/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll +++ b/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll @@ -45,8 +45,8 @@ private class RmiMethodParameterSource extends RemoteFlowSource { exists(RemoteCallableMethod method | method.getAParameter() = this.asParameter() and ( - getType() instanceof PrimitiveType or - getType() instanceof TypeString + this.getType() instanceof PrimitiveType or + this.getType() instanceof TypeString ) ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll b/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll index 0017582c0d9..ae9c88fb118 100644 --- a/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll +++ b/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll @@ -103,3 +103,19 @@ private class NumberTaintPreservingCallable extends TaintPreservingCallable { override predicate returnsTaintFrom(int arg) { arg = argument } } + +/** + * A `Content` that should be implicitly regarded as tainted whenever an object with such `Content` + * is itself tainted. + * + * For example, if we had a type `class Container { Contained field; }`, then by default a tainted + * `Container` and a `Container` with a tainted `Contained` stored in its `field` are distinct. + * + * If `any(DataFlow::FieldContent fc | fc.getField().hasQualifiedName("Container", "field"))` was + * included in this type however, then a tainted `Container` would imply that its `field` is also + * tainted (but not vice versa). + * + * Note that `TaintTracking::Configuration` applies this behaviour by default to array, collection, + * map-key and map-value content, so that e.g. a tainted `Map` is assumed to have tainted keys and values. + */ +abstract class TaintInheritingContent extends DataFlow::Content { } diff --git a/java/ql/lib/semmle/code/java/dataflow/SSA.qll b/java/ql/lib/semmle/code/java/dataflow/SSA.qll index 9a2a1df0915..dbcaafd3071 100644 --- a/java/ql/lib/semmle/code/java/dataflow/SSA.qll +++ b/java/ql/lib/semmle/code/java/dataflow/SSA.qll @@ -97,7 +97,7 @@ class SsaSourceVariable extends TSsaSourceVariable { else result = c.getName() + "(..)." + v.getName() ) or - result = this.(SsaSourceField).ppQualifier() + "." + getVariable().toString() + result = this.(SsaSourceField).ppQualifier() + "." + this.getVariable().toString() } /** @@ -117,7 +117,7 @@ class SsaSourceVariable extends TSsaSourceVariable { Location getLocation() { exists(LocalScopeVariable v | this = TLocalVar(_, v) and result = v.getLocation()) or - this instanceof SsaSourceField and result = getFirstAccess().getLocation() + this instanceof SsaSourceField and result = this.getFirstAccess().getLocation() } /** Gets the type of this variable. */ @@ -140,7 +140,7 @@ class SsaSourceField extends SsaSourceVariable { } /** Gets the field corresponding to this named field. */ - Field getField() { result = getVariable() } + Field getField() { result = this.getVariable() } /** Gets a string representation of the qualifier. */ string ppQualifier() { @@ -155,8 +155,8 @@ class SsaSourceField extends SsaSourceVariable { /** Holds if the field itself or any of the fields part of the qualifier are volatile. */ predicate isVolatile() { - getField().isVolatile() or - getQualifier().(SsaSourceField).isVolatile() + this.getField().isVolatile() or + this.getQualifier().(SsaSourceField).isVolatile() } } @@ -932,10 +932,10 @@ class SsaVariable extends TSsaVariable { string toString() { none() } /** Gets the source location for this element. */ - Location getLocation() { result = getCFGNode().getLocation() } + Location getLocation() { result = this.getCFGNode().getLocation() } /** Gets the `BasicBlock` in which this SSA variable is defined. */ - BasicBlock getBasicBlock() { result = getCFGNode().getBasicBlock() } + BasicBlock getBasicBlock() { result = this.getCFGNode().getBasicBlock() } /** Gets an access of this SSA variable. */ RValue getAUse() { @@ -989,14 +989,16 @@ class SsaUpdate extends SsaVariable { /** An SSA variable that is defined by a `VariableUpdate`. */ class SsaExplicitUpdate extends SsaUpdate, TSsaCertainUpdate { SsaExplicitUpdate() { - exists(VariableUpdate upd | upd = this.getCFGNode() and getDestVar(upd) = getSourceVariable()) + exists(VariableUpdate upd | + upd = this.getCFGNode() and getDestVar(upd) = this.getSourceVariable() + ) } - override string toString() { result = "SSA def(" + getSourceVariable() + ")" } + override string toString() { result = "SSA def(" + this.getSourceVariable() + ")" } /** Gets the `VariableUpdate` defining the SSA variable. */ VariableUpdate getDefiningExpr() { - result = this.getCFGNode() and getDestVar(result) = getSourceVariable() + result = this.getCFGNode() and getDestVar(result) = this.getSourceVariable() } } @@ -1010,22 +1012,22 @@ class SsaImplicitUpdate extends SsaUpdate { SsaImplicitUpdate() { not this instanceof SsaExplicitUpdate } override string toString() { - result = "SSA impl upd[" + getKind() + "](" + getSourceVariable() + ")" + result = "SSA impl upd[" + this.getKind() + "](" + this.getSourceVariable() + ")" } private string getKind() { this = TSsaUntracked(_, _) and result = "untracked" or - certainVariableUpdate(getSourceVariable().getQualifier(), getCFGNode(), _, _) and + certainVariableUpdate(this.getSourceVariable().getQualifier(), this.getCFGNode(), _, _) and result = "explicit qualifier" or - if uncertainVariableUpdate(getSourceVariable().getQualifier(), getCFGNode(), _, _) + if uncertainVariableUpdate(this.getSourceVariable().getQualifier(), this.getCFGNode(), _, _) then - if exists(getANonLocalUpdate()) + if exists(this.getANonLocalUpdate()) then result = "nonlocal + nonlocal qualifier" else result = "nonlocal qualifier" else ( - exists(getANonLocalUpdate()) and result = "nonlocal" + exists(this.getANonLocalUpdate()) and result = "nonlocal" ) } @@ -1034,9 +1036,9 @@ class SsaImplicitUpdate extends SsaUpdate { */ FieldWrite getANonLocalUpdate() { exists(SsaSourceField f, Callable setter | - f = getSourceVariable() and + f = this.getSourceVariable() and relevantFieldUpdate(setter, f.getField(), result) and - updatesNamedField(getCFGNode(), f, setter) + updatesNamedField(this.getCFGNode(), f, setter) ) } @@ -1049,8 +1051,8 @@ class SsaImplicitUpdate extends SsaUpdate { */ predicate assignsUnknownValue() { this = TSsaUntracked(_, _) or - certainVariableUpdate(getSourceVariable().getQualifier(), getCFGNode(), _, _) or - uncertainVariableUpdate(getSourceVariable().getQualifier(), getCFGNode(), _, _) + certainVariableUpdate(this.getSourceVariable().getQualifier(), this.getCFGNode(), _, _) or + uncertainVariableUpdate(this.getSourceVariable().getQualifier(), this.getCFGNode(), _, _) } } @@ -1072,30 +1074,31 @@ class SsaUncertainImplicitUpdate extends SsaImplicitUpdate, TSsaUncertainUpdate * includes initial values of parameters, fields, and closure variables. */ class SsaImplicitInit extends SsaVariable, TSsaEntryDef { - override string toString() { result = "SSA init(" + getSourceVariable() + ")" } + override string toString() { result = "SSA init(" + this.getSourceVariable() + ")" } /** Holds if this is a closure variable that captures the value of `capturedvar`. */ predicate captures(SsaVariable capturedvar) { - ssaDefReachesCapture(_, capturedvar, getSourceVariable()) + ssaDefReachesCapture(_, capturedvar, this.getSourceVariable()) } /** * Holds if the SSA variable is a parameter defined by its initial value in the callable. */ predicate isParameterDefinition(Parameter p) { - getSourceVariable() = TLocalVar(p.getCallable(), p) and p.getCallable().getBody() = getCFGNode() + this.getSourceVariable() = TLocalVar(p.getCallable(), p) and + p.getCallable().getBody() = this.getCFGNode() } } /** An SSA phi node. */ class SsaPhiNode extends SsaVariable, TSsaPhiNode { - override string toString() { result = "SSA phi(" + getSourceVariable() + ")" } + override string toString() { result = "SSA phi(" + this.getSourceVariable() + ")" } /** Gets an input to the phi node defining the SSA variable. */ SsaVariable getAPhiInput() { exists(BasicBlock phiPred, TrackedVar v | - v = getSourceVariable() and - getCFGNode().(BasicBlock).getABBPredecessor() = phiPred and + v = this.getSourceVariable() and + this.getCFGNode().(BasicBlock).getABBPredecessor() = phiPred and ssaDefReachesEndOfBlock(v, result, phiPred) ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/StringPrefixes.qll b/java/ql/lib/semmle/code/java/dataflow/StringPrefixes.qll new file mode 100644 index 00000000000..755deaba532 --- /dev/null +++ b/java/ql/lib/semmle/code/java/dataflow/StringPrefixes.qll @@ -0,0 +1,178 @@ +/** + * Provides classes and predicates for identifying expressions that may be appended to an interesting prefix. + * + * To use this library, extend the abstract class `InterestingPrefix` to have the library identify expressions that + * may be appended to it, then check `InterestingPrefix.getAnAppendedExpression(Expr)` to get your results. + * + * For example, to identify expressions that may follow "foo:" in some string, we could define: + * + * ``` + * private class FooPrefix extends InterestingPrefix { + * int offset; + * FooPrefix() { this.getStringValue().substring("foo:") = offset }; + * override int getOffset() { result = offset } + * }; + * + * predicate mayFollowFoo(Expr e) { e = any(FooPrefix fp).getAnAppendedExpression() } + * ``` + * + * This will identify all the `suffix` expressions in contexts such as: + * + * ``` + * "foo:" + suffix1 + * "barfoo:" + suffix2 + * stringBuilder.append("foo:").append(suffix3); + * String.format("%sfoo:%s", notSuffix, suffix4); + * ``` + */ + +import java +private import semmle.code.java.dataflow.TaintTracking +private import semmle.code.java.StringFormat + +/** + * A string constant that contains a prefix whose possibly-appended strings are + * returned by `getAnAppendedExpression`. + * + * Extend this class to specify prefixes whose possibly-appended strings should be analysed. + */ +abstract class InterestingPrefix extends CompileTimeConstantExpr { + /** + * Gets the offset in this constant string where the interesting prefix begins. + */ + abstract int getOffset(); + + /** + * Gets an expression that may follow this prefix in a derived string. + */ + Expr getAnAppendedExpression() { mayFollowInterestingPrefix(this, result) } +} + +private Expr getAnInterestingPrefix(InterestingPrefix root) { + result = root + or + result.(AddExpr).getAnOperand() = getAnInterestingPrefix(root) +} + +private class StringBuilderAppend extends MethodAccess { + StringBuilderAppend() { + this.getMethod().getDeclaringType() instanceof StringBuildingType and + this.getMethod().hasName("append") + } +} + +private class StringBuilderConstructorOrAppend extends Call { + StringBuilderConstructorOrAppend() { + this instanceof StringBuilderAppend or + this.(ClassInstanceExpr).getConstructedType() instanceof StringBuildingType + } +} + +private Expr getQualifier(Expr e) { result = e.(MethodAccess).getQualifier() } + +/** + * An extension of `StringBuilderVar` that also accounts for strings appended in StringBuilder/Buffer's constructor + * and in `append` calls chained onto the constructor call. + * + * The original `StringBuilderVar` doesn't care about these because it is designed to model taint, and + * in taint rules terms these are not needed, as the connection between construction, appends and the + * eventual `toString` is more obvious. + */ +private class StringBuilderVarExt extends StringBuilderVar { + /** + * Returns a first assignment after this StringBuilderVar is first assigned. + * + * For example, for `StringBuilder sbv = new StringBuilder("1").append("2"); sbv.append("3").append("4");` + * this returns the append of `"3"`. + */ + private StringBuilderAppend getAFirstAppendAfterAssignment() { + result = this.getAnAppend() and not result = this.getNextAppend(_) + } + + /** + * Gets the next `append` after `prev`, where `prev` is, perhaps after some more `append` or other + * chained calls, assigned to this `StringBuilderVar`. + */ + private StringBuilderAppend getNextAssignmentChainedAppend(StringBuilderConstructorOrAppend prev) { + getQualifier*(result) = this.getAnAssignedValue() and + result.getQualifier() = prev + } + + /** + * Get a constructor call or `append` call that contributes a string to this string builder. + */ + StringBuilderConstructorOrAppend getAConstructorOrAppend() { + exists(this.getNextAssignmentChainedAppend(result)) or + result = this.getAnAssignedValue() or + result = this.getAnAppend() + } + + /** + * Like `StringBuilderVar.getNextAppend`, except including appends and constructors directly + * assigned to this `StringBuilderVar`. + */ + private StringBuilderAppend getNextAppendIncludingAssignmentChains( + StringBuilderConstructorOrAppend prev + ) { + result = this.getNextAssignmentChainedAppend(prev) + or + prev = this.getAnAssignedValue() and + result = this.getAFirstAppendAfterAssignment() + or + result = this.getNextAppend(prev) + } + + /** + * Implements `StringBuilderVarExt.getNextAppendIncludingAssignmentChains+(prev)`. + */ + pragma[nomagic] + StringBuilderAppend getSubsequentAppendIncludingAssignmentChains( + StringBuilderConstructorOrAppend prev + ) { + result = this.getNextAppendIncludingAssignmentChains(prev) or + result = + this.getSubsequentAppendIncludingAssignmentChains(this.getNextAppendIncludingAssignmentChains(prev)) + } +} + +/** + * Holds if `follows` may be concatenated after `prefix`. + */ +private predicate mayFollowInterestingPrefix(InterestingPrefix prefix, Expr follows) { + // Expressions that come after an interesting prefix in a tree of string additions: + follows = + any(AddExpr add | add.getLeftOperand() = getAnInterestingPrefix(prefix)).getRightOperand() + or + // Sanitize expressions that come after an interesting prefix in a sequence of StringBuilder operations: + exists( + StringBuilderConstructorOrAppend appendSanitizingConstant, StringBuilderAppend subsequentAppend, + StringBuilderVarExt v + | + appendSanitizingConstant = v.getAConstructorOrAppend() and + appendSanitizingConstant.getArgument(0) = getAnInterestingPrefix(prefix) and + v.getSubsequentAppendIncludingAssignmentChains(appendSanitizingConstant) = subsequentAppend and + follows = subsequentAppend.getArgument(0) + ) + or + // Sanitize expressions that come after an interesting prefix in the args to a format call: + exists( + FormattingCall formatCall, FormatString formatString, int prefixOffset, int laterOffset, + int sanitizedArg + | + formatString = unique(FormatString fs | fs = formatCall.getAFormatString()) and + ( + // An interesting prefix argument comes before this: + exists(int argIdx | + formatCall.getArgumentToBeFormatted(argIdx) = prefix and + prefixOffset = formatString.getAnArgUsageOffset(argIdx) + ) + or + // The format string itself contains an interesting prefix that precedes subsequent arguments: + formatString = prefix.getStringValue() and + prefixOffset = prefix.getOffset() + ) and + laterOffset > prefixOffset and + laterOffset = formatString.getAnArgUsageOffset(sanitizedArg) and + follows = formatCall.getArgumentToBeFormatted(sanitizedArg) + ) +} diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll b/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll index 8193a33bcb3..e0e6e64321f 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll @@ -484,10 +484,10 @@ class BaseSsaVariable extends TBaseSsaVariable { string toString() { none() } - Location getLocation() { result = getCFGNode().getLocation() } + Location getLocation() { result = this.getCFGNode().getLocation() } /** Gets the `BasicBlock` in which this SSA variable is defined. */ - BasicBlock getBasicBlock() { result = getCFGNode().getBasicBlock() } + BasicBlock getBasicBlock() { result = this.getCFGNode().getBasicBlock() } /** Gets an access of this SSA variable. */ RValue getAUse() { ssaDefReachesUse(_, this, result) } @@ -532,14 +532,16 @@ class BaseSsaVariable extends TBaseSsaVariable { /** An SSA variable that is defined by a `VariableUpdate`. */ class BaseSsaUpdate extends BaseSsaVariable, TSsaUpdate { BaseSsaUpdate() { - exists(VariableUpdate upd | upd = this.getCFGNode() and getDestVar(upd) = getSourceVariable()) + exists(VariableUpdate upd | + upd = this.getCFGNode() and getDestVar(upd) = this.getSourceVariable() + ) } - override string toString() { result = "SSA def(" + getSourceVariable() + ")" } + override string toString() { result = "SSA def(" + this.getSourceVariable() + ")" } /** Gets the `VariableUpdate` defining the SSA variable. */ VariableUpdate getDefiningExpr() { - result = this.getCFGNode() and getDestVar(result) = getSourceVariable() + result = this.getCFGNode() and getDestVar(result) = this.getSourceVariable() } } @@ -548,30 +550,31 @@ class BaseSsaUpdate extends BaseSsaVariable, TSsaUpdate { * includes initial values of parameters, fields, and closure variables. */ class BaseSsaImplicitInit extends BaseSsaVariable, TSsaEntryDef { - override string toString() { result = "SSA init(" + getSourceVariable() + ")" } + override string toString() { result = "SSA init(" + this.getSourceVariable() + ")" } /** Holds if this is a closure variable that captures the value of `capturedvar`. */ predicate captures(BaseSsaVariable capturedvar) { - ssaDefReachesCapture(_, capturedvar, getSourceVariable()) + ssaDefReachesCapture(_, capturedvar, this.getSourceVariable()) } /** * Holds if the SSA variable is a parameter defined by its initial value in the callable. */ predicate isParameterDefinition(Parameter p) { - getSourceVariable() = TLocalVar(p.getCallable(), p) and p.getCallable().getBody() = getCFGNode() + this.getSourceVariable() = TLocalVar(p.getCallable(), p) and + p.getCallable().getBody() = this.getCFGNode() } } /** An SSA phi node. */ class BaseSsaPhiNode extends BaseSsaVariable, TSsaPhiNode { - override string toString() { result = "SSA phi(" + getSourceVariable() + ")" } + override string toString() { result = "SSA phi(" + this.getSourceVariable() + ")" } /** Gets an input to the phi node defining the SSA variable. */ BaseSsaVariable getAPhiInput() { exists(BasicBlock phiPred, BaseSsaSourceVariable v | - v = getSourceVariable() and - getCFGNode().(BasicBlock).getABBPredecessor() = phiPred and + v = this.getSourceVariable() and + this.getCFGNode().(BasicBlock).getABBPredecessor() = phiPred and ssaDefReachesEndOfBlock(v, result, phiPred) ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll index 6c72bbd451b..79fea09de4c 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll @@ -104,7 +104,9 @@ private class ContainerFlowSummaries extends SummaryModelCsv { "java.util;Map$Entry;true;setValue;;;Argument[0];MapValue of Argument[-1];value", "java.lang;Iterable;true;iterator;();;Element of Argument[-1];Element of ReturnValue;value", "java.lang;Iterable;true;spliterator;();;Element of Argument[-1];Element of ReturnValue;value", + "java.lang;Iterable;true;forEach;(Consumer);;Element of Argument[-1];Parameter[0] of Argument[0];value", "java.util;Iterator;true;next;;;Element of Argument[-1];ReturnValue;value", + "java.util;Iterator;true;forEachRemaining;(Consumer);;Element of Argument[-1];Parameter[0] of Argument[0];value", "java.util;ListIterator;true;previous;;;Element of Argument[-1];ReturnValue;value", "java.util;ListIterator;true;add;(Object);;Argument[0];Element of Argument[-1];value", "java.util;ListIterator;true;set;(Object);;Argument[0];Element of Argument[-1];value", @@ -135,6 +137,8 @@ private class ContainerFlowSummaries extends SummaryModelCsv { "java.util;Map;true;merge;(Object,Object,BiFunction);;Argument[1];MapValue of Argument[-1];value", "java.util;Map;true;putAll;(Map);;MapKey of Argument[0];MapKey of Argument[-1];value", "java.util;Map;true;putAll;(Map);;MapValue of Argument[0];MapValue of Argument[-1];value", + "java.util;Map;true;forEach;(BiConsumer);;MapKey of Argument[-1];Parameter[0] of Argument[0];value", + "java.util;Map;true;forEach;(BiConsumer);;MapValue of Argument[-1];Parameter[1] of Argument[0];value", "java.util;Collection;true;parallelStream;();;Element of Argument[-1];Element of ReturnValue;value", "java.util;Collection;true;stream;();;Element of Argument[-1];Element of ReturnValue;value", "java.util;Collection;true;toArray;;;Element of Argument[-1];ArrayElement of ReturnValue;value", diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll index f588a25a176..e11244c42b0 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -801,6 +801,9 @@ private module Cached { exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) } + cached + predicate allowParameterReturnInSelfCached(ParamNode p) { allowParameterReturnInSelf(p) } + cached newtype TCallContext = TAnyCallContext() or @@ -937,7 +940,7 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall { } override predicate relevantFor(DataFlowCallable callable) { - recordDataFlowCallSite(getCall(), callable) + recordDataFlowCallSite(this.getCall(), callable) } override predicate matchesCall(DataFlowCall call) { call = this.getCall() } @@ -1236,6 +1239,13 @@ class TypedContent extends MkTypedContent { /** Gets a textual representation of this content. */ string toString() { result = c.toString() } + + /** + * Holds if access paths with this `TypedContent` at their head always should + * be tracked at high precision. This disables adaptive access path precision + * for such access paths. + */ + predicate forceHighPrecision() { forceHighPrecision(c) } } /** @@ -1250,7 +1260,7 @@ abstract class AccessPathFront extends TAccessPathFront { TypedContent getHead() { this = TFrontHead(result) } - predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) } + predicate isClearedAt(Node n) { clearsContentCached(n, this.getHead().getContent()) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll index a55e65a81f6..dd64fc70039 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll @@ -175,6 +175,7 @@ module Consistency { query predicate postWithInFlow(Node n, string msg) { isPostUpdateNode(n) and + not clearsContent(n, _) and simpleLocalFlowStep(_, n) and msg = "PostUpdateNode should not be the target of local flow." } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll index 6cc7906ab33..5ea421db0c7 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll @@ -117,9 +117,9 @@ module Public { * Gets an upper bound on the type of this node. */ Type getTypeBound() { - result = getImprovedTypeBound() + result = this.getImprovedTypeBound() or - result = getType() and not exists(getImprovedTypeBound()) + result = this.getType() and not exists(this.getImprovedTypeBound()) } /** @@ -127,12 +127,12 @@ module Public { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { - getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) } } @@ -288,9 +288,9 @@ private class NewExpr extends PostUpdateNode, TExprNode { * A `PostUpdateNode` that is not a `ClassInstanceExpr`. */ abstract private class ImplicitPostUpdateNode extends PostUpdateNode { - override Location getLocation() { result = getPreUpdateNode().getLocation() } + override Location getLocation() { result = this.getPreUpdateNode().getLocation() } - override string toString() { result = getPreUpdateNode().toString() + " [post update]" } + override string toString() { result = this.getPreUpdateNode().toString() + " [post update]" } } private class ExplicitExprPostUpdate extends ImplicitPostUpdateNode, TExplicitExprPostUpdate { diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll index 1d24e027869..d0df4c33cf5 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll @@ -139,11 +139,7 @@ predicate readStep(Node node1, Content f, Node node2) { */ predicate clearsContent(Node n, Content c) { c instanceof FieldContent and - ( - n = any(PostUpdateNode pun | storeStep(_, c, pun)).getPreUpdateNode() - or - FlowSummaryImpl::Private::Steps::summaryStoresIntoArg(c, n) - ) + n = any(PostUpdateNode pun | storeStep(_, c, pun)).getPreUpdateNode() or FlowSummaryImpl::Private::Steps::summaryClearsContent(n, c) } @@ -234,6 +230,19 @@ class DataFlowCall extends TDataFlowCall { /** Gets the location of this call. */ abstract Location getLocation(); + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + final predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } } /** A source call, that is, a `Call`. */ @@ -308,6 +317,14 @@ predicate isUnreachableInCall(Node n, DataFlowCall call) { int accessPathLimit() { result = 5 } +/** + * Holds if access paths with `c` at their head always should be tracked at high + * precision. This disables adaptive access path precision for such access paths. + */ +predicate forceHighPrecision(Content c) { + c instanceof ArrayContent or c instanceof CollectionContent +} + /** * Holds if `n` does not require a `PostUpdateNode` as it either cannot be * modified or its modification cannot be observed, for example if it is a @@ -348,3 +365,14 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { /** Extra data-flow steps needed for lambda flow analysis. */ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() } + +/** + * Holds if flow is allowed to pass from parameter `p` and back to itself as a + * side-effect, resulting in a summary from `p` to itself. + * + * One example would be to allow flow like `p.foo = p.bar;`, which is disallowed + * by default as a heuristic. + */ +predicate allowParameterReturnInSelf(ParameterNode p) { + FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(p) +} diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll index f00bc34f055..8146e8ddc29 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll @@ -101,6 +101,8 @@ predicate hasNonlocalValue(FieldRead fr) { predicate localFlowStep(Node node1, Node node2) { simpleLocalFlowStep(node1, node2) or + adjacentUseUse(node1.asExpr(), node2.asExpr()) + or // Simple flow through library code is included in the exposed local // step relation, even though flow is technically inter-procedural FlowSummaryImpl::Private::Steps::summaryThroughStep(node1, node2, true) @@ -131,7 +133,8 @@ predicate simpleLocalFlowStep(Node node1, Node node2) { adjacentUseUse(node1.asExpr(), node2.asExpr()) and not exists(FieldRead fr | hasNonlocalValue(fr) and fr.getField().isStatic() and fr = node1.asExpr() - ) + ) and + not FlowSummaryImpl::Private::Steps::summaryClearsContentArg(node1, _) or ThisFlow::adjacentThisRefs(node1, node2) or @@ -182,7 +185,7 @@ class Content extends TContent { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0 diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll b/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll index 523516e60f8..5955285bd6f 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll @@ -186,10 +186,17 @@ module Private { TArgumentSummaryComponent(int i) { parameterPosition(i) } or TReturnSummaryComponent(ReturnKind rk) + private TSummaryComponent thisParam() { + result = TParameterSummaryComponent(instanceParameterPosition()) + } + newtype TSummaryComponentStack = TSingletonSummaryComponentStack(SummaryComponent c) or TConsSummaryComponentStack(SummaryComponent head, SummaryComponentStack tail) { tail.(RequiredSummaryComponentStack).required(head) + or + tail.(RequiredSummaryComponentStack).required(TParameterSummaryComponent(_)) and + head = thisParam() } pragma[nomagic] @@ -198,20 +205,65 @@ module Private { boolean preservesValue ) { c.propagatesFlow(input, output, preservesValue) + or + // observe side effects of callbacks on input arguments + c.propagatesFlow(output, input, preservesValue) and + preservesValue = true and + isCallbackParameter(input) and + isContentOfArgument(output) + or + // flow from the receiver of a callback into the instance-parameter + exists(SummaryComponentStack s, SummaryComponentStack callbackRef | + c.propagatesFlow(s, _, _) or c.propagatesFlow(_, s, _) + | + callbackRef = s.drop(_) and + (isCallbackParameter(callbackRef) or callbackRef.head() = TReturnSummaryComponent(_)) and + input = callbackRef.tail() and + output = TConsSummaryComponentStack(thisParam(), input) and + preservesValue = true + ) + } + + private predicate isCallbackParameter(SummaryComponentStack s) { + s.head() = TParameterSummaryComponent(_) and exists(s.tail()) + } + + private predicate isContentOfArgument(SummaryComponentStack s) { + s.head() = TContentSummaryComponent(_) and isContentOfArgument(s.tail()) + or + s = TSingletonSummaryComponentStack(TArgumentSummaryComponent(_)) + } + + private predicate outputState(SummarizedCallable c, SummaryComponentStack s) { + summary(c, _, s, _) + or + exists(SummaryComponentStack out | + outputState(c, out) and + out.head() = TContentSummaryComponent(_) and + s = out.tail() + ) + or + // Add the argument node corresponding to the requested post-update node + inputState(c, s) and isCallbackParameter(s) + } + + private predicate inputState(SummarizedCallable c, SummaryComponentStack s) { + summary(c, s, _, _) + or + exists(SummaryComponentStack inp | inputState(c, inp) and s = inp.tail()) + or + exists(SummaryComponentStack out | + outputState(c, out) and + out.head() = TParameterSummaryComponent(_) and + s = out.tail() + ) } private newtype TSummaryNodeState = - TSummaryNodeInputState(SummaryComponentStack s) { - exists(SummaryComponentStack input | - summary(_, input, _, _) and - s = input.drop(_) - ) - } or - TSummaryNodeOutputState(SummaryComponentStack s) { - exists(SummaryComponentStack output | - summary(_, _, output, _) and - s = output.drop(_) - ) + TSummaryNodeInputState(SummaryComponentStack s) { inputState(_, s) } or + TSummaryNodeOutputState(SummaryComponentStack s) { outputState(_, s) } or + TSummaryNodeClearsContentState(int i, boolean post) { + any(SummarizedCallable sc).clearsContent(i, _) and post in [false, true] } /** @@ -238,20 +290,14 @@ module Private { pragma[nomagic] predicate isInputState(SummarizedCallable c, SummaryComponentStack s) { this = TSummaryNodeInputState(s) and - exists(SummaryComponentStack input | - summary(c, input, _, _) and - s = input.drop(_) - ) + inputState(c, s) } /** Holds if this state is a valid output state for `c`. */ pragma[nomagic] predicate isOutputState(SummarizedCallable c, SummaryComponentStack s) { this = TSummaryNodeOutputState(s) and - exists(SummaryComponentStack output | - summary(c, _, output, _) and - s = output.drop(_) - ) + outputState(c, s) } /** Gets a textual representation of this state. */ @@ -265,6 +311,12 @@ module Private { this = TSummaryNodeOutputState(s) and result = "to write: " + s ) + or + exists(int i, boolean post, string postStr | + this = TSummaryNodeClearsContentState(i, post) and + (if post = true then postStr = " (post)" else postStr = "") and + result = "clear: " + i + postStr + ) } } @@ -286,6 +338,11 @@ module Private { not parameterReadState(c, state, _) or state.isOutputState(c, _) + or + exists(int i | + c.clearsContent(i, _) and + state = TSummaryNodeClearsContentState(i, _) + ) } pragma[noinline] @@ -321,6 +378,8 @@ module Private { parameterReadState(c, _, i) or isParameterPostUpdate(_, c, i) + or + c.clearsContent(i, _) } private predicate callbackOutput( @@ -331,19 +390,12 @@ module Private { receiver = summaryNodeInputState(c, s.drop(1)) } - private Node pre(Node post) { - summaryPostUpdateNode(post, result) - or - not summaryPostUpdateNode(post, _) and - result = post - } - private predicate callbackInput( SummarizedCallable c, SummaryComponentStack s, Node receiver, int i ) { any(SummaryNodeState state).isOutputState(c, s) and s.head() = TParameterSummaryComponent(i) and - receiver = pre(summaryNodeOutputState(c, s.drop(1))) + receiver = summaryNodeInputState(c, s.drop(1)) } /** Holds if a call targeting `receiver` should be synthesized inside `c`. */ @@ -395,11 +447,17 @@ module Private { or exists(int i | head = TParameterSummaryComponent(i) | result = - getCallbackParameterType(getNodeType(summaryNodeOutputState(pragma[only_bind_out](c), + getCallbackParameterType(getNodeType(summaryNodeInputState(pragma[only_bind_out](c), s.drop(1))), i) ) ) ) + or + exists(SummarizedCallable c, int i, ParamNode p | + n = summaryNode(c, TSummaryNodeClearsContentState(i, false)) and + p.isParameterOf(c, i) and + result = getNodeType(p) + ) } /** Holds if summary node `out` contains output of kind `rk` from call `c`. */ @@ -421,10 +479,19 @@ module Private { } /** Holds if summary node `post` is a post-update node with pre-update node `pre`. */ - predicate summaryPostUpdateNode(Node post, ParamNode pre) { + predicate summaryPostUpdateNode(Node post, Node pre) { exists(SummarizedCallable c, int i | isParameterPostUpdate(post, c, i) and - pre.isParameterOf(c, i) + pre.(ParamNode).isParameterOf(c, i) + or + pre = summaryNode(c, TSummaryNodeClearsContentState(i, false)) and + post = summaryNode(c, TSummaryNodeClearsContentState(i, true)) + ) + or + exists(SummarizedCallable callable, SummaryComponentStack s | + callbackInput(callable, s, _, _) and + pre = summaryNodeOutputState(callable, s) and + post = summaryNodeInputState(callable, s) ) } @@ -436,6 +503,17 @@ module Private { ) } + /** + * Holds if flow is allowed to pass from parameter `p`, to a return + * node, and back out to `p`. + */ + predicate summaryAllowParameterReturnInSelf(ParamNode p) { + exists(SummarizedCallable c, int i | + c.clearsContent(i, _) and + p.isParameterOf(c, i) + ) + } + /** Provides a compilation of flow summaries to atomic data-flow steps. */ module Steps { /** @@ -462,7 +540,21 @@ module Private { // for `StringBuilder.append(x)` with a specified value flow from qualifier to // return value and taint flow from argument 0 to the qualifier, then this // allows us to infer taint flow from argument 0 to the return value. - summaryPostUpdateNode(pred, succ) and preservesValue = true + succ instanceof ParamNode and + summaryPostUpdateNode(pred, succ) and + preservesValue = true + or + // Similarly we would like to chain together summaries where values get passed + // into callbacks along the way. + pred instanceof ArgNode and + summaryPostUpdateNode(succ, pred) and + preservesValue = true + or + exists(SummarizedCallable c, int i | + pred.(ParamNode).isParameterOf(c, i) and + succ = summaryNode(c, TSummaryNodeClearsContentState(i, _)) and + preservesValue = true + ) } /** @@ -490,10 +582,39 @@ module Private { } /** - * Holds if values stored inside content `c` are cleared when passed as - * input of type `input` in `call`. + * Holds if values stored inside content `c` are cleared at `n`. `n` is a + * synthesized summary node, so in order for values to be cleared at calls + * to the relevant method, it is important that flow does not pass over + * the argument, either via use-use flow or def-use flow. + * + * Example: + * + * ``` + * a.b = taint; + * a.clearB(); // assume we have a flow summary for `clearB` that clears `b` on the qualifier + * sink(a.b); + * ``` + * + * In the above, flow should not pass from `a` on the first line (or the second + * line) to `a` on the third line. Instead, there will be synthesized flow from + * `a` on line 2 to the post-update node for `a` on that line (via an intermediate + * node where field `b` is cleared). */ - predicate summaryClearsContent(ArgNode arg, Content c) { + predicate summaryClearsContent(Node n, Content c) { + exists(SummarizedCallable sc, int i | + n = summaryNode(sc, TSummaryNodeClearsContentState(i, true)) and + sc.clearsContent(i, c) + ) + } + + /** + * Holds if values stored inside content `c` are cleared inside a + * callable to which `arg` is an argument. + * + * In such cases, it is important to prevent use-use flow out of + * `arg` (see comment for `summaryClearsContent`). + */ + predicate summaryClearsContentArg(ArgNode arg, Content c) { exists(DataFlowCall call, int i | viableCallable(call).(SummarizedCallable).clearsContent(i, c) and arg.argumentOf(call, i) @@ -553,25 +674,6 @@ module Private { ret.getKind() = rk ) } - - /** - * Holds if data is written into content `c` of argument `arg` using a flow summary. - * - * Depending on the type of `c`, this predicate may be relevant to include in the - * definition of `clearsContent()`. - */ - predicate summaryStoresIntoArg(Content c, Node arg) { - exists(ParamUpdateReturnKind rk, ReturnNodeExt ret, PostUpdateNode out | - exists(DataFlowCall call, SummarizedCallable callable | - getNodeEnclosingCallable(ret) = callable and - viableCallable(call) = callable and - summaryStoreStep(_, c, ret) and - ret.getKind() = pragma[only_bind_into](rk) and - out = rk.getAnOutNode(call) and - arg = out.getPreUpdateNode() - ) - ) - } } /** @@ -824,4 +926,95 @@ module Private { ) } } + + /** + * Provides query predicates for rendering the generated data flow graph for + * a summarized callable. + * + * Import this module into a `.ql` file of `@kind graph` to render the graph. + * The graph is restricted to callables from `RelevantSummarizedCallable`. + */ + module RenderSummarizedCallable { + /** A summarized callable to include in the graph. */ + abstract class RelevantSummarizedCallable extends SummarizedCallable { } + + private newtype TNodeOrCall = + MkNode(Node n) { + exists(RelevantSummarizedCallable c | + n = summaryNode(c, _) + or + n.(ParamNode).isParameterOf(c, _) + ) + } or + MkCall(DataFlowCall call) { + call = summaryDataFlowCall(_) and + call.getEnclosingCallable() instanceof RelevantSummarizedCallable + } + + private class NodeOrCall extends TNodeOrCall { + Node asNode() { this = MkNode(result) } + + DataFlowCall asCall() { this = MkCall(result) } + + string toString() { + result = this.asNode().toString() + or + result = this.asCall().toString() + } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.asNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + or + this.asCall().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + } + + query predicate nodes(NodeOrCall n, string key, string val) { + key = "semmle.label" and val = n.toString() + } + + private predicate edgesComponent(NodeOrCall a, NodeOrCall b, string value) { + exists(boolean preservesValue | + Private::Steps::summaryLocalStep(a.asNode(), b.asNode(), preservesValue) and + if preservesValue = true then value = "value" else value = "taint" + ) + or + exists(Content c | + Private::Steps::summaryReadStep(a.asNode(), c, b.asNode()) and + value = "read (" + c + ")" + or + Private::Steps::summaryStoreStep(a.asNode(), c, b.asNode()) and + value = "store (" + c + ")" + or + Private::Steps::summaryClearsContent(a.asNode(), c) and + b = a and + value = "clear (" + c + ")" + ) + or + summaryPostUpdateNode(b.asNode(), a.asNode()) and + value = "post-update" + or + b.asCall() = summaryDataFlowCall(a.asNode()) and + value = "receiver" + or + exists(int i | + summaryArgumentNode(b.asCall(), a.asNode(), i) and + value = "argument (" + i + ")" + ) + } + + query predicate edges(NodeOrCall a, NodeOrCall b, string key, string value) { + key = "semmle.label" and + value = strictconcat(string s | edgesComponent(a, b, s) | s, " / ") + } + } } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll b/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll index d3a4612255d..e4c3091f05e 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll @@ -16,6 +16,9 @@ private module FlowSummaries { /** Holds is `i` is a valid parameter position. */ predicate parameterPosition(int i) { i in [-1 .. any(Parameter p).getPosition()] } +/** Gets the parameter position of the instance parameter. */ +int instanceParameterPosition() { result = -1 } + /** Gets the synthesized summary data-flow node for the given values. */ Node summaryNode(SummarizedCallable c, SummaryNodeState state) { result = getSummaryNode(c, state) } @@ -37,6 +40,8 @@ DataFlowType getReturnType(SummarizedCallable c, ReturnKind rk) { */ DataFlowType getCallbackParameterType(DataFlowType t, int i) { result = getErasedRepr(t.(FunctionalInterface).getRunMethod().getParameterType(i)) + or + result = getErasedRepr(t.(FunctionalInterface)) and i = -1 } /** diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll b/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll index 58ecc4c0a01..e4bfaaae1cc 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll @@ -83,7 +83,11 @@ private module Cached { not sink.getTypeBound() instanceof PrimitiveType and not sink.getTypeBound() instanceof BoxedType and not sink.getTypeBound() instanceof NumberType and - containerContent(f) + ( + containerContent(f) + or + f instanceof TaintInheritingContent + ) ) or FlowSummaryImpl::Private::Steps::summaryLocalStep(src, sink, false) @@ -265,7 +269,7 @@ private predicate taintPreservingQualifierToMethod(Method m) { m.getName() = "toString" ) or - m.getDeclaringType().hasQualifiedName("java.io", "ObjectInputStream") and + m.getDeclaringType() instanceof TypeObjectInputStream and m.getName().matches("read%") or m instanceof GetterMethod and @@ -281,11 +285,11 @@ private predicate taintPreservingQualifierToMethod(Method m) { private class StringReplaceMethod extends TaintPreservingCallable { StringReplaceMethod() { - getDeclaringType() instanceof TypeString and + this.getDeclaringType() instanceof TypeString and ( - hasName("replace") or - hasName("replaceAll") or - hasName("replaceFirst") + this.hasName("replace") or + this.hasName("replaceAll") or + this.hasName("replaceFirst") ) } @@ -439,7 +443,7 @@ class ObjectOutputStreamVar extends LocalVariableDecl { } MethodAccess getAWriteObjectMethodAccess() { - result.getQualifier() = getAnAccess() and + result.getQualifier() = this.getAnAccess() and result.getMethod().hasName("writeObject") } } @@ -484,7 +488,7 @@ private class FormatterVar extends LocalVariableDecl { } MethodAccess getAFormatMethodAccess() { - result.getQualifier() = getAnAccess() and + result.getQualifier() = this.getAnAccess() and result.getMethod().hasName("format") } } @@ -509,13 +513,13 @@ private class FormatterCallable extends TaintPreservingCallable { } override predicate returnsTaintFrom(int arg) { - if this instanceof Constructor then arg = 0 else arg = [-1 .. getNumberOfParameters()] + if this instanceof Constructor then arg = 0 else arg = [-1 .. this.getNumberOfParameters()] } override predicate transfersTaint(int src, int sink) { this.hasName("format") and sink = -1 and - src = [0 .. getNumberOfParameters()] + src = [0 .. this.getNumberOfParameters()] } } @@ -528,13 +532,13 @@ module StringBuilderVarModule { * build up a query using string concatenation. */ class StringBuilderVar extends LocalVariableDecl { - StringBuilderVar() { getType() instanceof StringBuildingType } + StringBuilderVar() { this.getType() instanceof StringBuildingType } /** * Gets a call that adds something to this string builder, from the argument at the given index. */ MethodAccess getAnInput(int arg) { - result.getQualifier() = getAChainedReference() and + result.getQualifier() = this.getAChainedReference() and ( result.getMethod().getName() = "append" and arg = 0 or @@ -548,20 +552,20 @@ module StringBuilderVarModule { * Gets a call that appends something to this string builder. */ MethodAccess getAnAppend() { - result.getQualifier() = getAChainedReference() and + result.getQualifier() = this.getAChainedReference() and result.getMethod().getName() = "append" } MethodAccess getNextAppend(MethodAccess append) { - result = getAnAppend() and - append = getAnAppend() and + result = this.getAnAppend() and + append = this.getAnAppend() and ( result.getQualifier() = append or not exists(MethodAccess chainAccess | chainAccess.getQualifier() = append) and exists(RValue sbva1, RValue sbva2 | adjacentUseUse(sbva1, sbva2) and - append.getQualifier() = getAChainedReference(sbva1) and + append.getQualifier() = this.getAChainedReference(sbva1) and result.getQualifier() = sbva2 ) ) @@ -571,7 +575,7 @@ module StringBuilderVarModule { * Gets a call that converts this string builder to a string. */ MethodAccess getToStringCall() { - result.getQualifier() = getAChainedReference() and + result.getQualifier() = this.getAChainedReference() and result.getMethod().getName() = "toString" } @@ -586,7 +590,7 @@ module StringBuilderVarModule { /** * Gets an expression that refers to this `StringBuilder`, possibly after some chained calls. */ - Expr getAChainedReference() { result = getAChainedReference(_) } + Expr getAChainedReference() { result = this.getAChainedReference(_) } } } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll index 8c4eb2a1144..2b90b128dd3 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll @@ -2,10 +2,10 @@ * Provides Java-specific definitions for use in sign analysis. */ module Private { + private import java as J import semmle.code.java.dataflow.RangeUtils as RU private import semmle.code.java.dataflow.SSA as Ssa private import semmle.code.java.controlflow.Guards as G - private import java as J private import Sign import Impl diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll index 558ecd1b88b..e450c11b5ab 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll @@ -28,7 +28,7 @@ class SsaReadPositionBlock extends SsaReadPosition, TSsaReadPositionBlock { /** Gets the basic block corresponding to this position. */ BasicBlock getBlock() { this = TSsaReadPositionBlock(result) } - override predicate hasReadOfVar(SsaVariable v) { getBlock() = getAReadBasicBlock(v) } + override predicate hasReadOfVar(SsaVariable v) { this.getBlock() = getAReadBasicBlock(v) } override string toString() { result = "block" } } @@ -49,8 +49,8 @@ class SsaReadPositionPhiInputEdge extends SsaReadPosition, TSsaReadPositionPhiIn /** Holds if `inp` is an input to `phi` along this edge. */ predicate phiInput(SsaPhiNode phi, SsaVariable inp) { - phi.hasInputFromBlock(inp, getOrigBlock()) and - getPhiBlock() = phi.getBasicBlock() + phi.hasInputFromBlock(inp, this.getOrigBlock()) and + this.getPhiBlock() = phi.getBasicBlock() } override string toString() { result = "edge" } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/java/ql/lib/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll index f4f73b8247c..acb029c23d9 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration { predicate isSanitizer(DataFlow::Node node) { none() } final override predicate isBarrier(DataFlow::Node node) { - isSanitizer(node) or + this.isSanitizer(node) or defaultTaintSanitizer(node) } /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } - final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } + final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) } /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } - final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } + final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } - final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } + final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { + this.isSanitizerGuard(guard) + } /** * Holds if the additional taint propagation step from `node1` to `node2` @@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration { predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() } final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - isAdditionalTaintStep(node1, node2) or + this.isAdditionalTaintStep(node1, node2) or defaultAdditionalTaintStep(node1, node2) } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/java/ql/lib/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll index f4f73b8247c..acb029c23d9 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration { predicate isSanitizer(DataFlow::Node node) { none() } final override predicate isBarrier(DataFlow::Node node) { - isSanitizer(node) or + this.isSanitizer(node) or defaultTaintSanitizer(node) } /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } - final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } + final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) } /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } - final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } + final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } - final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } + final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { + this.isSanitizerGuard(guard) + } /** * Holds if the additional taint propagation step from `node1` to `node2` @@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration { predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() } final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - isAdditionalTaintStep(node1, node2) or + this.isAdditionalTaintStep(node1, node2) or defaultAdditionalTaintStep(node1, node2) } diff --git a/java/ql/lib/semmle/code/java/deadcode/DeadCode.qll b/java/ql/lib/semmle/code/java/deadcode/DeadCode.qll index 48ee3d5563b..53e0ccc1f35 100644 --- a/java/ql/lib/semmle/code/java/deadcode/DeadCode.qll +++ b/java/ql/lib/semmle/code/java/deadcode/DeadCode.qll @@ -82,19 +82,19 @@ class SuppressedConstructor extends Constructor { SuppressedConstructor() { // Must be private or protected to suppress it. ( - isPrivate() + this.isPrivate() or // A protected, suppressed constructor only makes sense in a non-abstract class. - isProtected() and not getDeclaringType().isAbstract() + this.isProtected() and not this.getDeclaringType().isAbstract() ) and // Must be no-arg in order to replace the compiler generated default constructor. - getNumberOfParameters() = 0 and + this.getNumberOfParameters() = 0 and // Not the compiler-generated constructor itself. - not isDefaultConstructor() and + not this.isDefaultConstructor() and // Verify that there is only one statement, which is the `super()` call. This exists // even for empty constructors. - getBody().(BlockStmt).getNumStmt() = 1 and - getBody().(BlockStmt).getAStmt().(SuperConstructorInvocationStmt).getNumArgument() = 0 and + this.getBody().(BlockStmt).getNumStmt() = 1 and + this.getBody().(BlockStmt).getAStmt().(SuperConstructorInvocationStmt).getNumArgument() = 0 and // A constructor that is called is not acting to suppress the default constructor. We permit // calls from suppressed and default constructors - in both cases, they can only come from // sub-class constructors. @@ -105,7 +105,9 @@ class SuppressedConstructor extends Constructor { ) and // If other constructors are declared, then no compiler-generated constructor is added, so // this constructor is not acting to suppress the default compiler-generated constructor. - not exists(Constructor other | other = getDeclaringType().getAConstructor() and other != this) + not exists(Constructor other | + other = this.getDeclaringType().getAConstructor() and other != this + ) } } @@ -114,7 +116,7 @@ class SuppressedConstructor extends Constructor { */ class NamespaceClass extends RefType { NamespaceClass() { - fromSource() and + this.fromSource() and // All members, apart from the default constructor and, if present, a "suppressed" constructor // must be static. There must be at least one member apart from the permitted constructors. forex(Member m | @@ -125,7 +127,9 @@ class NamespaceClass extends RefType { m.isStatic() ) and // Must only extend other namespace classes, or `Object`. - forall(RefType r | r = getASupertype() | r instanceof TypeObject or r instanceof NamespaceClass) + forall(RefType r | r = this.getASupertype() | + r instanceof TypeObject or r instanceof NamespaceClass + ) } } @@ -197,7 +201,7 @@ class DeadClass extends SourceClassOrInterface { /** * Identify all the "dead" roots of this dead class. */ - DeadRoot getADeadRoot() { result = getADeadRoot(getACallable()) } + DeadRoot getADeadRoot() { result = getADeadRoot(this.getACallable()) } /** * Holds if this dead class is only used within the class itself. @@ -206,8 +210,8 @@ class DeadClass extends SourceClassOrInterface { // Accessed externally if any callable in the class has a possible liveness cause outside the // class. Only one step is required. not exists(Callable c | - c = possibleLivenessCause(getACallable()) and - not c = getACallable() + c = possibleLivenessCause(this.getACallable()) and + not c = this.getACallable() ) } } @@ -229,7 +233,7 @@ abstract class WhitelistedLiveClass extends RefType { } */ class DeadMethod extends Callable { DeadMethod() { - fromSource() and + this.fromSource() and not isLive(this) and not this.(Constructor).isDefaultConstructor() and // Ignore `SuppressedConstructor`s in `NamespaceClass`es. There is no reason to use a suppressed diff --git a/java/ql/lib/semmle/code/java/deadcode/DeadField.qll b/java/ql/lib/semmle/code/java/deadcode/DeadField.qll index dc6d1dc6dfd..c36b33f6296 100644 --- a/java/ql/lib/semmle/code/java/deadcode/DeadField.qll +++ b/java/ql/lib/semmle/code/java/deadcode/DeadField.qll @@ -10,7 +10,7 @@ import semmle.code.java.frameworks.jackson.JacksonSerializability * This defines the set of fields for which we will determine liveness. */ library class SourceField extends Field { - SourceField() { fromSource() } + SourceField() { this.fromSource() } } /** @@ -26,7 +26,7 @@ class DeadField extends SourceField { */ predicate isInDeadScope() { // `EnumConstant`s, and fields in dead classes, are reported in other queries. - getDeclaringType() instanceof DeadClass or + this.getDeclaringType() instanceof DeadClass or this instanceof EnumConstant } } @@ -37,7 +37,7 @@ class DeadField extends SourceField { */ class LiveField extends SourceField { LiveField() { - exists(FieldRead access | access = getAnAccess() | + exists(FieldRead access | access = this.getAnAccess() | isLive(access.getEnclosingCallable()) or exists(Annotation a | @@ -89,11 +89,11 @@ abstract class WhitelistedLiveField extends Field { } */ class SerialVersionUIDField extends ReflectivelyReadField { SerialVersionUIDField() { - hasName("serialVersionUID") and - isStatic() and - isFinal() and - getType().hasName("long") and - getDeclaringType().getASupertype*() instanceof TypeSerializable + this.hasName("serialVersionUID") and + this.isStatic() and + this.isFinal() and + this.getType().hasName("long") and + this.getDeclaringType().getASupertype*() instanceof TypeSerializable } } @@ -104,7 +104,7 @@ class SerialVersionUIDField extends ReflectivelyReadField { class LiveJaxbBoundField extends ReflectivelyReadField, JaxbBoundField { LiveJaxbBoundField() { // If the class is considered live, it must have at least one live constructor. - exists(Constructor c | c = getDeclaringType().getAConstructor() | isLive(c)) + exists(Constructor c | c = this.getDeclaringType().getAConstructor() | isLive(c)) } } @@ -114,11 +114,11 @@ class LiveJaxbBoundField extends ReflectivelyReadField, JaxbBoundField { */ class JUnitAnnotatedField extends ReflectivelyReadField { JUnitAnnotatedField() { - hasAnnotation("org.junit.experimental.theories", "DataPoint") or - hasAnnotation("org.junit.experimental.theories", "DataPoints") or - hasAnnotation("org.junit.runners", "Parameterized$Parameter") or - hasAnnotation("org.junit", "Rule") or - hasAnnotation("org.junit", "ClassRule") + this.hasAnnotation("org.junit.experimental.theories", "DataPoint") or + this.hasAnnotation("org.junit.experimental.theories", "DataPoints") or + this.hasAnnotation("org.junit.runners", "Parameterized$Parameter") or + this.hasAnnotation("org.junit", "Rule") or + this.hasAnnotation("org.junit", "ClassRule") } } @@ -164,8 +164,8 @@ class JPAReadField extends ReflectivelyReadField { ) | not this.hasAnnotation("javax.persistence", "Transient") and - not isStatic() and - not isFinal() + not this.isStatic() and + not this.isFinal() ) } } diff --git a/java/ql/lib/semmle/code/java/deadcode/EntryPoints.qll b/java/ql/lib/semmle/code/java/deadcode/EntryPoints.qll index aef97efd309..0a0dd2d0808 100644 --- a/java/ql/lib/semmle/code/java/deadcode/EntryPoints.qll +++ b/java/ql/lib/semmle/code/java/deadcode/EntryPoints.qll @@ -102,7 +102,7 @@ library class JacksonReflectivelyConstructedClass extends ReflectivelyConstructe override Callable getALiveCallable() { // Constructors may be called by Jackson, if they are a no-arg, they have a suitable annotation, // or inherit a suitable annotation through a mixin. - result = getAConstructor() and + result = this.getAConstructor() and ( result.getNumberOfParameters() = 0 or result.getAnAnnotation() instanceof JacksonAnnotation or @@ -153,7 +153,7 @@ class DeserializedClass extends ReflectivelyConstructedClass { */ class NewInstanceCall extends EntryPoint, NewInstance { override Constructor getALiveCallable() { - result = getInferredConstructor() and + result = this.getInferredConstructor() and // The `newInstance(...)` call must be used in a live context. isLive(this.getEnclosingCallable()) } @@ -164,7 +164,7 @@ class NewInstanceCall extends EntryPoint, NewInstance { */ class ReflectiveMethodAccessEntryPoint extends EntryPoint, ReflectiveMethodAccess { override Method getALiveCallable() { - result = inferAccessedMethod() and + result = this.inferAccessedMethod() and // The `getMethod(...)` call must be used in a live context. isLive(this.getEnclosingCallable()) } @@ -210,8 +210,8 @@ class JaxbXmlEnum extends AnnotationEntryPoint { class JaxbXmlType extends AnnotationEntryPoint, JaxbType { override Callable getALiveCallable() { // Must have a live no-arg constructor for JAXB to perform marshal/unmarshal. - exists(Constructor c | c = getAConstructor() and c.getNumberOfParameters() = 0 | isLive(c)) and - result = getACallable() and + exists(Constructor c | c = this.getAConstructor() and c.getNumberOfParameters() = 0 | isLive(c)) and + result = this.getACallable() and ( // A bound getter or setter. result instanceof JaxbBoundGetterSetter @@ -262,7 +262,7 @@ class ManagedBeanImplEntryPoint extends EntryPoint, RegisteredManagedBeanImpl { // Find the method that will be called for each method on each managed bean that this class // implements. this.inherits(result) and - result.(Method).overrides(getAnImplementedManagedBean().getAMethod()) + result.(Method).overrides(this.getAnImplementedManagedBean().getAMethod()) } } @@ -377,7 +377,7 @@ class JavaxResourceAnnotatedMethod extends CallableEntryPointOnConstructedClass */ class JavaxManagedBeanReflectivelyConstructed extends ReflectivelyConstructedClass { JavaxManagedBeanReflectivelyConstructed() { - getAnAnnotation() instanceof JavaxManagedBeanAnnotation + this.getAnAnnotation() instanceof JavaxManagedBeanAnnotation } } @@ -413,13 +413,13 @@ class PersistencePropertyMethod extends CallableEntryPoint { */ class PersistenceCallbackMethod extends CallableEntryPoint { PersistenceCallbackMethod() { - getAnAnnotation() instanceof PrePersistAnnotation or - getAnAnnotation() instanceof PreRemoveAnnotation or - getAnAnnotation() instanceof PreUpdateAnnotation or - getAnAnnotation() instanceof PostPersistAnnotation or - getAnAnnotation() instanceof PostRemoveAnnotation or - getAnAnnotation() instanceof PostUpdateAnnotation or - getAnAnnotation() instanceof PostLoadAnnotation + this.getAnAnnotation() instanceof PrePersistAnnotation or + this.getAnAnnotation() instanceof PreRemoveAnnotation or + this.getAnAnnotation() instanceof PreUpdateAnnotation or + this.getAnAnnotation() instanceof PostPersistAnnotation or + this.getAnAnnotation() instanceof PostRemoveAnnotation or + this.getAnAnnotation() instanceof PostUpdateAnnotation or + this.getAnAnnotation() instanceof PostLoadAnnotation } } @@ -429,20 +429,20 @@ class PersistenceCallbackMethod extends CallableEntryPoint { */ class ArbitraryXMLEntryPoint extends ReflectivelyConstructedClass { ArbitraryXMLEntryPoint() { - fromSource() and + this.fromSource() and exists(XMLAttribute attribute | attribute.getName() = "className" or attribute.getName().matches("%ClassName") or attribute.getName() = "class" or attribute.getName().matches("%Class") | - attribute.getValue() = getQualifiedName() + attribute.getValue() = this.getQualifiedName() ) } override Callable getALiveCallable() { // Any constructor on these classes, as we don't know which may be called. - result = getAConstructor() + result = this.getAConstructor() } } diff --git a/java/ql/lib/semmle/code/java/deadcode/TestEntryPoints.qll b/java/ql/lib/semmle/code/java/deadcode/TestEntryPoints.qll index d9bc8a07997..2d8b28e4de9 100644 --- a/java/ql/lib/semmle/code/java/deadcode/TestEntryPoints.qll +++ b/java/ql/lib/semmle/code/java/deadcode/TestEntryPoints.qll @@ -18,7 +18,7 @@ class TestMethodEntry extends CallableEntryPoint { or exists(AnnotationType a | a = this.getAnAnnotation().getType() | a.hasQualifiedName("org.junit.runners", "Parameterized$Parameters") and - getDeclaringType() instanceof ParameterizedJUnitTest + this.getDeclaringType() instanceof ParameterizedJUnitTest ) } } @@ -28,12 +28,12 @@ class TestMethodEntry extends CallableEntryPoint { */ class BeforeOrAfterEntry extends CallableEntryPoint { BeforeOrAfterEntry() { - getAnAnnotation() instanceof TestNGBeforeAnnotation or - getAnAnnotation() instanceof TestNGAfterAnnotation or - getAnAnnotation() instanceof BeforeAnnotation or - getAnAnnotation() instanceof BeforeClassAnnotation or - getAnAnnotation() instanceof AfterAnnotation or - getAnAnnotation() instanceof AfterClassAnnotation + this.getAnAnnotation() instanceof TestNGBeforeAnnotation or + this.getAnAnnotation() instanceof TestNGAfterAnnotation or + this.getAnAnnotation() instanceof BeforeAnnotation or + this.getAnAnnotation() instanceof BeforeClassAnnotation or + this.getAnAnnotation() instanceof AfterAnnotation or + this.getAnAnnotation() instanceof AfterClassAnnotation } } @@ -44,7 +44,7 @@ class JUnitTheories extends CallableEntryPoint { JUnitTheories() { exists(AnnotationType a | a = this.getAnAnnotation().getType() and - getDeclaringType() instanceof JUnitTheoryTest + this.getDeclaringType() instanceof JUnitTheoryTest | a.hasQualifiedName("org.junit.experimental.theories", "Theory") or a.hasQualifiedName("org.junit.experimental.theories", "DataPoint") or @@ -63,7 +63,7 @@ class JUnitDataPointField extends ReflectivelyReadField { a.hasQualifiedName("org.junit.experimental.theories", "DataPoint") or a.hasQualifiedName("org.junit.experimental.theories", "DataPoints") ) and - getDeclaringType() instanceof JUnitTheoryTest + this.getDeclaringType() instanceof JUnitTheoryTest ) } } @@ -152,7 +152,7 @@ class CucumberConstructedClass extends ReflectivelyConstructedClass { // Consider any constructor to be live - Cucumber calls a runtime-specified dependency // injection framework (possibly an in-built one) to construct these instances, so any // constructor could be called. - result = getAConstructor() + result = this.getAConstructor() } } diff --git a/java/ql/lib/semmle/code/java/deadcode/WebEntryPoints.qll b/java/ql/lib/semmle/code/java/deadcode/WebEntryPoints.qll index b31a04e11be..b9034600ca3 100644 --- a/java/ql/lib/semmle/code/java/deadcode/WebEntryPoints.qll +++ b/java/ql/lib/semmle/code/java/deadcode/WebEntryPoints.qll @@ -29,7 +29,7 @@ class ServletConstructedClass extends ReflectivelyConstructedClass { */ class ServletListenerClass extends ReflectivelyConstructedClass { ServletListenerClass() { - getAnAncestor() instanceof ServletWebXMLListenerType and + this.getAnAncestor() instanceof ServletWebXMLListenerType and // If we have seen any `web.xml` files, this listener will be considered to be live only if it is // referred to as a listener-class in at least one. If no `web.xml` files are found, we assume // that XML extraction was not enabled, and therefore consider all listener classes as live. @@ -47,7 +47,7 @@ class ServletListenerClass extends ReflectivelyConstructedClass { */ class ServletFilterClass extends ReflectivelyConstructedClass { ServletFilterClass() { - getASupertype*().hasQualifiedName("javax.servlet", "Filter") and + this.getASupertype*().hasQualifiedName("javax.servlet", "Filter") and // If we have seen any `web.xml` files, this filter will be considered to be live only if it is // referred to as a filter-class in at least one. If no `web.xml` files are found, we assume // that XML extraction was not enabled, and therefore consider all filter classes as live. diff --git a/java/ql/lib/semmle/code/java/dispatch/WrappedInvocation.qll b/java/ql/lib/semmle/code/java/dispatch/WrappedInvocation.qll index 26fcd109df2..94f67445785 100644 --- a/java/ql/lib/semmle/code/java/dispatch/WrappedInvocation.qll +++ b/java/ql/lib/semmle/code/java/dispatch/WrappedInvocation.qll @@ -39,7 +39,10 @@ private Expr getRunnerArgument(MethodAccess ma, Method runmethod) { or getRunnerArgument(ma, runmethod).(CastExpr).getExpr() = result or - getRunnerArgument(ma, runmethod).(VarAccess).getVariable().getAnAssignedValue() = result + pragma[only_bind_out](getRunnerArgument(ma, runmethod)) + .(VarAccess) + .getVariable() + .getAnAssignedValue() = result } /** diff --git a/java/ql/lib/semmle/code/java/frameworks/Camel.qll b/java/ql/lib/semmle/code/java/frameworks/Camel.qll index cd845d0b33e..09bfa73d460 100644 --- a/java/ql/lib/semmle/code/java/frameworks/Camel.qll +++ b/java/ql/lib/semmle/code/java/frameworks/Camel.qll @@ -48,7 +48,7 @@ class CamelToBeanURI extends CamelToURI { /** * Gets the bean referenced by this URI. */ - SpringBean getRefBean() { result.getBeanIdentifier() = getBeanIdentifier() } + SpringBean getRefBean() { result.getBeanIdentifier() = this.getBeanIdentifier() } } /** diff --git a/java/ql/lib/semmle/code/java/frameworks/Guice.qll b/java/ql/lib/semmle/code/java/frameworks/Guice.qll index f7154df4bd2..8dfb6398398 100644 --- a/java/ql/lib/semmle/code/java/frameworks/Guice.qll +++ b/java/ql/lib/semmle/code/java/frameworks/Guice.qll @@ -31,7 +31,7 @@ class GuiceProvider extends Interface { * A method that overrides the `get` method on the interface `com.google.inject.Provider`. */ Method getAnOverridingGetMethod() { - exists(Method m | m.getSourceDeclaration() = getGetMethod() | result.overrides*(m)) + exists(Method m | m.getSourceDeclaration() = this.getGetMethod() | result.overrides*(m)) } } diff --git a/java/ql/lib/semmle/code/java/frameworks/JAXB.qll b/java/ql/lib/semmle/code/java/frameworks/JAXB.qll index 2b4e3702623..bf5e33cb004 100644 --- a/java/ql/lib/semmle/code/java/frameworks/JAXB.qll +++ b/java/ql/lib/semmle/code/java/frameworks/JAXB.qll @@ -17,11 +17,11 @@ library class JAXBMarshalMethod extends Method { } class JaxbAnnotationType extends AnnotationType { - JaxbAnnotationType() { getPackage().getName() = "javax.xml.bind.annotation" } + JaxbAnnotationType() { this.getPackage().getName() = "javax.xml.bind.annotation" } } class JaxbAnnotated extends Annotatable { - JaxbAnnotated() { getAnAnnotation().getType() instanceof JaxbAnnotationType } + JaxbAnnotated() { this.getAnAnnotation().getType() instanceof JaxbAnnotationType } predicate hasJaxbAnnotation(string name) { hasJaxbAnnotation(this, name) } } @@ -62,8 +62,8 @@ class JaxbType extends Class { * Gets the `XmlAccessType` associated with this class. */ XmlAccessType getXmlAccessType() { - if exists(getDeclaredAccessType()) - then result = getDeclaredAccessType() + if exists(this.getDeclaredAccessType()) + then result = this.getDeclaredAccessType() else // Default access type, if not specified. result.isPublicMember() @@ -81,22 +81,22 @@ class XmlAccessType extends EnumConstant { /** * All public getter/setter pairs and public fields will be bound. */ - predicate isPublicMember() { getName() = "PUBLIC_MEMBER" } + predicate isPublicMember() { this.getName() = "PUBLIC_MEMBER" } /** * All non-static, non-transient fields will be bound. */ - predicate isField() { getName() = "FIELD" } + predicate isField() { this.getName() = "FIELD" } /** * All getter/setter pairs will be bound. */ - predicate isProperty() { getName() = "PROPERTY" } + predicate isProperty() { this.getName() = "PROPERTY" } /** * Nothing will be bound automatically. */ - predicate isNone() { getName() = "NONE" } + predicate isNone() { this.getName() = "NONE" } } /** @@ -105,10 +105,10 @@ class XmlAccessType extends EnumConstant { */ class JaxbMemberAnnotation extends JaxbAnnotationType { JaxbMemberAnnotation() { - hasName("XmlElement") or - hasName("XmlAttribute") or - hasName("XmlElementRefs") or - hasName("XmlElements") + this.hasName("XmlElement") or + this.hasName("XmlAttribute") or + this.hasName("XmlElementRefs") or + this.hasName("XmlElements") } } @@ -121,14 +121,14 @@ private predicate isTransient(Member m) { hasJaxbAnnotation(m, "XmlTransient") } class JaxbBoundField extends Field { JaxbBoundField() { // Fields cannot be static, because JAXB creates instances. - not isStatic() and + not this.isStatic() and // Fields cannot be final, because JAXB instantiates the object, then sets the properties. - not isFinal() and + not this.isFinal() and // No transient fields are ever bound. not isTransient(this) and ( // Explicitly annotated to be bound. - exists(getAnAnnotation().getType().(JaxbMemberAnnotation)) + exists(this.getAnAnnotation().getType().(JaxbMemberAnnotation)) or // Within a JAXB type which has an `XmlAcessType` that binds this field. exists(JaxbType type | this.getDeclaringType() = type | @@ -136,7 +136,7 @@ class JaxbBoundField extends Field { type.getXmlAccessType().isField() or // Only public fields are automatically bound in this access type. - type.getXmlAccessType().isPublicMember() and isPublic() + type.getXmlAccessType().isPublicMember() and this.isPublic() ) ) } @@ -157,7 +157,7 @@ library class GetterOrSetterMethod extends Method { * Holds if this method has a "pair"ed method, e.g. whether there is an equivalent getter if this * is a setter, and vice versa. */ - predicate isProperty() { exists(getPair()) } + predicate isProperty() { exists(this.getPair()) } /** * Gets the "pair" method, if one exists; that is, the getter if this is a setter, and vice versa. @@ -183,16 +183,16 @@ class JaxbBoundGetterSetter extends GetterOrSetterMethod { this.getField() instanceof JaxbBoundField or // An annotation on this method or the pair that indicate that it is a valid setter/getter. - getThisOrPair().getAnAnnotation().getType() instanceof JaxbMemberAnnotation + this.getThisOrPair().getAnAnnotation().getType() instanceof JaxbMemberAnnotation or // Within a JAXB type which has an `XmlAcessType` that binds this method. exists(JaxbType c | this.getDeclaringType() = c | // If this is a "property" - both a setter and getter present for the XML element or attribute // - the `XmlAccessType` of the declaring type may cause this property to be bound. - isProperty() and + this.isProperty() and ( // In the `PUBLIC_MEMBER` case all public properties are considered bound. - c.getXmlAccessType().isPublicMember() and isPublic() + c.getXmlAccessType().isPublicMember() and this.isPublic() or // In "property" all properties are considered bound. c.getXmlAccessType().isProperty() diff --git a/java/ql/lib/semmle/code/java/frameworks/JUnitAnnotations.qll b/java/ql/lib/semmle/code/java/frameworks/JUnitAnnotations.qll index 18afe9403e2..8716f7d625c 100644 --- a/java/ql/lib/semmle/code/java/frameworks/JUnitAnnotations.qll +++ b/java/ql/lib/semmle/code/java/frameworks/JUnitAnnotations.qll @@ -64,5 +64,5 @@ class RunWithAnnotation extends Annotation { /** * Gets the runner that will be used. */ - Type getRunner() { result = getValue("value").(TypeLiteral).getReferencedType() } + Type getRunner() { result = this.getValue("value").(TypeLiteral).getReferencedType() } } diff --git a/java/ql/lib/semmle/code/java/frameworks/Jackson.qll b/java/ql/lib/semmle/code/java/frameworks/Jackson.qll index 5612311c730..f93f1afccc8 100644 --- a/java/ql/lib/semmle/code/java/frameworks/Jackson.qll +++ b/java/ql/lib/semmle/code/java/frameworks/Jackson.qll @@ -7,31 +7,31 @@ private import semmle.code.java.dataflow.DataFlow private class ObjectMapper extends RefType { ObjectMapper() { - getASupertype*().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectMapper") + this.getASupertype*().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectMapper") } } /** A builder for building Jackson's `JsonMapper`. */ class MapperBuilder extends RefType { MapperBuilder() { - hasQualifiedName("com.fasterxml.jackson.databind.cfg", "MapperBuilder") + this.hasQualifiedName("com.fasterxml.jackson.databind.cfg", "MapperBuilder") } } private class JsonFactory extends RefType { - JsonFactory() { hasQualifiedName("com.fasterxml.jackson.core", "JsonFactory") } + JsonFactory() { this.hasQualifiedName("com.fasterxml.jackson.core", "JsonFactory") } } private class JsonParser extends RefType { - JsonParser() { hasQualifiedName("com.fasterxml.jackson.core", "JsonParser") } + JsonParser() { this.hasQualifiedName("com.fasterxml.jackson.core", "JsonParser") } } /** A type descriptor in Jackson libraries. For example, `java.lang.Class`. */ class JacksonTypeDescriptorType extends RefType { JacksonTypeDescriptorType() { this instanceof TypeClass or - hasQualifiedName("com.fasterxml.jackson.databind", "JavaType") or - hasQualifiedName("com.fasterxml.jackson.core.type", "TypeReference") + this.hasQualifiedName("com.fasterxml.jackson.databind", "JavaType") or + this.hasQualifiedName("com.fasterxml.jackson.core.type", "TypeReference") } } diff --git a/java/ql/lib/semmle/code/java/frameworks/JaxWS.qll b/java/ql/lib/semmle/code/java/frameworks/JaxWS.qll index c3bd9531376..71bbf381fd0 100644 --- a/java/ql/lib/semmle/code/java/frameworks/JaxWS.qll +++ b/java/ql/lib/semmle/code/java/frameworks/JaxWS.qll @@ -25,9 +25,7 @@ string getAJaxRsPackage(string subpackage) { result = getAJaxRsPackage() + "." + class JaxWsEndpoint extends Class { JaxWsEndpoint() { exists(AnnotationType a | a = this.getAnAnnotation().getType() | - a.hasName("WebService") or - a.hasName("WebServiceProvider") or - a.hasName("WebServiceClient") + a.hasName(["WebService", "WebServiceProvider", "WebServiceClient"]) ) } @@ -35,8 +33,7 @@ class JaxWsEndpoint extends Class { Callable getARemoteMethod() { result = this.getACallable() and exists(AnnotationType a | a = result.getAnAnnotation().getType() | - a.hasName("WebMethod") or - a.hasName("WebEndpoint") + a.hasName(["WebMethod", "WebEndpoint"]) ) } } @@ -62,12 +59,7 @@ class JaxRsResourceMethod extends Method { a = this.getAnAnnotation().getType() and a.getPackage().getName() = getAJaxRsPackage() | - a.hasName("GET") or - a.hasName("POST") or - a.hasName("DELETE") or - a.hasName("PUT") or - a.hasName("OPTIONS") or - a.hasName("HEAD") + a.hasName(["GET", "POST", "DELETE", "PUT", "OPTIONS", "HEAD"]) ) or // A JaxRS resource method can also inherit these annotations from a supertype, but only if @@ -201,13 +193,10 @@ class JaxRsInjectionAnnotation extends JaxRSAnnotation { a = this.getType() and a.getPackage().getName() = getAJaxRsPackage() | - a.hasName("BeanParam") or - a.hasName("CookieParam") or - a.hasName("FormParam") or - a.hasName("HeaderParam") or - a.hasName("MatrixParam") or - a.hasName("PathParam") or - a.hasName("QueryParam") + a.hasName([ + "BeanParam", "CookieParam", "FormParam", "HeaderParam", "MatrixParam", "PathParam", + "QueryParam" + ]) ) or this.getType().hasQualifiedName(getAJaxRsPackage("core"), "Context") diff --git a/java/ql/lib/semmle/code/java/frameworks/Jndi.qll b/java/ql/lib/semmle/code/java/frameworks/Jndi.qll index fce87dcddab..9294c0c97c7 100644 --- a/java/ql/lib/semmle/code/java/frameworks/Jndi.qll +++ b/java/ql/lib/semmle/code/java/frameworks/Jndi.qll @@ -41,39 +41,39 @@ class TypeLdapName extends Class { /** A method with the name `addAll` declared in `javax.naming.ldap.LdapName`. */ class MethodLdapNameAddAll extends Method { MethodLdapNameAddAll() { - getDeclaringType() instanceof TypeLdapName and - hasName("addAll") + this.getDeclaringType() instanceof TypeLdapName and + this.hasName("addAll") } } /** A method with the name `clone` declared in `javax.naming.ldap.LdapName`. */ class MethodLdapNameClone extends Method { MethodLdapNameClone() { - getDeclaringType() instanceof TypeLdapName and - hasName("clone") + this.getDeclaringType() instanceof TypeLdapName and + this.hasName("clone") } } /** A method with the name `getAll` declared in `javax.naming.ldap.LdapName`. */ class MethodLdapNameGetAll extends Method { MethodLdapNameGetAll() { - getDeclaringType() instanceof TypeLdapName and - hasName("getAll") + this.getDeclaringType() instanceof TypeLdapName and + this.hasName("getAll") } } /** A method with the name `getRdns` declared in `javax.naming.ldap.LdapName`. */ class MethodLdapNameGetRdns extends Method { MethodLdapNameGetRdns() { - getDeclaringType() instanceof TypeLdapName and - hasName("getRdns") + this.getDeclaringType() instanceof TypeLdapName and + this.hasName("getRdns") } } /** A method with the name `toString` declared in `javax.naming.ldap.LdapName`. */ class MethodLdapNameToString extends Method { MethodLdapNameToString() { - getDeclaringType() instanceof TypeLdapName and - hasName("toString") + this.getDeclaringType() instanceof TypeLdapName and + this.hasName("toString") } } diff --git a/java/ql/lib/semmle/code/java/frameworks/Kryo.qll b/java/ql/lib/semmle/code/java/frameworks/Kryo.qll index 317148d56b5..057b10de632 100644 --- a/java/ql/lib/semmle/code/java/frameworks/Kryo.qll +++ b/java/ql/lib/semmle/code/java/frameworks/Kryo.qll @@ -11,8 +11,8 @@ private import semmle.code.java.dataflow.FlowSteps */ class Kryo extends RefType { Kryo() { - hasQualifiedName("com.esotericsoftware.kryo", "Kryo") or - hasQualifiedName("com.esotericsoftware.kryo5", "Kryo") + this.hasQualifiedName("com.esotericsoftware.kryo", "Kryo") or + this.hasQualifiedName("com.esotericsoftware.kryo5", "Kryo") } } @@ -21,8 +21,8 @@ class Kryo extends RefType { */ class KryoInput extends RefType { KryoInput() { - hasQualifiedName("com.esotericsoftware.kryo.io", "Input") or - hasQualifiedName("com.esotericsoftware.kryo5.io", "Input") + this.hasQualifiedName("com.esotericsoftware.kryo.io", "Input") or + this.hasQualifiedName("com.esotericsoftware.kryo5.io", "Input") } } @@ -31,8 +31,8 @@ class KryoInput extends RefType { */ class KryoPool extends RefType { KryoPool() { - hasQualifiedName("com.esotericsoftware.kryo.pool", "KryoPool") or - hasQualifiedName("com.esotericsoftware.kryo5.pool", "KryoPool") + this.hasQualifiedName("com.esotericsoftware.kryo.pool", "KryoPool") or + this.hasQualifiedName("com.esotericsoftware.kryo5.pool", "KryoPool") } } @@ -41,8 +41,8 @@ class KryoPool extends RefType { */ class KryoPoolBuilder extends RefType { KryoPoolBuilder() { - hasQualifiedName("com.esotericsoftware.kryo.pool", "KryoPool$Builder") or - hasQualifiedName("com.esotericsoftware.kryo5.pool", "KryoPool$Builder") + this.hasQualifiedName("com.esotericsoftware.kryo.pool", "KryoPool$Builder") or + this.hasQualifiedName("com.esotericsoftware.kryo5.pool", "KryoPool$Builder") } } @@ -51,10 +51,10 @@ class KryoPoolBuilder extends RefType { */ class KryoPoolBuilderMethod extends Method { KryoPoolBuilderMethod() { - getDeclaringType() instanceof KryoPoolBuilder and + this.getDeclaringType() instanceof KryoPoolBuilder and ( - getReturnType() instanceof KryoPoolBuilder or - getReturnType() instanceof KryoPool + this.getReturnType() instanceof KryoPoolBuilder or + this.getReturnType() instanceof KryoPool ) } } @@ -92,7 +92,7 @@ class KryoEnableWhiteListing extends MethodAccess { */ class KryoPoolRunMethod extends Method { KryoPoolRunMethod() { - getDeclaringType() instanceof KryoPool and - hasName("run") + this.getDeclaringType() instanceof KryoPool and + this.hasName("run") } } diff --git a/java/ql/lib/semmle/code/java/frameworks/Mail.qll b/java/ql/lib/semmle/code/java/frameworks/Mail.qll new file mode 100644 index 00000000000..93116009862 --- /dev/null +++ b/java/ql/lib/semmle/code/java/frameworks/Mail.qll @@ -0,0 +1,27 @@ +/** Provides classes and predicates to work with email */ + +import java + +/** + * The class `javax.mail.Session` or `jakarta.mail.Session`. + */ +class MailSession extends Class { + MailSession() { this.hasQualifiedName(["javax.mail", "jakarta.mail"], "Session") } +} + +/** + * The method `getInstance` of the classes `javax.mail.Session` or `jakarta.mail.Session`. + */ +class MailSessionGetInstanceMethod extends Method { + MailSessionGetInstanceMethod() { + this.getDeclaringType() instanceof MailSession and + this.getName() = "getInstance" + } +} + +/** + * A subtype of the class `org.apache.commons.mail.Email`. + */ +class ApacheEmail extends Class { + ApacheEmail() { this.getASupertype*().hasQualifiedName("org.apache.commons.mail", "Email") } +} diff --git a/java/ql/lib/semmle/code/java/frameworks/Mockito.qll b/java/ql/lib/semmle/code/java/frameworks/Mockito.qll index 1745d3a9f5b..e42da4db357 100644 --- a/java/ql/lib/semmle/code/java/frameworks/Mockito.qll +++ b/java/ql/lib/semmle/code/java/frameworks/Mockito.qll @@ -11,8 +11,8 @@ import java */ class MockitoVerifyMethod extends Method { MockitoVerifyMethod() { - getDeclaringType().getPackage().getName().matches("org.mockito%") and - hasName("verify") + this.getDeclaringType().getPackage().getName().matches("org.mockito%") and + this.hasName("verify") } } @@ -21,7 +21,7 @@ class MockitoVerifyMethod extends Method { */ class MockitoVerifiedMethodAccess extends MethodAccess { MockitoVerifiedMethodAccess() { - getQualifier().(MethodAccess).getMethod() instanceof MockitoVerifyMethod + this.getQualifier().(MethodAccess).getMethod() instanceof MockitoVerifyMethod } } @@ -41,8 +41,8 @@ class MockitoMockableType extends ClassOrInterface { */ class MockitoInitMocks extends Method { MockitoInitMocks() { - getDeclaringType().hasQualifiedName("org.mockito", "MockitoAnnotations") and - hasName("initMocks") + this.getDeclaringType().hasQualifiedName("org.mockito", "MockitoAnnotations") and + this.hasName("initMocks") } } @@ -61,10 +61,10 @@ class MockitoInitedTest extends Class { or // Call to `MockitoAnnotations.initMocks()`, either by the constructor or by a `@Before` method. exists(MockitoInitMocks initMocks | - getAConstructor().calls*(initMocks) + this.getAConstructor().calls*(initMocks) or exists(Method m | - m = getAnAncestor().getAMethod() and + m = this.getAnAncestor().getAMethod() and ( m.hasAnnotation("org.junit", "Before") or m.hasAnnotation("org.testng.annotations", "BeforeMethod") @@ -85,8 +85,8 @@ class MockitoInitedTest extends Class { */ class MockitoAnnotation extends Annotation { MockitoAnnotation() { - getType().getPackage().getName().matches("org.mockito") or - getType().getPackage().getName().matches("org.mockito.%") + this.getType().getPackage().getName().matches("org.mockito") or + this.getType().getPackage().getName().matches("org.mockito.%") } } @@ -95,11 +95,11 @@ class MockitoAnnotation extends Annotation { */ class MockitoExclusiveAnnotation extends MockitoAnnotation { MockitoExclusiveAnnotation() { - getType().hasQualifiedName("org.mockito", "Mock") or - getType().hasQualifiedName("org.mockito", "MockitoAnnotations$Mock") or - getType().hasQualifiedName("org.mockito", "InjectMocks") or - getType().hasQualifiedName("org.mockito", "Spy") or - getType().hasQualifiedName("org.mockito", "Captor") + this.getType().hasQualifiedName("org.mockito", "Mock") or + this.getType().hasQualifiedName("org.mockito", "MockitoAnnotations$Mock") or + this.getType().hasQualifiedName("org.mockito", "InjectMocks") or + this.getType().hasQualifiedName("org.mockito", "Spy") or + this.getType().hasQualifiedName("org.mockito", "Captor") } } @@ -107,16 +107,16 @@ class MockitoExclusiveAnnotation extends MockitoAnnotation { * A field which has a Mockito annotation. */ class MockitoAnnotatedField extends Field { - MockitoAnnotatedField() { getAnAnnotation() instanceof MockitoAnnotation } + MockitoAnnotatedField() { this.getAnAnnotation() instanceof MockitoAnnotation } /** * Holds if this field will be processed by Mockito. */ predicate isValid() { // Mockito annotations are never parsed if the test isn't properly initialized. - getDeclaringType() instanceof MockitoInitedTest and + this.getDeclaringType() instanceof MockitoInitedTest and // There should only be one "exclusive" mockito annotation per field. - count(getAnAnnotation().(MockitoExclusiveAnnotation)) = 1 + count(this.getAnAnnotation().(MockitoExclusiveAnnotation)) = 1 } } @@ -125,16 +125,16 @@ class MockitoAnnotatedField extends Field { */ class MockitoMockedField extends MockitoAnnotatedField { MockitoMockedField() { - hasAnnotation("org.mockito", "Mock") + this.hasAnnotation("org.mockito", "Mock") or // Deprecated style. - hasAnnotation("org.mockito", "MockitoAnnotations$Mock") + this.hasAnnotation("org.mockito", "MockitoAnnotations$Mock") } override predicate isValid() { super.isValid() and // The type must also be mockable, otherwise it will not be initialized. - getType() instanceof MockitoMockableType + this.getType() instanceof MockitoMockableType } /** @@ -142,12 +142,13 @@ class MockitoMockedField extends MockitoAnnotatedField { */ predicate isReferencedByInjection() { exists(MockitoInjectedField injectedField | - injectedField.getDeclaringType() = getDeclaringType() + injectedField.getDeclaringType() = this.getDeclaringType() | // A `@Mock` is injected if it is used in one of the invoked callables (constructor or // setter), or injected directly onto a field. - getType().(RefType).getAnAncestor() = injectedField.getAnInvokedCallable().getAParamType() or - getType().(RefType).getAnAncestor() = injectedField.getASetField().getType() + this.getType().(RefType).getAnAncestor() = + injectedField.getAnInvokedCallable().getAParamType() or + this.getType().(RefType).getAnAncestor() = injectedField.getASetField().getType() ) } } @@ -156,25 +157,25 @@ class MockitoMockedField extends MockitoAnnotatedField { * A field annotated with `@InjectMocks`. */ class MockitoInjectedField extends MockitoAnnotatedField { - MockitoInjectedField() { hasAnnotation("org.mockito", "InjectMocks") } + MockitoInjectedField() { this.hasAnnotation("org.mockito", "InjectMocks") } override predicate isValid() { super.isValid() and ( // If we need to initialize the field, it is only valid if the type is a `Class` that is not // local, is static if it is a nested class, and is not abstract. - exists(getInitializer()) + exists(this.getInitializer()) or - exists(Class c | c = getType() | + exists(Class c | c = this.getType() | not c.isLocal() and - (getType() instanceof NestedClass implies c.(NestedClass).isStatic()) and + (this.getType() instanceof NestedClass implies c.(NestedClass).isStatic()) and not c.isAbstract() ) ) and ( // If neither of these is true, then mockito will fail to initialize this field. - usingConstructorInjection() or - usingPropertyInjection() + this.usingConstructorInjection() or + this.usingPropertyInjection() ) } @@ -184,7 +185,8 @@ class MockitoInjectedField extends MockitoAnnotatedField { * Note: this does not include the no-arg constructor. */ predicate usingConstructorInjection() { - not exists(getInitializer()) and exists(getMockInjectedClass().getAMostMockableConstructor()) + not exists(this.getInitializer()) and + exists(this.getMockInjectedClass().getAMostMockableConstructor()) } /** @@ -194,10 +196,10 @@ class MockitoInjectedField extends MockitoAnnotatedField { * constructor, in addition to any property. */ predicate usingPropertyInjection() { - not usingConstructorInjection() and + not this.usingConstructorInjection() and ( - exists(getInitializer()) or - exists(getMockInjectedClass().getNoArgsConstructor()) + exists(this.getInitializer()) or + exists(this.getMockInjectedClass().getNoArgsConstructor()) ) } @@ -212,18 +214,18 @@ class MockitoInjectedField extends MockitoAnnotatedField { Callable getAnInvokedCallable() { exists(MockitoMockInjectedClass mockInjectedClass | // This is the type we are constructing/injecting. - mockInjectedClass = getType() + mockInjectedClass = this.getType() | - if usingConstructorInjection() + if this.usingConstructorInjection() then // If there is no initializer for this field, and there is a most mockable constructor, // then we are doing a parameterized injection of mocks into a most mockable constructor. result = mockInjectedClass.getAMostMockableConstructor() else - if usingPropertyInjection() + if this.usingPropertyInjection() then // We will call the no-arg constructor if the field wasn't initialized. - not exists(getInitializer()) and + not exists(this.getInitializer()) and result = mockInjectedClass.getNoArgsConstructor() or // Perform property injection into setter fields, but only where there exists a mock @@ -249,9 +251,9 @@ class MockitoInjectedField extends MockitoAnnotatedField { * Field injection only occurs if property injection and not constructor injection is used. */ Field getASetField() { - if usingPropertyInjection() + if this.usingPropertyInjection() then - result = getMockInjectedClass().getASetField() and + result = this.getMockInjectedClass().getASetField() and exists(MockitoMockedField mockedField | mockedField.getDeclaringType() = this.getDeclaringType() and mockedField.isValid() @@ -268,15 +270,15 @@ class MockitoInjectedField extends MockitoAnnotatedField { * A field annotated with the Mockito `@Spy` annotation. */ class MockitoSpiedField extends MockitoAnnotatedField { - MockitoSpiedField() { hasAnnotation("org.mockito", "Spy") } + MockitoSpiedField() { this.hasAnnotation("org.mockito", "Spy") } override predicate isValid() { super.isValid() and ( - exists(getInitializer()) + exists(this.getInitializer()) or exists(Constructor c | - c = getType().(RefType).getAConstructor() and c.getNumberOfParameters() = 0 + c = this.getType().(RefType).getAConstructor() and c.getNumberOfParameters() = 0 ) ) } @@ -284,7 +286,7 @@ class MockitoSpiedField extends MockitoAnnotatedField { /** * Holds if construction ever occurs. */ - predicate isConstructed() { not exists(getInitializer()) } + predicate isConstructed() { not exists(this.getInitializer()) } } private int mockableParameterCount(Constructor constructor) { @@ -312,8 +314,8 @@ library class MockitoMockInjectedClass extends Class { * Mockito will call only one of them, but which one is dependent on the JVM... */ Constructor getAMostMockableConstructor() { - result = getAConstructor() and - mockableParameterCount(result) = max(mockableParameterCount(getAConstructor())) and + result = this.getAConstructor() and + mockableParameterCount(result) = max(mockableParameterCount(this.getAConstructor())) and result.getNumberOfParameters() > 0 } @@ -331,7 +333,7 @@ library class MockitoMockInjectedClass extends Class { * it sets. */ Method getASetterMethod() { - result = getAMethod() and + result = this.getAMethod() and exists(MockitoSettableField settableField | result = settableField.getSetterMethod()) } @@ -342,7 +344,7 @@ library class MockitoMockInjectedClass extends Class { * setter method. */ MockitoSettableField getASetField() { - result = getAField() and + result = this.getAField() and not exists(result.getSetterMethod()) } } @@ -353,8 +355,8 @@ library class MockitoMockInjectedClass extends Class { */ class MockitoSettableField extends Field { MockitoSettableField() { - not isFinal() and - not isStatic() and + not this.isFinal() and + not this.isStatic() and exists(MockitoMockInjectedClass injectedClass | injectedClass = this.getDeclaringType()) } diff --git a/java/ql/lib/semmle/code/java/frameworks/Networking.qll b/java/ql/lib/semmle/code/java/frameworks/Networking.qll index 10a1999b8b8..6e5ad743d44 100644 --- a/java/ql/lib/semmle/code/java/frameworks/Networking.qll +++ b/java/ql/lib/semmle/code/java/frameworks/Networking.qll @@ -6,39 +6,39 @@ import semmle.code.java.Type /** The type `java.net.URLConnection`. */ class TypeUrlConnection extends RefType { - TypeUrlConnection() { hasQualifiedName("java.net", "URLConnection") } + TypeUrlConnection() { this.hasQualifiedName("java.net", "URLConnection") } } /** The type `java.net.Socket`. */ class TypeSocket extends RefType { - TypeSocket() { hasQualifiedName("java.net", "Socket") } + TypeSocket() { this.hasQualifiedName("java.net", "Socket") } } /** The type `java.net.URL`. */ class TypeUrl extends RefType { - TypeUrl() { hasQualifiedName("java.net", "URL") } + TypeUrl() { this.hasQualifiedName("java.net", "URL") } } /** The type `java.net.URI`. */ class TypeUri extends RefType { - TypeUri() { hasQualifiedName("java.net", "URI") } + TypeUri() { this.hasQualifiedName("java.net", "URI") } } /** The method `java.net.URLConnection::getInputStream`. */ class URLConnectionGetInputStreamMethod extends Method { URLConnectionGetInputStreamMethod() { - getDeclaringType() instanceof TypeUrlConnection and - hasName("getInputStream") and - hasNoParameters() + this.getDeclaringType() instanceof TypeUrlConnection and + this.hasName("getInputStream") and + this.hasNoParameters() } } /** The method `java.net.Socket::getInputStream`. */ class SocketGetInputStreamMethod extends Method { SocketGetInputStreamMethod() { - getDeclaringType() instanceof TypeSocket and - hasName("getInputStream") and - hasNoParameters() + this.getDeclaringType() instanceof TypeSocket and + this.hasName("getInputStream") and + this.hasNoParameters() } } diff --git a/java/ql/lib/semmle/code/java/frameworks/Protobuf.qll b/java/ql/lib/semmle/code/java/frameworks/Protobuf.qll index 7382294f6f9..7dc29171147 100644 --- a/java/ql/lib/semmle/code/java/frameworks/Protobuf.qll +++ b/java/ql/lib/semmle/code/java/frameworks/Protobuf.qll @@ -30,7 +30,7 @@ class ProtobufMessageLite extends Interface { * Gets a static method named `parseFrom` (or similar) declared on a subtype of the `MessageLite` interface. */ Method getAParseFromMethod() { - result = getASubtype+().getAMethod() and + result = this.getASubtype+().getAMethod() and result.getName().matches("parse%From") and result.isStatic() } @@ -40,13 +40,7 @@ class ProtobufMessageLite extends Interface { */ Method getAGetterMethod() { exists(RefType decl | decl = result.getDeclaringType() and decl = this.getASubtype+() | - exists(string name, string suffix | - suffix = "" or - suffix = "list" or - suffix = "map" or - suffix = "ordefault" or - suffix = "orthrow" - | + exists(string name, string suffix | suffix = ["", "list", "map", "ordefault", "orthrow"] | exists(Field f | f.getDeclaringType() = decl | f.getName().toLowerCase().replaceAll("_", "") = name ) and diff --git a/java/ql/lib/semmle/code/java/frameworks/Rmi.qll b/java/ql/lib/semmle/code/java/frameworks/Rmi.qll index 80243b3ddf8..74a449086cf 100644 --- a/java/ql/lib/semmle/code/java/frameworks/Rmi.qll +++ b/java/ql/lib/semmle/code/java/frameworks/Rmi.qll @@ -14,5 +14,5 @@ class RemoteCallableMethod extends Method { private predicate remoteCallableMethod(Method method) { method.getDeclaringType().getASupertype() instanceof TypeRemote or - exists(Method meth | remoteCallableMethod(meth) and method.getAnOverride() = meth) + exists(Method meth | remoteCallableMethod(meth) and method.overrides(meth)) } diff --git a/java/ql/lib/semmle/code/java/frameworks/Servlets.qll b/java/ql/lib/semmle/code/java/frameworks/Servlets.qll index 1dd4373fb54..76c82b37786 100644 --- a/java/ql/lib/semmle/code/java/frameworks/Servlets.qll +++ b/java/ql/lib/semmle/code/java/frameworks/Servlets.qll @@ -74,7 +74,7 @@ library class HttpServletRequestGetQueryStringMethod extends Method { /** * The method `getPathInfo()` declared in `javax.servlet.http.HttpServletRequest`. */ -library class HttpServletRequestGetPathMethod extends Method { +class HttpServletRequestGetPathMethod extends Method { HttpServletRequestGetPathMethod() { getDeclaringType() instanceof HttpServletRequest and hasName("getPathInfo") and @@ -120,7 +120,7 @@ library class HttpServletRequestGetHeaderNamesMethod extends Method { /** * The method `getRequestURL()` declared in `javax.servlet.http.HttpServletRequest`. */ -library class HttpServletRequestGetRequestURLMethod extends Method { +class HttpServletRequestGetRequestURLMethod extends Method { HttpServletRequestGetRequestURLMethod() { getDeclaringType() instanceof HttpServletRequest and hasName("getRequestURL") and @@ -131,7 +131,7 @@ library class HttpServletRequestGetRequestURLMethod extends Method { /** * The method `getRequestURI()` declared in `javax.servlet.http.HttpServletRequest`. */ -library class HttpServletRequestGetRequestURIMethod extends Method { +class HttpServletRequestGetRequestURIMethod extends Method { HttpServletRequestGetRequestURIMethod() { getDeclaringType() instanceof HttpServletRequest and hasName("getRequestURI") and @@ -191,6 +191,16 @@ class HttpServletResponseSendErrorMethod extends Method { } } +/** + * The method `getRequestDispatcher(String)` declared in `javax.servlet.http.HttpServletRequest` or `javax.servlet.ServletRequest`. + */ +class ServletRequestGetRequestDispatcherMethod extends Method { + ServletRequestGetRequestDispatcherMethod() { + getDeclaringType() instanceof ServletRequest and + hasName("getRequestDispatcher") + } +} + /** * The method `sendRedirect(String)` declared in `javax.servlet.http.HttpServletResponse`. */ diff --git a/java/ql/lib/semmle/code/java/frameworks/SnakeYaml.qll b/java/ql/lib/semmle/code/java/frameworks/SnakeYaml.qll index db5687f916a..31532dcb02d 100644 --- a/java/ql/lib/semmle/code/java/frameworks/SnakeYaml.qll +++ b/java/ql/lib/semmle/code/java/frameworks/SnakeYaml.qll @@ -37,14 +37,14 @@ private class SafeYamlConstructionFlowConfig extends DataFlow2::Configuration { src.asExpr() instanceof SafeSnakeYamlConstruction } - override predicate isSink(DataFlow::Node sink) { sink = yamlClassInstanceExprArgument(_) } + override predicate isSink(DataFlow::Node sink) { sink = this.yamlClassInstanceExprArgument(_) } private DataFlow::ExprNode yamlClassInstanceExprArgument(ClassInstanceExpr cie) { cie.getConstructedType() instanceof Yaml and result.getExpr() = cie.getArgument(0) } - ClassInstanceExpr getSafeYaml() { hasFlowTo(yamlClassInstanceExprArgument(result)) } + ClassInstanceExpr getSafeYaml() { this.hasFlowTo(this.yamlClassInstanceExprArgument(result)) } } /** @@ -70,13 +70,13 @@ private class SafeYamlFlowConfig extends DataFlow3::Configuration { override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SafeYaml } - override predicate isSink(DataFlow::Node sink) { sink = yamlParseQualifier(_) } + override predicate isSink(DataFlow::Node sink) { sink = this.yamlParseQualifier(_) } private DataFlow::ExprNode yamlParseQualifier(SnakeYamlParse syp) { result.getExpr() = syp.getQualifier() } - SnakeYamlParse getASafeSnakeYamlParse() { hasFlowTo(yamlParseQualifier(result)) } + SnakeYamlParse getASafeSnakeYamlParse() { this.hasFlowTo(this.yamlParseQualifier(result)) } } /** diff --git a/java/ql/lib/semmle/code/java/frameworks/SpringLdap.qll b/java/ql/lib/semmle/code/java/frameworks/SpringLdap.qll index 68124273f40..2db8435c22c 100644 --- a/java/ql/lib/semmle/code/java/frameworks/SpringLdap.qll +++ b/java/ql/lib/semmle/code/java/frameworks/SpringLdap.qll @@ -77,8 +77,8 @@ class TypeLdapOperations extends Interface { */ class MethodSpringLdapTemplateAuthenticate extends Method { MethodSpringLdapTemplateAuthenticate() { - getDeclaringType() instanceof TypeSpringLdapTemplate and - hasName("authenticate") + this.getDeclaringType() instanceof TypeSpringLdapTemplate and + this.hasName("authenticate") } } @@ -88,8 +88,8 @@ class MethodSpringLdapTemplateAuthenticate extends Method { */ class MethodSpringLdapTemplateFind extends Method { MethodSpringLdapTemplateFind() { - getDeclaringType() instanceof TypeSpringLdapTemplate and - hasName("find") + this.getDeclaringType() instanceof TypeSpringLdapTemplate and + this.hasName("find") } } @@ -99,8 +99,8 @@ class MethodSpringLdapTemplateFind extends Method { */ class MethodSpringLdapTemplateFindOne extends Method { MethodSpringLdapTemplateFindOne() { - getDeclaringType() instanceof TypeSpringLdapTemplate and - hasName("findOne") + this.getDeclaringType() instanceof TypeSpringLdapTemplate and + this.hasName("findOne") } } @@ -110,8 +110,8 @@ class MethodSpringLdapTemplateFindOne extends Method { */ class MethodSpringLdapTemplateSearch extends Method { MethodSpringLdapTemplateSearch() { - getDeclaringType() instanceof TypeSpringLdapTemplate and - hasName("search") + this.getDeclaringType() instanceof TypeSpringLdapTemplate and + this.hasName("search") } } @@ -121,8 +121,8 @@ class MethodSpringLdapTemplateSearch extends Method { */ class MethodSpringLdapTemplateSearchForContext extends Method { MethodSpringLdapTemplateSearchForContext() { - getDeclaringType() instanceof TypeSpringLdapTemplate and - hasName("searchForContext") + this.getDeclaringType() instanceof TypeSpringLdapTemplate and + this.hasName("searchForContext") } } @@ -132,8 +132,8 @@ class MethodSpringLdapTemplateSearchForContext extends Method { */ class MethodSpringLdapTemplateSearchForObject extends Method { MethodSpringLdapTemplateSearchForObject() { - getDeclaringType() instanceof TypeSpringLdapTemplate and - hasName("searchForObject") + this.getDeclaringType() instanceof TypeSpringLdapTemplate and + this.hasName("searchForObject") } } @@ -143,8 +143,8 @@ class MethodSpringLdapTemplateSearchForObject extends Method { */ class MethodSpringLdapQueryBuilderFilter extends Method { MethodSpringLdapQueryBuilderFilter() { - getDeclaringType() instanceof TypeSpringLdapQueryBuilder and - hasName("filter") + this.getDeclaringType() instanceof TypeSpringLdapQueryBuilder and + this.hasName("filter") } } @@ -154,8 +154,8 @@ class MethodSpringLdapQueryBuilderFilter extends Method { */ class MethodSpringLdapQueryBuilderBase extends Method { MethodSpringLdapQueryBuilderBase() { - getDeclaringType() instanceof TypeSpringLdapQueryBuilder and - hasName("base") + this.getDeclaringType() instanceof TypeSpringLdapQueryBuilder and + this.hasName("base") } } @@ -165,8 +165,8 @@ class MethodSpringLdapQueryBuilderBase extends Method { */ class MethodSpringLdapNameBuilderNewInstance extends Method { MethodSpringLdapNameBuilderNewInstance() { - getDeclaringType() instanceof TypeSpringLdapNameBuilder and - hasName("newInstance") + this.getDeclaringType() instanceof TypeSpringLdapNameBuilder and + this.hasName("newInstance") } } @@ -176,8 +176,8 @@ class MethodSpringLdapNameBuilderNewInstance extends Method { */ class MethodSpringLdapNameBuilderAdd extends Method { MethodSpringLdapNameBuilderAdd() { - getDeclaringType() instanceof TypeSpringLdapNameBuilder and - hasName("add") + this.getDeclaringType() instanceof TypeSpringLdapNameBuilder and + this.hasName("add") } } @@ -187,8 +187,8 @@ class MethodSpringLdapNameBuilderAdd extends Method { */ class MethodSpringLdapNameBuilderBuild extends Method { MethodSpringLdapNameBuilderBuild() { - getDeclaringType() instanceof TypeSpringLdapNameBuilder and - hasName("build") + this.getDeclaringType() instanceof TypeSpringLdapNameBuilder and + this.hasName("build") } } @@ -198,7 +198,7 @@ class MethodSpringLdapNameBuilderBuild extends Method { */ class MethodSpringLdapUtilsNewLdapName extends Method { MethodSpringLdapUtilsNewLdapName() { - getDeclaringType() instanceof TypeSpringLdapUtils and - hasName("newLdapName") + this.getDeclaringType() instanceof TypeSpringLdapUtils and + this.hasName("newLdapName") } } diff --git a/java/ql/lib/semmle/code/java/frameworks/Stream.qll b/java/ql/lib/semmle/code/java/frameworks/Stream.qll new file mode 100644 index 00000000000..6f2faafa9bc --- /dev/null +++ b/java/ql/lib/semmle/code/java/frameworks/Stream.qll @@ -0,0 +1,95 @@ +/** Definitions related to `java.util.stream`. */ + +import semmle.code.java.dataflow.ExternalFlow + +private class StreamModel extends SummaryModelCsv { + override predicate row(string s) { + s = + [ + "java.util.stream;BaseStream;true;iterator;();;Element of Argument[-1];Element of ReturnValue;value", + "java.util.stream;BaseStream;true;onClose;(Runnable);;Element of Argument[-1];Element of ReturnValue;value", + "java.util.stream;BaseStream;true;parallel;();;Element of Argument[-1];Element of ReturnValue;value", + "java.util.stream;BaseStream;true;sequential;();;Element of Argument[-1];Element of ReturnValue;value", + "java.util.stream;BaseStream;true;spliterator;();;Element of Argument[-1];Element of ReturnValue;value", + "java.util.stream;BaseStream;true;unordered;();;Element of Argument[-1];Element of ReturnValue;value", + "java.util.stream;Stream;true;allMatch;(Predicate);;Element of Argument[-1];Parameter[0] of Argument[0];value", + "java.util.stream;Stream;true;anyMatch;(Predicate);;Element of Argument[-1];Parameter[0] of Argument[0];value", + "java.util.stream;Stream;true;collect;(Supplier,BiConsumer,BiConsumer);;ReturnValue of Argument[0];Parameter[0] of Argument[1];value", + "java.util.stream;Stream;true;collect;(Supplier,BiConsumer,BiConsumer);;Parameter[0] of Argument[1];ReturnValue;value", + "java.util.stream;Stream;true;collect;(Supplier,BiConsumer,BiConsumer);;Parameter[0] of Argument[1];Parameter[0..1] of Argument[2];value", + "java.util.stream;Stream;true;collect;(Supplier,BiConsumer,BiConsumer);;Parameter[0..1] of Argument[2];Parameter[0] of Argument[1];value", + "java.util.stream;Stream;true;collect;(Supplier,BiConsumer,BiConsumer);;Element of Argument[-1];Parameter[1] of Argument[1];value", + // Missing: collect(Collector collector) + "java.util.stream;Stream;true;concat;(Stream,Stream);;Element of Argument[0..1];Element of ReturnValue;value", + "java.util.stream;Stream;true;distinct;();;Element of Argument[-1];Element of ReturnValue;value", + "java.util.stream;Stream;true;dropWhile;(Predicate);;Element of Argument[-1];Parameter[0] of Argument[0];value", + "java.util.stream;Stream;true;dropWhile;(Predicate);;Element of Argument[-1];Element of ReturnValue;value", + "java.util.stream;Stream;true;filter;(Predicate);;Element of Argument[-1];Parameter[0] of Argument[0];value", + "java.util.stream;Stream;true;filter;(Predicate);;Element of Argument[-1];Element of ReturnValue;value", + "java.util.stream;Stream;true;findAny;();;Element of Argument[-1];Element of ReturnValue;value", + "java.util.stream;Stream;true;findFirst;();;Element of Argument[-1];Element of ReturnValue;value", + "java.util.stream;Stream;true;flatMap;(Function);;Element of Argument[-1];Parameter[0] of Argument[0];value", + "java.util.stream;Stream;true;flatMap;(Function);;Element of ReturnValue of Argument[0];Element of ReturnValue;value", + "java.util.stream;Stream;true;flatMapToDouble;(Function);;Element of Argument[-1];Parameter[0] of Argument[0];value", + "java.util.stream;Stream;true;flatMapToInt;(Function);;Element of Argument[-1];Parameter[0] of Argument[0];value", + "java.util.stream;Stream;true;flatMapToLong;(Function);;Element of Argument[-1];Parameter[0] of Argument[0];value", + "java.util.stream;Stream;true;forEach;(Consumer);;Element of Argument[-1];Parameter[0] of Argument[0];value", + "java.util.stream;Stream;true;forEachOrdered;(Consumer);;Element of Argument[-1];Parameter[0] of Argument[0];value", + "java.util.stream;Stream;true;generate;(Supplier);;ReturnValue of Argument[0];Element of ReturnValue;value", + "java.util.stream;Stream;true;iterate;(Object,Predicate,UnaryOperator);;Argument[0];Element of ReturnValue;value", + "java.util.stream;Stream;true;iterate;(Object,Predicate,UnaryOperator);;Argument[0];Parameter[0] of Argument[1..2];value", + "java.util.stream;Stream;true;iterate;(Object,Predicate,UnaryOperator);;ReturnValue of Argument[2];Element of ReturnValue;value", + "java.util.stream;Stream;true;iterate;(Object,Predicate,UnaryOperator);;ReturnValue of Argument[2];Parameter[0] of Argument[1..2];value", + "java.util.stream;Stream;true;iterate;(Object,UnaryOperator);;Argument[0];Element of ReturnValue;value", + "java.util.stream;Stream;true;iterate;(Object,UnaryOperator);;Argument[0];Parameter[0] of Argument[1];value", + "java.util.stream;Stream;true;iterate;(Object,UnaryOperator);;ReturnValue of Argument[1];Element of ReturnValue;value", + "java.util.stream;Stream;true;iterate;(Object,UnaryOperator);;ReturnValue of Argument[1];Parameter[0] of Argument[1];value", + "java.util.stream;Stream;true;limit;(long);;Element of Argument[-1];Element of ReturnValue;value", + "java.util.stream;Stream;true;map;(Function);;Element of Argument[-1];Parameter[0] of Argument[0];value", + "java.util.stream;Stream;true;map;(Function);;ReturnValue of Argument[0];Element of ReturnValue;value", + // Missing for mapMulti(BiConsumer) (not currently supported): + // Argument[0] of Parameter[1] of Argument[0] -> Element of Parameter[1] of Argument[0] + // Element of Parameter[1] of Argument[0] -> Element of ReturnValue + "java.util.stream;Stream;true;mapMulti;(BiConsumer);;Element of Argument[-1];Parameter[0] of Argument[0];value", + "java.util.stream;Stream;true;mapMultiToDouble;(BiConsumer);;Element of Argument[-1];Parameter[0] of Argument[0];value", + "java.util.stream;Stream;true;mapMultiToInt;(BiConsumer);;Element of Argument[-1];Parameter[0] of Argument[0];value", + "java.util.stream;Stream;true;mapMultiToLong;(BiConsumer);;Element of Argument[-1];Parameter[0] of Argument[0];value", + "java.util.stream;Stream;true;mapToDouble;(ToDoubleFunction);;Element of Argument[-1];Parameter[0] of Argument[0];value", + "java.util.stream;Stream;true;mapToInt;(ToIntFunction);;Element of Argument[-1];Parameter[0] of Argument[0];value", + "java.util.stream;Stream;true;mapToLong;(ToLongFunction);;Element of Argument[-1];Parameter[0] of Argument[0];value", + "java.util.stream;Stream;true;max;(Comparator);;Element of Argument[-1];Element of ReturnValue;value", + "java.util.stream;Stream;true;max;(Comparator);;Element of Argument[-1];Parameter[0..1] of Argument[0];value", + "java.util.stream;Stream;true;min;(Comparator);;Element of Argument[-1];Element of ReturnValue;value", + "java.util.stream;Stream;true;min;(Comparator);;Element of Argument[-1];Parameter[0..1] of Argument[0];value", + "java.util.stream;Stream;true;noneMatch;(Predicate);;Element of Argument[-1];Parameter[0] of Argument[0];value", + "java.util.stream;Stream;true;of;(Object);;Argument[0];Element of ReturnValue;value", + "java.util.stream;Stream;true;of;(Object[]);;ArrayElement of Argument[0];Element of ReturnValue;value", + "java.util.stream;Stream;true;ofNullable;(Object);;Argument[0];Element of ReturnValue;value", + "java.util.stream;Stream;true;peek;(Consumer);;Element of Argument[-1];Parameter[0] of Argument[0];value", + "java.util.stream;Stream;true;peek;(Consumer);;Element of Argument[-1];Element of ReturnValue;value", + "java.util.stream;Stream;true;reduce;(BinaryOperator);;Element of Argument[-1];Parameter[0..1] of Argument[0];value", + "java.util.stream;Stream;true;reduce;(BinaryOperator);;Element of Argument[-1];Element of ReturnValue;value", + "java.util.stream;Stream;true;reduce;(BinaryOperator);;ReturnValue of Argument[0];Parameter[0..1] of Argument[0];value", + "java.util.stream;Stream;true;reduce;(BinaryOperator);;ReturnValue of Argument[0];Element of ReturnValue;value", + "java.util.stream;Stream;true;reduce;(Object,BinaryOperator);;Element of Argument[-1];Parameter[0..1] of Argument[1];value", + "java.util.stream;Stream;true;reduce;(Object,BinaryOperator);;Argument[0];Parameter[0..1] of Argument[1];value", + "java.util.stream;Stream;true;reduce;(Object,BinaryOperator);;Argument[0];ReturnValue;value", + "java.util.stream;Stream;true;reduce;(Object,BinaryOperator);;ReturnValue of Argument[1];Parameter[0..1] of Argument[1];value", + "java.util.stream;Stream;true;reduce;(Object,BinaryOperator);;ReturnValue of Argument[1];ReturnValue;value", + "java.util.stream;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Element of Argument[-1];Parameter[1] of Argument[1];value", + "java.util.stream;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Argument[0];Parameter[0] of Argument[1];value", + "java.util.stream;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Argument[0];Parameter[0..1] of Argument[2];value", + "java.util.stream;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Argument[0];ReturnValue;value", + "java.util.stream;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;ReturnValue of Argument[1..2];Parameter[0] of Argument[1];value", + "java.util.stream;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;ReturnValue of Argument[1..2];Parameter[0..1] of Argument[2];value", + "java.util.stream;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;ReturnValue of Argument[1..2];ReturnValue;value", + "java.util.stream;Stream;true;skip;(long);;Element of Argument[-1];Element of ReturnValue;value", + "java.util.stream;Stream;true;sorted;;;Element of Argument[-1];Element of ReturnValue;value", + "java.util.stream;Stream;true;sorted;(Comparator);;Element of Argument[-1];Parameter[0..1] of Argument[0];value", + "java.util.stream;Stream;true;takeWhile;(Predicate);;Element of Argument[-1];Parameter[0] of Argument[0];value", + "java.util.stream;Stream;true;takeWhile;(Predicate);;Element of Argument[-1];Element of ReturnValue;value", + "java.util.stream;Stream;true;toArray;;;Element of Argument[-1];ArrayElement of ReturnValue;value", + "java.util.stream;Stream;true;toList;();;Element of Argument[-1];Element of ReturnValue;value" + ] + } +} diff --git a/java/ql/lib/semmle/code/java/frameworks/Thrift.qll b/java/ql/lib/semmle/code/java/frameworks/Thrift.qll index 60ca7ee7b4d..9b2e2fcd5a2 100644 --- a/java/ql/lib/semmle/code/java/frameworks/Thrift.qll +++ b/java/ql/lib/semmle/code/java/frameworks/Thrift.qll @@ -27,7 +27,7 @@ class ThriftIface extends Interface { Method getAnImplementingMethod() { result.getDeclaringType().(Class).getASupertype+() = this and - result.overrides(getAMethod()) and + result.overrides(this.getAMethod()) and not result.getFile() = this.getFile() } } diff --git a/java/ql/lib/semmle/code/java/frameworks/UnboundId.qll b/java/ql/lib/semmle/code/java/frameworks/UnboundId.qll index 8eee0f14ce5..88156da0264 100644 --- a/java/ql/lib/semmle/code/java/frameworks/UnboundId.qll +++ b/java/ql/lib/semmle/code/java/frameworks/UnboundId.qll @@ -35,79 +35,79 @@ class TypeUnboundIdLDAPConnection extends Class { /** A method with the name `setBaseDN` declared in `com.unboundid.ldap.sdk.SearchRequest`. */ class MethodUnboundIdSearchRequestSetBaseDN extends Method { MethodUnboundIdSearchRequestSetBaseDN() { - getDeclaringType() instanceof TypeUnboundIdSearchRequest and - hasName("setBaseDN") + this.getDeclaringType() instanceof TypeUnboundIdSearchRequest and + this.hasName("setBaseDN") } } /** A method with the name `setFilter` declared in `com.unboundid.ldap.sdk.SearchRequest`. */ class MethodUnboundIdSearchRequestSetFilter extends Method { MethodUnboundIdSearchRequestSetFilter() { - getDeclaringType() instanceof TypeUnboundIdSearchRequest and - hasName("setFilter") + this.getDeclaringType() instanceof TypeUnboundIdSearchRequest and + this.hasName("setFilter") } } /** A method with the name `create` declared in `com.unboundid.ldap.sdk.Filter`. */ class MethodUnboundIdFilterCreate extends Method { MethodUnboundIdFilterCreate() { - getDeclaringType() instanceof TypeUnboundIdLdapFilter and - hasName("create") + this.getDeclaringType() instanceof TypeUnboundIdLdapFilter and + this.hasName("create") } } /** A method with the name `createANDFilter` declared in `com.unboundid.ldap.sdk.Filter`. */ class MethodUnboundIdFilterCreateANDFilter extends Method { MethodUnboundIdFilterCreateANDFilter() { - getDeclaringType() instanceof TypeUnboundIdLdapFilter and - hasName("createANDFilter") + this.getDeclaringType() instanceof TypeUnboundIdLdapFilter and + this.hasName("createANDFilter") } } /** A method with the name `createORFilter` declared in `com.unboundid.ldap.sdk.Filter`. */ class MethodUnboundIdFilterCreateORFilter extends Method { MethodUnboundIdFilterCreateORFilter() { - getDeclaringType() instanceof TypeUnboundIdLdapFilter and - hasName("createORFilter") + this.getDeclaringType() instanceof TypeUnboundIdLdapFilter and + this.hasName("createORFilter") } } /** A method with the name `createNOTFilter` declared in `com.unboundid.ldap.sdk.Filter`. */ class MethodUnboundIdFilterCreateNOTFilter extends Method { MethodUnboundIdFilterCreateNOTFilter() { - getDeclaringType() instanceof TypeUnboundIdLdapFilter and - hasName("createNOTFilter") + this.getDeclaringType() instanceof TypeUnboundIdLdapFilter and + this.hasName("createNOTFilter") } } /** A method with the name `simplifyFilter` declared in `com.unboundid.ldap.sdk.Filter`. */ class MethodUnboundIdFilterSimplifyFilter extends Method { MethodUnboundIdFilterSimplifyFilter() { - getDeclaringType() instanceof TypeUnboundIdLdapFilter and - hasName("simplifyFilter") + this.getDeclaringType() instanceof TypeUnboundIdLdapFilter and + this.hasName("simplifyFilter") } } /** A method with the name `search` declared in `com.unboundid.ldap.sdk.LDAPConnection`. */ class MethodUnboundIdLDAPConnectionSearch extends Method { MethodUnboundIdLDAPConnectionSearch() { - getDeclaringType() instanceof TypeUnboundIdLDAPConnection and - hasName("search") + this.getDeclaringType() instanceof TypeUnboundIdLDAPConnection and + this.hasName("search") } } /** A method with the name `asyncSearch` declared in `com.unboundid.ldap.sdk.LDAPConnection`. */ class MethodUnboundIdLDAPConnectionAsyncSearch extends Method { MethodUnboundIdLDAPConnectionAsyncSearch() { - getDeclaringType() instanceof TypeUnboundIdLDAPConnection and - hasName("asyncSearch") + this.getDeclaringType() instanceof TypeUnboundIdLDAPConnection and + this.hasName("asyncSearch") } } /** A method with the name `searchForEntry` declared in `com.unboundid.ldap.sdk.LDAPConnection`. */ class MethodUnboundIdLDAPConnectionSearchForEntry extends Method { MethodUnboundIdLDAPConnectionSearchForEntry() { - getDeclaringType() instanceof TypeUnboundIdLDAPConnection and - hasName("searchForEntry") + this.getDeclaringType() instanceof TypeUnboundIdLDAPConnection and + this.hasName("searchForEntry") } } diff --git a/java/ql/lib/semmle/code/java/frameworks/android/Android.qll b/java/ql/lib/semmle/code/java/frameworks/android/Android.qll index 95d6fd7339c..c019dc11bd8 100644 --- a/java/ql/lib/semmle/code/java/frameworks/android/Android.qll +++ b/java/ql/lib/semmle/code/java/frameworks/android/Android.qll @@ -6,19 +6,29 @@ import java import semmle.code.java.dataflow.ExternalFlow import semmle.code.xml.AndroidManifest +/** + * Gets a transitive superType avoiding magic optimisation + */ +pragma[nomagic] +private RefType getASuperTypePlus(RefType t) { result = t.getASupertype+() } + +/** + * Gets a reflexive/transitive superType avoiding magic optimisation + */ +pragma[inline] +private RefType getASuperTypeStar(RefType t) { result = getASuperTypePlus(t) or result = t } + /** * An Android component. That is, either an activity, a service, * a broadcast receiver, or a content provider. */ class AndroidComponent extends Class { AndroidComponent() { - // The casts here are due to misoptimisation if they are missing - // but are not needed semantically. - this.(Class).getASupertype*().hasQualifiedName("android.app", "Activity") or - this.(Class).getASupertype*().hasQualifiedName("android.app", "Service") or - this.(Class).getASupertype*().hasQualifiedName("android.content", "BroadcastReceiver") or - this.(Class).getASupertype*().hasQualifiedName("android.content", "ContentProvider") or - this.(Class).getASupertype*().hasQualifiedName("android.content", "ContentResolver") + getASuperTypeStar(this).hasQualifiedName("android.app", "Activity") or + getASuperTypeStar(this).hasQualifiedName("android.app", "Service") or + getASuperTypeStar(this).hasQualifiedName("android.content", "BroadcastReceiver") or + getASuperTypeStar(this).hasQualifiedName("android.content", "ContentProvider") or + getASuperTypeStar(this).hasQualifiedName("android.content", "ContentResolver") } /** The XML element corresponding to this Android component. */ @@ -27,10 +37,12 @@ class AndroidComponent extends Class { } /** Holds if this Android component is configured as `exported` in an `AndroidManifest.xml` file. */ - predicate isExported() { getAndroidComponentXmlElement().isExported() } + predicate isExported() { this.getAndroidComponentXmlElement().isExported() } /** Holds if this Android component has an intent filter configured in an `AndroidManifest.xml` file. */ - predicate hasIntentFilter() { exists(getAndroidComponentXmlElement().getAnIntentFilterElement()) } + predicate hasIntentFilter() { + exists(this.getAndroidComponentXmlElement().getAnIntentFilterElement()) + } } /** @@ -43,34 +55,34 @@ class ExportableAndroidComponent extends AndroidComponent { * `AndroidManifest.xml` file. */ override predicate isExported() { - getAndroidComponentXmlElement().isExported() + this.getAndroidComponentXmlElement().isExported() or - hasIntentFilter() and - not getAndroidComponentXmlElement().isNotExported() + this.hasIntentFilter() and + not this.getAndroidComponentXmlElement().isNotExported() } } /** An Android activity. */ class AndroidActivity extends ExportableAndroidComponent { - AndroidActivity() { this.getASupertype*().hasQualifiedName("android.app", "Activity") } + AndroidActivity() { getASuperTypeStar(this).hasQualifiedName("android.app", "Activity") } } /** An Android service. */ class AndroidService extends ExportableAndroidComponent { - AndroidService() { this.getASupertype*().hasQualifiedName("android.app", "Service") } + AndroidService() { getASuperTypeStar(this).hasQualifiedName("android.app", "Service") } } /** An Android broadcast receiver. */ class AndroidBroadcastReceiver extends ExportableAndroidComponent { AndroidBroadcastReceiver() { - this.getASupertype*().hasQualifiedName("android.content", "BroadcastReceiver") + getASuperTypeStar(this).hasQualifiedName("android.content", "BroadcastReceiver") } } /** An Android content provider. */ class AndroidContentProvider extends ExportableAndroidComponent { AndroidContentProvider() { - this.getASupertype*().hasQualifiedName("android.content", "ContentProvider") + getASuperTypeStar(this).hasQualifiedName("android.content", "ContentProvider") } /** @@ -78,14 +90,14 @@ class AndroidContentProvider extends ExportableAndroidComponent { * in an `AndroidManifest.xml` file. */ predicate requiresPermissions() { - getAndroidComponentXmlElement().(AndroidProviderXmlElement).requiresPermissions() + this.getAndroidComponentXmlElement().(AndroidProviderXmlElement).requiresPermissions() } } /** An Android content resolver. */ class AndroidContentResolver extends AndroidComponent { AndroidContentResolver() { - this.getASupertype*().hasQualifiedName("android.content", "ContentResolver") + getASuperTypeStar(this).hasQualifiedName("android.content", "ContentResolver") } } @@ -192,3 +204,44 @@ private class ContentProviderSourceModels extends SourceModelCsv { ] } } + +/** Interface for classes whose instances can be written to and restored from a Parcel. */ +class TypeParcelable extends Interface { + TypeParcelable() { this.hasQualifiedName("android.os", "Parcelable") } +} + +/** + * A method that overrides `android.os.Parcelable.Creator.createFromParcel`. + */ +class CreateFromParcelMethod extends Method { + CreateFromParcelMethod() { + this.hasName("createFromParcel") and + this.getEnclosingCallable().getDeclaringType().getASupertype*() instanceof TypeParcelable + } +} + +private class ParcelPropagationModels extends SummaryModelCsv { + override predicate row(string s) { + // Parcel readers that return their value + s = + "android.os;Parcel;false;read" + + [ + "Array", "ArrayList", "Boolean", "Bundle", "Byte", "Double", "FileDescriptor", "Float", + "HashMap", "Int", "Long", "Parcelable", "ParcelableArray", "PersistableBundle", + "Serializable", "Size", "SizeF", "SparseArray", "SparseBooleanArray", "String", + "StrongBinder", "TypedObject", "Value" + ] + ";;;Argument[-1];ReturnValue;taint" + or + // Parcel readers that write to an existing object + s = + "android.os;Parcel;false;read" + + [ + "BinderArray", "BinderList", "BooleanArray", "ByteArray", "CharArray", "DoubleArray", + "FloatArray", "IntArray", "List", "LongArray", "Map", "ParcelableList", "StringArray", + "StringList", "TypedArray", "TypedList" + ] + ";;;Argument[-1];Argument[0];taint" + or + // One Parcel method that aliases an argument to a return value + s = "android.os;Parcel;false;readParcelableList;;;Argument[0];ReturnValue;value" + } +} diff --git a/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll b/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll index 92c49f3101a..296d6b39b2b 100644 --- a/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll +++ b/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll @@ -1,54 +1,240 @@ import java +private import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.FlowSteps +import semmle.code.java.dataflow.ExternalFlow +/** + * The class `android.content.Intent`. + */ class TypeIntent extends Class { - TypeIntent() { hasQualifiedName("android.content", "Intent") } + TypeIntent() { this.hasQualifiedName("android.content", "Intent") } } +/** + * The class `android.app.Activity`. + */ class TypeActivity extends Class { - TypeActivity() { hasQualifiedName("android.app", "Activity") } + TypeActivity() { this.hasQualifiedName("android.app", "Activity") } } +/** + * The class `android.content.Context`. + */ class TypeContext extends RefType { - TypeContext() { hasQualifiedName("android.content", "Context") } + TypeContext() { this.hasQualifiedName("android.content", "Context") } } +/** + * The class `android.content.BroadcastReceiver`. + */ class TypeBroadcastReceiver extends Class { - TypeBroadcastReceiver() { hasQualifiedName("android.content", "BroadcastReceiver") } + TypeBroadcastReceiver() { this.hasQualifiedName("android.content", "BroadcastReceiver") } } +/** + * The method `Activity.getIntent` + */ class AndroidGetIntentMethod extends Method { - AndroidGetIntentMethod() { hasName("getIntent") and getDeclaringType() instanceof TypeActivity } + AndroidGetIntentMethod() { + this.hasName("getIntent") and this.getDeclaringType() instanceof TypeActivity + } } +/** + * The method `BroadcastReceiver.onReceive`. + */ class AndroidReceiveIntentMethod extends Method { AndroidReceiveIntentMethod() { - hasName("onReceive") and getDeclaringType() instanceof TypeBroadcastReceiver + this.hasName("onReceive") and this.getDeclaringType() instanceof TypeBroadcastReceiver } } +/** + * The method `Context.startActivity` or `startActivities`. + */ class ContextStartActivityMethod extends Method { ContextStartActivityMethod() { - (hasName("startActivity") or hasName("startActivities")) and - getDeclaringType() instanceof TypeContext + (this.hasName("startActivity") or this.hasName("startActivities")) and + this.getDeclaringType() instanceof TypeContext } } -class IntentGetExtraMethod extends Method, TaintPreservingCallable { - IntentGetExtraMethod() { - (getName().regexpMatch("get\\w+Extra") or hasName("getExtras")) and - getDeclaringType() instanceof TypeIntent - } - - override predicate returnsTaintFrom(int arg) { arg = -1 } +/** + * Specifies that if an `Intent` is tainted, then so are its synthetic fields. + */ +private class IntentFieldsInheritTaint extends DataFlow::SyntheticFieldContent, + TaintInheritingContent { + IntentFieldsInheritTaint() { this.getField().matches("android.content.Intent.%") } } -/** A getter on `android.os.BaseBundle` or `android.os.Bundle`. */ -class BundleGetterMethod extends Method, TaintPreservingCallable { - BundleGetterMethod() { - getDeclaringType().hasQualifiedName("android.os", ["BaseBundle", "Bundle"]) and - getName().matches("get%") +/** + * The method `Intent.getParcelableExtra`. + */ +class IntentGetParcelableExtraMethod extends Method { + IntentGetParcelableExtraMethod() { + this.hasName("getParcelableExtra") and + this.getDeclaringType() instanceof TypeIntent + } +} + +private class IntentBundleFlowSteps extends SummaryModelCsv { + override predicate row(string row) { + row = + [ + //"namespace;type;subtypes;name;signature;ext;input;output;kind" + "android.os;BaseBundle;true;get;(String);;MapValue of Argument[-1];ReturnValue;value", + "android.os;BaseBundle;true;getString;(String);;MapValue of Argument[-1];ReturnValue;value", + "android.os;BaseBundle;true;getString;(String,String);;MapValue of Argument[-1];ReturnValue;value", + "android.os;BaseBundle;true;getString;(String,String);;Argument[1];ReturnValue;value", + "android.os;BaseBundle;true;getStringArray;(String);;MapValue of Argument[-1];ReturnValue;value", + "android.os;BaseBundle;true;keySet;();;MapKey of Argument[-1];Element of ReturnValue;value", + "android.os;BaseBundle;true;putAll;(PersistableBundle);;MapKey of Argument[0];MapKey of Argument[-1];value", + "android.os;BaseBundle;true;putAll;(PersistableBundle);;MapValue of Argument[0];MapValue of Argument[-1];value", + "android.os;BaseBundle;true;putBoolean;;;Argument[0];MapKey of Argument[-1];value", + "android.os;BaseBundle;true;putBooleanArray;;;Argument[0];MapKey of Argument[-1];value", + "android.os;BaseBundle;true;putDouble;;;Argument[0];MapKey of Argument[-1];value", + "android.os;BaseBundle;true;putDoubleArray;;;Argument[0];MapKey of Argument[-1];value", + "android.os;BaseBundle;true;putInt;;;Argument[0];MapKey of Argument[-1];value", + "android.os;BaseBundle;true;putIntArray;;;Argument[0];MapKey of Argument[-1];value", + "android.os;BaseBundle;true;putLong;;;Argument[0];MapKey of Argument[-1];value", + "android.os;BaseBundle;true;putLongArray;;;Argument[0];MapKey of Argument[-1];value", + "android.os;BaseBundle;true;putString;;;Argument[0];MapKey of Argument[-1];value", + "android.os;BaseBundle;true;putString;;;Argument[1];MapValue of Argument[-1];value", + "android.os;BaseBundle;true;putStringArray;;;Argument[0];MapKey of Argument[-1];value", + "android.os;BaseBundle;true;putStringArray;;;Argument[1];MapValue of Argument[-1];value", + "android.os;Bundle;false;Bundle;(Bundle);;MapKey of Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;false;Bundle;(Bundle);;MapValue of Argument[0];MapValue of Argument[-1];value", + "android.os;Bundle;false;Bundle;(PersistableBundle);;MapKey of Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;false;Bundle;(PersistableBundle);;MapValue of Argument[0];MapValue of Argument[-1];value", + "android.os;Bundle;true;clone;();;MapKey of Argument[-1];MapKey of ReturnValue;value", + "android.os;Bundle;true;clone;();;MapValue of Argument[-1];MapValue of ReturnValue;value", + // model for Bundle.deepCopy is not fully precise, as some map values aren't copied by value + "android.os;Bundle;true;deepCopy;();;MapKey of Argument[-1];MapKey of ReturnValue;value", + "android.os;Bundle;true;deepCopy;();;MapValue of Argument[-1];MapValue of ReturnValue;value", + "android.os;Bundle;true;getBinder;(String);;MapValue of Argument[-1];ReturnValue;value", + "android.os;Bundle;true;getBundle;(String);;MapValue of Argument[-1];ReturnValue;value", + "android.os;Bundle;true;getByteArray;(String);;MapValue of Argument[-1];ReturnValue;value", + "android.os;Bundle;true;getCharArray;(String);;MapValue of Argument[-1];ReturnValue;value", + "android.os;Bundle;true;getCharSequence;(String);;MapValue of Argument[-1];ReturnValue;value", + "android.os;Bundle;true;getCharSequence;(String,CharSequence);;MapValue of Argument[-1];ReturnValue;value", + "android.os;Bundle;true;getCharSequence;(String,CharSequence);;Argument[1];ReturnValue;value", + "android.os;Bundle;true;getCharSequenceArray;(String);;MapValue of Argument[-1];ReturnValue;value", + "android.os;Bundle;true;getCharSequenceArrayList;(String);;MapValue of Argument[-1];ReturnValue;value", + "android.os;Bundle;true;getParcelable;(String);;MapValue of Argument[-1];ReturnValue;value", + "android.os;Bundle;true;getParcelableArray;(String);;MapValue of Argument[-1];ReturnValue;value", + "android.os;Bundle;true;getParcelableArrayList;(String);;MapValue of Argument[-1];ReturnValue;value", + "android.os;Bundle;true;getSerializable;(String);;MapValue of Argument[-1];ReturnValue;value", + "android.os;Bundle;true;getSparseParcelableArray;(String);;MapValue of Argument[-1];ReturnValue;value", + "android.os;Bundle;true;getStringArrayList;(String);;MapValue of Argument[-1];ReturnValue;value", + "android.os;Bundle;true;putAll;(Bundle);;MapKey of Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putAll;(Bundle);;MapValue of Argument[0];MapValue of Argument[-1];value", + "android.os;Bundle;true;putBinder;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putBinder;;;Argument[1];MapValue of Argument[-1];value", + "android.os;Bundle;true;putBundle;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putBundle;;;Argument[1];MapValue of Argument[-1];value", + "android.os;Bundle;true;putByte;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putByteArray;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putByteArray;;;Argument[1];MapValue of Argument[-1];value", + "android.os;Bundle;true;putChar;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putCharArray;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putCharArray;;;Argument[1];MapValue of Argument[-1];value", + "android.os;Bundle;true;putCharSequence;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putCharSequence;;;Argument[1];MapValue of Argument[-1];value", + "android.os;Bundle;true;putCharSequenceArray;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putCharSequenceArray;;;Argument[1];MapValue of Argument[-1];value", + "android.os;Bundle;true;putCharSequenceArrayList;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putCharSequenceArrayList;;;Argument[1];MapValue of Argument[-1];value", + "android.os;Bundle;true;putFloat;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putFloatArray;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putIntegerArrayList;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putParcelable;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putParcelable;;;Argument[1];MapValue of Argument[-1];value", + "android.os;Bundle;true;putParcelableArray;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putParcelableArray;;;Argument[1];MapValue of Argument[-1];value", + "android.os;Bundle;true;putParcelableArrayList;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putParcelableArrayList;;;Argument[1];MapValue of Argument[-1];value", + "android.os;Bundle;true;putSerializable;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putSerializable;;;Argument[1];MapValue of Argument[-1];value", + "android.os;Bundle;true;putShort;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putShortArray;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putSize;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putSizeF;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putSparseParcelableArray;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putSparseParcelableArray;;;Argument[1];MapValue of Argument[-1];value", + "android.os;Bundle;true;putStringArrayList;;;Argument[0];MapKey of Argument[-1];value", + "android.os;Bundle;true;putStringArrayList;;;Argument[1];MapValue of Argument[-1];value", + "android.os;Bundle;true;readFromParcel;;;Argument[0];MapKey of Argument[-1];taint", + "android.os;Bundle;true;readFromParcel;;;Argument[0];MapValue of Argument[-1];taint", + // currently only the Extras part of the intent and the data field are fully modelled + "android.content;Intent;false;Intent;(Intent);;MapKey of SyntheticField[android.content.Intent.extras] of Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value", + "android.content;Intent;false;Intent;(Intent);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[0];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value", + "android.content;Intent;false;Intent;(String,Uri);;Argument[1];SyntheticField[android.content.Intent.data] of Argument[-1];value", + "android.content;Intent;false;Intent;(String,Uri,Context,Class);;Argument[1];SyntheticField[android.content.Intent.data] of Argument[-1];value", + "android.content;Intent;true;addCategory;;;Argument[-1];ReturnValue;value", + "android.content;Intent;true;addFlags;;;Argument[-1];ReturnValue;value", + "android.content;Intent;false;createChooser;;;Argument[0..2];MapValue of SyntheticField[android.content.Intent.extras] of ReturnValue;value", + "android.content;Intent;true;getBundleExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value", + "android.content;Intent;true;getByteArrayExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value", + "android.content;Intent;true;getCharArrayExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value", + "android.content;Intent;true;getCharSequenceArrayExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value", + "android.content;Intent;true;getCharSequenceArrayListExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value", + "android.content;Intent;true;getCharSequenceExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value", + "android.content;Intent;true;getData;;;SyntheticField[android.content.Intent.data] of Argument[-1];ReturnValue;value", + "android.content;Intent;true;getDataString;;;SyntheticField[android.content.Intent.data] of Argument[-1];ReturnValue;taint", + "android.content;Intent;true;getExtras;();;SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value", + "android.content;Intent;false;getIntent;;;Argument[0];SyntheticField[android.content.Intent.data] of ReturnValue;taint", + "android.content;Intent;false;getIntentOld;;;Argument[0];SyntheticField[android.content.Intent.data] of ReturnValue;taint", + "android.content;Intent;true;getParcelableArrayExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value", + "android.content;Intent;true;getParcelableArrayListExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value", + "android.content;Intent;true;getParcelableExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value", + "android.content;Intent;true;getSerializableExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value", + "android.content;Intent;true;getStringArrayExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value", + "android.content;Intent;true;getStringArrayListExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value", + "android.content;Intent;true;getStringExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value", + "android.content;Intent;false;parseUri;;;Argument[0];SyntheticField[android.content.Intent.data] of ReturnValue;taint", + "android.content;Intent;true;putCharSequenceArrayListExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value", + "android.content;Intent;true;putCharSequenceArrayListExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value", + "android.content;Intent;true;putCharSequenceArrayListExtra;;;Argument[-1];ReturnValue;value", + "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value", + "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value", + "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value", + "android.content;Intent;true;putExtras;(Bundle);;MapKey of Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value", + "android.content;Intent;true;putExtras;(Bundle);;MapValue of Argument[0];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value", + "android.content;Intent;true;putExtras;(Bundle);;Argument[-1];ReturnValue;value", + "android.content;Intent;true;putExtras;(Intent);;MapKey of SyntheticField[android.content.Intent.extras] of Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value", + "android.content;Intent;true;putExtras;(Intent);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[0];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value", + "android.content;Intent;true;putExtras;(Intent);;Argument[-1];ReturnValue;value", + "android.content;Intent;true;putIntegerArrayListExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value", + "android.content;Intent;true;putIntegerArrayListExtra;;;Argument[-1];ReturnValue;value", + "android.content;Intent;true;putParcelableArrayListExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value", + "android.content;Intent;true;putParcelableArrayListExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value", + "android.content;Intent;true;putParcelableArrayListExtra;;;Argument[-1];ReturnValue;value", + "android.content;Intent;true;putStringArrayListExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value", + "android.content;Intent;true;putStringArrayListExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value", + "android.content;Intent;true;putStringArrayListExtra;;;Argument[-1];ReturnValue;value", + "android.content;Intent;true;replaceExtras;(Bundle);;MapKey of Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value", + "android.content;Intent;true;replaceExtras;(Bundle);;MapValue of Argument[0];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value", + "android.content;Intent;true;replaceExtras;(Bundle);;Argument[-1];ReturnValue;value", + "android.content;Intent;true;replaceExtras;(Intent);;MapKey of SyntheticField[android.content.Intent.extras] of Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value", + "android.content;Intent;true;replaceExtras;(Intent);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[0];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value", + "android.content;Intent;true;replaceExtras;(Intent);;Argument[-1];ReturnValue;value", + "android.content;Intent;true;setAction;;;Argument[-1];ReturnValue;value", + "android.content;Intent;true;setClass;;;Argument[-1];ReturnValue;value", + "android.content;Intent;true;setClassName;;;Argument[-1];ReturnValue;value", + "android.content;Intent;true;setComponent;;;Argument[-1];ReturnValue;value", + "android.content;Intent;true;setData;;;Argument[-1];ReturnValue;value", + "android.content;Intent;true;setData;;;Argument[0];SyntheticField[android.content.Intent.data] of Argument[-1];value", + "android.content;Intent;true;setDataAndNormalize;;;Argument[-1];ReturnValue;value", + "android.content;Intent;true;setDataAndNormalize;;;Argument[0];SyntheticField[android.content.Intent.data] of Argument[-1];value", + "android.content;Intent;true;setDataAndType;;;Argument[-1];ReturnValue;value", + "android.content;Intent;true;setDataAndType;;;Argument[0];SyntheticField[android.content.Intent.data] of Argument[-1];value", + "android.content;Intent;true;setDataAndTypeAndNormalize;;;Argument[-1];ReturnValue;value", + "android.content;Intent;true;setDataAndTypeAndNormalize;;;Argument[0];SyntheticField[android.content.Intent.data] of Argument[-1];value", + "android.content;Intent;true;setFlags;;;Argument[-1];ReturnValue;value", + "android.content;Intent;true;setIdentifier;;;Argument[-1];ReturnValue;value", + "android.content;Intent;true;setPackage;;;Argument[-1];ReturnValue;value", + "android.content;Intent;true;setType;;;Argument[-1];ReturnValue;value", + "android.content;Intent;true;setTypeAndNormalize;;;Argument[-1];ReturnValue;value" + ] } - - override predicate returnsTaintFrom(int arg) { arg = -1 } } diff --git a/java/ql/lib/semmle/code/java/frameworks/android/WebView.qll b/java/ql/lib/semmle/code/java/frameworks/android/WebView.qll index 683faa73bef..6717eed4f63 100644 --- a/java/ql/lib/semmle/code/java/frameworks/android/WebView.qll +++ b/java/ql/lib/semmle/code/java/frameworks/android/WebView.qll @@ -1,15 +1,15 @@ import java class TypeWebView extends Class { - TypeWebView() { hasQualifiedName("android.webkit", "WebView") } + TypeWebView() { this.hasQualifiedName("android.webkit", "WebView") } } class TypeWebViewClient extends Class { - TypeWebViewClient() { hasQualifiedName("android.webkit", "WebViewClient") } + TypeWebViewClient() { this.hasQualifiedName("android.webkit", "WebViewClient") } } class TypeWebSettings extends Class { - TypeWebSettings() { hasQualifiedName("android.webkit", "WebSettings") } + TypeWebSettings() { this.hasQualifiedName("android.webkit", "WebSettings") } } class WebViewGetSettingsMethod extends Method { diff --git a/java/ql/lib/semmle/code/java/frameworks/gigaspaces/GigaSpaces.qll b/java/ql/lib/semmle/code/java/frameworks/gigaspaces/GigaSpaces.qll index 6b70770b70e..b7596ebab49 100644 --- a/java/ql/lib/semmle/code/java/frameworks/gigaspaces/GigaSpaces.qll +++ b/java/ql/lib/semmle/code/java/frameworks/gigaspaces/GigaSpaces.qll @@ -37,7 +37,7 @@ predicate isGigaSpacesEventMethod(Method eventMethod) { class GigaSpacesSpaceIdGetterMethod extends Method { GigaSpacesSpaceIdGetterMethod() { getAnAnnotation().getType().hasQualifiedName("com.gigaspaces.annotation.pojo", "SpaceId") and - getName().prefix(3) = "get" + getName().matches("get%") } } @@ -48,7 +48,7 @@ class GigaSpacesSpaceIdSetterMethod extends Method { GigaSpacesSpaceIdSetterMethod() { exists(GigaSpacesSpaceIdGetterMethod getterMethod | getterMethod.getDeclaringType() = getDeclaringType() and - getName().prefix(3) = "set" + getName().matches("set%") | getterMethod.getName().suffix(3) = getName().suffix(3) ) @@ -62,6 +62,6 @@ class GigaSpacesSpaceIdSetterMethod extends Method { class GigaSpacesSpaceRoutingMethod extends Method { GigaSpacesSpaceRoutingMethod() { getAnAnnotation().getType().hasQualifiedName("com.gigaspaces.annotation.pojo", "SpaceRouting") and - getName().prefix(3) = "get" + getName().matches("get%") } } diff --git a/java/ql/lib/semmle/code/java/frameworks/google/GoogleHttpClientApi.qll b/java/ql/lib/semmle/code/java/frameworks/google/GoogleHttpClientApi.qll index 6f3da74f017..d98967566e8 100644 --- a/java/ql/lib/semmle/code/java/frameworks/google/GoogleHttpClientApi.qll +++ b/java/ql/lib/semmle/code/java/frameworks/google/GoogleHttpClientApi.qll @@ -25,14 +25,14 @@ private class TypeLiteralToParseAsFlowConfiguration extends DataFlowForSerializa ) } - TypeLiteral getSourceWithFlowToParseAs() { hasFlow(DataFlow::exprNode(result), _) } + TypeLiteral getSourceWithFlowToParseAs() { this.hasFlow(DataFlow::exprNode(result), _) } } /** A field that is deserialized by `HttpResponse.parseAs`. */ class HttpResponseParseAsDeserializableField extends DeserializableField { HttpResponseParseAsDeserializableField() { exists(RefType decltype, TypeLiteralToParseAsFlowConfiguration conf | - decltype = getDeclaringType() and + decltype = this.getDeclaringType() and conf.getSourceWithFlowToParseAs().getReferencedType() = decltype and decltype.fromSource() ) diff --git a/java/ql/lib/semmle/code/java/frameworks/google/Gson.qll b/java/ql/lib/semmle/code/java/frameworks/google/Gson.qll new file mode 100644 index 00000000000..b9329182ec9 --- /dev/null +++ b/java/ql/lib/semmle/code/java/frameworks/google/Gson.qll @@ -0,0 +1,36 @@ +/** + * Provides classes for working with the Gson framework. + */ + +import java +import semmle.code.java.dataflow.DataFlow +import semmle.code.java.frameworks.android.Android +import semmle.code.java.frameworks.android.Intent + +/** The class `com.google.gson.Gson`. */ +class Gson extends RefType { + Gson() { this.hasQualifiedName("com.google.gson", "Gson") } +} + +/** The `fromJson` deserialization method. */ +class GsonDeserializeMethod extends Method { + GsonDeserializeMethod() { + this.getDeclaringType() instanceof Gson and + this.hasName("fromJson") + } +} + +/** + * Holds if `intentNode` is an `Intent` used in the context `(T)intentNode.getParcelableExtra(...)` and + * `parcelNode` is the corresponding parameter of `Parcelable.Creator { public T createFromParcel(Parcel parcelNode) { }`, + * where `T` is a concrete type implementing `Parcelable`. + */ +predicate intentFlowsToParcel(DataFlow::Node intentNode, DataFlow::Node parcelNode) { + exists(MethodAccess getParcelableExtraCall, CreateFromParcelMethod cfpm, Type createdType | + intentNode.asExpr() = getParcelableExtraCall.getQualifier() and + getParcelableExtraCall.getMethod() instanceof IntentGetParcelableExtraMethod and + DataFlow::localExprFlow(getParcelableExtraCall, any(Expr e | e.getType() = createdType)) and + parcelNode.asParameter() = cfpm.getParameter(0) and + cfpm.getReturnType() = createdType + ) +} diff --git a/java/ql/lib/semmle/code/java/frameworks/gwt/GWT.qll b/java/ql/lib/semmle/code/java/frameworks/gwt/GWT.qll index 93d79813e39..0da20780482 100644 --- a/java/ql/lib/semmle/code/java/frameworks/gwt/GWT.qll +++ b/java/ql/lib/semmle/code/java/frameworks/gwt/GWT.qll @@ -38,7 +38,7 @@ class GwtEntryPointClass extends Class { isGwtXmlIncluded() implies // The entry point is live if it is specified in a `*.gwt.xml` file. - exists(getAGwtXmlFile()) + exists(this.getAGwtXmlFile()) } } @@ -48,7 +48,7 @@ class GwtEntryPointClass extends Class { */ class GwtCompilationUnit extends CompilationUnit { GwtCompilationUnit() { - exists(GwtXmlFile f | getRelativePath().matches(f.getARelativeSourcePath() + "%")) + exists(GwtXmlFile f | this.getRelativePath().matches(f.getARelativeSourcePath() + "%")) } } diff --git a/java/ql/lib/semmle/code/java/frameworks/gwt/GwtUiBinder.qll b/java/ql/lib/semmle/code/java/frameworks/gwt/GwtUiBinder.qll index dc8aa0b1ba6..8532cc81bb3 100644 --- a/java/ql/lib/semmle/code/java/frameworks/gwt/GwtUiBinder.qll +++ b/java/ql/lib/semmle/code/java/frameworks/gwt/GwtUiBinder.qll @@ -12,57 +12,62 @@ import GwtUiBinderXml * An annotation in the package `com.google.gwt.uibinder.client`. */ class GwtUiBinderClientAnnotation extends Annotation { - GwtUiBinderClientAnnotation() { getType().getPackage().hasName("com.google.gwt.uibinder.client") } + GwtUiBinderClientAnnotation() { + this.getType().getPackage().hasName("com.google.gwt.uibinder.client") + } } /** * A `@com.google.gwt.uibinder.client.UiHandler` annotation. */ class GwtUiHandlerAnnotation extends GwtUiBinderClientAnnotation { - GwtUiHandlerAnnotation() { getType().hasName("UiHandler") } + GwtUiHandlerAnnotation() { this.getType().hasName("UiHandler") } } /** * A `@com.google.gwt.uibinder.client.UiField` annotation. */ class GwtUiFieldAnnotation extends GwtUiBinderClientAnnotation { - GwtUiFieldAnnotation() { getType().hasName("UiField") } + GwtUiFieldAnnotation() { this.getType().hasName("UiField") } } /** * A `@com.google.gwt.uibinder.client.UiTemplate` annotation. */ class GwtUiTemplateAnnotation extends GwtUiBinderClientAnnotation { - GwtUiTemplateAnnotation() { getType().hasName("UiTemplate") } + GwtUiTemplateAnnotation() { this.getType().hasName("UiTemplate") } } /** * A `@com.google.gwt.uibinder.client.UiFactory` annotation. */ class GwtUiFactoryAnnotation extends GwtUiBinderClientAnnotation { - GwtUiFactoryAnnotation() { getType().hasName("UiFactory") } + GwtUiFactoryAnnotation() { this.getType().hasName("UiFactory") } } /** * A `@com.google.gwt.uibinder.client.UiConstructor` annotation. */ class GwtUiConstructorAnnotation extends GwtUiBinderClientAnnotation { - GwtUiConstructorAnnotation() { getType().hasName("UiConstructor") } + GwtUiConstructorAnnotation() { this.getType().hasName("UiConstructor") } } /** * A field that is reflectively written to, and read from, by the GWT UiBinder framework. */ class GwtUiField extends Field { - GwtUiField() { getAnAnnotation() instanceof GwtUiFieldAnnotation } + GwtUiField() { this.getAnAnnotation() instanceof GwtUiFieldAnnotation } /** * If true, the field must be filled before `UiBinder.createAndBindUi` is called. * If false, `UiBinder.createAndBindUi` will fill the field. */ predicate isProvided() { - getAnAnnotation().(GwtUiFieldAnnotation).getValue("provided").(BooleanLiteral).getBooleanValue() = - true + this.getAnAnnotation() + .(GwtUiFieldAnnotation) + .getValue("provided") + .(BooleanLiteral) + .getBooleanValue() = true } } @@ -70,14 +75,14 @@ class GwtUiField extends Field { * A method called as a handler for events thrown by GWT widgets. */ class GwtUiHandler extends Method { - GwtUiHandler() { getAnAnnotation() instanceof GwtUiHandlerAnnotation } + GwtUiHandler() { this.getAnAnnotation() instanceof GwtUiHandlerAnnotation } /** * Gets the name of the field for which this handler is registered. */ string getFieldName() { result = - getAnAnnotation() + this.getAnAnnotation() .(GwtUiHandlerAnnotation) .getValue("value") .(CompileTimeConstantExpr) @@ -89,7 +94,7 @@ class GwtUiHandler extends Method { */ GwtUiField getField() { result = this.getDeclaringType().getAField() and - result.getName() = getFieldName() + result.getName() = this.getFieldName() } } @@ -98,12 +103,12 @@ class GwtUiHandler extends Method { * construct an instance of a class specified in a UiBinder XML file. */ class GwtUiFactory extends Method { - GwtUiFactory() { getAnAnnotation() instanceof GwtUiFactoryAnnotation } + GwtUiFactory() { this.getAnAnnotation() instanceof GwtUiFactoryAnnotation } } /** * A constructor that may be called by the UiBinder framework as a result of a `GWT.create()` call. */ class GwtUiConstructor extends Constructor { - GwtUiConstructor() { getAnAnnotation() instanceof GwtUiConstructorAnnotation } + GwtUiConstructor() { this.getAnAnnotation() instanceof GwtUiConstructorAnnotation } } diff --git a/java/ql/lib/semmle/code/java/frameworks/gwt/GwtUiBinderXml.qll b/java/ql/lib/semmle/code/java/frameworks/gwt/GwtUiBinderXml.qll index 26d598b77ef..f5227e0a722 100644 --- a/java/ql/lib/semmle/code/java/frameworks/gwt/GwtUiBinderXml.qll +++ b/java/ql/lib/semmle/code/java/frameworks/gwt/GwtUiBinderXml.qll @@ -36,8 +36,8 @@ class GwtComponentTemplateElement extends XMLElement { */ Class getClass() { exists(string namespace | - namespace = getNamespace().getURI() and - result.getQualifiedName() = namespace.substring(11, namespace.length()) + "." + getName() + namespace = this.getNamespace().getURI() and + result.getQualifiedName() = namespace.substring(11, namespace.length()) + "." + this.getName() ) } } diff --git a/java/ql/lib/semmle/code/java/frameworks/gwt/GwtXml.qll b/java/ql/lib/semmle/code/java/frameworks/gwt/GwtXml.qll index 482d5d70e93..3ac223be2d5 100644 --- a/java/ql/lib/semmle/code/java/frameworks/gwt/GwtXml.qll +++ b/java/ql/lib/semmle/code/java/frameworks/gwt/GwtXml.qll @@ -16,24 +16,24 @@ class GwtXmlFile extends XMLFile { /** Gets the name of an inherited GWT module, for example `com.google.gwt.user.User`. */ string getAnInheritedModuleName() { - result = getModuleElement().getAnInheritsElement().getAnInheritedName() + result = this.getModuleElement().getAnInheritsElement().getAnInheritedName() } /** Gets a GWT module XML file (from source) inherited from this module. */ GwtXmlFile getAnInheritedXmlFile() { exists(GwtXmlFile f, string name | - name = getAnInheritedModuleName() and + name = this.getAnInheritedModuleName() and f.getAbsolutePath().matches("%/" + name.replaceAll(".", "/") + ".gwt.xml") and result = f ) } /** Gets the relative path of the folder containing this GWT module XML file. */ - string getRelativeRootFolderPath() { result = getParentContainer().getRelativePath() } + string getRelativeRootFolderPath() { result = this.getParentContainer().getRelativePath() } /** Gets a GWT-translatable source sub-folder explicitly defined in a `` element. */ string getAnExplicitSourceSubPath() { - result = getModuleElement().getASourceElement().getASourcePath() + result = this.getModuleElement().getASourceElement().getASourcePath() } /** @@ -41,9 +41,9 @@ class GwtXmlFile extends XMLFile { * Either the default `client` folder or as specified by `` tags. */ string getASourceSubPath() { - result = "client" and not exists(getAnExplicitSourceSubPath()) + result = "client" and not exists(this.getAnExplicitSourceSubPath()) or - result = getAnExplicitSourceSubPath() + result = this.getAnExplicitSourceSubPath() } /** @@ -52,7 +52,7 @@ class GwtXmlFile extends XMLFile { * (Includes the full relative root folder path of the GWT module.) */ string getARelativeSourcePath() { - result = getRelativeRootFolderPath() + "/" + getASourceSubPath() + result = this.getRelativeRootFolderPath() + "/" + this.getASourceSubPath() } } @@ -81,7 +81,7 @@ class GwtInheritsElement extends XMLElement { } /** Gets the name of an inherited GWT module, for example `com.google.gwt.user.User`. */ - string getAnInheritedName() { result = getAttribute("name").getValue() } + string getAnInheritedName() { result = this.getAttribute("name").getValue() } } /** An `` element within a GWT module XML file. */ @@ -92,7 +92,7 @@ class GwtEntryPointElement extends XMLElement { } /** Gets the name of a class that serves as a GWT entry-point. */ - string getClassName() { result = getAttribute("class").getValue().trim() } + string getClassName() { result = this.getAttribute("class").getValue().trim() } } /** A `` element within a GWT module XML file. */ @@ -104,11 +104,11 @@ class GwtSourceElement extends XMLElement { /** Gets a path specified to be GWT translatable source code. */ string getASourcePath() { - result = getAttribute("path").getValue() and + result = this.getAttribute("path").getValue() and // Conservative approximation, ignoring Ant-style `FileSet` semantics. - not exists(getAChild()) and - not exists(getAttribute("includes")) and - not exists(getAttribute("excludes")) + not exists(this.getAChild()) and + not exists(this.getAttribute("includes")) and + not exists(this.getAttribute("excludes")) } } @@ -120,5 +120,5 @@ class GwtServletElement extends XMLElement { } /** Gets the name of a class that is used as a servlet. */ - string getClassName() { result = getAttribute("class").getValue().trim() } + string getClassName() { result = this.getAttribute("class").getValue().trim() } } diff --git a/java/ql/lib/semmle/code/java/frameworks/j2objc/J2ObjC.qll b/java/ql/lib/semmle/code/java/frameworks/j2objc/J2ObjC.qll index c3a28fecd42..113c8b76024 100644 --- a/java/ql/lib/semmle/code/java/frameworks/j2objc/J2ObjC.qll +++ b/java/ql/lib/semmle/code/java/frameworks/j2objc/J2ObjC.qll @@ -10,9 +10,9 @@ import java class OCNIComment extends Javadoc { OCNIComment() { // The comment must start with `-[` ... - getChild(0).getText().matches("-[%") and + this.getChild(0).getText().matches("-[%") and // ... and it must end with `]-`. - getChild(getNumChild() - 1).getText().matches("%]-") + this.getChild(this.getNumChild() - 1).getText().matches("%]-") } } @@ -42,9 +42,9 @@ class OCNIMethodComment extends OCNIComment { */ class OCNIImport extends OCNIComment { OCNIImport() { - getAChild().getText().regexpMatch(".*#(import|include).*") and + this.getAChild().getText().regexpMatch(".*#(import|include).*") and not exists(RefType rt | rt.getFile() = this.getFile() | - rt.getLocation().getStartLine() < getLocation().getStartLine() + rt.getLocation().getStartLine() < this.getLocation().getStartLine() ) } } diff --git a/java/ql/lib/semmle/code/java/frameworks/jOOQ.qll b/java/ql/lib/semmle/code/java/frameworks/jOOQ.qll index c95c9f4c90f..8a6ee13ec57 100644 --- a/java/ql/lib/semmle/code/java/frameworks/jOOQ.qll +++ b/java/ql/lib/semmle/code/java/frameworks/jOOQ.qll @@ -24,11 +24,5 @@ predicate jOOQSqlMethod(Method m) { } private class SqlSinkCsv extends SinkModelCsv { - override predicate row(string row) { - row = - [ - //"package;type;overrides;name;signature;ext;spec;kind" - "org.jooq;PlainSQL;false;;;Annotated;Argument[0];sql" - ] - } + override predicate row(string row) { row = "org.jooq;PlainSQL;false;;;Annotated;Argument[0];sql" } } diff --git a/java/ql/lib/semmle/code/java/frameworks/jackson/JacksonSerializability.qll b/java/ql/lib/semmle/code/java/frameworks/jackson/JacksonSerializability.qll index 6dda49dbadd..5f6a5ea0811 100644 --- a/java/ql/lib/semmle/code/java/frameworks/jackson/JacksonSerializability.qll +++ b/java/ql/lib/semmle/code/java/frameworks/jackson/JacksonSerializability.qll @@ -32,21 +32,21 @@ abstract class JacksonSerializableType extends Type { } private class JacksonWriteValueMethod extends Method, TaintPreservingCallable { JacksonWriteValueMethod() { ( - getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectWriter") or - getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectMapper") + this.getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectWriter") or + this.getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectMapper") ) and - getName().matches("writeValue%") and - getParameter(getNumberOfParameters() - 1).getType() instanceof TypeObject + this.getName().matches("writeValue%") and + this.getParameter(this.getNumberOfParameters() - 1).getType() instanceof TypeObject } override predicate returnsTaintFrom(int arg) { - getNumberOfParameters() = 1 and + this.getNumberOfParameters() = 1 and arg = 0 } override predicate transfersTaint(int src, int sink) { - getNumberOfParameters() > 1 and - src = getNumberOfParameters() - 1 and + this.getNumberOfParameters() > 1 and + src = this.getNumberOfParameters() - 1 and sink = 0 } } @@ -58,10 +58,10 @@ private class JacksonWriteValueMethod extends Method, TaintPreservingCallable { private class JacksonReadValueMethod extends Method, TaintPreservingCallable { JacksonReadValueMethod() { ( - getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectReader") or - getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectMapper") + this.getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectReader") or + this.getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectMapper") ) and - hasName(["readValue", "readValues"]) + this.hasName(["readValue", "readValues"]) } override predicate returnsTaintFrom(int arg) { arg = 0 } @@ -109,7 +109,7 @@ private class TypeLiteralToJacksonDatabindFlowConfiguration extends DataFlowForS ) } - TypeLiteral getSourceWithFlowToJacksonDatabind() { hasFlow(DataFlow::exprNode(result), _) } + TypeLiteral getSourceWithFlowToJacksonDatabind() { this.hasFlow(DataFlow::exprNode(result), _) } } /** A type whose values are explicitly deserialized in a call to a Jackson method. */ @@ -139,7 +139,7 @@ private class FieldReferencedJacksonDeserializableType extends JacksonDeserializ class JacksonSerializableField extends SerializableField { JacksonSerializableField() { exists(JacksonSerializableType superType | - superType = getDeclaringType().getASupertype*() and + superType = this.getDeclaringType().getASupertype*() and not superType instanceof TypeObject and superType.fromSource() ) and @@ -151,7 +151,7 @@ class JacksonSerializableField extends SerializableField { class JacksonDeserializableField extends DeserializableField { JacksonDeserializableField() { exists(JacksonDeserializableType superType | - superType = getDeclaringType().getASupertype*() and + superType = this.getDeclaringType().getASupertype*() and not superType instanceof TypeObject and superType.fromSource() ) and @@ -161,7 +161,7 @@ class JacksonDeserializableField extends DeserializableField { /** A call to a field that may be deserialized using the Jackson JSON framework. */ private class JacksonDeserializableFieldAccess extends FieldAccess { - JacksonDeserializableFieldAccess() { getField() instanceof JacksonDeserializableField } + JacksonDeserializableFieldAccess() { this.getField() instanceof JacksonDeserializableField } } /** @@ -194,19 +194,19 @@ class JacksonAddMixinCall extends MethodAccess { /** * Gets a possible type for the target of the mixing, if any can be deduced. */ - RefType getATarget() { result = inferClassParameterType(getArgument(0)) } + RefType getATarget() { result = inferClassParameterType(this.getArgument(0)) } /** * Gets a possible type that will be mixed in, if any can be deduced. */ - RefType getAMixedInType() { result = inferClassParameterType(getArgument(1)) } + RefType getAMixedInType() { result = inferClassParameterType(this.getArgument(1)) } } /** * A Jackson annotation. */ class JacksonAnnotation extends Annotation { - JacksonAnnotation() { getType().getPackage().hasName("com.fasterxml.jackson.annotation") } + JacksonAnnotation() { this.getType().getPackage().hasName("com.fasterxml.jackson.annotation") } } /** @@ -228,7 +228,7 @@ class JacksonMixinType extends ClassOrInterface { * Gets a callable from this type that is mixed in by Jackson. */ Callable getAMixedInCallable() { - result = getACallable() and + result = this.getACallable() and ( result.(Constructor).isDefaultConstructor() or result.getAnAnnotation() instanceof JacksonAnnotation or @@ -240,7 +240,7 @@ class JacksonMixinType extends ClassOrInterface { * Gets a field that is mixed in by Jackson. */ Field getAMixedInField() { - result = getAField() and + result = this.getAField() and result.getAnAnnotation() instanceof JacksonAnnotation } } @@ -264,17 +264,17 @@ class JacksonMixedInCallable extends Callable { * Gets a callable on a possible target that this is mixed into. */ Callable getATargetCallable() { - exists(RefType targetType | targetType = getATargetType() | - result = getATargetType().getACallable() and + exists(RefType targetType | targetType = this.getATargetType() | + result = this.getATargetType().getACallable() and if this instanceof Constructor then // The mixed in type will have a different name to the target type, so just compare the // parameters. result.getSignature().suffix(targetType.getName().length()) = - getSignature().suffix(getDeclaringType().getName().length()) + this.getSignature().suffix(this.getDeclaringType().getName().length()) else // Signatures should match - result.getSignature() = getSignature() + result.getSignature() = this.getSignature() ) } } @@ -285,6 +285,7 @@ private class JacksonModel extends SummaryModelCsv { [ "com.fasterxml.jackson.databind;ObjectMapper;true;valueToTree;;;Argument[0];ReturnValue;taint", "com.fasterxml.jackson.databind;ObjectMapper;true;valueToTree;;;MapValue of Argument[0];ReturnValue;taint", + "com.fasterxml.jackson.databind;ObjectMapper;true;valueToTree;;;Element of MapValue of Argument[0];ReturnValue;taint", "com.fasterxml.jackson.databind;ObjectMapper;true;convertValue;;;Argument[0];ReturnValue;taint", "com.fasterxml.jackson.databind;ObjectMapper;false;createParser;;;Argument[0];ReturnValue;taint", "com.fasterxml.jackson.databind;ObjectReader;false;createParser;;;Argument[0];ReturnValue;taint", diff --git a/java/ql/lib/semmle/code/java/frameworks/javaee/Persistence.qll b/java/ql/lib/semmle/code/java/frameworks/javaee/Persistence.qll index e980cb2187a..82569754394 100644 --- a/java/ql/lib/semmle/code/java/frameworks/javaee/Persistence.qll +++ b/java/ql/lib/semmle/code/java/frameworks/javaee/Persistence.qll @@ -10,8 +10,8 @@ import java */ class PersistentEntity extends RefType { PersistentEntity() { - getAnAnnotation() instanceof EntityAnnotation or - getAnAnnotation() instanceof EmbeddableAnnotation + this.getAnAnnotation() instanceof EntityAnnotation or + this.getAnAnnotation() instanceof EmbeddableAnnotation } /** @@ -22,12 +22,12 @@ class PersistentEntity extends RefType { * instead. */ string getAccessType() { - if exists(getAccessTypeFromAnnotation()) - then result = getAccessTypeFromAnnotation() + if exists(this.getAccessTypeFromAnnotation()) + then result = this.getAccessTypeFromAnnotation() else // If the access type is not explicit, then the location of the `Id` annotation determines // which access type is used. - if getAMethod().hasAnnotation("javax.persistence", "Id") + if this.getAMethod().hasAnnotation("javax.persistence", "Id") then result = "property" else result = "field" } @@ -36,7 +36,7 @@ class PersistentEntity extends RefType { * Gets the access type for this entity as defined by a `@javax.persistence.Access` annotation, if any. */ string getAccessTypeFromAnnotation() { - exists(AccessAnnotation accessType | accessType = getAnAnnotation() | + exists(AccessAnnotation accessType | accessType = this.getAnAnnotation() | result = accessType.getValue("value").(FieldRead).getField().(EnumConstant).getName().toLowerCase() ) diff --git a/java/ql/lib/semmle/code/java/frameworks/javaee/PersistenceXML.qll b/java/ql/lib/semmle/code/java/frameworks/javaee/PersistenceXML.qll index 8051b470bd3..82fc32baca2 100644 --- a/java/ql/lib/semmle/code/java/frameworks/javaee/PersistenceXML.qll +++ b/java/ql/lib/semmle/code/java/frameworks/javaee/PersistenceXML.qll @@ -100,7 +100,7 @@ class PersistencePropertyElement extends XMLElement { * disables the EclipseLink shared cache. */ predicate disablesEclipseLinkSharedCache() { - getAttribute("name").getValue() = "eclipselink.cache.shared.default" and - getAttribute("value").getValue() = "false" + this.getAttribute("name").getValue() = "eclipselink.cache.shared.default" and + this.getAttribute("value").getValue() = "false" } } diff --git a/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJB.qll b/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJB.qll index 9f180dcd587..8509afc5622 100644 --- a/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJB.qll +++ b/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJB.qll @@ -8,7 +8,7 @@ import EJBJarXML */ abstract class EJB extends Class { /** Gets a `Callable` that is directly or indirectly called from within the EJB. */ - Callable getAUsedCallable() { getACallable().polyCalls*(result) } + Callable getAUsedCallable() { this.getACallable().polyCalls*(result) } } /** @@ -33,16 +33,16 @@ class SessionEJB extends EJB { // Either the EJB does not declare any business interfaces explicitly // and implements a single interface candidate, // which is then considered to be the business interface... - count(getAnExplicitBusinessInterface()) = 0 and - count(getAnImplementedBusinessInterfaceCandidate()) = 1 and - result = getAnImplementedBusinessInterfaceCandidate() + count(this.getAnExplicitBusinessInterface()) = 0 and + count(this.getAnImplementedBusinessInterfaceCandidate()) = 1 and + result = this.getAnImplementedBusinessInterfaceCandidate() or // ...or each business interface needs to be declared explicitly. ( - count(getAnImplementedBusinessInterfaceCandidate()) != 1 or - count(getAnExplicitBusinessInterface()) != 0 + count(this.getAnImplementedBusinessInterfaceCandidate()) != 1 or + count(this.getAnExplicitBusinessInterface()) != 0 ) and - result = getAnExplicitBusinessInterface() + result = this.getAnExplicitBusinessInterface() } /** @@ -198,7 +198,7 @@ abstract class EjbInterfaceAnnotation extends Annotation { // Returns the type `Foo` of any type literal `Foo.class` occurring // within the "value" element of this annotation. // Uses `getAChildExpr*()` since the "value" element can have type `Class` or `Class[]`. - exists(TypeLiteral tl | tl = getValue("value").getAChildExpr*() | + exists(TypeLiteral tl | tl = this.getValue("value").getAChildExpr*() | result = tl.getReferencedType() ) } @@ -447,7 +447,7 @@ class AnnotatedRemoteHomeInterface extends LegacyEjbRemoteHomeInterface { SessionEJB getAnEJB() { result.getAnAnnotation().(RemoteHomeAnnotation).getANamedType() = this } /** Gets a remote interface associated with this legacy remote home interface. */ - Interface getAnAssociatedRemoteInterface() { result = getACreateMethod().getReturnType() } + Interface getAnAssociatedRemoteInterface() { result = this.getACreateMethod().getReturnType() } } /** A legacy remote home interface specified within an XML deployment descriptor. */ @@ -511,7 +511,7 @@ class AnnotatedLocalHomeInterface extends LegacyEjbLocalHomeInterface { SessionEJB getAnEJB() { result.getAnAnnotation().(LocalHomeAnnotation).getANamedType() = this } /** Gets a local interface associated with this legacy local home interface. */ - Interface getAnAssociatedLocalInterface() { result = getACreateMethod().getReturnType() } + Interface getAnAssociatedLocalInterface() { result = this.getACreateMethod().getReturnType() } } /** A legacy local home interface specified within an XML deployment descriptor. */ @@ -562,8 +562,8 @@ class RemoteInterface extends Interface { /** Gets a remote method implementation for this remote interface. */ Method getARemoteMethodImplementation() { - result = getARemoteMethodImplementationChecked() or - result = getARemoteMethodImplementationUnchecked() + result = this.getARemoteMethodImplementationChecked() or + result = this.getARemoteMethodImplementationUnchecked() } /** @@ -572,7 +572,7 @@ class RemoteInterface extends Interface { * abstract methods or overriding within an interface hierarchy. */ Method getARemoteMethodImplementationChecked() { - result.overrides(getARemoteMethod()) and + result.overrides(this.getARemoteMethod()) and exists(result.getBody()) } @@ -586,9 +586,9 @@ class RemoteInterface extends Interface { */ Method getARemoteMethodImplementationUnchecked() { exists(SessionEJB ejb, Method rm | - ejb = getAnEJB() and + ejb = this.getAnEJB() and not ejb.getASupertype*() = this and - rm = getARemoteMethod() and + rm = this.getARemoteMethod() and result = getAnInheritedMatchingMethodIgnoreThrows(ejb, rm.getSignature()) and not exists(inheritsMatchingMethodExceptThrows(ejb, rm)) ) and diff --git a/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJBJarXML.qll b/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJBJarXML.qll index 02e73c2be5c..db89836ff9d 100644 --- a/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJBJarXML.qll +++ b/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJBJarXML.qll @@ -114,8 +114,8 @@ class EjbJarSessionElement extends EjbJarBeanTypeElement { * This is either a `business-local` or `business-remote` element. */ XMLElement getABusinessElement() { - result = getABusinessLocalElement() or - result = getABusinessRemoteElement() + result = this.getABusinessLocalElement() or + result = this.getABusinessRemoteElement() } /** Gets a `remote` child XML element of this `session` XML element. */ @@ -153,7 +153,7 @@ class EjbJarSessionElement extends EjbJarBeanTypeElement { * XML element nested within this `session` XML element. */ XMLElement getACreateMethodNameElement() { - result = getAnInitMethodElement().getACreateMethodElement().getAMethodNameElement() + result = this.getAnInitMethodElement().getACreateMethodElement().getAMethodNameElement() } /** @@ -161,7 +161,7 @@ class EjbJarSessionElement extends EjbJarBeanTypeElement { * XML element nested within this `session` XML element. */ XMLElement getABeanMethodNameElement() { - result = getAnInitMethodElement().getABeanMethodElement().getAMethodNameElement() + result = this.getAnInitMethodElement().getABeanMethodElement().getAMethodNameElement() } } diff --git a/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJBRestrictions.qll b/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJBRestrictions.qll index bb50f2c5d2f..c62b72ab9fc 100644 --- a/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJBRestrictions.qll +++ b/java/ql/lib/semmle/code/java/frameworks/javaee/ejb/EJBRestrictions.qll @@ -299,10 +299,7 @@ class RuntimeExitOrHaltMethod extends Method { (this.hasName("exit") or this.hasName("halt")) and this.getNumberOfParameters() = 1 and this.getParameter(0).getType().(PrimitiveType).hasName("int") and - this.getDeclaringType() - .getASupertype*() - .getSourceDeclaration() - .hasQualifiedName("java.lang", "Runtime") + this.getDeclaringType().getASupertype*().getSourceDeclaration() instanceof TypeRuntime } } @@ -315,10 +312,7 @@ class RuntimeAddOrRemoveShutdownHookMethod extends Method { (this.hasName("addShutdownHook") or this.hasName("removeShutdownHook")) and this.getNumberOfParameters() = 1 and this.getParameter(0).getType().(RefType).hasQualifiedName("java.lang", "Thread") and - this.getDeclaringType() - .getASupertype*() - .getSourceDeclaration() - .hasQualifiedName("java.lang", "Runtime") + this.getDeclaringType().getASupertype*().getSourceDeclaration() instanceof TypeRuntime } } @@ -414,33 +408,29 @@ class ForbiddenSerializationMethod extends Method { /** * A method named `enableReplaceObject` declared in - * the class `java.io.ObjectInputStream` or a subclass thereof. + * the class `java.io.ObjectOutputStream` or a subclass thereof. */ class EnableReplaceObjectMethod extends Method { EnableReplaceObjectMethod() { this.hasName("enableReplaceObject") and this.getNumberOfParameters() = 1 and this.getParameter(0).getType().(PrimitiveType).hasName("boolean") and - this.getDeclaringType() - .getASupertype*() - .getSourceDeclaration() - .hasQualifiedName("java.io", "ObjectOutputStream") + this.getDeclaringType().getASupertype*().getSourceDeclaration() instanceof + TypeObjectOutputStream } } /** * A method named `replaceObject` declared in - * the class `java.io.ObjectInputStream` or a subclass thereof. + * the class `java.io.ObjectOutputStream` or a subclass thereof. */ class ReplaceObjectMethod extends Method { ReplaceObjectMethod() { this.hasName("replaceObject") and this.getNumberOfParameters() = 1 and this.getParameter(0).getType() instanceof TypeObject and - this.getDeclaringType() - .getASupertype*() - .getSourceDeclaration() - .hasQualifiedName("java.io", "ObjectOutputStream") + this.getDeclaringType().getASupertype*().getSourceDeclaration() instanceof + TypeObjectOutputStream } } @@ -453,10 +443,7 @@ class EnableResolveObjectMethod extends Method { this.hasName("enableResolveObject") and this.getNumberOfParameters() = 1 and this.getParameter(0).getType().(PrimitiveType).hasName("boolean") and - this.getDeclaringType() - .getASupertype*() - .getSourceDeclaration() - .hasQualifiedName("java.io", "ObjectInputStream") + this.getDeclaringType().getASupertype*().getSourceDeclaration() instanceof TypeObjectInputStream } } @@ -469,10 +456,7 @@ class ResolveObjectMethod extends Method { this.hasName("resolveObject") and this.getNumberOfParameters() = 1 and this.getParameter(0).getType() instanceof TypeObject and - this.getDeclaringType() - .getASupertype*() - .getSourceDeclaration() - .hasQualifiedName("java.io", "ObjectInputStream") + this.getDeclaringType().getASupertype*().getSourceDeclaration() instanceof TypeObjectInputStream } } @@ -485,10 +469,7 @@ class ResolveClassMethod extends Method { this.hasName("resolveClass") and this.getNumberOfParameters() = 1 and this.getParameter(0).getType().(RefType).hasQualifiedName("java.io", "ObjectStreamClass") and - this.getDeclaringType() - .getASupertype*() - .getSourceDeclaration() - .hasQualifiedName("java.io", "ObjectInputStream") + this.getDeclaringType().getASupertype*().getSourceDeclaration() instanceof TypeObjectInputStream } } @@ -500,16 +481,8 @@ class ResolveProxyClassMethod extends Method { ResolveProxyClassMethod() { this.hasName("resolveProxyClass") and this.getNumberOfParameters() = 1 and - this.getParameter(0) - .getType() - .(Array) - .getComponentType() - .(RefType) - .hasQualifiedName("java.lang", "String") and - this.getDeclaringType() - .getASupertype*() - .getSourceDeclaration() - .hasQualifiedName("java.io", "ObjectInputStream") + this.getParameter(0).getType().(Array).getComponentType() instanceof TypeString and + this.getDeclaringType().getASupertype*().getSourceDeclaration() instanceof TypeObjectInputStream } } @@ -598,16 +571,13 @@ class SystemOrRuntimeLoadLibraryMethod extends Method { SystemOrRuntimeLoadLibraryMethod() { (this.hasName("load") or this.hasName("loadLibrary")) and this.getNumberOfParameters() = 1 and - this.getParameter(0).getType().(RefType).hasQualifiedName("java.lang", "String") and + this.getParameter(0).getType() instanceof TypeString and ( this.getDeclaringType() .getASupertype*() .getSourceDeclaration() .hasQualifiedName("java.lang", "System") or - this.getDeclaringType() - .getASupertype*() - .getSourceDeclaration() - .hasQualifiedName("java.lang", "Runtime") + this.getDeclaringType().getASupertype*().getSourceDeclaration() instanceof TypeRuntime ) } } @@ -619,9 +589,6 @@ class SystemOrRuntimeLoadLibraryMethod extends Method { class RuntimeExecMethod extends Method { RuntimeExecMethod() { this.hasName("exec") and - this.getDeclaringType() - .getASupertype*() - .getSourceDeclaration() - .hasQualifiedName("java.lang", "Runtime") + this.getDeclaringType().getASupertype*().getSourceDeclaration() instanceof TypeRuntime } } diff --git a/java/ql/lib/semmle/code/java/frameworks/javaee/jsf/JSFFacesContextXML.qll b/java/ql/lib/semmle/code/java/frameworks/javaee/jsf/JSFFacesContextXML.qll index 8d26c1efc62..2d6721298a9 100644 --- a/java/ql/lib/semmle/code/java/frameworks/javaee/jsf/JSFFacesContextXML.qll +++ b/java/ql/lib/semmle/code/java/frameworks/javaee/jsf/JSFFacesContextXML.qll @@ -25,14 +25,14 @@ class FacesConfigXMLElement extends XMLElement { /** * Gets the value for this element, with leading and trailing whitespace trimmed. */ - string getValue() { result = allCharactersString().trim() } + string getValue() { result = this.allCharactersString().trim() } } /** * An element in a JSF config file that declares a managed bean. */ class FacesConfigManagedBean extends FacesConfigXMLElement { - FacesConfigManagedBean() { getName() = "managed-bean" } + FacesConfigManagedBean() { this.getName() = "managed-bean" } } /** @@ -40,21 +40,21 @@ class FacesConfigManagedBean extends FacesConfigXMLElement { */ class FacesConfigManagedBeanClass extends FacesConfigXMLElement { FacesConfigManagedBeanClass() { - getName() = "managed-bean-class" and - getParent() instanceof FacesConfigManagedBean + this.getName() = "managed-bean-class" and + this.getParent() instanceof FacesConfigManagedBean } /** * Gets the `Class` of the managed bean. */ - Class getManagedBeanClass() { result.getQualifiedName() = getValue() } + Class getManagedBeanClass() { result.getQualifiedName() = this.getValue() } } /** * An element in a JSF config file that declares a custom component. */ class FacesConfigComponent extends FacesConfigXMLElement { - FacesConfigComponent() { getName() = "component" } + FacesConfigComponent() { this.getName() = "component" } } /** @@ -62,12 +62,12 @@ class FacesConfigComponent extends FacesConfigXMLElement { */ class FacesConfigComponentClass extends FacesConfigXMLElement { FacesConfigComponentClass() { - getName() = "component-class" and - getParent() instanceof FacesConfigComponent + this.getName() = "component-class" and + this.getParent() instanceof FacesConfigComponent } /** * Gets the `Class` of the faces component. */ - Class getFacesComponentClass() { result.getQualifiedName() = getValue() } + Class getFacesComponentClass() { result.getQualifiedName() = this.getValue() } } diff --git a/java/ql/lib/semmle/code/java/frameworks/javaee/jsf/JSFRenderer.qll b/java/ql/lib/semmle/code/java/frameworks/javaee/jsf/JSFRenderer.qll index 5703cde79ed..1b825d29c2f 100644 --- a/java/ql/lib/semmle/code/java/frameworks/javaee/jsf/JSFRenderer.qll +++ b/java/ql/lib/semmle/code/java/frameworks/javaee/jsf/JSFRenderer.qll @@ -33,9 +33,9 @@ private class ExternalContextSource extends SourceModelCsv { */ class FacesGetResponseWriterMethod extends Method { FacesGetResponseWriterMethod() { - getDeclaringType() instanceof FacesContext and - hasName("getResponseWriter") and - getNumberOfParameters() = 0 + this.getDeclaringType() instanceof FacesContext and + this.hasName("getResponseWriter") and + this.getNumberOfParameters() = 0 } } @@ -44,9 +44,9 @@ class FacesGetResponseWriterMethod extends Method { */ class FacesGetResponseStreamMethod extends Method { FacesGetResponseStreamMethod() { - getDeclaringType() instanceof FacesContext and - hasName("getResponseStream") and - getNumberOfParameters() = 0 + this.getDeclaringType() instanceof FacesContext and + this.hasName("getResponseStream") and + this.getNumberOfParameters() = 0 } } diff --git a/java/ql/lib/semmle/code/java/frameworks/javase/Http.qll b/java/ql/lib/semmle/code/java/frameworks/javase/Http.qll index 29a6c5f6646..5f03c0b190f 100644 --- a/java/ql/lib/semmle/code/java/frameworks/javase/Http.qll +++ b/java/ql/lib/semmle/code/java/frameworks/javase/Http.qll @@ -6,12 +6,12 @@ import java /** The interface representing `HttpRequest.Builder`. */ class TypeHttpRequestBuilder extends Interface { - TypeHttpRequestBuilder() { hasQualifiedName("java.net.http", "HttpRequest$Builder") } + TypeHttpRequestBuilder() { this.hasQualifiedName("java.net.http", "HttpRequest$Builder") } } /** The interface representing `java.net.http.HttpRequest`. */ class TypeHttpRequest extends Interface { - TypeHttpRequest() { hasQualifiedName("java.net.http", "HttpRequest") } + TypeHttpRequest() { this.hasQualifiedName("java.net.http", "HttpRequest") } } /** The `uri` method on `java.net.http.HttpRequest.Builder`. */ diff --git a/java/ql/lib/semmle/code/java/frameworks/play/Play.qll b/java/ql/lib/semmle/code/java/frameworks/play/Play.qll index efe2e128fee..695f64f53d1 100644 --- a/java/ql/lib/semmle/code/java/frameworks/play/Play.qll +++ b/java/ql/lib/semmle/code/java/frameworks/play/Play.qll @@ -45,7 +45,7 @@ class PlayAddCsrfTokenAnnotation extends Annotation { * The type `play.libs.F.Promise`. */ class PlayAsyncResultPromise extends MemberType { - PlayAsyncResultPromise() { hasQualifiedName("play.libs", "F$Promise") } + PlayAsyncResultPromise() { this.hasQualifiedName("play.libs", "F$Promise") } } /** diff --git a/java/ql/lib/semmle/code/java/frameworks/ratpack/Ratpack.qll b/java/ql/lib/semmle/code/java/frameworks/ratpack/Ratpack.qll new file mode 100644 index 00000000000..a53ac3df874 --- /dev/null +++ b/java/ql/lib/semmle/code/java/frameworks/ratpack/Ratpack.qll @@ -0,0 +1,87 @@ +/** + * Provides classes and predicates related to `ratpack.*`. + */ + +import java +private import semmle.code.java.dataflow.DataFlow +private import semmle.code.java.dataflow.FlowSteps +private import semmle.code.java.dataflow.ExternalFlow + +/** + * Ratpack methods that access user-supplied request data. + */ +private class RatpackHttpSource extends SourceModelCsv { + override predicate row(string row) { + row = + ["ratpack.http;", "ratpack.core.http;"] + + [ + "Request;true;getContentLength;;;ReturnValue;remote", + "Request;true;getCookies;;;ReturnValue;remote", + "Request;true;oneCookie;;;ReturnValue;remote", + "Request;true;getHeaders;;;ReturnValue;remote", + "Request;true;getPath;;;ReturnValue;remote", "Request;true;getQuery;;;ReturnValue;remote", + "Request;true;getQueryParams;;;ReturnValue;remote", + "Request;true;getRawUri;;;ReturnValue;remote", "Request;true;getUri;;;ReturnValue;remote", + "Request;true;getBody;;;ReturnValue;remote" + ] + or + // All Context#parse methods that return a Promise are remote flow sources. + row = + ["ratpack.handling;", "ratpack.core.handling;"] + "Context;true;parse;" + + [ + "(java.lang.Class);", "(com.google.common.reflect.TypeToken);", + "(java.lang.Class,java.lang.Object);", + "(com.google.common.reflect.TypeToken,java.lang.Object);", "(ratpack.core.parse.Parse);", + "(ratpack.parse.Parse);" + ] + ";ReturnValue;remote" + } +} + +/** + * Ratpack methods that propagate user-supplied request data as tainted. + */ +private class RatpackModel extends SummaryModelCsv { + override predicate row(string row) { + row = + ["ratpack.http;", "ratpack.core.http;"] + + [ + "TypedData;true;getBuffer;;;Argument[-1];ReturnValue;taint", + "TypedData;true;getBytes;;;Argument[-1];ReturnValue;taint", + "TypedData;true;getContentType;;;Argument[-1];ReturnValue;taint", + "TypedData;true;getInputStream;;;Argument[-1];ReturnValue;taint", + "TypedData;true;getText;;;Argument[-1];ReturnValue;taint", + "TypedData;true;writeTo;;;Argument[-1];Argument[0];taint", + "Headers;true;get;;;Argument[-1];ReturnValue;taint", + "Headers;true;getAll;;;Argument[-1];ReturnValue;taint", + "Headers;true;getNames;;;Argument[-1];ReturnValue;taint", + "Headers;true;asMultiValueMap;;;Argument[-1];ReturnValue;taint" + ] + or + row = + ["ratpack.form;", "ratpack.core.form;"] + + [ + "UploadedFile;true;getFileName;;;Argument[-1];ReturnValue;taint", + "Form;true;file;;;Argument[-1];ReturnValue;taint", + "Form;true;files;;;Argument[-1];ReturnValue;taint" + ] + or + row = + ["ratpack.handling;", "ratpack.core.handling;"] + + [ + "Context;true;parse;(ratpack.http.TypedData,ratpack.parse.Parse);;Argument[0];ReturnValue;taint", + "Context;true;parse;(ratpack.core.http.TypedData,ratpack.core.parse.Parse);;Argument[0];ReturnValue;taint", + "Context;true;parse;(ratpack.core.http.TypedData,ratpack.core.parse.Parse);;Argument[0];MapKey of ReturnValue;taint", + "Context;true;parse;(ratpack.core.http.TypedData,ratpack.core.parse.Parse);;Argument[0];MapValue of ReturnValue;taint" + ] + or + row = + ["ratpack.util;", "ratpack.func;"] + + [ + "MultiValueMap;true;getAll;;;MapKey of Argument[-1];MapKey of ReturnValue;value", + "MultiValueMap;true;getAll;();;MapValue of Argument[-1];Element of MapValue of ReturnValue;value", + "MultiValueMap;true;getAll;(Object);;MapValue of Argument[-1];Element of ReturnValue;value", + "MultiValueMap;true;asMultimap;;;MapKey of Argument[-1];MapKey of ReturnValue;value", + "MultiValueMap;true;asMultimap;;;MapValue of Argument[-1];MapValue of ReturnValue;value" + ] + } +} diff --git a/java/ql/lib/semmle/code/java/frameworks/ratpack/RatpackExec.qll b/java/ql/lib/semmle/code/java/frameworks/ratpack/RatpackExec.qll new file mode 100644 index 00000000000..ee73901224a --- /dev/null +++ b/java/ql/lib/semmle/code/java/frameworks/ratpack/RatpackExec.qll @@ -0,0 +1,79 @@ +/** + * Provides classes and predicates related to `ratpack.exec.*`. + */ + +import java +private import semmle.code.java.dataflow.DataFlow +private import semmle.code.java.dataflow.FlowSteps +private import semmle.code.java.dataflow.ExternalFlow + +/** + * Model for Ratpack `Promise` methods. + */ +private class RatpackExecModel extends SummaryModelCsv { + override predicate row(string row) { + //"namespace;type;overrides;name;signature;ext;inputspec;outputspec;kind", + row = + ["ratpack.exec;Promise;true;"] + + [ + // `Promise` creation methods + "value;;;Argument[0];Element of ReturnValue;value", + "flatten;;;Element of ReturnValue of Argument[0];Element of ReturnValue;value", + "sync;;;ReturnValue of Argument[0];Element of ReturnValue;value", + // `Promise` value transformation methods + "map;;;Element of Argument[-1];Parameter[0] of Argument[0];value", + "map;;;ReturnValue of Argument[0];Element of ReturnValue;value", + "blockingMap;;;Element of Argument[-1];Parameter[0] of Argument[0];value", + "blockingMap;;;ReturnValue of Argument[0];Element of ReturnValue;value", + "mapError;;;ReturnValue of Argument[1];Element of ReturnValue;value", + // `apply` passes the qualifier to the function as the first argument + "apply;;;Element of Argument[-1];Element of Parameter[0] of Argument[0];value", + "apply;;;Element of ReturnValue of Argument[0];Element of ReturnValue;value", + // `Promise` termination method + "then;;;Element of Argument[-1];Parameter[0] of Argument[0];value", + // 'next' accesses qualfier the 'Promise' value and also returns the qualifier + "next;;;Element of Argument[-1];Parameter[0] of Argument[0];value", + "next;;;Argument[-1];ReturnValue;value", + // 'cacheIf' accesses qualfier the 'Promise' value and also returns the qualifier + "cacheIf;;;Element of Argument[-1];Parameter[0] of Argument[0];value", + "cacheIf;;;Argument[-1];ReturnValue;value", + // 'route' accesses qualfier the 'Promise' value, and conditionally returns the qualifier or + // the result of the second argument + "route;;;Element of Argument[-1];Parameter[0] of Argument[0];value", + "route;;;Element of Argument[-1];Parameter[0] of Argument[1];value", + "route;;;Argument[-1];ReturnValue;value", + // `flatMap` type methods return their returned `Promise` + "flatMap;;;Element of Argument[-1];Parameter[0] of Argument[0];value", + "flatMap;;;Element of ReturnValue of Argument[0];Element of ReturnValue;value", + "flatMapError;;;Element of ReturnValue of Argument[1];Element of ReturnValue;value", + // `mapIf` methods conditionally map their values, or return themselves + "mapIf;;;Element of Argument[-1];Parameter[0] of Argument[0];value", + "mapIf;;;Element of Argument[-1];Parameter[0] of Argument[1];value", + "mapIf;;;Element of Argument[-1];Parameter[0] of Argument[2];value", + "mapIf;;;ReturnValue of Argument[1];Element of ReturnValue;value", + "mapIf;;;ReturnValue of Argument[2];Element of ReturnValue;value" + ] + } +} + +/** A reference type that extends a parameterization the Promise type. */ +private class RatpackPromise extends RefType { + RatpackPromise() { + this.getSourceDeclaration().getASourceSupertype*().hasQualifiedName("ratpack.exec", "Promise") + } +} + +/** + * Ratpack `Promise` method that will return `this`. + */ +private class RatpackPromiseFluentMethod extends FluentMethod { + RatpackPromiseFluentMethod() { + not this.isStatic() and + // It's generally safe to assume that if the return type exactly matches the declaring type, `this` will be returned. + exists(ParameterizedType t | + t instanceof RatpackPromise and + t = this.getDeclaringType() and + t = this.getReturnType() + ) + } +} diff --git a/java/ql/lib/semmle/code/java/frameworks/spring/SpringAutowire.qll b/java/ql/lib/semmle/code/java/frameworks/spring/SpringAutowire.qll index e695b26ba0c..f387b40a547 100644 --- a/java/ql/lib/semmle/code/java/frameworks/spring/SpringAutowire.qll +++ b/java/ql/lib/semmle/code/java/frameworks/spring/SpringAutowire.qll @@ -24,7 +24,7 @@ predicate hasInjectAnnotation(Annotatable a) { class SpringComponentConstructor extends Constructor { SpringComponentConstructor() { // Must be a live Spring component. - getDeclaringType().(SpringComponent).isLive() and + this.getDeclaringType().(SpringComponent).isLive() and ( this.getNumberOfParameters() = 0 or hasInjectAnnotation(this) @@ -93,8 +93,8 @@ class SpringBeanXMLAutowiredSetterMethod extends Method { ) ) and // The resulting bean is of the right type. - result.getClass().getAnAncestor() = getParameter(0).getType() and - getNumberOfParameters() = 1 and + result.getClass().getAnAncestor() = this.getParameter(0).getType() and + this.getNumberOfParameters() = 1 and this.getName().matches("set%") ) } @@ -110,7 +110,7 @@ class SpringBeanAutowiredCallable extends Callable { // Marked as `@Autowired`. hasInjectAnnotation(this) and // No autowiring occurs if there are no parameters - getNumberOfParameters() > 0 + this.getNumberOfParameters() > 0 } /** @@ -118,7 +118,7 @@ class SpringBeanAutowiredCallable extends Callable { * defined in. */ SpringBean getEnclosingSpringBean() { - result = getDeclaringType().(SpringBeanRefType).getSpringBean() + result = this.getDeclaringType().(SpringBeanRefType).getSpringBean() } /** @@ -129,22 +129,24 @@ class SpringBeanAutowiredCallable extends Callable { /** * Gets the qualifier annotation for parameter at `pos`, if any. */ - SpringQualifierAnnotation getQualifier(int pos) { result = getParameter(pos).getAnAnnotation() } + SpringQualifierAnnotation getQualifier(int pos) { + result = this.getParameter(pos).getAnAnnotation() + } /** * Gets the qualifier annotation for this method, if any. */ - SpringQualifierAnnotation getQualifier() { result = getAnAnnotation() } + SpringQualifierAnnotation getQualifier() { result = this.getAnAnnotation() } /** * Gets the resource annotation for this method, if any. */ - SpringResourceAnnotation getResource() { result = getAnAnnotation() } + SpringResourceAnnotation getResource() { result = this.getAnAnnotation() } /** * Gets a bean that will be injected into this callable. */ - SpringBean getAnInjectedBean() { result = getInjectedBean(_) } + SpringBean getAnInjectedBean() { result = this.getInjectedBean(_) } /** * Gets the `SpringBean`, if any, that will be injected for the parameter at position `pos`, @@ -152,24 +154,24 @@ class SpringBeanAutowiredCallable extends Callable { */ SpringBean getInjectedBean(int pos) { // Must be a sub-type of the parameter type - result.getClass().getAnAncestor() = getParameterType(pos) and + result.getClass().getAnAncestor() = this.getParameterType(pos) and // Now look up bean - if exists(getQualifier(pos)) + if exists(this.getQualifier(pos)) then // Resolved by `@Qualifier("qualifier")` specified on the parameter - result = getQualifier(pos).getSpringBean() + result = this.getQualifier(pos).getSpringBean() else - if exists(getQualifier()) and getNumberOfParameters() = 1 + if exists(this.getQualifier()) and this.getNumberOfParameters() = 1 then // Resolved by `@Qualifier("qualifier")` on the method pos = 0 and - result = getQualifier().getSpringBean() + result = this.getQualifier().getSpringBean() else - if exists(getResource().getNameValue()) and getNumberOfParameters() = 1 + if exists(this.getResource().getNameValue()) and this.getNumberOfParameters() = 1 then // Resolved by looking at the name part of `@Resource(name="qualifier")` pos = 0 and - result = getResource().getSpringBean() + result = this.getResource().getSpringBean() else // Otherwise no restrictions, just by type any() @@ -181,24 +183,24 @@ class SpringBeanAutowiredCallable extends Callable { */ SpringComponent getInjectedComponent(int pos) { // Must be a sub-type of the parameter type - result.getAnAncestor() = getParameterType(pos) and + result.getAnAncestor() = this.getParameterType(pos) and // Now look up bean - if exists(getQualifier(pos)) + if exists(this.getQualifier(pos)) then // Resolved by `@Qualifier("qualifier")` specified on the parameter - result = getQualifier(pos).getSpringComponent() + result = this.getQualifier(pos).getSpringComponent() else - if exists(getQualifier()) and getNumberOfParameters() = 1 + if exists(this.getQualifier()) and this.getNumberOfParameters() = 1 then // Resolved by `@Qualifier("qualifier")` on the method pos = 0 and - result = getQualifier().getSpringComponent() + result = this.getQualifier().getSpringComponent() else - if exists(getResource().getNameValue()) and getNumberOfParameters() = 1 + if exists(this.getResource().getNameValue()) and this.getNumberOfParameters() = 1 then // Resolved by looking at the name part of `@Resource(name="qualifier")` pos = 0 and - result = getResource().getSpringComponent() + result = this.getResource().getSpringComponent() else // Otherwise no restrictions, just by type any() @@ -219,7 +221,7 @@ class SpringBeanAutowiredField extends Field { * defined in. */ SpringBean getEnclosingSpringBean() { - result = getDeclaringType().(SpringBeanRefType).getSpringBean() + result = this.getDeclaringType().(SpringBeanRefType).getSpringBean() } /** @@ -230,12 +232,12 @@ class SpringBeanAutowiredField extends Field { /** * Gets the qualifier annotation for this method, if any. */ - SpringQualifierAnnotation getQualifier() { result = getAnAnnotation() } + SpringQualifierAnnotation getQualifier() { result = this.getAnAnnotation() } /** * Gets the resource annotation for this method, if any. */ - SpringResourceAnnotation getResource() { result = getAnAnnotation() } + SpringResourceAnnotation getResource() { result = this.getAnAnnotation() } /** * Gets the `SpringBean`, if any, that will be injected for this field, considering any `@Qualifier` @@ -243,17 +245,17 @@ class SpringBeanAutowiredField extends Field { */ SpringBean getInjectedBean() { // Must be a sub-type of the parameter type - result.getClass().getAnAncestor() = getType() and + result.getClass().getAnAncestor() = this.getType() and // Now look up bean - if exists(getQualifier()) + if exists(this.getQualifier()) then // Resolved by `@Qualifier("qualifier")` specified on the field - result = getQualifier().getSpringBean() + result = this.getQualifier().getSpringBean() else - if exists(getResource().getNameValue()) + if exists(this.getResource().getNameValue()) then // Resolved by looking at the name part of `@Resource(name="qualifier")` - result = getResource().getSpringBean() + result = this.getResource().getSpringBean() else // Otherwise no restrictions, just by type any() @@ -265,17 +267,17 @@ class SpringBeanAutowiredField extends Field { */ SpringComponent getInjectedComponent() { // Must be a sub-type of the parameter type - result.getAnAncestor() = getType() and + result.getAnAncestor() = this.getType() and // Now look up bean - if exists(getQualifier()) + if exists(this.getQualifier()) then // Resolved by `@Qualifier("qualifier")` specified on the field - result = getQualifier().getSpringComponent() + result = this.getQualifier().getSpringComponent() else - if exists(getResource().getNameValue()) + if exists(this.getResource().getNameValue()) then // Resolved by looking at the name part of `@Resource(name="qualifier")` - result = getResource().getSpringComponent() + result = this.getResource().getSpringComponent() else // Otherwise no restrictions, just by type any() @@ -287,9 +289,9 @@ class SpringBeanAutowiredField extends Field { */ class SpringQualifierAnnotationType extends AnnotationType { SpringQualifierAnnotationType() { - hasQualifiedName("org.springframework.beans.factory.annotation", "Qualifier") or - hasQualifiedName("javax.inject", "Qualifier") or - getAnAnnotation().getType() instanceof SpringQualifierAnnotationType + this.hasQualifiedName("org.springframework.beans.factory.annotation", "Qualifier") or + this.hasQualifiedName("javax.inject", "Qualifier") or + this.getAnAnnotation().getType() instanceof SpringQualifierAnnotationType } } @@ -299,15 +301,15 @@ class SpringQualifierAnnotationType extends AnnotationType { */ class SpringQualifierDefinitionAnnotation extends Annotation { SpringQualifierDefinitionAnnotation() { - getType() instanceof SpringQualifierAnnotationType and - getAnnotatedElement() instanceof SpringComponent + this.getType() instanceof SpringQualifierAnnotationType and + this.getAnnotatedElement() instanceof SpringComponent } /** * Gets the value of the qualifier field for this qualifier. */ string getQualifierValue() { - result = getValue("value").(CompileTimeConstantExpr).getStringValue() + result = this.getValue("value").(CompileTimeConstantExpr).getStringValue() } } @@ -315,24 +317,24 @@ class SpringQualifierDefinitionAnnotation extends Annotation { * A qualifier annotation on a method or field that is used to disambiguate which bean will be used. */ class SpringQualifierAnnotation extends Annotation { - SpringQualifierAnnotation() { getType() instanceof SpringQualifierAnnotationType } + SpringQualifierAnnotation() { this.getType() instanceof SpringQualifierAnnotationType } /** * Gets the value of the qualifier field for this qualifier. */ string getQualifierValue() { - result = getValue("value").(CompileTimeConstantExpr).getStringValue() + result = this.getValue("value").(CompileTimeConstantExpr).getStringValue() } /** * Gets the bean definition in an XML file that this qualifier resolves to, if any. */ - SpringBean getSpringBean() { result.getQualifierValue() = getQualifierValue() } + SpringBean getSpringBean() { result.getQualifierValue() = this.getQualifierValue() } /** * Gets the Spring component that this qualifier resolves to, if any. */ - SpringComponent getSpringComponent() { result.getQualifierValue() = getQualifierValue() } + SpringComponent getSpringComponent() { result.getQualifierValue() = this.getQualifierValue() } } /** @@ -340,20 +342,22 @@ class SpringQualifierAnnotation extends Annotation { * autowired by Spring, and can optionally specify a qualifier in the "name". */ class SpringResourceAnnotation extends Annotation { - SpringResourceAnnotation() { getType().hasQualifiedName("javax.inject", "Resource") } + SpringResourceAnnotation() { this.getType().hasQualifiedName("javax.inject", "Resource") } /** * Gets the specified name value, if any. */ - string getNameValue() { result = getValue("name").(CompileTimeConstantExpr).getStringValue() } + string getNameValue() { + result = this.getValue("name").(CompileTimeConstantExpr).getStringValue() + } /** * Gets the bean definition in an XML file that the resource resolves to, if any. */ - SpringBean getSpringBean() { result.getQualifierValue() = getNameValue() } + SpringBean getSpringBean() { result.getQualifierValue() = this.getNameValue() } /** * Gets the Spring component that this qualifier resolves to, if any. */ - SpringComponent getSpringComponent() { result.getQualifierValue() = getNameValue() } + SpringComponent getSpringComponent() { result.getQualifierValue() = this.getNameValue() } } diff --git a/java/ql/lib/semmle/code/java/frameworks/spring/SpringBean.qll b/java/ql/lib/semmle/code/java/frameworks/spring/SpringBean.qll index e4b97375efd..df3799153e4 100644 --- a/java/ql/lib/semmle/code/java/frameworks/spring/SpringBean.qll +++ b/java/ql/lib/semmle/code/java/frameworks/spring/SpringBean.qll @@ -16,7 +16,7 @@ class SpringBean extends SpringXMLElement { SpringBean() { this.getName() = "bean" and // Do not capture Camel beans, which are different - not getNamespace().getURI() = "http://camel.apache.org/schema/spring" + not this.getNamespace().getURI() = "http://camel.apache.org/schema/spring" } override string toString() { result = this.getBeanIdentifier() } @@ -383,7 +383,7 @@ class SpringBean extends SpringXMLElement { // If a factory bean is specified, use that, otherwise use the current bean. ( if exists(this.getFactoryBeanName()) - then result.getDeclaringType() = getFactoryBean().getClass() + then result.getDeclaringType() = this.getFactoryBean().getClass() else ( result.getDeclaringType() = this.getClass() and // Must be static because we don't yet have an instance. @@ -400,9 +400,9 @@ class SpringBean extends SpringXMLElement { * the bean identifier if no qualifier is specified. */ string getQualifierValue() { - if exists(getQualifier()) - then result = getQualifier().getQualifierValue() - else result = getBeanIdentifier() + if exists(this.getQualifier()) + then result = this.getQualifier().getQualifierValue() + else result = this.getBeanIdentifier() } /** diff --git a/java/ql/lib/semmle/code/java/frameworks/spring/SpringBeanFile.qll b/java/ql/lib/semmle/code/java/frameworks/spring/SpringBeanFile.qll index e9549676e1f..2417002a412 100644 --- a/java/ql/lib/semmle/code/java/frameworks/spring/SpringBeanFile.qll +++ b/java/ql/lib/semmle/code/java/frameworks/spring/SpringBeanFile.qll @@ -35,7 +35,12 @@ class SpringBeanFile extends XMLFile { */ string getAProfileExpr() { result = - getBeansElement().getAttribute("profile").getValue().splitAt(",").splitAt(" ").splitAt(";") and + this.getBeansElement() + .getAttribute("profile") + .getValue() + .splitAt(",") + .splitAt(" ") + .splitAt(";") and result.length() != 0 } diff --git a/java/ql/lib/semmle/code/java/frameworks/spring/SpringComponentScan.qll b/java/ql/lib/semmle/code/java/frameworks/spring/SpringComponentScan.qll index 568987114f2..6ee3e68d7f2 100644 --- a/java/ql/lib/semmle/code/java/frameworks/spring/SpringComponentScan.qll +++ b/java/ql/lib/semmle/code/java/frameworks/spring/SpringComponentScan.qll @@ -20,7 +20,7 @@ class SpringXMLComponentScan extends SpringXMLElement { * Gets a profile expression for which this `component-scan` is enabled, or nothing if it is * applicable to any profile. */ - string getAProfileExpr() { result = getSpringBeanFile().getAProfileExpr() } + string getAProfileExpr() { result = this.getSpringBeanFile().getAProfileExpr() } } /** @@ -29,7 +29,7 @@ class SpringXMLComponentScan extends SpringXMLElement { */ class SpringComponentScan extends Annotation { SpringComponentScan() { - getType().hasQualifiedName("org.springframework.context.annotation", "ComponentScan") + this.getType().hasQualifiedName("org.springframework.context.annotation", "ComponentScan") } /** @@ -37,13 +37,13 @@ class SpringComponentScan extends Annotation { */ string getBasePackages() { // "value" and "basePackages" are synonymous, and are simple strings - result = getAValue("basePackages").(StringLiteral).getRepresentedString() + result = this.getAValue("basePackages").(StringLiteral).getRepresentedString() or - result = getAValue("value").(StringLiteral).getRepresentedString() + result = this.getAValue("value").(StringLiteral).getRepresentedString() or exists(TypeLiteral typeLiteral | // Base package classes are type literals whose package should be considered a base package. - typeLiteral = getAValue("basePackageClasses") + typeLiteral = this.getAValue("basePackageClasses") | result = typeLiteral.getReferencedType().(RefType).getPackage().getName() ) @@ -97,10 +97,10 @@ class SpringBasePackage extends string { class SpringComponentAnnotation extends AnnotationType { SpringComponentAnnotation() { // Component used directly as an annotation. - hasQualifiedName("org.springframework.stereotype", "Component") + this.hasQualifiedName("org.springframework.stereotype", "Component") or // Component can be used as a meta-annotation on other annotation types. - getAnAnnotation().getType() instanceof SpringComponentAnnotation + this.getAnAnnotation().getType() instanceof SpringComponentAnnotation } } @@ -117,20 +117,20 @@ private predicate isSpringXMLEnabled() { exists(SpringXMLElement springXMLElemen */ class SpringComponent extends RefType { SpringComponent() { - getAnAnnotation().getType() instanceof SpringComponentAnnotation and + this.getAnAnnotation().getType() instanceof SpringComponentAnnotation and not this instanceof AnnotationType } /** * Gets a qualifier used to distinguish when this class should be autowired into other classes. */ - SpringQualifierDefinitionAnnotation getQualifier() { result = getAnAnnotation() } + SpringQualifierDefinitionAnnotation getQualifier() { result = this.getAnAnnotation() } /** * Gets the `@Component` or equivalent annotation. */ Annotation getComponentAnnotation() { - result = getAnAnnotation() and + result = this.getAnAnnotation() and result.getType() instanceof SpringComponentAnnotation } @@ -138,13 +138,14 @@ class SpringComponent extends RefType { * Gets the bean identifier for this component. */ string getBeanIdentifier() { - if exists(getComponentAnnotation().getValue("value")) + if exists(this.getComponentAnnotation().getValue("value")) then // If the name has been specified in the component annotation, use that. - result = getComponentAnnotation().getValue("value").(CompileTimeConstantExpr).getStringValue() + result = + this.getComponentAnnotation().getValue("value").(CompileTimeConstantExpr).getStringValue() else // Otherwise use the name of the class, with the initial letter lower cased. - exists(string name | name = getName() | + exists(string name | name = this.getName() | result = name.charAt(0).toLowerCase() + name.suffix(1) ) } @@ -154,13 +155,13 @@ class SpringComponent extends RefType { * resolving autowiring on other classes. */ string getQualifierValue() { - if exists(getQualifier()) + if exists(this.getQualifier()) then // If given a qualifier, use the value specified. - result = getQualifier().getQualifierValue() + result = this.getQualifier().getQualifierValue() else // Otherwise, default to the bean identifier. - result = getBeanIdentifier() + result = this.getBeanIdentifier() } /** @@ -184,8 +185,8 @@ class SpringComponent extends RefType { this.getPackage().getName() = sbp ) and ( - not exists(getAProfileExpr()) or - getAProfileExpr().(SpringProfileExpr).isActive() + not exists(this.getAProfileExpr()) or + this.getAProfileExpr().(SpringProfileExpr).isActive() ) } @@ -195,7 +196,7 @@ class SpringComponent extends RefType { */ string getAProfileExpr() { exists(Annotation profileAnnotation | - profileAnnotation = getAnAnnotation() and + profileAnnotation = this.getAnAnnotation() and profileAnnotation .getType() .hasQualifiedName("org.springframework.context.annotation", "Profile") diff --git a/java/ql/lib/semmle/code/java/frameworks/spring/SpringController.qll b/java/ql/lib/semmle/code/java/frameworks/spring/SpringController.qll index 6a3e9c9eb48..53829d02752 100644 --- a/java/ql/lib/semmle/code/java/frameworks/spring/SpringController.qll +++ b/java/ql/lib/semmle/code/java/frameworks/spring/SpringController.qll @@ -9,10 +9,10 @@ import SpringWebClient class SpringControllerAnnotation extends AnnotationType { SpringControllerAnnotation() { // `@Controller` used directly as an annotation. - hasQualifiedName("org.springframework.stereotype", "Controller") + this.hasQualifiedName("org.springframework.stereotype", "Controller") or // `@Controller` can be used as a meta-annotation on other annotation types. - getAnAnnotation().getType() instanceof SpringControllerAnnotation + this.getAnAnnotation().getType() instanceof SpringControllerAnnotation } } @@ -22,28 +22,30 @@ class SpringControllerAnnotation extends AnnotationType { * Rest controllers are the same as controllers, but imply the `@ResponseBody` annotation. */ class SpringRestControllerAnnotation extends SpringControllerAnnotation { - SpringRestControllerAnnotation() { hasName("RestController") } + SpringRestControllerAnnotation() { this.hasName("RestController") } } /** * A class annotated, directly or indirectly, as a Spring `Controller`. */ class SpringController extends Class { - SpringController() { getAnAnnotation().getType() instanceof SpringControllerAnnotation } + SpringController() { this.getAnAnnotation().getType() instanceof SpringControllerAnnotation } } /** * A class annotated, directly or indirectly, as a Spring `RestController`. */ class SpringRestController extends SpringController { - SpringRestController() { getAnAnnotation().getType() instanceof SpringRestControllerAnnotation } + SpringRestController() { + this.getAnAnnotation().getType() instanceof SpringRestControllerAnnotation + } } /** * A method on a Spring controller which is accessed by the Spring MVC framework. */ abstract class SpringControllerMethod extends Method { - SpringControllerMethod() { getDeclaringType() instanceof SpringController } + SpringControllerMethod() { this.getDeclaringType() instanceof SpringController } } /** @@ -83,10 +85,10 @@ class SpringInitBinderMethod extends SpringControllerMethod { class SpringRequestMappingAnnotationType extends AnnotationType { SpringRequestMappingAnnotationType() { // `@RequestMapping` used directly as an annotation. - hasQualifiedName("org.springframework.web.bind.annotation", "RequestMapping") + this.hasQualifiedName("org.springframework.web.bind.annotation", "RequestMapping") or // `@RequestMapping` can be used as a meta-annotation on other annotation types, e.g. GetMapping, PostMapping etc. - getAnAnnotation().getType() instanceof SpringRequestMappingAnnotationType + this.getAnAnnotation().getType() instanceof SpringRequestMappingAnnotationType } } @@ -96,7 +98,7 @@ class SpringRequestMappingAnnotationType extends AnnotationType { class SpringResponseBodyAnnotationType extends AnnotationType { SpringResponseBodyAnnotationType() { // `@ResponseBody` used directly as an annotation. - hasQualifiedName("org.springframework.web.bind.annotation", "ResponseBody") + this.hasQualifiedName("org.springframework.web.bind.annotation", "ResponseBody") } } @@ -129,7 +131,7 @@ class SpringRequestMappingMethod extends SpringControllerMethod { } /** Gets a request mapping parameter. */ - SpringRequestMappingParameter getARequestParameter() { result = getAParameter() } + SpringRequestMappingParameter getARequestParameter() { result = this.getAParameter() } /** Gets the "produces" @RequestMapping annotation value, if present. If an array is specified, gets the array. */ Expr getProducesExpr() { @@ -158,9 +160,9 @@ class SpringRequestMappingMethod extends SpringControllerMethod { /** Holds if this is considered an `@ResponseBody` method. */ predicate isResponseBody() { - getAnAnnotation().getType() instanceof SpringResponseBodyAnnotationType or - getDeclaringType().getAnAnnotation().getType() instanceof SpringResponseBodyAnnotationType or - getDeclaringType() instanceof SpringRestController + this.getAnAnnotation().getType() instanceof SpringResponseBodyAnnotationType or + this.getDeclaringType().getAnAnnotation().getType() instanceof SpringResponseBodyAnnotationType or + this.getDeclaringType() instanceof SpringRestController } } @@ -185,44 +187,50 @@ class SpringServletInputAnnotation extends Annotation { /** An annotation of the type `org.springframework.web.bind.annotation.ModelAttribute`. */ class SpringModelAttributeAnnotation extends Annotation { SpringModelAttributeAnnotation() { - getType().hasQualifiedName("org.springframework.web.bind.annotation", "ModelAttribute") + this.getType().hasQualifiedName("org.springframework.web.bind.annotation", "ModelAttribute") } } /** A parameter of a `SpringRequestMappingMethod`. */ class SpringRequestMappingParameter extends Parameter { - SpringRequestMappingParameter() { getCallable() instanceof SpringRequestMappingMethod } + SpringRequestMappingParameter() { this.getCallable() instanceof SpringRequestMappingMethod } /** Holds if the parameter should not be consider a direct source of taint. */ predicate isNotDirectlyTaintedInput() { - getType().(RefType).getAnAncestor() instanceof SpringWebRequest or - getType().(RefType).getAnAncestor() instanceof SpringNativeWebRequest or - getType().(RefType).getAnAncestor().hasQualifiedName("javax.servlet", "ServletRequest") or - getType().(RefType).getAnAncestor().hasQualifiedName("javax.servlet", "ServletResponse") or - getType().(RefType).getAnAncestor().hasQualifiedName("javax.servlet.http", "HttpSession") or - getType().(RefType).getAnAncestor().hasQualifiedName("javax.servlet.http", "PushBuilder") or - getType().(RefType).getAnAncestor().hasQualifiedName("java.security", "Principal") or - getType().(RefType).getAnAncestor().hasQualifiedName("org.springframework.http", "HttpMethod") or - getType().(RefType).getAnAncestor().hasQualifiedName("java.util", "Locale") or - getType().(RefType).getAnAncestor().hasQualifiedName("java.util", "TimeZone") or - getType().(RefType).getAnAncestor().hasQualifiedName("java.time", "ZoneId") or - getType().(RefType).getAnAncestor().hasQualifiedName("java.io", "OutputStream") or - getType().(RefType).getAnAncestor().hasQualifiedName("java.io", "Writer") or - getType() + this.getType().(RefType).getAnAncestor() instanceof SpringWebRequest or + this.getType().(RefType).getAnAncestor() instanceof SpringNativeWebRequest or + this.getType().(RefType).getAnAncestor().hasQualifiedName("javax.servlet", "ServletRequest") or + this.getType().(RefType).getAnAncestor().hasQualifiedName("javax.servlet", "ServletResponse") or + this.getType().(RefType).getAnAncestor().hasQualifiedName("javax.servlet.http", "HttpSession") or + this.getType().(RefType).getAnAncestor().hasQualifiedName("javax.servlet.http", "PushBuilder") or + this.getType().(RefType).getAnAncestor().hasQualifiedName("java.security", "Principal") or + this.getType() + .(RefType) + .getAnAncestor() + .hasQualifiedName("org.springframework.http", "HttpMethod") or + this.getType().(RefType).getAnAncestor().hasQualifiedName("java.util", "Locale") or + this.getType().(RefType).getAnAncestor().hasQualifiedName("java.util", "TimeZone") or + this.getType().(RefType).getAnAncestor().hasQualifiedName("java.time", "ZoneId") or + this.getType().(RefType).getAnAncestor().hasQualifiedName("java.io", "OutputStream") or + this.getType().(RefType).getAnAncestor().hasQualifiedName("java.io", "Writer") or + this.getType() .(RefType) .getAnAncestor() .hasQualifiedName("org.springframework.web.servlet.mvc.support", "RedirectAttributes") or // Also covers BindingResult. Note, you can access the field value through this interface, which should be considered tainted - getType().(RefType).getAnAncestor().hasQualifiedName("org.springframework.validation", "Errors") or - getType() + this.getType() + .(RefType) + .getAnAncestor() + .hasQualifiedName("org.springframework.validation", "Errors") or + this.getType() .(RefType) .getAnAncestor() .hasQualifiedName("org.springframework.web.bind.support", "SessionStatus") or - getType() + this.getType() .(RefType) .getAnAncestor() .hasQualifiedName("org.springframework.web.util", "UriComponentsBuilder") or - getType() + this.getType() .(RefType) .getAnAncestor() .hasQualifiedName("org.springframework.data.domain", "Pageable") or @@ -231,13 +239,13 @@ class SpringRequestMappingParameter extends Parameter { private predicate isExplicitlyTaintedInput() { // InputStream or Reader parameters allow access to the body of a request - getType().(RefType).getAnAncestor().hasQualifiedName("java.io", "InputStream") or - getType().(RefType).getAnAncestor().hasQualifiedName("java.io", "Reader") or + this.getType().(RefType).getAnAncestor().hasQualifiedName("java.io", "InputStream") or + this.getType().(RefType).getAnAncestor().hasQualifiedName("java.io", "Reader") or // The SpringServletInputAnnotations allow access to the URI, request parameters, cookie values and the body of the request this.getAnAnnotation() instanceof SpringServletInputAnnotation or // HttpEntity is like @RequestBody, but with a wrapper including the headers // TODO model unwrapping aspects - getType().(RefType).getASourceSupertype*() instanceof SpringHttpEntity or + this.getType().(RefType).getASourceSupertype*() instanceof SpringHttpEntity or this.getAnAnnotation() .getType() .hasQualifiedName("org.springframework.web.bind.annotation", "RequestAttribute") or @@ -249,35 +257,35 @@ class SpringRequestMappingParameter extends Parameter { private predicate isImplicitRequestParam() { // Any parameter which is not explicitly handled, is consider to be an `@RequestParam`, if // it is a simple bean property - not isNotDirectlyTaintedInput() and - not isExplicitlyTaintedInput() and + not this.isNotDirectlyTaintedInput() and + not this.isExplicitlyTaintedInput() and ( - getType() instanceof PrimitiveType or - getType() instanceof TypeString + this.getType() instanceof PrimitiveType or + this.getType() instanceof TypeString ) } private predicate isImplicitModelAttribute() { // Any parameter which is not explicitly handled, is consider to be an `@ModelAttribute`, if // it is not an implicit request param - not isNotDirectlyTaintedInput() and - not isExplicitlyTaintedInput() and - not isImplicitRequestParam() + not this.isNotDirectlyTaintedInput() and + not this.isExplicitlyTaintedInput() and + not this.isImplicitRequestParam() } /** Holds if this is an explicit or implicit `@ModelAttribute` parameter. */ predicate isModelAttribute() { - isImplicitModelAttribute() or - getAnAnnotation() instanceof SpringModelAttributeAnnotation + this.isImplicitModelAttribute() or + this.getAnAnnotation() instanceof SpringModelAttributeAnnotation } /** Holds if the input is tainted. */ predicate isTaintedInput() { - isExplicitlyTaintedInput() + this.isExplicitlyTaintedInput() or // Any parameter which is not explicitly identified, is consider to be an `@RequestParam`, if // it is a simple bean property) or a @ModelAttribute if not - not isNotDirectlyTaintedInput() + not this.isNotDirectlyTaintedInput() } } @@ -286,7 +294,7 @@ class SpringRequestMappingParameter extends Parameter { * the method, which will be used to render the response e.g. as a JSP file. */ abstract class SpringModel extends Parameter { - SpringModel() { getCallable() instanceof SpringRequestMappingMethod } + SpringModel() { this.getCallable() instanceof SpringRequestMappingMethod } /** * Types for which instances are placed inside the model. @@ -298,11 +306,11 @@ abstract class SpringModel extends Parameter { * A `java.util.Map` can be accepted as the model parameter for a Spring `RequestMapping` method. */ class SpringModelPlainMap extends SpringModel { - SpringModelPlainMap() { getType() instanceof MapType } + SpringModelPlainMap() { this.getType() instanceof MapType } override RefType getATypeInModel() { exists(MethodAccess methodCall | - methodCall.getQualifier() = getAnAccess() and + methodCall.getQualifier() = this.getAnAccess() and methodCall.getCallee().hasName("put") | result = methodCall.getArgument(1).getType() @@ -316,13 +324,13 @@ class SpringModelPlainMap extends SpringModel { */ class SpringModelModel extends SpringModel { SpringModelModel() { - getType().(RefType).hasQualifiedName("org.springframework.ui", "Model") or - getType().(RefType).hasQualifiedName("org.springframework.ui", "ModelMap") + this.getType().(RefType).hasQualifiedName("org.springframework.ui", "Model") or + this.getType().(RefType).hasQualifiedName("org.springframework.ui", "ModelMap") } override RefType getATypeInModel() { exists(MethodAccess methodCall | - methodCall.getQualifier() = getAnAccess() and + methodCall.getQualifier() = this.getAnAccess() and methodCall.getCallee().hasName("addAttribute") | result = methodCall.getArgument(methodCall.getNumArgument() - 1).getType() diff --git a/java/ql/lib/semmle/code/java/frameworks/spring/SpringExpression.qll b/java/ql/lib/semmle/code/java/frameworks/spring/SpringExpression.qll index 0b79587c551..155e11544fc 100644 --- a/java/ql/lib/semmle/code/java/frameworks/spring/SpringExpression.qll +++ b/java/ql/lib/semmle/code/java/frameworks/spring/SpringExpression.qll @@ -18,7 +18,7 @@ class ExpressionEvaluationMethod extends Method { * The class `org.springframework.expression.ExpressionParser`. */ class ExpressionParser extends RefType { - ExpressionParser() { hasQualifiedName("org.springframework.expression", "ExpressionParser") } + ExpressionParser() { this.hasQualifiedName("org.springframework.expression", "ExpressionParser") } } /** @@ -26,7 +26,7 @@ class ExpressionParser extends RefType { */ class SimpleEvaluationContextBuilder extends RefType { SimpleEvaluationContextBuilder() { - hasQualifiedName("org.springframework.expression.spel.support", + this.hasQualifiedName("org.springframework.expression.spel.support", "SimpleEvaluationContext$Builder") } } @@ -35,7 +35,7 @@ class SimpleEvaluationContextBuilder extends RefType { * The class `org.springframework.expression.Expression`. */ class Expression extends RefType { - Expression() { hasQualifiedName("org.springframework.expression", "Expression") } + Expression() { this.hasQualifiedName("org.springframework.expression", "Expression") } } /** @@ -43,6 +43,6 @@ class Expression extends RefType { */ class SimpleEvaluationContext extends RefType { SimpleEvaluationContext() { - hasQualifiedName("org.springframework.expression.spel.support", "SimpleEvaluationContext") + this.hasQualifiedName("org.springframework.expression.spel.support", "SimpleEvaluationContext") } } diff --git a/java/ql/lib/semmle/code/java/frameworks/spring/SpringFlex.qll b/java/ql/lib/semmle/code/java/frameworks/spring/SpringFlex.qll index 30ca9db14f7..7ed0f78fd37 100644 --- a/java/ql/lib/semmle/code/java/frameworks/spring/SpringFlex.qll +++ b/java/ql/lib/semmle/code/java/frameworks/spring/SpringFlex.qll @@ -16,22 +16,22 @@ class SpringRemotingDestination extends SpringXMLElement { * Gets the bean that this remoting destination refers to. */ SpringBean getSpringBean() { - result = getParent() or - result.getBeanIdentifier() = getAttribute("ref").getValue() + result = this.getParent() or + result.getBeanIdentifier() = this.getAttribute("ref").getValue() } /** * Methods that are specifically included when the bean is exposed as a remote destination. */ string getAnIncludeMethod() { - result = getAttribute("include-methods").getValue().splitAt(",").trim() + result = this.getAttribute("include-methods").getValue().splitAt(",").trim() } /** * Methods that are specifically excluded when the bean is exposed as a remote destination. */ string getAnExcludeMethod() { - result = getAttribute("exclude-methods").getValue().splitAt(",").trim() + result = this.getAttribute("exclude-methods").getValue().splitAt(",").trim() } } @@ -44,7 +44,7 @@ class SpringRemotingDestinationClass extends Class { this = remotingDestination.getSpringBean().getClass() ) or - hasAnnotation("org.springframework.flex.remoting", "RemotingDestination") and + this.hasAnnotation("org.springframework.flex.remoting", "RemotingDestination") and // Must either be a live bean, or a live component. ( this.(SpringComponent).isLive() or @@ -66,11 +66,11 @@ class SpringRemotingDestinationClass extends Class { * basis, only those methods that are not marked as excluded are exported. */ predicate isIncluding() { - exists(Method m | m = getAMethod() | + exists(Method m | m = this.getAMethod() | m.hasAnnotation("org.springframework.flex.remoting", "RemotingInclude") ) or - exists(getRemotingDestinationXML().getAnIncludeMethod()) + exists(this.getRemotingDestinationXML().getAnIncludeMethod()) } /** @@ -78,13 +78,13 @@ class SpringRemotingDestinationClass extends Class { */ Method getARemotingMethod() { result = this.getAMethod() and - if isIncluding() + if this.isIncluding() then result.hasAnnotation("org.springframework.flex.remoting", "RemotingInclude") or - result.getName() = getRemotingDestinationXML().getAnIncludeMethod() + result.getName() = this.getRemotingDestinationXML().getAnIncludeMethod() else ( not result.hasAnnotation("org.springframework.flex.remoting", "RemotingExclude") and - not result.getName() = getRemotingDestinationXML().getAnExcludeMethod() + not result.getName() = this.getRemotingDestinationXML().getAnExcludeMethod() ) } } diff --git a/java/ql/lib/semmle/code/java/frameworks/spring/SpringHttp.qll b/java/ql/lib/semmle/code/java/frameworks/spring/SpringHttp.qll index 2d313cc8584..d03cea80850 100644 --- a/java/ql/lib/semmle/code/java/frameworks/spring/SpringHttp.qll +++ b/java/ql/lib/semmle/code/java/frameworks/spring/SpringHttp.qll @@ -280,7 +280,7 @@ private DataFlow::Node getABodyBuilderWithExplicitContentType(Expr contentType) .hasQualifiedName("org.springframework.http", "ResponseEntity$BodyBuilder") ) or - DataFlow::localFlow(getABodyBuilderWithExplicitContentType(contentType), result) + DataFlow::localFlowStep(getABodyBuilderWithExplicitContentType(contentType), result) } private DataFlow::Node getASanitizedBodyBuilder() { diff --git a/java/ql/lib/semmle/code/java/frameworks/spring/SpringProfile.qll b/java/ql/lib/semmle/code/java/frameworks/spring/SpringProfile.qll index 32ee55723b2..7f284b0771f 100644 --- a/java/ql/lib/semmle/code/java/frameworks/spring/SpringProfile.qll +++ b/java/ql/lib/semmle/code/java/frameworks/spring/SpringProfile.qll @@ -26,10 +26,10 @@ class SpringProfileExpr extends string { */ predicate isActive() { ( - getProfile() instanceof AlwaysEnabledSpringProfile or - getProfile() instanceof SometimesEnabledSpringProfile + this.getProfile() instanceof AlwaysEnabledSpringProfile or + this.getProfile() instanceof SometimesEnabledSpringProfile ) and - not getProfile() instanceof NeverEnabledSpringProfile + not this.getProfile() instanceof NeverEnabledSpringProfile } } @@ -37,7 +37,7 @@ class SpringProfileExpr extends string { * A Spring profile expression that begins with "!", indicating a negated expression. */ class NotSpringProfileExpr extends SpringProfileExpr { - NotSpringProfileExpr() { this.prefix(1) = "!" } + NotSpringProfileExpr() { this.matches("!%") } /** * Gets the profile described in this profile expression. @@ -48,7 +48,7 @@ class NotSpringProfileExpr extends SpringProfileExpr { * This profile expression is active if it can ever be evaluated to true, according to our * knowledge of which profiles are sometimes/never/always enabled. */ - override predicate isActive() { not getProfile() instanceof AlwaysEnabledSpringProfile } + override predicate isActive() { not this.getProfile() instanceof AlwaysEnabledSpringProfile } } /** diff --git a/java/ql/lib/semmle/code/java/frameworks/spring/SpringWeb.qll b/java/ql/lib/semmle/code/java/frameworks/spring/SpringWeb.qll index dd8e660fd26..b91c6de4933 100644 --- a/java/ql/lib/semmle/code/java/frameworks/spring/SpringWeb.qll +++ b/java/ql/lib/semmle/code/java/frameworks/spring/SpringWeb.qll @@ -25,7 +25,7 @@ class SpringNativeWebRequest extends Class { */ class ModelAndView extends Class { ModelAndView() { - hasQualifiedName(["org.springframework.web.servlet", "org.springframework.web.portlet"], + this.hasQualifiedName(["org.springframework.web.servlet", "org.springframework.web.portlet"], "ModelAndView") } } @@ -33,7 +33,7 @@ class ModelAndView extends Class { /** A call to the Spring `ModelAndView.setViewName` method. */ class SpringModelAndViewSetViewNameCall extends MethodAccess { SpringModelAndViewSetViewNameCall() { - getMethod().getDeclaringType() instanceof ModelAndView and - getMethod().hasName("setViewName") + this.getMethod().getDeclaringType() instanceof ModelAndView and + this.getMethod().hasName("setViewName") } } diff --git a/java/ql/lib/semmle/code/java/frameworks/struts/StrutsActions.qll b/java/ql/lib/semmle/code/java/frameworks/struts/StrutsActions.qll index 0edfaa3711c..775eb3e2b6f 100644 --- a/java/ql/lib/semmle/code/java/frameworks/struts/StrutsActions.qll +++ b/java/ql/lib/semmle/code/java/frameworks/struts/StrutsActions.qll @@ -86,7 +86,7 @@ class Struts2ActionClass extends Class { * Holds if this action class extends the preparable interface. */ predicate isPreparable() { - getAnAncestor().hasQualifiedName("com.opensymphony.xwork2", "Preparable") + this.getAnAncestor().hasQualifiedName("com.opensymphony.xwork2", "Preparable") } /** @@ -96,10 +96,10 @@ class Struts2ActionClass extends Class { * methods only exist if the class `isPreparable()`. */ Method getPrepareMethod() { - isPreparable() and + this.isPreparable() and exists(Struts2ActionMethod actionMethod | - actionMethod = getActionMethod() and - inherits(result) and + actionMethod = this.getActionMethod() and + this.inherits(result) and result .hasName("prepare" + actionMethod.getName().charAt(0).toUpperCase() + actionMethod.getName().suffix(1)) diff --git a/java/ql/lib/semmle/code/java/frameworks/struts/StrutsAnnotations.qll b/java/ql/lib/semmle/code/java/frameworks/struts/StrutsAnnotations.qll index c024f4d6b16..5ee8f25724e 100644 --- a/java/ql/lib/semmle/code/java/frameworks/struts/StrutsAnnotations.qll +++ b/java/ql/lib/semmle/code/java/frameworks/struts/StrutsAnnotations.qll @@ -16,7 +16,7 @@ class StrutsActionAnnotation extends StrutsAnnotation { StrutsActionAnnotation() { this.getType().hasName("Action") } Callable getActionCallable() { - result = getAnnotatedElement() + result = this.getAnnotatedElement() or exists(StrutsActionsAnnotation actions | this = actions.getAnAction() | result = actions.getAnnotatedElement() diff --git a/java/ql/lib/semmle/code/java/frameworks/struts/StrutsConventions.qll b/java/ql/lib/semmle/code/java/frameworks/struts/StrutsConventions.qll index b6b62b72cef..35faa0e55ff 100644 --- a/java/ql/lib/semmle/code/java/frameworks/struts/StrutsConventions.qll +++ b/java/ql/lib/semmle/code/java/frameworks/struts/StrutsConventions.qll @@ -8,8 +8,8 @@ import semmle.code.xml.MavenPom */ library class Struts2ConventionDependency extends Dependency { Struts2ConventionDependency() { - getGroup().getValue() = "org.apache.struts" and - getArtifact().getValue() = "struts2-convention-plugin" + this.getGroup().getValue() = "org.apache.struts" and + this.getArtifact().getValue() = "struts2-convention-plugin" } } @@ -100,7 +100,7 @@ class Struts2ConventionActionClass extends Class { isStrutsConventionPluginUsed(this) and exists(string ancestorPackage | // Has an ancestor package on the whitelist - ancestorPackage = getPackage().getName().splitAt(".") and + ancestorPackage = this.getPackage().getName().splitAt(".") and ( ancestorPackage = "struts" or ancestorPackage = "struts2" or @@ -109,7 +109,7 @@ class Struts2ConventionActionClass extends Class { ) ) and ( - getName().matches("%" + getConventionSuffix(this)) or + this.getName().matches("%" + getConventionSuffix(this)) or this.getAnAncestor().hasQualifiedName("com.opensymphony.xwork2", "Action") ) } diff --git a/java/ql/lib/semmle/code/java/frameworks/struts/StrutsXML.qll b/java/ql/lib/semmle/code/java/frameworks/struts/StrutsXML.qll index 35340e890cf..8e69c5d9a83 100644 --- a/java/ql/lib/semmle/code/java/frameworks/struts/StrutsXML.qll +++ b/java/ql/lib/semmle/code/java/frameworks/struts/StrutsXML.qll @@ -31,18 +31,18 @@ abstract class StrutsXMLFile extends XMLFile { /** * Gets a transitively included file. */ - StrutsXMLFile getAnIncludedFile() { result = getADirectlyIncludedFile*() } + StrutsXMLFile getAnIncludedFile() { result = this.getADirectlyIncludedFile*() } /** * Gets a `` defined in this file, or an included file. */ - StrutsXMLConstant getAConstant() { result.getFile() = getAnIncludedFile() } + StrutsXMLConstant getAConstant() { result.getFile() = this.getAnIncludedFile() } /** * Gets the value of the constant with the given `name`. */ string getConstantValue(string name) { - exists(StrutsXMLConstant constant | constant = getAConstant() | + exists(StrutsXMLConstant constant | constant = this.getAConstant() | constant.getConstantName() = name and result = constant.getConstantValue() ) @@ -56,8 +56,8 @@ abstract class StrutsXMLFile extends XMLFile { */ class StrutsRootXMLFile extends StrutsXMLFile { StrutsRootXMLFile() { - getBaseName() = "struts.xml" or - getBaseName() = "struts-plugin.xml" + this.getBaseName() = "struts.xml" or + this.getBaseName() = "struts-plugin.xml" } } @@ -73,7 +73,7 @@ class StrutsIncludedXMLFile extends StrutsXMLFile { */ class StrutsFolder extends Folder { StrutsFolder() { - exists(Container c | c = getAChildContainer() | + exists(Container c | c = this.getAChildContainer() | c instanceof StrutsFolder or c instanceof StrutsXMLFile ) @@ -82,14 +82,14 @@ class StrutsFolder extends Folder { /** * Holds if this folder has a unique Struts root configuration file. */ - predicate isUnique() { count(getAStrutsRootFile()) = 1 } + predicate isUnique() { count(this.getAStrutsRootFile()) = 1 } /** * Gets a struts root configuration that applies to this folder. */ StrutsRootXMLFile getAStrutsRootFile() { - result = getAChildContainer() or - result = getAChildContainer().(StrutsFolder).getAStrutsRootFile() + result = this.getAChildContainer() or + result = this.getAChildContainer().(StrutsFolder).getAStrutsRootFile() } } @@ -102,7 +102,7 @@ class StrutsXMLElement extends XMLElement { /** * Gets the value for this element, with leading and trailing whitespace trimmed. */ - string getValue() { result = allCharactersString().trim() } + string getValue() { result = this.allCharactersString().trim() } } /** @@ -121,7 +121,7 @@ class StrutsXMLInclude extends StrutsXMLElement { * potentially be included. */ XMLFile getIncludedFile() { - exists(string file | file = getAttribute("file").getValue() | + exists(string file | file = this.getAttribute("file").getValue() | result.getAbsolutePath().matches("%" + escapeForMatch(file)) ) } @@ -157,10 +157,10 @@ class StrutsXMLAction extends StrutsXMLElement { * Gets the `Class` that is referenced by this Struts action. */ Class getActionClass() { - strutsWildcardMatching(result.getQualifiedName(), getAttribute("class").getValue()) + strutsWildcardMatching(result.getQualifiedName(), this.getAttribute("class").getValue()) } - string getMethodName() { result = getAttribute("method").getValue() } + string getMethodName() { result = this.getAttribute("method").getValue() } /** * Gets the `Method` which is referenced by this action. @@ -168,9 +168,9 @@ class StrutsXMLAction extends StrutsXMLElement { * If no method is specified in the attributes of this element, a method named `execute` is chosen. */ Method getActionMethod() { - getActionClass().inherits(result) and - if exists(getMethodName()) - then strutsWildcardMatching(result.getName(), getMethodName()) + this.getActionClass().inherits(result) and + if exists(this.getMethodName()) + then strutsWildcardMatching(result.getName(), this.getMethodName()) else result.hasName("execute") } } @@ -179,9 +179,9 @@ class StrutsXMLAction extends StrutsXMLElement { * A `` property, representing a configuration parameter to struts. */ class StrutsXMLConstant extends StrutsXMLElement { - StrutsXMLConstant() { getName() = "constant" } + StrutsXMLConstant() { this.getName() = "constant" } - string getConstantName() { result = getAttribute("name").getValue() } + string getConstantName() { result = this.getAttribute("name").getValue() } - string getConstantValue() { result = getAttribute("value").getValue() } + string getConstantValue() { result = this.getAttribute("value").getValue() } } diff --git a/java/ql/lib/semmle/code/java/metrics/MetricRefType.qll b/java/ql/lib/semmle/code/java/metrics/MetricRefType.qll index 79c65dd1bef..34525e64e77 100755 --- a/java/ql/lib/semmle/code/java/metrics/MetricRefType.qll +++ b/java/ql/lib/semmle/code/java/metrics/MetricRefType.qll @@ -137,7 +137,9 @@ class MetricRefType extends RefType, MetricElement { /** Holds if the specified callable should be included in the CK cohesion computation. */ predicate includeInLackOfCohesionCK(Callable c) { not c instanceof TestMethod and - exists(Field f | c.getDeclaringType() = this and c.accesses(f) and relevantFieldForCohesion(f)) + exists(Field f | + c.getDeclaringType() = this and c.accesses(f) and this.relevantFieldForCohesion(f) + ) } pragma[noopt] @@ -152,8 +154,8 @@ class MetricRefType extends RefType, MetricElement { /** Holds if a (non-ignored) callable reads a field relevant for cohesion. */ private predicate relevantCallableAndFieldCK(Callable m, Field f) { - includeInLackOfCohesionCK(m) and - relevantFieldForCohesion(f) and + this.includeInLackOfCohesionCK(m) and + this.relevantFieldForCohesion(f) and m.accesses(f) and m.getDeclaringType() = this } @@ -180,12 +182,12 @@ class MetricRefType extends RefType, MetricElement { */ float getLackOfCohesionCK() { exists(int callables, int linked, float n | - callables = count(Callable m | includeInLackOfCohesionCK(m)) and + callables = count(Callable m | this.includeInLackOfCohesionCK(m)) and linked = count(Callable m1, Callable m2 | exists(Field f | - relevantCallableAndFieldCK(m1, f) and - relevantCallableAndFieldCK(m2, f) and + this.relevantCallableAndFieldCK(m1, f) and + this.relevantCallableAndFieldCK(m2, f) and m1 != m2 ) ) and @@ -207,7 +209,7 @@ class MetricRefType extends RefType, MetricElement { int getADepth() { this.hasQualifiedName("java.lang", "Object") and result = 0 or - not cyclic() and result = this.getASupertype().(MetricRefType).getADepth() + 1 + not this.cyclic() and result = this.getASupertype().(MetricRefType).getADepth() + 1 } /** @@ -229,10 +231,10 @@ class MetricRefType extends RefType, MetricElement { int getADepth(RefType reference) { this = reference and result = 0 or - not cyclic() and result = this.getASupertype().(MetricRefType).getADepth(reference) + 1 + not this.cyclic() and result = this.getASupertype().(MetricRefType).getADepth(reference) + 1 } - private predicate cyclic() { getASupertype+() = this } + private predicate cyclic() { this.getASupertype+() = this } /** Gets the depth of inheritance metric relative to the specified reference type. */ int getInheritanceDepth(RefType reference) { result = max(this.getADepth(reference)) } @@ -264,11 +266,7 @@ class MetricRefType extends RefType, MetricElement { * for use with the specialization index metric. */ predicate ignoreOverride(Method c) { - c.hasStringSignature("equals(Object)") or - c.hasStringSignature("hashCode()") or - c.hasStringSignature("toString()") or - c.hasStringSignature("finalize()") or - c.hasStringSignature("clone()") + c.hasStringSignature(["equals(Object)", "hashCode()", "toString()", "finalize()", "clone()"]) } /** Gets a method that overrides a non-abstract method in a super type. */ diff --git a/java/ql/lib/semmle/code/java/security/AndroidSensitiveCommunicationQuery.qll b/java/ql/lib/semmle/code/java/security/AndroidSensitiveCommunicationQuery.qll new file mode 100644 index 00000000000..a03f74d5044 --- /dev/null +++ b/java/ql/lib/semmle/code/java/security/AndroidSensitiveCommunicationQuery.qll @@ -0,0 +1,179 @@ +/** Provides definitions to reason about Android Sensitive Communication queries */ + +import java +import semmle.code.java.dataflow.TaintTracking +import semmle.code.java.frameworks.android.Intent +import semmle.code.java.security.SensitiveActions + +/** + * Gets regular expression for matching names of Android variables that indicate the value being held contains sensitive information. + */ +private string getAndroidSensitiveInfoRegex() { result = "(?i).*(email|phone|ticket).*" } + +/** Finds variables that hold sensitive information judging by their names. */ +private class SensitiveInfoExpr extends Expr { + SensitiveInfoExpr() { + exists(Variable v | this = v.getAnAccess() | + v.getName().regexpMatch([getCommonSensitiveInfoRegex(), getAndroidSensitiveInfoRegex()]) + ) + } +} + +private predicate maybeNullArg(Expr ex) { + exists(DataFlow::Node src, DataFlow::Node sink, MethodAccess ma | + ex = ma.getAnArgument() and + sink.asExpr() = ex and + src.asExpr() instanceof NullLiteral + | + DataFlow::localFlow(src, sink) + ) +} + +private predicate maybeEmptyArrayArg(Expr ex) { + exists(DataFlow::Node src, DataFlow::Node sink, MethodAccess ma | + ex = ma.getAnArgument() and + sink.asExpr() = ex and + src.asExpr().(ArrayCreationExpr).getFirstDimensionSize() = 0 + | + DataFlow::localFlow(src, sink) + ) +} + +/** + * Holds if a `sendBroadcast` call doesn't specify receiver permission. + */ +private predicate isSensitiveBroadcastSink(DataFlow::Node sendBroadcastCallArg) { + exists(MethodAccess ma, string name | ma.getMethod().hasName(name) | + ma.getMethod().getDeclaringType().getASourceSupertype*() instanceof TypeContext and + sendBroadcastCallArg.asExpr() = ma.getAnArgument() and + ( + name = "sendBroadcast" and + ( + // sendBroadcast(Intent intent) + ma.getNumArgument() = 1 + or + // sendBroadcast(Intent intent, String receiverPermission) + maybeNullArg(ma.getArgument(1)) + ) + or + name = "sendBroadcastAsUser" and + ( + // sendBroadcastAsUser(Intent intent, UserHandle user) + ma.getNumArgument() = 2 + or + // sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission) + maybeNullArg(ma.getArgument(2)) + ) + or + // sendBroadcastWithMultiplePermissions(Intent intent, String[] receiverPermissions) + name = "sendBroadcastWithMultiplePermissions" and + maybeEmptyArrayArg(ma.getArgument(1)) + or + // Method calls of `sendOrderedBroadcast` whose second argument is always `receiverPermission` + name = "sendOrderedBroadcast" and + ( + // sendOrderedBroadcast(Intent intent, String receiverPermission) + // sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) + maybeNullArg(ma.getArgument(1)) and + ma.getNumArgument() = [2, 7] + or + // sendOrderedBroadcast(Intent intent, String receiverPermission, String receiverAppOp, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) + maybeNullArg(ma.getArgument(1)) and + maybeNullArg(ma.getArgument(2)) and + ma.getNumArgument() = 8 + ) + or + // sendOrderedBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) + name = "sendOrderedBroadcastAsUser" and + maybeNullArg(ma.getArgument(2)) + or + // sendStickyBroadcast(Intent intent) + // sendStickyBroadcast(Intent intent, Bundle options) + // sendStickyBroadcastAsUser(Intent intent, UserHandle user) + // sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) + // sendStickyOrderedBroadcastAsUser(Intent intent, UserHandle user, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) + name = + [ + "sendStickyBroadcast", "sendStickyBroadcastAsUser", "sendStickyOrderedBroadcast", + "sendStickyOrderedBroadcastAsUser" + ] + ) + ) +} + +/** + * Holds if `arg` is an argument in a use of a `startActivity` or `startService` method that sends an Intent to another application. + */ +private predicate isStartActivityOrServiceSink(DataFlow::Node arg) { + exists(MethodAccess ma, string name | ma.getMethod().hasName(name) | + arg.asExpr() = ma.getArgument(0) and + ma.getMethod().getDeclaringType().getASourceSupertype*() instanceof TypeContext and + // startActivity(Intent intent) + // startActivity(Intent intent, Bundle options) + // startActivities(Intent[] intents) + // startActivities(Intent[] intents, Bundle options) + // startService(Intent service) + // startForegroundService(Intent service) + // bindService (Intent service, int flags, Executor executor, ServiceConnection conn) + // bindService (Intent service, Executor executor, ServiceConnection conn) + name = + ["startActivity", "startActivities", "startService", "startForegroundService", "bindService"] + ) +} + +private predicate isCleanIntent(Expr intent) { + intent.getType() instanceof TypeIntent and + ( + exists(MethodAccess setRecieverMa | + setRecieverMa.getQualifier() = intent and + setRecieverMa.getMethod().hasName(["setPackage", "setClass", "setClassName", "setComponent"]) + ) + or + // Handle the cases where the PackageContext and Class are set at construction time + // Intent(Context packageContext, Class cls) + // Intent(String action, Uri uri, Context packageContext, Class cls) + exists(ConstructorCall cc | cc = intent | + cc.getConstructedType() instanceof TypeIntent and + cc.getNumArgument() > 1 and + ( + cc.getArgument(0).getType() instanceof TypeContext and + not maybeNullArg(cc.getArgument(1)) + or + cc.getArgument(2).getType() instanceof TypeContext and + not maybeNullArg(cc.getArgument(3)) + ) + ) + ) +} + +/** + * Taint configuration tracking flow from variables containing sensitive information to broadcast Intents. + */ +class SensitiveCommunicationConfig extends TaintTracking::Configuration { + SensitiveCommunicationConfig() { this = "Sensitive Communication Configuration" } + + override predicate isSource(DataFlow::Node source) { + source.asExpr() instanceof SensitiveInfoExpr + } + + override predicate isSink(DataFlow::Node sink) { + isSensitiveBroadcastSink(sink) + or + isStartActivityOrServiceSink(sink) + } + + /** + * Holds if broadcast doesn't specify receiving package name of the 3rd party app + */ + override predicate isSanitizer(DataFlow::Node node) { + exists(DataFlow::Node intent | isCleanIntent(intent.asExpr()) | + DataFlow::localFlow(intent, node) + ) + } + + override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) { + super.allowImplicitRead(node, c) + or + this.isSink(node) + } +} diff --git a/java/ql/lib/semmle/code/java/security/CommandArguments.qll b/java/ql/lib/semmle/code/java/security/CommandArguments.qll index c0934e49411..8be5536f25c 100644 --- a/java/ql/lib/semmle/code/java/security/CommandArguments.qll +++ b/java/ql/lib/semmle/code/java/security/CommandArguments.qll @@ -69,10 +69,10 @@ private class CommandArgumentList extends SsaExplicitUpdate { /** Gets a use of the variable for which the list could be empty. */ private RValue getAUseBeforeFirstAdd() { - result = getAFirstUse() + result = this.getAFirstUse() or exists(RValue mid | - mid = getAUseBeforeFirstAdd() and + mid = this.getAUseBeforeFirstAdd() and adjacentUseUse(mid, result) and not exists(MethodAccess ma | mid = ma.getQualifier() and @@ -85,25 +85,25 @@ private class CommandArgumentList extends SsaExplicitUpdate { * Gets an addition to this list, i.e. a call to an `add` or `addAll` method. */ MethodAccess getAnAdd() { - result.getQualifier() = getAUse() and + result.getQualifier() = this.getAUse() and result.getMethod().getName().matches("add%") } /** Gets an addition to this list which could be its first element. */ MethodAccess getAFirstAdd() { - result = getAnAdd() and - result.getQualifier() = getAUseBeforeFirstAdd() + result = this.getAnAdd() and + result.getQualifier() = this.getAUseBeforeFirstAdd() } /** Gets an addition to this list which is not the first element. */ MethodAccess getASubsequentAdd() { - result = getAnAdd() and - not result = getAFirstAdd() + result = this.getAnAdd() and + not result = this.getAFirstAdd() } /** Holds if the first element of this list is a shell command. */ predicate isShell() { - exists(MethodAccess ma | ma = getAFirstAdd() and isShell(ma.getArgument(0))) + exists(MethodAccess ma | ma = this.getAFirstAdd() and isShell(ma.getArgument(0))) } } @@ -122,7 +122,7 @@ private predicate arrayLValue(ArrayAccess acc) { exists(Assignment a | a.getDest private class CommandArgumentArray extends SsaExplicitUpdate { CommandArgumentArray() { this.getSourceVariable().getType() instanceof ArrayOfStringType and - forall(ArrayAccess a | a.getArray() = getAUse() and arrayLValue(a) | + forall(ArrayAccess a | a.getArray() = this.getAUse() and arrayLValue(a) | a.getIndexExpr() instanceof CompileTimeConstantExpr ) } @@ -139,7 +139,7 @@ private class CommandArgumentArray extends SsaExplicitUpdate { } /** Gets an expression that is written to the given index of this array. */ - Expr getAWrite(int index) { result = getAWrite(index, _) } + Expr getAWrite(int index) { result = this.getAWrite(index, _) } } /** @@ -147,20 +147,20 @@ private class CommandArgumentArray extends SsaExplicitUpdate { */ private class CommandArgArrayImmutableFirst extends CommandArgumentArray { CommandArgArrayImmutableFirst() { - (exists(getAWrite(0)) or exists(firstElementOf(this.getDefiningExpr()))) and + (exists(this.getAWrite(0)) or exists(firstElementOf(this.getDefiningExpr()))) and forall(RValue use | exists(this.getAWrite(0, use)) | use = this.getAFirstUse()) } /** Gets the first element of this array. */ Expr getFirstElement() { - result = getAWrite(0) + result = this.getAWrite(0) or not exists(getAWrite(0)) and - result = firstElementOf(getDefiningExpr()) + result = firstElementOf(this.getDefiningExpr()) } /** Holds if the first element of this array is a shell command. */ - predicate isShell() { isShell(getFirstElement()) } + predicate isShell() { isShell(this.getFirstElement()) } } /** Gets the first element of an imutable array of strings */ diff --git a/java/ql/lib/semmle/code/java/security/Encryption.qll b/java/ql/lib/semmle/code/java/security/Encryption.qll index 3e65375d91f..8f4cef23ee6 100644 --- a/java/ql/lib/semmle/code/java/security/Encryption.qll +++ b/java/ql/lib/semmle/code/java/security/Encryption.qll @@ -18,7 +18,7 @@ class X509TrustManager extends RefType { } class HttpsURLConnection extends RefType { - HttpsURLConnection() { hasQualifiedName("javax.net.ssl", "HttpsURLConnection") } + HttpsURLConnection() { this.hasQualifiedName("javax.net.ssl", "HttpsURLConnection") } } class SSLSocketFactory extends RefType { @@ -26,16 +26,16 @@ class SSLSocketFactory extends RefType { } class SSLContext extends RefType { - SSLContext() { hasQualifiedName("javax.net.ssl", "SSLContext") } + SSLContext() { this.hasQualifiedName("javax.net.ssl", "SSLContext") } } /** The `javax.net.ssl.SSLSession` class. */ class SSLSession extends RefType { - SSLSession() { hasQualifiedName("javax.net.ssl", "SSLSession") } + SSLSession() { this.hasQualifiedName("javax.net.ssl", "SSLSession") } } class HostnameVerifier extends RefType { - HostnameVerifier() { hasQualifiedName("javax.net.ssl", "HostnameVerifier") } + HostnameVerifier() { this.hasQualifiedName("javax.net.ssl", "HostnameVerifier") } } /** The Java class `javax.crypto.KeyGenerator`. */ @@ -51,10 +51,10 @@ class KeyPairGenerator extends RefType { /** The `verify` method of the class `javax.net.ssl.HostnameVerifier`. */ class HostnameVerifierVerify extends Method { HostnameVerifierVerify() { - hasName("verify") and - getDeclaringType().getASupertype*() instanceof HostnameVerifier and - getParameterType(0) instanceof TypeString and - getParameterType(1) instanceof SSLSession + this.hasName("verify") and + this.getDeclaringType().getASupertype*() instanceof HostnameVerifier and + this.getParameterType(0) instanceof TypeString and + this.getParameterType(1) instanceof SSLSession } } @@ -67,37 +67,37 @@ class TrustManagerCheckMethod extends Method { class CreateSocket extends Method { CreateSocket() { - hasName("createSocket") and - getDeclaringType() instanceof SSLSocketFactory + this.hasName("createSocket") and + this.getDeclaringType() instanceof SSLSocketFactory } } class GetSocketFactory extends Method { GetSocketFactory() { - hasName("getSocketFactory") and - getDeclaringType() instanceof SSLContext + this.hasName("getSocketFactory") and + this.getDeclaringType() instanceof SSLContext } } class SetConnectionFactoryMethod extends Method { SetConnectionFactoryMethod() { - hasName("setSSLSocketFactory") and - getDeclaringType().getASupertype*() instanceof HttpsURLConnection + this.hasName("setSSLSocketFactory") and + this.getDeclaringType().getASupertype*() instanceof HttpsURLConnection } } class SetHostnameVerifierMethod extends Method { SetHostnameVerifierMethod() { - hasName("setHostnameVerifier") and - getDeclaringType().getASupertype*() instanceof HttpsURLConnection + this.hasName("setHostnameVerifier") and + this.getDeclaringType().getASupertype*() instanceof HttpsURLConnection } } /** The `setDefaultHostnameVerifier` method of the class `javax.net.ssl.HttpsURLConnection`. */ class SetDefaultHostnameVerifierMethod extends Method { SetDefaultHostnameVerifierMethod() { - hasName("setDefaultHostnameVerifier") and - getDeclaringType().getASupertype*() instanceof HttpsURLConnection + this.hasName("setDefaultHostnameVerifier") and + this.getDeclaringType().getASupertype*() instanceof HttpsURLConnection } } diff --git a/java/ql/lib/semmle/code/java/security/ExternalAPIs.qll b/java/ql/lib/semmle/code/java/security/ExternalAPIs.qll index f2675645a6a..3b264bd0283 100644 --- a/java/ql/lib/semmle/code/java/security/ExternalAPIs.qll +++ b/java/ql/lib/semmle/code/java/security/ExternalAPIs.qll @@ -17,38 +17,38 @@ private class DefaultSafeExternalAPIMethod extends SafeExternalAPIMethod { DefaultSafeExternalAPIMethod() { this instanceof EqualsMethod or - getName().regexpMatch("size|length|compareTo|getClass|lastIndexOf") + this.getName().regexpMatch("size|length|compareTo|getClass|lastIndexOf") or this.getDeclaringType().hasQualifiedName("org.apache.commons.lang3", "Validate") or - getQualifiedName() = "Objects.equals" + this.getQualifiedName() = "Objects.equals" or - getDeclaringType() instanceof TypeString and getName() = "equals" + this.getDeclaringType() instanceof TypeString and this.getName() = "equals" or - getDeclaringType().hasQualifiedName("com.google.common.base", "Preconditions") + this.getDeclaringType().hasQualifiedName("com.google.common.base", "Preconditions") or - getDeclaringType().getPackage().getName().matches("org.junit%") + this.getDeclaringType().getPackage().getName().matches("org.junit%") or - getDeclaringType().hasQualifiedName("com.google.common.base", "Strings") and - getName() = "isNullOrEmpty" + this.getDeclaringType().hasQualifiedName("com.google.common.base", "Strings") and + this.getName() = "isNullOrEmpty" or - getDeclaringType().hasQualifiedName("org.apache.commons.lang3", "StringUtils") and - getName() = "isNotEmpty" + this.getDeclaringType().hasQualifiedName("org.apache.commons.lang3", "StringUtils") and + this.getName() = "isNotEmpty" or - getDeclaringType().hasQualifiedName("java.lang", "Character") and - getName() = "isDigit" + this.getDeclaringType().hasQualifiedName("java.lang", "Character") and + this.getName() = "isDigit" or - getDeclaringType().hasQualifiedName("java.lang", "String") and - getName().regexpMatch("equalsIgnoreCase|regionMatches") + this.getDeclaringType().hasQualifiedName("java.lang", "String") and + this.getName().regexpMatch("equalsIgnoreCase|regionMatches") or - getDeclaringType().hasQualifiedName("java.lang", "Boolean") and - getName() = "parseBoolean" + this.getDeclaringType().hasQualifiedName("java.lang", "Boolean") and + this.getName() = "parseBoolean" or - getDeclaringType().hasQualifiedName("org.apache.commons.io", "IOUtils") and - getName() = "closeQuietly" + this.getDeclaringType().hasQualifiedName("org.apache.commons.io", "IOUtils") and + this.getName() = "closeQuietly" or - getDeclaringType().hasQualifiedName("org.springframework.util", "StringUtils") and - getName().regexpMatch("hasText|isEmpty") + this.getDeclaringType().hasQualifiedName("org.springframework.util", "StringUtils") and + this.getName().regexpMatch("hasText|isEmpty") } } @@ -90,7 +90,8 @@ class ExternalAPIDataNode extends DataFlow::Node { /** Gets the description of the method being called. */ string getMethodDescription() { - result = getMethod().getDeclaringType().getPackage() + "." + getMethod().getQualifiedName() + result = + this.getMethod().getDeclaringType().getPackage() + "." + this.getMethod().getQualifiedName() } } @@ -130,7 +131,7 @@ class ExternalAPIUsedWithUntrustedData extends TExternalAPI { /** Gets the number of untrusted sources used with this external API. */ int getNumberOfUntrustedSources() { - result = count(getUntrustedDataNode().getAnUntrustedSource()) + result = count(this.getUntrustedDataNode().getAnUntrustedSource()) } /** Gets a textual representation of this element. */ diff --git a/java/ql/lib/semmle/code/java/security/InformationLeak.qll b/java/ql/lib/semmle/code/java/security/InformationLeak.qll index f68ddd5b121..3ea674521a0 100644 --- a/java/ql/lib/semmle/code/java/security/InformationLeak.qll +++ b/java/ql/lib/semmle/code/java/security/InformationLeak.qll @@ -9,9 +9,7 @@ import semmle.code.java.security.XSS private class DefaultInformationLeakSinkModel extends SinkModelCsv { override predicate row(string row) { row = - [ - "javax.servlet.http;HttpServletResponse;false;sendError;(int,String);;Argument[1];information-leak" - ] + "javax.servlet.http;HttpServletResponse;false;sendError;(int,String);;Argument[1];information-leak" } } diff --git a/java/ql/lib/semmle/code/java/security/JexlInjectionQuery.qll b/java/ql/lib/semmle/code/java/security/JexlInjectionQuery.qll index bfb77715569..9e70be5c12f 100644 --- a/java/ql/lib/semmle/code/java/security/JexlInjectionQuery.qll +++ b/java/ql/lib/semmle/code/java/security/JexlInjectionQuery.qll @@ -168,7 +168,9 @@ private predicate createJexlEngineStep(DataFlow::Node fromNode, DataFlow::Node t * A method that creates a JEXL script. */ private class CreateJexlScriptMethod extends Method { - CreateJexlScriptMethod() { getDeclaringType() instanceof JexlEngine and hasName("createScript") } + CreateJexlScriptMethod() { + this.getDeclaringType() instanceof JexlEngine and this.hasName("createScript") + } } /** @@ -176,8 +178,11 @@ private class CreateJexlScriptMethod extends Method { */ private class CreateJexlTemplateMethod extends Method { CreateJexlTemplateMethod() { - (getDeclaringType() instanceof JxltEngine or getDeclaringType() instanceof UnifiedJexl) and - hasName("createTemplate") + ( + this.getDeclaringType() instanceof JxltEngine or + this.getDeclaringType() instanceof UnifiedJexl + ) and + this.hasName("createTemplate") } } @@ -186,40 +191,42 @@ private class CreateJexlTemplateMethod extends Method { */ private class CreateJexlExpressionMethod extends Method { CreateJexlExpressionMethod() { - (getDeclaringType() instanceof JexlEngine or getDeclaringType() instanceof JxltEngine) and - hasName("createExpression") + (this.getDeclaringType() instanceof JexlEngine or this.getDeclaringType() instanceof JxltEngine) and + this.hasName("createExpression") or - getDeclaringType() instanceof UnifiedJexl and hasName("parse") + this.getDeclaringType() instanceof UnifiedJexl and this.hasName("parse") } } private class JexlRefType extends RefType { - JexlRefType() { getPackage().hasName(["org.apache.commons.jexl2", "org.apache.commons.jexl3"]) } + JexlRefType() { + this.getPackage().hasName(["org.apache.commons.jexl2", "org.apache.commons.jexl3"]) + } } private class JexlBuilder extends JexlRefType { - JexlBuilder() { hasName("JexlBuilder") } + JexlBuilder() { this.hasName("JexlBuilder") } } private class JexlEngine extends JexlRefType { - JexlEngine() { hasName("JexlEngine") } + JexlEngine() { this.hasName("JexlEngine") } } private class JxltEngine extends JexlRefType { - JxltEngine() { hasName("JxltEngine") } + JxltEngine() { this.hasName("JxltEngine") } } private class UnifiedJexl extends JexlRefType { - UnifiedJexl() { hasName("UnifiedJEXL") } + UnifiedJexl() { this.hasName("UnifiedJEXL") } } private class JexlUberspect extends Interface { JexlUberspect() { - hasQualifiedName("org.apache.commons.jexl2.introspection", "Uberspect") or - hasQualifiedName("org.apache.commons.jexl3.introspection", "JexlUberspect") + this.hasQualifiedName("org.apache.commons.jexl2.introspection", "Uberspect") or + this.hasQualifiedName("org.apache.commons.jexl3.introspection", "JexlUberspect") } } private class Reader extends RefType { - Reader() { hasQualifiedName("java.io", "Reader") } + Reader() { this.hasQualifiedName("java.io", "Reader") } } diff --git a/java/ql/lib/semmle/code/java/security/Mail.qll b/java/ql/lib/semmle/code/java/security/Mail.qll new file mode 100644 index 00000000000..e126ca57dff --- /dev/null +++ b/java/ql/lib/semmle/code/java/security/Mail.qll @@ -0,0 +1,74 @@ +/** Provides classes and predicates to reason about email vulnerabilities. */ + +import java +import semmle.code.java.frameworks.Mail +private import semmle.code.java.frameworks.Properties + +/** + * The insecure way to set Java properties in mail sessions. + * 1. Set the `mail.smtp.auth` property to provide the SMTP Transport with a username and password when connecting to the SMTP server or + * set the `mail.smtp.ssl.socketFactory`/`mail.smtp.ssl.socketFactory.class` property to create an SMTP SSL socket. + * 2. No `mail.smtp.ssl.checkserveridentity` property is enabled. + */ +predicate isInsecureMailPropertyConfig(Variable properties) { + exists(MethodAccess ma | + ma.getMethod() instanceof SetPropertyMethod and + ma.getQualifier() = properties.getAnAccess() + | + getStringValue(ma.getArgument(0)).matches("%.auth%") and //mail.smtp.auth + getStringValue(ma.getArgument(1)) = "true" + or + getStringValue(ma.getArgument(0)).matches("%.socketFactory%") //mail.smtp.socketFactory or mail.smtp.socketFactory.class + ) and + not exists(MethodAccess ma | + ma.getMethod() instanceof SetPropertyMethod and + ma.getQualifier() = properties.getAnAccess() + | + getStringValue(ma.getArgument(0)).matches("%.ssl.checkserveridentity%") and //mail.smtp.ssl.checkserveridentity + getStringValue(ma.getArgument(1)) = "true" + ) +} + +/** + * Holds if `ma` enables TLS/SSL with Apache Email. + */ +predicate enablesEmailSsl(MethodAccess ma) { + ma.getMethod().hasName(["setSSLOnConnect", "setStartTLSRequired"]) and + ma.getMethod().getDeclaringType() instanceof ApacheEmail and + ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true +} + +/** + * Holds if a SSL certificate check is enabled on an access of `apacheEmail` with Apache Email. + */ +predicate hasSslCertificateCheck(Variable apacheEmail) { + exists(MethodAccess ma | + ma.getQualifier() = apacheEmail.getAnAccess() and + ma.getMethod().hasName("setSSLCheckServerIdentity") and + ma.getMethod().getDeclaringType() instanceof ApacheEmail and + ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true + ) +} + +/** + * Returns the string value of `expr` if it is a `CompileTimeConstantExpr`, + * or the string value of its operands if it is an `AddExpr`. + */ +private string getStringValue(Expr expr) { + result = expr.(CompileTimeConstantExpr).getStringValue() + or + result = getStringValue(expr.(AddExpr).getAnOperand()) +} + +/** + * A method to set Java properties, either using the `Properties` class + * or the `Dictionary` class. + */ +private class SetPropertyMethod extends Method { + SetPropertyMethod() { + this instanceof PropertiesSetPropertyMethod + or + this.hasName("put") and + this.getDeclaringType().getASourceSupertype*().hasQualifiedName("java.util", "Dictionary") + } +} diff --git a/java/ql/lib/semmle/code/java/security/MvelInjection.qll b/java/ql/lib/semmle/code/java/security/MvelInjection.qll index 984641fbd18..a75f582b72a 100644 --- a/java/ql/lib/semmle/code/java/security/MvelInjection.qll +++ b/java/ql/lib/semmle/code/java/security/MvelInjection.qll @@ -183,8 +183,8 @@ private predicate templateCompileStep(DataFlow::Node node1, DataFlow::Node node2 */ private class MvelScriptEngineCompilationMethod extends Method { MvelScriptEngineCompilationMethod() { - getDeclaringType() instanceof MvelScriptEngine and - hasName(["compile", "compiledScript"]) + this.getDeclaringType() instanceof MvelScriptEngine and + this.hasName(["compile", "compiledScript"]) } } @@ -193,8 +193,8 @@ private class MvelScriptEngineCompilationMethod extends Method { */ private class TemplateCompilerCompileMethod extends Method { TemplateCompilerCompileMethod() { - getDeclaringType() instanceof TemplateCompiler and - hasName("compile") + this.getDeclaringType() instanceof TemplateCompiler and + this.hasName("compile") } } @@ -203,31 +203,31 @@ private class TemplateCompilerCompileMethod extends Method { */ private class TemplateCompilerCompileTemplateMethod extends Method { TemplateCompilerCompileTemplateMethod() { - getDeclaringType() instanceof TemplateCompiler and - hasName("compileTemplate") + this.getDeclaringType() instanceof TemplateCompiler and + this.hasName("compileTemplate") } } private class MVEL extends RefType { - MVEL() { hasQualifiedName("org.mvel2", "MVEL") } + MVEL() { this.hasQualifiedName("org.mvel2", "MVEL") } } private class ExpressionCompiler extends RefType { - ExpressionCompiler() { hasQualifiedName("org.mvel2.compiler", "ExpressionCompiler") } + ExpressionCompiler() { this.hasQualifiedName("org.mvel2.compiler", "ExpressionCompiler") } } private class CompiledAccExpression extends RefType { - CompiledAccExpression() { hasQualifiedName("org.mvel2.compiler", "CompiledAccExpression") } + CompiledAccExpression() { this.hasQualifiedName("org.mvel2.compiler", "CompiledAccExpression") } } private class MvelScriptEngine extends RefType { - MvelScriptEngine() { hasQualifiedName("org.mvel2.jsr223", "MvelScriptEngine") } + MvelScriptEngine() { this.hasQualifiedName("org.mvel2.jsr223", "MvelScriptEngine") } } private class MvelCompiledScript extends RefType { - MvelCompiledScript() { hasQualifiedName("org.mvel2.jsr223", "MvelCompiledScript") } + MvelCompiledScript() { this.hasQualifiedName("org.mvel2.jsr223", "MvelCompiledScript") } } private class TemplateCompiler extends RefType { - TemplateCompiler() { hasQualifiedName("org.mvel2.templates", "TemplateCompiler") } + TemplateCompiler() { this.hasQualifiedName("org.mvel2.templates", "TemplateCompiler") } } diff --git a/java/ql/lib/semmle/code/java/security/RelativePaths.qll b/java/ql/lib/semmle/code/java/security/RelativePaths.qll index 34ffcc5bb04..22bcf7b7d87 100644 --- a/java/ql/lib/semmle/code/java/security/RelativePaths.qll +++ b/java/ql/lib/semmle/code/java/security/RelativePaths.qll @@ -7,10 +7,7 @@ import java predicate relativePath(Element tree, string command) { exists(StringLiteral lit, string text | tree = lit and text = lit.getRepresentedString() | text != "" and - ( - text.regexpMatch("[^/\\\\ \t]*") or - text.regexpMatch("[^/\\\\ \t]*[ \t].*") - ) and + text.regexpMatch(["[^/\\\\ \t]*", "[^/\\\\ \t]*[ \t].*"]) and command = text.replaceAll("\t", " ").splitAt(" ", 0).replaceAll("\"", "") ) or @@ -35,39 +32,11 @@ predicate arrayStartingWithRelative(Element tree, string command) { * because they do not correspond to files in the filesystem. */ predicate shellBuiltin(string command) { - command = "." or - command = "[" or - command = "[[" or - command = "alias" or - command = "builtin" or - command = "case" or - command = "command" or - command = "compgen" or - command = "complete" or - command = "compopt" or - command = "echo" or - command = "eval" or - command = "exec" or - command = "false" or - command = "fc" or - command = "for" or - command = "getopts" or - command = "help" or - command = "history" or - command = "if" or - command = "kill" or - command = "printf" or - command = "pwd" or - command = "select" or - command = "source" or - command = "test" or - command = "time" or - command = "times" or - command = "trap" or - command = "true" or - command = "type" or - command = "typeset" or - command = "ulimit" or - command = "until" or - command = "while" + command = + [ + ".", "[", "[[", "alias", "builtin", "case", "command", "compgen", "complete", "compopt", + "echo", "eval", "exec", "false", "fc", "for", "getopts", "help", "history", "if", "kill", + "printf", "pwd", "select", "source", "test", "time", "times", "trap", "true", "type", + "typeset", "ulimit", "until", "while" + ] } diff --git a/java/ql/lib/semmle/code/java/security/RequestForgery.qll b/java/ql/lib/semmle/code/java/security/RequestForgery.qll index b23d0a855c0..b7d3e4a8a77 100644 --- a/java/ql/lib/semmle/code/java/security/RequestForgery.qll +++ b/java/ql/lib/semmle/code/java/security/RequestForgery.qll @@ -7,8 +7,7 @@ import semmle.code.java.frameworks.spring.Spring import semmle.code.java.frameworks.JaxWS import semmle.code.java.frameworks.javase.Http import semmle.code.java.dataflow.DataFlow -import semmle.code.java.dataflow.TaintTracking -private import semmle.code.java.StringFormat +private import semmle.code.java.dataflow.StringPrefixes private import semmle.code.java.dataflow.ExternalFlow /** @@ -52,10 +51,10 @@ private class PrimitiveSanitizer extends RequestForgerySanitizer { } } -private class HostnameSanitizingConstantPrefix extends CompileTimeConstantExpr { +private class HostnameSanitizingPrefix extends InterestingPrefix { int offset; - HostnameSanitizingConstantPrefix() { + HostnameSanitizingPrefix() { // Matches strings that look like when prepended to untrusted input, they will restrict // the host or entity addressed: for example, anything containing `?` or `#`, or a slash that // doesn't appear to be a protocol specifier (e.g. `http://` is not sanitizing), or specifically @@ -66,143 +65,7 @@ private class HostnameSanitizingConstantPrefix extends CompileTimeConstantExpr { ) } - /** - * Gets the offset in this constant string where a sanitizing substring begins. - */ - int getOffset() { result = offset } -} - -private Expr getAHostnameSanitizingPrefix() { - result instanceof HostnameSanitizingConstantPrefix - or - result.(AddExpr).getAnOperand() = getAHostnameSanitizingPrefix() -} - -private class StringBuilderAppend extends MethodAccess { - StringBuilderAppend() { - this.getMethod().getDeclaringType() instanceof StringBuildingType and - this.getMethod().hasName("append") - } -} - -private class StringBuilderConstructorOrAppend extends Call { - StringBuilderConstructorOrAppend() { - this instanceof StringBuilderAppend or - this.(ClassInstanceExpr).getConstructedType() instanceof StringBuildingType - } -} - -private Expr getQualifier(Expr e) { result = e.(MethodAccess).getQualifier() } - -/** - * An extension of `StringBuilderVar` that also accounts for strings appended in StringBuilder/Buffer's constructor - * and in `append` calls chained onto the constructor call. - * - * The original `StringBuilderVar` doesn't care about these because it is designed to model taint, and - * in taint rules terms these are not needed, as the connection between construction, appends and the - * eventual `toString` is more obvious. - */ -private class StringBuilderVarExt extends StringBuilderVar { - /** - * Returns a first assignment after this StringBuilderVar is first assigned. - * - * For example, for `StringBuilder sbv = new StringBuilder("1").append("2"); sbv.append("3").append("4");` - * this returns the append of `"3"`. - */ - private StringBuilderAppend getAFirstAppendAfterAssignment() { - result = this.getAnAppend() and not result = this.getNextAppend(_) - } - - /** - * Gets the next `append` after `prev`, where `prev` is, perhaps after some more `append` or other - * chained calls, assigned to this `StringBuilderVar`. - */ - private StringBuilderAppend getNextAssignmentChainedAppend(StringBuilderConstructorOrAppend prev) { - getQualifier*(result) = this.getAnAssignedValue() and - result.getQualifier() = prev - } - - /** - * Get a constructor call or `append` call that contributes a string to this string builder. - */ - StringBuilderConstructorOrAppend getAConstructorOrAppend() { - exists(this.getNextAssignmentChainedAppend(result)) or - result = this.getAnAssignedValue() or - result = this.getAnAppend() - } - - /** - * Like `StringBuilderVar.getNextAppend`, except including appends and constructors directly - * assigned to this `StringBuilderVar`. - */ - private StringBuilderAppend getNextAppendIncludingAssignmentChains( - StringBuilderConstructorOrAppend prev - ) { - result = getNextAssignmentChainedAppend(prev) - or - prev = this.getAnAssignedValue() and - result = this.getAFirstAppendAfterAssignment() - or - result = this.getNextAppend(prev) - } - - /** - * Implements `StringBuilderVarExt.getNextAppendIncludingAssignmentChains+(prev)`. - */ - pragma[nomagic] - StringBuilderAppend getSubsequentAppendIncludingAssignmentChains( - StringBuilderConstructorOrAppend prev - ) { - result = this.getNextAppendIncludingAssignmentChains(prev) or - result = - this.getSubsequentAppendIncludingAssignmentChains(this.getNextAppendIncludingAssignmentChains(prev)) - } -} - -/** - * An expression that is sanitized because it is concatenated onto a string that looks like - * a hostname or a URL separator, preventing the appended string from arbitrarily controlling - * the addressed server. - */ -private class HostnameSanitizedExpr extends Expr { - HostnameSanitizedExpr() { - // Sanitize expressions that come after a sanitizing prefix in a tree of string additions: - this = - any(AddExpr add | add.getLeftOperand() = getAHostnameSanitizingPrefix()).getRightOperand() - or - // Sanitize expressions that come after a sanitizing prefix in a sequence of StringBuilder operations: - exists( - StringBuilderConstructorOrAppend appendSanitizingConstant, - StringBuilderAppend subsequentAppend, StringBuilderVarExt v - | - appendSanitizingConstant = v.getAConstructorOrAppend() and - appendSanitizingConstant.getArgument(0) = getAHostnameSanitizingPrefix() and - v.getSubsequentAppendIncludingAssignmentChains(appendSanitizingConstant) = subsequentAppend and - this = subsequentAppend.getArgument(0) - ) - or - // Sanitize expressions that come after a sanitizing prefix in the args to a format call: - exists( - FormattingCall formatCall, FormatString formatString, HostnameSanitizingConstantPrefix prefix, - int sanitizedFromOffset, int laterOffset, int sanitizedArg - | - formatString = unique(FormatString fs | fs = formatCall.getAFormatString()) and - ( - // A sanitizing argument comes before this: - exists(int argIdx | - formatCall.getArgumentToBeFormatted(argIdx) = prefix and - sanitizedFromOffset = formatString.getAnArgUsageOffset(argIdx) - ) - or - // The format string itself sanitizes subsequent arguments: - formatString = prefix.getStringValue() and - sanitizedFromOffset = prefix.getOffset() - ) and - laterOffset > sanitizedFromOffset and - laterOffset = formatString.getAnArgUsageOffset(sanitizedArg) and - this = formatCall.getArgumentToBeFormatted(sanitizedArg) - ) - } + override int getOffset() { result = offset } } /** @@ -210,5 +73,5 @@ private class HostnameSanitizedExpr extends Expr { * host of a URL. */ private class HostnameSantizer extends RequestForgerySanitizer { - HostnameSantizer() { this.asExpr() instanceof HostnameSanitizedExpr } + HostnameSantizer() { this.asExpr() = any(HostnameSanitizingPrefix hsp).getAnAppendedExpression() } } diff --git a/java/ql/lib/semmle/code/java/security/SensitiveActions.qll b/java/ql/lib/semmle/code/java/security/SensitiveActions.qll index b8cdf5e0833..24610836693 100644 --- a/java/ql/lib/semmle/code/java/security/SensitiveActions.qll +++ b/java/ql/lib/semmle/code/java/security/SensitiveActions.qll @@ -14,11 +14,7 @@ import java private string suspicious() { - result = "%password%" or - result = "%passwd%" or - result = "%account%" or - result = "%accnt%" or - result = "%trusted%" + result = ["%password%", "%passwd%", "%account%", "%accnt%", "%trusted%"] } private string nonSuspicious() { diff --git a/java/ql/lib/semmle/code/java/security/SpelInjectionQuery.qll b/java/ql/lib/semmle/code/java/security/SpelInjectionQuery.qll index ffccce1fbfa..0d9cdc853bb 100644 --- a/java/ql/lib/semmle/code/java/security/SpelInjectionQuery.qll +++ b/java/ql/lib/semmle/code/java/security/SpelInjectionQuery.qll @@ -58,8 +58,8 @@ private class SafeEvaluationContextFlowConfig extends DataFlow2::Configuration { */ private class SafeContextSource extends DataFlow::ExprNode { SafeContextSource() { - isSimpleEvaluationContextConstructorCall(getExpr()) or - isSimpleEvaluationContextBuilderCall(getExpr()) + isSimpleEvaluationContextConstructorCall(this.getExpr()) or + isSimpleEvaluationContextBuilderCall(this.getExpr()) } } diff --git a/java/ql/lib/semmle/code/java/security/UnsafeDeserializationQuery.qll b/java/ql/lib/semmle/code/java/security/UnsafeDeserializationQuery.qll index 5f38317acdb..4152cb907ad 100644 --- a/java/ql/lib/semmle/code/java/security/UnsafeDeserializationQuery.qll +++ b/java/ql/lib/semmle/code/java/security/UnsafeDeserializationQuery.qll @@ -17,12 +17,13 @@ private import semmle.code.java.frameworks.Jackson private import semmle.code.java.frameworks.Jabsorb private import semmle.code.java.frameworks.JoddJson private import semmle.code.java.frameworks.Flexjson +private import semmle.code.java.frameworks.google.Gson private import semmle.code.java.frameworks.apache.Lang private import semmle.code.java.Reflection private class ObjectInputStreamReadObjectMethod extends Method { ObjectInputStreamReadObjectMethod() { - this.getDeclaringType().getASourceSupertype*().hasQualifiedName("java.io", "ObjectInputStream") and + this.getDeclaringType().getASourceSupertype*() instanceof TypeObjectInputStream and (this.hasName("readObject") or this.hasName("readUnshared")) } } @@ -66,10 +67,10 @@ private class SafeKryo extends DataFlow2::Configuration { } override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - stepKryoPoolBuilderFactoryArgToConstructor(node1, node2) or - stepKryoPoolRunMethodAccessQualifierToFunctionalArgument(node1, node2) or - stepKryoPoolBuilderChainMethod(node1, node2) or - stepKryoPoolBorrowMethod(node1, node2) + this.stepKryoPoolBuilderFactoryArgToConstructor(node1, node2) or + this.stepKryoPoolRunMethodAccessQualifierToFunctionalArgument(node1, node2) or + this.stepKryoPoolBuilderChainMethod(node1, node2) or + this.stepKryoPoolBorrowMethod(node1, node2) } /** @@ -207,6 +208,10 @@ predicate unsafeDeserialization(MethodAccess ma, Expr sink) { or m instanceof FlexjsonDeserializeMethod and sink = ma.getArgument(0) + or + m instanceof GsonDeserializeMethod and + sink = ma.getArgument(0) and + any(UnsafeTypeConfig config).hasFlowToExpr(ma.getArgument(1)) ) } @@ -249,6 +254,8 @@ class UnsafeDeserializationConfig extends TaintTracking::Configuration { createJacksonJsonParserStep(pred, succ) or createJacksonTreeNodeStep(pred, succ) + or + intentFlowsToParcel(pred, succ) } override predicate isSanitizer(DataFlow::Node node) { @@ -362,9 +369,15 @@ class UnsafeTypeConfig extends TaintTracking2::Configuration { ma.getMethod() instanceof JabsorbUnmarshallMethod or ma.getMethod() instanceof JoddJsonParseMethod + or + ma.getMethod() instanceof GsonDeserializeMethod ) and // Note `JacksonTypeDescriptorType` includes plain old `java.lang.Class` - arg.getType() instanceof JacksonTypeDescriptorType and + ( + arg.getType() instanceof JacksonTypeDescriptorType + or + arg.getType().(RefType).hasQualifiedName("java.lang.reflect", "Type") + ) and arg = sink.asExpr() ) } @@ -375,7 +388,8 @@ class UnsafeTypeConfig extends TaintTracking2::Configuration { */ override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) { resolveClassStep(fromNode, toNode) or - looksLikeResolveClassStep(fromNode, toNode) + looksLikeResolveClassStep(fromNode, toNode) or + intentFlowsToParcel(fromNode, toNode) } } diff --git a/java/ql/lib/semmle/code/java/security/XSS.qll b/java/ql/lib/semmle/code/java/security/XSS.qll index 941b3c6c7fe..c2144c18921 100644 --- a/java/ql/lib/semmle/code/java/security/XSS.qll +++ b/java/ql/lib/semmle/code/java/security/XSS.qll @@ -79,7 +79,7 @@ private class XssVulnerableWriterSourceToWritingMethodFlowConfig extends TaintTr /** A method that can be used to output data to an output stream or writer. */ private class WritingMethod extends Method { WritingMethod() { - getDeclaringType().getASupertype*().hasQualifiedName("java.io", _) and + this.getDeclaringType().getASupertype*().hasQualifiedName("java.io", _) and ( this.getName().matches("print%") or this.getName() = "append" or diff --git a/java/ql/lib/semmle/code/xml/AndroidManifest.qll b/java/ql/lib/semmle/code/xml/AndroidManifest.qll index ae40b800ef9..234aacca967 100644 --- a/java/ql/lib/semmle/code/xml/AndroidManifest.qll +++ b/java/ql/lib/semmle/code/xml/AndroidManifest.qll @@ -36,7 +36,7 @@ class AndroidManifestXmlElement extends XMLElement { /** * Gets the value of the `package` attribute of this `` element. */ - string getPackageAttributeValue() { result = getAttributeValue("package") } + string getPackageAttributeValue() { result = this.getAttributeValue("package") } } /** @@ -141,7 +141,7 @@ class AndroidComponentXmlElement extends XMLElement { */ string getComponentName() { exists(XMLAttribute attr | - attr = getAnAttribute() and + attr = this.getAnAttribute() and attr.getNamespace().getPrefix() = "android" and attr.getName() = "name" | @@ -153,12 +153,15 @@ class AndroidComponentXmlElement extends XMLElement { * Gets the resolved value of the `android:name` attribute of this component element. */ string getResolvedComponentName() { - if getComponentName().matches(".%") + if this.getComponentName().matches(".%") then result = - getParent().(XMLElement).getParent().(AndroidManifestXmlElement).getPackageAttributeValue() + - getComponentName() - else result = getComponentName() + this.getParent() + .(XMLElement) + .getParent() + .(AndroidManifestXmlElement) + .getPackageAttributeValue() + this.getComponentName() + else result = this.getComponentName() } /** @@ -166,7 +169,7 @@ class AndroidComponentXmlElement extends XMLElement { */ string getExportedAttributeValue() { exists(XMLAttribute attr | - attr = getAnAttribute() and + attr = this.getAnAttribute() and attr.getNamespace().getPrefix() = "android" and attr.getName() = "exported" | @@ -177,12 +180,12 @@ class AndroidComponentXmlElement extends XMLElement { /** * Holds if the `android:exported` attribute of this component element is `true`. */ - predicate isExported() { getExportedAttributeValue() = "true" } + predicate isExported() { this.getExportedAttributeValue() = "true" } /** * Holds if the `android:exported` attribute of this component element is explicitly set to `false`. */ - predicate isNotExported() { getExportedAttributeValue() = "false" } + predicate isNotExported() { this.getExportedAttributeValue() = "false" } } /** @@ -212,7 +215,7 @@ class AndroidActionXmlElement extends XMLElement { */ string getActionName() { exists(XMLAttribute attr | - attr = getAnAttribute() and + attr = this.getAnAttribute() and attr.getNamespace().getPrefix() = "android" and attr.getName() = "name" | diff --git a/java/ql/lib/semmle/code/xml/MavenPom.qll b/java/ql/lib/semmle/code/xml/MavenPom.qll index fe6985ba811..6303de34348 100644 --- a/java/ql/lib/semmle/code/xml/MavenPom.qll +++ b/java/ql/lib/semmle/code/xml/MavenPom.qll @@ -32,7 +32,9 @@ class ProtoPom extends XMLElement { * tag was provided. */ string getVersionString() { - if exists(getVersion().getValue()) then result = getVersion().getValue() else result = "" + if exists(this.getVersion().getValue()) + then result = this.getVersion().getValue() + else result = "" } /** Gets a Maven coordinate of the form `groupId:artifactId`. */ @@ -78,25 +80,25 @@ class Pom extends ProtoPom { Dependencies getDependencies() { result = this.getAChild() } /** Gets a child XML element named "dependencyManagement". */ - DependencyManagement getDependencyManagement() { result = getAChild() } + DependencyManagement getDependencyManagement() { result = this.getAChild() } /** Gets a Dependency element for this POM. */ - Dependency getADependency() { result = getAChild().(Dependencies).getADependency() } + Dependency getADependency() { result = this.getAChild().(Dependencies).getADependency() } /** * Gets a property defined in the `` section of this POM. */ - PomProperty getALocalProperty() { result = getAChild().(PomProperties).getAProperty() } + PomProperty getALocalProperty() { result = this.getAChild().(PomProperties).getAProperty() } /** * Gets a property value defined for this project, either in a local `` section, or * in the `` section of an ancestor POM. */ PomProperty getAProperty() { - result = getALocalProperty() + result = this.getALocalProperty() or - result = getParentPom().getAProperty() and - not getALocalProperty().getName() = result.getName() + result = this.getParentPom().getAProperty() and + not this.getALocalProperty().getName() = result.getName() } /** @@ -105,7 +107,7 @@ class Pom extends ProtoPom { */ PomProperty getProperty(string name) { result.getName() = name and - result = getAProperty() + result = this.getAProperty() } /** @@ -114,11 +116,11 @@ class Pom extends ProtoPom { PomElement getProjectProperty() { ( // It must either be a child of the POM, or a child of the parent node of the POM - result = getAChild() + result = this.getAChild() or - result = getParentPom().getAChild() and + result = this.getParentPom().getAChild() and // The parent project property is not shadowed by a local project property - not exists(PomElement p | p = getAChild() and p.getName() = result.getName()) + not exists(PomElement p | p = this.getAChild() and p.getName() = result.getName()) ) and // Can't be a property if it has children of its own not exists(result.getAChild()) @@ -129,16 +131,16 @@ class Pom extends ProtoPom { * occurs by considering the properties defined by this project or an ancestor project. */ string resolvePlaceholder(string name) { - if name.prefix(8) = "project." + if name.matches("project.%") then exists(PomElement p | - p = getProjectProperty() and + p = this.getProjectProperty() and "project." + p.getName() = name and result = p.getValue() ) else exists(PomProperty prop | - prop = getAProperty() and prop.getName() = name and result = prop.getValue() + prop = this.getAProperty() and prop.getName() = name and result = prop.getValue() ) } @@ -147,24 +149,24 @@ class Pom extends ProtoPom { * is transitively available, i.e. one with scope "compile". */ Dependency getAnExportedDependency() { - result = getADependency() and result.getScope() = "compile" + result = this.getADependency() and result.getScope() = "compile" } /** * Gets a POM dependency that is exported by this POM. An exported dependency is one that * is transitively available, i.e. one with scope "compile". */ - Pom getAnExportedPom() { result = getAnExportedDependency().getPom() } + Pom getAnExportedPom() { result = this.getAnExportedDependency().getPom() } /** * Gets the `` element of this POM, if any. */ - Parent getParentElement() { result = getAChild() } + Parent getParentElement() { result = this.getAChild() } /** * Gets the POM referred to by the `` element of this POM, if any. */ - Pom getParentPom() { result = getParentElement().getPom() } + Pom getParentPom() { result = this.getParentElement().getPom() } /** * Gets the version specified for dependency `dep` in a `dependencyManagement` @@ -172,11 +174,11 @@ class Pom extends ProtoPom { * is specified. */ string getVersionStringForDependency(Dependency dep) { - if exists(getDependencyManagement().getDependency(dep)) - then result = getDependencyManagement().getDependency(dep).getVersionString() + if exists(this.getDependencyManagement().getDependency(dep)) + then result = this.getDependencyManagement().getDependency(dep).getVersionString() else - if exists(getParentPom()) - then result = getParentPom().getVersionStringForDependency(dep) + if exists(this.getParentPom()) + then result = this.getParentPom().getVersionStringForDependency(dep) else result = "" } @@ -189,24 +191,24 @@ class Pom extends ProtoPom { */ Folder getSourceDirectory() { exists(string relativePath | - if exists(getProperty("sourceDirectory")) + if exists(this.getProperty("sourceDirectory")) then // A custom source directory has been specified. - relativePath = getProperty("sourceDirectory").getValue() + relativePath = this.getProperty("sourceDirectory").getValue() else // The Maven default source directory. relativePath = "src" | // Resolve the relative path against the base directory for this POM result.getAbsolutePath() = - normalize(getFile().getParentContainer().getAbsolutePath() + "/" + relativePath) + normalize(this.getFile().getParentContainer().getAbsolutePath() + "/" + relativePath) ) } /** * Gets a `RefType` contained in the source directory. */ - RefType getASourceRefType() { result.getFile().getParentContainer*() = getSourceDirectory() } + RefType getASourceRefType() { result.getFile().getParentContainer*() = this.getSourceDirectory() } } /** @@ -235,13 +237,13 @@ class Dependency extends ProtoPom { * be the string contents of that tag, otherwise it defaults to "compile". */ string getScope() { - if exists(getAChild().(Scope)) - then exists(Scope s | s = getAChild() and result = s.getValue()) + if exists(this.getAChild().(Scope)) + then exists(Scope s | s = this.getAChild() and result = s.getValue()) else result = "compile" } override string getVersionString() { - if exists(getVersion()) + if exists(this.getVersion()) then result = super.getVersionString() else if exists(Pom p | this = p.getADependency()) @@ -263,11 +265,11 @@ class PomDependency extends Dependency { source.getADependency() = this and // Consider dependencies that can be used at compile time. ( - getScope() = "compile" + this.getScope() = "compile" or // Provided dependencies are like compile time dependencies except (a) they are not packaged // when creating the jar and (b) they are not transitive. - getScope() = "provided" + this.getScope() = "provided" // We ignore "test" dependencies because they can be runtime or compile time dependencies ) ) @@ -284,11 +286,11 @@ class PomElement extends XMLElement { */ string getValue() { exists(string s | - s = allCharactersString() and + s = this.allCharactersString() and if s.matches("${%") then // Resolve the placeholder in the parent POM - result = getParent*().(Pom).resolvePlaceholder(s.substring(2, s.length() - 1)) + result = this.getParent*().(Pom).resolvePlaceholder(s.substring(2, s.length() - 1)) else result = s ) } @@ -335,18 +337,18 @@ class Dependencies extends PomElement { /** An XML element named "dependencyManagement", as found in Maven POM XML files. */ class DependencyManagement extends PomElement { - DependencyManagement() { getName() = "dependencyManagement" } + DependencyManagement() { this.getName() = "dependencyManagement" } - Dependencies getDependencies() { result = getAChild() } + Dependencies getDependencies() { result = this.getAChild() } - Dependency getADependency() { result = getDependencies().getADependency() } + Dependency getADependency() { result = this.getDependencies().getADependency() } /** * Gets a dependency declared in this `dependencyManagement` element that has * the same (short) coordinates as `dep`. */ Dependency getDependency(Dependency dep) { - result = getADependency() and + result = this.getADependency() and result.getShortCoordinate() = dep.getShortCoordinate() } } @@ -365,7 +367,7 @@ class PomProperties extends PomElement { * Represents a single property. */ class PomProperty extends PomElement { - PomProperty() { getParent() instanceof PomProperties } + PomProperty() { this.getParent() instanceof PomProperties } } /** @@ -378,7 +380,7 @@ class DeclaredRepository extends PomElement { * Gets the url for this repository. If the `url` tag is present, this will * be the string contents of that tag. */ - string getUrl() { result = getAChild("url").(PomElement).getValue() } + string getUrl() { result = this.getAChild("url").(PomElement).getValue() } } /** @@ -386,12 +388,16 @@ class DeclaredRepository extends PomElement { * "repository" with a parent name ".m2" is considered to be a Maven repository. */ class MavenRepo extends Folder { - MavenRepo() { getBaseName() = "repository" and getParentContainer().getBaseName() = ".m2" } + MavenRepo() { + this.getBaseName() = "repository" and this.getParentContainer().getBaseName() = ".m2" + } /** * Gets a Jar file contained within this repository. */ - File getAJarFile() { result = getAChildContainer*().(File) and result.getExtension() = "jar" } + File getAJarFile() { + result = this.getAChildContainer*().(File) and result.getExtension() = "jar" + } /** * Gets any jar artifacts in this repository that match the POM project definition. This is an @@ -400,7 +406,7 @@ class MavenRepo extends Folder { * For all other qualifiers, all matches are returned regardless of version. */ MavenRepoJar getAnArtifact(ProtoPom pom) { - result = getAJarFile() and + result = this.getAJarFile() and if exists(MavenRepoJar mrj | mrj.preciseMatch(pom)) or versionHardMatch(pom) then // Either a hard match qualifier, or soft and there is at least one precise match @@ -432,7 +438,7 @@ class MavenRepoJar extends File { // Assuming the standard layout, the first part of the directory structure from the Maven // repository will be the groupId converted to a path by replacing "." with "/". result = - getParentContainer() + this.getParentContainer() .getParentContainer() .getParentContainer() .getAbsolutePath() @@ -444,44 +450,44 @@ class MavenRepoJar extends File { /** * DEPRECATED: name changed to `getGroupId` for consistent use of camel-case. */ - deprecated string getGroupID() { result = getGroupId() } + deprecated string getGroupID() { result = this.getGroupId() } /** * Gets the `artifactId` of this jar. */ - string getArtifactId() { result = getParentContainer().getParentContainer().getBaseName() } + string getArtifactId() { result = this.getParentContainer().getParentContainer().getBaseName() } /** * DEPRECATED: name changed to `getArtifactId` for consistent casing and consistent spelling with Maven. */ - deprecated string getArtefactID() { result = getArtifactId() } + deprecated string getArtefactID() { result = this.getArtifactId() } /** * Gets the artifact version string of this jar. */ - string getVersion() { result = getParentContainer().getBaseName() } + string getVersion() { result = this.getParentContainer().getBaseName() } /** * Holds if this jar is an artifact for the given POM or dependency, regardless of which version it is. */ predicate artifactMatches(ProtoPom pom) { - pom.getGroup().getValue() = getGroupId() and - pom.getArtifact().getValue() = getArtifactId() + pom.getGroup().getValue() = this.getGroupId() and + pom.getArtifact().getValue() = this.getArtifactId() } /** * DEPRECATED: name changed to `artifactMatches` for consistent spelling with Maven. */ - deprecated predicate artefactMatches(ProtoPom pom) { artifactMatches(pom) } + deprecated predicate artefactMatches(ProtoPom pom) { this.artifactMatches(pom) } /** * Holds if this jar is both an artifact for the POM, and has a version string that matches the POM * version string. Only soft and hard version matches are supported. */ predicate preciseMatch(ProtoPom pom) { - artifactMatches(pom) and + this.artifactMatches(pom) and if versionHardMatch(pom) - then ("[" + getVersion() + "]").matches(pom.getVersionString() + "%") - else getVersion().matches(pom.getVersionString() + "%") + then ("[" + this.getVersion() + "]").matches(pom.getVersionString() + "%") + else this.getVersion().matches(pom.getVersionString() + "%") } } diff --git a/java/ql/lib/semmle/code/xml/WebXML.qll b/java/ql/lib/semmle/code/xml/WebXML.qll index 306f908afd5..c7dec5fd600 100644 --- a/java/ql/lib/semmle/code/xml/WebXML.qll +++ b/java/ql/lib/semmle/code/xml/WebXML.qll @@ -37,7 +37,7 @@ class WebXMLElement extends XMLElement { /** * Gets the value for this element, with leading and trailing whitespace trimmed. */ - string getValue() { result = allCharactersString().trim() } + string getValue() { result = this.allCharactersString().trim() } } /** @@ -49,33 +49,33 @@ class WebContextParameter extends WebXMLElement { /** * Gets the `` element of this ``. */ - WebContextParamName getParamName() { result = getAChild() } + WebContextParamName getParamName() { result = this.getAChild() } /** * Gets the `` element of this ``. */ - WebContextParamValue getParamValue() { result = getAChild() } + WebContextParamValue getParamValue() { result = this.getAChild() } } /** * A `` element in a `web.xml` file. */ class WebContextParamName extends WebXMLElement { - WebContextParamName() { getName() = "param-name" } + WebContextParamName() { this.getName() = "param-name" } } /** * A `` element in a `web.xml` file. */ class WebContextParamValue extends WebXMLElement { - WebContextParamValue() { getName() = "param-value" } + WebContextParamValue() { this.getName() = "param-value" } } /** * A `` element in a `web.xml` file. */ class WebFilter extends WebXMLElement { - WebFilter() { getName() = "filter" } + WebFilter() { this.getName() = "filter" } } /** @@ -83,18 +83,18 @@ class WebFilter extends WebXMLElement { */ class WebFilterClass extends WebXMLElement { WebFilterClass() { - getName() = "filter-class" and - getParent() instanceof WebFilter + this.getName() = "filter-class" and + this.getParent() instanceof WebFilter } - Class getClass() { result.getQualifiedName() = getValue() } + Class getClass() { result.getQualifiedName() = this.getValue() } } /** * A `` element in a `web.xml` file. */ class WebServlet extends WebXMLElement { - WebServlet() { getName() = "servlet" } + WebServlet() { this.getName() = "servlet" } } /** @@ -102,18 +102,18 @@ class WebServlet extends WebXMLElement { */ class WebServletClass extends WebXMLElement { WebServletClass() { - getName() = "servlet-class" and - getParent() instanceof WebServlet + this.getName() = "servlet-class" and + this.getParent() instanceof WebServlet } - Class getClass() { result.getQualifiedName() = getValue() } + Class getClass() { result.getQualifiedName() = this.getValue() } } /** * A `` element in a `web.xml` file. */ class WebListener extends WebXMLElement { - WebListener() { getName() = "listener" } + WebListener() { this.getName() = "listener" } } /** @@ -121,14 +121,14 @@ class WebListener extends WebXMLElement { */ class WebListenerClass extends WebXMLElement { WebListenerClass() { - getName() = "listener-class" and - getParent() instanceof WebListener + this.getName() = "listener-class" and + this.getParent() instanceof WebListener } /** * Gets the `Class` instance associated with this element. */ - Class getClass() { result.getQualifiedName() = getValue() } + Class getClass() { result.getQualifiedName() = this.getValue() } } /** @@ -140,12 +140,12 @@ class WebErrorPage extends WebXMLElement { /** * Gets the `` element of this ``. */ - WebErrorPageType getPageType() { result = getAChild() } + WebErrorPageType getPageType() { result = this.getAChild() } /** * Gets the `` element of this ``. */ - WebErrorPageLocation getPageLocation() { result = getAChild() } + WebErrorPageLocation getPageLocation() { result = this.getAChild() } } /** @@ -153,8 +153,8 @@ class WebErrorPage extends WebXMLElement { */ class WebErrorPageType extends WebXMLElement { WebErrorPageType() { - getName() = "exception-type" and - getParent() instanceof WebErrorPage + this.getName() = "exception-type" and + this.getParent() instanceof WebErrorPage } } @@ -163,7 +163,7 @@ class WebErrorPageType extends WebXMLElement { */ class WebErrorPageLocation extends WebXMLElement { WebErrorPageLocation() { - getName() = "location" and - getParent() instanceof WebErrorPage + this.getName() = "location" and + this.getParent() instanceof WebErrorPage } } diff --git a/java/ql/lib/semmle/code/xml/XML.qll b/java/ql/lib/semmle/code/xml/XML.qll index 5871fed0ddd..76f3b3cb022 100755 --- a/java/ql/lib/semmle/code/xml/XML.qll +++ b/java/ql/lib/semmle/code/xml/XML.qll @@ -24,7 +24,7 @@ class XMLLocatable extends @xmllocatable, TXMLLocatable { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -108,7 +108,7 @@ class XMLParent extends @xmlparent { } /** Gets the text value contained in this XML parent. */ - string getTextValue() { result = allCharactersString() } + string getTextValue() { result = this.allCharactersString() } /** Gets a printable representation of this XML parent. */ string toString() { result = this.getName() } @@ -119,7 +119,7 @@ class XMLFile extends XMLParent, File { XMLFile() { xmlEncoding(this, _) } /** Gets a printable representation of this XML file. */ - override string toString() { result = getName() } + override string toString() { result = this.getName() } /** Gets the name of this XML file. */ override string getName() { result = File.super.getAbsolutePath() } @@ -129,14 +129,14 @@ class XMLFile extends XMLParent, File { * * Gets the path of this XML file. */ - deprecated string getPath() { result = getAbsolutePath() } + deprecated string getPath() { result = this.getAbsolutePath() } /** * DEPRECATED: Use `getParentContainer().getAbsolutePath()` instead. * * Gets the path of the folder that contains this XML file. */ - deprecated string getFolder() { result = getParentContainer().getAbsolutePath() } + deprecated string getFolder() { result = this.getParentContainer().getAbsolutePath() } /** Gets the encoding of this XML file. */ string getEncoding() { xmlEncoding(this, result) } @@ -200,7 +200,7 @@ class XMLDTD extends XMLLocatable, @xmldtd { */ class XMLElement extends @xmlelement, XMLParent, XMLLocatable { /** Holds if this XML element has the given `name`. */ - predicate hasName(string name) { name = getName() } + predicate hasName(string name) { name = this.getName() } /** Gets the name of this XML element. */ override string getName() { xmlElements(this, result, _, _, _) } @@ -239,7 +239,7 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable { string getAttributeValue(string name) { result = this.getAttribute(name).getValue() } /** Gets a printable representation of this XML element. */ - override string toString() { result = getName() } + override string toString() { result = this.getName() } } /** diff --git a/java/ql/lib/tutorial.qll b/java/ql/lib/tutorial.qll new file mode 100644 index 00000000000..8cb1797a532 --- /dev/null +++ b/java/ql/lib/tutorial.qll @@ -0,0 +1,1207 @@ +/** + * This library is used in the QL detective tutorials. + * + * Note: Data is usually stored in a separate database and the QL libraries only contain predicates, + * but for this tutorial both the data and the predicates are stored in the library. + */ +class Person extends string { + Person() { + this = "Ronil" or + this = "Dina" or + this = "Ravi" or + this = "Bruce" or + this = "Jo" or + this = "Aida" or + this = "Esme" or + this = "Charlie" or + this = "Fred" or + this = "Meera" or + this = "Maya" or + this = "Chad" or + this = "Tiana" or + this = "Laura" or + this = "George" or + this = "Will" or + this = "Mary" or + this = "Almira" or + this = "Susannah" or + this = "Rhoda" or + this = "Cynthia" or + this = "Eunice" or + this = "Olive" or + this = "Virginia" or + this = "Angeline" or + this = "Helen" or + this = "Cornelia" or + this = "Harriet" or + this = "Mahala" or + this = "Abby" or + this = "Margaret" or + this = "Deb" or + this = "Minerva" or + this = "Severus" or + this = "Lavina" or + this = "Adeline" or + this = "Cath" or + this = "Elisa" or + this = "Lucretia" or + this = "Anne" or + this = "Eleanor" or + this = "Joanna" or + this = "Adam" or + this = "Agnes" or + this = "Rosanna" or + this = "Clara" or + this = "Melissa" or + this = "Amy" or + this = "Isabel" or + this = "Jemima" or + this = "Cordelia" or + this = "Melinda" or + this = "Delila" or + this = "Jeremiah" or + this = "Elijah" or + this = "Hester" or + this = "Walter" or + this = "Oliver" or + this = "Hugh" or + this = "Aaron" or + this = "Reuben" or + this = "Eli" or + this = "Amos" or + this = "Augustus" or + this = "Theodore" or + this = "Ira" or + this = "Timothy" or + this = "Cyrus" or + this = "Horace" or + this = "Simon" or + this = "Asa" or + this = "Frank" or + this = "Nelson" or + this = "Leonard" or + this = "Harrison" or + this = "Anthony" or + this = "Louis" or + this = "Milton" or + this = "Noah" or + this = "Cornelius" or + this = "Abdul" or + this = "Warren" or + this = "Harvey" or + this = "Dennis" or + this = "Wesley" or + this = "Sylvester" or + this = "Gilbert" or + this = "Sullivan" or + this = "Edmund" or + this = "Wilson" or + this = "Perry" or + this = "Matthew" or + this = "Simba" or + this = "Nala" or + this = "Rafiki" or + this = "Shenzi" or + this = "Ernest" or + this = "Gertrude" or + this = "Oscar" or + this = "Lilian" or + this = "Raymond" or + this = "Elgar" or + this = "Elmer" or + this = "Herbert" or + this = "Maude" or + this = "Mae" or + this = "Otto" or + this = "Edwin" or + this = "Ophelia" or + this = "Parsley" or + this = "Sage" or + this = "Rosemary" or + this = "Thyme" or + this = "Garfunkel" or + this = "King Basil" or + this = "Stephen" + } + + /** Gets the hair color of the person. If the person is bald, there is no result. */ + string getHairColor() { + this = "Ronil" and result = "black" + or + this = "Dina" and result = "black" + or + this = "Ravi" and result = "black" + or + this = "Bruce" and result = "brown" + or + this = "Jo" and result = "red" + or + this = "Aida" and result = "blond" + or + this = "Esme" and result = "blond" + or + this = "Fred" and result = "gray" + or + this = "Meera" and result = "brown" + or + this = "Maya" and result = "brown" + or + this = "Chad" and result = "brown" + or + this = "Tiana" and result = "black" + or + this = "Laura" and result = "blond" + or + this = "George" and result = "blond" + or + this = "Will" and result = "blond" + or + this = "Mary" and result = "blond" + or + this = "Almira" and result = "black" + or + this = "Susannah" and result = "blond" + or + this = "Rhoda" and result = "blond" + or + this = "Cynthia" and result = "gray" + or + this = "Eunice" and result = "white" + or + this = "Olive" and result = "brown" + or + this = "Virginia" and result = "brown" + or + this = "Angeline" and result = "red" + or + this = "Helen" and result = "white" + or + this = "Cornelia" and result = "gray" + or + this = "Harriet" and result = "white" + or + this = "Mahala" and result = "black" + or + this = "Abby" and result = "red" + or + this = "Margaret" and result = "brown" + or + this = "Deb" and result = "brown" + or + this = "Minerva" and result = "brown" + or + this = "Severus" and result = "black" + or + this = "Lavina" and result = "brown" + or + this = "Adeline" and result = "brown" + or + this = "Cath" and result = "brown" + or + this = "Elisa" and result = "brown" + or + this = "Lucretia" and result = "gray" + or + this = "Anne" and result = "black" + or + this = "Eleanor" and result = "brown" + or + this = "Joanna" and result = "brown" + or + this = "Adam" and result = "black" + or + this = "Agnes" and result = "black" + or + this = "Rosanna" and result = "gray" + or + this = "Clara" and result = "blond" + or + this = "Melissa" and result = "brown" + or + this = "Amy" and result = "brown" + or + this = "Isabel" and result = "black" + or + this = "Jemima" and result = "red" + or + this = "Cordelia" and result = "red" + or + this = "Melinda" and result = "gray" + or + this = "Delila" and result = "white" + or + this = "Jeremiah" and result = "gray" + or + this = "Hester" and result = "black" + or + this = "Walter" and result = "black" + or + this = "Aaron" and result = "gray" + or + this = "Reuben" and result = "gray" + or + this = "Eli" and result = "gray" + or + this = "Amos" and result = "white" + or + this = "Augustus" and result = "white" + or + this = "Theodore" and result = "white" + or + this = "Timothy" and result = "brown" + or + this = "Cyrus" and result = "brown" + or + this = "Horace" and result = "brown" + or + this = "Simon" and result = "brown" + or + this = "Asa" and result = "brown" + or + this = "Frank" and result = "brown" + or + this = "Nelson" and result = "black" + or + this = "Leonard" and result = "black" + or + this = "Harrison" and result = "black" + or + this = "Anthony" and result = "black" + or + this = "Louis" and result = "black" + or + this = "Milton" and result = "blond" + or + this = "Noah" and result = "blond" + or + this = "Cornelius" and result = "red" + or + this = "Abdul" and result = "brown" + or + this = "Warren" and result = "red" + or + this = "Harvey" and result = "blond" + or + this = "Dennis" and result = "blond" + or + this = "Wesley" and result = "brown" + or + this = "Sylvester" and result = "brown" + or + this = "Gilbert" and result = "brown" + or + this = "Sullivan" and result = "brown" + or + this = "Edmund" and result = "brown" + or + this = "Wilson" and result = "blond" + or + this = "Perry" and result = "black" + or + this = "Simba" and result = "brown" + or + this = "Nala" and result = "brown" + or + this = "Rafiki" and result = "red" + or + this = "Shenzi" and result = "gray" + or + this = "Ernest" and result = "blond" + or + this = "Gertrude" and result = "brown" + or + this = "Oscar" and result = "blond" + or + this = "Lilian" and result = "brown" + or + this = "Raymond" and result = "brown" + or + this = "Elgar" and result = "brown" + or + this = "Elmer" and result = "brown" + or + this = "Herbert" and result = "brown" + or + this = "Maude" and result = "brown" + or + this = "Mae" and result = "brown" + or + this = "Otto" and result = "black" + or + this = "Edwin" and result = "black" + or + this = "Ophelia" and result = "brown" + or + this = "Parsley" and result = "brown" + or + this = "Sage" and result = "brown" + or + this = "Rosemary" and result = "brown" + or + this = "Thyme" and result = "brown" + or + this = "Garfunkel" and result = "brown" + or + this = "King Basil" and result = "brown" + or + this = "Stephen" and result = "black" + or + this = "Stephen" and result = "gray" + } + + /** Gets the age of the person (in years). If the person is deceased, there is no result. */ + int getAge() { + this = "Ronil" and result = 21 + or + this = "Dina" and result = 53 + or + this = "Ravi" and result = 16 + or + this = "Bruce" and result = 35 + or + this = "Jo" and result = 47 + or + this = "Aida" and result = 26 + or + this = "Esme" and result = 25 + or + this = "Charlie" and result = 31 + or + this = "Fred" and result = 68 + or + this = "Meera" and result = 62 + or + this = "Maya" and result = 29 + or + this = "Chad" and result = 49 + or + this = "Tiana" and result = 18 + or + this = "Laura" and result = 2 + or + this = "George" and result = 3 + or + this = "Will" and result = 41 + or + this = "Mary" and result = 51 + or + this = "Almira" and result = 1 + or + this = "Susannah" and result = 97 + or + this = "Rhoda" and result = 39 + or + this = "Cynthia" and result = 89 + or + this = "Eunice" and result = 83 + or + this = "Olive" and result = 25 + or + this = "Virginia" and result = 52 + or + this = "Angeline" and result = 22 + or + this = "Helen" and result = 79 + or + this = "Cornelia" and result = 59 + or + this = "Harriet" and result = 57 + or + this = "Mahala" and result = 61 + or + this = "Abby" and result = 24 + or + this = "Margaret" and result = 59 + or + this = "Deb" and result = 31 + or + this = "Minerva" and result = 72 + or + this = "Severus" and result = 61 + or + this = "Lavina" and result = 33 + or + this = "Adeline" and result = 17 + or + this = "Cath" and result = 22 + or + this = "Elisa" and result = 9 + or + this = "Lucretia" and result = 56 + or + this = "Anne" and result = 11 + or + this = "Eleanor" and result = 80 + or + this = "Joanna" and result = 43 + or + this = "Adam" and result = 37 + or + this = "Agnes" and result = 47 + or + this = "Rosanna" and result = 61 + or + this = "Clara" and result = 31 + or + this = "Melissa" and result = 37 + or + this = "Amy" and result = 12 + or + this = "Isabel" and result = 6 + or + this = "Jemima" and result = 16 + or + this = "Cordelia" and result = 21 + or + this = "Melinda" and result = 55 + or + this = "Delila" and result = 66 + or + this = "Jeremiah" and result = 54 + or + this = "Elijah" and result = 42 + or + this = "Hester" and result = 68 + or + this = "Walter" and result = 66 + or + this = "Oliver" and result = 33 + or + this = "Hugh" and result = 51 + or + this = "Aaron" and result = 49 + or + this = "Reuben" and result = 58 + or + this = "Eli" and result = 70 + or + this = "Amos" and result = 65 + or + this = "Augustus" and result = 56 + or + this = "Theodore" and result = 69 + or + this = "Ira" and result = 1 + or + this = "Timothy" and result = 54 + or + this = "Cyrus" and result = 78 + or + this = "Horace" and result = 34 + or + this = "Simon" and result = 23 + or + this = "Asa" and result = 28 + or + this = "Frank" and result = 59 + or + this = "Nelson" and result = 38 + or + this = "Leonard" and result = 58 + or + this = "Harrison" and result = 7 + or + this = "Anthony" and result = 2 + or + this = "Louis" and result = 34 + or + this = "Milton" and result = 36 + or + this = "Noah" and result = 48 + or + this = "Cornelius" and result = 41 + or + this = "Abdul" and result = 67 + or + this = "Warren" and result = 47 + or + this = "Harvey" and result = 31 + or + this = "Dennis" and result = 39 + or + this = "Wesley" and result = 13 + or + this = "Sylvester" and result = 19 + or + this = "Gilbert" and result = 16 + or + this = "Sullivan" and result = 17 + or + this = "Edmund" and result = 29 + or + this = "Wilson" and result = 27 + or + this = "Perry" and result = 31 + or + this = "Matthew" and result = 55 + or + this = "Simba" and result = 8 + or + this = "Nala" and result = 7 + or + this = "Rafiki" and result = 76 + or + this = "Shenzi" and result = 67 + } + + /** Gets the height of the person (in cm). If the person is deceased, there is no result. */ + float getHeight() { + this = "Ronil" and result = 183.0 + or + this = "Dina" and result = 155.1 + or + this = "Ravi" and result = 175.2 + or + this = "Bruce" and result = 191.3 + or + this = "Jo" and result = 163.4 + or + this = "Aida" and result = 182.6 + or + this = "Esme" and result = 176.9 + or + this = "Charlie" and result = 189.7 + or + this = "Fred" and result = 179.4 + or + this = "Meera" and result = 160.1 + or + this = "Maya" and result = 153.0 + or + this = "Chad" and result = 168.5 + or + this = "Tiana" and result = 149.7 + or + this = "Laura" and result = 87.5 + or + this = "George" and result = 96.4 + or + this = "Will" and result = 167.1 + or + this = "Mary" and result = 159.8 + or + this = "Almira" and result = 62.1 + or + this = "Susannah" and result = 145.8 + or + this = "Rhoda" and result = 180.1 + or + this = "Cynthia" and result = 161.8 + or + this = "Eunice" and result = 153.2 + or + this = "Olive" and result = 179.9 + or + this = "Virginia" and result = 165.1 + or + this = "Angeline" and result = 172.3 + or + this = "Helen" and result = 163.1 + or + this = "Cornelia" and result = 160.8 + or + this = "Harriet" and result = 163.2 + or + this = "Mahala" and result = 157.7 + or + this = "Abby" and result = 174.5 + or + this = "Margaret" and result = 165.6 + or + this = "Deb" and result = 171.6 + or + this = "Minerva" and result = 168.7 + or + this = "Severus" and result = 188.8 + or + this = "Lavina" and result = 155.1 + or + this = "Adeline" and result = 165.5 + or + this = "Cath" and result = 147.8 + or + this = "Elisa" and result = 129.4 + or + this = "Lucretia" and result = 153.6 + or + this = "Anne" and result = 140.4 + or + this = "Eleanor" and result = 151.1 + or + this = "Joanna" and result = 167.2 + or + this = "Adam" and result = 155.5 + or + this = "Agnes" and result = 156.8 + or + this = "Rosanna" and result = 162.4 + or + this = "Clara" and result = 158.6 + or + this = "Melissa" and result = 182.3 + or + this = "Amy" and result = 147.1 + or + this = "Isabel" and result = 121.4 + or + this = "Jemima" and result = 149.8 + or + this = "Cordelia" and result = 151.7 + or + this = "Melinda" and result = 154.4 + or + this = "Delila" and result = 163.4 + or + this = "Jeremiah" and result = 167.5 + or + this = "Elijah" and result = 184.5 + or + this = "Hester" and result = 152.7 + or + this = "Walter" and result = 159.6 + or + this = "Oliver" and result = 192.4 + or + this = "Hugh" and result = 173.1 + or + this = "Aaron" and result = 176.6 + or + this = "Reuben" and result = 169.9 + or + this = "Eli" and result = 180.4 + or + this = "Amos" and result = 167.4 + or + this = "Augustus" and result = 156.5 + or + this = "Theodore" and result = 176.6 + or + this = "Ira" and result = 54.1 + or + this = "Timothy" and result = 172.2 + or + this = "Cyrus" and result = 157.9 + or + this = "Horace" and result = 169.3 + or + this = "Simon" and result = 157.1 + or + this = "Asa" and result = 149.4 + or + this = "Frank" and result = 167.2 + or + this = "Nelson" and result = 173.0 + or + this = "Leonard" and result = 172.0 + or + this = "Harrison" and result = 126.0 + or + this = "Anthony" and result = 98.4 + or + this = "Louis" and result = 186.8 + or + this = "Milton" and result = 157.8 + or + this = "Noah" and result = 190.5 + or + this = "Cornelius" and result = 183.1 + or + this = "Abdul" and result = 182.0 + or + this = "Warren" and result = 175.0 + or + this = "Harvey" and result = 169.3 + or + this = "Dennis" and result = 160.4 + or + this = "Wesley" and result = 139.8 + or + this = "Sylvester" and result = 188.2 + or + this = "Gilbert" and result = 177.6 + or + this = "Sullivan" and result = 168.3 + or + this = "Edmund" and result = 159.2 + or + this = "Wilson" and result = 167.6 + or + this = "Perry" and result = 189.1 + or + this = "Matthew" and result = 167.2 + or + this = "Simba" and result = 140.1 + or + this = "Nala" and result = 138.0 + or + this = "Rafiki" and result = 139.3 + or + this = "Shenzi" and result = 171.1 + } + + /** Gets the location of the person's home ("north", "south", "east", or "west"). If the person is deceased, there is no result. */ + string getLocation() { + this = "Ronil" and result = "north" + or + this = "Dina" and result = "north" + or + this = "Ravi" and result = "north" + or + this = "Bruce" and result = "south" + or + this = "Jo" and result = "west" + or + this = "Aida" and result = "east" + or + this = "Esme" and result = "east" + or + this = "Charlie" and result = "south" + or + this = "Fred" and result = "west" + or + this = "Meera" and result = "south" + or + this = "Maya" and result = "south" + or + this = "Chad" and result = "south" + or + this = "Tiana" and result = "west" + or + this = "Laura" and result = "south" + or + this = "George" and result = "south" + or + this = "Will" and result = "south" + or + this = "Mary" and result = "south" + or + this = "Almira" and result = "south" + or + this = "Susannah" and result = "north" + or + this = "Rhoda" and result = "north" + or + this = "Cynthia" and result = "north" + or + this = "Eunice" and result = "north" + or + this = "Olive" and result = "west" + or + this = "Virginia" and result = "west" + or + this = "Angeline" and result = "west" + or + this = "Helen" and result = "west" + or + this = "Cornelia" and result = "east" + or + this = "Harriet" and result = "east" + or + this = "Mahala" and result = "east" + or + this = "Abby" and result = "east" + or + this = "Margaret" and result = "east" + or + this = "Deb" and result = "east" + or + this = "Minerva" and result = "south" + or + this = "Severus" and result = "north" + or + this = "Lavina" and result = "east" + or + this = "Adeline" and result = "west" + or + this = "Cath" and result = "east" + or + this = "Elisa" and result = "east" + or + this = "Lucretia" and result = "north" + or + this = "Anne" and result = "north" + or + this = "Eleanor" and result = "south" + or + this = "Joanna" and result = "south" + or + this = "Adam" and result = "east" + or + this = "Agnes" and result = "east" + or + this = "Rosanna" and result = "east" + or + this = "Clara" and result = "east" + or + this = "Melissa" and result = "west" + or + this = "Amy" and result = "west" + or + this = "Isabel" and result = "west" + or + this = "Jemima" and result = "west" + or + this = "Cordelia" and result = "west" + or + this = "Melinda" and result = "west" + or + this = "Delila" and result = "south" + or + this = "Jeremiah" and result = "north" + or + this = "Elijah" and result = "north" + or + this = "Hester" and result = "east" + or + this = "Walter" and result = "east" + or + this = "Oliver" and result = "east" + or + this = "Hugh" and result = "south" + or + this = "Aaron" and result = "south" + or + this = "Reuben" and result = "west" + or + this = "Eli" and result = "west" + or + this = "Amos" and result = "east" + or + this = "Augustus" and result = "south" + or + this = "Theodore" and result = "west" + or + this = "Ira" and result = "south" + or + this = "Timothy" and result = "north" + or + this = "Cyrus" and result = "north" + or + this = "Horace" and result = "east" + or + this = "Simon" and result = "east" + or + this = "Asa" and result = "east" + or + this = "Frank" and result = "west" + or + this = "Nelson" and result = "west" + or + this = "Leonard" and result = "west" + or + this = "Harrison" and result = "north" + or + this = "Anthony" and result = "north" + or + this = "Louis" and result = "north" + or + this = "Milton" and result = "south" + or + this = "Noah" and result = "south" + or + this = "Cornelius" and result = "east" + or + this = "Abdul" and result = "east" + or + this = "Warren" and result = "west" + or + this = "Harvey" and result = "west" + or + this = "Dennis" and result = "west" + or + this = "Wesley" and result = "west" + or + this = "Sylvester" and result = "south" + or + this = "Gilbert" and result = "east" + or + this = "Sullivan" and result = "east" + or + this = "Edmund" and result = "north" + or + this = "Wilson" and result = "north" + or + this = "Perry" and result = "west" + or + this = "Matthew" and result = "east" + or + this = "Simba" and result = "south" + or + this = "Nala" and result = "south" + or + this = "Rafiki" and result = "north" + or + this = "Shenzi" and result = "west" + } + + /** Holds if the person is deceased. */ + predicate isDeceased() { + this = "Ernest" or + this = "Gertrude" or + this = "Oscar" or + this = "Lilian" or + this = "Edwin" or + this = "Raymond" or + this = "Elgar" or + this = "Elmer" or + this = "Herbert" or + this = "Maude" or + this = "Mae" or + this = "Otto" or + this = "Ophelia" or + this = "Parsley" or + this = "Sage" or + this = "Rosemary" or + this = "Thyme" or + this = "Garfunkel" or + this = "King Basil" + } + + /** Gets a parent of the person (alive or deceased). */ + Person getAParent() { + this = "Stephen" and result = "Edmund" + or + this = "Edmund" and result = "Augustus" + or + this = "Augustus" and result = "Stephen" + or + this = "Abby" and result = "Cornelia" + or + this = "Abby" and result = "Amos" + or + this = "Abdul" and result = "Susannah" + or + this = "Adam" and result = "Amos" + or + this = "Adeline" and result = "Melinda" + or + this = "Adeline" and result = "Frank" + or + this = "Agnes" and result = "Abdul" + or + this = "Aida" and result = "Agnes" + or + this = "Almira" and result = "Sylvester" + or + this = "Amos" and result = "Eunice" + or + this = "Amy" and result = "Noah" + or + this = "Amy" and result = "Chad" + or + this = "Angeline" and result = "Reuben" + or + this = "Angeline" and result = "Lucretia" + or + this = "Anne" and result = "Rhoda" + or + this = "Anne" and result = "Louis" + or + this = "Anthony" and result = "Lavina" + or + this = "Anthony" and result = "Asa" + or + this = "Asa" and result = "Cornelia" + or + this = "Cath" and result = "Harriet" + or + this = "Charlie" and result = "Matthew" + or + this = "Clara" and result = "Ernest" + or + this = "Cornelia" and result = "Cynthia" + or + this = "Cornelius" and result = "Eli" + or + this = "Deb" and result = "Margaret" + or + this = "Dennis" and result = "Fred" + or + this = "Eli" and result = "Susannah" + or + this = "Elijah" and result = "Delila" + or + this = "Elisa" and result = "Deb" + or + this = "Elisa" and result = "Horace" + or + this = "Esme" and result = "Margaret" + or + this = "Frank" and result = "Eleanor" + or + this = "Frank" and result = "Cyrus" + or + this = "George" and result = "Maya" + or + this = "George" and result = "Wilson" + or + this = "Gilbert" and result = "Cornelius" + or + this = "Harriet" and result = "Cynthia" + or + this = "Harrison" and result = "Louis" + or + this = "Harvey" and result = "Fred" + or + this = "Helen" and result = "Susannah" + or + this = "Hester" and result = "Edwin" + or + this = "Hugh" and result = "Cyrus" + or + this = "Hugh" and result = "Helen" + or + this = "Ira" and result = "Maya" + or + this = "Ira" and result = "Wilson" + or + this = "Isabel" and result = "Perry" + or + this = "Isabel" and result = "Harvey" + or + this = "Jemima" and result = "Melinda" + or + this = "Jemima" and result = "Frank" + or + this = "Ernest" and result = "Lilian" + or + this = "Ernest" and result = "Oscar" + or + this = "Gertrude" and result = "Ophelia" + or + this = "Gertrude" and result = "Raymond" + or + this = "Lilian" and result = "Elgar" + or + this = "Lilian" and result = "Mae" + or + this = "Raymond" and result = "Elgar" + or + this = "Raymond" and result = "Mae" + or + this = "Elmer" and result = "Ophelia" + or + this = "Elmer" and result = "Raymond" + or + this = "Herbert" and result = "Ophelia" + or + this = "Herbert" and result = "Raymond" + or + this = "Maude" and result = "Ophelia" + or + this = "Maude" and result = "Raymond" + or + this = "Otto" and result = "Elgar" + or + this = "Otto" and result = "Mae" + or + this = "Edwin" and result = "Otto" + or + this = "Parsley" and result = "Simon" + or + this = "Parsley" and result = "Garfunkel" + or + this = "Sage" and result = "Simon" + or + this = "Sage" and result = "Garfunkel" + or + this = "Rosemary" and result = "Simon" + or + this = "Rosemary" and result = "Garfunkel" + or + this = "Thyme" and result = "Simon" + or + this = "Thyme" and result = "Garfunkel" + or + this = "King Basil" and result = "Ophelia" + or + this = "King Basil" and result = "Raymond" + or + this = "Jo" and result = "Theodore" + or + this = "Joanna" and result = "Shenzi" + or + this = "Laura" and result = "Maya" + or + this = "Laura" and result = "Wilson" + or + this = "Lavina" and result = "Mahala" + or + this = "Lavina" and result = "Walter" + or + this = "Leonard" and result = "Cyrus" + or + this = "Leonard" and result = "Helen" + or + this = "Lucretia" and result = "Eleanor" + or + this = "Lucretia" and result = "Cyrus" + or + this = "Mahala" and result = "Eunice" + or + this = "Margaret" and result = "Cynthia" + or + this = "Matthew" and result = "Cyrus" + or + this = "Matthew" and result = "Helen" + or + this = "Maya" and result = "Meera" + or + this = "Melinda" and result = "Rafiki" + or + this = "Melissa" and result = "Mahala" + or + this = "Melissa" and result = "Walter" + or + this = "Nala" and result = "Bruce" + or + this = "Nelson" and result = "Mahala" + or + this = "Nelson" and result = "Walter" + or + this = "Noah" and result = "Eli" + or + this = "Olive" and result = "Reuben" + or + this = "Olive" and result = "Lucretia" + or + this = "Oliver" and result = "Matthew" + or + this = "Perry" and result = "Leonard" + or + this = "Ravi" and result = "Dina" + or + this = "Simba" and result = "Will" + or + this = "Simon" and result = "Margaret" + or + this = "Sullivan" and result = "Cornelius" + or + this = "Sylvester" and result = "Timothy" + or + this = "Theodore" and result = "Susannah" + or + this = "Tiana" and result = "Jo" + or + this = "Virginia" and result = "Helen" + or + this = "Warren" and result = "Shenzi" + or + this = "Wesley" and result = "Warren" + or + this = "Wesley" and result = "Jo" + or + this = "Will" and result = "Eli" + } + + /** Holds if the person is allowed in the region. Initially, all villagers are allowed in every region. */ + predicate isAllowedIn(string region) { + region = "north" or + region = "south" or + region = "east" or + region = "west" + } +} + +/** Returns a parent of the person. */ +Person parentOf(Person p) { result = p.getAParent() } diff --git a/java/ql/src/Advisory/Documentation/SpuriousJavadocParam.java b/java/ql/src/Advisory/Documentation/SpuriousJavadocParam.java index 20513b19a9c..bef5de55717 100644 --- a/java/ql/src/Advisory/Documentation/SpuriousJavadocParam.java +++ b/java/ql/src/Advisory/Documentation/SpuriousJavadocParam.java @@ -44,7 +44,7 @@ public void parameterized(T parameter){ ... } * * @param The type of the elements. */ -class Generic { ...} +class Generic { ... } /** * GOOD: A proper Javadoc comment. diff --git a/java/ql/src/AlertSuppression.ql b/java/ql/src/AlertSuppression.ql index 925e377198c..77eea91d819 100644 --- a/java/ql/src/AlertSuppression.ql +++ b/java/ql/src/AlertSuppression.ql @@ -64,7 +64,7 @@ class SuppressionScope extends @javadoc { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/java/ql/src/AlertSuppressionAnnotations.ql b/java/ql/src/AlertSuppressionAnnotations.ql index 5d477a59675..4f9ad26ce60 100644 --- a/java/ql/src/AlertSuppressionAnnotations.ql +++ b/java/ql/src/AlertSuppressionAnnotations.ql @@ -75,7 +75,7 @@ class SuppressionScope extends @annotation { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/java/ql/src/Compatibility/JDK9/JdkInternalAccess.ql b/java/ql/src/Compatibility/JDK9/JdkInternalAccess.ql index aef37497735..15a3170cab3 100644 --- a/java/ql/src/Compatibility/JDK9/JdkInternalAccess.ql +++ b/java/ql/src/Compatibility/JDK9/JdkInternalAccess.ql @@ -111,16 +111,11 @@ predicate jdkPackage(Package p) { p.getName() = pkgName or p.getName().prefix(pkgName.length() + 1) = pkgName + "." | - pkgName = "com.sun" or - pkgName = "sun" or - pkgName = "java" or - pkgName = "javax" or - pkgName = "com.oracle.net" or - pkgName = "genstubs" or - pkgName = "jdk" or - pkgName = "build.tools" or - pkgName = "org.omg.CORBA" or - pkgName = "org.ietf.jgss" + pkgName = + [ + "com.sun", "sun", "java", "javax", "com.oracle.net", "genstubs", "jdk", "build.tools", + "org.omg.CORBA", "org.ietf.jgss" + ] ) } diff --git a/java/ql/src/Compatibility/JDK9/JdkInternals.qll b/java/ql/src/Compatibility/JDK9/JdkInternals.qll index 4cdda13a6a7..b74acd6f5e6 100644 --- a/java/ql/src/Compatibility/JDK9/JdkInternals.qll +++ b/java/ql/src/Compatibility/JDK9/JdkInternals.qll @@ -8,1032 +8,482 @@ * https://hg.openjdk.java.net/jdk9/jdk9/langtools/file/6ba2130e87bd/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdk8_internals.txt */ predicate jdkInternalApi(string p) { - p = "apple.applescript" or - p = "apple.laf" or - p = "apple.launcher" or - p = "apple.security" or - p = "com.apple.concurrent" or - p = "com.apple.eawt" or - p = "com.apple.eawt.event" or - p = "com.apple.eio" or - p = "com.apple.laf" or - p = "com.apple.laf.resources" or - p = "com.oracle.jrockit.jfr" or - p = "com.oracle.jrockit.jfr.client" or - p = "com.oracle.jrockit.jfr.management" or - p = "com.oracle.security.ucrypto" or - p = "com.oracle.util" or - p = "com.oracle.webservices.internal.api" or - p = "com.oracle.webservices.internal.api.databinding" or - p = "com.oracle.webservices.internal.api.message" or - p = "com.oracle.webservices.internal.impl.encoding" or - p = "com.oracle.webservices.internal.impl.internalspi.encoding" or - p = "com.oracle.xmlns.internal.webservices.jaxws_databinding" or - p = "com.sun.accessibility.internal.resources" or - p = "com.sun.activation.registries" or - p = "com.sun.awt" or - p = "com.sun.beans" or - p = "com.sun.beans.decoder" or - p = "com.sun.beans.editors" or - p = "com.sun.beans.finder" or - p = "com.sun.beans.infos" or - p = "com.sun.beans.util" or - p = "com.sun.codemodel.internal" or - p = "com.sun.codemodel.internal.fmt" or - p = "com.sun.codemodel.internal.util" or - p = "com.sun.codemodel.internal.writer" or - p = "com.sun.corba.se.impl.activation" or - p = "com.sun.corba.se.impl.copyobject" or - p = "com.sun.corba.se.impl.corba" or - p = "com.sun.corba.se.impl.dynamicany" or - p = "com.sun.corba.se.impl.encoding" or - p = "com.sun.corba.se.impl.interceptors" or - p = "com.sun.corba.se.impl.io" or - p = "com.sun.corba.se.impl.ior" or - p = "com.sun.corba.se.impl.ior.iiop" or - p = "com.sun.corba.se.impl.javax.rmi" or - p = "com.sun.corba.se.impl.javax.rmi.CORBA" or - p = "com.sun.corba.se.impl.legacy.connection" or - p = "com.sun.corba.se.impl.logging" or - p = "com.sun.corba.se.impl.monitoring" or - p = "com.sun.corba.se.impl.naming.cosnaming" or - p = "com.sun.corba.se.impl.naming.namingutil" or - p = "com.sun.corba.se.impl.naming.pcosnaming" or - p = "com.sun.corba.se.impl.oa" or - p = "com.sun.corba.se.impl.oa.poa" or - p = "com.sun.corba.se.impl.oa.toa" or - p = "com.sun.corba.se.impl.orb" or - p = "com.sun.corba.se.impl.orbutil" or - p = "com.sun.corba.se.impl.orbutil.closure" or - p = "com.sun.corba.se.impl.orbutil.concurrent" or - p = "com.sun.corba.se.impl.orbutil.fsm" or - p = "com.sun.corba.se.impl.orbutil.graph" or - p = "com.sun.corba.se.impl.orbutil.threadpool" or - p = "com.sun.corba.se.impl.presentation.rmi" or - p = "com.sun.corba.se.impl.protocol" or - p = "com.sun.corba.se.impl.protocol.giopmsgheaders" or - p = "com.sun.corba.se.impl.resolver" or - p = "com.sun.corba.se.impl.transport" or - p = "com.sun.corba.se.impl.util" or - p = "com.sun.corba.se.internal.CosNaming" or - p = "com.sun.corba.se.internal.Interceptors" or - p = "com.sun.corba.se.internal.POA" or - p = "com.sun.corba.se.internal.corba" or - p = "com.sun.corba.se.internal.iiop" or - p = "com.sun.corba.se.org.omg.CORBA" or - p = "com.sun.corba.se.pept.broker" or - p = "com.sun.corba.se.pept.encoding" or - p = "com.sun.corba.se.pept.protocol" or - p = "com.sun.corba.se.pept.transport" or - p = "com.sun.corba.se.spi.activation" or - p = "com.sun.corba.se.spi.activation.InitialNameServicePackage" or - p = "com.sun.corba.se.spi.activation.LocatorPackage" or - p = "com.sun.corba.se.spi.activation.RepositoryPackage" or - p = "com.sun.corba.se.spi.copyobject" or - p = "com.sun.corba.se.spi.encoding" or - p = "com.sun.corba.se.spi.extension" or - p = "com.sun.corba.se.spi.ior" or - p = "com.sun.corba.se.spi.ior.iiop" or - p = "com.sun.corba.se.spi.legacy.connection" or - p = "com.sun.corba.se.spi.legacy.interceptor" or - p = "com.sun.corba.se.spi.logging" or - p = "com.sun.corba.se.spi.monitoring" or - p = "com.sun.corba.se.spi.oa" or - p = "com.sun.corba.se.spi.orb" or - p = "com.sun.corba.se.spi.orbutil.closure" or - p = "com.sun.corba.se.spi.orbutil.fsm" or - p = "com.sun.corba.se.spi.orbutil.proxy" or - p = "com.sun.corba.se.spi.orbutil.threadpool" or - p = "com.sun.corba.se.spi.presentation.rmi" or - p = "com.sun.corba.se.spi.protocol" or - p = "com.sun.corba.se.spi.resolver" or - p = "com.sun.corba.se.spi.servicecontext" or - p = "com.sun.corba.se.spi.transport" or - p = "com.sun.crypto.provider" or - p = "com.sun.demo.jvmti.hprof" or - p = "com.sun.deploy.uitoolkit.impl.fx" or - p = "com.sun.deploy.uitoolkit.impl.fx.ui" or - p = "com.sun.deploy.uitoolkit.impl.fx.ui.resources" or - p = "com.sun.glass.events" or - p = "com.sun.glass.events.mac" or - p = "com.sun.glass.ui" or - p = "com.sun.glass.ui.delegate" or - p = "com.sun.glass.ui.gtk" or - p = "com.sun.glass.ui.mac" or - p = "com.sun.glass.ui.win" or - p = "com.sun.glass.utils" or - p = "com.sun.image.codec.jpeg" or - p = "com.sun.imageio.plugins.bmp" or - p = "com.sun.imageio.plugins.common" or - p = "com.sun.imageio.plugins.gif" or - p = "com.sun.imageio.plugins.jpeg" or - p = "com.sun.imageio.plugins.png" or - p = "com.sun.imageio.plugins.wbmp" or - p = "com.sun.imageio.spi" or - p = "com.sun.imageio.stream" or - p = "com.sun.istack.internal" or - p = "com.sun.istack.internal.localization" or - p = "com.sun.istack.internal.logging" or - p = "com.sun.istack.internal.tools" or - p = "com.sun.java.accessibility" or - p = "com.sun.java.accessibility.util.java.awt" or - p = "com.sun.java.browser.dom" or - p = "com.sun.java.browser.net" or - p = "com.sun.java.swing" or - p = "com.sun.java.swing.plaf.gtk" or - p = "com.sun.java.swing.plaf.gtk.resources" or - p = "com.sun.java.swing.plaf.motif" or - p = "com.sun.java.swing.plaf.motif.resources" or - p = "com.sun.java.swing.plaf.nimbus" or - p = "com.sun.java.swing.plaf.windows" or - p = "com.sun.java.swing.plaf.windows.resources" or - p = "com.sun.java.util.jar.pack" or - p = "com.sun.java_cup.internal.runtime" or - p = "com.sun.javafx" or - p = "com.sun.javafx.animation" or - p = "com.sun.javafx.applet" or - p = "com.sun.javafx.application" or - p = "com.sun.javafx.beans" or - p = "com.sun.javafx.beans.event" or - p = "com.sun.javafx.binding" or - p = "com.sun.javafx.charts" or - p = "com.sun.javafx.collections" or - p = "com.sun.javafx.css" or - p = "com.sun.javafx.css.converters" or - p = "com.sun.javafx.css.parser" or - p = "com.sun.javafx.cursor" or - p = "com.sun.javafx.effect" or - p = "com.sun.javafx.embed" or - p = "com.sun.javafx.event" or - p = "com.sun.javafx.font" or - p = "com.sun.javafx.font.coretext" or - p = "com.sun.javafx.font.directwrite" or - p = "com.sun.javafx.font.freetype" or - p = "com.sun.javafx.font.t2k" or - p = "com.sun.javafx.fxml" or - p = "com.sun.javafx.fxml.builder" or - p = "com.sun.javafx.fxml.expression" or - p = "com.sun.javafx.geom" or - p = "com.sun.javafx.geom.transform" or - p = "com.sun.javafx.geometry" or - p = "com.sun.javafx.iio" or - p = "com.sun.javafx.iio.bmp" or - p = "com.sun.javafx.iio.common" or - p = "com.sun.javafx.iio.gif" or - p = "com.sun.javafx.iio.ios" or - p = "com.sun.javafx.iio.jpeg" or - p = "com.sun.javafx.iio.png" or - p = "com.sun.javafx.image" or - p = "com.sun.javafx.image.impl" or - p = "com.sun.javafx.jmx" or - p = "com.sun.javafx.logging" or - p = "com.sun.javafx.media" or - p = "com.sun.javafx.menu" or - p = "com.sun.javafx.perf" or - p = "com.sun.javafx.print" or - p = "com.sun.javafx.property" or - p = "com.sun.javafx.property.adapter" or - p = "com.sun.javafx.robot" or - p = "com.sun.javafx.robot.impl" or - p = "com.sun.javafx.runtime" or - p = "com.sun.javafx.runtime.async" or - p = "com.sun.javafx.runtime.eula" or - p = "com.sun.javafx.scene" or - p = "com.sun.javafx.scene.control" or - p = "com.sun.javafx.scene.control.behavior" or - p = "com.sun.javafx.scene.control.skin" or - p = "com.sun.javafx.scene.control.skin.resources" or - p = "com.sun.javafx.scene.input" or - p = "com.sun.javafx.scene.layout.region" or - p = "com.sun.javafx.scene.paint" or - p = "com.sun.javafx.scene.shape" or - p = "com.sun.javafx.scene.text" or - p = "com.sun.javafx.scene.transform" or - p = "com.sun.javafx.scene.traversal" or - p = "com.sun.javafx.scene.web" or - p = "com.sun.javafx.scene.web.behavior" or - p = "com.sun.javafx.scene.web.skin" or - p = "com.sun.javafx.sg.prism" or - p = "com.sun.javafx.sg.prism.web" or - p = "com.sun.javafx.stage" or - p = "com.sun.javafx.text" or - p = "com.sun.javafx.tk" or - p = "com.sun.javafx.tk.quantum" or - p = "com.sun.javafx.util" or - p = "com.sun.javafx.webkit" or - p = "com.sun.javafx.webkit.drt" or - p = "com.sun.javafx.webkit.prism" or - p = "com.sun.javafx.webkit.prism.theme" or - p = "com.sun.javafx.webkit.theme" or - p = "com.sun.jmx.defaults" or - p = "com.sun.jmx.interceptor" or - p = "com.sun.jmx.mbeanserver" or - p = "com.sun.jmx.remote.internal" or - p = "com.sun.jmx.remote.protocol.iiop" or - p = "com.sun.jmx.remote.protocol.rmi" or - p = "com.sun.jmx.remote.security" or - p = "com.sun.jmx.remote.util" or - p = "com.sun.jmx.snmp" or - p = "com.sun.jmx.snmp.IPAcl" or - p = "com.sun.jmx.snmp.agent" or - p = "com.sun.jmx.snmp.daemon" or - p = "com.sun.jmx.snmp.defaults" or - p = "com.sun.jmx.snmp.internal" or - p = "com.sun.jmx.snmp.mpm" or - p = "com.sun.jmx.snmp.tasks" or - p = "com.sun.jndi.cosnaming" or - p = "com.sun.jndi.dns" or - p = "com.sun.jndi.ldap" or - p = "com.sun.jndi.ldap.ext" or - p = "com.sun.jndi.ldap.pool" or - p = "com.sun.jndi.ldap.sasl" or - p = "com.sun.jndi.rmi.registry" or - p = "com.sun.jndi.toolkit.corba" or - p = "com.sun.jndi.toolkit.ctx" or - p = "com.sun.jndi.toolkit.dir" or - p = "com.sun.jndi.toolkit.url" or - p = "com.sun.jndi.url.corbaname" or - p = "com.sun.jndi.url.dns" or - p = "com.sun.jndi.url.iiop" or - p = "com.sun.jndi.url.iiopname" or - p = "com.sun.jndi.url.ldap" or - p = "com.sun.jndi.url.ldaps" or - p = "com.sun.jndi.url.rmi" or - p = "com.sun.management.jmx" or - p = "com.sun.media.jfxmedia" or - p = "com.sun.media.jfxmedia.control" or - p = "com.sun.media.jfxmedia.effects" or - p = "com.sun.media.jfxmedia.events" or - p = "com.sun.media.jfxmedia.locator" or - p = "com.sun.media.jfxmedia.logging" or - p = "com.sun.media.jfxmedia.track" or - p = "com.sun.media.jfxmediaimpl" or - p = "com.sun.media.jfxmediaimpl.platform" or - p = "com.sun.media.jfxmediaimpl.platform.gstreamer" or - p = "com.sun.media.jfxmediaimpl.platform.ios" or - p = "com.sun.media.jfxmediaimpl.platform.java" or - p = "com.sun.media.jfxmediaimpl.platform.osx" or - p = "com.sun.media.sound" or - p = "com.sun.naming.internal" or - p = "com.sun.net.ssl" or - p = "com.sun.net.ssl.internal.ssl" or - p = "com.sun.net.ssl.internal.www.protocol.https" or - p = "com.sun.nio.file" or - p = "com.sun.nio.zipfs" or - p = "com.sun.openpisces" or - p = "com.sun.org.apache.bcel.internal" or - p = "com.sun.org.apache.bcel.internal.classfile" or - p = "com.sun.org.apache.bcel.internal.generic" or - p = "com.sun.org.apache.bcel.internal.util" or - p = "com.sun.org.apache.regexp.internal" or - p = "com.sun.org.apache.xalan.internal" or - p = "com.sun.org.apache.xalan.internal.extensions" or - p = "com.sun.org.apache.xalan.internal.lib" or - p = "com.sun.org.apache.xalan.internal.res" or - p = "com.sun.org.apache.xalan.internal.templates" or - p = "com.sun.org.apache.xalan.internal.utils" or - p = "com.sun.org.apache.xalan.internal.xslt" or - p = "com.sun.org.apache.xalan.internal.xsltc" or - p = "com.sun.org.apache.xalan.internal.xsltc.cmdline" or - p = "com.sun.org.apache.xalan.internal.xsltc.cmdline.getopt" or - p = "com.sun.org.apache.xalan.internal.xsltc.compiler" or - p = "com.sun.org.apache.xalan.internal.xsltc.compiler.util" or - p = "com.sun.org.apache.xalan.internal.xsltc.dom" or - p = "com.sun.org.apache.xalan.internal.xsltc.runtime" or - p = "com.sun.org.apache.xalan.internal.xsltc.runtime.output" or - p = "com.sun.org.apache.xalan.internal.xsltc.trax" or - p = "com.sun.org.apache.xalan.internal.xsltc.util" or - p = "com.sun.org.apache.xerces.internal.dom" or - p = "com.sun.org.apache.xerces.internal.dom.events" or - p = "com.sun.org.apache.xerces.internal.impl" or - p = "com.sun.org.apache.xerces.internal.impl.dtd" or - p = "com.sun.org.apache.xerces.internal.impl.dtd.models" or - p = "com.sun.org.apache.xerces.internal.impl.dv" or - p = "com.sun.org.apache.xerces.internal.impl.dv.dtd" or - p = "com.sun.org.apache.xerces.internal.impl.dv.util" or - p = "com.sun.org.apache.xerces.internal.impl.dv.xs" or - p = "com.sun.org.apache.xerces.internal.impl.io" or - p = "com.sun.org.apache.xerces.internal.impl.msg" or - p = "com.sun.org.apache.xerces.internal.impl.validation" or - p = "com.sun.org.apache.xerces.internal.impl.xpath" or - p = "com.sun.org.apache.xerces.internal.impl.xpath.regex" or - p = "com.sun.org.apache.xerces.internal.impl.xs" or - p = "com.sun.org.apache.xerces.internal.impl.xs.identity" or - p = "com.sun.org.apache.xerces.internal.impl.xs.models" or - p = "com.sun.org.apache.xerces.internal.impl.xs.opti" or - p = "com.sun.org.apache.xerces.internal.impl.xs.traversers" or - p = "com.sun.org.apache.xerces.internal.impl.xs.util" or - p = "com.sun.org.apache.xerces.internal.jaxp" or - p = "com.sun.org.apache.xerces.internal.jaxp.datatype" or - p = "com.sun.org.apache.xerces.internal.jaxp.validation" or - p = "com.sun.org.apache.xerces.internal.parsers" or - p = "com.sun.org.apache.xerces.internal.util" or - p = "com.sun.org.apache.xerces.internal.utils" or - p = "com.sun.org.apache.xerces.internal.xinclude" or - p = "com.sun.org.apache.xerces.internal.xni" or - p = "com.sun.org.apache.xerces.internal.xni.grammars" or - p = "com.sun.org.apache.xerces.internal.xni.parser" or - p = "com.sun.org.apache.xerces.internal.xpointer" or - p = "com.sun.org.apache.xerces.internal.xs" or - p = "com.sun.org.apache.xerces.internal.xs.datatypes" or - p = "com.sun.org.apache.xml.internal.dtm" or - p = "com.sun.org.apache.xml.internal.dtm.ref" or - p = "com.sun.org.apache.xml.internal.dtm.ref.dom2dtm" or - p = "com.sun.org.apache.xml.internal.dtm.ref.sax2dtm" or - p = "com.sun.org.apache.xml.internal.res" or - p = "com.sun.org.apache.xml.internal.resolver" or - p = "com.sun.org.apache.xml.internal.resolver.helpers" or - p = "com.sun.org.apache.xml.internal.resolver.readers" or - p = "com.sun.org.apache.xml.internal.resolver.tools" or - p = "com.sun.org.apache.xml.internal.security" or - p = "com.sun.org.apache.xml.internal.security.algorithms" or - p = "com.sun.org.apache.xml.internal.security.algorithms.implementations" or - p = "com.sun.org.apache.xml.internal.security.c14n" or - p = "com.sun.org.apache.xml.internal.security.c14n.helper" or - p = "com.sun.org.apache.xml.internal.security.c14n.implementations" or - p = "com.sun.org.apache.xml.internal.security.encryption" or - p = "com.sun.org.apache.xml.internal.security.exceptions" or - p = "com.sun.org.apache.xml.internal.security.keys" or - p = "com.sun.org.apache.xml.internal.security.keys.content" or - p = "com.sun.org.apache.xml.internal.security.keys.content.keyvalues" or - p = "com.sun.org.apache.xml.internal.security.keys.content.x509" or - p = "com.sun.org.apache.xml.internal.security.keys.keyresolver" or - p = "com.sun.org.apache.xml.internal.security.keys.keyresolver.implementations" or - p = "com.sun.org.apache.xml.internal.security.keys.storage" or - p = "com.sun.org.apache.xml.internal.security.keys.storage.implementations" or - p = "com.sun.org.apache.xml.internal.security.signature" or - p = "com.sun.org.apache.xml.internal.security.signature.reference" or - p = "com.sun.org.apache.xml.internal.security.transforms" or - p = "com.sun.org.apache.xml.internal.security.transforms.implementations" or - p = "com.sun.org.apache.xml.internal.security.transforms.params" or - p = "com.sun.org.apache.xml.internal.security.utils" or - p = "com.sun.org.apache.xml.internal.security.utils.resolver" or - p = "com.sun.org.apache.xml.internal.security.utils.resolver.implementations" or - p = "com.sun.org.apache.xml.internal.serialize" or - p = "com.sun.org.apache.xml.internal.serializer" or - p = "com.sun.org.apache.xml.internal.serializer.utils" or - p = "com.sun.org.apache.xml.internal.utils" or - p = "com.sun.org.apache.xml.internal.utils.res" or - p = "com.sun.org.apache.xpath.internal" or - p = "com.sun.org.apache.xpath.internal.axes" or - p = "com.sun.org.apache.xpath.internal.compiler" or - p = "com.sun.org.apache.xpath.internal.domapi" or - p = "com.sun.org.apache.xpath.internal.functions" or - p = "com.sun.org.apache.xpath.internal.jaxp" or - p = "com.sun.org.apache.xpath.internal.objects" or - p = "com.sun.org.apache.xpath.internal.operations" or - p = "com.sun.org.apache.xpath.internal.patterns" or - p = "com.sun.org.apache.xpath.internal.res" or - p = "com.sun.org.glassfish.external.amx" or - p = "com.sun.org.glassfish.external.arc" or - p = "com.sun.org.glassfish.external.probe.provider" or - p = "com.sun.org.glassfish.external.probe.provider.annotations" or - p = "com.sun.org.glassfish.external.statistics" or - p = "com.sun.org.glassfish.external.statistics.annotations" or - p = "com.sun.org.glassfish.external.statistics.impl" or - p = "com.sun.org.glassfish.gmbal" or - p = "com.sun.org.glassfish.gmbal.util" or - p = "com.sun.org.omg.CORBA" or - p = "com.sun.org.omg.CORBA.ValueDefPackage" or - p = "com.sun.org.omg.CORBA.portable" or - p = "com.sun.org.omg.SendingContext" or - p = "com.sun.org.omg.SendingContext.CodeBasePackage" or - p = "com.sun.pisces" or - p = "com.sun.prism" or - p = "com.sun.prism.d3d" or - p = "com.sun.prism.es2" or - p = "com.sun.prism.image" or - p = "com.sun.prism.impl" or - p = "com.sun.prism.impl.packrect" or - p = "com.sun.prism.impl.paint" or - p = "com.sun.prism.impl.ps" or - p = "com.sun.prism.impl.shape" or - p = "com.sun.prism.j2d" or - p = "com.sun.prism.j2d.paint" or - p = "com.sun.prism.j2d.print" or - p = "com.sun.prism.paint" or - p = "com.sun.prism.ps" or - p = "com.sun.prism.shader" or - p = "com.sun.prism.shape" or - p = "com.sun.prism.sw" or - p = "com.sun.rmi.rmid" or - p = "com.sun.rowset" or - p = "com.sun.rowset.internal" or - p = "com.sun.rowset.providers" or - p = "com.sun.scenario" or - p = "com.sun.scenario.animation" or - p = "com.sun.scenario.animation.shared" or - p = "com.sun.scenario.effect" or - p = "com.sun.scenario.effect.impl" or - p = "com.sun.scenario.effect.impl.es2" or - p = "com.sun.scenario.effect.impl.hw" or - p = "com.sun.scenario.effect.impl.hw.d3d" or - p = "com.sun.scenario.effect.impl.prism" or - p = "com.sun.scenario.effect.impl.prism.ps" or - p = "com.sun.scenario.effect.impl.prism.sw" or - p = "com.sun.scenario.effect.impl.state" or - p = "com.sun.scenario.effect.impl.sw" or - p = "com.sun.scenario.effect.impl.sw.java" or - p = "com.sun.scenario.effect.impl.sw.sse" or - p = "com.sun.scenario.effect.light" or - p = "com.sun.security.cert.internal.x509" or - p = "com.sun.security.ntlm" or - p = "com.sun.security.sasl" or - p = "com.sun.security.sasl.digest" or - p = "com.sun.security.sasl.gsskerb" or - p = "com.sun.security.sasl.ntlm" or - p = "com.sun.security.sasl.util" or - p = "com.sun.swing.internal.plaf.basic.resources" or - p = "com.sun.swing.internal.plaf.metal.resources" or - p = "com.sun.swing.internal.plaf.synth.resources" or - p = "com.sun.tools.classfile" or - p = "com.sun.tools.corba.se.idl" or - p = "com.sun.tools.corba.se.idl.constExpr" or - p = "com.sun.tools.corba.se.idl.som.cff" or - p = "com.sun.tools.corba.se.idl.som.idlemit" or - p = "com.sun.tools.corba.se.idl.toJavaPortable" or - p = "com.sun.tools.doclets.formats.html" or - p = "com.sun.tools.doclets.formats.html.markup" or - p = "com.sun.tools.doclets.formats.html.resources" or - p = "com.sun.tools.doclets.internal.toolkit" or - p = "com.sun.tools.doclets.internal.toolkit.builders" or - p = "com.sun.tools.doclets.internal.toolkit.resources" or - p = "com.sun.tools.doclets.internal.toolkit.taglets" or - p = "com.sun.tools.doclets.internal.toolkit.util" or - p = "com.sun.tools.doclets.internal.toolkit.util.links" or - p = "com.sun.tools.doclint" or - p = "com.sun.tools.doclint.resources" or - p = "com.sun.tools.example.debug.expr" or - p = "com.sun.tools.example.debug.tty" or - p = "com.sun.tools.extcheck" or - p = "com.sun.tools.hat" or - p = "com.sun.tools.hat.internal.model" or - p = "com.sun.tools.hat.internal.oql" or - p = "com.sun.tools.hat.internal.parser" or - p = "com.sun.tools.hat.internal.server" or - p = "com.sun.tools.hat.internal.util" or - p = "com.sun.tools.internal.jxc" or - p = "com.sun.tools.internal.jxc.ap" or - p = "com.sun.tools.internal.jxc.api" or - p = "com.sun.tools.internal.jxc.api.impl.j2s" or - p = "com.sun.tools.internal.jxc.gen.config" or - p = "com.sun.tools.internal.jxc.model.nav" or - p = "com.sun.tools.internal.ws" or - p = "com.sun.tools.internal.ws.api" or - p = "com.sun.tools.internal.ws.api.wsdl" or - p = "com.sun.tools.internal.ws.processor" or - p = "com.sun.tools.internal.ws.processor.generator" or - p = "com.sun.tools.internal.ws.processor.model" or - p = "com.sun.tools.internal.ws.processor.model.exporter" or - p = "com.sun.tools.internal.ws.processor.model.java" or - p = "com.sun.tools.internal.ws.processor.model.jaxb" or - p = "com.sun.tools.internal.ws.processor.modeler" or - p = "com.sun.tools.internal.ws.processor.modeler.annotation" or - p = "com.sun.tools.internal.ws.processor.modeler.wsdl" or - p = "com.sun.tools.internal.ws.processor.util" or - p = "com.sun.tools.internal.ws.resources" or - p = "com.sun.tools.internal.ws.spi" or - p = "com.sun.tools.internal.ws.util" or - p = "com.sun.tools.internal.ws.util.xml" or - p = "com.sun.tools.internal.ws.wscompile" or - p = "com.sun.tools.internal.ws.wscompile.plugin.at_generated" or - p = "com.sun.tools.internal.ws.wsdl.document" or - p = "com.sun.tools.internal.ws.wsdl.document.http" or - p = "com.sun.tools.internal.ws.wsdl.document.jaxws" or - p = "com.sun.tools.internal.ws.wsdl.document.mime" or - p = "com.sun.tools.internal.ws.wsdl.document.schema" or - p = "com.sun.tools.internal.ws.wsdl.document.soap" or - p = "com.sun.tools.internal.ws.wsdl.framework" or - p = "com.sun.tools.internal.ws.wsdl.parser" or - p = "com.sun.tools.internal.xjc" or - p = "com.sun.tools.internal.xjc.addon.accessors" or - p = "com.sun.tools.internal.xjc.addon.at_generated" or - p = "com.sun.tools.internal.xjc.addon.code_injector" or - p = "com.sun.tools.internal.xjc.addon.episode" or - p = "com.sun.tools.internal.xjc.addon.locator" or - p = "com.sun.tools.internal.xjc.addon.sync" or - p = "com.sun.tools.internal.xjc.api" or - p = "com.sun.tools.internal.xjc.api.impl.s2j" or - p = "com.sun.tools.internal.xjc.api.util" or - p = "com.sun.tools.internal.xjc.generator.annotation.spec" or - p = "com.sun.tools.internal.xjc.generator.bean" or - p = "com.sun.tools.internal.xjc.generator.bean.field" or - p = "com.sun.tools.internal.xjc.generator.util" or - p = "com.sun.tools.internal.xjc.model" or - p = "com.sun.tools.internal.xjc.model.nav" or - p = "com.sun.tools.internal.xjc.outline" or - p = "com.sun.tools.internal.xjc.reader" or - p = "com.sun.tools.internal.xjc.reader.dtd" or - p = "com.sun.tools.internal.xjc.reader.dtd.bindinfo" or - p = "com.sun.tools.internal.xjc.reader.gbind" or - p = "com.sun.tools.internal.xjc.reader.internalizer" or - p = "com.sun.tools.internal.xjc.reader.relaxng" or - p = "com.sun.tools.internal.xjc.reader.xmlschema" or - p = "com.sun.tools.internal.xjc.reader.xmlschema.bindinfo" or - p = "com.sun.tools.internal.xjc.reader.xmlschema.ct" or - p = "com.sun.tools.internal.xjc.reader.xmlschema.parser" or - p = "com.sun.tools.internal.xjc.runtime" or - p = "com.sun.tools.internal.xjc.util" or - p = "com.sun.tools.internal.xjc.writer" or - p = "com.sun.tools.javac.api" or - p = "com.sun.tools.javac.code" or - p = "com.sun.tools.javac.comp" or - p = "com.sun.tools.javac.file" or - p = "com.sun.tools.javac.jvm" or - p = "com.sun.tools.javac.main" or - p = "com.sun.tools.javac.model" or - p = "com.sun.tools.javac.nio" or - p = "com.sun.tools.javac.parser" or - p = "com.sun.tools.javac.processing" or - p = "com.sun.tools.javac.resources" or - p = "com.sun.tools.javac.sym" or - p = "com.sun.tools.javac.tree" or - p = "com.sun.tools.javac.util" or - p = "com.sun.tools.javadoc.api" or - p = "com.sun.tools.javadoc.resources" or - p = "com.sun.tools.javah" or - p = "com.sun.tools.javah.resources" or - p = "com.sun.tools.javap" or - p = "com.sun.tools.javap.resources" or - p = "com.sun.tools.jdeps" or - p = "com.sun.tools.jdeps.resources" or - p = "com.sun.tools.jdi" or - p = "com.sun.tools.jdi.resources" or - p = "com.sun.tools.script.shell" or - p = "com.sun.tracing" or - p = "com.sun.tracing.dtrace" or - p = "com.sun.webkit" or - p = "com.sun.webkit.dom" or - p = "com.sun.webkit.event" or - p = "com.sun.webkit.graphics" or - p = "com.sun.webkit.network" or - p = "com.sun.webkit.network.about" or - p = "com.sun.webkit.network.data" or - p = "com.sun.webkit.perf" or - p = "com.sun.webkit.plugin" or - p = "com.sun.webkit.text" or - p = "com.sun.xml.internal.bind" or - p = "com.sun.xml.internal.bind.annotation" or - p = "com.sun.xml.internal.bind.api" or - p = "com.sun.xml.internal.bind.api.impl" or - p = "com.sun.xml.internal.bind.marshaller" or - p = "com.sun.xml.internal.bind.unmarshaller" or - p = "com.sun.xml.internal.bind.util" or - p = "com.sun.xml.internal.bind.v2" or - p = "com.sun.xml.internal.bind.v2.bytecode" or - p = "com.sun.xml.internal.bind.v2.model.annotation" or - p = "com.sun.xml.internal.bind.v2.model.core" or - p = "com.sun.xml.internal.bind.v2.model.impl" or - p = "com.sun.xml.internal.bind.v2.model.nav" or - p = "com.sun.xml.internal.bind.v2.model.runtime" or - p = "com.sun.xml.internal.bind.v2.model.util" or - p = "com.sun.xml.internal.bind.v2.runtime" or - p = "com.sun.xml.internal.bind.v2.runtime.output" or - p = "com.sun.xml.internal.bind.v2.runtime.property" or - p = "com.sun.xml.internal.bind.v2.runtime.reflect" or - p = "com.sun.xml.internal.bind.v2.runtime.reflect.opt" or - p = "com.sun.xml.internal.bind.v2.runtime.unmarshaller" or - p = "com.sun.xml.internal.bind.v2.schemagen" or - p = "com.sun.xml.internal.bind.v2.schemagen.episode" or - p = "com.sun.xml.internal.bind.v2.schemagen.xmlschema" or - p = "com.sun.xml.internal.bind.v2.util" or - p = "com.sun.xml.internal.dtdparser" or - p = "com.sun.xml.internal.fastinfoset" or - p = "com.sun.xml.internal.fastinfoset.algorithm" or - p = "com.sun.xml.internal.fastinfoset.alphabet" or - p = "com.sun.xml.internal.fastinfoset.dom" or - p = "com.sun.xml.internal.fastinfoset.org.apache.xerces.util" or - p = "com.sun.xml.internal.fastinfoset.sax" or - p = "com.sun.xml.internal.fastinfoset.stax" or - p = "com.sun.xml.internal.fastinfoset.stax.events" or - p = "com.sun.xml.internal.fastinfoset.stax.factory" or - p = "com.sun.xml.internal.fastinfoset.stax.util" or - p = "com.sun.xml.internal.fastinfoset.tools" or - p = "com.sun.xml.internal.fastinfoset.util" or - p = "com.sun.xml.internal.fastinfoset.vocab" or - p = "com.sun.xml.internal.messaging.saaj" or - p = "com.sun.xml.internal.messaging.saaj.client.p2p" or - p = "com.sun.xml.internal.messaging.saaj.packaging.mime" or - p = "com.sun.xml.internal.messaging.saaj.packaging.mime.internet" or - p = "com.sun.xml.internal.messaging.saaj.packaging.mime.util" or - p = "com.sun.xml.internal.messaging.saaj.soap" or - p = "com.sun.xml.internal.messaging.saaj.soap.dynamic" or - p = "com.sun.xml.internal.messaging.saaj.soap.impl" or - p = "com.sun.xml.internal.messaging.saaj.soap.name" or - p = "com.sun.xml.internal.messaging.saaj.soap.ver1_1" or - p = "com.sun.xml.internal.messaging.saaj.soap.ver1_2" or - p = "com.sun.xml.internal.messaging.saaj.util" or - p = "com.sun.xml.internal.messaging.saaj.util.transform" or - p = "com.sun.xml.internal.org.jvnet.fastinfoset" or - p = "com.sun.xml.internal.org.jvnet.fastinfoset.sax" or - p = "com.sun.xml.internal.org.jvnet.fastinfoset.sax.helpers" or - p = "com.sun.xml.internal.org.jvnet.fastinfoset.stax" or - p = "com.sun.xml.internal.org.jvnet.mimepull" or - p = "com.sun.xml.internal.org.jvnet.staxex" or - p = "com.sun.xml.internal.rngom.ast.builder" or - p = "com.sun.xml.internal.rngom.ast.om" or - p = "com.sun.xml.internal.rngom.ast.util" or - p = "com.sun.xml.internal.rngom.binary" or - p = "com.sun.xml.internal.rngom.binary.visitor" or - p = "com.sun.xml.internal.rngom.digested" or - p = "com.sun.xml.internal.rngom.dt" or - p = "com.sun.xml.internal.rngom.dt.builtin" or - p = "com.sun.xml.internal.rngom.nc" or - p = "com.sun.xml.internal.rngom.parse" or - p = "com.sun.xml.internal.rngom.parse.compact" or - p = "com.sun.xml.internal.rngom.parse.host" or - p = "com.sun.xml.internal.rngom.parse.xml" or - p = "com.sun.xml.internal.rngom.util" or - p = "com.sun.xml.internal.rngom.xml.sax" or - p = "com.sun.xml.internal.rngom.xml.util" or - p = "com.sun.xml.internal.stream" or - p = "com.sun.xml.internal.stream.buffer" or - p = "com.sun.xml.internal.stream.buffer.sax" or - p = "com.sun.xml.internal.stream.buffer.stax" or - p = "com.sun.xml.internal.stream.dtd" or - p = "com.sun.xml.internal.stream.dtd.nonvalidating" or - p = "com.sun.xml.internal.stream.events" or - p = "com.sun.xml.internal.stream.util" or - p = "com.sun.xml.internal.stream.writers" or - p = "com.sun.xml.internal.txw2" or - p = "com.sun.xml.internal.txw2.annotation" or - p = "com.sun.xml.internal.txw2.output" or - p = "com.sun.xml.internal.ws" or - p = "com.sun.xml.internal.ws.addressing" or - p = "com.sun.xml.internal.ws.addressing.model" or - p = "com.sun.xml.internal.ws.addressing.policy" or - p = "com.sun.xml.internal.ws.addressing.v200408" or - p = "com.sun.xml.internal.ws.api" or - p = "com.sun.xml.internal.ws.api.addressing" or - p = "com.sun.xml.internal.ws.api.client" or - p = "com.sun.xml.internal.ws.api.config.management" or - p = "com.sun.xml.internal.ws.api.config.management.policy" or - p = "com.sun.xml.internal.ws.api.databinding" or - p = "com.sun.xml.internal.ws.api.fastinfoset" or - p = "com.sun.xml.internal.ws.api.ha" or - p = "com.sun.xml.internal.ws.api.handler" or - p = "com.sun.xml.internal.ws.api.message" or - p = "com.sun.xml.internal.ws.api.message.saaj" or - p = "com.sun.xml.internal.ws.api.message.stream" or - p = "com.sun.xml.internal.ws.api.model" or - p = "com.sun.xml.internal.ws.api.model.soap" or - p = "com.sun.xml.internal.ws.api.model.wsdl" or - p = "com.sun.xml.internal.ws.api.model.wsdl.editable" or - p = "com.sun.xml.internal.ws.api.pipe" or - p = "com.sun.xml.internal.ws.api.pipe.helper" or - p = "com.sun.xml.internal.ws.api.policy" or - p = "com.sun.xml.internal.ws.api.policy.subject" or - p = "com.sun.xml.internal.ws.api.server" or - p = "com.sun.xml.internal.ws.api.streaming" or - p = "com.sun.xml.internal.ws.api.wsdl.parser" or - p = "com.sun.xml.internal.ws.api.wsdl.writer" or - p = "com.sun.xml.internal.ws.assembler" or - p = "com.sun.xml.internal.ws.assembler.dev" or - p = "com.sun.xml.internal.ws.assembler.jaxws" or - p = "com.sun.xml.internal.ws.binding" or - p = "com.sun.xml.internal.ws.client" or - p = "com.sun.xml.internal.ws.client.dispatch" or - p = "com.sun.xml.internal.ws.client.sei" or - p = "com.sun.xml.internal.ws.commons.xmlutil" or - p = "com.sun.xml.internal.ws.config.management.policy" or - p = "com.sun.xml.internal.ws.config.metro.dev" or - p = "com.sun.xml.internal.ws.config.metro.util" or - p = "com.sun.xml.internal.ws.db" or - p = "com.sun.xml.internal.ws.db.glassfish" or - p = "com.sun.xml.internal.ws.developer" or - p = "com.sun.xml.internal.ws.dump" or - p = "com.sun.xml.internal.ws.encoding" or - p = "com.sun.xml.internal.ws.encoding.fastinfoset" or - p = "com.sun.xml.internal.ws.encoding.policy" or - p = "com.sun.xml.internal.ws.encoding.soap" or - p = "com.sun.xml.internal.ws.encoding.soap.streaming" or - p = "com.sun.xml.internal.ws.encoding.xml" or - p = "com.sun.xml.internal.ws.fault" or - p = "com.sun.xml.internal.ws.handler" or - p = "com.sun.xml.internal.ws.message" or - p = "com.sun.xml.internal.ws.message.jaxb" or - p = "com.sun.xml.internal.ws.message.saaj" or - p = "com.sun.xml.internal.ws.message.source" or - p = "com.sun.xml.internal.ws.message.stream" or - p = "com.sun.xml.internal.ws.model" or - p = "com.sun.xml.internal.ws.model.soap" or - p = "com.sun.xml.internal.ws.model.wsdl" or - p = "com.sun.xml.internal.ws.org.objectweb.asm" or - p = "com.sun.xml.internal.ws.policy" or - p = "com.sun.xml.internal.ws.policy.jaxws" or - p = "com.sun.xml.internal.ws.policy.jaxws.spi" or - p = "com.sun.xml.internal.ws.policy.privateutil" or - p = "com.sun.xml.internal.ws.policy.sourcemodel" or - p = "com.sun.xml.internal.ws.policy.sourcemodel.attach" or - p = "com.sun.xml.internal.ws.policy.sourcemodel.wspolicy" or - p = "com.sun.xml.internal.ws.policy.spi" or - p = "com.sun.xml.internal.ws.policy.subject" or - p = "com.sun.xml.internal.ws.protocol.soap" or - p = "com.sun.xml.internal.ws.protocol.xml" or - p = "com.sun.xml.internal.ws.resources" or - p = "com.sun.xml.internal.ws.runtime.config" or - p = "com.sun.xml.internal.ws.server" or - p = "com.sun.xml.internal.ws.server.provider" or - p = "com.sun.xml.internal.ws.server.sei" or - p = "com.sun.xml.internal.ws.spi" or - p = "com.sun.xml.internal.ws.spi.db" or - p = "com.sun.xml.internal.ws.streaming" or - p = "com.sun.xml.internal.ws.transport" or - p = "com.sun.xml.internal.ws.transport.http" or - p = "com.sun.xml.internal.ws.transport.http.client" or - p = "com.sun.xml.internal.ws.transport.http.server" or - p = "com.sun.xml.internal.ws.util" or - p = "com.sun.xml.internal.ws.util.exception" or - p = "com.sun.xml.internal.ws.util.pipe" or - p = "com.sun.xml.internal.ws.util.xml" or - p = "com.sun.xml.internal.ws.wsdl" or - p = "com.sun.xml.internal.ws.wsdl.parser" or - p = "com.sun.xml.internal.ws.wsdl.writer" or - p = "com.sun.xml.internal.ws.wsdl.writer.document" or - p = "com.sun.xml.internal.ws.wsdl.writer.document.http" or - p = "com.sun.xml.internal.ws.wsdl.writer.document.soap" or - p = "com.sun.xml.internal.ws.wsdl.writer.document.soap12" or - p = "com.sun.xml.internal.ws.wsdl.writer.document.xsd" or - p = "com.sun.xml.internal.xsom" or - p = "com.sun.xml.internal.xsom.impl" or - p = "com.sun.xml.internal.xsom.impl.parser" or - p = "com.sun.xml.internal.xsom.impl.parser.state" or - p = "com.sun.xml.internal.xsom.impl.scd" or - p = "com.sun.xml.internal.xsom.impl.util" or - p = "com.sun.xml.internal.xsom.parser" or - p = "com.sun.xml.internal.xsom.util" or - p = "com.sun.xml.internal.xsom.visitor" or - p = "java.awt.dnd.peer" or - p = "java.awt.peer" or - p = "javafx.embed.swt" or - p = "jdk" or - p = "jdk.internal.cmm" or - p = "jdk.internal.dynalink" or - p = "jdk.internal.dynalink.beans" or - p = "jdk.internal.dynalink.linker" or - p = "jdk.internal.dynalink.support" or - p = "jdk.internal.instrumentation" or - p = "jdk.internal.org.objectweb.asm" or - p = "jdk.internal.org.objectweb.asm.commons" or - p = "jdk.internal.org.objectweb.asm.signature" or - p = "jdk.internal.org.objectweb.asm.tree" or - p = "jdk.internal.org.objectweb.asm.tree.analysis" or - p = "jdk.internal.org.objectweb.asm.util" or - p = "jdk.internal.org.xml.sax" or - p = "jdk.internal.org.xml.sax.helpers" or - p = "jdk.internal.util.xml" or - p = "jdk.internal.util.xml.impl" or - p = "jdk.jfr.events" or - p = "jdk.management.resource.internal" or - p = "jdk.management.resource.internal.inst" or - p = "jdk.nashorn.internal" or - p = "jdk.nashorn.internal.codegen" or - p = "jdk.nashorn.internal.codegen.types" or - p = "jdk.nashorn.internal.ir" or - p = "jdk.nashorn.internal.ir.annotations" or - p = "jdk.nashorn.internal.ir.debug" or - p = "jdk.nashorn.internal.ir.visitor" or - p = "jdk.nashorn.internal.lookup" or - p = "jdk.nashorn.internal.objects" or - p = "jdk.nashorn.internal.objects.annotations" or - p = "jdk.nashorn.internal.parser" or - p = "jdk.nashorn.internal.runtime" or - p = "jdk.nashorn.internal.runtime.arrays" or - p = "jdk.nashorn.internal.runtime.events" or - p = "jdk.nashorn.internal.runtime.linker" or - p = "jdk.nashorn.internal.runtime.logging" or - p = "jdk.nashorn.internal.runtime.options" or - p = "jdk.nashorn.internal.runtime.regexp" or - p = "jdk.nashorn.internal.runtime.regexp.joni" or - p = "jdk.nashorn.internal.runtime.regexp.joni.ast" or - p = "jdk.nashorn.internal.runtime.regexp.joni.constants" or - p = "jdk.nashorn.internal.runtime.regexp.joni.encoding" or - p = "jdk.nashorn.internal.runtime.regexp.joni.exception" or - p = "jdk.nashorn.internal.scripts" or - p = "jdk.nashorn.tools" or - p = "oracle.jrockit.jfr" or - p = "oracle.jrockit.jfr.events" or - p = "oracle.jrockit.jfr.jdkevents" or - p = "oracle.jrockit.jfr.jdkevents.throwabletransform" or - p = "oracle.jrockit.jfr.openmbean" or - p = "oracle.jrockit.jfr.parser" or - p = "oracle.jrockit.jfr.settings" or - p = "oracle.jrockit.jfr.tools" or - p = "org.jcp.xml.dsig.internal" or - p = "org.jcp.xml.dsig.internal.dom" or - p = "org.omg.stub.javax.management.remote.rmi" or - p = "org.relaxng.datatype" or - p = "org.relaxng.datatype.helpers" or - p = "sun.applet" or - p = "sun.applet.resources" or - p = "sun.audio" or - p = "sun.awt" or - p = "sun.awt.X11" or - p = "sun.awt.datatransfer" or - p = "sun.awt.dnd" or - p = "sun.awt.event" or - p = "sun.awt.geom" or - p = "sun.awt.im" or - p = "sun.awt.image" or - p = "sun.awt.image.codec" or - p = "sun.awt.motif" or - p = "sun.awt.resources" or - p = "sun.awt.shell" or - p = "sun.awt.util" or - p = "sun.awt.windows" or - p = "sun.corba" or - p = "sun.dc" or - p = "sun.dc.path" or - p = "sun.dc.pr" or - p = "sun.font" or - p = "sun.instrument" or - p = "sun.invoke" or - p = "sun.invoke.anon" or - p = "sun.invoke.empty" or - p = "sun.invoke.util" or - p = "sun.io" or - p = "sun.java2d" or - p = "sun.java2d.cmm" or - p = "sun.java2d.cmm.kcms" or - p = "sun.java2d.cmm.lcms" or - p = "sun.java2d.d3d" or - p = "sun.java2d.jules" or - p = "sun.java2d.loops" or - p = "sun.java2d.opengl" or - p = "sun.java2d.pipe" or - p = "sun.java2d.pipe.hw" or - p = "sun.java2d.pisces" or - p = "sun.java2d.windows" or - p = "sun.java2d.x11" or - p = "sun.java2d.xr" or - p = "sun.jvmstat.monitor" or - p = "sun.jvmstat.monitor.event" or - p = "sun.jvmstat.monitor.remote" or - p = "sun.jvmstat.perfdata.monitor" or - p = "sun.jvmstat.perfdata.monitor.protocol.file" or - p = "sun.jvmstat.perfdata.monitor.protocol.local" or - p = "sun.jvmstat.perfdata.monitor.protocol.rmi" or - p = "sun.jvmstat.perfdata.monitor.v1_0" or - p = "sun.jvmstat.perfdata.monitor.v2_0" or - p = "sun.launcher" or - p = "sun.launcher.resources" or - p = "sun.lwawt" or - p = "sun.lwawt.macosx" or - p = "sun.management" or - p = "sun.management.counter" or - p = "sun.management.counter.perf" or - p = "sun.management.jdp" or - p = "sun.management.jmxremote" or - p = "sun.management.resources" or - p = "sun.management.snmp" or - p = "sun.management.snmp.jvminstr" or - p = "sun.management.snmp.jvmmib" or - p = "sun.management.snmp.util" or - p = "sun.misc" or - p = "sun.misc.resources" or - p = "sun.net" or - p = "sun.net.dns" or - p = "sun.net.ftp" or - p = "sun.net.ftp.impl" or - p = "sun.net.httpserver" or - p = "sun.net.idn" or - p = "sun.net.sdp" or - p = "sun.net.smtp" or - p = "sun.net.spi" or - p = "sun.net.spi.nameservice" or - p = "sun.net.spi.nameservice.dns" or - p = "sun.net.util" or - p = "sun.net.www" or - p = "sun.net.www.content.audio" or - p = "sun.net.www.content.image" or - p = "sun.net.www.content.text" or - p = "sun.net.www.http" or - p = "sun.net.www.protocol.file" or - p = "sun.net.www.protocol.ftp" or - p = "sun.net.www.protocol.http" or - p = "sun.net.www.protocol.http.logging" or - p = "sun.net.www.protocol.http.ntlm" or - p = "sun.net.www.protocol.http.spnego" or - p = "sun.net.www.protocol.https" or - p = "sun.net.www.protocol.jar" or - p = "sun.net.www.protocol.mailto" or - p = "sun.net.www.protocol.netdoc" or - p = "sun.nio" or - p = "sun.nio.ch" or - p = "sun.nio.ch.sctp" or - p = "sun.nio.cs" or - p = "sun.nio.cs.ext" or - p = "sun.nio.fs" or - p = "sun.print" or - p = "sun.print.resources" or - p = "sun.reflect" or - p = "sun.reflect.annotation" or - p = "sun.reflect.generics.factory" or - p = "sun.reflect.generics.parser" or - p = "sun.reflect.generics.reflectiveObjects" or - p = "sun.reflect.generics.repository" or - p = "sun.reflect.generics.scope" or - p = "sun.reflect.generics.tree" or - p = "sun.reflect.generics.visitor" or - p = "sun.reflect.misc" or - p = "sun.rmi.log" or - p = "sun.rmi.registry" or - p = "sun.rmi.rmic" or - p = "sun.rmi.rmic.iiop" or - p = "sun.rmi.rmic.newrmic" or - p = "sun.rmi.rmic.newrmic.jrmp" or - p = "sun.rmi.runtime" or - p = "sun.rmi.server" or - p = "sun.rmi.transport" or - p = "sun.rmi.transport.proxy" or - p = "sun.rmi.transport.tcp" or - p = "sun.security.acl" or - p = "sun.security.action" or - p = "sun.security.ec" or - p = "sun.security.internal.interfaces" or - p = "sun.security.internal.spec" or - p = "sun.security.jca" or - p = "sun.security.jgss" or - p = "sun.security.jgss.krb5" or - p = "sun.security.jgss.spi" or - p = "sun.security.jgss.spnego" or - p = "sun.security.jgss.wrapper" or - p = "sun.security.krb5" or - p = "sun.security.krb5.internal" or - p = "sun.security.krb5.internal.ccache" or - p = "sun.security.krb5.internal.crypto" or - p = "sun.security.krb5.internal.crypto.dk" or - p = "sun.security.krb5.internal.ktab" or - p = "sun.security.krb5.internal.rcache" or - p = "sun.security.krb5.internal.tools" or - p = "sun.security.krb5.internal.util" or - p = "sun.security.mscapi" or - p = "sun.security.pkcs" or - p = "sun.security.pkcs10" or - p = "sun.security.pkcs11" or - p = "sun.security.pkcs11.wrapper" or - p = "sun.security.pkcs12" or - p = "sun.security.provider" or - p = "sun.security.provider.certpath" or - p = "sun.security.provider.certpath.ldap" or - p = "sun.security.provider.certpath.ssl" or - p = "sun.security.rsa" or - p = "sun.security.smartcardio" or - p = "sun.security.ssl" or - p = "sun.security.ssl.krb5" or - p = "sun.security.timestamp" or - p = "sun.security.tools" or - p = "sun.security.tools.jarsigner" or - p = "sun.security.tools.keytool" or - p = "sun.security.tools.policytool" or - p = "sun.security.util" or - p = "sun.security.validator" or - p = "sun.security.x509" or - p = "sun.swing" or - p = "sun.swing.icon" or - p = "sun.swing.plaf" or - p = "sun.swing.plaf.synth" or - p = "sun.swing.plaf.windows" or - p = "sun.swing.table" or - p = "sun.swing.text" or - p = "sun.swing.text.html" or - p = "sun.text" or - p = "sun.text.bidi" or - p = "sun.text.normalizer" or - p = "sun.text.resources" or - p = "sun.text.resources.en" or - p = "sun.tools.asm" or - p = "sun.tools.attach" or - p = "sun.tools.jar" or - p = "sun.tools.jar.resources" or - p = "sun.tools.java" or - p = "sun.tools.javac" or - p = "sun.tools.jcmd" or - p = "sun.tools.jconsole" or - p = "sun.tools.jconsole.inspector" or - p = "sun.tools.jinfo" or - p = "sun.tools.jmap" or - p = "sun.tools.jps" or - p = "sun.tools.jstack" or - p = "sun.tools.jstat" or - p = "sun.tools.jstatd" or - p = "sun.tools.native2ascii" or - p = "sun.tools.native2ascii.resources" or - p = "sun.tools.serialver" or - p = "sun.tools.tree" or - p = "sun.tools.util" or - p = "sun.tracing" or - p = "sun.tracing.dtrace" or - p = "sun.usagetracker" or - p = "sun.util" or - p = "sun.util.calendar" or - p = "sun.util.cldr" or - p = "sun.util.locale" or - p = "sun.util.locale.provider" or - p = "sun.util.logging" or - p = "sun.util.logging.resources" or - p = "sun.util.resources" or - p = "sun.util.resources.en" or - p = "sun.util.spi" or - p = "sun.util.xml" + p = + [ + "apple.applescript", "apple.laf", "apple.launcher", "apple.security", "com.apple.concurrent", + "com.apple.eawt", "com.apple.eawt.event", "com.apple.eio", "com.apple.laf", + "com.apple.laf.resources", "com.oracle.jrockit.jfr", "com.oracle.jrockit.jfr.client", + "com.oracle.jrockit.jfr.management", "com.oracle.security.ucrypto", "com.oracle.util", + "com.oracle.webservices.internal.api", "com.oracle.webservices.internal.api.databinding", + "com.oracle.webservices.internal.api.message", + "com.oracle.webservices.internal.impl.encoding", + "com.oracle.webservices.internal.impl.internalspi.encoding", + "com.oracle.xmlns.internal.webservices.jaxws_databinding", + "com.sun.accessibility.internal.resources", "com.sun.activation.registries", "com.sun.awt", + "com.sun.beans", "com.sun.beans.decoder", "com.sun.beans.editors", "com.sun.beans.finder", + "com.sun.beans.infos", "com.sun.beans.util", "com.sun.codemodel.internal", + "com.sun.codemodel.internal.fmt", "com.sun.codemodel.internal.util", + "com.sun.codemodel.internal.writer", "com.sun.corba.se.impl.activation", + "com.sun.corba.se.impl.copyobject", "com.sun.corba.se.impl.corba", + "com.sun.corba.se.impl.dynamicany", "com.sun.corba.se.impl.encoding", + "com.sun.corba.se.impl.interceptors", "com.sun.corba.se.impl.io", "com.sun.corba.se.impl.ior", + "com.sun.corba.se.impl.ior.iiop", "com.sun.corba.se.impl.javax.rmi", + "com.sun.corba.se.impl.javax.rmi.CORBA", "com.sun.corba.se.impl.legacy.connection", + "com.sun.corba.se.impl.logging", "com.sun.corba.se.impl.monitoring", + "com.sun.corba.se.impl.naming.cosnaming", "com.sun.corba.se.impl.naming.namingutil", + "com.sun.corba.se.impl.naming.pcosnaming", "com.sun.corba.se.impl.oa", + "com.sun.corba.se.impl.oa.poa", "com.sun.corba.se.impl.oa.toa", "com.sun.corba.se.impl.orb", + "com.sun.corba.se.impl.orbutil", "com.sun.corba.se.impl.orbutil.closure", + "com.sun.corba.se.impl.orbutil.concurrent", "com.sun.corba.se.impl.orbutil.fsm", + "com.sun.corba.se.impl.orbutil.graph", "com.sun.corba.se.impl.orbutil.threadpool", + "com.sun.corba.se.impl.presentation.rmi", "com.sun.corba.se.impl.protocol", + "com.sun.corba.se.impl.protocol.giopmsgheaders", "com.sun.corba.se.impl.resolver", + "com.sun.corba.se.impl.transport", "com.sun.corba.se.impl.util", + "com.sun.corba.se.internal.CosNaming", "com.sun.corba.se.internal.Interceptors", + "com.sun.corba.se.internal.POA", "com.sun.corba.se.internal.corba", + "com.sun.corba.se.internal.iiop", "com.sun.corba.se.org.omg.CORBA", + "com.sun.corba.se.pept.broker", "com.sun.corba.se.pept.encoding", + "com.sun.corba.se.pept.protocol", "com.sun.corba.se.pept.transport", + "com.sun.corba.se.spi.activation", + "com.sun.corba.se.spi.activation.InitialNameServicePackage", + "com.sun.corba.se.spi.activation.LocatorPackage", + "com.sun.corba.se.spi.activation.RepositoryPackage", "com.sun.corba.se.spi.copyobject", + "com.sun.corba.se.spi.encoding", "com.sun.corba.se.spi.extension", "com.sun.corba.se.spi.ior", + "com.sun.corba.se.spi.ior.iiop", "com.sun.corba.se.spi.legacy.connection", + "com.sun.corba.se.spi.legacy.interceptor", "com.sun.corba.se.spi.logging", + "com.sun.corba.se.spi.monitoring", "com.sun.corba.se.spi.oa", "com.sun.corba.se.spi.orb", + "com.sun.corba.se.spi.orbutil.closure", "com.sun.corba.se.spi.orbutil.fsm", + "com.sun.corba.se.spi.orbutil.proxy", "com.sun.corba.se.spi.orbutil.threadpool", + "com.sun.corba.se.spi.presentation.rmi", "com.sun.corba.se.spi.protocol", + "com.sun.corba.se.spi.resolver", "com.sun.corba.se.spi.servicecontext", + "com.sun.corba.se.spi.transport", "com.sun.crypto.provider", "com.sun.demo.jvmti.hprof", + "com.sun.deploy.uitoolkit.impl.fx", "com.sun.deploy.uitoolkit.impl.fx.ui", + "com.sun.deploy.uitoolkit.impl.fx.ui.resources", "com.sun.glass.events", + "com.sun.glass.events.mac", "com.sun.glass.ui", "com.sun.glass.ui.delegate", + "com.sun.glass.ui.gtk", "com.sun.glass.ui.mac", "com.sun.glass.ui.win", "com.sun.glass.utils", + "com.sun.image.codec.jpeg", "com.sun.imageio.plugins.bmp", "com.sun.imageio.plugins.common", + "com.sun.imageio.plugins.gif", "com.sun.imageio.plugins.jpeg", "com.sun.imageio.plugins.png", + "com.sun.imageio.plugins.wbmp", "com.sun.imageio.spi", "com.sun.imageio.stream", + "com.sun.istack.internal", "com.sun.istack.internal.localization", + "com.sun.istack.internal.logging", "com.sun.istack.internal.tools", + "com.sun.java.accessibility", "com.sun.java.accessibility.util.java.awt", + "com.sun.java.browser.dom", "com.sun.java.browser.net", "com.sun.java.swing", + "com.sun.java.swing.plaf.gtk", "com.sun.java.swing.plaf.gtk.resources", + "com.sun.java.swing.plaf.motif", "com.sun.java.swing.plaf.motif.resources", + "com.sun.java.swing.plaf.nimbus", "com.sun.java.swing.plaf.windows", + "com.sun.java.swing.plaf.windows.resources", "com.sun.java.util.jar.pack", + "com.sun.java_cup.internal.runtime", "com.sun.javafx", "com.sun.javafx.animation", + "com.sun.javafx.applet", "com.sun.javafx.application", "com.sun.javafx.beans", + "com.sun.javafx.beans.event", "com.sun.javafx.binding", "com.sun.javafx.charts", + "com.sun.javafx.collections", "com.sun.javafx.css", "com.sun.javafx.css.converters", + "com.sun.javafx.css.parser", "com.sun.javafx.cursor", "com.sun.javafx.effect", + "com.sun.javafx.embed", "com.sun.javafx.event", "com.sun.javafx.font", + "com.sun.javafx.font.coretext", "com.sun.javafx.font.directwrite", + "com.sun.javafx.font.freetype", "com.sun.javafx.font.t2k", "com.sun.javafx.fxml", + "com.sun.javafx.fxml.builder", "com.sun.javafx.fxml.expression", "com.sun.javafx.geom", + "com.sun.javafx.geom.transform", "com.sun.javafx.geometry", "com.sun.javafx.iio", + "com.sun.javafx.iio.bmp", "com.sun.javafx.iio.common", "com.sun.javafx.iio.gif", + "com.sun.javafx.iio.ios", "com.sun.javafx.iio.jpeg", "com.sun.javafx.iio.png", + "com.sun.javafx.image", "com.sun.javafx.image.impl", "com.sun.javafx.jmx", + "com.sun.javafx.logging", "com.sun.javafx.media", "com.sun.javafx.menu", + "com.sun.javafx.perf", "com.sun.javafx.print", "com.sun.javafx.property", + "com.sun.javafx.property.adapter", "com.sun.javafx.robot", "com.sun.javafx.robot.impl", + "com.sun.javafx.runtime", "com.sun.javafx.runtime.async", "com.sun.javafx.runtime.eula", + "com.sun.javafx.scene", "com.sun.javafx.scene.control", + "com.sun.javafx.scene.control.behavior", "com.sun.javafx.scene.control.skin", + "com.sun.javafx.scene.control.skin.resources", "com.sun.javafx.scene.input", + "com.sun.javafx.scene.layout.region", "com.sun.javafx.scene.paint", + "com.sun.javafx.scene.shape", "com.sun.javafx.scene.text", "com.sun.javafx.scene.transform", + "com.sun.javafx.scene.traversal", "com.sun.javafx.scene.web", + "com.sun.javafx.scene.web.behavior", "com.sun.javafx.scene.web.skin", + "com.sun.javafx.sg.prism", "com.sun.javafx.sg.prism.web", "com.sun.javafx.stage", + "com.sun.javafx.text", "com.sun.javafx.tk", "com.sun.javafx.tk.quantum", + "com.sun.javafx.util", "com.sun.javafx.webkit", "com.sun.javafx.webkit.drt", + "com.sun.javafx.webkit.prism", "com.sun.javafx.webkit.prism.theme", + "com.sun.javafx.webkit.theme", "com.sun.jmx.defaults", "com.sun.jmx.interceptor", + "com.sun.jmx.mbeanserver", "com.sun.jmx.remote.internal", "com.sun.jmx.remote.protocol.iiop", + "com.sun.jmx.remote.protocol.rmi", "com.sun.jmx.remote.security", "com.sun.jmx.remote.util", + "com.sun.jmx.snmp", "com.sun.jmx.snmp.IPAcl", "com.sun.jmx.snmp.agent", + "com.sun.jmx.snmp.daemon", "com.sun.jmx.snmp.defaults", "com.sun.jmx.snmp.internal", + "com.sun.jmx.snmp.mpm", "com.sun.jmx.snmp.tasks", "com.sun.jndi.cosnaming", + "com.sun.jndi.dns", "com.sun.jndi.ldap", "com.sun.jndi.ldap.ext", "com.sun.jndi.ldap.pool", + "com.sun.jndi.ldap.sasl", "com.sun.jndi.rmi.registry", "com.sun.jndi.toolkit.corba", + "com.sun.jndi.toolkit.ctx", "com.sun.jndi.toolkit.dir", "com.sun.jndi.toolkit.url", + "com.sun.jndi.url.corbaname", "com.sun.jndi.url.dns", "com.sun.jndi.url.iiop", + "com.sun.jndi.url.iiopname", "com.sun.jndi.url.ldap", "com.sun.jndi.url.ldaps", + "com.sun.jndi.url.rmi", "com.sun.management.jmx", "com.sun.media.jfxmedia", + "com.sun.media.jfxmedia.control", "com.sun.media.jfxmedia.effects", + "com.sun.media.jfxmedia.events", "com.sun.media.jfxmedia.locator", + "com.sun.media.jfxmedia.logging", "com.sun.media.jfxmedia.track", + "com.sun.media.jfxmediaimpl", "com.sun.media.jfxmediaimpl.platform", + "com.sun.media.jfxmediaimpl.platform.gstreamer", "com.sun.media.jfxmediaimpl.platform.ios", + "com.sun.media.jfxmediaimpl.platform.java", "com.sun.media.jfxmediaimpl.platform.osx", + "com.sun.media.sound", "com.sun.naming.internal", "com.sun.net.ssl", + "com.sun.net.ssl.internal.ssl", "com.sun.net.ssl.internal.www.protocol.https", + "com.sun.nio.file", "com.sun.nio.zipfs", "com.sun.openpisces", + "com.sun.org.apache.bcel.internal", "com.sun.org.apache.bcel.internal.classfile", + "com.sun.org.apache.bcel.internal.generic", "com.sun.org.apache.bcel.internal.util", + "com.sun.org.apache.regexp.internal", "com.sun.org.apache.xalan.internal", + "com.sun.org.apache.xalan.internal.extensions", "com.sun.org.apache.xalan.internal.lib", + "com.sun.org.apache.xalan.internal.res", "com.sun.org.apache.xalan.internal.templates", + "com.sun.org.apache.xalan.internal.utils", "com.sun.org.apache.xalan.internal.xslt", + "com.sun.org.apache.xalan.internal.xsltc", "com.sun.org.apache.xalan.internal.xsltc.cmdline", + "com.sun.org.apache.xalan.internal.xsltc.cmdline.getopt", + "com.sun.org.apache.xalan.internal.xsltc.compiler", + "com.sun.org.apache.xalan.internal.xsltc.compiler.util", + "com.sun.org.apache.xalan.internal.xsltc.dom", + "com.sun.org.apache.xalan.internal.xsltc.runtime", + "com.sun.org.apache.xalan.internal.xsltc.runtime.output", + "com.sun.org.apache.xalan.internal.xsltc.trax", + "com.sun.org.apache.xalan.internal.xsltc.util", "com.sun.org.apache.xerces.internal.dom", + "com.sun.org.apache.xerces.internal.dom.events", "com.sun.org.apache.xerces.internal.impl", + "com.sun.org.apache.xerces.internal.impl.dtd", + "com.sun.org.apache.xerces.internal.impl.dtd.models", + "com.sun.org.apache.xerces.internal.impl.dv", + "com.sun.org.apache.xerces.internal.impl.dv.dtd", + "com.sun.org.apache.xerces.internal.impl.dv.util", + "com.sun.org.apache.xerces.internal.impl.dv.xs", "com.sun.org.apache.xerces.internal.impl.io", + "com.sun.org.apache.xerces.internal.impl.msg", + "com.sun.org.apache.xerces.internal.impl.validation", + "com.sun.org.apache.xerces.internal.impl.xpath", + "com.sun.org.apache.xerces.internal.impl.xpath.regex", + "com.sun.org.apache.xerces.internal.impl.xs", + "com.sun.org.apache.xerces.internal.impl.xs.identity", + "com.sun.org.apache.xerces.internal.impl.xs.models", + "com.sun.org.apache.xerces.internal.impl.xs.opti", + "com.sun.org.apache.xerces.internal.impl.xs.traversers", + "com.sun.org.apache.xerces.internal.impl.xs.util", "com.sun.org.apache.xerces.internal.jaxp", + "com.sun.org.apache.xerces.internal.jaxp.datatype", + "com.sun.org.apache.xerces.internal.jaxp.validation", + "com.sun.org.apache.xerces.internal.parsers", "com.sun.org.apache.xerces.internal.util", + "com.sun.org.apache.xerces.internal.utils", "com.sun.org.apache.xerces.internal.xinclude", + "com.sun.org.apache.xerces.internal.xni", "com.sun.org.apache.xerces.internal.xni.grammars", + "com.sun.org.apache.xerces.internal.xni.parser", + "com.sun.org.apache.xerces.internal.xpointer", "com.sun.org.apache.xerces.internal.xs", + "com.sun.org.apache.xerces.internal.xs.datatypes", "com.sun.org.apache.xml.internal.dtm", + "com.sun.org.apache.xml.internal.dtm.ref", "com.sun.org.apache.xml.internal.dtm.ref.dom2dtm", + "com.sun.org.apache.xml.internal.dtm.ref.sax2dtm", "com.sun.org.apache.xml.internal.res", + "com.sun.org.apache.xml.internal.resolver", + "com.sun.org.apache.xml.internal.resolver.helpers", + "com.sun.org.apache.xml.internal.resolver.readers", + "com.sun.org.apache.xml.internal.resolver.tools", "com.sun.org.apache.xml.internal.security", + "com.sun.org.apache.xml.internal.security.algorithms", + "com.sun.org.apache.xml.internal.security.algorithms.implementations", + "com.sun.org.apache.xml.internal.security.c14n", + "com.sun.org.apache.xml.internal.security.c14n.helper", + "com.sun.org.apache.xml.internal.security.c14n.implementations", + "com.sun.org.apache.xml.internal.security.encryption", + "com.sun.org.apache.xml.internal.security.exceptions", + "com.sun.org.apache.xml.internal.security.keys", + "com.sun.org.apache.xml.internal.security.keys.content", + "com.sun.org.apache.xml.internal.security.keys.content.keyvalues", + "com.sun.org.apache.xml.internal.security.keys.content.x509", + "com.sun.org.apache.xml.internal.security.keys.keyresolver", + "com.sun.org.apache.xml.internal.security.keys.keyresolver.implementations", + "com.sun.org.apache.xml.internal.security.keys.storage", + "com.sun.org.apache.xml.internal.security.keys.storage.implementations", + "com.sun.org.apache.xml.internal.security.signature", + "com.sun.org.apache.xml.internal.security.signature.reference", + "com.sun.org.apache.xml.internal.security.transforms", + "com.sun.org.apache.xml.internal.security.transforms.implementations", + "com.sun.org.apache.xml.internal.security.transforms.params", + "com.sun.org.apache.xml.internal.security.utils", + "com.sun.org.apache.xml.internal.security.utils.resolver", + "com.sun.org.apache.xml.internal.security.utils.resolver.implementations", + "com.sun.org.apache.xml.internal.serialize", "com.sun.org.apache.xml.internal.serializer", + "com.sun.org.apache.xml.internal.serializer.utils", "com.sun.org.apache.xml.internal.utils", + "com.sun.org.apache.xml.internal.utils.res", "com.sun.org.apache.xpath.internal", + "com.sun.org.apache.xpath.internal.axes", "com.sun.org.apache.xpath.internal.compiler", + "com.sun.org.apache.xpath.internal.domapi", "com.sun.org.apache.xpath.internal.functions", + "com.sun.org.apache.xpath.internal.jaxp", "com.sun.org.apache.xpath.internal.objects", + "com.sun.org.apache.xpath.internal.operations", "com.sun.org.apache.xpath.internal.patterns", + "com.sun.org.apache.xpath.internal.res", "com.sun.org.glassfish.external.amx", + "com.sun.org.glassfish.external.arc", "com.sun.org.glassfish.external.probe.provider", + "com.sun.org.glassfish.external.probe.provider.annotations", + "com.sun.org.glassfish.external.statistics", + "com.sun.org.glassfish.external.statistics.annotations", + "com.sun.org.glassfish.external.statistics.impl", "com.sun.org.glassfish.gmbal", + "com.sun.org.glassfish.gmbal.util", "com.sun.org.omg.CORBA", + "com.sun.org.omg.CORBA.ValueDefPackage", "com.sun.org.omg.CORBA.portable", + "com.sun.org.omg.SendingContext", "com.sun.org.omg.SendingContext.CodeBasePackage", + "com.sun.pisces", "com.sun.prism", "com.sun.prism.d3d", "com.sun.prism.es2", + "com.sun.prism.image", "com.sun.prism.impl", "com.sun.prism.impl.packrect", + "com.sun.prism.impl.paint", "com.sun.prism.impl.ps", "com.sun.prism.impl.shape", + "com.sun.prism.j2d", "com.sun.prism.j2d.paint", "com.sun.prism.j2d.print", + "com.sun.prism.paint", "com.sun.prism.ps", "com.sun.prism.shader", "com.sun.prism.shape", + "com.sun.prism.sw", "com.sun.rmi.rmid", "com.sun.rowset", "com.sun.rowset.internal", + "com.sun.rowset.providers", "com.sun.scenario", "com.sun.scenario.animation", + "com.sun.scenario.animation.shared", "com.sun.scenario.effect", + "com.sun.scenario.effect.impl", "com.sun.scenario.effect.impl.es2", + "com.sun.scenario.effect.impl.hw", "com.sun.scenario.effect.impl.hw.d3d", + "com.sun.scenario.effect.impl.prism", "com.sun.scenario.effect.impl.prism.ps", + "com.sun.scenario.effect.impl.prism.sw", "com.sun.scenario.effect.impl.state", + "com.sun.scenario.effect.impl.sw", "com.sun.scenario.effect.impl.sw.java", + "com.sun.scenario.effect.impl.sw.sse", "com.sun.scenario.effect.light", + "com.sun.security.cert.internal.x509", "com.sun.security.ntlm", "com.sun.security.sasl", + "com.sun.security.sasl.digest", "com.sun.security.sasl.gsskerb", "com.sun.security.sasl.ntlm", + "com.sun.security.sasl.util", "com.sun.swing.internal.plaf.basic.resources", + "com.sun.swing.internal.plaf.metal.resources", "com.sun.swing.internal.plaf.synth.resources", + "com.sun.tools.classfile", "com.sun.tools.corba.se.idl", + "com.sun.tools.corba.se.idl.constExpr", "com.sun.tools.corba.se.idl.som.cff", + "com.sun.tools.corba.se.idl.som.idlemit", "com.sun.tools.corba.se.idl.toJavaPortable", + "com.sun.tools.doclets.formats.html", "com.sun.tools.doclets.formats.html.markup", + "com.sun.tools.doclets.formats.html.resources", "com.sun.tools.doclets.internal.toolkit", + "com.sun.tools.doclets.internal.toolkit.builders", + "com.sun.tools.doclets.internal.toolkit.resources", + "com.sun.tools.doclets.internal.toolkit.taglets", + "com.sun.tools.doclets.internal.toolkit.util", + "com.sun.tools.doclets.internal.toolkit.util.links", "com.sun.tools.doclint", + "com.sun.tools.doclint.resources", "com.sun.tools.example.debug.expr", + "com.sun.tools.example.debug.tty", "com.sun.tools.extcheck", "com.sun.tools.hat", + "com.sun.tools.hat.internal.model", "com.sun.tools.hat.internal.oql", + "com.sun.tools.hat.internal.parser", "com.sun.tools.hat.internal.server", + "com.sun.tools.hat.internal.util", "com.sun.tools.internal.jxc", + "com.sun.tools.internal.jxc.ap", "com.sun.tools.internal.jxc.api", + "com.sun.tools.internal.jxc.api.impl.j2s", "com.sun.tools.internal.jxc.gen.config", + "com.sun.tools.internal.jxc.model.nav", "com.sun.tools.internal.ws", + "com.sun.tools.internal.ws.api", "com.sun.tools.internal.ws.api.wsdl", + "com.sun.tools.internal.ws.processor", "com.sun.tools.internal.ws.processor.generator", + "com.sun.tools.internal.ws.processor.model", + "com.sun.tools.internal.ws.processor.model.exporter", + "com.sun.tools.internal.ws.processor.model.java", + "com.sun.tools.internal.ws.processor.model.jaxb", + "com.sun.tools.internal.ws.processor.modeler", + "com.sun.tools.internal.ws.processor.modeler.annotation", + "com.sun.tools.internal.ws.processor.modeler.wsdl", + "com.sun.tools.internal.ws.processor.util", "com.sun.tools.internal.ws.resources", + "com.sun.tools.internal.ws.spi", "com.sun.tools.internal.ws.util", + "com.sun.tools.internal.ws.util.xml", "com.sun.tools.internal.ws.wscompile", + "com.sun.tools.internal.ws.wscompile.plugin.at_generated", + "com.sun.tools.internal.ws.wsdl.document", "com.sun.tools.internal.ws.wsdl.document.http", + "com.sun.tools.internal.ws.wsdl.document.jaxws", + "com.sun.tools.internal.ws.wsdl.document.mime", + "com.sun.tools.internal.ws.wsdl.document.schema", + "com.sun.tools.internal.ws.wsdl.document.soap", "com.sun.tools.internal.ws.wsdl.framework", + "com.sun.tools.internal.ws.wsdl.parser", "com.sun.tools.internal.xjc", + "com.sun.tools.internal.xjc.addon.accessors", "com.sun.tools.internal.xjc.addon.at_generated", + "com.sun.tools.internal.xjc.addon.code_injector", "com.sun.tools.internal.xjc.addon.episode", + "com.sun.tools.internal.xjc.addon.locator", "com.sun.tools.internal.xjc.addon.sync", + "com.sun.tools.internal.xjc.api", "com.sun.tools.internal.xjc.api.impl.s2j", + "com.sun.tools.internal.xjc.api.util", "com.sun.tools.internal.xjc.generator.annotation.spec", + "com.sun.tools.internal.xjc.generator.bean", + "com.sun.tools.internal.xjc.generator.bean.field", + "com.sun.tools.internal.xjc.generator.util", "com.sun.tools.internal.xjc.model", + "com.sun.tools.internal.xjc.model.nav", "com.sun.tools.internal.xjc.outline", + "com.sun.tools.internal.xjc.reader", "com.sun.tools.internal.xjc.reader.dtd", + "com.sun.tools.internal.xjc.reader.dtd.bindinfo", "com.sun.tools.internal.xjc.reader.gbind", + "com.sun.tools.internal.xjc.reader.internalizer", "com.sun.tools.internal.xjc.reader.relaxng", + "com.sun.tools.internal.xjc.reader.xmlschema", + "com.sun.tools.internal.xjc.reader.xmlschema.bindinfo", + "com.sun.tools.internal.xjc.reader.xmlschema.ct", + "com.sun.tools.internal.xjc.reader.xmlschema.parser", "com.sun.tools.internal.xjc.runtime", + "com.sun.tools.internal.xjc.util", "com.sun.tools.internal.xjc.writer", + "com.sun.tools.javac.api", "com.sun.tools.javac.code", "com.sun.tools.javac.comp", + "com.sun.tools.javac.file", "com.sun.tools.javac.jvm", "com.sun.tools.javac.main", + "com.sun.tools.javac.model", "com.sun.tools.javac.nio", "com.sun.tools.javac.parser", + "com.sun.tools.javac.processing", "com.sun.tools.javac.resources", "com.sun.tools.javac.sym", + "com.sun.tools.javac.tree", "com.sun.tools.javac.util", "com.sun.tools.javadoc.api", + "com.sun.tools.javadoc.resources", "com.sun.tools.javah", "com.sun.tools.javah.resources", + "com.sun.tools.javap", "com.sun.tools.javap.resources", "com.sun.tools.jdeps", + "com.sun.tools.jdeps.resources", "com.sun.tools.jdi", "com.sun.tools.jdi.resources", + "com.sun.tools.script.shell", "com.sun.tracing", "com.sun.tracing.dtrace", "com.sun.webkit", + "com.sun.webkit.dom", "com.sun.webkit.event", "com.sun.webkit.graphics", + "com.sun.webkit.network", "com.sun.webkit.network.about", "com.sun.webkit.network.data", + "com.sun.webkit.perf", "com.sun.webkit.plugin", "com.sun.webkit.text", + "com.sun.xml.internal.bind", "com.sun.xml.internal.bind.annotation", + "com.sun.xml.internal.bind.api", "com.sun.xml.internal.bind.api.impl", + "com.sun.xml.internal.bind.marshaller", "com.sun.xml.internal.bind.unmarshaller", + "com.sun.xml.internal.bind.util", "com.sun.xml.internal.bind.v2", + "com.sun.xml.internal.bind.v2.bytecode", "com.sun.xml.internal.bind.v2.model.annotation", + "com.sun.xml.internal.bind.v2.model.core", "com.sun.xml.internal.bind.v2.model.impl", + "com.sun.xml.internal.bind.v2.model.nav", "com.sun.xml.internal.bind.v2.model.runtime", + "com.sun.xml.internal.bind.v2.model.util", "com.sun.xml.internal.bind.v2.runtime", + "com.sun.xml.internal.bind.v2.runtime.output", + "com.sun.xml.internal.bind.v2.runtime.property", + "com.sun.xml.internal.bind.v2.runtime.reflect", + "com.sun.xml.internal.bind.v2.runtime.reflect.opt", + "com.sun.xml.internal.bind.v2.runtime.unmarshaller", "com.sun.xml.internal.bind.v2.schemagen", + "com.sun.xml.internal.bind.v2.schemagen.episode", + "com.sun.xml.internal.bind.v2.schemagen.xmlschema", "com.sun.xml.internal.bind.v2.util", + "com.sun.xml.internal.dtdparser", "com.sun.xml.internal.fastinfoset", + "com.sun.xml.internal.fastinfoset.algorithm", "com.sun.xml.internal.fastinfoset.alphabet", + "com.sun.xml.internal.fastinfoset.dom", + "com.sun.xml.internal.fastinfoset.org.apache.xerces.util", + "com.sun.xml.internal.fastinfoset.sax", "com.sun.xml.internal.fastinfoset.stax", + "com.sun.xml.internal.fastinfoset.stax.events", + "com.sun.xml.internal.fastinfoset.stax.factory", "com.sun.xml.internal.fastinfoset.stax.util", + "com.sun.xml.internal.fastinfoset.tools", "com.sun.xml.internal.fastinfoset.util", + "com.sun.xml.internal.fastinfoset.vocab", "com.sun.xml.internal.messaging.saaj", + "com.sun.xml.internal.messaging.saaj.client.p2p", + "com.sun.xml.internal.messaging.saaj.packaging.mime", + "com.sun.xml.internal.messaging.saaj.packaging.mime.internet", + "com.sun.xml.internal.messaging.saaj.packaging.mime.util", + "com.sun.xml.internal.messaging.saaj.soap", + "com.sun.xml.internal.messaging.saaj.soap.dynamic", + "com.sun.xml.internal.messaging.saaj.soap.impl", + "com.sun.xml.internal.messaging.saaj.soap.name", + "com.sun.xml.internal.messaging.saaj.soap.ver1_1", + "com.sun.xml.internal.messaging.saaj.soap.ver1_2", "com.sun.xml.internal.messaging.saaj.util", + "com.sun.xml.internal.messaging.saaj.util.transform", + "com.sun.xml.internal.org.jvnet.fastinfoset", + "com.sun.xml.internal.org.jvnet.fastinfoset.sax", + "com.sun.xml.internal.org.jvnet.fastinfoset.sax.helpers", + "com.sun.xml.internal.org.jvnet.fastinfoset.stax", "com.sun.xml.internal.org.jvnet.mimepull", + "com.sun.xml.internal.org.jvnet.staxex", "com.sun.xml.internal.rngom.ast.builder", + "com.sun.xml.internal.rngom.ast.om", "com.sun.xml.internal.rngom.ast.util", + "com.sun.xml.internal.rngom.binary", "com.sun.xml.internal.rngom.binary.visitor", + "com.sun.xml.internal.rngom.digested", "com.sun.xml.internal.rngom.dt", + "com.sun.xml.internal.rngom.dt.builtin", "com.sun.xml.internal.rngom.nc", + "com.sun.xml.internal.rngom.parse", "com.sun.xml.internal.rngom.parse.compact", + "com.sun.xml.internal.rngom.parse.host", "com.sun.xml.internal.rngom.parse.xml", + "com.sun.xml.internal.rngom.util", "com.sun.xml.internal.rngom.xml.sax", + "com.sun.xml.internal.rngom.xml.util", "com.sun.xml.internal.stream", + "com.sun.xml.internal.stream.buffer", "com.sun.xml.internal.stream.buffer.sax", + "com.sun.xml.internal.stream.buffer.stax", "com.sun.xml.internal.stream.dtd", + "com.sun.xml.internal.stream.dtd.nonvalidating", "com.sun.xml.internal.stream.events", + "com.sun.xml.internal.stream.util", "com.sun.xml.internal.stream.writers", + "com.sun.xml.internal.txw2", "com.sun.xml.internal.txw2.annotation", + "com.sun.xml.internal.txw2.output", "com.sun.xml.internal.ws", + "com.sun.xml.internal.ws.addressing", "com.sun.xml.internal.ws.addressing.model", + "com.sun.xml.internal.ws.addressing.policy", "com.sun.xml.internal.ws.addressing.v200408", + "com.sun.xml.internal.ws.api", "com.sun.xml.internal.ws.api.addressing", + "com.sun.xml.internal.ws.api.client", "com.sun.xml.internal.ws.api.config.management", + "com.sun.xml.internal.ws.api.config.management.policy", + "com.sun.xml.internal.ws.api.databinding", "com.sun.xml.internal.ws.api.fastinfoset", + "com.sun.xml.internal.ws.api.ha", "com.sun.xml.internal.ws.api.handler", + "com.sun.xml.internal.ws.api.message", "com.sun.xml.internal.ws.api.message.saaj", + "com.sun.xml.internal.ws.api.message.stream", "com.sun.xml.internal.ws.api.model", + "com.sun.xml.internal.ws.api.model.soap", "com.sun.xml.internal.ws.api.model.wsdl", + "com.sun.xml.internal.ws.api.model.wsdl.editable", "com.sun.xml.internal.ws.api.pipe", + "com.sun.xml.internal.ws.api.pipe.helper", "com.sun.xml.internal.ws.api.policy", + "com.sun.xml.internal.ws.api.policy.subject", "com.sun.xml.internal.ws.api.server", + "com.sun.xml.internal.ws.api.streaming", "com.sun.xml.internal.ws.api.wsdl.parser", + "com.sun.xml.internal.ws.api.wsdl.writer", "com.sun.xml.internal.ws.assembler", + "com.sun.xml.internal.ws.assembler.dev", "com.sun.xml.internal.ws.assembler.jaxws", + "com.sun.xml.internal.ws.binding", "com.sun.xml.internal.ws.client", + "com.sun.xml.internal.ws.client.dispatch", "com.sun.xml.internal.ws.client.sei", + "com.sun.xml.internal.ws.commons.xmlutil", "com.sun.xml.internal.ws.config.management.policy", + "com.sun.xml.internal.ws.config.metro.dev", "com.sun.xml.internal.ws.config.metro.util", + "com.sun.xml.internal.ws.db", "com.sun.xml.internal.ws.db.glassfish", + "com.sun.xml.internal.ws.developer", "com.sun.xml.internal.ws.dump", + "com.sun.xml.internal.ws.encoding", "com.sun.xml.internal.ws.encoding.fastinfoset", + "com.sun.xml.internal.ws.encoding.policy", "com.sun.xml.internal.ws.encoding.soap", + "com.sun.xml.internal.ws.encoding.soap.streaming", "com.sun.xml.internal.ws.encoding.xml", + "com.sun.xml.internal.ws.fault", "com.sun.xml.internal.ws.handler", + "com.sun.xml.internal.ws.message", "com.sun.xml.internal.ws.message.jaxb", + "com.sun.xml.internal.ws.message.saaj", "com.sun.xml.internal.ws.message.source", + "com.sun.xml.internal.ws.message.stream", "com.sun.xml.internal.ws.model", + "com.sun.xml.internal.ws.model.soap", "com.sun.xml.internal.ws.model.wsdl", + "com.sun.xml.internal.ws.org.objectweb.asm", "com.sun.xml.internal.ws.policy", + "com.sun.xml.internal.ws.policy.jaxws", "com.sun.xml.internal.ws.policy.jaxws.spi", + "com.sun.xml.internal.ws.policy.privateutil", "com.sun.xml.internal.ws.policy.sourcemodel", + "com.sun.xml.internal.ws.policy.sourcemodel.attach", + "com.sun.xml.internal.ws.policy.sourcemodel.wspolicy", "com.sun.xml.internal.ws.policy.spi", + "com.sun.xml.internal.ws.policy.subject", "com.sun.xml.internal.ws.protocol.soap", + "com.sun.xml.internal.ws.protocol.xml", "com.sun.xml.internal.ws.resources", + "com.sun.xml.internal.ws.runtime.config", "com.sun.xml.internal.ws.server", + "com.sun.xml.internal.ws.server.provider", "com.sun.xml.internal.ws.server.sei", + "com.sun.xml.internal.ws.spi", "com.sun.xml.internal.ws.spi.db", + "com.sun.xml.internal.ws.streaming", "com.sun.xml.internal.ws.transport", + "com.sun.xml.internal.ws.transport.http", "com.sun.xml.internal.ws.transport.http.client", + "com.sun.xml.internal.ws.transport.http.server", "com.sun.xml.internal.ws.util", + "com.sun.xml.internal.ws.util.exception", "com.sun.xml.internal.ws.util.pipe", + "com.sun.xml.internal.ws.util.xml", "com.sun.xml.internal.ws.wsdl", + "com.sun.xml.internal.ws.wsdl.parser", "com.sun.xml.internal.ws.wsdl.writer", + "com.sun.xml.internal.ws.wsdl.writer.document", + "com.sun.xml.internal.ws.wsdl.writer.document.http", + "com.sun.xml.internal.ws.wsdl.writer.document.soap", + "com.sun.xml.internal.ws.wsdl.writer.document.soap12", + "com.sun.xml.internal.ws.wsdl.writer.document.xsd", "com.sun.xml.internal.xsom", + "com.sun.xml.internal.xsom.impl", "com.sun.xml.internal.xsom.impl.parser", + "com.sun.xml.internal.xsom.impl.parser.state", "com.sun.xml.internal.xsom.impl.scd", + "com.sun.xml.internal.xsom.impl.util", "com.sun.xml.internal.xsom.parser", + "com.sun.xml.internal.xsom.util", "com.sun.xml.internal.xsom.visitor", "java.awt.dnd.peer", + "java.awt.peer", "javafx.embed.swt", "jdk", "jdk.internal.cmm", "jdk.internal.dynalink", + "jdk.internal.dynalink.beans", "jdk.internal.dynalink.linker", + "jdk.internal.dynalink.support", "jdk.internal.instrumentation", + "jdk.internal.org.objectweb.asm", "jdk.internal.org.objectweb.asm.commons", + "jdk.internal.org.objectweb.asm.signature", "jdk.internal.org.objectweb.asm.tree", + "jdk.internal.org.objectweb.asm.tree.analysis", "jdk.internal.org.objectweb.asm.util", + "jdk.internal.org.xml.sax", "jdk.internal.org.xml.sax.helpers", "jdk.internal.util.xml", + "jdk.internal.util.xml.impl", "jdk.jfr.events", "jdk.management.resource.internal", + "jdk.management.resource.internal.inst", "jdk.nashorn.internal", + "jdk.nashorn.internal.codegen", "jdk.nashorn.internal.codegen.types", + "jdk.nashorn.internal.ir", "jdk.nashorn.internal.ir.annotations", + "jdk.nashorn.internal.ir.debug", "jdk.nashorn.internal.ir.visitor", + "jdk.nashorn.internal.lookup", "jdk.nashorn.internal.objects", + "jdk.nashorn.internal.objects.annotations", "jdk.nashorn.internal.parser", + "jdk.nashorn.internal.runtime", "jdk.nashorn.internal.runtime.arrays", + "jdk.nashorn.internal.runtime.events", "jdk.nashorn.internal.runtime.linker", + "jdk.nashorn.internal.runtime.logging", "jdk.nashorn.internal.runtime.options", + "jdk.nashorn.internal.runtime.regexp", "jdk.nashorn.internal.runtime.regexp.joni", + "jdk.nashorn.internal.runtime.regexp.joni.ast", + "jdk.nashorn.internal.runtime.regexp.joni.constants", + "jdk.nashorn.internal.runtime.regexp.joni.encoding", + "jdk.nashorn.internal.runtime.regexp.joni.exception", "jdk.nashorn.internal.scripts", + "jdk.nashorn.tools", "oracle.jrockit.jfr", "oracle.jrockit.jfr.events", + "oracle.jrockit.jfr.jdkevents", "oracle.jrockit.jfr.jdkevents.throwabletransform", + "oracle.jrockit.jfr.openmbean", "oracle.jrockit.jfr.parser", "oracle.jrockit.jfr.settings", + "oracle.jrockit.jfr.tools", "org.jcp.xml.dsig.internal", "org.jcp.xml.dsig.internal.dom", + "org.omg.stub.javax.management.remote.rmi", "org.relaxng.datatype", + "org.relaxng.datatype.helpers", "sun.applet", "sun.applet.resources", "sun.audio", "sun.awt", + "sun.awt.X11", "sun.awt.datatransfer", "sun.awt.dnd", "sun.awt.event", "sun.awt.geom", + "sun.awt.im", "sun.awt.image", "sun.awt.image.codec", "sun.awt.motif", "sun.awt.resources", + "sun.awt.shell", "sun.awt.util", "sun.awt.windows", "sun.corba", "sun.dc", "sun.dc.path", + "sun.dc.pr", "sun.font", "sun.instrument", "sun.invoke", "sun.invoke.anon", + "sun.invoke.empty", "sun.invoke.util", "sun.io", "sun.java2d", "sun.java2d.cmm", + "sun.java2d.cmm.kcms", "sun.java2d.cmm.lcms", "sun.java2d.d3d", "sun.java2d.jules", + "sun.java2d.loops", "sun.java2d.opengl", "sun.java2d.pipe", "sun.java2d.pipe.hw", + "sun.java2d.pisces", "sun.java2d.windows", "sun.java2d.x11", "sun.java2d.xr", + "sun.jvmstat.monitor", "sun.jvmstat.monitor.event", "sun.jvmstat.monitor.remote", + "sun.jvmstat.perfdata.monitor", "sun.jvmstat.perfdata.monitor.protocol.file", + "sun.jvmstat.perfdata.monitor.protocol.local", "sun.jvmstat.perfdata.monitor.protocol.rmi", + "sun.jvmstat.perfdata.monitor.v1_0", "sun.jvmstat.perfdata.monitor.v2_0", "sun.launcher", + "sun.launcher.resources", "sun.lwawt", "sun.lwawt.macosx", "sun.management", + "sun.management.counter", "sun.management.counter.perf", "sun.management.jdp", + "sun.management.jmxremote", "sun.management.resources", "sun.management.snmp", + "sun.management.snmp.jvminstr", "sun.management.snmp.jvmmib", "sun.management.snmp.util", + "sun.misc", "sun.misc.resources", "sun.net", "sun.net.dns", "sun.net.ftp", "sun.net.ftp.impl", + "sun.net.httpserver", "sun.net.idn", "sun.net.sdp", "sun.net.smtp", "sun.net.spi", + "sun.net.spi.nameservice", "sun.net.spi.nameservice.dns", "sun.net.util", "sun.net.www", + "sun.net.www.content.audio", "sun.net.www.content.image", "sun.net.www.content.text", + "sun.net.www.http", "sun.net.www.protocol.file", "sun.net.www.protocol.ftp", + "sun.net.www.protocol.http", "sun.net.www.protocol.http.logging", + "sun.net.www.protocol.http.ntlm", "sun.net.www.protocol.http.spnego", + "sun.net.www.protocol.https", "sun.net.www.protocol.jar", "sun.net.www.protocol.mailto", + "sun.net.www.protocol.netdoc", "sun.nio", "sun.nio.ch", "sun.nio.ch.sctp", "sun.nio.cs", + "sun.nio.cs.ext", "sun.nio.fs", "sun.print", "sun.print.resources", "sun.reflect", + "sun.reflect.annotation", "sun.reflect.generics.factory", "sun.reflect.generics.parser", + "sun.reflect.generics.reflectiveObjects", "sun.reflect.generics.repository", + "sun.reflect.generics.scope", "sun.reflect.generics.tree", "sun.reflect.generics.visitor", + "sun.reflect.misc", "sun.rmi.log", "sun.rmi.registry", "sun.rmi.rmic", "sun.rmi.rmic.iiop", + "sun.rmi.rmic.newrmic", "sun.rmi.rmic.newrmic.jrmp", "sun.rmi.runtime", "sun.rmi.server", + "sun.rmi.transport", "sun.rmi.transport.proxy", "sun.rmi.transport.tcp", "sun.security.acl", + "sun.security.action", "sun.security.ec", "sun.security.internal.interfaces", + "sun.security.internal.spec", "sun.security.jca", "sun.security.jgss", + "sun.security.jgss.krb5", "sun.security.jgss.spi", "sun.security.jgss.spnego", + "sun.security.jgss.wrapper", "sun.security.krb5", "sun.security.krb5.internal", + "sun.security.krb5.internal.ccache", "sun.security.krb5.internal.crypto", + "sun.security.krb5.internal.crypto.dk", "sun.security.krb5.internal.ktab", + "sun.security.krb5.internal.rcache", "sun.security.krb5.internal.tools", + "sun.security.krb5.internal.util", "sun.security.mscapi", "sun.security.pkcs", + "sun.security.pkcs10", "sun.security.pkcs11", "sun.security.pkcs11.wrapper", + "sun.security.pkcs12", "sun.security.provider", "sun.security.provider.certpath", + "sun.security.provider.certpath.ldap", "sun.security.provider.certpath.ssl", + "sun.security.rsa", "sun.security.smartcardio", "sun.security.ssl", "sun.security.ssl.krb5", + "sun.security.timestamp", "sun.security.tools", "sun.security.tools.jarsigner", + "sun.security.tools.keytool", "sun.security.tools.policytool", "sun.security.util", + "sun.security.validator", "sun.security.x509", "sun.swing", "sun.swing.icon", + "sun.swing.plaf", "sun.swing.plaf.synth", "sun.swing.plaf.windows", "sun.swing.table", + "sun.swing.text", "sun.swing.text.html", "sun.text", "sun.text.bidi", "sun.text.normalizer", + "sun.text.resources", "sun.text.resources.en", "sun.tools.asm", "sun.tools.attach", + "sun.tools.jar", "sun.tools.jar.resources", "sun.tools.java", "sun.tools.javac", + "sun.tools.jcmd", "sun.tools.jconsole", "sun.tools.jconsole.inspector", "sun.tools.jinfo", + "sun.tools.jmap", "sun.tools.jps", "sun.tools.jstack", "sun.tools.jstat", "sun.tools.jstatd", + "sun.tools.native2ascii", "sun.tools.native2ascii.resources", "sun.tools.serialver", + "sun.tools.tree", "sun.tools.util", "sun.tracing", "sun.tracing.dtrace", "sun.usagetracker", + "sun.util", "sun.util.calendar", "sun.util.cldr", "sun.util.locale", + "sun.util.locale.provider", "sun.util.logging", "sun.util.logging.resources", + "sun.util.resources", "sun.util.resources.en", "sun.util.spi", "sun.util.xml" + ] } diff --git a/java/ql/src/Likely Bugs/Collections/IteratorRemoveMayFail.ql b/java/ql/src/Likely Bugs/Collections/IteratorRemoveMayFail.ql index a4d15a4e370..b350eecae7f 100644 --- a/java/ql/src/Likely Bugs/Collections/IteratorRemoveMayFail.ql +++ b/java/ql/src/Likely Bugs/Collections/IteratorRemoveMayFail.ql @@ -30,7 +30,7 @@ predicate containsSpecialCollection(Expr e, SpecialCollectionCreation origin) { e = origin or exists(Variable v | - containsSpecialCollection(v.getAnAssignedValue(), origin) and + containsSpecialCollection(pragma[only_bind_into](v.getAnAssignedValue()), origin) and e = v.getAnAccess() ) or @@ -52,7 +52,7 @@ predicate iterOfSpecialCollection(Expr e, SpecialCollectionCreation origin) { ) or exists(Variable v | - iterOfSpecialCollection(v.getAnAssignedValue(), origin) and + iterOfSpecialCollection(pragma[only_bind_into](v.getAnAssignedValue()), origin) and e = v.getAnAccess() ) or diff --git a/java/ql/src/Likely Bugs/Comparison/StringComparison.ql b/java/ql/src/Likely Bugs/Comparison/StringComparison.ql index 4681feafc00..1f33ece559d 100644 --- a/java/ql/src/Likely Bugs/Comparison/StringComparison.ql +++ b/java/ql/src/Likely Bugs/Comparison/StringComparison.ql @@ -41,10 +41,9 @@ class StringValue extends Expr { } } -predicate variableValuesInterned(Variable v) { +pragma[noinline] +predicate candidateVariable(Variable v) { v.fromSource() and - // All assignments to variables are interned. - forall(StringValue sv | sv = v.getAnAssignedValue() | sv.isInterned()) and // For parameters, assume they could be non-interned. not v instanceof Parameter and // If the string is modified with `+=`, then the new string is not interned @@ -52,6 +51,12 @@ predicate variableValuesInterned(Variable v) { not exists(AssignOp append | append.getDest() = v.getAnAccess()) } +predicate variableValuesInterned(Variable v) { + candidateVariable(v) and + // All assignments to variables are interned. + forall(StringValue sv | sv = v.getAnAssignedValue() | sv.isInterned()) +} + from EqualityTest e, StringValue lhs, StringValue rhs where e.getLeftOperand() = lhs and diff --git a/java/ql/src/Likely Bugs/Concurrency/SynchWriteObject.ql b/java/ql/src/Likely Bugs/Concurrency/SynchWriteObject.ql index dc35a5db380..cb93e28d740 100644 --- a/java/ql/src/Likely Bugs/Concurrency/SynchWriteObject.ql +++ b/java/ql/src/Likely Bugs/Concurrency/SynchWriteObject.ql @@ -20,7 +20,7 @@ where m.getDeclaringType().getASupertype*() instanceof TypeSerializable and m.hasName("writeObject") and m.getNumberOfParameters() = 1 and - m.getAParamType().(Class).hasQualifiedName("java.io", "ObjectOutputStream") and + m.getAParamType() instanceof TypeObjectOutputStream and m.isSynchronized() and not exists(Method s | m.getDeclaringType().inherits(s) and diff --git a/java/ql/src/Likely Bugs/Reflection/AnnotationPresentCheck.ql b/java/ql/src/Likely Bugs/Reflection/AnnotationPresentCheck.ql index 6af39558a8c..22729eebd66 100644 --- a/java/ql/src/Likely Bugs/Reflection/AnnotationPresentCheck.ql +++ b/java/ql/src/Likely Bugs/Reflection/AnnotationPresentCheck.ql @@ -19,9 +19,8 @@ where m.getNumberOfParameters() = 1 and c.getArgument(0).getType() = p and p.getATypeArgument() = t and - not exists(Annotation a | + not exists(RetentionAnnotation a | t.getAnAnnotation() = a and - a.getType().hasQualifiedName("java.lang.annotation", "Retention") and a.getAValue().(VarAccess).getVariable().hasName("RUNTIME") ) select c, "Call to isAnnotationPresent where no annotation has the RUNTIME retention policy." diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseType.qll b/java/ql/src/Likely Bugs/Resource Leaks/CloseType.qll index 46d88f86f41..d88f1bb82c7 100644 --- a/java/ql/src/Likely Bugs/Resource Leaks/CloseType.qll +++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseType.qll @@ -228,8 +228,8 @@ private predicate closeCalled(Variable v) { ) or // The "close" call could happen indirectly inside a helper method of unknown name. - exists(int i | exprs(v.getAnAccess(), _, _, e, i) | - exists(Parameter p, int j | params(p, _, j, e.getMethod(), _) | + exists(int i | e.getArgument(i) = v.getAnAccess() | + exists(Parameter p, int j | p.getPosition() = j and p.getCallable() = e.getMethod() | closeCalled(p) and i = j or // The helper method could be iterating over a varargs parameter. diff --git a/java/ql/src/Likely Bugs/Serialization/IncorrectSerializableMethods.java b/java/ql/src/Likely Bugs/Serialization/IncorrectSerializableMethods.java index 57fd8a64cc2..d12ab5dd887 100644 --- a/java/ql/src/Likely Bugs/Serialization/IncorrectSerializableMethods.java +++ b/java/ql/src/Likely Bugs/Serialization/IncorrectSerializableMethods.java @@ -5,6 +5,12 @@ class WrongNetRequest implements Serializable { //... } + // BAD: Does not match the exact signature required for a custom + // deserialization protocol. Will not be called during deserialization. + void readObjectNoData() { + //... + } + // BAD: Does not match the exact signature required for a custom // serialization protocol. Will not be called during serialization. protected void writeObject(ObjectOutputStream out) { @@ -18,6 +24,11 @@ class NetRequest implements Serializable { //... } + // GOOD: Signature for a custom deserialization implementation. + private void readObjectNoData() { + //... + } + // GOOD: Signature for a custom serialization implementation. private void writeObject(ObjectOutputStream out) { //... diff --git a/java/ql/src/Likely Bugs/Serialization/IncorrectSerializableMethods.qhelp b/java/ql/src/Likely Bugs/Serialization/IncorrectSerializableMethods.qhelp index 8fe19086671..caf48b65107 100644 --- a/java/ql/src/Likely Bugs/Serialization/IncorrectSerializableMethods.qhelp +++ b/java/ql/src/Likely Bugs/Serialization/IncorrectSerializableMethods.qhelp @@ -7,15 +7,16 @@

    A serializable object that defines its own serialization protocol using the methods -readObject and writeObject must use the signature that is expected by the -Java serialization framework. Otherwise, the default serialization mechanism is used. +readObject, readObjectNoData or writeObject must use +the signature that is expected by the Java serialization framework. Otherwise, the default +serialization mechanism is used.

    -Make sure that the signatures of readObject and writeObject on -serializable classes use these exact signatures: +Make sure that the signatures of readObject, readObjectNoData and +writeObject on serializable classes match these expected signatures:

    @@ -23,9 +24,9 @@ serializable classes use these exact signatures:
    -

    In the following example, WrongNetRequest defines readObject and -writeObject using the wrong signatures. However, NetRequest defines them -correctly.

    +

    In the following example, WrongNetRequest defines readObject, +readObjectNoData and writeObject using the wrong signatures. However, +NetRequest defines them correctly.

    diff --git a/java/ql/src/Likely Bugs/Serialization/IncorrectSerializableMethods.ql b/java/ql/src/Likely Bugs/Serialization/IncorrectSerializableMethods.ql index 3990f94936b..815245d1a7d 100644 --- a/java/ql/src/Likely Bugs/Serialization/IncorrectSerializableMethods.ql +++ b/java/ql/src/Likely Bugs/Serialization/IncorrectSerializableMethods.ql @@ -1,7 +1,7 @@ /** * @name Serialization methods do not match required signature - * @description A serialized class that implements 'readObject' or 'writeObject' but does not use - * the correct signatures causes the default serialization mechanism to be used. + * @description A serialized class that implements 'readObject', 'readObjectNoData' or 'writeObject' but + * does not use the correct signatures causes the default serialization mechanism to be used. * @kind problem * @problem.severity warning * @precision medium @@ -13,11 +13,20 @@ import java -from Method m, TypeSerializable serializable +from Method m, TypeSerializable serializable, string reason where + m.fromSource() and m.getDeclaringType().hasSupertype+(serializable) and - m.getNumberOfParameters() = 1 and - m.getAParameter().getType().(RefType).hasQualifiedName("java.io", "ObjectOutputStream") and - (m.hasName("readObject") or m.hasName("writeObject")) and - not m.isPrivate() -select m, "readObject and writeObject should be private methods." + ( + m.hasStringSignature("readObject(ObjectInputStream)") or + m.hasStringSignature("readObjectNoData()") or + m.hasStringSignature("writeObject(ObjectOutputStream)") + ) and + ( + not m.isPrivate() and reason = "Method must be private" + or + m.isStatic() and reason = "Method must not be static" + or + not m.getReturnType() instanceof VoidType and reason = "Return type must be void" + ) +select m, "Not recognized by Java serialization framework: " + reason diff --git a/java/ql/src/Likely Bugs/Serialization/IncorrectSerializableMethodsSig.java b/java/ql/src/Likely Bugs/Serialization/IncorrectSerializableMethodsSig.java index fca8fd6f558..c5163660ece 100644 --- a/java/ql/src/Likely Bugs/Serialization/IncorrectSerializableMethodsSig.java +++ b/java/ql/src/Likely Bugs/Serialization/IncorrectSerializableMethodsSig.java @@ -1,4 +1,6 @@ private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException; +private void readObjectNoData() + throws ObjectStreamException; private void writeObject(java.io.ObjectOutputStream out) throws IOException; \ No newline at end of file diff --git a/java/ql/src/Performance/InnerClassCouldBeStatic.ql b/java/ql/src/Performance/InnerClassCouldBeStatic.ql index 2160916c3ea..ec7dd944d7c 100644 --- a/java/ql/src/Performance/InnerClassCouldBeStatic.ql +++ b/java/ql/src/Performance/InnerClassCouldBeStatic.ql @@ -17,6 +17,7 @@ import java * since package-protected fields are not inherited by classes in different * packages, but it's enough for the purposes of this check. */ +pragma[nomagic] predicate inherits(Class c, Field f) { f = c.getAField() or diff --git a/java/ql/src/Security/CWE/CWE-297/InsecureJavaMail.qhelp b/java/ql/src/Security/CWE/CWE-297/InsecureJavaMail.qhelp new file mode 100644 index 00000000000..406d9666ce6 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-297/InsecureJavaMail.qhelp @@ -0,0 +1,35 @@ + + + + +

    JavaMail is commonly used in Java applications to send emails. There are popular third-party libraries like Apache Commons Email which are built on JavaMail and facilitate integration. Authenticated mail sessions require user credentials and mail sessions can require SSL/TLS authentication. It is a common security vulnerability that host-specific certificate data is not validated or is incorrectly validated. Failing to validate the certificate makes the SSL session susceptible to a man-in-the-middle attack.

    +

    This query checks whether the SSL certificate is validated when credentials are used and SSL is enabled in email communications.

    +

    The query has code for both plain JavaMail invocation and mailing through Apache SimpleMail to make it more comprehensive.

    +
    + + +

    Validate SSL certificate when sensitive information is sent in email communications.

    +
    + + +

    The following two examples show two ways of configuring secure emails through JavaMail or Apache SimpleMail. In the 'BAD' case, +credentials are sent in an SSL session without certificate validation. In the 'GOOD' case, the certificate is validated.

    + + +
    + + +
  • + Jakarta Mail: + SSL Notes. +
  • +
  • + Apache Commons: + Email security. +
  • +
  • + Log4j2: + Add support for specifying an SSL configuration for SmtpAppender (CVE-2020-9488). +
  • +
    +
    \ No newline at end of file diff --git a/java/ql/src/Security/CWE/CWE-297/InsecureJavaMail.ql b/java/ql/src/Security/CWE/CWE-297/InsecureJavaMail.ql new file mode 100644 index 00000000000..10e122d31c7 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-297/InsecureJavaMail.ql @@ -0,0 +1,24 @@ +/** + * @name Insecure JavaMail SSL Configuration + * @description Configuring a Java application to use authenticated mail session + * over SSL without certificate validation + * makes the session susceptible to a man-in-the-middle attack. + * @kind problem + * @problem.severity warning + * @security-severity 5.9 + * @precision medium + * @id java/insecure-smtp-ssl + * @tags security + * external/cwe/cwe-297 + */ + +import java +import semmle.code.java.security.Mail + +from MethodAccess ma +where + ma.getMethod() instanceof MailSessionGetInstanceMethod and + isInsecureMailPropertyConfig(ma.getArgument(0).(VarAccess).getVariable()) + or + enablesEmailSsl(ma) and not hasSslCertificateCheck(ma.getQualifier().(VarAccess).getVariable()) +select ma, "Java mailing has insecure SSL configuration" diff --git a/java/ql/src/experimental/Security/CWE/CWE-297/JavaMail.java b/java/ql/src/Security/CWE/CWE-297/JavaMail.java similarity index 100% rename from java/ql/src/experimental/Security/CWE/CWE-297/JavaMail.java rename to java/ql/src/Security/CWE/CWE-297/JavaMail.java diff --git a/java/ql/src/experimental/Security/CWE/CWE-297/SimpleMail.java b/java/ql/src/Security/CWE/CWE-297/SimpleMail.java similarity index 100% rename from java/ql/src/experimental/Security/CWE/CWE-297/SimpleMail.java rename to java/ql/src/Security/CWE/CWE-297/SimpleMail.java diff --git a/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.qhelp b/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.qhelp index e3c1d8df033..d1933ad4ac2 100644 --- a/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.qhelp +++ b/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.qhelp @@ -15,7 +15,7 @@ may have unforeseen effects, such as the execution of arbitrary code.

    There are many different serialization frameworks. This query currently supports Kryo, XmlDecoder, XStream, SnakeYaml, JYaml, JsonIO, YAMLBeans, HessianBurlap, Castor, Burlap, -Jackson, Jabsorb, Jodd JSON, Flexjson and Java IO serialization through +Jackson, Jabsorb, Jodd JSON, Flexjson, Gson and Java IO serialization through ObjectInputStream/ObjectOutputStream.

    @@ -113,6 +113,10 @@ Jodd JSON documentation on deserialization: RCE in Flexjson: Flexjson deserialization. +
  • +Android Intent deserialization vulnerabilities with GSON parser: +Insecure use of JSON parsers. +
  • diff --git a/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsApiCall.ql b/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsApiCall.ql index 13cb2a7a69d..a787d2ddfd3 100644 --- a/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsApiCall.ql +++ b/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsApiCall.ql @@ -27,9 +27,30 @@ class HardcodedCredentialApiCallConfiguration extends DataFlow::Configuration { override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { node1.asExpr().getType() instanceof TypeString and - exists(MethodAccess ma | ma.getMethod().hasName(["getBytes", "toCharArray"]) | - node2.asExpr() = ma and - ma.getQualifier() = node1.asExpr() + ( + exists(MethodAccess ma | ma.getMethod().hasName(["getBytes", "toCharArray"]) | + node2.asExpr() = ma and + ma.getQualifier() = node1.asExpr() + ) + or + // These base64 routines are usually taint propagators, and this is not a general + // TaintTracking::Configuration, so we must specifically include them here + // as a common transform applied to a constant before passing to a remote API. + exists(MethodAccess ma | + ma.getMethod() + .hasQualifiedName([ + "java.util", "cn.hutool.core.codec", "org.apache.shiro.codec", + "apache.commons.codec.binary", "org.springframework.util" + ], ["Base64$Encoder", "Base64$Decoder", "Base64", "Base64Utils"], + [ + "encode", "encodeToString", "decode", "decodeBase64", "encodeBase64", + "encodeBase64Chunked", "encodeBase64String", "encodeBase64URLSafe", + "encodeBase64URLSafeString" + ]) + | + node1.asExpr() = ma.getArgument(0) and + node2.asExpr() = ma + ) ) } diff --git a/java/ql/src/Security/CWE/CWE-798/SensitiveApi.qll b/java/ql/src/Security/CWE/CWE-798/SensitiveApi.qll index 56072496293..86ecd73f289 100644 --- a/java/ql/src/Security/CWE/CWE-798/SensitiveApi.qll +++ b/java/ql/src/Security/CWE/CWE-798/SensitiveApi.qll @@ -11,126 +11,119 @@ predicate javaApiCallablePasswordParam(Callable c, int i) { private predicate javaApiCallablePasswordParam(string s) { // Auto-generated using an auxiliary query run on the JDK source code. - s = "com.sun.crypto.provider.JceKeyStore;engineLoad(InputStream, char[]);1" or - s = "com.sun.crypto.provider.JceKeyStore;engineGetKey(String, char[]);1" or - s = "com.sun.crypto.provider.JceKeyStore;engineSetKeyEntry(String, Key, char[], Certificate[]);2" or - s = "com.sun.crypto.provider.JceKeyStore;engineStore(OutputStream, char[]);1" or - s = "com.sun.crypto.provider.JceKeyStore;getPreKeyedHash(char[]);0" or - s = "com.sun.crypto.provider.KeyProtector;KeyProtector(char[]);0" or - s = "com.sun.crypto.provider.PBKDF2KeyImpl;deriveKey(Mac, byte[], byte[], int, int);1" or - s = "com.sun.crypto.provider.PBKDF2KeyImpl;getPasswordBytes(char[]);0" or - s = "com.sun.istack.internal.tools.DefaultAuthenticator$AuthInfo;AuthInfo(URL, String, String);2" or - s = "com.sun.net.httpserver.BasicAuthenticator;checkCredentials(String, String);1" or - s = "com.sun.net.ssl.KeyManagerFactory;init(KeyStore, char[]);1" or - s = "com.sun.net.ssl.KeyManagerFactorySpi;engineInit(KeyStore, char[]);1" or - s = "com.sun.net.ssl.KeyManagerFactorySpiWrapper;engineInit(KeyStore, char[]);1" or s = - "com.sun.org.apache.xml.internal.security.keys.keyresolver.implementations.PrivateKeyResolver;PrivateKeyResolver(KeyStore, char[]);1" or - s = - "com.sun.org.apache.xml.internal.security.keys.keyresolver.implementations.SecretKeyResolver;SecretKeyResolver(KeyStore, char[]);1" or - s = "com.sun.rowset.JdbcRowSetImpl;JdbcRowSetImpl(String, String, String);2" or - s = "com.sun.rowset.JdbcRowSetImpl;setPassword(String);0" or - s = "com.sun.security.auth.module.JndiLoginModule;verifyPassword(String, String);1" or - s = "com.sun.security.auth.module.JndiLoginModule;verifyPassword(String, String);0" or - s = "com.sun.security.ntlm.Client;Client(String, String, String, String, char[]);4" or - s = "com.sun.security.ntlm.NTLM;getP2(char[]);0" or - s = "com.sun.security.ntlm.NTLM;getP1(char[]);0" or - s = - "com.sun.security.sasl.digest.DigestMD5Base;generateResponseValue(String, String, String, String, String, char[], byte[], byte[], int, byte[]);5" or - s = - "com.sun.security.sasl.digest.DigestMD5Server;generateResponseAuth(String, char[], byte[], int, byte[]);1" or - s = "com.sun.tools.internal.ws.wscompile.AuthInfo;AuthInfo(URL, String, String);2" or - s = "java.net.PasswordAuthentication;PasswordAuthentication(String, char[]);1" or - s = "java.security.KeyStore;setKeyEntry(String, Key, char[], Certificate[]);2" or - s = "java.security.KeyStore;store(OutputStream, char[]);1" or - s = "java.security.KeyStore;getKey(String, char[]);1" or - s = "java.security.KeyStore;load(InputStream, char[]);1" or - s = - "java.security.KeyStore$PasswordProtection;PasswordProtection(char[], String, AlgorithmParameterSpec);0" or - s = "java.security.KeyStore$PasswordProtection;PasswordProtection(char[]);0" or - s = "java.security.KeyStoreSpi;engineStore(OutputStream, char[]);1" or - s = "java.security.KeyStoreSpi;engineLoad(InputStream, char[]);1" or - s = "java.security.KeyStoreSpi;engineSetKeyEntry(String, Key, char[], Certificate[]);2" or - s = "java.security.KeyStoreSpi;engineGetKey(String, char[]);1" or - s = "java.sql.DriverManager;getConnection(String, String, String);2" or - s = "javax.crypto.spec.PBEKeySpec;PBEKeySpec(char[], byte[], int);0" or - s = "javax.crypto.spec.PBEKeySpec;PBEKeySpec(char[], byte[], int, int);0" or - s = "javax.crypto.spec.PBEKeySpec;PBEKeySpec(char[]);0" or - s = "javax.net.ssl.KeyManagerFactory;init(KeyStore, char[]);1" or - s = "javax.net.ssl.KeyManagerFactorySpi;engineInit(KeyStore, char[]);1" or - s = "javax.security.auth.callback.PasswordCallback;setPassword(char[]);0" or - s = "javax.security.auth.kerberos.KerberosKey;KerberosKey(KerberosPrincipal, char[], String);1" or - s = "javax.security.auth.kerberos.KeyImpl;KeyImpl(KerberosPrincipal, char[], String);1" or - s = "javax.sql.ConnectionPoolDataSource;getPooledConnection(String, String);1" or - s = "javax.sql.DataSource;getConnection(String, String);1" or - s = "javax.sql.RowSet;setPassword(String);0" or - s = "javax.sql.XADataSource;getXAConnection(String, String);1" or - s = "sun.net.ftp.FtpClient;login(String, char[]);1" or - s = "sun.net.ftp.FtpClient;login(String, char[], String);1" or - s = "sun.net.ftp.impl.FtpClient;login(String, char[], String);1" or - s = "sun.net.ftp.impl.FtpClient;login(String, char[]);1" or - s = "sun.net.ftp.impl.FtpClient;tryLogin(String, char[]);1" or - s = "sun.net.www.protocol.http.DigestAuthentication;encode(String, char[], MessageDigest);1" or - s = - "sun.net.www.protocol.http.DigestAuthentication;computeDigest(boolean, String, char[], String, String, String, String, String, String);2" or - s = "sun.security.krb5.EncryptionKey;acquireSecretKey(char[], String, int, byte[]);0" or - s = "sun.security.krb5.EncryptionKey;stringToKey(char[], String, byte[], int);0" or - s = "sun.security.krb5.EncryptionKey;EncryptionKey(char[], String, String);0" or - s = "sun.security.krb5.EncryptionKey;acquireSecretKeys(char[], String);0" or - s = - "sun.security.krb5.EncryptionKey;acquireSecretKey(PrincipalName, char[], int, SaltAndParams);1" or - s = "sun.security.krb5.KrbAsRep;decryptUsingPassword(char[], KrbAsReq, PrincipalName);0" or - s = "sun.security.krb5.internal.crypto.Aes128;stringToKey(char[], String, byte[]);0" or - s = "sun.security.krb5.internal.crypto.Aes256;stringToKey(char[], String, byte[]);0" or - s = "sun.security.krb5.internal.crypto.ArcFourHmac;stringToKey(char[]);0" or - s = "sun.security.krb5.internal.crypto.Des;char_to_key(char[]);0" or - s = "sun.security.krb5.internal.crypto.Des;string_to_key_bytes(char[]);0" or - s = "sun.security.krb5.internal.crypto.dk.AesDkCrypto;stringToKey(char[], String, byte[]);0" or - s = "sun.security.krb5.internal.crypto.dk.ArcFourCrypto;stringToKey(char[]);0" or - s = "sun.security.pkcs11.P11KeyStore;engineLoad(InputStream, char[]);1" or - s = "sun.security.pkcs11.P11KeyStore;engineGetKey(String, char[]);1" or - s = "sun.security.pkcs11.P11KeyStore;engineStore(OutputStream, char[]);1" or - s = "sun.security.pkcs11.P11KeyStore;engineSetKeyEntry(String, Key, char[], Certificate[]);2" or - s = "sun.security.pkcs11.P11KeyStore$PasswordCallbackHandler;PasswordCallbackHandler(char[]);0" or - s = "sun.security.pkcs11.Secmod$KeyStoreLoadParameter;KeyStoreLoadParameter(TrustType, char[]);1" or - s = "sun.security.pkcs12.PKCS12KeyStore;engineGetKey(String, char[]);1" or - s = "sun.security.pkcs12.PKCS12KeyStore;calculateMac(char[], byte[]);0" or - s = "sun.security.pkcs12.PKCS12KeyStore;encryptContent(byte[], char[]);1" or - s = "sun.security.pkcs12.PKCS12KeyStore;loadSafeContents(DerInputStream, char[]);1" or - s = "sun.security.pkcs12.PKCS12KeyStore;engineSetKeyEntry(String, Key, char[], Certificate[]);2" or - s = "sun.security.pkcs12.PKCS12KeyStore;engineStore(OutputStream, char[]);1" or - s = "sun.security.pkcs12.PKCS12KeyStore;engineLoad(InputStream, char[]);1" or - s = "sun.security.pkcs12.PKCS12KeyStore;getPBEKey(char[]);0" or - s = "sun.security.pkcs12.PKCS12KeyStore;createEncryptedData(char[]);0" or - s = "sun.security.provider.DomainKeyStore;engineGetKey(String, char[]);1" or - s = "sun.security.provider.DomainKeyStore;engineSetKeyEntry(String, Key, char[], Certificate[]);2" or - s = "sun.security.provider.DomainKeyStore;engineStore(OutputStream, char[]);1" or - s = "sun.security.provider.DomainKeyStore;engineLoad(InputStream, char[]);1" or - s = "sun.security.provider.JavaKeyStore;engineSetKeyEntry(String, Key, char[], Certificate[]);2" or - s = "sun.security.provider.JavaKeyStore;engineLoad(InputStream, char[]);1" or - s = "sun.security.provider.JavaKeyStore;getPreKeyedHash(char[]);0" or - s = "sun.security.provider.JavaKeyStore;engineGetKey(String, char[]);1" or - s = "sun.security.provider.JavaKeyStore;engineStore(OutputStream, char[]);1" or - s = "sun.security.provider.KeyProtector;KeyProtector(char[]);0" or - s = "sun.security.ssl.KeyManagerFactoryImpl$SunX509;engineInit(KeyStore, char[]);1" or - s = "sun.security.ssl.KeyManagerFactoryImpl$X509;engineInit(KeyStore, char[]);1" or - s = "sun.security.ssl.SunX509KeyManagerImpl;SunX509KeyManagerImpl(KeyStore, char[]);1" or - s = "sun.security.tools.keytool.Main;getNewPasswd(String, char[]);1" or - s = - "sun.tools.jconsole.ConnectDialog;setConnectionParameters(String, String, int, String, String, String);4" or - s = "sun.tools.jconsole.JConsole;addHost(String, int, String, String);3" or - s = "sun.tools.jconsole.JConsole;addUrl(String, String, String, boolean);2" or - s = "sun.tools.jconsole.JConsole;addHost(String, int, String, String, boolean);3" or - s = "sun.tools.jconsole.JConsole;showConnectDialog(String, String, int, String, String, String);4" or - s = "sun.tools.jconsole.JConsole;failed(Exception, String, String, String);3" or - s = "sun.tools.jconsole.ProxyClient;getCacheKey(String, String, String);2" or - s = "sun.tools.jconsole.ProxyClient;setParameters(JMXServiceURL, String, String);2" or - s = "sun.tools.jconsole.ProxyClient;ProxyClient(String, String, String);2" or - s = "sun.tools.jconsole.ProxyClient;ProxyClient(String, int, String, String);3" or - s = "sun.tools.jconsole.ProxyClient;getProxyClient(String, int, String, String);3" or - s = "sun.tools.jconsole.ProxyClient;getProxyClient(String, String, String);2" or - s = "sun.tools.jconsole.ProxyClient;getCacheKey(String, int, String, String);3" or - s = "com.amazonaws.auth.BasicAWSCredentials;BasicAWSCredentials(String, String);1" + [ + "com.sun.crypto.provider.JceKeyStore;engineLoad(InputStream, char[]);1", + "com.sun.crypto.provider.JceKeyStore;engineGetKey(String, char[]);1", + "com.sun.net.ssl.KeyManagerFactory;init(KeyStore, char[]);1", + "sun.tools.jconsole.JConsole;addUrl(String, String, String, boolean);2", + "sun.tools.jconsole.JConsole;addHost(String, int, String, String, boolean);3", + "sun.tools.jconsole.JConsole;showConnectDialog(String, String, int, String, String, String);4", + "sun.tools.jconsole.JConsole;failed(Exception, String, String, String);3", + "sun.tools.jconsole.ProxyClient;getCacheKey(String, String, String);2", + "sun.tools.jconsole.ProxyClient;setParameters(JMXServiceURL, String, String);2", + "sun.tools.jconsole.ProxyClient;ProxyClient(String, String, String);2", + "sun.tools.jconsole.ProxyClient;ProxyClient(String, int, String, String);3", + "sun.tools.jconsole.ProxyClient;getProxyClient(String, int, String, String);3", + "sun.tools.jconsole.ProxyClient;getProxyClient(String, String, String);2", + "com.sun.net.ssl.KeyManagerFactorySpi;engineInit(KeyStore, char[]);1", + "sun.tools.jconsole.ProxyClient;getCacheKey(String, int, String, String);3", + "com.sun.net.ssl.KeyManagerFactorySpiWrapper;engineInit(KeyStore, char[]);1", + "com.sun.org.apache.xml.internal.security.keys.keyresolver.implementations.PrivateKeyResolver;PrivateKeyResolver(KeyStore, char[]);1", + "com.sun.org.apache.xml.internal.security.keys.keyresolver.implementations.SecretKeyResolver;SecretKeyResolver(KeyStore, char[]);1", + "com.sun.rowset.JdbcRowSetImpl;JdbcRowSetImpl(String, String, String);2", + "com.sun.rowset.JdbcRowSetImpl;setPassword(String);0", + "com.sun.security.auth.module.JndiLoginModule;verifyPassword(String, String);1", + "com.sun.security.auth.module.JndiLoginModule;verifyPassword(String, String);0", + "com.sun.security.ntlm.Client;Client(String, String, String, String, char[]);4", + "com.sun.crypto.provider.JceKeyStore;engineSetKeyEntry(String, Key, char[], Certificate[]);2", + "com.sun.security.ntlm.NTLM;getP2(char[]);0", "com.sun.security.ntlm.NTLM;getP1(char[]);0", + "com.sun.security.sasl.digest.DigestMD5Base;generateResponseValue(String, String, String, String, String, char[], byte[], byte[], int, byte[]);5", + "com.sun.security.sasl.digest.DigestMD5Server;generateResponseAuth(String, char[], byte[], int, byte[]);1", + "com.sun.tools.internal.ws.wscompile.AuthInfo;AuthInfo(URL, String, String);2", + "java.net.PasswordAuthentication;PasswordAuthentication(String, char[]);1", + "java.security.KeyStore;setKeyEntry(String, Key, char[], Certificate[]);2", + "java.security.KeyStore;store(OutputStream, char[]);1", + "java.security.KeyStore;getKey(String, char[]);1", + "java.security.KeyStore;load(InputStream, char[]);1", + "com.sun.crypto.provider.JceKeyStore;engineStore(OutputStream, char[]);1", + "java.security.KeyStore$PasswordProtection;PasswordProtection(char[], String, AlgorithmParameterSpec);0", + "java.security.KeyStore$PasswordProtection;PasswordProtection(char[]);0", + "java.security.KeyStoreSpi;engineStore(OutputStream, char[]);1", + "java.security.KeyStoreSpi;engineLoad(InputStream, char[]);1", + "java.security.KeyStoreSpi;engineSetKeyEntry(String, Key, char[], Certificate[]);2", + "java.security.KeyStoreSpi;engineGetKey(String, char[]);1", + "java.sql.DriverManager;getConnection(String, String, String);2", + "javax.crypto.spec.PBEKeySpec;PBEKeySpec(char[], byte[], int);0", + "javax.crypto.spec.PBEKeySpec;PBEKeySpec(char[], byte[], int, int);0", + "javax.crypto.spec.PBEKeySpec;PBEKeySpec(char[]);0", + "com.sun.crypto.provider.JceKeyStore;getPreKeyedHash(char[]);0", + "javax.net.ssl.KeyManagerFactory;init(KeyStore, char[]);1", + "javax.net.ssl.KeyManagerFactorySpi;engineInit(KeyStore, char[]);1", + "javax.security.auth.callback.PasswordCallback;setPassword(char[]);0", + "javax.security.auth.kerberos.KerberosKey;KerberosKey(KerberosPrincipal, char[], String);1", + "javax.security.auth.kerberos.KeyImpl;KeyImpl(KerberosPrincipal, char[], String);1", + "javax.sql.ConnectionPoolDataSource;getPooledConnection(String, String);1", + "javax.sql.DataSource;getConnection(String, String);1", + "javax.sql.RowSet;setPassword(String);0", + "javax.sql.XADataSource;getXAConnection(String, String);1", + "sun.net.ftp.FtpClient;login(String, char[]);1", + "com.sun.crypto.provider.KeyProtector;KeyProtector(char[]);0", + "sun.net.ftp.FtpClient;login(String, char[], String);1", + "sun.net.ftp.impl.FtpClient;login(String, char[], String);1", + "sun.net.ftp.impl.FtpClient;login(String, char[]);1", + "sun.net.ftp.impl.FtpClient;tryLogin(String, char[]);1", + "sun.net.www.protocol.http.DigestAuthentication;encode(String, char[], MessageDigest);1", + "sun.net.www.protocol.http.DigestAuthentication;computeDigest(boolean, String, char[], String, String, String, String, String, String);2", + "sun.security.krb5.EncryptionKey;acquireSecretKey(char[], String, int, byte[]);0", + "sun.security.krb5.EncryptionKey;stringToKey(char[], String, byte[], int);0", + "sun.security.krb5.EncryptionKey;EncryptionKey(char[], String, String);0", + "sun.security.krb5.EncryptionKey;acquireSecretKeys(char[], String);0", + "com.sun.crypto.provider.PBKDF2KeyImpl;deriveKey(Mac, byte[], byte[], int, int);1", + "sun.security.krb5.EncryptionKey;acquireSecretKey(PrincipalName, char[], int, SaltAndParams);1", + "sun.security.krb5.KrbAsRep;decryptUsingPassword(char[], KrbAsReq, PrincipalName);0", + "sun.security.krb5.internal.crypto.Aes128;stringToKey(char[], String, byte[]);0", + "sun.security.krb5.internal.crypto.Aes256;stringToKey(char[], String, byte[]);0", + "sun.security.krb5.internal.crypto.ArcFourHmac;stringToKey(char[]);0", + "sun.security.krb5.internal.crypto.Des;char_to_key(char[]);0", + "sun.security.krb5.internal.crypto.Des;string_to_key_bytes(char[]);0", + "sun.security.krb5.internal.crypto.dk.AesDkCrypto;stringToKey(char[], String, byte[]);0", + "sun.security.krb5.internal.crypto.dk.ArcFourCrypto;stringToKey(char[]);0", + "sun.security.pkcs11.P11KeyStore;engineLoad(InputStream, char[]);1", + "com.sun.crypto.provider.PBKDF2KeyImpl;getPasswordBytes(char[]);0", + "sun.security.pkcs11.P11KeyStore;engineGetKey(String, char[]);1", + "sun.security.pkcs11.P11KeyStore;engineStore(OutputStream, char[]);1", + "sun.security.pkcs11.P11KeyStore;engineSetKeyEntry(String, Key, char[], Certificate[]);2", + "sun.security.pkcs11.P11KeyStore$PasswordCallbackHandler;PasswordCallbackHandler(char[]);0", + "sun.security.pkcs11.Secmod$KeyStoreLoadParameter;KeyStoreLoadParameter(TrustType, char[]);1", + "sun.security.pkcs12.PKCS12KeyStore;engineGetKey(String, char[]);1", + "sun.security.pkcs12.PKCS12KeyStore;calculateMac(char[], byte[]);0", + "sun.security.pkcs12.PKCS12KeyStore;encryptContent(byte[], char[]);1", + "sun.security.pkcs12.PKCS12KeyStore;loadSafeContents(DerInputStream, char[]);1", + "sun.security.pkcs12.PKCS12KeyStore;engineSetKeyEntry(String, Key, char[], Certificate[]);2", + "com.sun.istack.internal.tools.DefaultAuthenticator$AuthInfo;AuthInfo(URL, String, String);2", + "sun.security.pkcs12.PKCS12KeyStore;engineStore(OutputStream, char[]);1", + "sun.security.pkcs12.PKCS12KeyStore;engineLoad(InputStream, char[]);1", + "sun.security.pkcs12.PKCS12KeyStore;getPBEKey(char[]);0", + "sun.security.pkcs12.PKCS12KeyStore;createEncryptedData(char[]);0", + "sun.security.provider.DomainKeyStore;engineGetKey(String, char[]);1", + "sun.security.provider.DomainKeyStore;engineSetKeyEntry(String, Key, char[], Certificate[]);2", + "sun.security.provider.DomainKeyStore;engineStore(OutputStream, char[]);1", + "sun.security.provider.DomainKeyStore;engineLoad(InputStream, char[]);1", + "sun.security.provider.JavaKeyStore;engineSetKeyEntry(String, Key, char[], Certificate[]);2", + "sun.security.provider.JavaKeyStore;engineLoad(InputStream, char[]);1", + "com.sun.net.httpserver.BasicAuthenticator;checkCredentials(String, String);1", + "sun.security.provider.JavaKeyStore;getPreKeyedHash(char[]);0", + "sun.security.provider.JavaKeyStore;engineGetKey(String, char[]);1", + "sun.security.provider.JavaKeyStore;engineStore(OutputStream, char[]);1", + "sun.security.provider.KeyProtector;KeyProtector(char[]);0", + "sun.security.ssl.KeyManagerFactoryImpl$SunX509;engineInit(KeyStore, char[]);1", + "sun.security.ssl.KeyManagerFactoryImpl$X509;engineInit(KeyStore, char[]);1", + "sun.security.ssl.SunX509KeyManagerImpl;SunX509KeyManagerImpl(KeyStore, char[]);1", + "sun.security.tools.keytool.Main;getNewPasswd(String, char[]);1", + "sun.tools.jconsole.ConnectDialog;setConnectionParameters(String, String, int, String, String, String);4", + "sun.tools.jconsole.JConsole;addHost(String, int, String, String);3" + ] } /** @@ -144,65 +137,56 @@ predicate javaApiCallableUsernameParam(Callable c, int i) { private predicate javaApiCallableUsernameParam(string s) { // Auto-generated using an auxiliary query run on the JDK source code. - s = "com.sun.istack.internal.tools.DefaultAuthenticator$AuthInfo;AuthInfo(URL, String, String);1" or s = - "com.sun.jndi.ldap.DigestClientId;DigestClientId(int, String, int, String, Control[], OutputStream, String, String, Object, Hashtable);7" or - s = - "com.sun.jndi.ldap.LdapClient;getInstance(boolean, String, int, String, int, int, OutputStream, int, String, Control[], String, String, Object, Hashtable);11" or - s = - "com.sun.jndi.ldap.LdapPoolManager;getLdapClient(String, int, String, int, int, OutputStream, int, String, Control[], String, String, Object, Hashtable);10" or - s = - "com.sun.jndi.ldap.SimpleClientId;SimpleClientId(int, String, int, String, Control[], OutputStream, String, String, Object);7" or - s = "com.sun.net.httpserver.BasicAuthenticator;checkCredentials(String, String);0" or - s = "com.sun.net.httpserver.HttpPrincipal;HttpPrincipal(String, String);0" or - s = "com.sun.rowset.JdbcRowSetImpl;JdbcRowSetImpl(String, String, String);1" or - s = "com.sun.security.ntlm.Client;Client(String, String, String, String, char[]);2" or - s = "com.sun.security.ntlm.Server;getPassword(String, String);1" or - s = - "com.sun.security.sasl.digest.DigestMD5Server;generateResponseAuth(String, char[], byte[], int, byte[]);0" or - s = "com.sun.tools.internal.ws.wscompile.AuthInfo;AuthInfo(URL, String, String);1" or - s = "java.net.PasswordAuthentication;PasswordAuthentication(String, char[]);0" or - s = "java.sql.DriverManager;getConnection(String, String, String);1" or - s = - "javax.print.attribute.standard.JobOriginatingUserName;JobOriginatingUserName(String, Locale);0" or - s = "javax.print.attribute.standard.RequestingUserName;RequestingUserName(String, Locale);0" or - s = "javax.sql.ConnectionPoolDataSource;getPooledConnection(String, String);0" or - s = "javax.sql.DataSource;getConnection(String, String);0" or - s = "javax.sql.XADataSource;getXAConnection(String, String);0" or - s = "sun.jvmstat.perfdata.monitor.protocol.local.LocalVmManager;LocalVmManager(String);0" or - s = "sun.jvmstat.perfdata.monitor.protocol.local.PerfDataFile;getFile(String, int);0" or - s = "sun.jvmstat.perfdata.monitor.protocol.local.PerfDataFile;getTempDirectory(String);0" or - s = - "sun.jvmstat.perfdata.monitor.protocol.rmi.RemoteVmManager;RemoteVmManager(RemoteHost, String);1" or - s = "sun.misc.Perf;attach(String, int, int);0" or - s = "sun.misc.Perf;attach(String, int, String);0" or - s = "sun.misc.Perf;attachImpl(String, int, int);0" or - s = "sun.net.ftp.FtpClient;login(String, char[], String);0" or - s = "sun.net.ftp.FtpClient;login(String, char[]);0" or - s = "sun.net.ftp.FtpDirEntry;setUser(String);0" or - s = "sun.net.ftp.impl.FtpClient;login(String, char[], String);0" or - s = "sun.net.ftp.impl.FtpClient;tryLogin(String, char[]);0" or - s = "sun.net.ftp.impl.FtpClient;login(String, char[]);0" or - s = - "sun.net.www.protocol.http.DigestAuthentication;computeDigest(boolean, String, char[], String, String, String, String, String, String);1" or - s = "sun.security.acl.PrincipalImpl;PrincipalImpl(String);0" or - s = - "sun.tools.jconsole.ConnectDialog;setConnectionParameters(String, String, int, String, String, String);3" or - s = "sun.tools.jconsole.JConsole;failed(Exception, String, String, String);2" or - s = "sun.tools.jconsole.JConsole;addHost(String, int, String, String, boolean);2" or - s = "sun.tools.jconsole.JConsole;addUrl(String, String, String, boolean);1" or - s = "sun.tools.jconsole.JConsole;addHost(String, int, String, String);2" or - s = "sun.tools.jconsole.JConsole;showConnectDialog(String, String, int, String, String, String);3" or - s = "sun.tools.jconsole.ProxyClient;ProxyClient(String, String, String);1" or - s = "sun.tools.jconsole.ProxyClient;ProxyClient(String, int, String, String);2" or - s = "sun.tools.jconsole.ProxyClient;setParameters(JMXServiceURL, String, String);1" or - s = "sun.tools.jconsole.ProxyClient;getCacheKey(String, String, String);1" or - s = "sun.tools.jconsole.ProxyClient;getCacheKey(String, int, String, String);2" or - s = "sun.tools.jconsole.ProxyClient;getProxyClient(String, String, String);1" or - s = "sun.tools.jconsole.ProxyClient;getConnectionName(String, String);1" or - s = "sun.tools.jconsole.ProxyClient;getProxyClient(String, int, String, String);2" or - s = "sun.tools.jconsole.ProxyClient;getConnectionName(String, int, String);2" or - s = "com.amazonaws.auth.BasicAWSCredentials;BasicAWSCredentials(String, String);0" + [ + "com.sun.istack.internal.tools.DefaultAuthenticator$AuthInfo;AuthInfo(URL, String, String);1", + "com.sun.jndi.ldap.DigestClientId;DigestClientId(int, String, int, String, Control[], OutputStream, String, String, Object, Hashtable);7", + "com.sun.security.sasl.digest.DigestMD5Server;generateResponseAuth(String, char[], byte[], int, byte[]);0", + "com.sun.tools.internal.ws.wscompile.AuthInfo;AuthInfo(URL, String, String);1", + "java.net.PasswordAuthentication;PasswordAuthentication(String, char[]);0", + "java.sql.DriverManager;getConnection(String, String, String);1", + "javax.print.attribute.standard.JobOriginatingUserName;JobOriginatingUserName(String, Locale);0", + "javax.print.attribute.standard.RequestingUserName;RequestingUserName(String, Locale);0", + "javax.sql.ConnectionPoolDataSource;getPooledConnection(String, String);0", + "javax.sql.DataSource;getConnection(String, String);0", + "javax.sql.XADataSource;getXAConnection(String, String);0", + "sun.jvmstat.perfdata.monitor.protocol.local.LocalVmManager;LocalVmManager(String);0", + "com.sun.jndi.ldap.LdapClient;getInstance(boolean, String, int, String, int, int, OutputStream, int, String, Control[], String, String, Object, Hashtable);11", + "sun.jvmstat.perfdata.monitor.protocol.local.PerfDataFile;getFile(String, int);0", + "sun.jvmstat.perfdata.monitor.protocol.local.PerfDataFile;getTempDirectory(String);0", + "sun.jvmstat.perfdata.monitor.protocol.rmi.RemoteVmManager;RemoteVmManager(RemoteHost, String);1", + "sun.misc.Perf;attach(String, int, int);0", "sun.misc.Perf;attach(String, int, String);0", + "sun.misc.Perf;attachImpl(String, int, int);0", + "sun.net.ftp.FtpClient;login(String, char[], String);0", + "sun.net.ftp.FtpClient;login(String, char[]);0", "sun.net.ftp.FtpDirEntry;setUser(String);0", + "sun.net.ftp.impl.FtpClient;login(String, char[], String);0", + "com.sun.jndi.ldap.LdapPoolManager;getLdapClient(String, int, String, int, int, OutputStream, int, String, Control[], String, String, Object, Hashtable);10", + "sun.net.ftp.impl.FtpClient;tryLogin(String, char[]);0", + "sun.net.ftp.impl.FtpClient;login(String, char[]);0", + "sun.net.www.protocol.http.DigestAuthentication;computeDigest(boolean, String, char[], String, String, String, String, String, String);1", + "sun.security.acl.PrincipalImpl;PrincipalImpl(String);0", + "sun.tools.jconsole.ConnectDialog;setConnectionParameters(String, String, int, String, String, String);3", + "sun.tools.jconsole.JConsole;failed(Exception, String, String, String);2", + "sun.tools.jconsole.JConsole;addHost(String, int, String, String, boolean);2", + "sun.tools.jconsole.JConsole;addUrl(String, String, String, boolean);1", + "sun.tools.jconsole.JConsole;addHost(String, int, String, String);2", + "sun.tools.jconsole.JConsole;showConnectDialog(String, String, int, String, String, String);3", + "com.sun.jndi.ldap.SimpleClientId;SimpleClientId(int, String, int, String, Control[], OutputStream, String, String, Object);7", + "sun.tools.jconsole.ProxyClient;ProxyClient(String, String, String);1", + "sun.tools.jconsole.ProxyClient;ProxyClient(String, int, String, String);2", + "sun.tools.jconsole.ProxyClient;setParameters(JMXServiceURL, String, String);1", + "sun.tools.jconsole.ProxyClient;getCacheKey(String, String, String);1", + "sun.tools.jconsole.ProxyClient;getCacheKey(String, int, String, String);2", + "sun.tools.jconsole.ProxyClient;getProxyClient(String, String, String);1", + "sun.tools.jconsole.ProxyClient;getConnectionName(String, String);1", + "sun.tools.jconsole.ProxyClient;getProxyClient(String, int, String, String);2", + "sun.tools.jconsole.ProxyClient;getConnectionName(String, int, String);2", + "com.sun.net.httpserver.BasicAuthenticator;checkCredentials(String, String);0", + "com.sun.net.httpserver.HttpPrincipal;HttpPrincipal(String, String);0", + "com.sun.rowset.JdbcRowSetImpl;JdbcRowSetImpl(String, String, String);1", + "com.sun.security.ntlm.Client;Client(String, String, String, String, char[]);2", + "com.sun.security.ntlm.Server;getPassword(String, String);1" + ] } /** @@ -216,283 +200,221 @@ predicate javaApiCallableCryptoKeyParam(Callable c, int i) { private predicate javaApiCallableCryptoKeyParam(string s) { // Auto-generated using an auxiliary query run on the JDK source code. - s = "com.sun.crypto.provider.AESCipher;engineUnwrap(byte[], String, int);0" or - s = "com.sun.crypto.provider.AESCrypt;init(boolean, String, byte[]);2" or - s = "com.sun.crypto.provider.AESWrapCipher;engineUnwrap(byte[], String, int);0" or - s = "com.sun.crypto.provider.ARCFOURCipher;init(byte[]);0" or - s = "com.sun.crypto.provider.ARCFOURCipher;engineUnwrap(byte[], String, int);0" or - s = "com.sun.crypto.provider.BlowfishCipher;engineUnwrap(byte[], String, int);0" or - s = "com.sun.crypto.provider.BlowfishCrypt;init(boolean, String, byte[]);2" or - s = "com.sun.crypto.provider.CipherBlockChaining;init(boolean, String, byte[], byte[]);2" or - s = "com.sun.crypto.provider.CipherCore;unwrap(byte[], String, int);0" or - s = "com.sun.crypto.provider.CipherFeedback;init(boolean, String, byte[], byte[]);2" or - s = "com.sun.crypto.provider.CipherWithWrappingSpi;constructPublicKey(byte[], String);0" or - s = "com.sun.crypto.provider.CipherWithWrappingSpi;engineUnwrap(byte[], String, int);0" or - s = "com.sun.crypto.provider.CipherWithWrappingSpi;constructSecretKey(byte[], String);0" or - s = "com.sun.crypto.provider.CipherWithWrappingSpi;constructPrivateKey(byte[], String);0" or - s = "com.sun.crypto.provider.ConstructKeys;constructPrivateKey(byte[], String);0" or - s = "com.sun.crypto.provider.ConstructKeys;constructSecretKey(byte[], String);0" or - s = "com.sun.crypto.provider.ConstructKeys;constructPublicKey(byte[], String);0" or - s = "com.sun.crypto.provider.CounterMode;init(boolean, String, byte[], byte[]);2" or - s = "com.sun.crypto.provider.DESCipher;engineUnwrap(byte[], String, int);0" or - s = "com.sun.crypto.provider.DESCrypt;expandKey(byte[]);0" or - s = "com.sun.crypto.provider.DESCrypt;init(boolean, String, byte[]);2" or - s = "com.sun.crypto.provider.DESKey;DESKey(byte[], int);0" or - s = "com.sun.crypto.provider.DESKey;DESKey(byte[]);0" or - s = "com.sun.crypto.provider.DESKeyGenerator;setParityBit(byte[], int);0" or - s = "com.sun.crypto.provider.DESedeCipher;engineUnwrap(byte[], String, int);0" or - s = "com.sun.crypto.provider.DESedeKey;DESedeKey(byte[], int);0" or - s = "com.sun.crypto.provider.DESedeKey;DESedeKey(byte[]);0" or - s = "com.sun.crypto.provider.DESedeWrapCipher;engineUnwrap(byte[], String, int);0" or - s = "com.sun.crypto.provider.DHPrivateKey;DHPrivateKey(byte[]);0" or - s = "com.sun.crypto.provider.DHPublicKey;DHPublicKey(byte[]);0" or - s = "com.sun.crypto.provider.ElectronicCodeBook;init(boolean, String, byte[], byte[]);2" or - s = "com.sun.crypto.provider.FeedbackCipher;init(boolean, String, byte[], byte[]);2" or - s = "com.sun.crypto.provider.GaloisCounterMode;init(boolean, String, byte[], byte[]);2" or - s = "com.sun.crypto.provider.GaloisCounterMode;init(boolean, String, byte[], byte[], int);2" or - s = "com.sun.crypto.provider.JceKeyStore;engineSetKeyEntry(String, byte[], Certificate[]);1" or - s = "com.sun.crypto.provider.KeyProtector;recover(byte[]);0" or - s = "com.sun.crypto.provider.OutputFeedback;init(boolean, String, byte[], byte[]);2" or - s = "com.sun.crypto.provider.PBECipherCore;unwrap(byte[], String, int);0" or - s = "com.sun.crypto.provider.PBES1Core;unwrap(byte[], String, int);0" or - s = "com.sun.crypto.provider.PBES2Core;engineUnwrap(byte[], String, int);0" or - s = "com.sun.crypto.provider.PBEWithMD5AndDESCipher;engineUnwrap(byte[], String, int);0" or - s = "com.sun.crypto.provider.PBEWithMD5AndTripleDESCipher;engineUnwrap(byte[], String, int);0" or - s = "com.sun.crypto.provider.PCBC;init(boolean, String, byte[], byte[]);2" or - s = "com.sun.crypto.provider.PKCS12PBECipherCore;implUnwrap(byte[], String, int);0" or s = - "com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndDESede;engineUnwrap(byte[], String, int);0" or - s = - "com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndRC2_128;engineUnwrap(byte[], String, int);0" or - s = - "com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndRC2_40;engineUnwrap(byte[], String, int);0" or - s = - "com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndRC4_128;engineUnwrap(byte[], String, int);0" or - s = - "com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndRC4_40;engineUnwrap(byte[], String, int);0" or - s = "com.sun.crypto.provider.RC2Cipher;engineUnwrap(byte[], String, int);0" or - s = "com.sun.crypto.provider.RC2Crypt;init(boolean, String, byte[]);2" or - s = "com.sun.crypto.provider.RSACipher;engineUnwrap(byte[], String, int);0" or - s = "com.sun.crypto.provider.SymmetricCipher;init(boolean, String, byte[]);2" or - s = - "com.sun.crypto.provider.TlsMasterSecretGenerator$TlsMasterSecretKey;TlsMasterSecretKey(byte[], int, int);0" or - s = "java.security.KeyStore;setKeyEntry(String, byte[], Certificate[]);1" or - s = "java.security.KeyStoreSpi;engineSetKeyEntry(String, byte[], Certificate[]);1" or - s = "java.security.cert.X509CertSelector;setSubjectPublicKey(byte[]);0" or - s = "java.security.spec.EncodedKeySpec;EncodedKeySpec(byte[]);0" or - s = "java.security.spec.PKCS8EncodedKeySpec;PKCS8EncodedKeySpec(byte[]);0" or - s = "java.security.spec.X509EncodedKeySpec;X509EncodedKeySpec(byte[]);0" or - s = "javax.crypto.Cipher;unwrap(byte[], String, int);0" or - s = "javax.crypto.CipherSpi;engineUnwrap(byte[], String, int);0" or - s = "javax.crypto.EncryptedPrivateKeyInfo;checkPKCS8Encoding(byte[]);0" or - s = "javax.crypto.spec.DESKeySpec;isWeak(byte[], int);0" or - s = "javax.crypto.spec.DESKeySpec;DESKeySpec(byte[], int);0" or - s = "javax.crypto.spec.DESKeySpec;isParityAdjusted(byte[], int);0" or - s = "javax.crypto.spec.DESKeySpec;DESKeySpec(byte[]);0" or - s = "javax.crypto.spec.DESedeKeySpec;isParityAdjusted(byte[], int);0" or - s = "javax.crypto.spec.DESedeKeySpec;DESedeKeySpec(byte[], int);0" or - s = "javax.crypto.spec.DESedeKeySpec;DESedeKeySpec(byte[]);0" or - s = "javax.crypto.spec.SecretKeySpec;SecretKeySpec(byte[], String);0" or - s = "javax.crypto.spec.SecretKeySpec;SecretKeySpec(byte[], int, int, String);0" or - s = "javax.security.auth.kerberos.KerberosKey;KerberosKey(KerberosPrincipal, byte[], int, int);1" or - s = - "javax.security.auth.kerberos.KerberosTicket;KerberosTicket(byte[], KerberosPrincipal, KerberosPrincipal, byte[], int, boolean[], Date, Date, Date, Date, InetAddress[]);3" or - s = - "javax.security.auth.kerberos.KerberosTicket;init(byte[], KerberosPrincipal, KerberosPrincipal, byte[], int, boolean[], Date, Date, Date, Date, InetAddress[]);3" or - s = "javax.security.auth.kerberos.KeyImpl;KeyImpl(byte[], int);0" or - s = "sun.security.jgss.krb5.CipherHelper;getInitializedDes(boolean, byte[], byte[]);1" or - s = "sun.security.jgss.krb5.CipherHelper;getDesCbcChecksum(byte[], byte[], byte[], int, int);0" or - s = "sun.security.jgss.krb5.CipherHelper;getDesEncryptionKey(byte[]);0" or - s = - "sun.security.jgss.krb5.CipherHelper;desCbcDecrypt(WrapToken, byte[], byte[], int, int, byte[], int);1" or - s = - "sun.security.jgss.krb5.CipherHelper;desCbcDecrypt(WrapToken, byte[], InputStream, int, byte[], int);1" or - s = - "sun.security.jgss.krb5.Krb5InitCredential;Krb5InitCredential(Krb5NameElement, byte[], KerberosPrincipal, KerberosPrincipal, byte[], int, boolean[], Date, Date, Date, Date, InetAddress[]);4" or - s = - "sun.security.jgss.krb5.Krb5InitCredential;Krb5InitCredential(Krb5NameElement, Credentials, byte[], KerberosPrincipal, KerberosPrincipal, byte[], int, boolean[], Date, Date, Date, Date, InetAddress[]);5" or - s = - "sun.security.krb5.Credentials;Credentials(byte[], String, String, byte[], int, boolean[], Date, Date, Date, Date, InetAddress[]);3" or - s = "sun.security.krb5.EncryptionKey;EncryptionKey(int, byte[]);1" or - s = "sun.security.krb5.EncryptionKey;EncryptionKey(byte[], int, Integer);0" or - s = "sun.security.krb5.internal.crypto.Aes128;decryptRaw(byte[], int, byte[], byte[], int, int);0" or - s = "sun.security.krb5.internal.crypto.Aes128;calculateChecksum(byte[], int, byte[], int, int);0" or - s = "sun.security.krb5.internal.crypto.Aes128;decrypt(byte[], int, byte[], byte[], int, int);0" or - s = "sun.security.krb5.internal.crypto.Aes128;encryptRaw(byte[], int, byte[], byte[], int, int);0" or - s = "sun.security.krb5.internal.crypto.Aes128;encrypt(byte[], int, byte[], byte[], int, int);0" or - s = - "sun.security.krb5.internal.crypto.Aes128CtsHmacSha1EType;encrypt(byte[], byte[], byte[], int);1" or - s = "sun.security.krb5.internal.crypto.Aes128CtsHmacSha1EType;decrypt(byte[], byte[], int);1" or - s = "sun.security.krb5.internal.crypto.Aes128CtsHmacSha1EType;encrypt(byte[], byte[], int);1" or - s = - "sun.security.krb5.internal.crypto.Aes128CtsHmacSha1EType;decrypt(byte[], byte[], byte[], int);1" or - s = "sun.security.krb5.internal.crypto.Aes256;encrypt(byte[], int, byte[], byte[], int, int);0" or - s = "sun.security.krb5.internal.crypto.Aes256;decryptRaw(byte[], int, byte[], byte[], int, int);0" or - s = "sun.security.krb5.internal.crypto.Aes256;calculateChecksum(byte[], int, byte[], int, int);0" or - s = "sun.security.krb5.internal.crypto.Aes256;encryptRaw(byte[], int, byte[], byte[], int, int);0" or - s = "sun.security.krb5.internal.crypto.Aes256;decrypt(byte[], int, byte[], byte[], int, int);0" or - s = - "sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType;encrypt(byte[], byte[], byte[], int);1" or - s = - "sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType;decrypt(byte[], byte[], byte[], int);1" or - s = "sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType;decrypt(byte[], byte[], int);1" or - s = "sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType;encrypt(byte[], byte[], int);1" or - s = - "sun.security.krb5.internal.crypto.ArcFourHmac;encryptRaw(byte[], int, byte[], byte[], int, int);0" or - s = - "sun.security.krb5.internal.crypto.ArcFourHmac;decryptRaw(byte[], int, byte[], byte[], int, int, byte[]);0" or - s = - "sun.security.krb5.internal.crypto.ArcFourHmac;decrypt(byte[], int, byte[], byte[], int, int);0" or - s = - "sun.security.krb5.internal.crypto.ArcFourHmac;decryptSeq(byte[], int, byte[], byte[], int, int);0" or - s = - "sun.security.krb5.internal.crypto.ArcFourHmac;encrypt(byte[], int, byte[], byte[], int, int);0" or - s = - "sun.security.krb5.internal.crypto.ArcFourHmac;calculateChecksum(byte[], int, byte[], int, int);0" or - s = - "sun.security.krb5.internal.crypto.ArcFourHmac;encryptSeq(byte[], int, byte[], byte[], int, int);0" or - s = "sun.security.krb5.internal.crypto.ArcFourHmacEType;decrypt(byte[], byte[], int);1" or - s = "sun.security.krb5.internal.crypto.ArcFourHmacEType;encrypt(byte[], byte[], int);1" or - s = "sun.security.krb5.internal.crypto.ArcFourHmacEType;decrypt(byte[], byte[], byte[], int);1" or - s = "sun.security.krb5.internal.crypto.ArcFourHmacEType;encrypt(byte[], byte[], byte[], int);1" or - s = - "sun.security.krb5.internal.crypto.CksumType;calculateKeyedChecksum(byte[], int, byte[], int);2" or - s = - "sun.security.krb5.internal.crypto.CksumType;verifyKeyedChecksum(byte[], int, byte[], byte[], int);2" or - s = - "sun.security.krb5.internal.crypto.Crc32CksumType;verifyKeyedChecksum(byte[], int, byte[], byte[], int);2" or - s = - "sun.security.krb5.internal.crypto.Crc32CksumType;calculateKeyedChecksum(byte[], int, byte[], int);2" or - s = "sun.security.krb5.internal.crypto.Des;cbc_encrypt(byte[], byte[], byte[], byte[], boolean);2" or - s = "sun.security.krb5.internal.crypto.Des;set_parity(byte[]);0" or - s = "sun.security.krb5.internal.crypto.Des;bad_key(byte[]);0" or - s = "sun.security.krb5.internal.crypto.Des;des_cksum(byte[], byte[], byte[]);2" or - s = "sun.security.krb5.internal.crypto.Des3;decryptRaw(byte[], int, byte[], byte[], int, int);0" or - s = "sun.security.krb5.internal.crypto.Des3;encrypt(byte[], int, byte[], byte[], int, int);0" or - s = "sun.security.krb5.internal.crypto.Des3;encryptRaw(byte[], int, byte[], byte[], int, int);0" or - s = "sun.security.krb5.internal.crypto.Des3;decrypt(byte[], int, byte[], byte[], int, int);0" or - s = "sun.security.krb5.internal.crypto.Des3;calculateChecksum(byte[], int, byte[], int, int);0" or - s = "sun.security.krb5.internal.crypto.Des3CbcHmacSha1KdEType;encrypt(byte[], byte[], int);1" or - s = - "sun.security.krb5.internal.crypto.Des3CbcHmacSha1KdEType;encrypt(byte[], byte[], byte[], int);1" or - s = - "sun.security.krb5.internal.crypto.Des3CbcHmacSha1KdEType;decrypt(byte[], byte[], byte[], int);1" or - s = "sun.security.krb5.internal.crypto.Des3CbcHmacSha1KdEType;decrypt(byte[], byte[], int);1" or - s = "sun.security.krb5.internal.crypto.DesCbcCrcEType;decrypt(byte[], byte[], int);1" or - s = "sun.security.krb5.internal.crypto.DesCbcCrcEType;encrypt(byte[], byte[], int);1" or - s = "sun.security.krb5.internal.crypto.DesCbcEType;encrypt(byte[], byte[], int);1" or - s = "sun.security.krb5.internal.crypto.DesCbcEType;decrypt(byte[], byte[], byte[], int);1" or - s = "sun.security.krb5.internal.crypto.DesCbcEType;encrypt(byte[], byte[], byte[], int);1" or - s = "sun.security.krb5.internal.crypto.DesCbcEType;decrypt(byte[], byte[], int);1" or - s = - "sun.security.krb5.internal.crypto.DesMacCksumType;calculateKeyedChecksum(byte[], int, byte[], int);2" or - s = "sun.security.krb5.internal.crypto.DesMacCksumType;decryptKeyedChecksum(byte[], byte[]);1" or - s = - "sun.security.krb5.internal.crypto.DesMacCksumType;verifyKeyedChecksum(byte[], int, byte[], byte[], int);2" or - s = - "sun.security.krb5.internal.crypto.DesMacKCksumType;calculateKeyedChecksum(byte[], int, byte[], int);2" or - s = - "sun.security.krb5.internal.crypto.DesMacKCksumType;verifyKeyedChecksum(byte[], int, byte[], byte[], int);2" or - s = "sun.security.krb5.internal.crypto.EType;encrypt(byte[], byte[], byte[], int);1" or - s = "sun.security.krb5.internal.crypto.EType;decrypt(byte[], byte[], byte[], int);1" or - s = "sun.security.krb5.internal.crypto.EType;decrypt(byte[], byte[], int);1" or - s = "sun.security.krb5.internal.crypto.EType;encrypt(byte[], byte[], int);1" or - s = - "sun.security.krb5.internal.crypto.HmacMd5ArcFourCksumType;verifyKeyedChecksum(byte[], int, byte[], byte[], int);2" or - s = - "sun.security.krb5.internal.crypto.HmacMd5ArcFourCksumType;calculateKeyedChecksum(byte[], int, byte[], int);2" or - s = - "sun.security.krb5.internal.crypto.HmacSha1Aes128CksumType;verifyKeyedChecksum(byte[], int, byte[], byte[], int);2" or - s = - "sun.security.krb5.internal.crypto.HmacSha1Aes128CksumType;calculateKeyedChecksum(byte[], int, byte[], int);2" or - s = - "sun.security.krb5.internal.crypto.HmacSha1Aes256CksumType;verifyKeyedChecksum(byte[], int, byte[], byte[], int);2" or - s = - "sun.security.krb5.internal.crypto.HmacSha1Aes256CksumType;calculateKeyedChecksum(byte[], int, byte[], int);2" or - s = - "sun.security.krb5.internal.crypto.HmacSha1Des3KdCksumType;calculateKeyedChecksum(byte[], int, byte[], int);2" or - s = - "sun.security.krb5.internal.crypto.HmacSha1Des3KdCksumType;verifyKeyedChecksum(byte[], int, byte[], byte[], int);2" or - s = "sun.security.krb5.internal.crypto.NullEType;decrypt(byte[], byte[], byte[], int);1" or - s = "sun.security.krb5.internal.crypto.NullEType;decrypt(byte[], byte[], int);1" or - s = "sun.security.krb5.internal.crypto.NullEType;encrypt(byte[], byte[], byte[], int);1" or - s = "sun.security.krb5.internal.crypto.NullEType;encrypt(byte[], byte[], int);1" or - s = - "sun.security.krb5.internal.crypto.RsaMd5CksumType;verifyKeyedChecksum(byte[], int, byte[], byte[], int);2" or - s = - "sun.security.krb5.internal.crypto.RsaMd5CksumType;calculateKeyedChecksum(byte[], int, byte[], int);2" or - s = "sun.security.krb5.internal.crypto.RsaMd5DesCksumType;decryptKeyedChecksum(byte[], byte[]);1" or - s = - "sun.security.krb5.internal.crypto.RsaMd5DesCksumType;verifyKeyedChecksum(byte[], int, byte[], byte[], int);2" or - s = - "sun.security.krb5.internal.crypto.RsaMd5DesCksumType;calculateKeyedChecksum(byte[], int, byte[], int);2" or - s = - "sun.security.krb5.internal.crypto.dk.AesDkCrypto;encryptCTS(byte[], int, byte[], byte[], byte[], int, int, boolean);0" or - s = - "sun.security.krb5.internal.crypto.dk.AesDkCrypto;decrypt(byte[], int, byte[], byte[], int, int);0" or - s = - "sun.security.krb5.internal.crypto.dk.AesDkCrypto;encryptRaw(byte[], int, byte[], byte[], int, int);0" or - s = - "sun.security.krb5.internal.crypto.dk.AesDkCrypto;calculateChecksum(byte[], int, byte[], int, int);0" or - s = - "sun.security.krb5.internal.crypto.dk.AesDkCrypto;encrypt(byte[], int, byte[], byte[], byte[], int, int);0" or - s = "sun.security.krb5.internal.crypto.dk.AesDkCrypto;getHmac(byte[], byte[]);0" or - s = "sun.security.krb5.internal.crypto.dk.AesDkCrypto;getCipher(byte[], byte[], int);0" or - s = - "sun.security.krb5.internal.crypto.dk.AesDkCrypto;decryptRaw(byte[], int, byte[], byte[], int, int);0" or - s = - "sun.security.krb5.internal.crypto.dk.AesDkCrypto;decryptCTS(byte[], int, byte[], byte[], int, int, boolean);0" or - s = - "sun.security.krb5.internal.crypto.dk.ArcFourCrypto;decryptSeq(byte[], int, byte[], byte[], int, int);0" or - s = - "sun.security.krb5.internal.crypto.dk.ArcFourCrypto;decryptRaw(byte[], int, byte[], byte[], int, int, byte[]);0" or - s = - "sun.security.krb5.internal.crypto.dk.ArcFourCrypto;decrypt(byte[], int, byte[], byte[], int, int);0" or - s = - "sun.security.krb5.internal.crypto.dk.ArcFourCrypto;encryptRaw(byte[], int, byte[], byte[], int, int);0" or - s = "sun.security.krb5.internal.crypto.dk.ArcFourCrypto;getCipher(byte[], byte[], int);0" or - s = - "sun.security.krb5.internal.crypto.dk.ArcFourCrypto;encryptSeq(byte[], int, byte[], byte[], int, int);0" or - s = - "sun.security.krb5.internal.crypto.dk.ArcFourCrypto;calculateChecksum(byte[], int, byte[], int, int);0" or - s = - "sun.security.krb5.internal.crypto.dk.ArcFourCrypto;encrypt(byte[], int, byte[], byte[], byte[], int, int);0" or - s = "sun.security.krb5.internal.crypto.dk.ArcFourCrypto;getHmac(byte[], byte[]);0" or - s = "sun.security.krb5.internal.crypto.dk.Des3DkCrypto;keyCorrection(byte[]);0" or - s = "sun.security.krb5.internal.crypto.dk.Des3DkCrypto;getCipher(byte[], byte[], int);0" or - s = "sun.security.krb5.internal.crypto.dk.Des3DkCrypto;getHmac(byte[], byte[]);0" or - s = "sun.security.krb5.internal.crypto.dk.Des3DkCrypto;setParityBit(byte[]);0" or - s = - "sun.security.krb5.internal.crypto.dk.DkCrypto;encrypt(byte[], int, byte[], byte[], byte[], int, int);0" or - s = - "sun.security.krb5.internal.crypto.dk.DkCrypto;decrypt(byte[], int, byte[], byte[], int, int);0" or - s = - "sun.security.krb5.internal.crypto.dk.DkCrypto;encryptRaw(byte[], int, byte[], byte[], int, int);0" or - s = - "sun.security.krb5.internal.crypto.dk.DkCrypto;calculateChecksum(byte[], int, byte[], int, int);0" or - s = - "sun.security.krb5.internal.crypto.dk.DkCrypto;decryptRaw(byte[], int, byte[], byte[], int, int);0" or - s = "sun.security.krb5.internal.crypto.dk.DkCrypto;getHmac(byte[], byte[]);0" or - s = "sun.security.krb5.internal.crypto.dk.DkCrypto;getCipher(byte[], byte[], int);0" or - s = "sun.security.krb5.internal.crypto.dk.DkCrypto;dk(byte[], byte[]);0" or - s = "sun.security.krb5.internal.crypto.dk.DkCrypto;dr(byte[], byte[]);0" or - s = "sun.security.pkcs.PKCS8Key;decode(byte[]);0" or - s = "sun.security.pkcs.PKCS8Key;PKCS8Key(AlgorithmId, byte[]);1" or - s = "sun.security.pkcs.PKCS8Key;buildPKCS8Key(AlgorithmId, byte[]);1" or - s = "sun.security.pkcs.PKCS8Key;encode(DerOutputStream, AlgorithmId, byte[]);2" or - s = "sun.security.pkcs11.ConstructKeys;constructPublicKey(byte[], String);0" or - s = "sun.security.pkcs11.ConstructKeys;constructPrivateKey(byte[], String);0" or - s = "sun.security.pkcs11.ConstructKeys;constructSecretKey(byte[], String);0" or - s = "sun.security.pkcs11.P11Cipher;engineUnwrap(byte[], String, int);0" or - s = "sun.security.pkcs11.P11KeyStore;engineSetKeyEntry(String, byte[], Certificate[]);1" or - s = "sun.security.pkcs11.P11RSACipher;engineUnwrap(byte[], String, int);0" or - s = "sun.security.pkcs11.P11SecretKeyFactory;fixDESParity(byte[], int);0" or - s = "sun.security.pkcs12.PKCS12KeyStore;engineSetKeyEntry(String, byte[], Certificate[]);1" or - s = "sun.security.provider.DomainKeyStore;engineSetKeyEntry(String, byte[], Certificate[]);1" or - s = "sun.security.provider.JavaKeyStore;engineSetKeyEntry(String, byte[], Certificate[]);1" or - s = "sun.security.tools.keytool.Main;recoverKey(String, char[], char[]);2" or - s = "sun.security.tools.keytool.Main;getKeyPasswd(String, String, char[]);2" or - s = "sun.security.x509.X509Key;decode(byte[]);0" + [ + "com.sun.crypto.provider.AESCipher;engineUnwrap(byte[], String, int);0", + "com.sun.crypto.provider.AESCrypt;init(boolean, String, byte[]);2", + "com.sun.crypto.provider.CipherWithWrappingSpi;constructPublicKey(byte[], String);0", + "sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType;encrypt(byte[], byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType;decrypt(byte[], byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType;decrypt(byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType;encrypt(byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.ArcFourHmac;encryptRaw(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.ArcFourHmac;decryptRaw(byte[], int, byte[], byte[], int, int, byte[]);0", + "sun.security.krb5.internal.crypto.ArcFourHmac;decrypt(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.ArcFourHmac;decryptSeq(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.ArcFourHmac;encrypt(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.ArcFourHmac;calculateChecksum(byte[], int, byte[], int, int);0", + "com.sun.crypto.provider.CipherWithWrappingSpi;engineUnwrap(byte[], String, int);0", + "sun.security.krb5.internal.crypto.ArcFourHmac;encryptSeq(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.ArcFourHmacEType;decrypt(byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.ArcFourHmacEType;encrypt(byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.ArcFourHmacEType;decrypt(byte[], byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.ArcFourHmacEType;encrypt(byte[], byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.CksumType;calculateKeyedChecksum(byte[], int, byte[], int);2", + "sun.security.krb5.internal.crypto.CksumType;verifyKeyedChecksum(byte[], int, byte[], byte[], int);2", + "sun.security.krb5.internal.crypto.Crc32CksumType;verifyKeyedChecksum(byte[], int, byte[], byte[], int);2", + "sun.security.krb5.internal.crypto.Crc32CksumType;calculateKeyedChecksum(byte[], int, byte[], int);2", + "sun.security.krb5.internal.crypto.Des;cbc_encrypt(byte[], byte[], byte[], byte[], boolean);2", + "com.sun.crypto.provider.CipherWithWrappingSpi;constructSecretKey(byte[], String);0", + "sun.security.krb5.internal.crypto.Des;set_parity(byte[]);0", + "sun.security.krb5.internal.crypto.Des;bad_key(byte[]);0", + "sun.security.krb5.internal.crypto.Des;des_cksum(byte[], byte[], byte[]);2", + "sun.security.krb5.internal.crypto.Des3;decryptRaw(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.Des3;encrypt(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.Des3;encryptRaw(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.Des3;decrypt(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.Des3;calculateChecksum(byte[], int, byte[], int, int);0", + "sun.security.krb5.internal.crypto.Des3CbcHmacSha1KdEType;encrypt(byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.Des3CbcHmacSha1KdEType;encrypt(byte[], byte[], byte[], int);1", + "com.sun.crypto.provider.CipherWithWrappingSpi;constructPrivateKey(byte[], String);0", + "sun.security.krb5.internal.crypto.Des3CbcHmacSha1KdEType;decrypt(byte[], byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.Des3CbcHmacSha1KdEType;decrypt(byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.DesCbcCrcEType;decrypt(byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.DesCbcCrcEType;encrypt(byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.DesCbcEType;encrypt(byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.DesCbcEType;decrypt(byte[], byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.DesCbcEType;encrypt(byte[], byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.DesCbcEType;decrypt(byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.DesMacCksumType;calculateKeyedChecksum(byte[], int, byte[], int);2", + "sun.security.krb5.internal.crypto.DesMacCksumType;decryptKeyedChecksum(byte[], byte[]);1", + "com.sun.crypto.provider.ConstructKeys;constructPrivateKey(byte[], String);0", + "sun.security.krb5.internal.crypto.DesMacCksumType;verifyKeyedChecksum(byte[], int, byte[], byte[], int);2", + "sun.security.krb5.internal.crypto.DesMacKCksumType;calculateKeyedChecksum(byte[], int, byte[], int);2", + "sun.security.krb5.internal.crypto.DesMacKCksumType;verifyKeyedChecksum(byte[], int, byte[], byte[], int);2", + "sun.security.krb5.internal.crypto.EType;encrypt(byte[], byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.EType;decrypt(byte[], byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.EType;decrypt(byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.EType;encrypt(byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.HmacMd5ArcFourCksumType;verifyKeyedChecksum(byte[], int, byte[], byte[], int);2", + "sun.security.krb5.internal.crypto.HmacMd5ArcFourCksumType;calculateKeyedChecksum(byte[], int, byte[], int);2", + "sun.security.krb5.internal.crypto.HmacSha1Aes128CksumType;verifyKeyedChecksum(byte[], int, byte[], byte[], int);2", + "com.sun.crypto.provider.ConstructKeys;constructSecretKey(byte[], String);0", + "sun.security.krb5.internal.crypto.HmacSha1Aes128CksumType;calculateKeyedChecksum(byte[], int, byte[], int);2", + "sun.security.krb5.internal.crypto.HmacSha1Aes256CksumType;verifyKeyedChecksum(byte[], int, byte[], byte[], int);2", + "sun.security.krb5.internal.crypto.HmacSha1Aes256CksumType;calculateKeyedChecksum(byte[], int, byte[], int);2", + "sun.security.krb5.internal.crypto.HmacSha1Des3KdCksumType;calculateKeyedChecksum(byte[], int, byte[], int);2", + "sun.security.krb5.internal.crypto.HmacSha1Des3KdCksumType;verifyKeyedChecksum(byte[], int, byte[], byte[], int);2", + "sun.security.krb5.internal.crypto.NullEType;decrypt(byte[], byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.NullEType;decrypt(byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.NullEType;encrypt(byte[], byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.NullEType;encrypt(byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.RsaMd5CksumType;verifyKeyedChecksum(byte[], int, byte[], byte[], int);2", + "com.sun.crypto.provider.ConstructKeys;constructPublicKey(byte[], String);0", + "sun.security.krb5.internal.crypto.RsaMd5CksumType;calculateKeyedChecksum(byte[], int, byte[], int);2", + "sun.security.krb5.internal.crypto.RsaMd5DesCksumType;decryptKeyedChecksum(byte[], byte[]);1", + "sun.security.krb5.internal.crypto.RsaMd5DesCksumType;verifyKeyedChecksum(byte[], int, byte[], byte[], int);2", + "sun.security.krb5.internal.crypto.RsaMd5DesCksumType;calculateKeyedChecksum(byte[], int, byte[], int);2", + "sun.security.krb5.internal.crypto.dk.AesDkCrypto;encryptCTS(byte[], int, byte[], byte[], byte[], int, int, boolean);0", + "sun.security.krb5.internal.crypto.dk.AesDkCrypto;decrypt(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.dk.AesDkCrypto;encryptRaw(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.dk.AesDkCrypto;calculateChecksum(byte[], int, byte[], int, int);0", + "sun.security.krb5.internal.crypto.dk.AesDkCrypto;encrypt(byte[], int, byte[], byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.dk.AesDkCrypto;getHmac(byte[], byte[]);0", + "com.sun.crypto.provider.CounterMode;init(boolean, String, byte[], byte[]);2", + "sun.security.krb5.internal.crypto.dk.AesDkCrypto;getCipher(byte[], byte[], int);0", + "sun.security.krb5.internal.crypto.dk.AesDkCrypto;decryptRaw(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.dk.AesDkCrypto;decryptCTS(byte[], int, byte[], byte[], int, int, boolean);0", + "sun.security.krb5.internal.crypto.dk.ArcFourCrypto;decryptSeq(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.dk.ArcFourCrypto;decryptRaw(byte[], int, byte[], byte[], int, int, byte[]);0", + "sun.security.krb5.internal.crypto.dk.ArcFourCrypto;decrypt(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.dk.ArcFourCrypto;encryptRaw(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.dk.ArcFourCrypto;getCipher(byte[], byte[], int);0", + "sun.security.krb5.internal.crypto.dk.ArcFourCrypto;encryptSeq(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.dk.ArcFourCrypto;calculateChecksum(byte[], int, byte[], int, int);0", + "com.sun.crypto.provider.DESCipher;engineUnwrap(byte[], String, int);0", + "sun.security.krb5.internal.crypto.dk.ArcFourCrypto;encrypt(byte[], int, byte[], byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.dk.ArcFourCrypto;getHmac(byte[], byte[]);0", + "sun.security.krb5.internal.crypto.dk.Des3DkCrypto;keyCorrection(byte[]);0", + "sun.security.krb5.internal.crypto.dk.Des3DkCrypto;getCipher(byte[], byte[], int);0", + "sun.security.krb5.internal.crypto.dk.Des3DkCrypto;getHmac(byte[], byte[]);0", + "sun.security.krb5.internal.crypto.dk.Des3DkCrypto;setParityBit(byte[]);0", + "sun.security.krb5.internal.crypto.dk.DkCrypto;encrypt(byte[], int, byte[], byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.dk.DkCrypto;decrypt(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.dk.DkCrypto;encryptRaw(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.dk.DkCrypto;calculateChecksum(byte[], int, byte[], int, int);0", + "com.sun.crypto.provider.DESCrypt;expandKey(byte[]);0", + "sun.security.krb5.internal.crypto.dk.DkCrypto;decryptRaw(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.dk.DkCrypto;getHmac(byte[], byte[]);0", + "sun.security.krb5.internal.crypto.dk.DkCrypto;getCipher(byte[], byte[], int);0", + "sun.security.krb5.internal.crypto.dk.DkCrypto;dk(byte[], byte[]);0", + "sun.security.krb5.internal.crypto.dk.DkCrypto;dr(byte[], byte[]);0", + "sun.security.pkcs.PKCS8Key;decode(byte[]);0", + "sun.security.pkcs.PKCS8Key;PKCS8Key(AlgorithmId, byte[]);1", + "sun.security.pkcs.PKCS8Key;buildPKCS8Key(AlgorithmId, byte[]);1", + "sun.security.pkcs.PKCS8Key;encode(DerOutputStream, AlgorithmId, byte[]);2", + "sun.security.pkcs11.ConstructKeys;constructPublicKey(byte[], String);0", + "com.sun.crypto.provider.AESWrapCipher;engineUnwrap(byte[], String, int);0", + "com.sun.crypto.provider.DESCrypt;init(boolean, String, byte[]);2", + "sun.security.pkcs11.ConstructKeys;constructPrivateKey(byte[], String);0", + "sun.security.pkcs11.ConstructKeys;constructSecretKey(byte[], String);0", + "sun.security.pkcs11.P11Cipher;engineUnwrap(byte[], String, int);0", + "sun.security.pkcs11.P11KeyStore;engineSetKeyEntry(String, byte[], Certificate[]);1", + "sun.security.pkcs11.P11RSACipher;engineUnwrap(byte[], String, int);0", + "sun.security.pkcs11.P11SecretKeyFactory;fixDESParity(byte[], int);0", + "sun.security.pkcs12.PKCS12KeyStore;engineSetKeyEntry(String, byte[], Certificate[]);1", + "sun.security.provider.DomainKeyStore;engineSetKeyEntry(String, byte[], Certificate[]);1", + "sun.security.provider.JavaKeyStore;engineSetKeyEntry(String, byte[], Certificate[]);1", + "sun.security.tools.keytool.Main;recoverKey(String, char[], char[]);2", + "com.sun.crypto.provider.DESKey;DESKey(byte[], int);0", + "sun.security.tools.keytool.Main;getKeyPasswd(String, String, char[]);2", + "sun.security.x509.X509Key;decode(byte[]);0", + "com.sun.crypto.provider.DESKey;DESKey(byte[]);0", + "com.sun.crypto.provider.DESKeyGenerator;setParityBit(byte[], int);0", + "com.sun.crypto.provider.DESedeCipher;engineUnwrap(byte[], String, int);0", + "com.sun.crypto.provider.DESedeKey;DESedeKey(byte[], int);0", + "com.sun.crypto.provider.DESedeKey;DESedeKey(byte[]);0", + "com.sun.crypto.provider.DESedeWrapCipher;engineUnwrap(byte[], String, int);0", + "com.sun.crypto.provider.DHPrivateKey;DHPrivateKey(byte[]);0", + "com.sun.crypto.provider.DHPublicKey;DHPublicKey(byte[]);0", + "com.sun.crypto.provider.ARCFOURCipher;init(byte[]);0", + "com.sun.crypto.provider.ElectronicCodeBook;init(boolean, String, byte[], byte[]);2", + "com.sun.crypto.provider.FeedbackCipher;init(boolean, String, byte[], byte[]);2", + "com.sun.crypto.provider.GaloisCounterMode;init(boolean, String, byte[], byte[]);2", + "com.sun.crypto.provider.GaloisCounterMode;init(boolean, String, byte[], byte[], int);2", + "com.sun.crypto.provider.JceKeyStore;engineSetKeyEntry(String, byte[], Certificate[]);1", + "com.sun.crypto.provider.KeyProtector;recover(byte[]);0", + "com.sun.crypto.provider.OutputFeedback;init(boolean, String, byte[], byte[]);2", + "com.sun.crypto.provider.PBECipherCore;unwrap(byte[], String, int);0", + "com.sun.crypto.provider.PBES1Core;unwrap(byte[], String, int);0", + "com.sun.crypto.provider.PBES2Core;engineUnwrap(byte[], String, int);0", + "com.sun.crypto.provider.ARCFOURCipher;engineUnwrap(byte[], String, int);0", + "com.sun.crypto.provider.PBEWithMD5AndDESCipher;engineUnwrap(byte[], String, int);0", + "com.sun.crypto.provider.PBEWithMD5AndTripleDESCipher;engineUnwrap(byte[], String, int);0", + "com.sun.crypto.provider.PCBC;init(boolean, String, byte[], byte[]);2", + "com.sun.crypto.provider.PKCS12PBECipherCore;implUnwrap(byte[], String, int);0", + "com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndDESede;engineUnwrap(byte[], String, int);0", + "com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndRC2_128;engineUnwrap(byte[], String, int);0", + "com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndRC2_40;engineUnwrap(byte[], String, int);0", + "com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndRC4_128;engineUnwrap(byte[], String, int);0", + "com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndRC4_40;engineUnwrap(byte[], String, int);0", + "com.sun.crypto.provider.RC2Cipher;engineUnwrap(byte[], String, int);0", + "com.sun.crypto.provider.BlowfishCipher;engineUnwrap(byte[], String, int);0", + "com.sun.crypto.provider.RC2Crypt;init(boolean, String, byte[]);2", + "com.sun.crypto.provider.RSACipher;engineUnwrap(byte[], String, int);0", + "com.sun.crypto.provider.SymmetricCipher;init(boolean, String, byte[]);2", + "com.sun.crypto.provider.TlsMasterSecretGenerator$TlsMasterSecretKey;TlsMasterSecretKey(byte[], int, int);0", + "java.security.KeyStore;setKeyEntry(String, byte[], Certificate[]);1", + "java.security.KeyStoreSpi;engineSetKeyEntry(String, byte[], Certificate[]);1", + "java.security.cert.X509CertSelector;setSubjectPublicKey(byte[]);0", + "java.security.spec.EncodedKeySpec;EncodedKeySpec(byte[]);0", + "java.security.spec.PKCS8EncodedKeySpec;PKCS8EncodedKeySpec(byte[]);0", + "java.security.spec.X509EncodedKeySpec;X509EncodedKeySpec(byte[]);0", + "com.sun.crypto.provider.BlowfishCrypt;init(boolean, String, byte[]);2", + "javax.crypto.Cipher;unwrap(byte[], String, int);0", + "javax.crypto.CipherSpi;engineUnwrap(byte[], String, int);0", + "javax.crypto.EncryptedPrivateKeyInfo;checkPKCS8Encoding(byte[]);0", + "javax.crypto.spec.DESKeySpec;isWeak(byte[], int);0", + "javax.crypto.spec.DESKeySpec;DESKeySpec(byte[], int);0", + "javax.crypto.spec.DESKeySpec;isParityAdjusted(byte[], int);0", + "javax.crypto.spec.DESKeySpec;DESKeySpec(byte[]);0", + "javax.crypto.spec.DESedeKeySpec;isParityAdjusted(byte[], int);0", + "javax.crypto.spec.DESedeKeySpec;DESedeKeySpec(byte[], int);0", + "javax.crypto.spec.DESedeKeySpec;DESedeKeySpec(byte[]);0", + "com.sun.crypto.provider.CipherBlockChaining;init(boolean, String, byte[], byte[]);2", + "javax.crypto.spec.SecretKeySpec;SecretKeySpec(byte[], String);0", + "javax.crypto.spec.SecretKeySpec;SecretKeySpec(byte[], int, int, String);0", + "javax.security.auth.kerberos.KerberosKey;KerberosKey(KerberosPrincipal, byte[], int, int);1", + "javax.security.auth.kerberos.KerberosTicket;KerberosTicket(byte[], KerberosPrincipal, KerberosPrincipal, byte[], int, boolean[], Date, Date, Date, Date, InetAddress[]);3", + "javax.security.auth.kerberos.KerberosTicket;init(byte[], KerberosPrincipal, KerberosPrincipal, byte[], int, boolean[], Date, Date, Date, Date, InetAddress[]);3", + "javax.security.auth.kerberos.KeyImpl;KeyImpl(byte[], int);0", + "sun.security.jgss.krb5.CipherHelper;getInitializedDes(boolean, byte[], byte[]);1", + "sun.security.jgss.krb5.CipherHelper;getDesCbcChecksum(byte[], byte[], byte[], int, int);0", + "sun.security.jgss.krb5.CipherHelper;getDesEncryptionKey(byte[]);0", + "sun.security.jgss.krb5.CipherHelper;desCbcDecrypt(WrapToken, byte[], byte[], int, int, byte[], int);1", + "com.sun.crypto.provider.CipherCore;unwrap(byte[], String, int);0", + "sun.security.jgss.krb5.CipherHelper;desCbcDecrypt(WrapToken, byte[], InputStream, int, byte[], int);1", + "sun.security.jgss.krb5.Krb5InitCredential;Krb5InitCredential(Krb5NameElement, byte[], KerberosPrincipal, KerberosPrincipal, byte[], int, boolean[], Date, Date, Date, Date, InetAddress[]);4", + "sun.security.jgss.krb5.Krb5InitCredential;Krb5InitCredential(Krb5NameElement, Credentials, byte[], KerberosPrincipal, KerberosPrincipal, byte[], int, boolean[], Date, Date, Date, Date, InetAddress[]);5", + "sun.security.krb5.Credentials;Credentials(byte[], String, String, byte[], int, boolean[], Date, Date, Date, Date, InetAddress[]);3", + "sun.security.krb5.EncryptionKey;EncryptionKey(int, byte[]);1", + "sun.security.krb5.EncryptionKey;EncryptionKey(byte[], int, Integer);0", + "sun.security.krb5.internal.crypto.Aes128;decryptRaw(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.Aes128;calculateChecksum(byte[], int, byte[], int, int);0", + "sun.security.krb5.internal.crypto.Aes128;decrypt(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.Aes128;encryptRaw(byte[], int, byte[], byte[], int, int);0", + "com.sun.crypto.provider.CipherFeedback;init(boolean, String, byte[], byte[]);2", + "sun.security.krb5.internal.crypto.Aes128;encrypt(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.Aes128CtsHmacSha1EType;encrypt(byte[], byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.Aes128CtsHmacSha1EType;decrypt(byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.Aes128CtsHmacSha1EType;encrypt(byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.Aes128CtsHmacSha1EType;decrypt(byte[], byte[], byte[], int);1", + "sun.security.krb5.internal.crypto.Aes256;encrypt(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.Aes256;decryptRaw(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.Aes256;calculateChecksum(byte[], int, byte[], int, int);0", + "sun.security.krb5.internal.crypto.Aes256;encryptRaw(byte[], int, byte[], byte[], int, int);0", + "sun.security.krb5.internal.crypto.Aes256;decrypt(byte[], int, byte[], byte[], int, int);0" + ] } /** @@ -505,10 +427,17 @@ predicate otherApiCallableCredentialParam(Callable c, int i) { } private predicate otherApiCallableCredentialParam(string s) { - s = "javax.crypto.spec.IvParameterSpec;IvParameterSpec(byte[]);0" or - s = "javax.crypto.spec.IvParameterSpec;IvParameterSpec(byte[], int, int);0" or s = - "org.springframework.security.core.userdetails.User;User(String, String, boolean, boolean, boolean, boolean, Collection);0" or - s = - "org.springframework.security.core.userdetails.User;User(String, String, boolean, boolean, boolean, boolean, Collection);1" + [ + "javax.crypto.spec.IvParameterSpec;IvParameterSpec(byte[]);0", + "javax.crypto.spec.IvParameterSpec;IvParameterSpec(byte[], int, int);0", + "org.springframework.security.core.userdetails.User;User(String, String, boolean, boolean, boolean, boolean, Collection);0", + "org.springframework.security.core.userdetails.User;User(String, String, boolean, boolean, boolean, boolean, Collection);1", + "com.amazonaws.auth.BasicAWSCredentials;BasicAWSCredentials(String, String);0", + "com.amazonaws.auth.BasicAWSCredentials;BasicAWSCredentials(String, String);1", + "com.azure.identity.UsernamePasswordCredentialBuilder;username(String);0", + "com.azure.identity.UsernamePasswordCredentialBuilder;password(String);0", + "com.azure.identity.ClientSecretCredentialBuilder;clientSecret(String);0", + "org.apache.shiro.mgt.AbstractRememberMeManager;setCipherKey(byte[]);0" + ] } diff --git a/java/ql/src/experimental/Security/CWE/CWE-927/SensitiveBroadcast.java b/java/ql/src/Security/CWE/CWE-927/SensitiveCommunication.java similarity index 100% rename from java/ql/src/experimental/Security/CWE/CWE-927/SensitiveBroadcast.java rename to java/ql/src/Security/CWE/CWE-927/SensitiveCommunication.java diff --git a/java/ql/src/experimental/Security/CWE/CWE-927/SensitiveBroadcast.qhelp b/java/ql/src/Security/CWE/CWE-927/SensitiveCommunication.qhelp similarity index 62% rename from java/ql/src/experimental/Security/CWE/CWE-927/SensitiveBroadcast.qhelp rename to java/ql/src/Security/CWE/CWE-927/SensitiveCommunication.qhelp index 0879d01e295..f3617310e2d 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-927/SensitiveBroadcast.qhelp +++ b/java/ql/src/Security/CWE/CWE-927/SensitiveCommunication.qhelp @@ -2,30 +2,24 @@ -

    Broadcast intents in an Android application are visible to all applications installed on the same mobile device, exposing all sensitive information they contain.

    -

    Broadcasts are vulnerable to passive eavesdropping or active denial of service attacks when an intent is broadcast without specifying any receiver permission or receiver application.

    +

    When an implicit Intent is used with a method such as startActivity, startService, or sendBroadcast, it may be read by other applications on the device.

    +

    This means that sensitive data in these Intents may be leaked.

    -

    - Specify a receiver permission or application when broadcasting intents, or switch to - LocalBroadcastManager - or the latest - LiveData - library. +

    + For sendBroadcast methods, a receiver permission may be specified so that only applications with a certain permission may receive the Intent; + or a LocalBroadcastManager may be used. + Otherwise, ensure that Intents containing sensitive data have an explicit receiver class set.

    -

    The following example shows two ways of broadcasting intents. In the 'BAD' case, no "receiver permission" is specified. In the 'GOOD' case, "receiver permission" or "receiver application" is specified.

    - +

    The following example shows two ways of broadcasting Intents. In the 'BAD' case, no "receiver permission" is specified. In the 'GOOD' case, "receiver permission" or "receiver application" is specified.

    +
    -
  • - CWE: - CWE-927: Use of Implicit Intent for Sensitive Communication -
  • Android Developers: Security considerations and best practices for sending and receiving broadcasts @@ -46,5 +40,9 @@ Android Developers: Android LiveData Overview
  • +
  • + Oversecured: + Interception of Android implicit intents +
  • diff --git a/java/ql/src/Security/CWE/CWE-927/SensitiveCommunication.ql b/java/ql/src/Security/CWE/CWE-927/SensitiveCommunication.ql new file mode 100644 index 00000000000..0042fbad694 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-927/SensitiveCommunication.ql @@ -0,0 +1,21 @@ +/** + * @name Leaking sensitive information through an implicit Intent + * @description An Android application uses implicit Intents containing sensitive data + * in a way that exposes it to arbitrary applications on the device. + * @kind path-problem + * @problem.severity warning + * @security-severity 8.2 + * @precision medium + * @id java/android/sensitive-communication + * @tags security + * external/cwe/cwe-927 + */ + +import java +import semmle.code.java.security.AndroidSensitiveCommunicationQuery +import DataFlow::PathGraph + +from SensitiveCommunicationConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sink +where cfg.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "This call may leak sensitive information from $@.", + source.getNode(), "here" diff --git a/java/ql/src/Violations of Best Practice/Magic Constants/MagicConstants.qll b/java/ql/src/Violations of Best Practice/Magic Constants/MagicConstants.qll index 96c4d753d8f..15a738ba927 100644 --- a/java/ql/src/Violations of Best Practice/Magic Constants/MagicConstants.qll +++ b/java/ql/src/Violations of Best Practice/Magic Constants/MagicConstants.qll @@ -5,179 +5,30 @@ import java */ private predicate trivialPositiveIntValue(string s) { - s = "0" or - s = "1" or - s = "2" or - s = "3" or - s = "4" or - s = "5" or - s = "6" or - s = "7" or - s = "8" or - s = "9" or - s = "10" or - s = "11" or - s = "12" or - s = "13" or - s = "14" or - s = "15" or - s = "16" or - s = "17" or - s = "18" or - s = "19" or - s = "20" or - s = "16" or - s = "32" or - s = "64" or - s = "128" or - s = "256" or - s = "512" or - s = "1024" or - s = "2048" or - s = "4096" or - s = "16384" or - s = "32768" or - s = "65536" or - s = "1048576" or - s = "2147483648" or - s = "4294967296" or - s = "15" or - s = "31" or - s = "63" or - s = "127" or - s = "255" or - s = "511" or - s = "1023" or - s = "2047" or - s = "4095" or - s = "16383" or - s = "32767" or - s = "65535" or - s = "1048577" or - s = "2147483647" or - s = "4294967295" or - s = "0x00000001" or - s = "0x00000002" or - s = "0x00000004" or - s = "0x00000008" or - s = "0x00000010" or - s = "0x00000020" or - s = "0x00000040" or - s = "0x00000080" or - s = "0x00000100" or - s = "0x00000200" or - s = "0x00000400" or - s = "0x00000800" or - s = "0x00001000" or - s = "0x00002000" or - s = "0x00004000" or - s = "0x00008000" or - s = "0x00010000" or - s = "0x00020000" or - s = "0x00040000" or - s = "0x00080000" or - s = "0x00100000" or - s = "0x00200000" or - s = "0x00400000" or - s = "0x00800000" or - s = "0x01000000" or - s = "0x02000000" or - s = "0x04000000" or - s = "0x08000000" or - s = "0x10000000" or - s = "0x20000000" or - s = "0x40000000" or - s = "0x80000000" or - s = "0x00000001" or - s = "0x00000003" or - s = "0x00000007" or - s = "0x0000000f" or - s = "0x0000001f" or - s = "0x0000003f" or - s = "0x0000007f" or - s = "0x000000ff" or - s = "0x000001ff" or - s = "0x000003ff" or - s = "0x000007ff" or - s = "0x00000fff" or - s = "0x00001fff" or - s = "0x00003fff" or - s = "0x00007fff" or - s = "0x0000ffff" or - s = "0x0001ffff" or - s = "0x0003ffff" or - s = "0x0007ffff" or - s = "0x000fffff" or - s = "0x001fffff" or - s = "0x003fffff" or - s = "0x007fffff" or - s = "0x00ffffff" or - s = "0x01ffffff" or - s = "0x03ffffff" or - s = "0x07ffffff" or - s = "0x0fffffff" or - s = "0x1fffffff" or - s = "0x3fffffff" or - s = "0x7fffffff" or - s = "0xffffffff" or - s = "0x0001" or - s = "0x0002" or - s = "0x0004" or - s = "0x0008" or - s = "0x0010" or - s = "0x0020" or - s = "0x0040" or - s = "0x0080" or - s = "0x0100" or - s = "0x0200" or - s = "0x0400" or - s = "0x0800" or - s = "0x1000" or - s = "0x2000" or - s = "0x4000" or - s = "0x8000" or - s = "0x0001" or - s = "0x0003" or - s = "0x0007" or - s = "0x000f" or - s = "0x001f" or - s = "0x003f" or - s = "0x007f" or - s = "0x00ff" or - s = "0x01ff" or - s = "0x03ff" or - s = "0x07ff" or - s = "0x0fff" or - s = "0x1fff" or - s = "0x3fff" or - s = "0x7fff" or - s = "0xffff" or - s = "0x01" or - s = "0x02" or - s = "0x04" or - s = "0x08" or - s = "0x10" or - s = "0x20" or - s = "0x40" or - s = "0x80" or - s = "0x01" or - s = "0x03" or - s = "0x07" or - s = "0x0f" or - s = "0x1f" or - s = "0x3f" or - s = "0x7f" or - s = "0xff" or - s = "0x00" or - s = "10" or - s = "100" or - s = "1000" or - s = "10000" or - s = "100000" or - s = "1000000" or - s = "10000000" or - s = "100000000" or - s = "1000000000" + s = + [ + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", + "17", "18", "19", "20", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", + "16384", "32768", "65536", "1048576", "2147483648", "4294967296", "15", "31", "63", "127", + "255", "511", "1023", "2047", "4095", "16383", "32767", "65535", "1048577", "2147483647", + "4294967295", "0x00000001", "0x00000002", "0x00000004", "0x00000008", "0x00000010", + "0x00000020", "0x00000040", "0x00000080", "0x00000100", "0x00000200", "0x00000400", + "0x00000800", "0x00001000", "0x00002000", "0x00004000", "0x00008000", "0x00010000", + "0x00020000", "0x00040000", "0x00080000", "0x00100000", "0x00200000", "0x00400000", + "0x00800000", "0x01000000", "0x02000000", "0x04000000", "0x08000000", "0x10000000", + "0x20000000", "0x40000000", "0x80000000", "0x00000001", "0x00000003", "0x00000007", + "0x0000000f", "0x0000001f", "0x0000003f", "0x0000007f", "0x000000ff", "0x000001ff", + "0x000003ff", "0x000007ff", "0x00000fff", "0x00001fff", "0x00003fff", "0x00007fff", + "0x0000ffff", "0x0001ffff", "0x0003ffff", "0x0007ffff", "0x000fffff", "0x001fffff", + "0x003fffff", "0x007fffff", "0x00ffffff", "0x01ffffff", "0x03ffffff", "0x07ffffff", + "0x0fffffff", "0x1fffffff", "0x3fffffff", "0x7fffffff", "0xffffffff", "0x0001", "0x0002", + "0x0004", "0x0008", "0x0010", "0x0020", "0x0040", "0x0080", "0x0100", "0x0200", "0x0400", + "0x0800", "0x1000", "0x2000", "0x4000", "0x8000", "0x0001", "0x0003", "0x0007", "0x000f", + "0x001f", "0x003f", "0x007f", "0x00ff", "0x01ff", "0x03ff", "0x07ff", "0x0fff", "0x1fff", + "0x3fff", "0x7fff", "0xffff", "0x01", "0x02", "0x04", "0x08", "0x10", "0x20", "0x40", "0x80", + "0x01", "0x03", "0x07", "0x0f", "0x1f", "0x3f", "0x7f", "0xff", "0x00", "10", "100", "1000", + "10000", "100000", "1000000", "10000000", "100000000", "1000000000" + ] } private predicate trivialIntValue(string s) { diff --git a/java/ql/src/Violations of Best Practice/Magic Constants/MagicConstantsString.ql b/java/ql/src/Violations of Best Practice/Magic Constants/MagicConstantsString.ql index c0f03400da2..c28a66687ee 100644 --- a/java/ql/src/Violations of Best Practice/Magic Constants/MagicConstantsString.ql +++ b/java/ql/src/Violations of Best Practice/Magic Constants/MagicConstantsString.ql @@ -20,34 +20,15 @@ import MagicConstants * https://docs.oracle.com/javase/7/docs/api/java/lang/System.html#getProperties() */ predicate isSystemProperty(string e) { - e = "java.version" or - e = "java.vendor" or - e = "java.vendor.url" or - e = "java.home" or - e = "java.vm.specification.version" or - e = "java.vm.specification.vendor" or - e = "java.vm.specification.name" or - e = "java.vm.version" or - e = "java.vm.vendor" or - e = "java.vm.name" or - e = "java.specification.version" or - e = "java.specification.vendor" or - e = "java.specification.name" or - e = "java.class.version" or - e = "java.class.path" or - e = "java.library.path" or - e = "java.io.tmpdir" or - e = "java.compiler" or - e = "java.ext.dirs" or - e = "os.name" or - e = "os.arch" or - e = "os.version" or - e = "file.separator" or - e = "path.separator" or - e = "line.separator" or - e = "user.name" or - e = "user.home" or - e = "user.dir" + e = + [ + "java.version", "java.vendor", "java.specification.version", "java.specification.vendor", + "java.specification.name", "java.class.version", "java.class.path", "java.library.path", + "java.io.tmpdir", "java.compiler", "java.ext.dirs", "os.name", "java.vendor.url", "os.arch", + "os.version", "file.separator", "path.separator", "line.separator", "user.name", "user.home", + "user.dir", "java.home", "java.vm.specification.version", "java.vm.specification.vendor", + "java.vm.specification.name", "java.vm.version", "java.vm.vendor", "java.vm.name" + ] } predicate trivialContext(Literal e) { diff --git a/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingOverloading.ql b/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingOverloading.ql index 1d8509c6e03..22bf9d36142 100644 --- a/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingOverloading.ql +++ b/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingOverloading.ql @@ -50,6 +50,7 @@ private predicate whitelist(string name) { name = "visit" } * Method `m` has name `name`, number of parameters `numParams` * and is declared in `t` or inherited from a supertype of `t`. */ +pragma[nomagic] private predicate candidateMethod(RefType t, Method m, string name, int numParam) { exists(Method n | n.getSourceDeclaration() = m | t.inherits(n)) and m.getName() = name and diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql b/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql index 52c4c9417e2..698dae57b96 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql @@ -58,10 +58,10 @@ class ManagementSecurityConfig extends ApplicationProperties { string getValue() { result = this.getValueElement().getValue().trim() } /** Holds if `management.security.enabled` is set to `false`. */ - predicate hasSecurityDisabled() { getValue() = "false" } + predicate hasSecurityDisabled() { this.getValue() = "false" } /** Holds if `management.security.enabled` is set to `true`. */ - predicate hasSecurityEnabled() { getValue() = "true" } + predicate hasSecurityEnabled() { this.getValue() = "true" } } /** The configuration property `management.endpoints.web.exposure.include`. */ diff --git a/java/ql/src/experimental/Security/CWE/CWE-200/AndroidFileIntentSink.qll b/java/ql/src/experimental/Security/CWE/CWE-200/AndroidFileIntentSink.qll new file mode 100644 index 00000000000..597590dbaa1 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-200/AndroidFileIntentSink.qll @@ -0,0 +1,60 @@ +/** Provides Android sink models related to file creation. */ + +import java +import semmle.code.java.dataflow.DataFlow +import semmle.code.java.dataflow.ExternalFlow +import semmle.code.java.frameworks.android.Android +import semmle.code.java.frameworks.android.Intent + +/** A sink representing methods creating a file in Android. */ +class AndroidFileSink extends DataFlow::Node { + AndroidFileSink() { sinkNode(this, "create-file") } +} + +/** + * The Android class `android.os.AsyncTask` for running tasks off the UI thread to achieve + * better user experience. + */ +class AsyncTask extends RefType { + AsyncTask() { this.hasQualifiedName("android.os", "AsyncTask") } +} + +/** The `execute` or `executeOnExecutor` method of Android's `AsyncTask` class. */ +class ExecuteAsyncTaskMethod extends Method { + int paramIndex; + + ExecuteAsyncTaskMethod() { + this.getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof AsyncTask and + ( + this.getName() = "execute" and paramIndex = 0 + or + this.getName() = "executeOnExecutor" and paramIndex = 1 + ) + } + + int getParamIndex() { result = paramIndex } +} + +/** The `doInBackground` method of Android's `AsyncTask` class. */ +class AsyncTaskRunInBackgroundMethod extends Method { + AsyncTaskRunInBackgroundMethod() { + this.getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof AsyncTask and + this.getName() = "doInBackground" + } +} + +/** The service start method of Android's `Context` class. */ +class ContextStartServiceMethod extends Method { + ContextStartServiceMethod() { + this.getName() = ["startService", "startForegroundService"] and + this.getDeclaringType().getASupertype*() instanceof TypeContext + } +} + +/** The `onStartCommand` method of Android's `Service` class. */ +class ServiceOnStartCommandMethod extends Method { + ServiceOnStartCommandMethod() { + this.hasName("onStartCommand") and + this.getDeclaringType() instanceof AndroidService + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-200/AndroidFileIntentSource.qll b/java/ql/src/experimental/Security/CWE/CWE-200/AndroidFileIntentSource.qll new file mode 100644 index 00000000000..22935997afe --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-200/AndroidFileIntentSource.qll @@ -0,0 +1,81 @@ +/** Provides summary models relating to file content inputs of Android. */ + +import java +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.dataflow.TaintTracking2 +import semmle.code.java.frameworks.android.Android + +/** The `startActivityForResult` method of Android's `Activity` class. */ +class StartActivityForResultMethod extends Method { + StartActivityForResultMethod() { + this.getDeclaringType().getASupertype*() instanceof AndroidActivity and + this.getName() = "startActivityForResult" + } +} + +/** An instance of `android.content.Intent` constructed passing `GET_CONTENT` to the constructor. */ +class GetContentIntent extends ClassInstanceExpr { + GetContentIntent() { + this.getConstructedType() instanceof TypeIntent and + this.getArgument(0).(CompileTimeConstantExpr).getStringValue() = + "android.intent.action.GET_CONTENT" + or + exists(Field f | + this.getArgument(0) = f.getAnAccess() and + f.hasName("ACTION_GET_CONTENT") and + f.getDeclaringType() instanceof TypeIntent + ) + } +} + +/** Taint configuration that identifies `GET_CONTENT` `Intent` instances passed to `startActivityForResult`. */ +class GetContentIntentConfig extends TaintTracking2::Configuration { + GetContentIntentConfig() { this = "GetContentIntentConfig" } + + override predicate isSource(DataFlow2::Node src) { + exists(GetContentIntent gi | src.asExpr() = gi) + } + + override predicate isSink(DataFlow2::Node sink) { + exists(MethodAccess ma | + ma.getMethod() instanceof StartActivityForResultMethod and sink.asExpr() = ma.getArgument(0) + ) + } + + override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content content) { + super.allowImplicitRead(node, content) + or + // Allow the wrapped intent created by Intent.getChooser to be consumed + // by at the sink: + isSink(node) and + ( + content.(DataFlow::SyntheticFieldContent).getField() = "android.content.Intent.extras" + or + content instanceof DataFlow::MapValueContent + ) + } +} + +/** A `GET_CONTENT` `Intent` instances that is passed to `startActivityForResult`. */ +class AndroidFileIntentInput extends DataFlow::Node { + MethodAccess ma; + + AndroidFileIntentInput() { + this.asExpr() = ma.getArgument(0) and + ma.getMethod() instanceof StartActivityForResultMethod and + exists(GetContentIntentConfig cc, GetContentIntent gi | + cc.hasFlow(DataFlow::exprNode(gi), DataFlow::exprNode(ma.getArgument(0))) + ) + } + + /** The request code passed to `startActivityForResult`, which is to be matched in `onActivityResult()`. */ + int getRequestCode() { result = ma.getArgument(1).(CompileTimeConstantExpr).getIntValue() } +} + +/** The `onActivityForResult` method of Android `Activity` */ +class OnActivityForResultMethod extends Method { + OnActivityForResultMethod() { + this.getDeclaringType().getASupertype*() instanceof AndroidActivity and + this.getName() = "onActivityResult" + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-200/LoadFileFromAppActivity.java b/java/ql/src/experimental/Security/CWE/CWE-200/LoadFileFromAppActivity.java new file mode 100644 index 00000000000..8c4d2a2f0da --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-200/LoadFileFromAppActivity.java @@ -0,0 +1,31 @@ +public class LoadFileFromAppActivity extends Activity { + public static final int REQUEST_CODE__SELECT_CONTENT_FROM_APPS = 99; + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == LoadFileFromAppActivity.REQUEST_CODE__SELECT_CONTENT_FROM_APPS && + resultCode == RESULT_OK) { + + { + // BAD: Load file without validation + loadOfContentFromApps(data, resultCode); + } + + { + // GOOD: load file with validation + if (!data.getData().getPath().startsWith("/data/data")) { + loadOfContentFromApps(data, resultCode); + } + } + } + } + + private void loadOfContentFromApps(Intent contentIntent, int resultCode) { + Uri streamsToUpload = contentIntent.getData(); + try { + RandomAccessFile file = new RandomAccessFile(streamsToUpload.getPath(), "r"); + } catch (Exception ex) { + ex.printStackTrace(); + } + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-200/SensitiveAndroidFileLeak.qhelp b/java/ql/src/experimental/Security/CWE/CWE-200/SensitiveAndroidFileLeak.qhelp new file mode 100644 index 00000000000..ca4a7e668ea --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-200/SensitiveAndroidFileLeak.qhelp @@ -0,0 +1,38 @@ + + + +

    The Android API allows to start an activity in another mobile application and receive a result back. +When starting an activity to retrieve a file from another application, missing input validation can +lead to leaking of sensitive configuration file or user data because the intent could refer to paths +which are accessible to the receiver application, but are intended to be application-private. +

    +
    + + +

    +When loading file data from an activity of another application, validate that the file path is not the receiver's +protected directory, which is a subdirectory of the Android application directory /data/data/. +

    +
    + + +

    +The following examples show a bad situation and a good situation respectively. In the bad situation, a +file is loaded without path validation. In the good situation, a file is loaded with path validation. +

    + +
    + + +
  • +Google: +Android: Interacting with Other Apps. +
  • +
  • +CVE: +CVE-2021-32695: File Sharing Flow Initiated by a Victim Leaks Sensitive Data to a Malicious App. +
  • +
    +
    \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-200/SensitiveAndroidFileLeak.ql b/java/ql/src/experimental/Security/CWE/CWE-200/SensitiveAndroidFileLeak.ql new file mode 100644 index 00000000000..6cc6c09be87 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-200/SensitiveAndroidFileLeak.ql @@ -0,0 +1,84 @@ +/** + * @name Leaking sensitive Android file + * @description Using a path specified in an Android Intent without validation could leak arbitrary + * Android configuration file and sensitive user data. + * @kind path-problem + * @id java/sensitive-android-file-leak + * @problem.severity warning + * @tags security + * external/cwe/cwe-200 + */ + +import java +import semmle.code.java.controlflow.Guards +import AndroidFileIntentSink +import AndroidFileIntentSource +import DataFlow::PathGraph + +private class StartsWithSanitizer extends DataFlow::BarrierGuard { + StartsWithSanitizer() { this.(MethodAccess).getMethod().hasName("startsWith") } + + override predicate checks(Expr e, boolean branch) { + e = + [ + this.(MethodAccess).getQualifier(), + this.(MethodAccess).getQualifier().(MethodAccess).getQualifier() + ] and + branch = false + } +} + +class AndroidFileLeakConfig extends TaintTracking::Configuration { + AndroidFileLeakConfig() { this = "AndroidFileLeakConfig" } + + /** + * Holds if `src` is a read of some Intent-typed variable guarded by a check like + * `requestCode == someCode`, where `requestCode` is the first + * argument to `Activity.onActivityResult` and `someCode` is + * any request code used in a call to `startActivityForResult(intent, someCode)`. + */ + override predicate isSource(DataFlow::Node src) { + exists( + OnActivityForResultMethod oafr, ConditionBlock cb, CompileTimeConstantExpr cc, + VarAccess intentVar + | + cb.getCondition().(EQExpr).hasOperands(oafr.getParameter(0).getAnAccess(), cc) and + cc.getIntValue() = any(AndroidFileIntentInput fi).getRequestCode() and + intentVar.getType() instanceof TypeIntent and + cb.controls(intentVar.getBasicBlock(), true) and + src.asExpr() = intentVar + ) + } + + /** Holds if it is a sink of file access in Android. */ + override predicate isSink(DataFlow::Node sink) { sink instanceof AndroidFileSink } + + override predicate isAdditionalTaintStep(DataFlow::Node prev, DataFlow::Node succ) { + exists(MethodAccess aema, AsyncTaskRunInBackgroundMethod arm | + // fileAsyncTask.execute(params) will invoke doInBackground(params) of FileAsyncTask + aema.getQualifier().getType() = arm.getDeclaringType() and + aema.getMethod() instanceof ExecuteAsyncTaskMethod and + prev.asExpr() = aema.getArgument(aema.getMethod().(ExecuteAsyncTaskMethod).getParamIndex()) and + succ.asParameter() = arm.getParameter(0) + ) + or + exists(MethodAccess csma, ServiceOnStartCommandMethod ssm, ClassInstanceExpr ce | + // An intent passed to startService will later be passed to the onStartCommand event of the corresponding service + csma.getMethod() instanceof ContextStartServiceMethod and + ce.getConstructedType() instanceof TypeIntent and // Intent intent = new Intent(context, FileUploader.class); + ce.getArgument(1).(TypeLiteral).getReferencedType() = ssm.getDeclaringType() and + DataFlow::localExprFlow(ce, csma.getArgument(0)) and // context.startService(intent); + prev.asExpr() = csma.getArgument(0) and + succ.asParameter() = ssm.getParameter(0) // public int onStartCommand(Intent intent, int flags, int startId) {...} in FileUploader + ) + } + + override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { + guard instanceof StartsWithSanitizer + } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, AndroidFileLeakConfig conf +where conf.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Leaking arbitrary Android file from $@.", source.getNode(), + "this user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureJavaMail.qhelp b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureJavaMail.qhelp deleted file mode 100644 index ea1405aa4ee..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureJavaMail.qhelp +++ /dev/null @@ -1,36 +0,0 @@ - - - - -

    JavaMail is commonly used in Java applications to send emails. There are popular third-party libraries like Apache Commons Email which are built on JavaMail and facilitate integration. Authenticated mail sessions require user credentials and mail sessions can require SSL/TLS authentication. It is a common security vulnerability that host-specific certificate data is not validated or is incorrectly validated. Failing to validate the certificate makes the SSL session susceptible to a man-in-the-middle attack.

    -

    This query checks whether SSL certificate is validated when username/password is sent in authenticator and when SSL is enabled.

    -

    The query has code for both plain JavaMail invocation and mailing through Apache SimpleMail to make it more comprehensive.

    -
    - - -

    Validate SSL certificate when sensitive information is sent in email communications.

    -
    - - -

    The following two examples show two ways of configuring secure emails through JavaMail or Apache SimpleMail. In the 'BAD' case, -credentials are sent in an SSL session without certificate validation. In the 'GOOD' case, the certificate is validated.

    - - -
    - - -
  • - CWE-297 -
  • -
  • - Log4j2: - Add support for specifying an SSL configuration for SmtpAppender (CVE-2020-9488) -
  • -
  • - SonarSource rule: - SMTP SSL connection should check server identity -
  • -
    -
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureJavaMail.ql b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureJavaMail.ql deleted file mode 100644 index d0e0dc8b81a..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureJavaMail.ql +++ /dev/null @@ -1,107 +0,0 @@ -/** - * @name Insecure JavaMail SSL Configuration - * @description Java application configured to use authenticated mail session - * over SSL does not validate the SSL certificate to properly - * ensure that it is actually associated with that host. - * @kind problem - * @problem.severity warning - * @precision medium - * @id java/insecure-smtp-ssl - * @tags security - * external/cwe/cwe-297 - */ - -import java - -/** - * The method to set Java properties - */ -class SetPropertyMethod extends Method { - SetPropertyMethod() { - this.hasName("setProperty") and - this.getDeclaringType().hasQualifiedName("java.util", "Properties") - or - this.hasName("put") and - this.getDeclaringType().getASourceSupertype*().hasQualifiedName("java.util", "Dictionary") - } -} - -/** - * The insecure way to set Java properties in mail sessions. - * 1. Set the mail.smtp.auth property to provide the SMTP Transport with a username and password when connecting to the SMTP server or - * set the mail.smtp.ssl.socketFactory/mail.smtp.ssl.socketFactory.class property to create an SMTP SSL socket. - * 2. No mail.smtp.ssl.checkserveridentity property is enabled. - */ -predicate isInsecureMailPropertyConfig(VarAccess propertiesVarAccess) { - exists(MethodAccess ma | - ma.getMethod() instanceof SetPropertyMethod and - ma.getQualifier() = propertiesVarAccess.getVariable().getAnAccess() and - ( - getStringValue(ma.getArgument(0)).matches("%.auth%") and //mail.smtp.auth - getStringValue(ma.getArgument(1)) = "true" - or - getStringValue(ma.getArgument(0)).matches("%.socketFactory%") //mail.smtp.socketFactory or mail.smtp.socketFactory.class - ) - ) and - not exists(MethodAccess ma | - ma.getMethod() instanceof SetPropertyMethod and - ma.getQualifier() = propertiesVarAccess.getVariable().getAnAccess() and - ( - getStringValue(ma.getArgument(0)).matches("%.ssl.checkserveridentity%") and //mail.smtp.ssl.checkserveridentity - getStringValue(ma.getArgument(1)) = "true" - ) - ) -} - -/** - * Helper method to get string value of an argument - */ -string getStringValue(Expr expr) { - result = expr.(CompileTimeConstantExpr).getStringValue() - or - result = getStringValue(expr.(AddExpr).getLeftOperand()) - or - result = getStringValue(expr.(AddExpr).getRightOperand()) -} - -/** - * The JavaMail session class `javax.mail.Session` - */ -class MailSession extends RefType { - MailSession() { this.hasQualifiedName("javax.mail", "Session") } -} - -/** - * The class of Apache SimpleMail - */ -class SimpleMail extends RefType { - SimpleMail() { this.hasQualifiedName("org.apache.commons.mail", "SimpleEmail") } -} - -/** - * Has TLS/SSL enabled with SimpleMail - */ -predicate enableTLSWithSimpleMail(MethodAccess ma) { - ma.getMethod().hasName("setSSLOnConnect") and - ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true -} - -/** - * Has no certificate check - */ -predicate hasNoCertCheckWithSimpleMail(VarAccess va) { - not exists(MethodAccess ma | - ma.getQualifier() = va.getVariable().getAnAccess() and - ma.getMethod().hasName("setSSLCheckServerIdentity") and - ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true - ) -} - -from MethodAccess ma -where - ma.getMethod().getDeclaringType() instanceof MailSession and - ma.getMethod().getName() = "getInstance" and - isInsecureMailPropertyConfig(ma.getArgument(0)) - or - enableTLSWithSimpleMail(ma) and hasNoCertCheckWithSimpleMail(ma.getQualifier()) -select ma, "Java mailing has insecure SSL configuration" diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.ql b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.ql index f870d6f423e..722c615f5b5 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.ql @@ -39,7 +39,7 @@ private predicate hasVulnerableMethod(RefType type) { | not parameterType instanceof PrimitiveType and not parameterType instanceof TypeString and - not parameterType.(RefType).hasQualifiedName("java.io", "ObjectInputStream") + not parameterType instanceof TypeObjectInputStream ) } diff --git a/java/ql/src/experimental/Security/CWE/CWE-552/UnsafeUrlForward.java b/java/ql/src/experimental/Security/CWE/CWE-552/UnsafeUrlForward.java new file mode 100644 index 00000000000..d159c405736 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-552/UnsafeUrlForward.java @@ -0,0 +1,38 @@ +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.servlet.ModelAndView; + +@Controller +public class UnsafeUrlForward { + + @GetMapping("/bad1") + public ModelAndView bad1(String url) { + return new ModelAndView(url); + } + + @GetMapping("/bad2") + public void bad2(String url, HttpServletRequest request, HttpServletResponse response) { + try { + request.getRequestDispatcher("/WEB-INF/jsp/" + url + ".jsp").include(request, response); + } catch (ServletException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @GetMapping("/good1") + public void good1(String url, HttpServletRequest request, HttpServletResponse response) { + try { + request.getRequestDispatcher("/index.jsp?token=" + url).forward(request, response); + } catch (ServletException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-552/UnsafeUrlForward.qhelp b/java/ql/src/experimental/Security/CWE/CWE-552/UnsafeUrlForward.qhelp new file mode 100644 index 00000000000..572b14bb02f --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-552/UnsafeUrlForward.qhelp @@ -0,0 +1,31 @@ + + + + + +

    Constructing a server-side redirect path with user input could allow an attacker to download application binaries +(including application classes or jar files) or view arbitrary files within protected directories.

    + +
    + + +

    In order to prevent untrusted URL forwarding, it is recommended to avoid concatenating user input directly into the forwarding URL.

    + +
    + + +

    The following examples show the bad case and the good case respectively. +The bad methods show an HTTP request parameter being used directly in a URL forward +without validating the input, which may cause file leakage. In good1 method, +ordinary forwarding requests are shown, which will not cause file leakage. +

    + + + +
    + +
  • File Disclosure: Unsafe Url Forward.
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-552/UnsafeUrlForward.ql b/java/ql/src/experimental/Security/CWE/CWE-552/UnsafeUrlForward.ql new file mode 100644 index 00000000000..f9f13f72886 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-552/UnsafeUrlForward.ql @@ -0,0 +1,58 @@ +/** + * @name Unsafe url forward from remote source + * @description URL forward based on unvalidated user-input + * may cause file information disclosure. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/unsafe-url-forward + * @tags security + * external/cwe-552 + */ + +import java +import UnsafeUrlForward +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.frameworks.Servlets +import DataFlow::PathGraph + +private class StartsWithSanitizer extends DataFlow::BarrierGuard { + StartsWithSanitizer() { + this.(MethodAccess).getMethod().hasName("startsWith") and + this.(MethodAccess).getMethod().getDeclaringType() instanceof TypeString and + this.(MethodAccess).getMethod().getNumberOfParameters() = 1 + } + + override predicate checks(Expr e, boolean branch) { + e = this.(MethodAccess).getQualifier() and branch = true + } +} + +class UnsafeUrlForwardFlowConfig extends TaintTracking::Configuration { + UnsafeUrlForwardFlowConfig() { this = "UnsafeUrlForwardFlowConfig" } + + override predicate isSource(DataFlow::Node source) { + source instanceof RemoteFlowSource and + not exists(MethodAccess ma, Method m | ma.getMethod() = m | + ( + m instanceof HttpServletRequestGetRequestURIMethod or + m instanceof HttpServletRequestGetRequestURLMethod or + m instanceof HttpServletRequestGetPathMethod + ) and + ma = source.asExpr() + ) + } + + override predicate isSink(DataFlow::Node sink) { sink instanceof UnsafeUrlForwardSink } + + override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { + guard instanceof StartsWithSanitizer + } + + override predicate isSanitizer(DataFlow::Node node) { node instanceof UnsafeUrlForwardSanitizer } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, UnsafeUrlForwardFlowConfig conf +where conf.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Potentially untrusted URL forward due to $@.", + source.getNode(), "user-provided value" diff --git a/java/ql/src/experimental/Security/CWE/CWE-552/UnsafeUrlForward.qll b/java/ql/src/experimental/Security/CWE/CWE-552/UnsafeUrlForward.qll new file mode 100644 index 00000000000..f2656316203 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-552/UnsafeUrlForward.qll @@ -0,0 +1,72 @@ +import java +import DataFlow +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.frameworks.Servlets +import semmle.code.java.frameworks.spring.SpringWeb +import semmle.code.java.security.RequestForgery +private import semmle.code.java.dataflow.StringPrefixes + +/** A sanitizer for unsafe url forward vulnerabilities. */ +abstract class UnsafeUrlForwardSanitizer extends DataFlow::Node { } + +private class PrimitiveSanitizer extends UnsafeUrlForwardSanitizer { + PrimitiveSanitizer() { + this.getType() instanceof PrimitiveType or + this.getType() instanceof BoxedType or + this.getType() instanceof NumberType + } +} + +private class SanitizingPrefix extends InterestingPrefix { + SanitizingPrefix() { + not this.getStringValue().matches("/WEB-INF/%") and + not this.getStringValue() = "forward:" + } + + override int getOffset() { result = 0 } +} + +private class FollowsSanitizingPrefix extends UnsafeUrlForwardSanitizer { + FollowsSanitizingPrefix() { this.asExpr() = any(SanitizingPrefix fp).getAnAppendedExpression() } +} + +abstract class UnsafeUrlForwardSink extends DataFlow::Node { } + +/** An argument to `ServletRequest.getRequestDispatcher`. */ +private class RequestDispatcherSink extends UnsafeUrlForwardSink { + RequestDispatcherSink() { + exists(MethodAccess ma | + ma.getMethod() instanceof ServletRequestGetRequestDispatcherMethod and + ma.getArgument(0) = this.asExpr() + ) + } +} + +/** An argument to `new ModelAndView` or `ModelAndView.setViewName`. */ +private class SpringModelAndViewSink extends UnsafeUrlForwardSink { + SpringModelAndViewSink() { + exists(ClassInstanceExpr cie | + cie.getConstructedType() instanceof ModelAndView and + cie.getArgument(0) = this.asExpr() + ) + or + exists(SpringModelAndViewSetViewNameCall smavsvnc | smavsvnc.getArgument(0) = this.asExpr()) + } +} + +private class ForwardPrefix extends InterestingPrefix { + ForwardPrefix() { this.getStringValue() = "forward:" } + + override int getOffset() { result = 0 } +} + +/** + * An expression appended (perhaps indirectly) to `"forward:"`, and which + * is reachable from a Spring entry point. + */ +private class SpringUrlForwardSink extends UnsafeUrlForwardSink { + SpringUrlForwardSink() { + any(SpringRequestMappingMethod sqmm).polyCalls*(this.getEnclosingCallable()) and + this.asExpr() = any(ForwardPrefix fp).getAnAppendedExpression() + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-927/SensitiveBroadcast.ql b/java/ql/src/experimental/Security/CWE/CWE-927/SensitiveBroadcast.ql deleted file mode 100644 index 85751918a6e..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-927/SensitiveBroadcast.ql +++ /dev/null @@ -1,175 +0,0 @@ -/** - * @name Broadcasting sensitive data to all Android applications - * @description An Android application uses implicit intents to broadcast - * sensitive data to all applications without specifying any - * receiver permission. - * @kind path-problem - * @problem.severity warning - * @precision medium - * @id java/sensitive-broadcast - * @tags security - * external/cwe/cwe-927 - */ - -import java -import semmle.code.java.dataflow.DataFlow3 -import semmle.code.java.dataflow.TaintTracking -import semmle.code.java.frameworks.android.Intent -import semmle.code.java.security.SensitiveActions -import DataFlow::PathGraph - -/** - * Gets regular expression for matching names of Android variables that indicate the value being held contains sensitive information. - */ -private string getAndroidSensitiveInfoRegex() { result = "(?i).*(email|phone|ticket).*" } - -/** - * Method call to pass information to the `Intent` object. - */ -class PutIntentExtraMethodAccess extends MethodAccess { - PutIntentExtraMethodAccess() { - ( - getMethod().getName().matches("put%Extra") or - getMethod().hasName("putExtras") - ) and - getMethod().getDeclaringType() instanceof TypeIntent - } -} - -/** - * Method call to pass information to the intent extra bundle object. - */ -class PutBundleExtraMethodAccess extends MethodAccess { - PutBundleExtraMethodAccess() { - getMethod().getName().regexpMatch("put\\w+") and - getMethod().getDeclaringType().getASupertype*().hasQualifiedName("android.os", "BaseBundle") - } -} - -/** Finds variables that hold sensitive information judging by their names. */ -class SensitiveInfoExpr extends Expr { - SensitiveInfoExpr() { - exists(Variable v | this = v.getAnAccess() | - v.getName().regexpMatch([getCommonSensitiveInfoRegex(), getAndroidSensitiveInfoRegex()]) - ) - } -} - -/** - * A method access of the `Context.sendBroadcast` family. - */ -class SendBroadcastMethodAccess extends MethodAccess { - SendBroadcastMethodAccess() { - this.getMethod().getDeclaringType() instanceof TypeContext and - this.getMethod().getName().matches("send%Broadcast%") - } -} - -private class NullArgFlowConfig extends DataFlow2::Configuration { - NullArgFlowConfig() { this = "Flow configuration with a null argument" } - - override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof NullLiteral } - - override predicate isSink(DataFlow::Node sink) { - exists(SendBroadcastMethodAccess ma | sink.asExpr() = ma.getAnArgument()) - } -} - -private class EmptyArrayArgFlowConfig extends DataFlow3::Configuration { - EmptyArrayArgFlowConfig() { this = "Flow configuration with an empty array argument" } - - override predicate isSource(DataFlow::Node src) { - src.asExpr().(ArrayCreationExpr).getFirstDimensionSize() = 0 - } - - override predicate isSink(DataFlow::Node sink) { - exists(SendBroadcastMethodAccess ma | sink.asExpr() = ma.getAnArgument()) - } -} - -/** - * Holds if a `sendBroadcast` call doesn't specify receiver permission. - */ -predicate isSensitiveBroadcastSink(DataFlow::Node sink) { - exists(SendBroadcastMethodAccess ma | - sink.asExpr() = ma.getAnArgument() and - ( - ma.getMethod().hasName("sendBroadcast") and - ( - ma.getNumArgument() = 1 // sendBroadcast(Intent intent) - or - // sendBroadcast(Intent intent, String receiverPermission) - exists(NullArgFlowConfig conf | conf.hasFlow(_, DataFlow::exprNode(ma.getArgument(1)))) - ) - or - ma.getMethod().hasName("sendBroadcastAsUser") and - ( - ma.getNumArgument() = 2 or // sendBroadcastAsUser(Intent intent, UserHandle user) - exists(NullArgFlowConfig conf | conf.hasFlow(_, DataFlow::exprNode(ma.getArgument(2)))) // sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission) - ) - or - ma.getMethod().hasName("sendBroadcastWithMultiplePermissions") and - exists(EmptyArrayArgFlowConfig config | - config.hasFlow(_, DataFlow::exprNode(ma.getArgument(1))) // sendBroadcastWithMultiplePermissions(Intent intent, String[] receiverPermissions) - ) - or - // Method calls of `sendOrderedBroadcast` whose second argument is always `receiverPermission` - ma.getMethod().hasName("sendOrderedBroadcast") and - ( - // sendOrderedBroadcast(Intent intent, String receiverPermission) or sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) - exists(NullArgFlowConfig conf | conf.hasFlow(_, DataFlow::exprNode(ma.getArgument(1)))) and - ma.getNumArgument() <= 7 - or - // sendOrderedBroadcast(Intent intent, String receiverPermission, String receiverAppOp, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) - exists(NullArgFlowConfig conf | conf.hasFlow(_, DataFlow::exprNode(ma.getArgument(1)))) and - exists(NullArgFlowConfig conf | conf.hasFlow(_, DataFlow::exprNode(ma.getArgument(2)))) and - ma.getNumArgument() = 8 - ) - or - // Method call of `sendOrderedBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)` - ma.getMethod().hasName("sendOrderedBroadcastAsUser") and - exists(NullArgFlowConfig conf | conf.hasFlow(_, DataFlow::exprNode(ma.getArgument(2)))) - ) - ) -} - -/** - * Taint configuration tracking flow from variables containing sensitive information to broadcast intents. - */ -class SensitiveBroadcastConfig extends TaintTracking::Configuration { - SensitiveBroadcastConfig() { this = "Sensitive Broadcast Configuration" } - - override predicate isSource(DataFlow::Node source) { - source.asExpr() instanceof SensitiveInfoExpr - } - - override predicate isSink(DataFlow::Node sink) { isSensitiveBroadcastSink(sink) } - - /** - * Holds if there is an additional flow step from `PutIntentExtraMethodAccess` or `PutBundleExtraMethodAccess` that taints the `Intent` or its extras `Bundle`. - */ - override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { - exists(PutIntentExtraMethodAccess pia | - node1.asExpr() = pia.getAnArgument() and node2.asExpr() = pia.getQualifier() - ) - or - exists(PutBundleExtraMethodAccess pba | - node1.asExpr() = pba.getAnArgument() and node2.asExpr() = pba.getQualifier() - ) - } - - /** - * Holds if broadcast doesn't specify receiving package name of the 3rd party app - */ - override predicate isSanitizer(DataFlow::Node node) { - exists(MethodAccess setReceiverMa | - setReceiverMa.getMethod().hasName(["setPackage", "setClass", "setClassName", "setComponent"]) and - setReceiverMa.getQualifier().(VarAccess).getVariable().getAnAccess() = node.asExpr() - ) - } -} - -from SensitiveBroadcastConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sink -where cfg.hasFlowPath(source, sink) -select sink.getNode(), source, sink, "Sending $@ to broadcast.", source.getNode(), - "sensitive information" diff --git a/java/ql/src/external/Clover.qll b/java/ql/src/external/Clover.qll index d17acfd8408..1b6fd9fe5c0 100644 --- a/java/ql/src/external/Clover.qll +++ b/java/ql/src/external/Clover.qll @@ -152,7 +152,7 @@ class CloverClass extends CloverMetricsContainer { /** Gets the Java type for this Clover class. */ RefType getRealClass() { result - .hasQualifiedName(getPackage().getAttribute("name").getValue(), + .hasQualifiedName(this.getPackage().getAttribute("name").getValue(), getAttribute("name").getValue()) } } diff --git a/java/ql/src/qlpack.yml b/java/ql/src/qlpack.yml index 8632ff9df4e..b943646a906 100644 --- a/java/ql/src/qlpack.yml +++ b/java/ql/src/qlpack.yml @@ -2,6 +2,7 @@ name: codeql/java-queries version: 0.0.2 suites: codeql-suites extractor: java +defaultSuiteFile: codeql-suites/java-code-scanning.qls dependencies: codeql/java-all: "*" codeql/suite-helpers: "*" diff --git a/java/ql/src/utils/flow-testcase-generator/FlowTestCase.qll b/java/ql/src/utils/flowtestcasegenerator/FlowTestCase.qll similarity index 100% rename from java/ql/src/utils/flow-testcase-generator/FlowTestCase.qll rename to java/ql/src/utils/flowtestcasegenerator/FlowTestCase.qll diff --git a/java/ql/src/utils/flow-testcase-generator/FlowTestCaseSupportMethods.qll b/java/ql/src/utils/flowtestcasegenerator/FlowTestCaseSupportMethods.qll similarity index 94% rename from java/ql/src/utils/flow-testcase-generator/FlowTestCaseSupportMethods.qll rename to java/ql/src/utils/flowtestcasegenerator/FlowTestCaseSupportMethods.qll index 1e6c1bcc063..89cf470b4a5 100644 --- a/java/ql/src/utils/flow-testcase-generator/FlowTestCaseSupportMethods.qll +++ b/java/ql/src/utils/flowtestcasegenerator/FlowTestCaseSupportMethods.qll @@ -217,6 +217,22 @@ private class EnumerationGetMethod extends GetMethod { override string getCall(string arg) { result = "getElement(" + arg + ")" } } +private class StreamGetMethod extends GetMethod { + StreamGetMethod() { this = "streamgetmethod" } + + override predicate appliesTo(Type t, Content c) { + t.(RefType).getASourceSupertype*().hasQualifiedName("java.util.stream", "BaseStream") and + c instanceof CollectionContent + } + + override string getDefinition() { + result = " T getElement(BaseStream s) { return s.iterator().next(); }" + } + + bindingset[arg] + override string getCall(string arg) { result = "getElement(" + arg + ")" } +} + private class OptionalGetMethod extends GetMethod { OptionalGetMethod() { this = "optionalgetmethod" } @@ -392,6 +408,20 @@ private class IteratorGenMethod extends GenMethod { override string getCall(string arg) { result = "List.of(" + arg + ").iterator()" } } +private class StreamGenMethod extends GenMethod { + StreamGenMethod() { this = "streamgenmethod" } + + override predicate appliesTo(Type t, Content c) { + exists(GenericType op | op.hasQualifiedName("java.util.stream", ["BaseStream", "Stream"]) | + op.getAParameterizedType().getASupertype*() = t + ) and + c instanceof CollectionContent + } + + bindingset[arg] + override string getCall(string arg) { result = "Stream.of(" + arg + ")" } +} + private class OptionalGenMethod extends GenMethod { OptionalGenMethod() { this = "optionalgenmethod" } diff --git a/java/ql/src/utils/flow-testcase-generator/FlowTestCaseUtils.qll b/java/ql/src/utils/flowtestcasegenerator/FlowTestCaseUtils.qll similarity index 100% rename from java/ql/src/utils/flow-testcase-generator/FlowTestCaseUtils.qll rename to java/ql/src/utils/flowtestcasegenerator/FlowTestCaseUtils.qll diff --git a/java/ql/src/utils/flow-testcase-generator/GenerateFlowTestCase.py b/java/ql/src/utils/flowtestcasegenerator/GenerateFlowTestCase.py similarity index 97% rename from java/ql/src/utils/flow-testcase-generator/GenerateFlowTestCase.py rename to java/ql/src/utils/flowtestcasegenerator/GenerateFlowTestCase.py index cc4c90809ab..38d90830bf5 100755 --- a/java/ql/src/utils/flow-testcase-generator/GenerateFlowTestCase.py +++ b/java/ql/src/utils/flowtestcasegenerator/GenerateFlowTestCase.py @@ -130,7 +130,7 @@ with open(os.path.join(queryDir, "qlpack.yml"), "w") as f: f.write("name: test-generation-query\nversion: 0.0.0\nlibraryPathDependencies: codeql/java-queries") with open(qlFile, "w") as f: f.write( - "import java\nimport utils.GenerateFlowTestCase\n\nclass GenRow extends TargetSummaryModelCsv {\n\n\toverride predicate row(string r) {\n\t\tr = [\n") + "import java\nimport utils.flowtestcasegenerator.GenerateFlowTestCase\n\nclass GenRow extends TargetSummaryModelCsv {\n\n\toverride predicate row(string r) {\n\t\tr = [\n") f.write(",\n".join('\t\t\t"%s"' % spec.strip() for spec in specs)) f.write("\n\t\t]\n\t}\n}\n") diff --git a/java/ql/src/utils/flow-testcase-generator/GenerateFlowTestCase.qll b/java/ql/src/utils/flowtestcasegenerator/GenerateFlowTestCase.qll similarity index 100% rename from java/ql/src/utils/flow-testcase-generator/GenerateFlowTestCase.qll rename to java/ql/src/utils/flowtestcasegenerator/GenerateFlowTestCase.qll diff --git a/java/ql/src/utils/flow-testcase-generator/testHeader.qlfrag b/java/ql/src/utils/flowtestcasegenerator/testHeader.qlfrag similarity index 100% rename from java/ql/src/utils/flow-testcase-generator/testHeader.qlfrag rename to java/ql/src/utils/flowtestcasegenerator/testHeader.qlfrag diff --git a/java/ql/src/utils/flow-testcase-generator/testModelsFooter.qlfrag b/java/ql/src/utils/flowtestcasegenerator/testModelsFooter.qlfrag similarity index 100% rename from java/ql/src/utils/flow-testcase-generator/testModelsFooter.qlfrag rename to java/ql/src/utils/flowtestcasegenerator/testModelsFooter.qlfrag diff --git a/java/ql/src/utils/flow-testcase-generator/testModelsHeader.qlfrag b/java/ql/src/utils/flowtestcasegenerator/testModelsHeader.qlfrag similarity index 100% rename from java/ql/src/utils/flow-testcase-generator/testModelsHeader.qlfrag rename to java/ql/src/utils/flowtestcasegenerator/testModelsHeader.qlfrag diff --git a/java/ql/src/utils/stub-generator/Stubs.qll b/java/ql/src/utils/stub-generator/Stubs.qll index 47e4b5597f4..8101ca5e130 100644 --- a/java/ql/src/utils/stub-generator/Stubs.qll +++ b/java/ql/src/utils/stub-generator/Stubs.qll @@ -361,6 +361,12 @@ private predicate excludedMember(Member m) { m.(Method).getDeclaringType() instanceof EnumType and m.hasName(["values", "valueOf"]) and m.isStatic() + or + exists(Parameter p | + p = m.(Method).getAParameter() and + p.getType().fromSource() and + not p.getType().(RefType).isPublic() + ) } private string stubMember(Member m) { diff --git a/java/ql/test/TestUtilities/InlineExpectationsTest.qll b/java/ql/test/TestUtilities/InlineExpectationsTest.qll index d351bac89a8..52a790cca28 100644 --- a/java/ql/test/TestUtilities/InlineExpectationsTest.qll +++ b/java/ql/test/TestUtilities/InlineExpectationsTest.qll @@ -4,7 +4,7 @@ * (in that the `.expected` file should always be empty). * * To add this framework to a new language: - * - Add a file `InlineExpectationsTestPrivate.qll` that defines a `LineComment` class. This class + * - Add a file `InlineExpectationsTestPrivate.qll` that defines a `ExpectationComment` class. This class * must support a `getContents` method that returns the contents of the given comment, _excluding_ * the comment indicator itself. It should also define `toString` and `getLocation` as usual. * @@ -60,8 +60,8 @@ * * Example: * ```cpp - * int i = x + 5; // $const=5 - * int j = y + (7 - 3) // $const=7 const=3 const=4 // The result of the subtraction is a constant. + * int i = x + 5; // $ const=5 + * int j = y + (7 - 3) // $ const=7 const=3 const=4 // The result of the subtraction is a constant. * ``` * * For tests that contain known missing and spurious results, it is possible to further @@ -194,7 +194,7 @@ private int getEndOfColumnPosition(int start, string content) { } private predicate getAnExpectation( - LineComment comment, TColumn column, string expectation, string tags, string value + ExpectationComment comment, TColumn column, string expectation, string tags, string value ) { exists(string content | content = comment.getContents().regexpCapture(expectationCommentPattern(), 1) and @@ -247,14 +247,14 @@ private newtype TFailureLocatable = ) { test.hasActualResult(location, element, tag, value) } or - TValidExpectation(LineComment comment, string tag, string value, string knownFailure) { + TValidExpectation(ExpectationComment comment, string tag, string value, string knownFailure) { exists(TColumn column, string tags | getAnExpectation(comment, column, _, tags, value) and tag = tags.splitAt(",") and knownFailure = getColumnString(column) ) } or - TInvalidExpectation(LineComment comment, string expectation) { + TInvalidExpectation(ExpectationComment comment, string expectation) { getAnExpectation(comment, _, expectation, _, _) and not expectation.regexpMatch(expectationPattern()) } @@ -292,7 +292,7 @@ class ActualResult extends FailureLocatable, TActualResult { } abstract private class Expectation extends FailureLocatable { - LineComment comment; + ExpectationComment comment; override string toString() { result = comment.toString() } diff --git a/java/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll b/java/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll index e2c0e0091ba..1ee16e93d10 100644 --- a/java/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll +++ b/java/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll @@ -4,8 +4,8 @@ import java * A class representing line comments in Java, which is simply Javadoc restricted * to EOL comments, with an extra accessor used by the InlineExpectations core code */ -class LineComment extends Javadoc { - LineComment() { isEolComment(this) } +class ExpectationComment extends Javadoc { + ExpectationComment() { isEolComment(this) } /** Gets the contents of the given comment, _without_ the preceding comment marker (`//`). */ string getContents() { result = this.getChild(0).toString() } diff --git a/java/ql/test/TestUtilities/InlineFlowTest.qll b/java/ql/test/TestUtilities/InlineFlowTest.qll index 1ed61182260..e6a0a551d15 100644 --- a/java/ql/test/TestUtilities/InlineFlowTest.qll +++ b/java/ql/test/TestUtilities/InlineFlowTest.qll @@ -43,12 +43,14 @@ import semmle.code.java.dataflow.ExternalFlow import semmle.code.java.dataflow.TaintTracking import TestUtilities.InlineExpectationsTest +private predicate defaultSource(DataFlow::Node src) { + src.asExpr().(MethodAccess).getMethod().getName() = ["source", "taint"] +} + class DefaultValueFlowConf extends DataFlow::Configuration { DefaultValueFlowConf() { this = "qltest:defaultValueFlowConf" } - override predicate isSource(DataFlow::Node n) { - n.asExpr().(MethodAccess).getMethod().getName() = ["source", "taint"] - } + override predicate isSource(DataFlow::Node n) { defaultSource(n) } override predicate isSink(DataFlow::Node n) { exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument()) @@ -60,9 +62,7 @@ class DefaultValueFlowConf extends DataFlow::Configuration { class DefaultTaintFlowConf extends TaintTracking::Configuration { DefaultTaintFlowConf() { this = "qltest:defaultTaintFlowConf" } - override predicate isSource(DataFlow::Node n) { - n.asExpr().(MethodAccess).getMethod().getName() = ["source", "taint"] - } + override predicate isSource(DataFlow::Node n) { defaultSource(n) } override predicate isSink(DataFlow::Node n) { exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument()) @@ -71,6 +71,11 @@ class DefaultTaintFlowConf extends TaintTracking::Configuration { override int fieldFlowBranchLimit() { result = 1000 } } +private string getSourceArgString(DataFlow::Node src) { + defaultSource(src) and + src.asExpr().(MethodAccess).getAnArgument().(StringLiteral).getValue() = result +} + class InlineFlowTest extends InlineExpectationsTest { InlineFlowTest() { this = "HasFlowTest" } @@ -81,7 +86,7 @@ class InlineFlowTest extends InlineExpectationsTest { exists(DataFlow::Node src, DataFlow::Node sink | getValueFlowConfig().hasFlow(src, sink) | sink.getLocation() = location and element = sink.toString() and - value = "" + if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = "" ) or tag = "hasTaintFlow" and @@ -90,7 +95,7 @@ class InlineFlowTest extends InlineExpectationsTest { | sink.getLocation() = location and element = sink.toString() and - value = "" + if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = "" ) } diff --git a/java/ql/test/experimental/query-tests/security/CWE-200/FileService.java b/java/ql/test/experimental/query-tests/security/CWE-200/FileService.java new file mode 100644 index 00000000000..1e2895001e4 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-200/FileService.java @@ -0,0 +1,64 @@ +import java.io.FileOutputStream; + +import android.app.Service; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.AsyncTask; + +public class FileService extends Service { + public static String KEY_LOCAL_FILE = "local_file"; + /** + * Service initialization + */ + @Override + public void onCreate() { + super.onCreate(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + String localPath = intent.getStringExtra(KEY_LOCAL_FILE); + CopyAndUploadContentUrisTask copyTask = new CopyAndUploadContentUrisTask(); + + copyTask.execute( + copyTask.makeParamsToExecute(localPath) + ); + return 2; + } + + public class CopyAndUploadContentUrisTask extends AsyncTask { + public Object[] makeParamsToExecute( + String sourceUri + ) { + return new Object[] { + sourceUri + }; + } + + @Override + protected String doInBackground(Object[] params) { + FileOutputStream outputStream = null; + + try { + String[] uris = (String[]) params[1]; + outputStream = new FileOutputStream(uris[0]); + return "success"; + } catch (Exception e) { + } + return "failure"; + } + + @Override + protected void onPostExecute(String result) { + } + + @Override + protected void onPreExecute() { + } + + @Override + protected void onProgressUpdate(Void... values) { + } + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-200/GetFileActivity.java b/java/ql/test/experimental/query-tests/security/CWE-200/GetFileActivity.java new file mode 100644 index 00000000000..762ac1f43db --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-200/GetFileActivity.java @@ -0,0 +1,20 @@ +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; + +public class GetFileActivity extends Activity { + public static final int REQUEST_CODE__SELECT_CONTENT_FROM_APPS = 99; + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(-1); + + Intent action = new Intent(Intent.ACTION_GET_CONTENT); + action = action.setType("*/*").addCategory(Intent.CATEGORY_OPENABLE); + action.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); + + startActivityForResult( + Intent.createChooser(action, "Open File From Selected Application"), REQUEST_CODE__SELECT_CONTENT_FROM_APPS + ); + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-200/LeakFileActivity.java b/java/ql/test/experimental/query-tests/security/CWE-200/LeakFileActivity.java new file mode 100644 index 00000000000..3520ed0fd40 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-200/LeakFileActivity.java @@ -0,0 +1,26 @@ +import java.io.RandomAccessFile; + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; + +public class LeakFileActivity extends Activity { + @Override + // BAD: Load file from activity without validation + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == GetFileActivity.REQUEST_CODE__SELECT_CONTENT_FROM_APPS && + resultCode == RESULT_OK) { + loadOfContentFromApps(data, resultCode); + } + } + + private void loadOfContentFromApps(Intent contentIntent, int resultCode) { + Uri streamsToUpload = contentIntent.getData(); + try { + RandomAccessFile file = new RandomAccessFile(streamsToUpload.getPath(), "r"); + } catch (Exception ex) { + ex.printStackTrace(); + } + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-200/LeakFileActivity2.java b/java/ql/test/experimental/query-tests/security/CWE-200/LeakFileActivity2.java new file mode 100644 index 00000000000..56e695ec97a --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-200/LeakFileActivity2.java @@ -0,0 +1,19 @@ +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; + +public class LeakFileActivity2 extends Activity { + @Override + // BAD: Load file in a service without validation + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + Uri localPath = data.getData(); + + if (requestCode == GetFileActivity.REQUEST_CODE__SELECT_CONTENT_FROM_APPS && + resultCode == RESULT_OK) { + Intent intent = new Intent(this, FileService.class); + intent.putExtra(FileService.KEY_LOCAL_FILE, localPath); + startService(intent); + } + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-200/SafeFileActivity.java b/java/ql/test/experimental/query-tests/security/CWE-200/SafeFileActivity.java new file mode 100644 index 00000000000..a919888a658 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-200/SafeFileActivity.java @@ -0,0 +1,28 @@ +import java.io.RandomAccessFile; + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; + +public class SafeFileActivity extends Activity { + @Override + // GOOD: Load file from activity with path validation + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == GetFileActivity.REQUEST_CODE__SELECT_CONTENT_FROM_APPS && + resultCode == RESULT_OK) { + safeLoadOfContentFromApps(data, resultCode); + } + } + + private void safeLoadOfContentFromApps(Intent contentIntent, int resultCode) { + Uri streamsToUpload = contentIntent.getData(); + try { + if (!streamsToUpload.getPath().startsWith("/data/data")) { + RandomAccessFile file = new RandomAccessFile(streamsToUpload.getPath(), "r"); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-200/SensitiveAndroidFileLeak.expected b/java/ql/test/experimental/query-tests/security/CWE-200/SensitiveAndroidFileLeak.expected new file mode 100644 index 00000000000..b67c634ad9f --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-200/SensitiveAndroidFileLeak.expected @@ -0,0 +1,48 @@ +edges +| FileService.java:20:31:20:43 | intent : Intent | FileService.java:21:28:21:33 | intent : Intent | +| FileService.java:21:28:21:33 | intent : Intent | FileService.java:21:28:21:64 | getStringExtra(...) : Object | +| FileService.java:21:28:21:64 | getStringExtra(...) : Object | FileService.java:25:42:25:50 | localPath : Object | +| FileService.java:25:13:25:51 | makeParamsToExecute(...) : Object[] | FileService.java:40:41:40:55 | params : Object[] | +| FileService.java:25:13:25:51 | makeParamsToExecute(...) [[]] : Object | FileService.java:25:13:25:51 | makeParamsToExecute(...) : Object[] | +| FileService.java:25:42:25:50 | localPath : Object | FileService.java:25:13:25:51 | makeParamsToExecute(...) [[]] : Object | +| FileService.java:25:42:25:50 | localPath : Object | FileService.java:32:13:32:28 | sourceUri : Object | +| FileService.java:32:13:32:28 | sourceUri : Object | FileService.java:35:17:35:25 | sourceUri : Object | +| FileService.java:34:20:36:13 | {...} [[]] : Object | FileService.java:34:20:36:13 | new Object[] [[]] : Object | +| FileService.java:35:17:35:25 | sourceUri : Object | FileService.java:34:20:36:13 | {...} [[]] : Object | +| FileService.java:40:41:40:55 | params : Object[] | FileService.java:44:33:44:52 | (...)... : Object | +| FileService.java:44:33:44:52 | (...)... : Object | FileService.java:45:53:45:59 | ...[...] | +| LeakFileActivity2.java:15:13:15:18 | intent : Intent | LeakFileActivity2.java:16:26:16:31 | intent : Intent | +| LeakFileActivity2.java:16:26:16:31 | intent : Intent | FileService.java:20:31:20:43 | intent : Intent | +| LeakFileActivity.java:14:35:14:38 | data : Intent | LeakFileActivity.java:18:40:18:59 | contentIntent : Intent | +| LeakFileActivity.java:18:40:18:59 | contentIntent : Intent | LeakFileActivity.java:19:31:19:43 | contentIntent : Intent | +| LeakFileActivity.java:19:31:19:43 | contentIntent : Intent | LeakFileActivity.java:19:31:19:53 | getData(...) : Object | +| LeakFileActivity.java:19:31:19:53 | getData(...) : Object | LeakFileActivity.java:21:58:21:72 | streamsToUpload : Object | +| LeakFileActivity.java:21:58:21:72 | streamsToUpload : Object | LeakFileActivity.java:21:58:21:82 | getPath(...) | +nodes +| FileService.java:20:31:20:43 | intent : Intent | semmle.label | intent : Intent | +| FileService.java:21:28:21:33 | intent : Intent | semmle.label | intent : Intent | +| FileService.java:21:28:21:64 | getStringExtra(...) : Object | semmle.label | getStringExtra(...) : Object | +| FileService.java:25:13:25:51 | makeParamsToExecute(...) : Object[] | semmle.label | makeParamsToExecute(...) : Object[] | +| FileService.java:25:13:25:51 | makeParamsToExecute(...) [[]] : Object | semmle.label | makeParamsToExecute(...) [[]] : Object | +| FileService.java:25:42:25:50 | localPath : Object | semmle.label | localPath : Object | +| FileService.java:32:13:32:28 | sourceUri : Object | semmle.label | sourceUri : Object | +| FileService.java:34:20:36:13 | new Object[] [[]] : Object | semmle.label | new Object[] [[]] : Object | +| FileService.java:34:20:36:13 | {...} [[]] : Object | semmle.label | {...} [[]] : Object | +| FileService.java:35:17:35:25 | sourceUri : Object | semmle.label | sourceUri : Object | +| FileService.java:40:41:40:55 | params : Object[] | semmle.label | params : Object[] | +| FileService.java:44:33:44:52 | (...)... : Object | semmle.label | (...)... : Object | +| FileService.java:45:53:45:59 | ...[...] | semmle.label | ...[...] | +| LeakFileActivity2.java:15:13:15:18 | intent : Intent | semmle.label | intent : Intent | +| LeakFileActivity2.java:16:26:16:31 | intent : Intent | semmle.label | intent : Intent | +| LeakFileActivity.java:14:35:14:38 | data : Intent | semmle.label | data : Intent | +| LeakFileActivity.java:18:40:18:59 | contentIntent : Intent | semmle.label | contentIntent : Intent | +| LeakFileActivity.java:19:31:19:43 | contentIntent : Intent | semmle.label | contentIntent : Intent | +| LeakFileActivity.java:19:31:19:53 | getData(...) : Object | semmle.label | getData(...) : Object | +| LeakFileActivity.java:21:58:21:72 | streamsToUpload : Object | semmle.label | streamsToUpload : Object | +| LeakFileActivity.java:21:58:21:82 | getPath(...) | semmle.label | getPath(...) | +subpaths +| FileService.java:25:42:25:50 | localPath : Object | FileService.java:32:13:32:28 | sourceUri : Object | FileService.java:34:20:36:13 | new Object[] [[]] : Object | FileService.java:25:13:25:51 | makeParamsToExecute(...) [[]] : Object | +#select +| FileService.java:45:53:45:59 | ...[...] | LeakFileActivity2.java:15:13:15:18 | intent : Intent | FileService.java:45:53:45:59 | ...[...] | Leaking arbitrary Android file from $@. | LeakFileActivity2.java:15:13:15:18 | intent | this user input | +| FileService.java:45:53:45:59 | ...[...] | LeakFileActivity2.java:16:26:16:31 | intent : Intent | FileService.java:45:53:45:59 | ...[...] | Leaking arbitrary Android file from $@. | LeakFileActivity2.java:16:26:16:31 | intent | this user input | +| LeakFileActivity.java:21:58:21:82 | getPath(...) | LeakFileActivity.java:14:35:14:38 | data : Intent | LeakFileActivity.java:21:58:21:82 | getPath(...) | Leaking arbitrary Android file from $@. | LeakFileActivity.java:14:35:14:38 | data | this user input | diff --git a/java/ql/test/experimental/query-tests/security/CWE-200/SensitiveAndroidFileLeak.qlref b/java/ql/test/experimental/query-tests/security/CWE-200/SensitiveAndroidFileLeak.qlref new file mode 100644 index 00000000000..2959fb2e85c --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-200/SensitiveAndroidFileLeak.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-200/SensitiveAndroidFileLeak.ql \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-927/options b/java/ql/test/experimental/query-tests/security/CWE-200/options similarity index 100% rename from java/ql/test/experimental/query-tests/security/CWE-927/options rename to java/ql/test/experimental/query-tests/security/CWE-200/options diff --git a/java/ql/test/experimental/query-tests/security/CWE-297/InsecureJavaMail.expected b/java/ql/test/experimental/query-tests/security/CWE-297/InsecureJavaMail.expected deleted file mode 100644 index 0e3c219ace3..00000000000 --- a/java/ql/test/experimental/query-tests/security/CWE-297/InsecureJavaMail.expected +++ /dev/null @@ -1,2 +0,0 @@ -| InsecureJavaMail.java:29:27:29:72 | getInstance(...) | Java mailing has insecure SSL configuration | -| InsecureJavaMail.java:37:3:37:29 | setSSLOnConnect(...) | Java mailing has insecure SSL configuration | diff --git a/java/ql/test/experimental/query-tests/security/CWE-297/InsecureJavaMail.java b/java/ql/test/experimental/query-tests/security/CWE-297/InsecureJavaMail.java deleted file mode 100644 index 62c64027695..00000000000 --- a/java/ql/test/experimental/query-tests/security/CWE-297/InsecureJavaMail.java +++ /dev/null @@ -1,45 +0,0 @@ -import java.util.Properties; - -import javax.mail.Authenticator; -import javax.mail.PasswordAuthentication; -import javax.mail.Session; - -import org.apache.commons.mail.DefaultAuthenticator; -import org.apache.commons.mail.Email; -import org.apache.commons.mail.SimpleEmail; - -import java.util.Properties; - -class InsecureJavaMail { - public void testJavaMail() { - final Properties properties = new Properties(); - properties.put("mail.transport.protocol", "protocol"); - properties.put("mail.smtp.host", "hostname"); - properties.put("mail.smtp.socketFactory.class", "classname"); - - final javax.mail.Authenticator authenticator = new javax.mail.Authenticator() { - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication("username", "password"); - } - }; - if (null != authenticator) { - properties.put("mail.smtp.auth", "true"); - // properties.put("mail.smtp.ssl.checkserveridentity", "true"); - } - final Session session = Session.getInstance(properties, authenticator); - } - - public void testSimpleMail() throws Exception { - Email email = new SimpleEmail(); - email.setHostName("config.hostName"); - email.setSmtpPort(25); - email.setAuthenticator(new DefaultAuthenticator("config.username", "config.password")); - email.setSSLOnConnect(true); - // email.setSSLCheckServerIdentity(true); - email.setFrom("fromAddress"); - email.setSubject("subject"); - email.setMsg("body"); - email.addTo("toAddress"); - email.send(); - } -} diff --git a/java/ql/test/experimental/query-tests/security/CWE-297/InsecureJavaMail.qlref b/java/ql/test/experimental/query-tests/security/CWE-297/InsecureJavaMail.qlref deleted file mode 100644 index 565779521f3..00000000000 --- a/java/ql/test/experimental/query-tests/security/CWE-297/InsecureJavaMail.qlref +++ /dev/null @@ -1 +0,0 @@ -experimental/Security/CWE/CWE-297/InsecureJavaMail.ql diff --git a/java/ql/test/experimental/query-tests/security/CWE-297/options b/java/ql/test/experimental/query-tests/security/CWE-297/options deleted file mode 100644 index 51c4feeca1b..00000000000 --- a/java/ql/test/experimental/query-tests/security/CWE-297/options +++ /dev/null @@ -1 +0,0 @@ -// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/apache-commons-email-1.6.0:${testdir}/../../../../stubs/javamail-api-1.6.2 diff --git a/java/ql/test/experimental/query-tests/security/CWE-552/UnsafeUrlForward.expected b/java/ql/test/experimental/query-tests/security/CWE-552/UnsafeUrlForward.expected new file mode 100644 index 00000000000..b70a131f93c --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-552/UnsafeUrlForward.expected @@ -0,0 +1,31 @@ +edges +| UnsafeUrlForward.java:13:27:13:36 | url : String | UnsafeUrlForward.java:14:27:14:29 | url | +| UnsafeUrlForward.java:18:27:18:36 | url : String | UnsafeUrlForward.java:20:28:20:30 | url | +| UnsafeUrlForward.java:25:21:25:30 | url : String | UnsafeUrlForward.java:26:23:26:25 | url | +| UnsafeUrlForward.java:30:27:30:36 | url : String | UnsafeUrlForward.java:31:48:31:63 | ... + ... | +| UnsafeUrlForward.java:30:27:30:36 | url : String | UnsafeUrlForward.java:31:61:31:63 | url | +| UnsafeUrlForward.java:36:19:36:28 | url : String | UnsafeUrlForward.java:38:33:38:35 | url | +| UnsafeUrlForward.java:47:19:47:28 | url : String | UnsafeUrlForward.java:49:33:49:62 | ... + ... | +nodes +| UnsafeUrlForward.java:13:27:13:36 | url : String | semmle.label | url : String | +| UnsafeUrlForward.java:14:27:14:29 | url | semmle.label | url | +| UnsafeUrlForward.java:18:27:18:36 | url : String | semmle.label | url : String | +| UnsafeUrlForward.java:20:28:20:30 | url | semmle.label | url | +| UnsafeUrlForward.java:25:21:25:30 | url : String | semmle.label | url : String | +| UnsafeUrlForward.java:26:23:26:25 | url | semmle.label | url | +| UnsafeUrlForward.java:30:27:30:36 | url : String | semmle.label | url : String | +| UnsafeUrlForward.java:31:48:31:63 | ... + ... | semmle.label | ... + ... | +| UnsafeUrlForward.java:31:61:31:63 | url | semmle.label | url | +| UnsafeUrlForward.java:36:19:36:28 | url : String | semmle.label | url : String | +| UnsafeUrlForward.java:38:33:38:35 | url | semmle.label | url | +| UnsafeUrlForward.java:47:19:47:28 | url : String | semmle.label | url : String | +| UnsafeUrlForward.java:49:33:49:62 | ... + ... | semmle.label | ... + ... | +subpaths +#select +| UnsafeUrlForward.java:14:27:14:29 | url | UnsafeUrlForward.java:13:27:13:36 | url : String | UnsafeUrlForward.java:14:27:14:29 | url | Potentially untrusted URL forward due to $@. | UnsafeUrlForward.java:13:27:13:36 | url | user-provided value | +| UnsafeUrlForward.java:20:28:20:30 | url | UnsafeUrlForward.java:18:27:18:36 | url : String | UnsafeUrlForward.java:20:28:20:30 | url | Potentially untrusted URL forward due to $@. | UnsafeUrlForward.java:18:27:18:36 | url | user-provided value | +| UnsafeUrlForward.java:26:23:26:25 | url | UnsafeUrlForward.java:25:21:25:30 | url : String | UnsafeUrlForward.java:26:23:26:25 | url | Potentially untrusted URL forward due to $@. | UnsafeUrlForward.java:25:21:25:30 | url | user-provided value | +| UnsafeUrlForward.java:31:48:31:63 | ... + ... | UnsafeUrlForward.java:30:27:30:36 | url : String | UnsafeUrlForward.java:31:48:31:63 | ... + ... | Potentially untrusted URL forward due to $@. | UnsafeUrlForward.java:30:27:30:36 | url | user-provided value | +| UnsafeUrlForward.java:31:61:31:63 | url | UnsafeUrlForward.java:30:27:30:36 | url : String | UnsafeUrlForward.java:31:61:31:63 | url | Potentially untrusted URL forward due to $@. | UnsafeUrlForward.java:30:27:30:36 | url | user-provided value | +| UnsafeUrlForward.java:38:33:38:35 | url | UnsafeUrlForward.java:36:19:36:28 | url : String | UnsafeUrlForward.java:38:33:38:35 | url | Potentially untrusted URL forward due to $@. | UnsafeUrlForward.java:36:19:36:28 | url | user-provided value | +| UnsafeUrlForward.java:49:33:49:62 | ... + ... | UnsafeUrlForward.java:47:19:47:28 | url : String | UnsafeUrlForward.java:49:33:49:62 | ... + ... | Potentially untrusted URL forward due to $@. | UnsafeUrlForward.java:47:19:47:28 | url | user-provided value | diff --git a/java/ql/test/experimental/query-tests/security/CWE-552/UnsafeUrlForward.java b/java/ql/test/experimental/query-tests/security/CWE-552/UnsafeUrlForward.java new file mode 100644 index 00000000000..1e7ce3a97c5 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-552/UnsafeUrlForward.java @@ -0,0 +1,67 @@ +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.servlet.ModelAndView; + +@Controller +public class UnsafeUrlForward { + + @GetMapping("/bad1") + public ModelAndView bad1(String url) { + return new ModelAndView(url); + } + + @GetMapping("/bad2") + public ModelAndView bad2(String url) { + ModelAndView modelAndView = new ModelAndView(); + modelAndView.setViewName(url); + return modelAndView; + } + + @GetMapping("/bad3") + public String bad3(String url) { + return "forward:" + url + "/swagger-ui/index.html"; + } + + @GetMapping("/bad4") + public ModelAndView bad4(String url) { + ModelAndView modelAndView = new ModelAndView("forward:" + url); + return modelAndView; + } + + @GetMapping("/bad5") + public void bad5(String url, HttpServletRequest request, HttpServletResponse response) { + try { + request.getRequestDispatcher(url).include(request, response); + } catch (ServletException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @GetMapping("/bad6") + public void bad6(String url, HttpServletRequest request, HttpServletResponse response) { + try { + request.getRequestDispatcher("/WEB-INF/jsp/" + url + ".jsp").include(request, response); + } catch (ServletException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @GetMapping("/good1") + public void good1(String url, HttpServletRequest request, HttpServletResponse response) { + try { + request.getRequestDispatcher("/index.jsp?token=" + url).forward(request, response); + } catch (ServletException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-552/UnsafeUrlForward.qlref b/java/ql/test/experimental/query-tests/security/CWE-552/UnsafeUrlForward.qlref new file mode 100644 index 00000000000..2e4cb5e726a --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-552/UnsafeUrlForward.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-552/UnsafeUrlForward.ql \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-552/options b/java/ql/test/experimental/query-tests/security/CWE-552/options new file mode 100644 index 00000000000..ba166b547a0 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-552/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/springframework-5.3.8/ \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-755/NFEAndroidDoS.expected b/java/ql/test/experimental/query-tests/security/CWE-755/NFEAndroidDoS.expected index 16c97df96e8..73657a38158 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-755/NFEAndroidDoS.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-755/NFEAndroidDoS.expected @@ -1,17 +1,25 @@ edges -| NFEAndroidDoS.java:13:24:13:34 | getIntent(...) : Intent | NFEAndroidDoS.java:14:21:14:51 | parseDouble(...) | -| NFEAndroidDoS.java:22:21:22:31 | getIntent(...) : Intent | NFEAndroidDoS.java:23:15:23:40 | parseInt(...) | -| NFEAndroidDoS.java:25:22:25:32 | getIntent(...) : Intent | NFEAndroidDoS.java:26:16:26:42 | parseInt(...) | -| NFEAndroidDoS.java:43:24:43:34 | getIntent(...) : Intent | NFEAndroidDoS.java:44:21:44:43 | new Double(...) | -| NFEAndroidDoS.java:43:24:43:34 | getIntent(...) : Intent | NFEAndroidDoS.java:47:21:47:47 | valueOf(...) | +| NFEAndroidDoS.java:13:24:13:34 | getIntent(...) : Intent | NFEAndroidDoS.java:13:24:13:61 | getStringExtra(...) : Object | +| NFEAndroidDoS.java:13:24:13:61 | getStringExtra(...) : Object | NFEAndroidDoS.java:14:21:14:51 | parseDouble(...) | +| NFEAndroidDoS.java:22:21:22:31 | getIntent(...) : Intent | NFEAndroidDoS.java:22:21:22:55 | getStringExtra(...) : Object | +| NFEAndroidDoS.java:22:21:22:55 | getStringExtra(...) : Object | NFEAndroidDoS.java:23:15:23:40 | parseInt(...) | +| NFEAndroidDoS.java:25:22:25:32 | getIntent(...) : Intent | NFEAndroidDoS.java:25:22:25:57 | getStringExtra(...) : Object | +| NFEAndroidDoS.java:25:22:25:57 | getStringExtra(...) : Object | NFEAndroidDoS.java:26:16:26:42 | parseInt(...) | +| NFEAndroidDoS.java:43:24:43:34 | getIntent(...) : Intent | NFEAndroidDoS.java:43:24:43:61 | getStringExtra(...) : Object | +| NFEAndroidDoS.java:43:24:43:61 | getStringExtra(...) : Object | NFEAndroidDoS.java:44:21:44:43 | new Double(...) | +| NFEAndroidDoS.java:43:24:43:61 | getStringExtra(...) : Object | NFEAndroidDoS.java:47:21:47:47 | valueOf(...) | nodes | NFEAndroidDoS.java:13:24:13:34 | getIntent(...) : Intent | semmle.label | getIntent(...) : Intent | +| NFEAndroidDoS.java:13:24:13:61 | getStringExtra(...) : Object | semmle.label | getStringExtra(...) : Object | | NFEAndroidDoS.java:14:21:14:51 | parseDouble(...) | semmle.label | parseDouble(...) | | NFEAndroidDoS.java:22:21:22:31 | getIntent(...) : Intent | semmle.label | getIntent(...) : Intent | +| NFEAndroidDoS.java:22:21:22:55 | getStringExtra(...) : Object | semmle.label | getStringExtra(...) : Object | | NFEAndroidDoS.java:23:15:23:40 | parseInt(...) | semmle.label | parseInt(...) | | NFEAndroidDoS.java:25:22:25:32 | getIntent(...) : Intent | semmle.label | getIntent(...) : Intent | +| NFEAndroidDoS.java:25:22:25:57 | getStringExtra(...) : Object | semmle.label | getStringExtra(...) : Object | | NFEAndroidDoS.java:26:16:26:42 | parseInt(...) | semmle.label | parseInt(...) | | NFEAndroidDoS.java:43:24:43:34 | getIntent(...) : Intent | semmle.label | getIntent(...) : Intent | +| NFEAndroidDoS.java:43:24:43:61 | getStringExtra(...) : Object | semmle.label | getStringExtra(...) : Object | | NFEAndroidDoS.java:44:21:44:43 | new Double(...) | semmle.label | new Double(...) | | NFEAndroidDoS.java:47:21:47:47 | valueOf(...) | semmle.label | valueOf(...) | subpaths diff --git a/java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.expected b/java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.expected deleted file mode 100644 index d4c4fc36a4c..00000000000 --- a/java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.expected +++ /dev/null @@ -1,37 +0,0 @@ -edges -| SensitiveBroadcast.java:12:34:12:38 | token : String | SensitiveBroadcast.java:14:31:14:36 | intent | -| SensitiveBroadcast.java:13:41:13:52 | refreshToken : String | SensitiveBroadcast.java:14:31:14:36 | intent | -| SensitiveBroadcast.java:25:32:25:39 | password : String | SensitiveBroadcast.java:26:31:26:36 | intent | -| SensitiveBroadcast.java:36:35:36:39 | email : String | SensitiveBroadcast.java:38:31:38:36 | intent | -| SensitiveBroadcast.java:50:9:50:16 | userinfo [post update] [] : String | SensitiveBroadcast.java:52:31:52:36 | intent | -| SensitiveBroadcast.java:50:22:50:29 | password : String | SensitiveBroadcast.java:50:9:50:16 | userinfo [post update] [] : String | -| SensitiveBroadcast.java:97:35:97:40 | ticket : String | SensitiveBroadcast.java:98:54:98:59 | intent | -| SensitiveBroadcast.java:109:32:109:39 | passcode : String | SensitiveBroadcast.java:111:54:111:59 | intent | -| SensitiveBroadcast.java:136:33:136:38 | passwd : String | SensitiveBroadcast.java:140:54:140:59 | intent | -nodes -| SensitiveBroadcast.java:12:34:12:38 | token : String | semmle.label | token : String | -| SensitiveBroadcast.java:13:41:13:52 | refreshToken : String | semmle.label | refreshToken : String | -| SensitiveBroadcast.java:14:31:14:36 | intent | semmle.label | intent | -| SensitiveBroadcast.java:25:32:25:39 | password : String | semmle.label | password : String | -| SensitiveBroadcast.java:26:31:26:36 | intent | semmle.label | intent | -| SensitiveBroadcast.java:36:35:36:39 | email : String | semmle.label | email : String | -| SensitiveBroadcast.java:38:31:38:36 | intent | semmle.label | intent | -| SensitiveBroadcast.java:50:9:50:16 | userinfo [post update] [] : String | semmle.label | userinfo [post update] [] : String | -| SensitiveBroadcast.java:50:22:50:29 | password : String | semmle.label | password : String | -| SensitiveBroadcast.java:52:31:52:36 | intent | semmle.label | intent | -| SensitiveBroadcast.java:97:35:97:40 | ticket : String | semmle.label | ticket : String | -| SensitiveBroadcast.java:98:54:98:59 | intent | semmle.label | intent | -| SensitiveBroadcast.java:109:32:109:39 | passcode : String | semmle.label | passcode : String | -| SensitiveBroadcast.java:111:54:111:59 | intent | semmle.label | intent | -| SensitiveBroadcast.java:136:33:136:38 | passwd : String | semmle.label | passwd : String | -| SensitiveBroadcast.java:140:54:140:59 | intent | semmle.label | intent | -subpaths -#select -| SensitiveBroadcast.java:14:31:14:36 | intent | SensitiveBroadcast.java:12:34:12:38 | token : String | SensitiveBroadcast.java:14:31:14:36 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:12:34:12:38 | token | sensitive information | -| SensitiveBroadcast.java:14:31:14:36 | intent | SensitiveBroadcast.java:13:41:13:52 | refreshToken : String | SensitiveBroadcast.java:14:31:14:36 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:13:41:13:52 | refreshToken | sensitive information | -| SensitiveBroadcast.java:26:31:26:36 | intent | SensitiveBroadcast.java:25:32:25:39 | password : String | SensitiveBroadcast.java:26:31:26:36 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:25:32:25:39 | password | sensitive information | -| SensitiveBroadcast.java:38:31:38:36 | intent | SensitiveBroadcast.java:36:35:36:39 | email : String | SensitiveBroadcast.java:38:31:38:36 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:36:35:36:39 | email | sensitive information | -| SensitiveBroadcast.java:52:31:52:36 | intent | SensitiveBroadcast.java:50:22:50:29 | password : String | SensitiveBroadcast.java:52:31:52:36 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:50:22:50:29 | password | sensitive information | -| SensitiveBroadcast.java:98:54:98:59 | intent | SensitiveBroadcast.java:97:35:97:40 | ticket : String | SensitiveBroadcast.java:98:54:98:59 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:97:35:97:40 | ticket | sensitive information | -| SensitiveBroadcast.java:111:54:111:59 | intent | SensitiveBroadcast.java:109:32:109:39 | passcode : String | SensitiveBroadcast.java:111:54:111:59 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:109:32:109:39 | passcode | sensitive information | -| SensitiveBroadcast.java:140:54:140:59 | intent | SensitiveBroadcast.java:136:33:136:38 | passwd : String | SensitiveBroadcast.java:140:54:140:59 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:136:33:136:38 | passwd | sensitive information | diff --git a/java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.qlref b/java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.qlref deleted file mode 100644 index 20c5250d80d..00000000000 --- a/java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.qlref +++ /dev/null @@ -1 +0,0 @@ -experimental/Security/CWE/CWE-927/SensitiveBroadcast.ql diff --git a/java/ql/test/library-tests/MemberRefExpr/MemberRefExpr.expected b/java/ql/test/library-tests/MemberRefExpr/MemberRefExpr.expected new file mode 100644 index 00000000000..b02fa9c0e9a --- /dev/null +++ b/java/ql/test/library-tests/MemberRefExpr/MemberRefExpr.expected @@ -0,0 +1,14 @@ +| Test.java:24:26:24:51 | ...::... | Inner<>.Inner<> | Test$Generic$Inner.class:0:0:0:0 | Inner<> | +| Test.java:38:29:38:42 | ...::... | Object.toString | Test.java:1:7:1:10 | Test | +| Test.java:39:29:39:42 | ...::... | Object.hashCode | Test.java:1:7:1:10 | Test | +| Test.java:40:29:40:39 | ...::... | Object.clone | Test.java:1:7:1:10 | Test | +| Test.java:41:40:41:64 | ...::... | Object.toString | Test$Generic.class:0:0:0:0 | Generic | +| Test.java:43:23:43:36 | ...::... | Object.toString | Test.java:1:7:1:10 | Test | +| Test.java:44:23:44:36 | ...::... | Object.hashCode | Test.java:1:7:1:10 | Test | +| Test.java:45:23:45:33 | ...::... | Object.clone | Test.java:1:7:1:10 | Test | +| Test.java:48:22:48:35 | ...::... | Object.toString | Test.java:1:7:1:10 | Test | +| Test.java:51:13:51:21 | ...::... | Test.Test | Test.java:1:7:1:10 | Test | +| Test.java:52:13:52:32 | ...::... | Generic.Generic | Test$Generic.class:0:0:0:0 | Generic | +| Test.java:56:13:56:22 | ...::... | | file://:0:0:0:0 | int[] | +| Test.java:57:13:57:26 | ...::... | | file://:0:0:0:0 | Generic<>[] | +| Test.java:61:31:61:47 | ...::... | Test.doSomething | Test.java:1:7:1:10 | Test | diff --git a/java/ql/test/library-tests/MemberRefExpr/MemberRefExpr.ql b/java/ql/test/library-tests/MemberRefExpr/MemberRefExpr.ql new file mode 100644 index 00000000000..2f83916c50e --- /dev/null +++ b/java/ql/test/library-tests/MemberRefExpr/MemberRefExpr.ql @@ -0,0 +1,10 @@ +import java + +string getReferencedCallable(MemberRefExpr e) { + if exists(e.getReferencedCallable()) + then result = e.getReferencedCallable().getQualifiedName() + else result = "" +} + +from MemberRefExpr e +select e, getReferencedCallable(e), e.getReceiverType() diff --git a/java/ql/test/library-tests/MemberRefExpr/Test.java b/java/ql/test/library-tests/MemberRefExpr/Test.java new file mode 100644 index 00000000000..d477cf659a9 --- /dev/null +++ b/java/ql/test/library-tests/MemberRefExpr/Test.java @@ -0,0 +1,63 @@ +class Test { + interface Function { + Object apply(T o) throws Exception; + } + + interface IntFunction { + Object apply(int i); + } + + interface Supplier { + Object get() throws Exception; + } + + public Test() { } + + static class Generic { + public Generic() { } + + class Inner { + public Inner() { } + } + + void test() { + Supplier s = Generic.Inner::new; + } + } + + void doSomething() { } + + static class Sub extends Test { + } + + interface SubtypeConsumer { + void consume(Sub s); + } + + void test() { + Function f0 = Test::toString; + Function f1 = Test::hashCode; + Function f2 = Test::clone; + Function> f3 = Generic::toString; + + Supplier s0 = this::toString; + Supplier s1 = this::hashCode; + Supplier s2 = this::clone; + + // Discards result of method call + Runnable r = this::toString; + + Supplier[] classInstances = { + Test::new, + Generic::new, + }; + + IntFunction[] arrays = { + int[]::new, + Generic[]::new, + }; + + // SubtypeConsumer has `Sub` as parameter type, but receiver here is `Test` (supertype) + SubtypeConsumer sub = Test::doSomething; + } +} diff --git a/java/ql/test/library-tests/controlflow/basic/bbStmts.expected b/java/ql/test/library-tests/controlflow/basic/bbStmts.expected index fc1efcea747..e1a8d6b4ade 100644 --- a/java/ql/test/library-tests/controlflow/basic/bbStmts.expected +++ b/java/ql/test/library-tests/controlflow/basic/bbStmts.expected @@ -1,136 +1,136 @@ -| Test.java:3:14:3:17 | { ... } | Test.java:3:14:3:17 | Test | 2 | -| Test.java:3:14:3:17 | { ... } | Test.java:3:14:3:17 | super(...) | 1 | -| Test.java:3:14:3:17 | { ... } | Test.java:3:14:3:17 | { ... } | 0 | -| Test.java:4:14:4:17 | test | Test.java:4:14:4:17 | test | 0 | -| Test.java:4:21:76:2 | { ... } | Test.java:4:21:76:2 | { ... } | 0 | -| Test.java:4:21:76:2 | { ... } | Test.java:5:3:5:12 | var ...; | 1 | -| Test.java:4:21:76:2 | { ... } | Test.java:5:7:5:11 | x | 3 | -| Test.java:4:21:76:2 | { ... } | Test.java:5:11:5:11 | 0 | 2 | -| Test.java:4:21:76:2 | { ... } | Test.java:6:3:6:14 | var ...; | 4 | -| Test.java:4:21:76:2 | { ... } | Test.java:6:8:6:13 | y | 6 | -| Test.java:4:21:76:2 | { ... } | Test.java:6:12:6:13 | 50 | 5 | -| Test.java:4:21:76:2 | { ... } | Test.java:7:3:7:12 | var ...; | 7 | -| Test.java:4:21:76:2 | { ... } | Test.java:7:7:7:11 | z | 9 | -| Test.java:4:21:76:2 | { ... } | Test.java:7:11:7:11 | 0 | 8 | -| Test.java:4:21:76:2 | { ... } | Test.java:8:3:8:12 | var ...; | 10 | -| Test.java:4:21:76:2 | { ... } | Test.java:8:7:8:11 | w | 12 | -| Test.java:4:21:76:2 | { ... } | Test.java:8:11:8:11 | 0 | 11 | -| Test.java:4:21:76:2 | { ... } | Test.java:11:3:11:12 | if (...) | 13 | -| Test.java:4:21:76:2 | { ... } | Test.java:11:7:11:7 | x | 14 | -| Test.java:4:21:76:2 | { ... } | Test.java:11:7:11:11 | ... > ... | 16 | -| Test.java:4:21:76:2 | { ... } | Test.java:11:11:11:11 | 0 | 15 | -| Test.java:11:14:14:3 | { ... } | Test.java:11:14:14:3 | { ... } | 0 | -| Test.java:11:14:14:3 | { ... } | Test.java:12:4:12:9 | ...=... | 3 | -| Test.java:11:14:14:3 | { ... } | Test.java:12:4:12:10 | ; | 1 | -| Test.java:11:14:14:3 | { ... } | Test.java:12:8:12:9 | 20 | 2 | -| Test.java:11:14:14:3 | { ... } | Test.java:13:4:13:9 | ...=... | 6 | -| Test.java:11:14:14:3 | { ... } | Test.java:13:4:13:10 | ; | 4 | -| Test.java:11:14:14:3 | { ... } | Test.java:13:8:13:9 | 10 | 5 | -| Test.java:14:10:16:3 | { ... } | Test.java:14:10:16:3 | { ... } | 0 | -| Test.java:14:10:16:3 | { ... } | Test.java:15:4:15:9 | ...=... | 3 | -| Test.java:14:10:16:3 | { ... } | Test.java:15:4:15:10 | ; | 1 | -| Test.java:14:10:16:3 | { ... } | Test.java:15:8:15:9 | 30 | 2 | -| Test.java:18:3:18:8 | ; | Test.java:18:3:18:7 | ...=... | 2 | -| Test.java:18:3:18:8 | ; | Test.java:18:3:18:8 | ; | 0 | -| Test.java:18:3:18:8 | ; | Test.java:18:7:18:7 | 0 | 1 | -| Test.java:18:3:18:8 | ; | Test.java:21:3:21:11 | if (...) | 3 | -| Test.java:18:3:18:8 | ; | Test.java:21:6:21:6 | x | 4 | -| Test.java:18:3:18:8 | ; | Test.java:21:6:21:10 | ... < ... | 6 | -| Test.java:18:3:18:8 | ; | Test.java:21:10:21:10 | 0 | 5 | -| Test.java:22:4:22:10 | ; | Test.java:22:4:22:9 | ...=... | 2 | -| Test.java:22:4:22:10 | ; | Test.java:22:4:22:10 | ; | 0 | -| Test.java:22:4:22:10 | ; | Test.java:22:8:22:9 | 40 | 1 | -| Test.java:22:4:22:10 | ; | Test.java:27:3:27:8 | ...=... | 5 | -| Test.java:22:4:22:10 | ; | Test.java:27:3:27:9 | ; | 3 | -| Test.java:22:4:22:10 | ; | Test.java:27:7:27:8 | 10 | 4 | -| Test.java:22:4:22:10 | ; | Test.java:30:3:30:13 | if (...) | 6 | -| Test.java:22:4:22:10 | ; | Test.java:30:7:30:7 | x | 7 | -| Test.java:22:4:22:10 | ; | Test.java:30:7:30:12 | ... == ... | 9 | -| Test.java:22:4:22:10 | ; | Test.java:30:12:30:12 | 0 | 8 | -| Test.java:24:4:24:10 | return ... | Test.java:24:4:24:10 | return ... | 0 | -| Test.java:30:15:33:3 | { ... } | Test.java:30:15:33:3 | { ... } | 0 | -| Test.java:30:15:33:3 | { ... } | Test.java:31:4:31:9 | ...=... | 3 | -| Test.java:30:15:33:3 | { ... } | Test.java:31:4:31:10 | ; | 1 | -| Test.java:30:15:33:3 | { ... } | Test.java:31:8:31:9 | 60 | 2 | -| Test.java:30:15:33:3 | { ... } | Test.java:32:4:32:9 | ...=... | 6 | -| Test.java:30:15:33:3 | { ... } | Test.java:32:4:32:10 | ; | 4 | -| Test.java:30:15:33:3 | { ... } | Test.java:32:8:32:9 | 10 | 5 | -| Test.java:35:3:35:9 | ; | Test.java:35:3:35:8 | ...=... | 2 | -| Test.java:35:3:35:9 | ; | Test.java:35:3:35:9 | ; | 0 | -| Test.java:35:3:35:9 | ; | Test.java:35:7:35:8 | 20 | 1 | -| Test.java:35:3:35:9 | ; | Test.java:38:3:38:14 | while (...) | 3 | -| Test.java:38:9:38:9 | x | Test.java:38:9:38:9 | x | 0 | -| Test.java:38:9:38:9 | x | Test.java:38:9:38:13 | ... > ... | 2 | -| Test.java:38:9:38:9 | x | Test.java:38:13:38:13 | 0 | 1 | -| Test.java:38:16:41:3 | { ... } | Test.java:38:16:41:3 | { ... } | 0 | -| Test.java:38:16:41:3 | { ... } | Test.java:39:4:39:9 | ...=... | 3 | -| Test.java:38:16:41:3 | { ... } | Test.java:39:4:39:10 | ; | 1 | -| Test.java:38:16:41:3 | { ... } | Test.java:39:8:39:9 | 10 | 2 | -| Test.java:38:16:41:3 | { ... } | Test.java:40:4:40:4 | x | 5 | -| Test.java:38:16:41:3 | { ... } | Test.java:40:4:40:6 | ...-- | 6 | -| Test.java:38:16:41:3 | { ... } | Test.java:40:4:40:7 | ; | 4 | -| Test.java:43:3:43:9 | ; | Test.java:43:3:43:8 | ...=... | 2 | -| Test.java:43:3:43:9 | ; | Test.java:43:3:43:9 | ; | 0 | -| Test.java:43:3:43:9 | ; | Test.java:43:7:43:8 | 30 | 1 | -| Test.java:43:3:43:9 | ; | Test.java:46:3:46:29 | for (...;...;...) | 3 | -| Test.java:43:3:43:9 | ; | Test.java:46:11:46:15 | j | 5 | -| Test.java:43:3:43:9 | ; | Test.java:46:15:46:15 | 0 | 4 | -| Test.java:46:18:46:18 | j | Test.java:46:18:46:18 | j | 0 | -| Test.java:46:18:46:18 | j | Test.java:46:18:46:23 | ... < ... | 2 | -| Test.java:46:18:46:18 | j | Test.java:46:22:46:23 | 10 | 1 | -| Test.java:46:31:49:3 | { ... } | Test.java:46:26:46:26 | j | 7 | -| Test.java:46:31:49:3 | { ... } | Test.java:46:26:46:28 | ...++ | 8 | -| Test.java:46:31:49:3 | { ... } | Test.java:46:31:49:3 | { ... } | 0 | -| Test.java:46:31:49:3 | { ... } | Test.java:47:4:47:8 | ...=... | 3 | -| Test.java:46:31:49:3 | { ... } | Test.java:47:4:47:9 | ; | 1 | -| Test.java:46:31:49:3 | { ... } | Test.java:47:8:47:8 | 0 | 2 | -| Test.java:46:31:49:3 | { ... } | Test.java:48:4:48:9 | ...=... | 6 | -| Test.java:46:31:49:3 | { ... } | Test.java:48:4:48:10 | ; | 4 | -| Test.java:46:31:49:3 | { ... } | Test.java:48:8:48:9 | 10 | 5 | -| Test.java:51:3:51:9 | ; | Test.java:51:3:51:8 | ...=... | 2 | -| Test.java:51:3:51:9 | ; | Test.java:51:3:51:9 | ; | 0 | -| Test.java:51:3:51:9 | ; | Test.java:51:7:51:8 | 40 | 1 | -| Test.java:51:3:51:9 | ; | Test.java:54:3:54:29 | for (...;...;...) | 3 | -| Test.java:51:3:51:9 | ; | Test.java:54:11:54:15 | j | 5 | -| Test.java:51:3:51:9 | ; | Test.java:54:15:54:15 | 0 | 4 | -| Test.java:54:18:54:18 | j | Test.java:54:18:54:18 | j | 0 | -| Test.java:54:18:54:18 | j | Test.java:54:18:54:23 | ... < ... | 2 | -| Test.java:54:18:54:18 | j | Test.java:54:22:54:23 | 10 | 1 | -| Test.java:54:26:54:26 | j | Test.java:54:26:54:26 | j | 0 | -| Test.java:54:26:54:26 | j | Test.java:54:26:54:28 | ...++ | 1 | -| Test.java:54:31:68:3 | { ... } | Test.java:54:31:68:3 | { ... } | 0 | -| Test.java:54:31:68:3 | { ... } | Test.java:55:4:55:9 | ...=... | 3 | -| Test.java:54:31:68:3 | { ... } | Test.java:55:4:55:10 | ; | 1 | -| Test.java:54:31:68:3 | { ... } | Test.java:55:8:55:9 | 30 | 2 | -| Test.java:54:31:68:3 | { ... } | Test.java:56:4:56:12 | if (...) | 4 | -| Test.java:54:31:68:3 | { ... } | Test.java:56:7:56:7 | z | 5 | -| Test.java:54:31:68:3 | { ... } | Test.java:56:7:56:11 | ... > ... | 7 | -| Test.java:54:31:68:3 | { ... } | Test.java:56:11:56:11 | 0 | 6 | -| Test.java:57:5:57:13 | if (...) | Test.java:57:5:57:13 | if (...) | 0 | -| Test.java:57:5:57:13 | if (...) | Test.java:57:8:57:8 | y | 1 | -| Test.java:57:5:57:13 | if (...) | Test.java:57:8:57:12 | ... > ... | 3 | -| Test.java:57:5:57:13 | if (...) | Test.java:57:12:57:12 | 0 | 2 | -| Test.java:57:15:60:5 | { ... } | Test.java:57:15:60:5 | { ... } | 0 | -| Test.java:57:15:60:5 | { ... } | Test.java:58:6:58:10 | ...=... | 3 | -| Test.java:57:15:60:5 | { ... } | Test.java:58:6:58:11 | ; | 1 | -| Test.java:57:15:60:5 | { ... } | Test.java:58:10:58:10 | 0 | 2 | -| Test.java:57:15:60:5 | { ... } | Test.java:59:6:59:11 | break | 4 | -| Test.java:60:12:62:5 | { ... } | Test.java:60:12:62:5 | { ... } | 0 | -| Test.java:60:12:62:5 | { ... } | Test.java:61:6:61:11 | ...=... | 3 | -| Test.java:60:12:62:5 | { ... } | Test.java:61:6:61:12 | ; | 1 | -| Test.java:60:12:62:5 | { ... } | Test.java:61:10:61:11 | 20 | 2 | -| Test.java:60:12:62:5 | { ... } | Test.java:67:4:67:8 | ...=... | 6 | -| Test.java:60:12:62:5 | { ... } | Test.java:67:4:67:9 | ; | 4 | -| Test.java:60:12:62:5 | { ... } | Test.java:67:8:67:8 | 0 | 5 | -| Test.java:63:9:66:4 | { ... } | Test.java:63:9:66:4 | { ... } | 0 | -| Test.java:63:9:66:4 | { ... } | Test.java:64:5:64:10 | ...=... | 3 | -| Test.java:63:9:66:4 | { ... } | Test.java:64:5:64:11 | ; | 1 | -| Test.java:63:9:66:4 | { ... } | Test.java:64:9:64:10 | 10 | 2 | -| Test.java:63:9:66:4 | { ... } | Test.java:65:5:65:13 | continue | 4 | -| Test.java:70:3:70:9 | ; | Test.java:70:3:70:8 | ...=... | 2 | -| Test.java:70:3:70:9 | ; | Test.java:70:3:70:9 | ; | 0 | -| Test.java:70:3:70:9 | ; | Test.java:70:7:70:8 | 50 | 1 | -| Test.java:70:3:70:9 | ; | Test.java:74:3:74:8 | ...=... | 5 | -| Test.java:70:3:70:9 | ; | Test.java:74:3:74:9 | ; | 3 | -| Test.java:70:3:70:9 | ; | Test.java:74:7:74:8 | 40 | 4 | -| Test.java:70:3:70:9 | ; | Test.java:75:3:75:9 | return ... | 6 | +| Test.java:3:14:3:17 | { ... } | 0 | Test.java:3:14:3:17 | { ... } | +| Test.java:3:14:3:17 | { ... } | 1 | Test.java:3:14:3:17 | super(...) | +| Test.java:3:14:3:17 | { ... } | 2 | Test.java:3:14:3:17 | Test | +| Test.java:4:14:4:17 | test | 0 | Test.java:4:14:4:17 | test | +| Test.java:4:21:76:2 | { ... } | 0 | Test.java:4:21:76:2 | { ... } | +| Test.java:4:21:76:2 | { ... } | 1 | Test.java:5:3:5:12 | var ...; | +| Test.java:4:21:76:2 | { ... } | 2 | Test.java:5:11:5:11 | 0 | +| Test.java:4:21:76:2 | { ... } | 3 | Test.java:5:7:5:11 | x | +| Test.java:4:21:76:2 | { ... } | 4 | Test.java:6:3:6:14 | var ...; | +| Test.java:4:21:76:2 | { ... } | 5 | Test.java:6:12:6:13 | 50 | +| Test.java:4:21:76:2 | { ... } | 6 | Test.java:6:8:6:13 | y | +| Test.java:4:21:76:2 | { ... } | 7 | Test.java:7:3:7:12 | var ...; | +| Test.java:4:21:76:2 | { ... } | 8 | Test.java:7:11:7:11 | 0 | +| Test.java:4:21:76:2 | { ... } | 9 | Test.java:7:7:7:11 | z | +| Test.java:4:21:76:2 | { ... } | 10 | Test.java:8:3:8:12 | var ...; | +| Test.java:4:21:76:2 | { ... } | 11 | Test.java:8:11:8:11 | 0 | +| Test.java:4:21:76:2 | { ... } | 12 | Test.java:8:7:8:11 | w | +| Test.java:4:21:76:2 | { ... } | 13 | Test.java:11:3:11:12 | if (...) | +| Test.java:4:21:76:2 | { ... } | 14 | Test.java:11:7:11:7 | x | +| Test.java:4:21:76:2 | { ... } | 15 | Test.java:11:11:11:11 | 0 | +| Test.java:4:21:76:2 | { ... } | 16 | Test.java:11:7:11:11 | ... > ... | +| Test.java:11:14:14:3 | { ... } | 0 | Test.java:11:14:14:3 | { ... } | +| Test.java:11:14:14:3 | { ... } | 1 | Test.java:12:4:12:10 | ; | +| Test.java:11:14:14:3 | { ... } | 2 | Test.java:12:8:12:9 | 20 | +| Test.java:11:14:14:3 | { ... } | 3 | Test.java:12:4:12:9 | ...=... | +| Test.java:11:14:14:3 | { ... } | 4 | Test.java:13:4:13:10 | ; | +| Test.java:11:14:14:3 | { ... } | 5 | Test.java:13:8:13:9 | 10 | +| Test.java:11:14:14:3 | { ... } | 6 | Test.java:13:4:13:9 | ...=... | +| Test.java:14:10:16:3 | { ... } | 0 | Test.java:14:10:16:3 | { ... } | +| Test.java:14:10:16:3 | { ... } | 1 | Test.java:15:4:15:10 | ; | +| Test.java:14:10:16:3 | { ... } | 2 | Test.java:15:8:15:9 | 30 | +| Test.java:14:10:16:3 | { ... } | 3 | Test.java:15:4:15:9 | ...=... | +| Test.java:18:3:18:8 | ; | 0 | Test.java:18:3:18:8 | ; | +| Test.java:18:3:18:8 | ; | 1 | Test.java:18:7:18:7 | 0 | +| Test.java:18:3:18:8 | ; | 2 | Test.java:18:3:18:7 | ...=... | +| Test.java:18:3:18:8 | ; | 3 | Test.java:21:3:21:11 | if (...) | +| Test.java:18:3:18:8 | ; | 4 | Test.java:21:6:21:6 | x | +| Test.java:18:3:18:8 | ; | 5 | Test.java:21:10:21:10 | 0 | +| Test.java:18:3:18:8 | ; | 6 | Test.java:21:6:21:10 | ... < ... | +| Test.java:22:4:22:10 | ; | 0 | Test.java:22:4:22:10 | ; | +| Test.java:22:4:22:10 | ; | 1 | Test.java:22:8:22:9 | 40 | +| Test.java:22:4:22:10 | ; | 2 | Test.java:22:4:22:9 | ...=... | +| Test.java:22:4:22:10 | ; | 3 | Test.java:27:3:27:9 | ; | +| Test.java:22:4:22:10 | ; | 4 | Test.java:27:7:27:8 | 10 | +| Test.java:22:4:22:10 | ; | 5 | Test.java:27:3:27:8 | ...=... | +| Test.java:22:4:22:10 | ; | 6 | Test.java:30:3:30:13 | if (...) | +| Test.java:22:4:22:10 | ; | 7 | Test.java:30:7:30:7 | x | +| Test.java:22:4:22:10 | ; | 8 | Test.java:30:12:30:12 | 0 | +| Test.java:22:4:22:10 | ; | 9 | Test.java:30:7:30:12 | ... == ... | +| Test.java:24:4:24:10 | return ... | 0 | Test.java:24:4:24:10 | return ... | +| Test.java:30:15:33:3 | { ... } | 0 | Test.java:30:15:33:3 | { ... } | +| Test.java:30:15:33:3 | { ... } | 1 | Test.java:31:4:31:10 | ; | +| Test.java:30:15:33:3 | { ... } | 2 | Test.java:31:8:31:9 | 60 | +| Test.java:30:15:33:3 | { ... } | 3 | Test.java:31:4:31:9 | ...=... | +| Test.java:30:15:33:3 | { ... } | 4 | Test.java:32:4:32:10 | ; | +| Test.java:30:15:33:3 | { ... } | 5 | Test.java:32:8:32:9 | 10 | +| Test.java:30:15:33:3 | { ... } | 6 | Test.java:32:4:32:9 | ...=... | +| Test.java:35:3:35:9 | ; | 0 | Test.java:35:3:35:9 | ; | +| Test.java:35:3:35:9 | ; | 1 | Test.java:35:7:35:8 | 20 | +| Test.java:35:3:35:9 | ; | 2 | Test.java:35:3:35:8 | ...=... | +| Test.java:35:3:35:9 | ; | 3 | Test.java:38:3:38:14 | while (...) | +| Test.java:38:9:38:9 | x | 0 | Test.java:38:9:38:9 | x | +| Test.java:38:9:38:9 | x | 1 | Test.java:38:13:38:13 | 0 | +| Test.java:38:9:38:9 | x | 2 | Test.java:38:9:38:13 | ... > ... | +| Test.java:38:16:41:3 | { ... } | 0 | Test.java:38:16:41:3 | { ... } | +| Test.java:38:16:41:3 | { ... } | 1 | Test.java:39:4:39:10 | ; | +| Test.java:38:16:41:3 | { ... } | 2 | Test.java:39:8:39:9 | 10 | +| Test.java:38:16:41:3 | { ... } | 3 | Test.java:39:4:39:9 | ...=... | +| Test.java:38:16:41:3 | { ... } | 4 | Test.java:40:4:40:7 | ; | +| Test.java:38:16:41:3 | { ... } | 5 | Test.java:40:4:40:4 | x | +| Test.java:38:16:41:3 | { ... } | 6 | Test.java:40:4:40:6 | ...-- | +| Test.java:43:3:43:9 | ; | 0 | Test.java:43:3:43:9 | ; | +| Test.java:43:3:43:9 | ; | 1 | Test.java:43:7:43:8 | 30 | +| Test.java:43:3:43:9 | ; | 2 | Test.java:43:3:43:8 | ...=... | +| Test.java:43:3:43:9 | ; | 3 | Test.java:46:3:46:29 | for (...;...;...) | +| Test.java:43:3:43:9 | ; | 4 | Test.java:46:15:46:15 | 0 | +| Test.java:43:3:43:9 | ; | 5 | Test.java:46:11:46:15 | j | +| Test.java:46:18:46:18 | j | 0 | Test.java:46:18:46:18 | j | +| Test.java:46:18:46:18 | j | 1 | Test.java:46:22:46:23 | 10 | +| Test.java:46:18:46:18 | j | 2 | Test.java:46:18:46:23 | ... < ... | +| Test.java:46:31:49:3 | { ... } | 0 | Test.java:46:31:49:3 | { ... } | +| Test.java:46:31:49:3 | { ... } | 1 | Test.java:47:4:47:9 | ; | +| Test.java:46:31:49:3 | { ... } | 2 | Test.java:47:8:47:8 | 0 | +| Test.java:46:31:49:3 | { ... } | 3 | Test.java:47:4:47:8 | ...=... | +| Test.java:46:31:49:3 | { ... } | 4 | Test.java:48:4:48:10 | ; | +| Test.java:46:31:49:3 | { ... } | 5 | Test.java:48:8:48:9 | 10 | +| Test.java:46:31:49:3 | { ... } | 6 | Test.java:48:4:48:9 | ...=... | +| Test.java:46:31:49:3 | { ... } | 7 | Test.java:46:26:46:26 | j | +| Test.java:46:31:49:3 | { ... } | 8 | Test.java:46:26:46:28 | ...++ | +| Test.java:51:3:51:9 | ; | 0 | Test.java:51:3:51:9 | ; | +| Test.java:51:3:51:9 | ; | 1 | Test.java:51:7:51:8 | 40 | +| Test.java:51:3:51:9 | ; | 2 | Test.java:51:3:51:8 | ...=... | +| Test.java:51:3:51:9 | ; | 3 | Test.java:54:3:54:29 | for (...;...;...) | +| Test.java:51:3:51:9 | ; | 4 | Test.java:54:15:54:15 | 0 | +| Test.java:51:3:51:9 | ; | 5 | Test.java:54:11:54:15 | j | +| Test.java:54:18:54:18 | j | 0 | Test.java:54:18:54:18 | j | +| Test.java:54:18:54:18 | j | 1 | Test.java:54:22:54:23 | 10 | +| Test.java:54:18:54:18 | j | 2 | Test.java:54:18:54:23 | ... < ... | +| Test.java:54:26:54:26 | j | 0 | Test.java:54:26:54:26 | j | +| Test.java:54:26:54:26 | j | 1 | Test.java:54:26:54:28 | ...++ | +| Test.java:54:31:68:3 | { ... } | 0 | Test.java:54:31:68:3 | { ... } | +| Test.java:54:31:68:3 | { ... } | 1 | Test.java:55:4:55:10 | ; | +| Test.java:54:31:68:3 | { ... } | 2 | Test.java:55:8:55:9 | 30 | +| Test.java:54:31:68:3 | { ... } | 3 | Test.java:55:4:55:9 | ...=... | +| Test.java:54:31:68:3 | { ... } | 4 | Test.java:56:4:56:12 | if (...) | +| Test.java:54:31:68:3 | { ... } | 5 | Test.java:56:7:56:7 | z | +| Test.java:54:31:68:3 | { ... } | 6 | Test.java:56:11:56:11 | 0 | +| Test.java:54:31:68:3 | { ... } | 7 | Test.java:56:7:56:11 | ... > ... | +| Test.java:57:5:57:13 | if (...) | 0 | Test.java:57:5:57:13 | if (...) | +| Test.java:57:5:57:13 | if (...) | 1 | Test.java:57:8:57:8 | y | +| Test.java:57:5:57:13 | if (...) | 2 | Test.java:57:12:57:12 | 0 | +| Test.java:57:5:57:13 | if (...) | 3 | Test.java:57:8:57:12 | ... > ... | +| Test.java:57:15:60:5 | { ... } | 0 | Test.java:57:15:60:5 | { ... } | +| Test.java:57:15:60:5 | { ... } | 1 | Test.java:58:6:58:11 | ; | +| Test.java:57:15:60:5 | { ... } | 2 | Test.java:58:10:58:10 | 0 | +| Test.java:57:15:60:5 | { ... } | 3 | Test.java:58:6:58:10 | ...=... | +| Test.java:57:15:60:5 | { ... } | 4 | Test.java:59:6:59:11 | break | +| Test.java:60:12:62:5 | { ... } | 0 | Test.java:60:12:62:5 | { ... } | +| Test.java:60:12:62:5 | { ... } | 1 | Test.java:61:6:61:12 | ; | +| Test.java:60:12:62:5 | { ... } | 2 | Test.java:61:10:61:11 | 20 | +| Test.java:60:12:62:5 | { ... } | 3 | Test.java:61:6:61:11 | ...=... | +| Test.java:60:12:62:5 | { ... } | 4 | Test.java:67:4:67:9 | ; | +| Test.java:60:12:62:5 | { ... } | 5 | Test.java:67:8:67:8 | 0 | +| Test.java:60:12:62:5 | { ... } | 6 | Test.java:67:4:67:8 | ...=... | +| Test.java:63:9:66:4 | { ... } | 0 | Test.java:63:9:66:4 | { ... } | +| Test.java:63:9:66:4 | { ... } | 1 | Test.java:64:5:64:11 | ; | +| Test.java:63:9:66:4 | { ... } | 2 | Test.java:64:9:64:10 | 10 | +| Test.java:63:9:66:4 | { ... } | 3 | Test.java:64:5:64:10 | ...=... | +| Test.java:63:9:66:4 | { ... } | 4 | Test.java:65:5:65:13 | continue | +| Test.java:70:3:70:9 | ; | 0 | Test.java:70:3:70:9 | ; | +| Test.java:70:3:70:9 | ; | 1 | Test.java:70:7:70:8 | 50 | +| Test.java:70:3:70:9 | ; | 2 | Test.java:70:3:70:8 | ...=... | +| Test.java:70:3:70:9 | ; | 3 | Test.java:74:3:74:9 | ; | +| Test.java:70:3:70:9 | ; | 4 | Test.java:74:7:74:8 | 40 | +| Test.java:70:3:70:9 | ; | 5 | Test.java:74:3:74:8 | ...=... | +| Test.java:70:3:70:9 | ; | 6 | Test.java:75:3:75:9 | return ... | diff --git a/java/ql/test/library-tests/controlflow/basic/bbStmts.ql b/java/ql/test/library-tests/controlflow/basic/bbStmts.ql index 52c95e13650..89a32acf448 100644 --- a/java/ql/test/library-tests/controlflow/basic/bbStmts.ql +++ b/java/ql/test/library-tests/controlflow/basic/bbStmts.ql @@ -4,4 +4,4 @@ from BasicBlock b, ControlFlowNode n, int i where b.getNode(i) = n and b.getFile().(CompilationUnit).fromSource() -select b, n, i +select b, i, n diff --git a/java/ql/test/library-tests/dataflow/callback-dispatch/A.java b/java/ql/test/library-tests/dataflow/callback-dispatch/A.java index 4a4f0b11ded..680badfe604 100644 --- a/java/ql/test/library-tests/dataflow/callback-dispatch/A.java +++ b/java/ql/test/library-tests/dataflow/callback-dispatch/A.java @@ -1,5 +1,7 @@ package my.callback.qltest; +import java.util.*; + public class A { public interface Consumer1 { void eat(Object o); @@ -28,6 +30,20 @@ public class A { // con.eat(x); } + static T applyConsumer3_ret_postup(Consumer3 con) { + // summary: + // x = new T(); + // con.eat(x); + // return x; + return null; + } + + static void forEach(T[] xs, Consumer3 con) { + // summary: + // x = xs[..]; + // con.eat(x); + } + public interface Producer1 { T make(); } @@ -38,6 +54,14 @@ public class A { return null; } + static T produceConsume(Producer1 prod, Consumer3 con) { + // summary: + // x = prod.make(); + // con.eat(x); + // return x; + return null; + } + public interface Converter1 { T2 conv(T1 x); } @@ -109,5 +133,61 @@ public class A { }; applyConsumer3(new Integer[] { (Integer)source(12) }, pc); sink(applyProducer1(pc)[0]); // $ flow=11 + + Integer res = applyProducer1(new Producer1() { + private Integer ii = (Integer)source(13); + @Override public Integer make() { + return this.ii; + } + }); + sink(res); // $ flow=13 + + ArrayList list = new ArrayList<>(); + applyConsumer3(list, l -> l.add(source(14))); + sink(list.get(0)); // $ flow=14 + + Consumer3> tainter = l -> l.add(source(15)); + sink(applyConsumer3_ret_postup(tainter).get(0)); // $ flow=15 + + forEach(new Object[] {source(16)}, x -> sink(x)); // $ flow=16 + + // Spurious flow from 17 is reasonable as it would likely + // also occur if the lambda body was inlined in a for loop. + // It occurs from the combination of being able to observe + // the side-effect of the callback on the other argument and + // being able to chain summaries that update/read arguments, + // e.g. fluent apis. + // Spurious flow from 18 is due to not matching call targets + // in a return-from-call-to-enter-call flow sequence. + forEach(new Object[2][], xs -> { sink(xs[0]); xs[0] = source(17); }); // $ SPURIOUS: flow=17 flow=18 + + Object[][] xss = new Object[][] { { null } }; + forEach(xss, x -> {x[0] = source(18);}); + sink(xss[0][0]); // $ flow=18 + + Object res2 = produceConsume(() -> source(19), A::sink); // $ flow=19 + sink(res2); // $ flow=19 } + + static void applyConsumer1Field1Field2(A a1, A a2, Consumer1 con) { + // summary: + // con.eat(a1.field1); + // con.eat(a2.field2); + } + + static void wrapSinkToAvoidFieldSsa(A a) { sink(a.field1); } // $ flow=20 + + void foo3() { + A a1 = new A(); + a1.field1 = source(20); + A a2 = new A(); + applyConsumer1Field1Field2(a1, a2, p -> { + sink(p); // MISSING FLOW + }); + wrapSinkToAvoidFieldSsa(a1); + sink(a2.field2); + } + + public Object field1; + public Object field2; } diff --git a/java/ql/test/library-tests/dataflow/callback-dispatch/test.ql b/java/ql/test/library-tests/dataflow/callback-dispatch/test.ql index 934bdcfac28..ba9512cf38d 100644 --- a/java/ql/test/library-tests/dataflow/callback-dispatch/test.ql +++ b/java/ql/test/library-tests/dataflow/callback-dispatch/test.ql @@ -8,9 +8,15 @@ class SummaryModelTest extends SummaryModelCsv { row = [ "my.callback.qltest;A;false;applyConsumer1;(Object,Consumer1);;Argument[0];Parameter[0] of Argument[1];value", + "my.callback.qltest;A;false;applyConsumer1Field1Field2;(A,A,Consumer1);;Field[my.callback.qltest.A.field1] of Argument[0];Parameter[0] of Argument[2];value", + "my.callback.qltest;A;false;applyConsumer1Field1Field2;(A,A,Consumer1);;Field[my.callback.qltest.A.field2] of Argument[1];Parameter[0] of Argument[2];value", "my.callback.qltest;A;false;applyConsumer2;(Object,Consumer2);;Argument[0];Parameter[0] of Argument[1];value", "my.callback.qltest;A;false;applyConsumer3;(Object,Consumer3);;Argument[0];Parameter[0] of Argument[1];value", + "my.callback.qltest;A;false;applyConsumer3_ret_postup;(Consumer3);;Parameter[0] of Argument[0];ReturnValue;value", + "my.callback.qltest;A;false;forEach;(Object[],Consumer3);;ArrayElement of Argument[0];Parameter[0] of Argument[1];value", "my.callback.qltest;A;false;applyProducer1;(Producer1);;ReturnValue of Argument[0];ReturnValue;value", + "my.callback.qltest;A;false;produceConsume;(Producer1,Consumer3);;ReturnValue of Argument[0];Parameter[0] of Argument[1];value", + "my.callback.qltest;A;false;produceConsume;(Producer1,Consumer3);;Parameter[0] of Argument[1];ReturnValue;value", "my.callback.qltest;A;false;applyConverter1;(Object,Converter1);;Argument[0];Parameter[0] of Argument[1];value", "my.callback.qltest;A;false;applyConverter1;(Object,Converter1);;ReturnValue of Argument[1];ReturnValue;value" ] diff --git a/java/ql/test/library-tests/dataflow/collections/Test.java b/java/ql/test/library-tests/dataflow/collections/Test.java index 8557c13b1ec..216f373ca8c 100644 --- a/java/ql/test/library-tests/dataflow/collections/Test.java +++ b/java/ql/test/library-tests/dataflow/collections/Test.java @@ -25,5 +25,57 @@ public class Test { Iterator it = m.values().iterator(); String x5 = it.next(); sink(x5); // Flow + + it.forEachRemaining(x6 -> { + sink(x6); // Flow + }); + + m.forEach((x7_k, x8_v) -> { + sink(x7_k); // No flow + sink(x8_v); // Flow + }); + + m.entrySet().forEach(entry -> { + String x9 = entry.getKey(); + String x10 = entry.getValue(); + sink(x9); // No flow + sink(x10); // Flow + }); + } + + public void run2() { + HashMap m = new HashMap<>(); + + m.put(tainted, tainted); + + m.forEach((x11_k, x12_v) -> { + sink(x11_k); // Flow + sink(x12_v); // Flow + }); + + m.entrySet().forEach(entry -> { + String x13 = entry.getKey(); + String x14 = entry.getValue(); + sink(x13); // Flow + sink(x14); // Flow + }); + } + + public void run3() { + Set s = new HashSet<>(); + String x15 = s.iterator().next(); + sink(x15); // No flow + + s.forEach(x16 -> { + sink(x16); // No flow + }); + + s.add(tainted); + String x17 = s.iterator().next(); + sink(x17); // Flow + + s.forEach(x18 -> { + sink(x18); // Flow + }); } } diff --git a/java/ql/test/library-tests/dataflow/collections/flow.expected b/java/ql/test/library-tests/dataflow/collections/flow.expected index e2ccd7702b3..1a4bed0a6e7 100644 --- a/java/ql/test/library-tests/dataflow/collections/flow.expected +++ b/java/ql/test/library-tests/dataflow/collections/flow.expected @@ -2,3 +2,12 @@ | Test.java:13:18:13:24 | tainted | Test.java:18:10:18:11 | x3 | | Test.java:13:18:13:24 | tainted | Test.java:22:12:22:13 | x4 | | Test.java:13:18:13:24 | tainted | Test.java:27:10:27:11 | x5 | +| Test.java:13:18:13:24 | tainted | Test.java:30:12:30:13 | x6 | +| Test.java:13:18:13:24 | tainted | Test.java:35:12:35:15 | x8_v | +| Test.java:13:18:13:24 | tainted | Test.java:42:12:42:14 | x10 | +| Test.java:49:11:49:17 | tainted | Test.java:52:12:52:16 | x11_k | +| Test.java:49:11:49:17 | tainted | Test.java:59:12:59:14 | x13 | +| Test.java:49:20:49:26 | tainted | Test.java:53:12:53:16 | x12_v | +| Test.java:49:20:49:26 | tainted | Test.java:60:12:60:14 | x14 | +| Test.java:73:11:73:17 | tainted | Test.java:75:10:75:12 | x17 | +| Test.java:73:11:73:17 | tainted | Test.java:78:12:78:14 | x18 | diff --git a/java/ql/test/library-tests/dataflow/taintsources/A.java b/java/ql/test/library-tests/dataflow/taintsources/A.java index a2c30176c60..25320b315ad 100644 --- a/java/ql/test/library-tests/dataflow/taintsources/A.java +++ b/java/ql/test/library-tests/dataflow/taintsources/A.java @@ -14,36 +14,39 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class A { + + private static void sink(Object o) {} + public static void main(String[] args) { - String[] a = args; // user input - String s = args[0]; // user input + sink(args); // $hasLocalValueFlow + sink(args[0]); // $hasLocalTaintFlow } public static void userInput() throws SQLException, IOException, MalformedURLException { - System.getenv("test"); // user input + sink(System.getenv("test")); // $hasLocalValueFlow class TestServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { - req.getParameter("test"); // remote user input - req.getHeader("test"); // remote user input - req.getQueryString(); // remote user input - req.getCookies()[0].getValue(); // remote user input + throws ServletException, IOException { + sink(req.getParameter("test")); // $hasRemoteValueFlow + sink(req.getHeader("test")); // $hasRemoteValueFlow + sink(req.getQueryString()); // $hasRemoteValueFlow + sink(req.getCookies()[0].getValue()); // $hasRemoteValueFlow } } - new Properties().getProperty("test"); // user input - System.getProperty("test"); // user input + sink(new Properties().getProperty("test")); // $hasLocalValueFlow + sink(System.getProperty("test")); // $hasLocalValueFlow new Object() { public void test(ResultSet rs) throws SQLException { - rs.getString(0); // user input + sink(rs.getString(0)); // $hasLocalValueFlow } }; - new URL("test").openConnection().getInputStream(); // remote user input - new Socket("test", 1234).getInputStream(); // remote user input - InetAddress.getByName("test").getHostName(); // remote user input + sink(new URL("test").openConnection().getInputStream()); // $hasRemoteValueFlow + sink(new Socket("test", 1234).getInputStream()); // $hasRemoteValueFlow + sink(InetAddress.getByName("test").getHostName()); // $hasRemoteValueFlow - System.in.read(); // user input - new FileInputStream("test").read(); // user input + sink(System.in); // $hasLocalValueFlow + sink(new FileInputStream("test")); // $hasLocalValueFlow } } diff --git a/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java b/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java index 6f0051f0d10..17cc6b6a199 100644 --- a/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java +++ b/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java @@ -4,34 +4,39 @@ import android.app.Activity; public class IntentSources extends Activity { + private static void sink(Object o) {} + public void test() throws java.io.IOException { String trouble = this.getIntent().getStringExtra("key"); - Runtime.getRuntime().exec(trouble); + sink(trouble); // $hasRemoteTaintFlow } public void test2() throws java.io.IOException { String trouble = getIntent().getStringExtra("key"); - Runtime.getRuntime().exec(trouble); + sink(trouble); // $hasRemoteTaintFlow } public void test3() throws java.io.IOException { String trouble = getIntent().getExtras().getString("key"); - Runtime.getRuntime().exec(trouble); + sink(trouble); // $hasRemoteTaintFlow } } + class OtherClass { + private static void sink(Object o) {} + public void test(IntentSources is) throws java.io.IOException { String trouble = is.getIntent().getStringExtra("key"); - Runtime.getRuntime().exec(trouble); + sink(trouble); // $hasRemoteTaintFlow } } diff --git a/java/ql/test/library-tests/dataflow/taintsources/RmiFlowImpl.java b/java/ql/test/library-tests/dataflow/taintsources/RmiFlowImpl.java index f75c6ddf2b7..9e814bf7201 100644 --- a/java/ql/test/library-tests/dataflow/taintsources/RmiFlowImpl.java +++ b/java/ql/test/library-tests/dataflow/taintsources/RmiFlowImpl.java @@ -1,15 +1,18 @@ package security.library.dataflow; public class RmiFlowImpl implements RmiFlow { + + private static void sink(Object o) {} + public String listDirectory(String path) throws java.io.IOException { String command = "ls " + path; - Runtime.getRuntime().exec(command); + sink(command); // $hasRemoteTaintFlow return "pretend there are some results here"; } public String notRemotable(String path) throws java.io.IOException { String command = "ls " + path; - Runtime.getRuntime().exec(command); + sink(command); // Safe return "pretend there are some results here"; } } diff --git a/java/ql/test/library-tests/dataflow/taintsources/SpringMultiPart.java b/java/ql/test/library-tests/dataflow/taintsources/SpringMultiPart.java index 6702059ab70..33eeced2a4e 100644 --- a/java/ql/test/library-tests/dataflow/taintsources/SpringMultiPart.java +++ b/java/ql/test/library-tests/dataflow/taintsources/SpringMultiPart.java @@ -4,22 +4,24 @@ import org.springframework.web.multipart.MultipartRequest; public class SpringMultiPart { MultipartFile file; + private static void sink(Object o) {} + public void test() throws Exception { - file.getBytes(); - file.isEmpty(); - file.getInputStream(); - file.getResource(); - file.getName(); - file.getContentType(); - file.getOriginalFilename(); + sink(file.getBytes()); // $hasRemoteValueFlow + sink(file.isEmpty()); // Safe + sink(file.getInputStream()); // $hasRemoteValueFlow + sink(file.getResource()); // $hasRemoteValueFlow + sink(file.getName()); // $hasRemoteValueFlow + sink(file.getContentType()); // $hasRemoteValueFlow + sink(file.getOriginalFilename()); // $hasRemoteValueFlow } - + public void test(MultipartRequest request) { - request.getFile("name"); - request.getFileMap(); - request.getFileNames(); - request.getFiles("name"); - request.getMultiFileMap(); - request.getMultipartContentType("name"); + sink(request.getFile("name"));// $hasRemoteValueFlow + sink(request.getFileMap());// $hasRemoteValueFlow + sink(request.getFileNames());// $hasRemoteValueFlow + sink(request.getFiles("name"));// $hasRemoteValueFlow + sink(request.getMultiFileMap());// $hasRemoteValueFlow + sink(request.getMultipartContentType("name")); // $hasRemoteValueFlow } } diff --git a/java/ql/test/library-tests/dataflow/taintsources/SpringSavedRequest.java b/java/ql/test/library-tests/dataflow/taintsources/SpringSavedRequest.java index a4ee8c018a2..e61e0cbb827 100644 --- a/java/ql/test/library-tests/dataflow/taintsources/SpringSavedRequest.java +++ b/java/ql/test/library-tests/dataflow/taintsources/SpringSavedRequest.java @@ -4,23 +4,25 @@ import org.springframework.security.web.savedrequest.SimpleSavedRequest; public class SpringSavedRequest { SavedRequest sr; + private static void sink(Object o) {} + public void test() { - sr.getRedirectUrl(); - sr.getCookies(); - sr.getHeaderValues("name"); - sr.getHeaderNames(); - sr.getParameterValues("name"); - sr.getParameterMap(); + sink(sr.getRedirectUrl()); // $hasRemoteValueFlow + sink(sr.getCookies()); // $hasRemoteValueFlow + sink(sr.getHeaderValues("name")); // $hasRemoteValueFlow + sink(sr.getHeaderNames()); // $hasRemoteValueFlow + sink(sr.getParameterValues("name")); // $hasRemoteValueFlow + sink(sr.getParameterMap()); // $hasRemoteValueFlow } SimpleSavedRequest ssr; public void test2() { - ssr.getRedirectUrl(); - ssr.getCookies(); - ssr.getHeaderValues("name"); - ssr.getHeaderNames(); - ssr.getParameterValues("name"); - ssr.getParameterMap(); + sink(ssr.getRedirectUrl()); // $hasRemoteValueFlow + sink(ssr.getCookies()); // $hasRemoteValueFlow + sink(ssr.getHeaderValues("name")); // $hasRemoteValueFlow + sink(ssr.getHeaderNames()); // $hasRemoteValueFlow + sink(ssr.getParameterValues("name")); // $hasRemoteValueFlow + sink(ssr.getParameterMap()); // $hasRemoteValueFlow } } diff --git a/java/ql/test/library-tests/dataflow/taintsources/local.expected b/java/ql/test/library-tests/dataflow/taintsources/local.expected index 19e7359357a..e69de29bb2d 100644 --- a/java/ql/test/library-tests/dataflow/taintsources/local.expected +++ b/java/ql/test/library-tests/dataflow/taintsources/local.expected @@ -1,10 +0,0 @@ -| A.java:17:27:17:39 | args | A.java:17:27:17:39 | args | -| A.java:17:27:17:39 | args | A.java:18:18:18:21 | args | -| A.java:17:27:17:39 | args | A.java:19:16:19:19 | args | -| A.java:17:27:17:39 | args | A.java:19:16:19:22 | ...[...] | -| A.java:23:5:23:25 | getenv(...) | A.java:23:5:23:25 | getenv(...) | -| A.java:34:5:34:40 | getProperty(...) | A.java:34:5:34:40 | getProperty(...) | -| A.java:35:5:35:30 | getProperty(...) | A.java:35:5:35:30 | getProperty(...) | -| A.java:38:9:38:23 | getString(...) | A.java:38:9:38:23 | getString(...) | -| A.java:45:5:45:13 | System.in | A.java:45:5:45:13 | System.in | -| A.java:46:5:46:31 | new FileInputStream(...) | A.java:46:5:46:31 | new FileInputStream(...) | diff --git a/java/ql/test/library-tests/dataflow/taintsources/local.ql b/java/ql/test/library-tests/dataflow/taintsources/local.ql index 382b0f706d6..61faff7a992 100644 --- a/java/ql/test/library-tests/dataflow/taintsources/local.ql +++ b/java/ql/test/library-tests/dataflow/taintsources/local.ql @@ -1,18 +1,54 @@ import java -import semmle.code.java.dataflow.TaintTracking import semmle.code.java.dataflow.FlowSources +import TestUtilities.InlineExpectationsTest -class Conf extends TaintTracking::Configuration { - Conf() { this = "remote taint conf" } - - override predicate isSource(DataFlow::Node n) { - n instanceof UserInput and - not n instanceof RemoteFlowSource +class LocalSource extends DataFlow::Node { + LocalSource() { + this instanceof UserInput and + not this instanceof RemoteFlowSource } - - override predicate isSink(DataFlow::Node n) { any() } } -from DataFlow::Node src, DataFlow::Node sink, Conf conf -where conf.hasFlow(src, sink) -select src, sink +predicate isTestSink(DataFlow::Node n) { + exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument()) +} + +class LocalValueConf extends DataFlow::Configuration { + LocalValueConf() { this = "LocalValueConf" } + + override predicate isSource(DataFlow::Node n) { n instanceof LocalSource } + + override predicate isSink(DataFlow::Node n) { isTestSink(n) } +} + +class LocalTaintConf extends TaintTracking::Configuration { + LocalTaintConf() { this = "LocalTaintConf" } + + override predicate isSource(DataFlow::Node n) { n instanceof LocalSource } + + override predicate isSink(DataFlow::Node n) { isTestSink(n) } +} + +class LocalFlowTest extends InlineExpectationsTest { + LocalFlowTest() { this = "LocalFlowTest" } + + override string getARelevantTag() { result = ["hasLocalValueFlow", "hasLocalTaintFlow"] } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "hasLocalValueFlow" and + exists(DataFlow::Node src, DataFlow::Node sink | any(LocalValueConf c).hasFlow(src, sink) | + sink.getLocation() = location and + element = sink.toString() and + value = "" + ) + or + tag = "hasLocalTaintFlow" and + exists(DataFlow::Node src, DataFlow::Node sink | + any(LocalTaintConf c).hasFlow(src, sink) and not any(LocalValueConf c).hasFlow(src, sink) + | + sink.getLocation() = location and + element = sink.toString() and + value = "" + ) + } +} diff --git a/java/ql/test/library-tests/dataflow/taintsources/remote.expected b/java/ql/test/library-tests/dataflow/taintsources/remote.expected index f3b5685d4b9..e69de29bb2d 100644 --- a/java/ql/test/library-tests/dataflow/taintsources/remote.expected +++ b/java/ql/test/library-tests/dataflow/taintsources/remote.expected @@ -1,61 +0,0 @@ -| A.java:28:9:28:32 | getParameter(...) | A.java:28:9:28:32 | getParameter(...) | -| A.java:29:9:29:29 | getHeader(...) | A.java:29:9:29:29 | getHeader(...) | -| A.java:30:9:30:28 | getQueryString(...) | A.java:30:9:30:28 | getQueryString(...) | -| A.java:31:9:31:38 | getValue(...) | A.java:31:9:31:38 | getValue(...) | -| A.java:41:5:41:53 | getInputStream(...) | A.java:41:5:41:53 | getInputStream(...) | -| A.java:42:5:42:45 | getInputStream(...) | A.java:42:5:42:45 | getInputStream(...) | -| A.java:43:5:43:47 | getHostName(...) | A.java:43:5:43:47 | getHostName(...) | -| IntentSources.java:9:20:9:35 | getIntent(...) | ../../../stubs/google-android-9.0.0/android/content/Intent.java:1059:19:1059:32 | parameter this | -| IntentSources.java:9:20:9:35 | getIntent(...) | IntentSources.java:9:20:9:35 | getIntent(...) | -| IntentSources.java:9:20:9:35 | getIntent(...) | IntentSources.java:9:20:9:57 | getStringExtra(...) | -| IntentSources.java:9:20:9:35 | getIntent(...) | IntentSources.java:10:29:10:35 | trouble | -| IntentSources.java:16:20:16:30 | getIntent(...) | ../../../stubs/google-android-9.0.0/android/content/Intent.java:1059:19:1059:32 | parameter this | -| IntentSources.java:16:20:16:30 | getIntent(...) | IntentSources.java:16:20:16:30 | getIntent(...) | -| IntentSources.java:16:20:16:30 | getIntent(...) | IntentSources.java:16:20:16:52 | getStringExtra(...) | -| IntentSources.java:16:20:16:30 | getIntent(...) | IntentSources.java:17:29:17:35 | trouble | -| IntentSources.java:23:20:23:30 | getIntent(...) | ../../../stubs/google-android-9.0.0/android/content/Intent.java:1358:19:1358:27 | parameter this | -| IntentSources.java:23:20:23:30 | getIntent(...) | ../../../stubs/google-android-9.0.0/android/os/BaseBundle.java:600:19:600:27 | parameter this | -| IntentSources.java:23:20:23:30 | getIntent(...) | IntentSources.java:23:20:23:30 | getIntent(...) | -| IntentSources.java:23:20:23:30 | getIntent(...) | IntentSources.java:23:20:23:42 | getExtras(...) | -| IntentSources.java:23:20:23:30 | getIntent(...) | IntentSources.java:23:20:23:59 | getString(...) | -| IntentSources.java:23:20:23:30 | getIntent(...) | IntentSources.java:24:29:24:35 | trouble | -| IntentSources.java:33:20:33:33 | getIntent(...) | ../../../stubs/google-android-9.0.0/android/content/Intent.java:1059:19:1059:32 | parameter this | -| IntentSources.java:33:20:33:33 | getIntent(...) | IntentSources.java:33:20:33:33 | getIntent(...) | -| IntentSources.java:33:20:33:33 | getIntent(...) | IntentSources.java:33:20:33:55 | getStringExtra(...) | -| IntentSources.java:33:20:33:33 | getIntent(...) | IntentSources.java:34:29:34:35 | trouble | -| PlayResource.java:19:37:19:46 | uri | PlayResource.java:19:37:19:46 | uri | -| PlayResource.java:20:18:20:48 | getQueryString(...) | ../../../stubs/playframework-2.6.x/play/mvc/Results.java:634:33:634:42 | url | -| PlayResource.java:20:18:20:48 | getQueryString(...) | PlayResource.java:20:18:20:48 | getQueryString(...) | -| PlayResource.java:20:18:20:48 | getQueryString(...) | PlayResource.java:21:21:21:23 | url | -| PlayResource.java:24:42:24:53 | token | ../../../stubs/playframework-2.6.x/play/mvc/Results.java:94:27:94:40 | content | -| PlayResource.java:24:42:24:53 | token | PlayResource.java:24:42:24:53 | token | -| PlayResource.java:24:42:24:53 | token | PlayResource.java:25:30:25:34 | token | -| PlayResource.java:28:56:28:65 | uri | PlayResource.java:28:56:28:65 | uri | -| RmiFlowImpl.java:4:30:4:40 | path | RmiFlowImpl.java:4:30:4:40 | path | -| RmiFlowImpl.java:4:30:4:40 | path | RmiFlowImpl.java:5:20:5:31 | ... + ... | -| RmiFlowImpl.java:4:30:4:40 | path | RmiFlowImpl.java:5:28:5:31 | path | -| RmiFlowImpl.java:4:30:4:40 | path | RmiFlowImpl.java:6:29:6:35 | command | -| SpringMultiPart.java:8:3:8:17 | getBytes(...) | SpringMultiPart.java:8:3:8:17 | getBytes(...) | -| SpringMultiPart.java:10:3:10:23 | getInputStream(...) | SpringMultiPart.java:10:3:10:23 | getInputStream(...) | -| SpringMultiPart.java:11:3:11:20 | getResource(...) | SpringMultiPart.java:11:3:11:20 | getResource(...) | -| SpringMultiPart.java:12:3:12:16 | getName(...) | SpringMultiPart.java:12:3:12:16 | getName(...) | -| SpringMultiPart.java:13:3:13:23 | getContentType(...) | SpringMultiPart.java:13:3:13:23 | getContentType(...) | -| SpringMultiPart.java:14:3:14:28 | getOriginalFilename(...) | SpringMultiPart.java:14:3:14:28 | getOriginalFilename(...) | -| SpringMultiPart.java:18:3:18:25 | getFile(...) | SpringMultiPart.java:18:3:18:25 | getFile(...) | -| SpringMultiPart.java:19:3:19:22 | getFileMap(...) | SpringMultiPart.java:19:3:19:22 | getFileMap(...) | -| SpringMultiPart.java:20:3:20:24 | getFileNames(...) | SpringMultiPart.java:20:3:20:24 | getFileNames(...) | -| SpringMultiPart.java:21:3:21:26 | getFiles(...) | SpringMultiPart.java:21:3:21:26 | getFiles(...) | -| SpringMultiPart.java:22:3:22:27 | getMultiFileMap(...) | SpringMultiPart.java:22:3:22:27 | getMultiFileMap(...) | -| SpringMultiPart.java:23:3:23:41 | getMultipartContentType(...) | SpringMultiPart.java:23:3:23:41 | getMultipartContentType(...) | -| SpringSavedRequest.java:8:3:8:21 | getRedirectUrl(...) | SpringSavedRequest.java:8:3:8:21 | getRedirectUrl(...) | -| SpringSavedRequest.java:9:3:9:17 | getCookies(...) | SpringSavedRequest.java:9:3:9:17 | getCookies(...) | -| SpringSavedRequest.java:10:3:10:28 | getHeaderValues(...) | SpringSavedRequest.java:10:3:10:28 | getHeaderValues(...) | -| SpringSavedRequest.java:11:3:11:21 | getHeaderNames(...) | SpringSavedRequest.java:11:3:11:21 | getHeaderNames(...) | -| SpringSavedRequest.java:12:3:12:31 | getParameterValues(...) | SpringSavedRequest.java:12:3:12:31 | getParameterValues(...) | -| SpringSavedRequest.java:13:3:13:22 | getParameterMap(...) | SpringSavedRequest.java:13:3:13:22 | getParameterMap(...) | -| SpringSavedRequest.java:19:3:19:22 | getRedirectUrl(...) | SpringSavedRequest.java:19:3:19:22 | getRedirectUrl(...) | -| SpringSavedRequest.java:20:3:20:18 | getCookies(...) | SpringSavedRequest.java:20:3:20:18 | getCookies(...) | -| SpringSavedRequest.java:21:3:21:29 | getHeaderValues(...) | SpringSavedRequest.java:21:3:21:29 | getHeaderValues(...) | -| SpringSavedRequest.java:22:3:22:22 | getHeaderNames(...) | SpringSavedRequest.java:22:3:22:22 | getHeaderNames(...) | -| SpringSavedRequest.java:23:3:23:32 | getParameterValues(...) | SpringSavedRequest.java:23:3:23:32 | getParameterValues(...) | -| SpringSavedRequest.java:24:3:24:23 | getParameterMap(...) | SpringSavedRequest.java:24:3:24:23 | getParameterMap(...) | diff --git a/java/ql/test/library-tests/dataflow/taintsources/remote.ql b/java/ql/test/library-tests/dataflow/taintsources/remote.ql index 9466939ab1b..df5ba85227e 100644 --- a/java/ql/test/library-tests/dataflow/taintsources/remote.ql +++ b/java/ql/test/library-tests/dataflow/taintsources/remote.ql @@ -1,15 +1,47 @@ import java -import semmle.code.java.dataflow.TaintTracking import semmle.code.java.dataflow.FlowSources +import TestUtilities.InlineExpectationsTest -class Conf extends TaintTracking::Configuration { - Conf() { this = "remote taint conf" } +predicate isTestSink(DataFlow::Node n) { + exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument()) +} + +class RemoteValueConf extends DataFlow::Configuration { + RemoteValueConf() { this = "RemoteValueConf" } override predicate isSource(DataFlow::Node n) { n instanceof RemoteFlowSource } - override predicate isSink(DataFlow::Node n) { any() } + override predicate isSink(DataFlow::Node n) { isTestSink(n) } } -from DataFlow::Node src, DataFlow::Node sink, Conf conf -where conf.hasFlow(src, sink) -select src, sink +class RemoteTaintConf extends TaintTracking::Configuration { + RemoteTaintConf() { this = "RemoteTaintConf" } + + override predicate isSource(DataFlow::Node n) { n instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node n) { isTestSink(n) } +} + +class RemoteFlowTest extends InlineExpectationsTest { + RemoteFlowTest() { this = "RemoteFlowTest" } + + override string getARelevantTag() { result = ["hasRemoteValueFlow", "hasRemoteTaintFlow"] } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "hasRemoteValueFlow" and + exists(DataFlow::Node src, DataFlow::Node sink | any(RemoteValueConf c).hasFlow(src, sink) | + sink.getLocation() = location and + element = sink.toString() and + value = "" + ) + or + tag = "hasRemoteTaintFlow" and + exists(DataFlow::Node src, DataFlow::Node sink | + any(RemoteTaintConf c).hasFlow(src, sink) and not any(RemoteValueConf c).hasFlow(src, sink) + | + sink.getLocation() = location and + element = sink.toString() and + value = "" + ) + } +} diff --git a/java/ql/test/library-tests/frameworks/android/flow-steps/Test.java b/java/ql/test/library-tests/frameworks/android/flow-steps/Test.java new file mode 100644 index 00000000000..7ac9f2a8490 --- /dev/null +++ b/java/ql/test/library-tests/frameworks/android/flow-steps/Test.java @@ -0,0 +1,327 @@ +package generatedtest; + +import android.content.Intent; +import android.os.BaseBundle; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Parcel; +import android.os.ParcelFileDescriptor; +import android.os.Parcelable; +import android.os.PersistableBundle; +import android.util.Size; +import android.util.SizeF; +import android.util.SparseArray; +import android.util.SparseBooleanArray; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +// Test case generated by GenerateFlowTestCase.ql +public class Test { + + Object source() { return null; } + void sink(Object o) { } + + public void test() throws Exception { + + { + // "android.os;Parcel;false;readArray;;;Argument[-1];ReturnValue;taint" + Object[] out = null; + Parcel in = (Parcel)source(); + out = in.readArray(null); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readArrayList;;;Argument[-1];ReturnValue;taint" + ArrayList out = null; + Parcel in = (Parcel)source(); + out = in.readArrayList(null); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readBinderArray;;;Argument[-1];Argument[0];taint" + IBinder[] out = null; + Parcel in = (Parcel)source(); + in.readBinderArray(out); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readBinderList;;;Argument[-1];Argument[0];taint" + List out = null; + Parcel in = (Parcel)source(); + in.readBinderList(out); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readBoolean;;;Argument[-1];ReturnValue;taint" + boolean out = false; + Parcel in = (Parcel)source(); + out = in.readBoolean(); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readBooleanArray;;;Argument[-1];Argument[0];taint" + boolean[] out = null; + Parcel in = (Parcel)source(); + in.readBooleanArray(out); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readBundle;;;Argument[-1];ReturnValue;taint" + Bundle out = null; + Parcel in = (Parcel)source(); + out = in.readBundle(null); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readBundle;;;Argument[-1];ReturnValue;taint" + Bundle out = null; + Parcel in = (Parcel)source(); + out = in.readBundle(); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readByte;;;Argument[-1];ReturnValue;taint" + byte out = 0; + Parcel in = (Parcel)source(); + out = in.readByte(); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readByteArray;;;Argument[-1];Argument[0];taint" + byte[] out = null; + Parcel in = (Parcel)source(); + in.readByteArray(out); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readCharArray;;;Argument[-1];Argument[0];taint" + char[] out = null; + Parcel in = (Parcel)source(); + in.readCharArray(out); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readDouble;;;Argument[-1];ReturnValue;taint" + double out = 0.0; + Parcel in = (Parcel)source(); + out = in.readDouble(); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readDoubleArray;;;Argument[-1];Argument[0];taint" + double[] out = null; + Parcel in = (Parcel)source(); + in.readDoubleArray(out); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readFileDescriptor;;;Argument[-1];ReturnValue;taint" + ParcelFileDescriptor out = null; + Parcel in = (Parcel)source(); + out = in.readFileDescriptor(); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readFloat;;;Argument[-1];ReturnValue;taint" + float out = 0.0f; + Parcel in = (Parcel)source(); + out = in.readFloat(); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readFloatArray;;;Argument[-1];Argument[0];taint" + float[] out = null; + Parcel in = (Parcel)source(); + in.readFloatArray(out); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readHashMap;;;Argument[-1];ReturnValue;taint" + HashMap out = null; + Parcel in = (Parcel)source(); + out = in.readHashMap(null); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readInt;;;Argument[-1];ReturnValue;taint" + int out = 0; + Parcel in = (Parcel)source(); + out = in.readInt(); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readIntArray;;;Argument[-1];Argument[0];taint" + int[] out = null; + Parcel in = (Parcel)source(); + in.readIntArray(out); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readList;;;Argument[-1];Argument[0];taint" + List out = null; + Parcel in = (Parcel)source(); + in.readList(out, null); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readLong;;;Argument[-1];ReturnValue;taint" + long out = 0L; + Parcel in = (Parcel)source(); + out = in.readLong(); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readLongArray;;;Argument[-1];Argument[0];taint" + long[] out = null; + Parcel in = (Parcel)source(); + in.readLongArray(out); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readMap;;;Argument[-1];Argument[0];taint" + Map out = null; + Parcel in = (Parcel)source(); + in.readMap(out, null); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readParcelable;;;Argument[-1];ReturnValue;taint" + Parcelable out = null; + Parcel in = (Parcel)source(); + out = in.readParcelable(null); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readParcelableArray;;;Argument[-1];ReturnValue;taint" + Parcelable[] out = null; + Parcel in = (Parcel)source(); + out = in.readParcelableArray(null); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readParcelableList;;;Argument[-1];Argument[0];taint" + List out = null; + Parcel in = (Parcel)source(); + in.readParcelableList(out, null); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readParcelableList;;;Argument[0];ReturnValue;value" + List out = null; + List in = (List)source(); + Parcel instance = null; + out = instance.readParcelableList(in, null); + sink(out); // $ hasValueFlow + } + { + // "android.os;Parcel;false;readPersistableBundle;;;Argument[-1];ReturnValue;taint" + PersistableBundle out = null; + Parcel in = (Parcel)source(); + out = in.readPersistableBundle(null); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readPersistableBundle;;;Argument[-1];ReturnValue;taint" + PersistableBundle out = null; + Parcel in = (Parcel)source(); + out = in.readPersistableBundle(); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readSerializable;;;Argument[-1];ReturnValue;taint" + Serializable out = null; + Parcel in = (Parcel)source(); + out = in.readSerializable(); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readSize;;;Argument[-1];ReturnValue;taint" + Size out = null; + Parcel in = (Parcel)source(); + out = in.readSize(); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readSizeF;;;Argument[-1];ReturnValue;taint" + SizeF out = null; + Parcel in = (Parcel)source(); + out = in.readSizeF(); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readSparseArray;;;Argument[-1];ReturnValue;taint" + SparseArray out = null; + Parcel in = (Parcel)source(); + out = in.readSparseArray(null); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readSparseBooleanArray;;;Argument[-1];ReturnValue;taint" + SparseBooleanArray out = null; + Parcel in = (Parcel)source(); + out = in.readSparseBooleanArray(); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readString;;;Argument[-1];ReturnValue;taint" + String out = null; + Parcel in = (Parcel)source(); + out = in.readString(); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readStringArray;;;Argument[-1];Argument[0];taint" + String[] out = null; + Parcel in = (Parcel)source(); + in.readStringArray(out); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readStringList;;;Argument[-1];Argument[0];taint" + List out = null; + Parcel in = (Parcel)source(); + in.readStringList(out); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readStrongBinder;;;Argument[-1];ReturnValue;taint" + IBinder out = null; + Parcel in = (Parcel)source(); + out = in.readStrongBinder(); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readTypedArray;;;Argument[-1];Argument[0];taint" + Object[] out = null; + Parcel in = (Parcel)source(); + in.readTypedArray(out, null); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readTypedList;;;Argument[-1];Argument[0];taint" + List out = null; + Parcel in = (Parcel)source(); + in.readTypedList(out, null); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readTypedObject;;;Argument[-1];ReturnValue;taint" + Object out = null; + Parcel in = (Parcel)source(); + out = in.readTypedObject(null); + sink(out); // $ hasTaintFlow + } + { + // "android.os;Parcel;false;readValue;;;Argument[-1];ReturnValue;taint" + Object out = null; + Parcel in = (Parcel)source(); + out = in.readValue(null); + sink(out); // $ hasTaintFlow + } + + } + +} \ No newline at end of file diff --git a/java/ql/test/library-tests/frameworks/android/flow-steps/options b/java/ql/test/library-tests/frameworks/android/flow-steps/options new file mode 100644 index 00000000000..33cdc1ea940 --- /dev/null +++ b/java/ql/test/library-tests/frameworks/android/flow-steps/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/google-android-9.0.0 diff --git a/java/ql/test/library-tests/frameworks/android/flow-steps/test.expected b/java/ql/test/library-tests/frameworks/android/flow-steps/test.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/java/ql/test/library-tests/frameworks/android/flow-steps/test.ql b/java/ql/test/library-tests/frameworks/android/flow-steps/test.ql new file mode 100644 index 00000000000..5d91e4e8e26 --- /dev/null +++ b/java/ql/test/library-tests/frameworks/android/flow-steps/test.ql @@ -0,0 +1,2 @@ +import java +import TestUtilities.InlineFlowTest diff --git a/java/ql/test/library-tests/frameworks/android/intent/Test.java b/java/ql/test/library-tests/frameworks/android/intent/Test.java new file mode 100644 index 00000000000..9172c4c19ad --- /dev/null +++ b/java/ql/test/library-tests/frameworks/android/intent/Test.java @@ -0,0 +1,1603 @@ +package generatedtest; + +import android.content.Context; +import android.content.Intent; +import android.content.IntentSender; +import android.net.Uri; +import android.os.BaseBundle; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PersistableBundle; +import android.util.SparseArray; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Set; + +// Test case generated by GenerateFlowTestCase.ql +public class Test { + + T getElement(Iterable it) { return it.iterator().next(); } + Bundle getIntent_extras(Intent i) { return i.getExtras(); } + String getMapKey(BaseBundle b) { return b.keySet().iterator().next(); } + Object getMapValue(BaseBundle b) { return null; } + Intent newWithIntent_extras(Bundle b) { return null; } + Intent newWithIntent_data(Uri data) { return new Intent("title", data); } + Bundle newBundleWithMapKey(String k) { Bundle b = new Bundle(); b.putInt(k, 0); return b; } + PersistableBundle newPersistableBundleWithMapKey(String k) { PersistableBundle b = new PersistableBundle(); b.putInt(k, 0); return b; } + Bundle newBundleWithMapValue(Object element) { return null; } + PersistableBundle newPersistableBundleWithMapValue(Object element) { return null; } + Uri getData(Intent intent) { return intent.getData(); } + T source() { return null; } + void sink(Object o) { } + + public void test() throws Exception { + + { + // "android.content;Intent;false;Intent;(Intent);;MapKey of SyntheticField[android.content.Intent.extras] of Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + Intent in = (Intent)newWithIntent_extras(newBundleWithMapKey(source())); + out = new Intent(in); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;false;Intent;(Intent);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[0];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + Intent in = (Intent)newWithIntent_extras(newBundleWithMapValue(source())); + out = new Intent(in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;false;Intent;(String,Uri);;Argument[1];SyntheticField[android.content.Intent.data] of Argument[-1];value" + Intent out = null; + Uri in = (Uri)source(); + out = new Intent(null, in); + sink(getData(out)); // $ hasValueFlow + } + { + // "android.content;Intent;false;Intent;(String,Uri,Context,Class);;Argument[1];SyntheticField[android.content.Intent.data] of Argument[-1];value" + Intent out = null; + Uri in = (Uri)source(); + out = new Intent(null, in, null, null); + sink(getData(out)); // $ hasValueFlow + } + { + // "android.content;Intent;true;addCategory;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.addCategory(null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;addFlags;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.addFlags(0); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;false;createChooser;;;Argument[0..2];MapValue of SyntheticField[android.content.Intent.extras] of ReturnValue;value" + Intent out = null; + CharSequence in = (CharSequence)source(); + out = Intent.createChooser(null, in, null); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;false;createChooser;;;Argument[0..2];MapValue of SyntheticField[android.content.Intent.extras] of ReturnValue;value" + Intent out = null; + IntentSender in = (IntentSender)source(); + out = Intent.createChooser(null, null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;false;createChooser;;;Argument[0..2];MapValue of SyntheticField[android.content.Intent.extras] of ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = Intent.createChooser(in, null, null); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;getBundleExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value" + Bundle out = null; + Intent in = (Intent)newWithIntent_extras(newBundleWithMapValue(source())); + out = in.getBundleExtra(null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;getByteArrayExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value" + byte[] out = null; + Intent in = (Intent)newWithIntent_extras(newBundleWithMapValue(source())); + out = in.getByteArrayExtra(null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;getCharArrayExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value" + char[] out = null; + Intent in = (Intent)newWithIntent_extras(newBundleWithMapValue(source())); + out = in.getCharArrayExtra(null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;getCharSequenceArrayExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value" + CharSequence[] out = null; + Intent in = (Intent)newWithIntent_extras(newBundleWithMapValue(source())); + out = in.getCharSequenceArrayExtra(null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;getCharSequenceArrayListExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value" + ArrayList out = null; + Intent in = (Intent)newWithIntent_extras(newBundleWithMapValue(source())); + out = in.getCharSequenceArrayListExtra(null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;getCharSequenceExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value" + CharSequence out = null; + Intent in = (Intent)newWithIntent_extras(newBundleWithMapValue(source())); + out = in.getCharSequenceExtra(null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;getData;;;SyntheticField[android.content.Intent.data] of Argument[-1];ReturnValue;value" + Uri out = null; + Intent in = (Intent)newWithIntent_data(source()); + out = in.getData(); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;getDataString;;;SyntheticField[android.content.Intent.data] of Argument[-1];ReturnValue;taint" + String out = null; + Intent in = (Intent)newWithIntent_data(source()); + out = in.getDataString(); + sink(out); // $ hasTaintFlow + } + { + // "android.content;Intent;true;getExtras;();;SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value" + Bundle out = null; + Intent in = (Intent)newWithIntent_extras(source()); + out = in.getExtras(); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;false;getIntent;;;Argument[0];SyntheticField[android.content.Intent.data] of Argument[-1];taint" + Intent out = null; + String in = (String)source(); + out = Intent.getIntent(in); + sink(out.getData()); // $ hasTaintFlow + } + { + // "android.content;Intent;false;getIntentOld;;;Argument[0];SyntheticField[android.content.Intent.data] of Argument[-1];taint" + Intent out = null; + String in = (String)source(); + out = Intent.getIntentOld(in); + sink(out.getData()); // $ hasTaintFlow + } + { + // "android.content;Intent;true;getParcelableArrayExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value" + Parcelable[] out = null; + Intent in = (Intent)newWithIntent_extras(newBundleWithMapValue(source())); + out = in.getParcelableArrayExtra(null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;getParcelableArrayListExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value" + ArrayList out = null; + Intent in = (Intent)newWithIntent_extras(newBundleWithMapValue(source())); + out = in.getParcelableArrayListExtra(null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;getParcelableExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value" + Parcelable out = null; + Intent in = (Intent)newWithIntent_extras(newBundleWithMapValue(source())); + out = in.getParcelableExtra(null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;getSerializableExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value" + Serializable out = null; + Intent in = (Intent)newWithIntent_extras(newBundleWithMapValue(source())); + out = in.getSerializableExtra(null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;getStringArrayExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value" + String[] out = null; + Intent in = (Intent)newWithIntent_extras(newBundleWithMapValue(source())); + out = in.getStringArrayExtra(null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;getStringArrayListExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value" + ArrayList out = null; + Intent in = (Intent)newWithIntent_extras(newBundleWithMapValue(source())); + out = in.getStringArrayListExtra(null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;getStringExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value" + String out = null; + Intent in = (Intent)newWithIntent_extras(newBundleWithMapValue(source())); + out = in.getStringExtra(null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;false;parseUri;;;Argument[0];SyntheticField[android.content.Intent.data] of Argument[-1];taint" + Intent out = null; + String in = (String)source(); + out = Intent.parseUri(in, 0); + sink(out.getData()); // $ hasTaintFlow + } + { + // "android.content;Intent;true;putCharSequenceArrayListExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putCharSequenceArrayListExtra(null, null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putCharSequenceArrayListExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putCharSequenceArrayListExtra(in, null); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putCharSequenceArrayListExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + ArrayList in = (ArrayList)source(); + out.putCharSequenceArrayListExtra(null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, false); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, 0L); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, 0.0f); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, 0.0); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, 0); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, (short[])null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, (short)0); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, (long[])null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, (int[])null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, (float[])null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, (double[])null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, (char[])null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, (byte[])null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, (byte)0); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, (boolean[])null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, (String[])null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, (String)null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, (Serializable)null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, (Parcelable[])null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, (Parcelable)null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, (CharSequence[])null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, (CharSequence)null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, (Bundle)null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtra((String)null, '\0'); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, false); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, 0L); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, 0.0f); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, 0.0); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, 0); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, (short[])null); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, (short)0); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, (long[])null); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, (int[])null); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, (float[])null); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, (double[])null); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, (char[])null); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, (byte[])null); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, (byte)0); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, (boolean[])null); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, (String[])null); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, (String)null); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, (Serializable)null); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, (Parcelable[])null); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, (Parcelable)null); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, (CharSequence[])null); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, (CharSequence)null); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, (Bundle)null); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra(in, '\0'); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + short[] in = (short[])source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + short in = (short)source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + long[] in = (long[])source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + long in = (long)source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + int[] in = (int[])source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + int in = (int)source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + float[] in = (float[])source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + float in = (float)source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + double[] in = (double[])source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + double in = (double)source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + char[] in = (char[])source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + char in = (char)source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + byte[] in = (byte[])source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + byte in = (byte)source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + boolean[] in = (boolean[])source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + boolean in = (boolean)source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String[] in = (String[])source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + Serializable in = (Serializable)source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + Parcelable[] in = (Parcelable[])source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + Parcelable in = (Parcelable)source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + CharSequence[] in = (CharSequence[])source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + CharSequence in = (CharSequence)source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + Bundle in = (Bundle)source(); + out.putExtra((String)null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtras;(Bundle);;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtras((Bundle)null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtras;(Bundle);;MapKey of Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + Bundle in = (Bundle)newBundleWithMapKey(source()); + out.putExtras(in); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtras;(Bundle);;MapValue of Argument[0];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + Bundle in = (Bundle)newBundleWithMapValue(source()); + out.putExtras(in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtras;(Intent);;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putExtras((Intent)null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtras;(Intent);;MapKey of SyntheticField[android.content.Intent.extras] of Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + Intent in = (Intent)newWithIntent_extras(newBundleWithMapKey(source())); + out.putExtras(in); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putExtras;(Intent);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[0];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + Intent in = (Intent)newWithIntent_extras(newBundleWithMapValue(source())); + out.putExtras(in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putIntegerArrayListExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putIntegerArrayListExtra(null, null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putIntegerArrayListExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putIntegerArrayListExtra(in, null); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putParcelableArrayListExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putParcelableArrayListExtra(null, null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putParcelableArrayListExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putParcelableArrayListExtra(in, null); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putParcelableArrayListExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + ArrayList in = (ArrayList)source(); + out.putParcelableArrayListExtra(null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putStringArrayListExtra;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.putStringArrayListExtra(null, null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;putStringArrayListExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + String in = (String)source(); + out.putStringArrayListExtra(in, null); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;putStringArrayListExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + ArrayList in = (ArrayList)source(); + out.putStringArrayListExtra(null, in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;replaceExtras;(Bundle);;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.replaceExtras((Bundle)null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;replaceExtras;(Bundle);;MapKey of Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + Bundle in = (Bundle)newBundleWithMapKey(source()); + out.replaceExtras(in); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;replaceExtras;(Bundle);;MapValue of Argument[0];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + Bundle in = (Bundle)newBundleWithMapValue(source()); + out.replaceExtras(in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;replaceExtras;(Intent);;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.replaceExtras((Intent)null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;replaceExtras;(Intent);;MapKey of SyntheticField[android.content.Intent.extras] of Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + Intent in = (Intent)newWithIntent_extras(newBundleWithMapKey(source())); + out.replaceExtras(in); + sink(getMapKey(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;replaceExtras;(Intent);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[0];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value" + Intent out = null; + Intent in = (Intent)newWithIntent_extras(newBundleWithMapValue(source())); + out.replaceExtras(in); + sink(getMapValue(getIntent_extras(out))); // $ hasValueFlow + } + { + // "android.content;Intent;true;setAction;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.setAction(null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;setClass;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.setClass(null, null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;setClassName;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.setClassName((String)null, (String)null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;setClassName;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.setClassName((Context)null, (String)null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;setComponent;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.setComponent(null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;setData;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.setData(null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;setData;;;Argument[0];SyntheticField[android.content.Intent.data] of Argument[-1];value", + Uri in = (Uri)source(); + Intent instance = new Intent(); + instance.setData(in); + sink(instance.getData()); // $ hasValueFlow + } + { + // "android.content;Intent;true;setDataAndNormalize;;;Argument[0];SyntheticField[android.content.Intent.data] of Argument[-1];value", + Uri in = (Uri)source(); + Intent instance = new Intent(); + instance.setDataAndNormalize(in); + sink(instance.getData()); // $ hasValueFlow + } + { + // "android.content;Intent;true;setDataAndType;;;Argument[0];SyntheticField[android.content.Intent.data] of Argument[-1];value", + Uri in = (Uri)source(); + Intent instance = new Intent(); + instance.setDataAndType(in, null); + sink(instance.getData()); // $ hasValueFlow + } + { + // "android.content;Intent;true;setDataAndTypeAndNormalize;;;Argument[0];SyntheticField[android.content.Intent.data] of Argument[-1];value", + Uri in = (Uri)source(); + Intent instance = new Intent(); + instance.setDataAndTypeAndNormalize(in, null); + sink(instance.getData()); // $ hasValueFlow + } + { + // "android.content;Intent;true;setDataAndNormalize;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.setDataAndNormalize(null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;setDataAndType;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.setDataAndType(null, null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;setDataAndTypeAndNormalize;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.setDataAndTypeAndNormalize(null, null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;setFlags;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.setFlags(0); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;setIdentifier;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.setIdentifier(null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;setPackage;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.setPackage(null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;setType;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.setType(null); + sink(out); // $ hasValueFlow + } + { + // "android.content;Intent;true;setTypeAndNormalize;;;Argument[-1];ReturnValue;value" + Intent out = null; + Intent in = (Intent)source(); + out = in.setTypeAndNormalize(null); + sink(out); // $ hasValueFlow + } + { + // "android.os;BaseBundle;true;get;(String);;MapValue of Argument[-1];ReturnValue;value" + Object out = null; + BaseBundle in = (BaseBundle)newBundleWithMapValue(source()); + out = in.get(null); + sink(out); // $ hasValueFlow + } + { + // "android.os;BaseBundle;true;getString;(String);;MapValue of Argument[-1];ReturnValue;value" + String out = null; + BaseBundle in = (BaseBundle)newBundleWithMapValue(source()); + out = in.getString(null); + sink(out); // $ hasValueFlow + } + { + // "android.os;BaseBundle;true;getString;(String,String);;Argument[1];ReturnValue;value" + String out = null; + String in = (String)source(); + BaseBundle instance = null; + out = instance.getString(null, in); + sink(out); // $ hasValueFlow + } + { + // "android.os;BaseBundle;true;getString;(String,String);;MapValue of Argument[-1];ReturnValue;value" + String out = null; + BaseBundle in = (BaseBundle)newBundleWithMapValue(source()); + out = in.getString(null, null); + sink(out); // $ hasValueFlow + } + { + // "android.os;BaseBundle;true;getStringArray;(String);;MapValue of Argument[-1];ReturnValue;value" + String[] out = null; + BaseBundle in = (BaseBundle)newBundleWithMapValue(source()); + out = in.getStringArray(null); + sink(out); // $ hasValueFlow + } + { + // "android.os;BaseBundle;true;keySet;();;MapKey of Argument[-1];Element of ReturnValue;value" + Set out = null; + BaseBundle in = (BaseBundle)newBundleWithMapKey(source()); + out = in.keySet(); + sink(getElement(out)); // $ hasValueFlow + } + { + // "android.os;BaseBundle;true;putAll;(PersistableBundle);;MapKey of Argument[0];MapKey of Argument[-1];value" + BaseBundle out = null; + PersistableBundle in = newPersistableBundleWithMapKey(source()); + out.putAll(in); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;BaseBundle;true;putAll;(PersistableBundle);;MapValue of Argument[0];MapValue of Argument[-1];value" + BaseBundle out = null; + PersistableBundle in = newPersistableBundleWithMapValue(source()); + out.putAll(in); + sink(getMapValue(out)); // $ hasValueFlow + } + { + // "android.os;BaseBundle;true;putBoolean;;;Argument[0];MapKey of Argument[-1];value" + BaseBundle out = null; + String in = (String)source(); + out.putBoolean(in, false); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;BaseBundle;true;putBooleanArray;;;Argument[0];MapKey of Argument[-1];value" + BaseBundle out = null; + String in = (String)source(); + out.putBooleanArray(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;BaseBundle;true;putDouble;;;Argument[0];MapKey of Argument[-1];value" + BaseBundle out = null; + String in = (String)source(); + out.putDouble(in, 0.0); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;BaseBundle;true;putDoubleArray;;;Argument[0];MapKey of Argument[-1];value" + BaseBundle out = null; + String in = (String)source(); + out.putDoubleArray(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;BaseBundle;true;putInt;;;Argument[0];MapKey of Argument[-1];value" + BaseBundle out = null; + String in = (String)source(); + out.putInt(in, 0); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;BaseBundle;true;putIntArray;;;Argument[0];MapKey of Argument[-1];value" + BaseBundle out = null; + String in = (String)source(); + out.putIntArray(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;BaseBundle;true;putLong;;;Argument[0];MapKey of Argument[-1];value" + BaseBundle out = null; + String in = (String)source(); + out.putLong(in, 0L); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;BaseBundle;true;putLongArray;;;Argument[0];MapKey of Argument[-1];value" + BaseBundle out = null; + String in = (String)source(); + out.putLongArray(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;BaseBundle;true;putString;;;Argument[0];MapKey of Argument[-1];value" + BaseBundle out = null; + String in = (String)source(); + out.putString(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;BaseBundle;true;putString;;;Argument[1];MapValue of Argument[-1];value" + BaseBundle out = null; + String in = (String)source(); + out.putString(null, in); + sink(getMapValue(out)); // $ hasValueFlow + } + { + // "android.os;BaseBundle;true;putStringArray;;;Argument[0];MapKey of Argument[-1];value" + BaseBundle out = null; + String in = (String)source(); + out.putStringArray(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;BaseBundle;true;putStringArray;;;Argument[1];MapValue of Argument[-1];value" + BaseBundle out = null; + String[] in = (String[])source(); + out.putStringArray(null, in); + sink(getMapValue(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;false;Bundle;(Bundle);;MapKey of Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + Bundle in = (Bundle)newBundleWithMapKey(source()); + out = new Bundle(in); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;false;Bundle;(Bundle);;MapValue of Argument[0];MapValue of Argument[-1];value" + Bundle out = null; + Bundle in = (Bundle)newBundleWithMapValue(source()); + out = new Bundle(in); + sink(getMapValue(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;false;Bundle;(PersistableBundle);;MapKey of Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + PersistableBundle in = newPersistableBundleWithMapKey(source()); + out = new Bundle(in); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;false;Bundle;(PersistableBundle);;MapValue of Argument[0];MapValue of Argument[-1];value" + Bundle out = null; + PersistableBundle in = newPersistableBundleWithMapValue(source()); + out = new Bundle(in); + sink(getMapValue(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;clone;();;MapKey of Argument[-1];MapKey of ReturnValue;value" + Object out = null; + Bundle in = (Bundle)newBundleWithMapKey(source()); + out = in.clone(); + sink(getMapKey((Bundle)out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;clone;();;MapValue of Argument[-1];MapValue of ReturnValue;value" + Object out = null; + Bundle in = (Bundle)newBundleWithMapValue(source()); + out = in.clone(); + sink(getMapValue((Bundle)out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;deepCopy;();;MapKey of Argument[-1];MapKey of ReturnValue;value" + Bundle out = null; + Bundle in = (Bundle)newBundleWithMapKey(source()); + out = in.deepCopy(); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;deepCopy;();;MapValue of Argument[-1];MapValue of ReturnValue;value" + Bundle out = null; + Bundle in = (Bundle)newBundleWithMapValue(source()); + out = in.deepCopy(); + sink(getMapValue(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;getBinder;(String);;MapValue of Argument[-1];ReturnValue;value" + IBinder out = null; + Bundle in = (Bundle)newBundleWithMapValue(source()); + out = in.getBinder(null); + sink(out); // $ hasValueFlow + } + { + // "android.os;Bundle;true;getBundle;(String);;MapValue of Argument[-1];ReturnValue;value" + Bundle out = null; + Bundle in = (Bundle)newBundleWithMapValue(source()); + out = in.getBundle(null); + sink(out); // $ hasValueFlow + } + { + // "android.os;Bundle;true;getByteArray;(String);;MapValue of Argument[-1];ReturnValue;value" + byte[] out = null; + Bundle in = (Bundle)newBundleWithMapValue(source()); + out = in.getByteArray(null); + sink(out); // $ hasValueFlow + } + { + // "android.os;Bundle;true;getCharArray;(String);;MapValue of Argument[-1];ReturnValue;value" + char[] out = null; + Bundle in = (Bundle)newBundleWithMapValue(source()); + out = in.getCharArray(null); + sink(out); // $ hasValueFlow + } + { + // "android.os;Bundle;true;getCharSequence;(String);;MapValue of Argument[-1];ReturnValue;value" + CharSequence out = null; + Bundle in = (Bundle)newBundleWithMapValue(source()); + out = in.getCharSequence(null); + sink(out); // $ hasValueFlow + } + { + // "android.os;Bundle;true;getCharSequence;(String,CharSequence);;Argument[1];ReturnValue;value" + CharSequence out = null; + CharSequence in = (CharSequence)source(); + Bundle instance = null; + out = instance.getCharSequence(null, in); + sink(out); // $ hasValueFlow + } + { + // "android.os;Bundle;true;getCharSequence;(String,CharSequence);;MapValue of Argument[-1];ReturnValue;value" + CharSequence out = null; + Bundle in = (Bundle)newBundleWithMapValue(source()); + out = in.getCharSequence(null, null); + sink(out); // $ hasValueFlow + } + { + // "android.os;Bundle;true;getCharSequenceArray;(String);;MapValue of Argument[-1];ReturnValue;value" + CharSequence[] out = null; + Bundle in = (Bundle)newBundleWithMapValue(source()); + out = in.getCharSequenceArray(null); + sink(out); // $ hasValueFlow + } + { + // "android.os;Bundle;true;getCharSequenceArrayList;(String);;MapValue of Argument[-1];ReturnValue;value" + ArrayList out = null; + Bundle in = (Bundle)newBundleWithMapValue(source()); + out = in.getCharSequenceArrayList(null); + sink(out); // $ hasValueFlow + } + { + // "android.os;Bundle;true;getParcelable;(String);;MapValue of Argument[-1];ReturnValue;value" + Parcelable out = null; + Bundle in = (Bundle)newBundleWithMapValue(source()); + out = in.getParcelable(null); + sink(out); // $ hasValueFlow + } + { + // "android.os;Bundle;true;getParcelableArray;(String);;MapValue of Argument[-1];ReturnValue;value" + Parcelable[] out = null; + Bundle in = (Bundle)newBundleWithMapValue(source()); + out = in.getParcelableArray(null); + sink(out); // $ hasValueFlow + } + { + // "android.os;Bundle;true;getParcelableArrayList;(String);;MapValue of Argument[-1];ReturnValue;value" + ArrayList out = null; + Bundle in = (Bundle)newBundleWithMapValue(source()); + out = in.getParcelableArrayList(null); + sink(out); // $ hasValueFlow + } + { + // "android.os;Bundle;true;getSerializable;(String);;MapValue of Argument[-1];ReturnValue;value" + Serializable out = null; + Bundle in = (Bundle)newBundleWithMapValue(source()); + out = in.getSerializable(null); + sink(out); // $ hasValueFlow + } + { + // "android.os;Bundle;true;getSparseParcelableArray;(String);;MapValue of Argument[-1];ReturnValue;value" + SparseArray out = null; + Bundle in = (Bundle)newBundleWithMapValue(source()); + out = in.getSparseParcelableArray(null); + sink(out); // $ hasValueFlow + } + { + // "android.os;Bundle;true;getStringArrayList;(String);;MapValue of Argument[-1];ReturnValue;value" + ArrayList out = null; + Bundle in = (Bundle)newBundleWithMapValue(source()); + out = in.getStringArrayList(null); + sink(out); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putAll;(Bundle);;MapKey of Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + Bundle in = (Bundle)newBundleWithMapKey(source()); + out.putAll(in); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putAll;(Bundle);;MapValue of Argument[0];MapValue of Argument[-1];value" + Bundle out = null; + Bundle in = (Bundle)newBundleWithMapValue(source()); + out.putAll(in); + sink(getMapValue(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putBinder;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putBinder(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putBinder;;;Argument[1];MapValue of Argument[-1];value" + Bundle out = null; + IBinder in = (IBinder)source(); + out.putBinder(null, in); + sink(getMapValue(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putBundle;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putBundle(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putBundle;;;Argument[1];MapValue of Argument[-1];value" + Bundle out = null; + Bundle in = (Bundle)source(); + out.putBundle(null, in); + sink(getMapValue(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putByte;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putByte(in, (byte)0); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putByteArray;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putByteArray(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putByteArray;;;Argument[1];MapValue of Argument[-1];value" + Bundle out = null; + byte[] in = (byte[])source(); + out.putByteArray(null, in); + sink(getMapValue(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putChar;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putChar(in, '\0'); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putCharArray;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putCharArray(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putCharArray;;;Argument[1];MapValue of Argument[-1];value" + Bundle out = null; + char[] in = (char[])source(); + out.putCharArray(null, in); + sink(getMapValue(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putCharSequence;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putCharSequence(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putCharSequence;;;Argument[1];MapValue of Argument[-1];value" + Bundle out = null; + CharSequence in = (CharSequence)source(); + out.putCharSequence(null, in); + sink(getMapValue(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putCharSequenceArray;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putCharSequenceArray(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putCharSequenceArray;;;Argument[1];MapValue of Argument[-1];value" + Bundle out = null; + CharSequence[] in = (CharSequence[])source(); + out.putCharSequenceArray(null, in); + sink(getMapValue(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putCharSequenceArrayList;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putCharSequenceArrayList(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putCharSequenceArrayList;;;Argument[1];MapValue of Argument[-1];value" + Bundle out = null; + ArrayList in = (ArrayList)source(); + out.putCharSequenceArrayList(null, in); + sink(getMapValue(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putFloat;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putFloat(in, 0.0f); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putFloatArray;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putFloatArray(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putIntegerArrayList;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putIntegerArrayList(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putParcelable;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putParcelable(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putParcelable;;;Argument[1];MapValue of Argument[-1];value" + Bundle out = null; + Parcelable in = (Parcelable)source(); + out.putParcelable(null, in); + sink(getMapValue(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putParcelableArray;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putParcelableArray(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putParcelableArray;;;Argument[1];MapValue of Argument[-1];value" + Bundle out = null; + Parcelable[] in = (Parcelable[])source(); + out.putParcelableArray(null, in); + sink(getMapValue(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putParcelableArrayList;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putParcelableArrayList(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putParcelableArrayList;;;Argument[1];MapValue of Argument[-1];value" + Bundle out = null; + ArrayList in = (ArrayList)source(); + out.putParcelableArrayList(null, in); + sink(getMapValue(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putSerializable;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putSerializable(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putSerializable;;;Argument[1];MapValue of Argument[-1];value" + Bundle out = null; + Serializable in = (Serializable)source(); + out.putSerializable(null, in); + sink(getMapValue(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putShort;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putShort(in, (short)0); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putShortArray;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putShortArray(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putSize;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putSize(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putSizeF;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putSizeF(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putSparseParcelableArray;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putSparseParcelableArray(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putSparseParcelableArray;;;Argument[1];MapValue of Argument[-1];value" + Bundle out = null; + SparseArray in = (SparseArray)source(); + out.putSparseParcelableArray(null, in); + sink(getMapValue(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putStringArrayList;;;Argument[0];MapKey of Argument[-1];value" + Bundle out = null; + String in = (String)source(); + out.putStringArrayList(in, null); + sink(getMapKey(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;putStringArrayList;;;Argument[1];MapValue of Argument[-1];value" + Bundle out = null; + ArrayList in = (ArrayList)source(); + out.putStringArrayList(null, in); + sink(getMapValue(out)); // $ hasValueFlow + } + { + // "android.os;Bundle;true;readFromParcel;;;Argument[0];MapKey of Argument[-1];taint" + Bundle out = null; + Parcel in = (Parcel)source(); + out.readFromParcel(in); + sink(getMapKey(out)); // $ hasTaintFlow + } + { + // "android.os;Bundle;true;readFromParcel;;;Argument[0];MapValue of Argument[-1];taint" + Bundle out = null; + Parcel in = (Parcel)source(); + out.readFromParcel(in); + sink(getMapValue(out)); // $ hasTaintFlow + } + + } + +} \ No newline at end of file diff --git a/java/ql/test/library-tests/frameworks/android/intent/options b/java/ql/test/library-tests/frameworks/android/intent/options new file mode 100644 index 00000000000..020a4a1cebb --- /dev/null +++ b/java/ql/test/library-tests/frameworks/android/intent/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/android diff --git a/java/ql/test/library-tests/frameworks/android/intent/test.expected b/java/ql/test/library-tests/frameworks/android/intent/test.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/java/ql/test/library-tests/frameworks/android/intent/test.ql b/java/ql/test/library-tests/frameworks/android/intent/test.ql new file mode 100644 index 00000000000..d4bdcaf9335 --- /dev/null +++ b/java/ql/test/library-tests/frameworks/android/intent/test.ql @@ -0,0 +1,15 @@ +import java +import TestUtilities.InlineFlowTest + +class SummaryModelTest extends SummaryModelCsv { + override predicate row(string row) { + row = + [ + //"package;type;overrides;name;signature;ext;inputspec;outputspec;kind", + "generatedtest;Test;false;newBundleWithMapValue;(Object);;Argument[0];MapValue of ReturnValue;value", + "generatedtest;Test;false;newPersistableBundleWithMapValue;(Object);;Argument[0];MapValue of ReturnValue;value", + "generatedtest;Test;false;getMapValue;(BaseBundle);;MapValue of Argument[0];ReturnValue;value", + "generatedtest;Test;false;newWithIntent_extras;(Bundle);;Argument[0];SyntheticField[android.content.Intent.extras] of ReturnValue;value" + ] + } +} diff --git a/java/ql/test/library-tests/frameworks/ratpack/flow.expected b/java/ql/test/library-tests/frameworks/ratpack/flow.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/java/ql/test/library-tests/frameworks/ratpack/flow.ql b/java/ql/test/library-tests/frameworks/ratpack/flow.ql new file mode 100644 index 00000000000..a7f6f9aa460 --- /dev/null +++ b/java/ql/test/library-tests/frameworks/ratpack/flow.ql @@ -0,0 +1,33 @@ +import java +import semmle.code.java.dataflow.TaintTracking +import semmle.code.java.dataflow.FlowSources +import TestUtilities.InlineExpectationsTest + +class Conf extends TaintTracking::Configuration { + Conf() { this = "qltest:frameworks:ratpack" } + + override predicate isSource(DataFlow::Node n) { + n.asExpr().(MethodAccess).getMethod().hasName("taint") + or + n instanceof RemoteFlowSource + } + + override predicate isSink(DataFlow::Node n) { + exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument()) + } +} + +class HasFlowTest extends InlineExpectationsTest { + HasFlowTest() { this = "HasFlowTest" } + + override string getARelevantTag() { result = "hasTaintFlow" } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "hasTaintFlow" and + exists(DataFlow::Node src, DataFlow::Node sink, Conf conf | conf.hasFlow(src, sink) | + sink.getLocation() = location and + element = sink.toString() and + value = "" + ) + } +} diff --git a/java/ql/test/library-tests/frameworks/ratpack/options b/java/ql/test/library-tests/frameworks/ratpack/options new file mode 100644 index 00000000000..f9633549558 --- /dev/null +++ b/java/ql/test/library-tests/frameworks/ratpack/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/ratpack-1.9.x:${testdir}/../../../stubs/jackson-core-2.12:${testdir}/../../../stubs/jackson-databind-2.12:${testdir}/../../../stubs/guava-30.0:${testdir}/../../../stubs/guava-30.0:${testdir}/../../../stubs/netty-4.1.x diff --git a/java/ql/test/library-tests/frameworks/ratpack/resources/CollectionPassingTest.java b/java/ql/test/library-tests/frameworks/ratpack/resources/CollectionPassingTest.java new file mode 100644 index 00000000000..eb0ca9cad92 --- /dev/null +++ b/java/ql/test/library-tests/frameworks/ratpack/resources/CollectionPassingTest.java @@ -0,0 +1,70 @@ +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +import ratpack.core.handling.Context; +import ratpack.core.form.Form; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.List; +import java.util.function.Predicate; + +public class CollectionPassingTest { + + void sink(Object o) {} + + String taint() { + return null; + } + + void test_1(Context ctx) { + // Given + ctx + .getRequest() + .getBody() + .map(data -> ctx.parse(data, Form.form())) + .then(form -> { + // When + Map pojoMap = new HashMap<>(); + merge(form.asMultimap().asMap(), pojoMap); + // Then + sink(pojoMap.get("value")); //$hasTaintFlow + pojoMap.forEach((key, value) -> { + sink(value); //$hasTaintFlow + List values = (List) value; + sink(values.get(0)); //$hasTaintFlow + }); + }); + } + + void test_2() { + // Given + Map> taintedMap = new HashMap<>(); + taintedMap.put("value", ImmutableList.of(taint())); + Map pojoMap = new HashMap<>(); + // When + merge(taintedMap, pojoMap); + // Then + sink(pojoMap.get("value")); //$hasTaintFlow + pojoMap.forEach((key, value) -> { + sink(value); //$hasTaintFlow + List values = (List) value; + sink(values.get(0)); //$hasTaintFlow + }); + } + + + private static void merge(Map> params, Map defaults) { + for(Map.Entry> entry : params.entrySet()) { + String name = entry.getKey(); + Collection values = entry.getValue(); + defaults.put(name, extractSingleValueIfPossible(values)); + } + } + + private static Object extractSingleValueIfPossible(Collection values) { + return values.size() == 1 ? values.iterator().next() : ImmutableList.copyOf(values); + } + +} diff --git a/java/ql/test/library-tests/frameworks/ratpack/resources/IntegrationTest.java b/java/ql/test/library-tests/frameworks/ratpack/resources/IntegrationTest.java new file mode 100644 index 00000000000..83ca80fc37f --- /dev/null +++ b/java/ql/test/library-tests/frameworks/ratpack/resources/IntegrationTest.java @@ -0,0 +1,242 @@ +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.POJONode; +import com.fasterxml.jackson.databind.JsonSerializable; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import ratpack.core.handling.Context; +import ratpack.core.http.TypedData; +import ratpack.core.form.Form; +import ratpack.core.form.UploadedFile; +import ratpack.core.parse.Parse; +import ratpack.exec.Promise; +import ratpack.func.Action; +import ratpack.func.Function; +import ratpack.func.MultiValueMap; + +import java.io.OutputStream; +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.List; +import java.util.function.Predicate; + +import static ratpack.jackson.Jackson.jsonNode; + +class IntegrationTest { + + static class Pojo { + String value; + + List values; + + String getValue() { + return value; + } + + List getValues() { + return values; + } + } + + private final ObjectMapper objectMapper = new ObjectMapper(); + + void sink(Object o) {} + + String taint() { + return null; + } + + void test1(Context ctx) { + bindJson(ctx, Pojo.class) + .then(pojo ->{ + sink(pojo); //$hasTaintFlow + sink(pojo.value); //$hasTaintFlow + sink(pojo.getValue()); //$hasTaintFlow + }); + } + + void test2(Context ctx) { + bindForm(ctx, Pojo.class, defaults -> defaults.put("another", "potato")) + .then(pojo ->{ + sink(pojo); //$hasTaintFlow + sink(pojo.value); //$hasTaintFlow + sink(pojo.getValue()); //$hasTaintFlow + }); + } + + void test3() { + Object value = extractSingleValueIfPossible(ImmutableList.of("a", taint())); + sink(value); //$hasTaintFlow + List values = (List) value; + sink(values.get(1)); //$hasTaintFlow + Map weirdMap = new HashMap<>(); + weirdMap.put("a", value); + weirdMap.forEach((key, mapValue) -> { + sink(mapValue); //$hasTaintFlow + List values2 = (List) mapValue; + sink(values2.get(0)); //$hasTaintFlow + }); + } + + void test4(Context ctx) { + parseToForm(ctx, Pojo.class) + .map(pojoForm -> { + Map mergedParams = new HashMap<>(); + filterAndMerge(pojoForm, mergedParams, name -> false); + return mergedParams; + }).then(pojoMap -> { + sink(pojoMap.keySet().iterator().next()); //$hasTaintFlow + sink(pojoMap.get("value")); //$hasTaintFlow + pojoMap.forEach((key, value) -> { + sink(key); //$hasTaintFlow + sink(value); //$hasTaintFlow + List values = (List) value; + sink(values.get(0)); //$hasTaintFlow + }); + }); + } + + void test5(Context ctx) { + parseToForm(ctx, Pojo.class) + .map(pojoForm -> { + Map mergedParams = new HashMap<>(); + filterAndMerge_2(pojoForm, mergedParams, name -> false); + return mergedParams; + }).then(pojoMap -> { + sink(pojoMap.keySet().iterator().next()); //TODO:$hasTaintFlow + sink(pojoMap.get("value")); //TODO:$hasTaintFlow + pojoMap.forEach((key, value) -> { + sink(key); //TODO:$hasTaintFlow + sink(value); //TODO:$hasTaintFlow + List values = (List) value; + sink(values.get(0)); //TODO:$hasTaintFlow + }); + }); + } + + void test6(Context ctx) { + bindQuery(ctx, Pojo.class) + .then(pojo -> { + sink(pojo.getValue()); //$hasTaintFlow + sink(pojo.getValues()); //$hasTaintFlow + }); + } + + public Promise bindQuery(Context ctx, Class type) { + return bindQuery(ctx, type, Action.noop()); + } + + public Promise bindQuery(Context ctx, Class type, Action> defaults) { + return Promise.sync(() -> + bind(ctx, toObjectNode(ctx.getRequest().getQueryParams(), defaults, name -> false), type) + ); + } + + private Promise bindJson(Context ctx, Class type) { + return ctx.getRequest().getBody() + .map(data -> { + String dataText = data.getText(); + + try { + return ctx.parse(data, jsonNode(objectMapper)); + } catch (Exception e) { + String msg = "Unable to parse json data while binding type " + type.getCanonicalName() + " [jsonData: " + dataText + "]"; + throw new RuntimeException(msg, e); + } + }) + .map(json -> + bind(ctx, json, type) + ); + } + + private T bind(Context ctx, JsonNode input, Class type) { + T value; + try { + value = objectMapper.convertValue(input, type); + } catch (Exception e) { + throw new RuntimeException("Failed to convert input to " + type.getName(), e); + } + return value; + } + + private static Promise
    parseToForm(Context ctx, Class type) { + return ctx.getRequest().getBody() + .map(data -> { + try { + return ctx.parse(data, Form.form()); + } catch (Exception e) { + String msg = "Unable to parse form data while binding type " + type.getCanonicalName() + " [formData: " + data.getText() + "]"; + throw new RuntimeException(msg, e); + } + }); + } + + private Promise bindForm(Context ctx, Class type, Action> defaults) { + return parseToForm(ctx, type) + .map(form -> { + ObjectNode input = toObjectNode(form, defaults, s -> false); + Map> filesMap = form.files().getAll(); + filesMap.forEach((name, files) -> { + ArrayNode array = input.putArray(name); + files.forEach(f -> array.add(new POJONode(new UploadedFileWrapper(f)))); + }); + return bind(ctx, input, type); + }); + } + + private ObjectNode toObjectNode(MultiValueMap params, Action> defaults, Predicate paramFilter) throws Exception { + Map mergedParams = new HashMap<>(defaults.with(ImmutableMap.builder()).build()); + filterAndMerge(params, mergedParams, paramFilter); + return objectMapper.valueToTree(mergedParams); + } + + private static void filterAndMerge(MultiValueMap params, Map defaults, Predicate filter) { + for(Map.Entry> entry : params.asMultimap().asMap().entrySet()) { + String name = entry.getKey(); + Collection values = entry.getValue(); + if (!isEmptyAndHasDefault(name, values, defaults) && !filter.test(name)) { + defaults.put(name, extractSingleValueIfPossible(values)); + } + } + } + + private static void filterAndMerge_2(MultiValueMap params, Map defaults, Predicate filter) { + params.asMultimap().asMap().forEach((name, values) -> { + if (!isEmptyAndHasDefault(name, values, defaults) && !filter.test(name)) { + defaults.put(name, extractSingleValueIfPossible(values)); + } + }); + } + + private static boolean isEmptyAndHasDefault(String name, Collection values, Map defaults) { + // STUB - This is to make the compiler happy + return false; + } + + private static Object extractSingleValueIfPossible(Collection values) { + return values.size() == 1 ? values.iterator().next() : ImmutableList.copyOf(values); + } + + private static class UploadedFileWrapper implements JsonSerializable { + + private final UploadedFile file; + + private UploadedFileWrapper(UploadedFile file) { + this.file = file; + } + + @Override + public void serialize(Object gen, Object serializers) throws IOException { + // empty + } + + @Override + public void serializeWithType(Object gen, Object serializers, Object typeSer) throws IOException { + // empty + } + } +} diff --git a/java/ql/test/library-tests/frameworks/ratpack/resources/Resource.java b/java/ql/test/library-tests/frameworks/ratpack/resources/Resource.java new file mode 100644 index 00000000000..d85924234f2 --- /dev/null +++ b/java/ql/test/library-tests/frameworks/ratpack/resources/Resource.java @@ -0,0 +1,320 @@ +import ratpack.core.handling.Context; +import ratpack.core.http.TypedData; +import ratpack.core.form.Form; +import ratpack.core.form.UploadedFile; +import ratpack.core.parse.Parse; +import ratpack.exec.Promise; +import ratpack.func.Action; +import ratpack.func.Function; +import java.io.OutputStream; + +class Resource { + + void sink(Object o) {} + + String taint() { + return null; + } + + void test1(Context ctx) { + sink(ctx.getRequest().getContentLength()); //$hasTaintFlow + sink(ctx.getRequest().getCookies()); //$hasTaintFlow + sink(ctx.getRequest().oneCookie("Magic-Cookie")); //$hasTaintFlow + sink(ctx.getRequest().getHeaders()); //$hasTaintFlow + sink(ctx.getRequest().getHeaders().get("questionable_header")); //$hasTaintFlow + sink(ctx.getRequest().getHeaders().getAll("questionable_header")); //$hasTaintFlow + sink(ctx.getRequest().getHeaders().getNames()); //$hasTaintFlow + sink(ctx.getRequest().getHeaders().asMultiValueMap()); //$hasTaintFlow + sink(ctx.getRequest().getHeaders().asMultiValueMap().get("questionable_header")); //$hasTaintFlow + sink(ctx.getRequest().getPath()); //$hasTaintFlow + sink(ctx.getRequest().getQuery()); //$hasTaintFlow + sink(ctx.getRequest().getQueryParams()); //$hasTaintFlow + sink(ctx.getRequest().getQueryParams().get("questionable_parameter")); //$hasTaintFlow + sink(ctx.getRequest().getRawUri()); //$hasTaintFlow + sink(ctx.getRequest().getUri()); //$hasTaintFlow + } + + void test2(Context ctx, OutputStream os) { + ctx.getRequest().getBody().then(td -> { + sink(td); //$hasTaintFlow + sink(td.getText()); //$hasTaintFlow + sink(td.getBuffer()); //$hasTaintFlow + sink(td.getBytes()); //$hasTaintFlow + sink(td.getContentType()); //$hasTaintFlow + sink(td.getInputStream()); //$hasTaintFlow + sink(os); + td.writeTo(os); + sink(os); //$hasTaintFlow + if (td instanceof UploadedFile) { + UploadedFile uf = (UploadedFile) td; + sink(uf.getFileName()); //$hasTaintFlow + } + }); + } + + void test3(Context ctx) { + ctx.getRequest().getBody().map(TypedData::getText).then(s -> { + sink(s); //$hasTaintFlow + }); + ctx.getRequest().getBody().map(b -> { + sink(b); //$hasTaintFlow + sink(b.getText()); //$hasTaintFlow + return b.getText(); + }).then(t -> { + sink(t); //$hasTaintFlow + }); + ctx.getRequest().getBody().map(TypedData::getText).then(this::sink); //$hasTaintFlow + ctx + .getRequest() + .getBody() + .map(TypedData::getText) + .next(this::sink) //$hasTaintFlow + .then(this::sink); //$hasTaintFlow + } + + void test4() { + String tainted = taint(); + Promise.value(tainted); + Promise + .value(tainted) + .then(this::sink); //$hasTaintFlow + Promise + .value(tainted) + .map(a -> a) + .then(this::sink); //$hasTaintFlow + } + + void test5(Context ctx) { + ctx + .getRequest() + .getBody() + .map(data -> { + Form form = ctx.parse(data, Form.form()); + sink(form); //$hasTaintFlow + return form; + }) + .then(form -> { + sink(form.file("questionable_file")); //$hasTaintFlow + sink(form.file("questionable_file").getFileName()); //$hasTaintFlow + sink(form.files("questionable_files")); //$hasTaintFlow + sink(form.files()); //$hasTaintFlow + sink(form.get("questionable_parameter")); //$hasTaintFlow + sink(form.getAll().get("questionable_parameter").get(0)); //$hasTaintFlow + sink(form.getAll("questionable_parameter").get(0)); //$hasTaintFlow + sink(form.asMultimap().get("questionable_parameter")); //$hasTaintFlow + sink(form.asMultimap().asMap()); //$hasTaintFlow + form.asMultimap().asMap().forEach((name, values) -> { + sink(name); //$hasTaintFlow + sink(values); //$hasTaintFlow + }); + }); + } + + void test6(Context ctx) { + ctx + .parse(Parse.of(Form.class)) + .then(form -> { + sink(form); //$hasTaintFlow + }); + ctx + .parse(Form.class) + .then(form -> { + sink(form); //$hasTaintFlow + }); + ctx + .parse(Form.class, "Some Object") + .then(form -> { + sink(form); //$hasTaintFlow + }); + } + + void test7() { + String tainted = taint(); + Promise + .flatten(() -> Promise.value(tainted)) + .next(value -> { + sink(value); //$hasTaintFlow + }) + .onError(Action.noop()) + .next(value -> { + sink(value); //$hasTaintFlow + }) + .cache() + .next(value -> { + sink(value); //$hasTaintFlow + }) + .fork() + .next(value -> { + sink(value); //$hasTaintFlow + }) + .route(value -> { + sink(value); //$hasTaintFlow + return false; + }, value -> { + sink(value); //$hasTaintFlow + }) + .next(value -> { + sink(value); //$hasTaintFlow + }) + .cacheIf(value -> { + sink(value); //$hasTaintFlow + return true; + }) + .next(value -> { + sink(value); //$hasTaintFlow + }) + .onError(RuntimeException.class, Action.noop()) + .next(value -> { + sink(value); //$hasTaintFlow + }) + .then(value -> { + sink(value); //$hasTaintFlow + }); + } + + void test8() { + String tainted = taint(); + Promise + .sync(() -> tainted) + .mapError(RuntimeException.class, exception -> { + sink(exception); // no taint + return "potato"; + }) + .then(value -> { + sink(value); //$hasTaintFlow + }); + Promise + .value("potato") + .mapError(RuntimeException.class, exception -> { + return taint(); + }) + .then(value -> { + sink(value); //$hasTaintFlow + }); + Promise + .value(tainted) + .flatMapError(RuntimeException.class, exception -> { + sink(exception); // no taint + return Promise.value("potato"); + }) + .then(value -> { + sink(value); //$hasTaintFlow + }); + Promise + .value("potato") + .flatMapError(RuntimeException.class, exception -> { + return Promise.value(taint()); + }) + .then(value -> { + sink(value); //$hasTaintFlow + }); + } + + void test9() { + String tainted = taint(); + Promise + .value(tainted) + .map(Resource::identity) + .then(value -> { + sink(value); //$hasTaintFlow + }); + Promise + .value("potato") + .map(Resource::identity) + .then(value -> { + sink(value); // no taints flow + }); + Promise + .value(tainted) + .flatMap(v -> Promise.value(v)) + .then(value -> { + sink(value); //$hasTaintFlow + }); + } + + public static String identity(String input) { + return input; + } + + void test10() { + String tainted = taint(); + Promise + .value(tainted) + .apply(Resource::promiseIdentity) + .then(value -> { + sink(value); //$hasTaintFlow + }); + Promise + .value("potato") + .apply(Resource::promiseIdentity) + .then(value -> { + sink(value); // no taints flow + }); + } + + public static Promise promiseIdentity(Promise input) { + return input.map(i -> i); + } + + void test11() { + String tainted = taint(); + Promise + .value(tainted) + .map(a -> a) + .then(value -> { + sink(value); //$hasTaintFlow + }); + Promise + .value("potato") + .map(a -> a) + .then(value -> { + sink(value); // no taints flow + }); + } + + void test12() { + String tainted = taint(); + Promise + .sync(() -> tainted) + .mapIf(v -> { + sink(v); //$hasTaintFlow + return true; + }, v -> { + sink(v); //$hasTaintFlow + return v; + }) + .then(value -> { + sink(value); //$hasTaintFlow + }); + Promise + .sync(() -> tainted) + .mapIf(v -> { + sink(v); //$hasTaintFlow + return true; + }, vTrue -> { + sink(vTrue); //$hasTaintFlow + return vTrue; + }, vFalse -> { + sink(vFalse); //$hasTaintFlow + return vFalse; + }) + .then(value -> { + sink(value); //$hasTaintFlow + }); + Promise + .sync(() -> tainted) + .mapIf(v -> { + sink(v); //$hasTaintFlow + return true; + }, vTrue -> { + sink(vTrue); //$hasTaintFlow + return "potato"; + }, vFalse -> { + sink(vFalse); //$hasTaintFlow + return "potato"; + }) + .then(value -> { + sink(value); // no tainted flow + }); + } +} diff --git a/java/ql/test/library-tests/frameworks/stream/Test.java b/java/ql/test/library-tests/frameworks/stream/Test.java new file mode 100644 index 00000000000..040fce0158e --- /dev/null +++ b/java/ql/test/library-tests/frameworks/stream/Test.java @@ -0,0 +1,475 @@ +package generatedtest; + +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.PrimitiveIterator; +import java.util.Spliterator; +import java.util.stream.BaseStream; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +public class Test { + + T getArrayElement(T[] array) { return array[0]; } + T getElement(BaseStream s) { return s.iterator().next(); } + T getElement(Iterable it) { return it.iterator().next(); } + T getElement(Iterator it) { return it.next(); } + T getElement(Optional o) { return o.get(); } + T getElementSpliterator(Spliterator o) { return null; } + Object source(String tag) { return null; } + void sink(Object o) { } + + public void test() throws Exception { + + // *** Generated *** + { + // "java.util.stream;BaseStream;true;iterator;();;Element of Argument[-1];Element of ReturnValue;value" + Iterator out = null; + BaseStream in = (BaseStream)Stream.of(source("iterator_1")); + out = in.iterator(); + sink(getElement(out)); // $ hasValueFlow=iterator_1 + } + { + // "java.util.stream;BaseStream;true;onClose;(Runnable);;Element of Argument[-1];Element of ReturnValue;value" + BaseStream out = null; + BaseStream in = (BaseStream)Stream.of(source("onClose_1")); + out = in.onClose(null); + sink(getElement(out)); // $ hasValueFlow=onClose_1 + } + { + // "java.util.stream;BaseStream;true;parallel;();;Element of Argument[-1];Element of ReturnValue;value" + BaseStream out = null; + BaseStream in = (BaseStream)Stream.of(source("parallel_1")); + out = in.parallel(); + sink(getElement(out)); // $ hasValueFlow=parallel_1 + } + { + // "java.util.stream;BaseStream;true;sequential;();;Element of Argument[-1];Element of ReturnValue;value" + BaseStream out = null; + BaseStream in = (BaseStream)Stream.of(source("sequential_1")); + out = in.sequential(); + sink(getElement(out)); // $ hasValueFlow=sequential_1 + } + { + // "java.util.stream;BaseStream;true;spliterator;();;Element of Argument[-1];Element of ReturnValue;value" + Spliterator out = null; + BaseStream in = (BaseStream)Stream.of(source("spliterator_1")); + out = in.spliterator(); + sink(getElementSpliterator(out)); // $ hasValueFlow=spliterator_1 + } + { + // "java.util.stream;BaseStream;true;unordered;();;Element of Argument[-1];Element of ReturnValue;value" + BaseStream out = null; + BaseStream in = (BaseStream)Stream.of(source("unordered_1")); + out = in.unordered(); + sink(getElement(out)); // $ hasValueFlow=unordered_1 + } + { + // "java.util.stream;Stream;true;concat;(Stream,Stream);;Element of Argument[0..1];Element of ReturnValue;value" + Stream out = null; + Stream in = (Stream)Stream.of(source("concat_1")); + out = Stream.concat(in, null); + sink(getElement(out)); // $ hasValueFlow=concat_1 + } + { + // "java.util.stream;Stream;true;concat;(Stream,Stream);;Element of Argument[0..1];Element of ReturnValue;value" + Stream out = null; + Stream in = (Stream)Stream.of(source("concat_2")); + out = Stream.concat(null, in); + sink(getElement(out)); // $ hasValueFlow=concat_2 + } + { + // "java.util.stream;Stream;true;distinct;();;Element of Argument[-1];Element of ReturnValue;value" + Stream out = null; + Stream in = (Stream)Stream.of(source("distinct_1")); + out = in.distinct(); + sink(getElement(out)); // $ hasValueFlow=distinct_1 + } + { + // "java.util.stream;Stream;true;dropWhile;(Predicate);;Element of Argument[-1];Element of ReturnValue;value" + Stream out = null; + Stream in = (Stream)Stream.of(source("dropWhile_1")); + out = in.dropWhile(null); + sink(getElement(out)); // $ hasValueFlow=dropWhile_1 + } + { + // "java.util.stream;Stream;true;filter;(Predicate);;Element of Argument[-1];Element of ReturnValue;value" + Stream out = null; + Stream in = (Stream)Stream.of(source("filter_1")); + out = in.filter(null); + sink(getElement(out)); // $ hasValueFlow=filter_1 + } + { + // "java.util.stream;Stream;true;findAny;();;Element of Argument[-1];Element of ReturnValue;value" + Optional out = null; + Stream in = (Stream)Stream.of(source("findAny_1")); + out = in.findAny(); + sink(getElement(out)); // $ hasValueFlow=findAny_1 + } + { + // "java.util.stream;Stream;true;findFirst;();;Element of Argument[-1];Element of ReturnValue;value" + Optional out = null; + Stream in = (Stream)Stream.of(source("findFirst_1")); + out = in.findFirst(); + sink(getElement(out)); // $ hasValueFlow=findFirst_1 + } + { + // "java.util.stream;Stream;true;limit;(long);;Element of Argument[-1];Element of ReturnValue;value" + Stream out = null; + Stream in = (Stream)Stream.of(source("limit_1")); + out = in.limit(0L); + sink(getElement(out)); // $ hasValueFlow=limit_1 + } + { + // "java.util.stream;Stream;true;max;(Comparator);;Element of Argument[-1];Element of ReturnValue;value" + Optional out = null; + Stream in = (Stream)Stream.of(source("max_1")); + out = in.max(null); + sink(getElement(out)); // $ hasValueFlow=max_1 + } + { + // "java.util.stream;Stream;true;min;(Comparator);;Element of Argument[-1];Element of ReturnValue;value" + Optional out = null; + Stream in = (Stream)Stream.of(source("min_1")); + out = in.min(null); + sink(getElement(out)); // $ hasValueFlow=min_1 + } + { + // "java.util.stream;Stream;true;of;(Object);;Argument[0];Element of ReturnValue;value" + Stream out = null; + Object in = (Object)source("of_1"); + out = Stream.of(in); + sink(getElement(out)); // $ hasValueFlow=of_1 + } + { + // "java.util.stream;Stream;true;of;(Object[]);;ArrayElement of Argument[0];Element of ReturnValue;value" + Stream out = null; + Object[] in = (Object[])new Object[]{source("of_2")}; + out = Stream.of(in); + sink(getElement(out)); // $ hasValueFlow=of_2 + } + { + // "java.util.stream;Stream;true;ofNullable;(Object);;Argument[0];Element of ReturnValue;value" + Stream out = null; + Object in = (Object)source("ofNullable_1"); + out = Stream.ofNullable(in); + sink(getElement(out)); // $ hasValueFlow=ofNullable_1 + } + { + // "java.util.stream;Stream;true;peek;(Consumer);;Element of Argument[-1];Element of ReturnValue;value" + Stream out = null; + Stream in = (Stream)Stream.of(source("peek_1")); + out = in.peek(null); + sink(getElement(out)); // $ hasValueFlow=peek_1 + } + { + // "java.util.stream;Stream;true;skip;(long);;Element of Argument[-1];Element of ReturnValue;value" + Stream out = null; + Stream in = (Stream)Stream.of(source("skip_1")); + out = in.skip(0L); + sink(getElement(out)); // $ hasValueFlow=skip_1 + } + { + // "java.util.stream;Stream;true;sorted;;;Element of Argument[-1];Element of ReturnValue;value" + Stream out = null; + Stream in = (Stream)Stream.of(source("sorted_1")); + out = in.sorted(); + sink(getElement(out)); // $ hasValueFlow=sorted_1 + } + { + // "java.util.stream;Stream;true;sorted;;;Element of Argument[-1];Element of ReturnValue;value" + Stream out = null; + Stream in = (Stream)Stream.of(source("sorted_2")); + out = in.sorted(null); + sink(getElement(out)); // $ hasValueFlow=sorted_2 + } + { + // "java.util.stream;Stream;true;takeWhile;(Predicate);;Element of Argument[-1];Element of ReturnValue;value" + Stream out = null; + Stream in = (Stream)Stream.of(source("takeWhile_1")); + out = in.takeWhile(null); + sink(getElement(out)); // $ hasValueFlow=takeWhile_1 + } + { + // "java.util.stream;Stream;true;toArray;;;Element of Argument[-1];ArrayElement of ReturnValue;value" + Object[] out = null; + Stream in = (Stream)Stream.of(source("toArray_1")); + out = in.toArray(); + sink(getArrayElement(out)); // $ hasValueFlow=toArray_1 + } + { + // "java.util.stream;Stream;true;toArray;;;Element of Argument[-1];ArrayElement of ReturnValue;value" + Object[] out = null; + Stream in = (Stream)Stream.of(source("toArray_2")); + out = in.toArray(null); + sink(getArrayElement(out)); // $ hasValueFlow=toArray_2 + } + { + // "java.util.stream;Stream;true;toList;();;Element of Argument[-1];Element of ReturnValue;value" + List out = null; + Stream in = (Stream)Stream.of(source("toList_1")); + out = in.toList(); + sink(getElement(out)); // $ hasValueFlow=toList_1 + } + + // *** Handwritten *** + { + //java.util.stream;Stream;true;allMatch;(Predicate);;Element of Argument[-1];Parameter[0] of Argument[0];value + Stream in = Stream.of(source("allMatch")); + in.allMatch(x -> { sink(x); return true; }); // $ hasValueFlow=allMatch + } + { + //java.util.stream;Stream;true;anyMatch;(Predicate);;Element of Argument[-1];Parameter[0] of Argument[0];value + Stream in = Stream.of(source("anyMatch")); + in.anyMatch(x -> { sink(x); return true; }); // $ hasValueFlow=anyMatch + } + { + //java.util.stream;Stream;true;collect;(Supplier,BiConsumer,BiConsumer);;Element of Argument[-1];Parameter[1] of Argument[1];value + Stream in = Stream.of(source("collect")); + in.collect(null, (c,x) -> sink(x), null); // $ hasValueFlow=collect + } + { + //java.util.stream;Stream;true;collect;(Supplier,BiConsumer,BiConsumer);;Parameter[0..1] of Argument[2];Parameter[0] of Argument[1];value + Stream.of(new Object()).collect( + () -> new Object[1], + (a, x) -> sink(a[0]), // $ hasValueFlow=collect_2 hasValueFlow=collect_3 SPURIOUS: hasValueFlow=collect_4 hasValueFlow=collect_5 + (a1, a2) -> { + a1[0] = source("collect_2"); + a2[0] = source("collect_3"); + }); + } + { + //java.util.stream;Stream;true;collect;(Supplier,BiConsumer,BiConsumer);;Parameter[0] of Argument[1];Parameter[0..1] of Argument[2];value + //java.util.stream;Stream;true;collect;(Supplier,BiConsumer,BiConsumer);;Parameter[0] of Argument[1];ReturnValue;value + //java.util.stream;Stream;true;collect;(Supplier,BiConsumer,BiConsumer);;ReturnValue of Argument[0];Parameter[0] of Argument[1];value + Object[] out = Stream.of(new Object()).collect( + () -> new Object[] { source("collect_5") }, + (a, x) -> { + sink(a[0]); // $ hasValueFlow=collect_4 hasValueFlow=collect_5 SPURIOUS: hasValueFlow=collect_2 hasValueFlow=collect_3 + a[0] = source("collect_4"); + }, + (a1, a2) -> { + sink(a1[0]); // $ hasValueFlow=collect_4 hasValueFlow=collect_5 SPURIOUS: hasValueFlow=collect_2 hasValueFlow=collect_3 + sink(a2[0]); // $ hasValueFlow=collect_4 hasValueFlow=collect_5 SPURIOUS: hasValueFlow=collect_2 hasValueFlow=collect_3 + }); + sink(out[0]); // $ hasValueFlow=collect_4 hasValueFlow=collect_5 + } + { + Stream in = Stream.of(source("collect_6")); + Object[] out = in.collect( + () -> new Object[1], + (a, x) -> { a[0] = x; }, + (a1, a2) -> { + a1[0] = a2[0]; + a2[0] = a1[0]; + }); + sink(out[0]); // $ hasValueFlow=collect_6 + } + { + //java.util.stream;Stream;true;dropWhile;(Predicate);;Element of Argument[-1];Parameter[0] of Argument[0];value + Stream in = Stream.of(source("dropWhile")); + in.dropWhile(x -> { sink(x); return true; }); // $ hasValueFlow=dropWhile + } + { + //java.util.stream;Stream;true;filter;(Predicate);;Element of Argument[-1];Parameter[0] of Argument[0];value + Stream in = Stream.of(source("filter")); + in.filter(x -> { sink(x); return true; }); // $ hasValueFlow=filter + } + { + //java.util.stream;Stream;true;flatMap;(Function);;Element of Argument[-1];Parameter[0] of Argument[0];value + Stream in = Stream.of(source("flatMap")); + in.flatMap(x -> { sink(x); return Stream.empty(); }); // $ hasValueFlow=flatMap + } + { + //java.util.stream;Stream;true;flatMap;(Function);;Element of ReturnValue of Argument[0];Element of ReturnValue;value + Stream out = Stream.of(new Object()).flatMap(x -> Stream.of(source("flatMap_2"))); + sink(getElement(out)); // $ hasValueFlow=flatMap_2 + } + { + //java.util.stream;Stream;true;flatMapToDouble;(Function);;Element of Argument[-1];Parameter[0] of Argument[0];value + Stream in = Stream.of(source("flatMapToDouble")); + in.flatMapToDouble(x -> { sink(x); return null; }); // $ hasValueFlow=flatMapToDouble + } + { + //java.util.stream;Stream;true;flatMapToInt;(Function);;Element of Argument[-1];Parameter[0] of Argument[0];value + Stream in = Stream.of(source("flatMapToInt")); + in.flatMapToInt(x -> { sink(x); return null; }); // $ hasValueFlow=flatMapToInt + } + { + //java.util.stream;Stream;true;flatMapToLong;(Function);;Element of Argument[-1];Parameter[0] of Argument[0];value + Stream in = Stream.of(source("flatMapToLong")); + in.flatMapToLong(x -> { sink(x); return null; }); // $ hasValueFlow=flatMapToLong + } + { + //java.util.stream;Stream;true;forEach;(Consumer);;Element of Argument[-1];Parameter[0] of Argument[0];value + Stream in = Stream.of(source("forEach")); + in.forEach(x -> sink(x)); // $ hasValueFlow=forEach + } + { + //java.util.stream;Stream;true;forEachOrdered;(Consumer);;Element of Argument[-1];Parameter[0] of Argument[0];value + Stream in = Stream.of(source("forEachOrdered")); + in.forEachOrdered(x -> sink(x)); // $ hasValueFlow=forEachOrdered + } + { + //java.util.stream;Stream;true;generate;(Supplier);;ReturnValue of Argument[0];Element of ReturnValue;value + Stream out = Stream.generate(() -> source("generate")); + sink(getElement(out)); // $ hasValueFlow=generate + } + { + // "java.util.stream;Stream;true;iterate;(Object,Predicate,UnaryOperator);;Argument[0];Element of ReturnValue;value" + //java.util.stream;Stream;true;iterate;(Object,Predicate,UnaryOperator);;Argument[0];Parameter[0] of Argument[1..2];value + //java.util.stream;Stream;true;iterate;(Object,Predicate,UnaryOperator);;ReturnValue of Argument[2];Element of ReturnValue;value + //java.util.stream;Stream;true;iterate;(Object,Predicate,UnaryOperator);;ReturnValue of Argument[2];Parameter[0] of Argument[1..2];value + Stream out = null; + Object in = (Object)source("iterate_1"); + out = Stream.iterate(in, x -> { + sink(x); // $ hasValueFlow=iterate_1 hasValueFlow=iterate_2 + return true; + }, x -> { + sink(x); // $ hasValueFlow=iterate_1 hasValueFlow=iterate_2 + return source("iterate_2"); + }); + sink(getElement(out)); // $ hasValueFlow=iterate_1 hasValueFlow=iterate_2 + } + { + // "java.util.stream;Stream;true;iterate;(Object,UnaryOperator);;Argument[0];Element of ReturnValue;value" + //java.util.stream;Stream;true;iterate;(Object,UnaryOperator);;Argument[0];Parameter[0] of Argument[1];value + //java.util.stream;Stream;true;iterate;(Object,UnaryOperator);;ReturnValue of Argument[1];Element of ReturnValue;value + //java.util.stream;Stream;true;iterate;(Object,UnaryOperator);;ReturnValue of Argument[1];Parameter[0] of Argument[1];value + Stream out = null; + Object in = (Object)source("iterate_3"); + out = Stream.iterate(in, x -> { + sink(x); // $ hasValueFlow=iterate_3 hasValueFlow=iterate_4 + return source("iterate_4"); + }); + sink(getElement(out)); // $ hasValueFlow=iterate_3 hasValueFlow=iterate_4 + } + { + //java.util.stream;Stream;true;map;(Function);;Element of Argument[-1];Parameter[0] of Argument[0];value + //java.util.stream;Stream;true;map;(Function);;ReturnValue of Argument[0];Element of ReturnValue;value + Stream in = Stream.of(source("map_1")); + Stream out = in.map(x -> { sink(x); return source("map_2"); }); // $ hasValueFlow=map_1 + sink(getElement(out)); // $ hasValueFlow=map_2 + } + { + //java.util.stream;Stream;true;mapMulti;(BiConsumer);;Element of Argument[-1];Parameter[0] of Argument[0];value + Stream in = Stream.of(source("mapMulti")); + in.mapMulti((x, consumer) -> sink(x)); // $ hasValueFlow=mapMulti + } + { + //java.util.stream;Stream;true;mapMultiToDouble;(BiConsumer);;Element of Argument[-1];Parameter[0] of Argument[0];value + Stream in = Stream.of(source("mapMultiToDouble")); + in.mapMultiToDouble((x, consumer) -> sink(x)); // $ hasValueFlow=mapMultiToDouble + } + { + //java.util.stream;Stream;true;mapMultiToInt;(BiConsumer);;Element of Argument[-1];Parameter[0] of Argument[0];value + Stream in = Stream.of(source("mapMultiToInt")); + in.mapMultiToInt((x, consumer) -> sink(x)); // $ hasValueFlow=mapMultiToInt + } + { + //java.util.stream;Stream;true;mapMultiToLong;(BiConsumer);;Element of Argument[-1];Parameter[0] of Argument[0];value + Stream in = Stream.of(source("mapMultiToLong")); + in.mapMultiToLong((x, consumer) -> sink(x)); // $ hasValueFlow=mapMultiToLong + } + { + //java.util.stream;Stream;true;mapToDouble;(ToDoubleFunction);;Element of Argument[-1];Parameter[0] of Argument[0];value + Stream in = Stream.of(source("mapToDouble")); + in.mapToDouble(x -> { sink(x); return 0.0; }); // $ hasValueFlow=mapToDouble + } + { + //java.util.stream;Stream;true;mapToInt;(ToIntFunction);;Element of Argument[-1];Parameter[0] of Argument[0];value + Stream in = Stream.of(source("mapToInt")); + in.mapToInt(x -> { sink(x); return 0; }); // $ hasValueFlow=mapToInt + } + { + //java.util.stream;Stream;true;mapToLong;(ToLongFunction);;Element of Argument[-1];Parameter[0] of Argument[0];value + Stream in = Stream.of(source("mapToLong")); + in.mapToLong(x -> { sink(x); return 0; }); // $ hasValueFlow=mapToLong + } + { + //java.util.stream;Stream;true;max;(Comparator);;Element of Argument[-1];Parameter[0..1] of Argument[0];value + Stream in = Stream.of(source("max")); + in.max((x,y) -> { sink(x); return 0; }); // $ hasValueFlow=max + in.max((x,y) -> { sink(y); return 0; }); // $ hasValueFlow=max + } + { + //java.util.stream;Stream;true;min;(Comparator);;Element of Argument[-1];Parameter[0..1] of Argument[0];value + Stream in = Stream.of(source("min")); + in.min((x,y) -> { sink(x); return 0; }); // $ hasValueFlow=min + in.min((x,y) -> { sink(y); return 0; }); // $ hasValueFlow=min + } + { + //java.util.stream;Stream;true;noneMatch;(Predicate);;Element of Argument[-1];Parameter[0] of Argument[0];value + Stream in = Stream.of(source("noneMatch")); + in.noneMatch(x -> { sink(x); return true; }); // $ hasValueFlow=noneMatch + } + { + //java.util.stream;Stream;true;peek;(Consumer);;Element of Argument[-1];Parameter[0] of Argument[0];value + Stream in = Stream.of(source("peek")); + in.peek(x -> sink(x)); // $ hasValueFlow=peek + } + { + // "java.util.stream;Stream;true;reduce;(BinaryOperator);;Element of Argument[-1];Element of ReturnValue;value" + //java.util.stream;Stream;true;reduce;(BinaryOperator);;Element of Argument[-1];Parameter[0..1] of Argument[0];value + //java.util.stream;Stream;true;reduce;(BinaryOperator);;ReturnValue of Argument[0];Element of ReturnValue;value + //java.util.stream;Stream;true;reduce;(BinaryOperator);;ReturnValue of Argument[0];Parameter[0..1] of Argument[0];value + Stream in = Stream.of(source("reduce_1")); + Optional out = in.reduce((x,y) -> { + sink(x); // $ hasValueFlow=reduce_1 hasValueFlow=reduce_2 + sink(y); // $ hasValueFlow=reduce_1 hasValueFlow=reduce_2 + return source("reduce_2"); + }); + sink(getElement(out)); // $ hasValueFlow=reduce_1 hasValueFlow=reduce_2 + } + { + // "java.util.stream;Stream;true;reduce;(Object,BinaryOperator);;Argument[0];ReturnValue;value" + //java.util.stream;Stream;true;reduce;(Object,BinaryOperator);;Argument[0];Parameter[0..1] of Argument[1];value + //java.util.stream;Stream;true;reduce;(Object,BinaryOperator);;Element of Argument[-1];Parameter[0..1] of Argument[1];value + //java.util.stream;Stream;true;reduce;(Object,BinaryOperator);;ReturnValue of Argument[1];Parameter[0..1] of Argument[1];value + //java.util.stream;Stream;true;reduce;(Object,BinaryOperator);;ReturnValue of Argument[1];ReturnValue;value + Stream in = Stream.of(source("reduce_3")); + Object out = in.reduce(source("reduce_4"), (x,y) -> { + sink(x); // $ hasValueFlow=reduce_3 hasValueFlow=reduce_4 hasValueFlow=reduce_5 + sink(y); // $ hasValueFlow=reduce_3 hasValueFlow=reduce_4 hasValueFlow=reduce_5 + return source("reduce_5"); + }); + sink(out); // $ hasValueFlow=reduce_4 hasValueFlow=reduce_5 SPURIOUS: hasValueFlow=reduce_3 + } + { + // "java.util.stream;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Argument[0];ReturnValue;value" + //java.util.stream;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Argument[0];Parameter[0..1] of Argument[2];value + //java.util.stream;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Argument[0];Parameter[0] of Argument[1];value + //java.util.stream;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Element of Argument[-1];Parameter[1] of Argument[1];value + //java.util.stream;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;ReturnValue of Argument[1..2];Parameter[0..1] of Argument[2];value + //java.util.stream;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;ReturnValue of Argument[1..2];Parameter[0] of Argument[1];value + //java.util.stream;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;ReturnValue of Argument[1..2];ReturnValue;value + Stream in = Stream.of(source("reduce_6")); + Object out = in.reduce(source("reduce_7"), (x,y) -> { + sink(x); // $ hasValueFlow=reduce_7 hasValueFlow=reduce_8 hasValueFlow=reduce_9 + sink(y); // $ hasValueFlow=reduce_6 + return source("reduce_8"); + }, (x,y) -> { + sink(x); // $ hasValueFlow=reduce_7 hasValueFlow=reduce_8 hasValueFlow=reduce_9 + sink(y); // $ hasValueFlow=reduce_7 hasValueFlow=reduce_8 hasValueFlow=reduce_9 + return source("reduce_9"); + }); + sink(out); // $ hasValueFlow=reduce_7 hasValueFlow=reduce_8 hasValueFlow=reduce_9 + } + { + //java.util.stream;Stream;true;sorted;(Comparator);;Element of Argument[-1];Parameter[0..1] of Argument[0];value + Stream in = Stream.of(source("sorted")); + in.sorted((x,y) -> { sink(x); return 0; }); // $ hasValueFlow=sorted + in.sorted((x,y) -> { sink(y); return 0; }); // $ hasValueFlow=sorted + } + { + //java.util.stream;Stream;true;takeWhile;(Predicate);;Element of Argument[-1];Parameter[0] of Argument[0];value + Stream in = Stream.of(source("takeWhile")); + in.takeWhile(x -> { sink(x); return true; }); // $ hasValueFlow=takeWhile + } + + } + +} diff --git a/java/ql/test/library-tests/frameworks/stream/test.expected b/java/ql/test/library-tests/frameworks/stream/test.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/java/ql/test/library-tests/frameworks/stream/test.ql b/java/ql/test/library-tests/frameworks/stream/test.ql new file mode 100644 index 00000000000..b9baef32869 --- /dev/null +++ b/java/ql/test/library-tests/frameworks/stream/test.ql @@ -0,0 +1,9 @@ +import java +import TestUtilities.InlineFlowTest + +class SummaryModelTest extends SummaryModelCsv { + override predicate row(string row) { + row = + "generatedtest;Test;false;getElementSpliterator;(Spliterator);;Element of Argument[0];ReturnValue;value" + } +} diff --git a/java/ql/test/library-tests/literals/booleanLiterals/BooleanLiterals.java b/java/ql/test/library-tests/literals/booleanLiterals/BooleanLiterals.java new file mode 100644 index 00000000000..feff9a908e4 --- /dev/null +++ b/java/ql/test/library-tests/literals/booleanLiterals/BooleanLiterals.java @@ -0,0 +1,31 @@ +package booleanLiterals; + +public class BooleanLiterals { + boolean[] booleans = { + true, + false, + // Using Unicode escapes (which are handled during pre-processing) + \u0074\u0072\u0075\u0065, // true + }; + + // The operation expression (e.g. `&&`) is not a literal + boolean[] logicOperations = { + true && true, + false && false, + true && false, + true || true, + false || false, + true || false, + }; + + Object[] nonBooleanLiterals = { + "true", + "false", + 1, + 0, + Boolean.TRUE, + Boolean.FALSE, + }; + + boolean nonLiteral; +} diff --git a/java/ql/test/library-tests/literals/booleanLiterals/booleanLiterals.expected b/java/ql/test/library-tests/literals/booleanLiterals/booleanLiterals.expected new file mode 100644 index 00000000000..75acbb8d07f --- /dev/null +++ b/java/ql/test/library-tests/literals/booleanLiterals/booleanLiterals.expected @@ -0,0 +1,15 @@ +| BooleanLiterals.java:5:3:5:6 | true | true | true | +| BooleanLiterals.java:6:3:6:7 | false | false | false | +| BooleanLiterals.java:8:8:8:26 | 4\\u0072\\u0075\\u0065 | true | true | +| BooleanLiterals.java:13:3:13:6 | true | true | true | +| BooleanLiterals.java:13:11:13:14 | true | true | true | +| BooleanLiterals.java:14:3:14:7 | false | false | false | +| BooleanLiterals.java:14:12:14:16 | false | false | false | +| BooleanLiterals.java:15:3:15:6 | true | true | true | +| BooleanLiterals.java:15:11:15:15 | false | false | false | +| BooleanLiterals.java:16:3:16:6 | true | true | true | +| BooleanLiterals.java:16:11:16:14 | true | true | true | +| BooleanLiterals.java:17:3:17:7 | false | false | false | +| BooleanLiterals.java:17:12:17:16 | false | false | false | +| BooleanLiterals.java:18:3:18:6 | true | true | true | +| BooleanLiterals.java:18:11:18:15 | false | false | false | diff --git a/java/ql/test/library-tests/literals/literalBoolean.ql b/java/ql/test/library-tests/literals/booleanLiterals/booleanLiterals.ql similarity index 100% rename from java/ql/test/library-tests/literals/literalBoolean.ql rename to java/ql/test/library-tests/literals/booleanLiterals/booleanLiterals.ql diff --git a/java/ql/test/library-tests/literals/charLiterals/CharLiterals.java b/java/ql/test/library-tests/literals/charLiterals/CharLiterals.java new file mode 100644 index 00000000000..51274f9899e --- /dev/null +++ b/java/ql/test/library-tests/literals/charLiterals/CharLiterals.java @@ -0,0 +1,44 @@ +package charLiterals; + +public class CharLiterals { + char[] chars = { + 'a', + '\u0061', // 'a' + '\u0000', + '\uFFFF', + '\ufFfF', + '\0', + '\n', + '"', + '\\', + '\'', + '\123', // octal escape sequence for 'S' + '\uD800', // high surrogate + '\uDC00', // low surrogate + // Using Unicode escapes (which are handled during pre-processing) + '\u005C\u005C', // escaped backslash + '\u005C\u0027', // escaped single quote + \u0027a\u0027, // 'a' + }; + + // + and - are not part of the literal + int[] charsWithSign = { + +'a', + -'a', + }; + + // The operation expression (e.g. `+`) is not a literal + int[] numericOperations = { + 'a' + 'b', + }; + + Object[] nonCharLiterals = { + "a", + "", + "\uD800\uDC00", // surrogate pair + 0, + Character.MIN_VALUE, + }; + + char nonLiteral; +} diff --git a/java/ql/test/library-tests/literals/charLiterals/charLiterals.expected b/java/ql/test/library-tests/literals/charLiterals/charLiterals.expected new file mode 100644 index 00000000000..987f7c17fe4 --- /dev/null +++ b/java/ql/test/library-tests/literals/charLiterals/charLiterals.expected @@ -0,0 +1,20 @@ +| CharLiterals.java:5:3:5:5 | 'a' | a | +| CharLiterals.java:6:3:6:10 | '\\u0061' | a | +| CharLiterals.java:7:3:7:10 | '\\u0000' | \u0000 | +| CharLiterals.java:8:3:8:10 | '\\uFFFF' | \uffff | +| CharLiterals.java:9:3:9:10 | '\\ufFfF' | \uffff | +| CharLiterals.java:10:3:10:6 | '\\0' | \u0000 | +| CharLiterals.java:11:3:11:6 | '\\n' | \n | +| CharLiterals.java:12:3:12:5 | '"' | " | +| CharLiterals.java:13:3:13:6 | '\\\\' | \\ | +| CharLiterals.java:14:3:14:6 | '\\'' | ' | +| CharLiterals.java:15:3:15:8 | '\\123' | S | +| CharLiterals.java:16:3:16:10 | '\\uD800' | \ufffd | +| CharLiterals.java:17:3:17:10 | '\\uDC00' | \ufffd | +| CharLiterals.java:19:3:19:16 | '\\u005C\\u005C' | \\ | +| CharLiterals.java:20:3:20:16 | '\\u005C\\u0027' | ' | +| CharLiterals.java:21:8:21:15 | 7a\\u0027 | a | +| CharLiterals.java:26:4:26:6 | 'a' | a | +| CharLiterals.java:27:4:27:6 | 'a' | a | +| CharLiterals.java:32:3:32:5 | 'a' | a | +| CharLiterals.java:32:9:32:11 | 'b' | b | diff --git a/java/ql/test/library-tests/literals/literalChar.ql b/java/ql/test/library-tests/literals/charLiterals/charLiterals.ql similarity index 100% rename from java/ql/test/library-tests/literals/literalChar.ql rename to java/ql/test/library-tests/literals/charLiterals/charLiterals.ql diff --git a/java/ql/test/library-tests/literals/doubleLiterals/DoubleLiterals.java b/java/ql/test/library-tests/literals/doubleLiterals/DoubleLiterals.java new file mode 100644 index 00000000000..bf437c31a6a --- /dev/null +++ b/java/ql/test/library-tests/literals/doubleLiterals/DoubleLiterals.java @@ -0,0 +1,50 @@ +package doubleLiterals; + +public class DoubleLiterals { + double[] doubles = { + 0.0, + 0d, + 0D, + .0d, + .0, + 0., + 1.234567890123456789, + 1.55555555555555555555, + // From the JLS + 1e1, + 1.7976931348623157E308, + 0x1.f_ffff_ffff_ffffP+1023, + 4.9e-324, + 0x0.0_0000_0000_0001P-1022, + 0x1.0P-1074, + // Using Unicode escapes (which are handled during pre-processing) + \u0030\u002E\u0030, // 0.0 + }; + + // + and - are not part of the literal + double[] doublesWithSign = { + +0.0, + -0.0, + +1.0, + -1.0, + +1.7976931348623157E308, + -1.7976931348623157E308, + }; + + // The operation expression (e.g. `+`) is not a literal + double[] numericOperations = { + 0.0 + 0.0, + 0.0 / 0.0, + }; + + Object[] nonDoubleLiterals = { + "0", + '0', + 0, + 0.0f, + (double) 0.0f, + Double.MIN_VALUE, + }; + + double nonLiteral; +} diff --git a/java/ql/test/library-tests/literals/doubleLiterals/doubleLiterals.expected b/java/ql/test/library-tests/literals/doubleLiterals/doubleLiterals.expected new file mode 100644 index 00000000000..574ba90a98f --- /dev/null +++ b/java/ql/test/library-tests/literals/doubleLiterals/doubleLiterals.expected @@ -0,0 +1,25 @@ +| DoubleLiterals.java:5:3:5:5 | 0.0 | 0.0 | 0.0 | +| DoubleLiterals.java:6:3:6:4 | 0d | 0.0 | 0.0 | +| DoubleLiterals.java:7:3:7:4 | 0D | 0.0 | 0.0 | +| DoubleLiterals.java:8:3:8:5 | .0d | 0.0 | 0.0 | +| DoubleLiterals.java:9:3:9:4 | .0 | 0.0 | 0.0 | +| DoubleLiterals.java:10:3:10:4 | 0. | 0.0 | 0.0 | +| DoubleLiterals.java:11:3:11:22 | 1.234567890123456789 | 1.2345678901234567 | 1.2345678901234567 | +| DoubleLiterals.java:12:3:12:24 | 1.55555555555555555555 | 1.5555555555555556 | 1.5555555555555556 | +| DoubleLiterals.java:14:3:14:5 | 1e1 | 10.0 | 10.0 | +| DoubleLiterals.java:15:3:15:24 | 1.7976931348623157E308 | 1.7976931348623157E308 | 1.7976931348623157E308 | +| DoubleLiterals.java:16:3:16:28 | 0x1.f_ffff_ffff_ffffP+1023 | 1.7976931348623157E308 | 1.7976931348623157E308 | +| DoubleLiterals.java:17:3:17:10 | 4.9e-324 | 4.9E-324 | 4.9E-324 | +| DoubleLiterals.java:18:3:18:28 | 0x0.0_0000_0000_0001P-1022 | 4.9E-324 | 4.9E-324 | +| DoubleLiterals.java:19:3:19:13 | 0x1.0P-1074 | 4.9E-324 | 4.9E-324 | +| DoubleLiterals.java:21:8:21:20 | 0\\u002E\\u0030 | 0.0 | 0.0 | +| DoubleLiterals.java:26:4:26:6 | 0.0 | 0.0 | 0.0 | +| DoubleLiterals.java:27:4:27:6 | 0.0 | 0.0 | 0.0 | +| DoubleLiterals.java:28:4:28:6 | 1.0 | 1.0 | 1.0 | +| DoubleLiterals.java:29:4:29:6 | 1.0 | 1.0 | 1.0 | +| DoubleLiterals.java:30:4:30:25 | 1.7976931348623157E308 | 1.7976931348623157E308 | 1.7976931348623157E308 | +| DoubleLiterals.java:31:4:31:25 | 1.7976931348623157E308 | 1.7976931348623157E308 | 1.7976931348623157E308 | +| DoubleLiterals.java:36:3:36:5 | 0.0 | 0.0 | 0.0 | +| DoubleLiterals.java:36:9:36:11 | 0.0 | 0.0 | 0.0 | +| DoubleLiterals.java:37:3:37:5 | 0.0 | 0.0 | 0.0 | +| DoubleLiterals.java:37:9:37:11 | 0.0 | 0.0 | 0.0 | diff --git a/java/ql/test/library-tests/literals/literalDouble.ql b/java/ql/test/library-tests/literals/doubleLiterals/doubleLiterals.ql similarity index 100% rename from java/ql/test/library-tests/literals/literalDouble.ql rename to java/ql/test/library-tests/literals/doubleLiterals/doubleLiterals.ql diff --git a/java/ql/test/library-tests/literals/floatLiterals/FloatLiterals.java b/java/ql/test/library-tests/literals/floatLiterals/FloatLiterals.java new file mode 100644 index 00000000000..fc9722d1052 --- /dev/null +++ b/java/ql/test/library-tests/literals/floatLiterals/FloatLiterals.java @@ -0,0 +1,49 @@ +package floatLiterals; + +public class FloatLiterals { + float[] floats = { + 0.0f, + 0f, + .0f, + 0.f, + 1_0_0.0f, + 1.234567890123456789f, + 1.55555555555555555555f, + // From the JLS + 1e1f, + 3.4028235e38f, + 0x1.fffffeP+127f, + 1.4e-45f, + 0x0.000002P-126f, + 0x1.0P-149f, + // Using Unicode escapes (which are handled during pre-processing) + \u0030\u002E\u0030\u0066, // 0.0f + }; + + // + and - are not part of the literal + float[] floatsWithSign = { + +0.f, + -0.f, + +1.0f, + -1.0f, + +3.4028235e38f, + -3.4028235e38f, + }; + + // The operation expression (e.g. `+`) is not a literal + float[] numericOperations = { + 0.0f + 0.0f, + 0.0f / 0.0f, + }; + + Object[] nonFloatLiterals = { + "0f", + '0', + 0, + 0.0, + (float) 0.0, + Float.MIN_VALUE, + }; + + float nonLiteral; +} diff --git a/java/ql/test/library-tests/literals/floatLiterals/floatLiterals.expected b/java/ql/test/library-tests/literals/floatLiterals/floatLiterals.expected new file mode 100644 index 00000000000..c1a499f0f52 --- /dev/null +++ b/java/ql/test/library-tests/literals/floatLiterals/floatLiterals.expected @@ -0,0 +1,24 @@ +| FloatLiterals.java:5:3:5:6 | 0.0f | 0.0 | 0.0 | +| FloatLiterals.java:6:3:6:4 | 0f | 0.0 | 0.0 | +| FloatLiterals.java:7:3:7:5 | .0f | 0.0 | 0.0 | +| FloatLiterals.java:8:3:8:5 | 0.f | 0.0 | 0.0 | +| FloatLiterals.java:9:3:9:10 | 1_0_0.0f | 100.0 | 100.0 | +| FloatLiterals.java:10:3:10:23 | 1.234567890123456789f | 1.2345679 | 1.2345679 | +| FloatLiterals.java:11:3:11:25 | 1.55555555555555555555f | 1.5555556 | 1.5555556 | +| FloatLiterals.java:13:3:13:6 | 1e1f | 10.0 | 10.0 | +| FloatLiterals.java:14:3:14:15 | 3.4028235e38f | 3.4028235E38 | 3.4028235E38 | +| FloatLiterals.java:15:3:15:18 | 0x1.fffffeP+127f | 3.4028235E38 | 3.4028235E38 | +| FloatLiterals.java:16:3:16:10 | 1.4e-45f | 1.4E-45 | 1.4E-45 | +| FloatLiterals.java:17:3:17:18 | 0x0.000002P-126f | 1.4E-45 | 1.4E-45 | +| FloatLiterals.java:18:3:18:13 | 0x1.0P-149f | 1.4E-45 | 1.4E-45 | +| FloatLiterals.java:20:8:20:26 | 0\\u002E\\u0030\\u0066 | 0.0 | 0.0 | +| FloatLiterals.java:25:4:25:6 | 0.f | 0.0 | 0.0 | +| FloatLiterals.java:26:4:26:6 | 0.f | 0.0 | 0.0 | +| FloatLiterals.java:27:4:27:7 | 1.0f | 1.0 | 1.0 | +| FloatLiterals.java:28:4:28:7 | 1.0f | 1.0 | 1.0 | +| FloatLiterals.java:29:4:29:16 | 3.4028235e38f | 3.4028235E38 | 3.4028235E38 | +| FloatLiterals.java:30:4:30:16 | 3.4028235e38f | 3.4028235E38 | 3.4028235E38 | +| FloatLiterals.java:35:3:35:6 | 0.0f | 0.0 | 0.0 | +| FloatLiterals.java:35:10:35:13 | 0.0f | 0.0 | 0.0 | +| FloatLiterals.java:36:3:36:6 | 0.0f | 0.0 | 0.0 | +| FloatLiterals.java:36:10:36:13 | 0.0f | 0.0 | 0.0 | diff --git a/java/ql/test/library-tests/literals/literalFloat.ql b/java/ql/test/library-tests/literals/floatLiterals/floatLiterals.ql similarity index 100% rename from java/ql/test/library-tests/literals/literalFloat.ql rename to java/ql/test/library-tests/literals/floatLiterals/floatLiterals.ql diff --git a/java/ql/test/library-tests/literals/integerLiterals/IntegerLiterals.java b/java/ql/test/library-tests/literals/integerLiterals/IntegerLiterals.java new file mode 100644 index 00000000000..965e3238c1f --- /dev/null +++ b/java/ql/test/library-tests/literals/integerLiterals/IntegerLiterals.java @@ -0,0 +1,57 @@ +package integerLiterals; + +public class IntegerLiterals { + int[] ints = { + 0, + 1, + 0_0, + 0___0, + 0_12, // octal + 0X012, // hex + 0xaBcDeF, // hex + 0B11, // binary + 0x80000000, + 2147483647, + -2147483648, // special case: sign is part of the literal + // From the JLS + 0x7fff_ffff, + 0177_7777_7777, // octal + 0b0111_1111_1111_1111_1111_1111_1111_1111, // binary + 0x8000_0000, + 0200_0000_0000, + 0b1000_0000_0000_0000_0000_0000_0000_0000, + 0xffff_ffff, + 0377_7777_7777, + 0b1111_1111_1111_1111_1111_1111_1111_1111, + // Using Unicode escapes (which are handled during pre-processing) + \u0030, // 0 + }; + + // + and - are not part of the literal + int[] intsWithSign = { + +0, + -0, + +1, + -1, + +2147483647, + }; + + // The operation expression (e.g. `+`) is not a literal + int[] numericOperations = { + 1 + 1, + 0 / 0, + }; + + Object[] nonIntLiterals = { + "0", + '0', + 0L, + (int) 0L, + 0.0, + (int) 0.0, + Integer.MIN_VALUE, + 'a' + 'b', // result is int 195, but this is not a literal + }; + + int nonLiteral; +} diff --git a/java/ql/test/library-tests/literals/integerLiterals/integerLiterals.expected b/java/ql/test/library-tests/literals/integerLiterals/integerLiterals.expected new file mode 100644 index 00000000000..3ff7870ca66 --- /dev/null +++ b/java/ql/test/library-tests/literals/integerLiterals/integerLiterals.expected @@ -0,0 +1,30 @@ +| IntegerLiterals.java:5:3:5:3 | 0 | 0 | 0 | +| IntegerLiterals.java:6:3:6:3 | 1 | 1 | 1 | +| IntegerLiterals.java:7:3:7:5 | 0_0 | 0 | 0 | +| IntegerLiterals.java:8:3:8:7 | 0___0 | 0 | 0 | +| IntegerLiterals.java:9:3:9:6 | 0_12 | 10 | 10 | +| IntegerLiterals.java:10:3:10:7 | 0X012 | 18 | 18 | +| IntegerLiterals.java:11:3:11:10 | 0xaBcDeF | 11259375 | 11259375 | +| IntegerLiterals.java:12:3:12:6 | 0B11 | 3 | 3 | +| IntegerLiterals.java:13:3:13:12 | 0x80000000 | -2147483648 | -2147483648 | +| IntegerLiterals.java:14:3:14:12 | 2147483647 | 2147483647 | 2147483647 | +| IntegerLiterals.java:15:3:15:13 | -2147483648 | -2147483648 | -2147483648 | +| IntegerLiterals.java:17:3:17:13 | 0x7fff_ffff | 2147483647 | 2147483647 | +| IntegerLiterals.java:18:3:18:16 | 0177_7777_7777 | 2147483647 | 2147483647 | +| IntegerLiterals.java:19:3:19:43 | 0b0111_1111_1111_1111_1111_1111_1111_1111 | 2147483647 | 2147483647 | +| IntegerLiterals.java:20:3:20:13 | 0x8000_0000 | -2147483648 | -2147483648 | +| IntegerLiterals.java:21:3:21:16 | 0200_0000_0000 | -2147483648 | -2147483648 | +| IntegerLiterals.java:22:3:22:43 | 0b1000_0000_0000_0000_0000_0000_0000_0000 | -2147483648 | -2147483648 | +| IntegerLiterals.java:23:3:23:13 | 0xffff_ffff | -1 | -1 | +| IntegerLiterals.java:24:3:24:16 | 0377_7777_7777 | -1 | -1 | +| IntegerLiterals.java:25:3:25:43 | 0b1111_1111_1111_1111_1111_1111_1111_1111 | -1 | -1 | +| IntegerLiterals.java:27:8:27:8 | 0 | 0 | 0 | +| IntegerLiterals.java:32:4:32:4 | 0 | 0 | 0 | +| IntegerLiterals.java:33:4:33:4 | 0 | 0 | 0 | +| IntegerLiterals.java:34:4:34:4 | 1 | 1 | 1 | +| IntegerLiterals.java:35:4:35:4 | 1 | 1 | 1 | +| IntegerLiterals.java:36:4:36:13 | 2147483647 | 2147483647 | 2147483647 | +| IntegerLiterals.java:41:3:41:3 | 1 | 1 | 1 | +| IntegerLiterals.java:41:7:41:7 | 1 | 1 | 1 | +| IntegerLiterals.java:42:3:42:3 | 0 | 0 | 0 | +| IntegerLiterals.java:42:7:42:7 | 0 | 0 | 0 | diff --git a/java/ql/test/library-tests/literals/literalInteger.ql b/java/ql/test/library-tests/literals/integerLiterals/integerLiterals.ql similarity index 100% rename from java/ql/test/library-tests/literals/literalInteger.ql rename to java/ql/test/library-tests/literals/integerLiterals/integerLiterals.ql diff --git a/java/ql/test/library-tests/literals/literalBoolean.expected b/java/ql/test/library-tests/literals/literalBoolean.expected deleted file mode 100644 index bfc9f1a043f..00000000000 --- a/java/ql/test/library-tests/literals/literalBoolean.expected +++ /dev/null @@ -1,3 +0,0 @@ -| literals/Literals.java:11:22:11:25 | true | true | true | -| literals/Literals.java:16:3:16:6 | true | true | true | -| literals/Literals.java:17:3:17:7 | false | false | false | diff --git a/java/ql/test/library-tests/literals/literalChar.expected b/java/ql/test/library-tests/literals/literalChar.expected deleted file mode 100644 index 00eb45eff57..00000000000 --- a/java/ql/test/library-tests/literals/literalChar.expected +++ /dev/null @@ -1,10 +0,0 @@ -| literals/Literals.java:12:22:12:24 | 'x' | x | -| literals/Literals.java:21:3:21:5 | 'a' | a | -| literals/Literals.java:22:3:22:10 | '\\u0061' | a | -| literals/Literals.java:23:3:23:10 | '\\u0000' | \u0000 | -| literals/Literals.java:24:3:24:6 | '\\0' | \u0000 | -| literals/Literals.java:25:3:25:6 | '\\n' | \n | -| literals/Literals.java:26:3:26:6 | '\\0' | \u0000 | -| literals/Literals.java:27:3:27:6 | '\\\\' | \\ | -| literals/Literals.java:28:3:28:6 | '\\'' | ' | -| literals/Literals.java:29:3:29:8 | '\\123' | S | diff --git a/java/ql/test/library-tests/literals/literalDouble.expected b/java/ql/test/library-tests/literals/literalDouble.expected deleted file mode 100644 index ba64bdd8446..00000000000 --- a/java/ql/test/library-tests/literals/literalDouble.expected +++ /dev/null @@ -1,16 +0,0 @@ -| literals/Literals.java:10:22:10:27 | 456.0D | 456.0 | 456.0 | -| literals/Literals.java:33:3:33:5 | 0.0 | 0.0 | 0.0 | -| literals/Literals.java:34:3:34:4 | 0d | 0.0 | 0.0 | -| literals/Literals.java:35:3:35:5 | .0d | 0.0 | 0.0 | -| literals/Literals.java:36:3:36:4 | .0 | 0.0 | 0.0 | -| literals/Literals.java:37:4:37:6 | 0.d | 0.0 | 0.0 | -| literals/Literals.java:38:4:38:6 | 0.d | 0.0 | 0.0 | -| literals/Literals.java:39:3:39:22 | 1.234567890123456789 | 1.2345678901234567 | 1.2345678901234567 | -| literals/Literals.java:40:3:40:24 | 1.55555555555555555555 | 1.5555555555555556 | 1.5555555555555556 | -| literals/Literals.java:42:3:42:5 | 1e1 | 10.0 | 10.0 | -| literals/Literals.java:43:3:43:24 | 1.7976931348623157E308 | 1.7976931348623157E308 | 1.7976931348623157E308 | -| literals/Literals.java:44:4:44:25 | 1.7976931348623157E308 | 1.7976931348623157E308 | 1.7976931348623157E308 | -| literals/Literals.java:45:3:45:28 | 0x1.f_ffff_ffff_ffffP+1023 | 1.7976931348623157E308 | 1.7976931348623157E308 | -| literals/Literals.java:46:3:46:10 | 4.9e-324 | 4.9E-324 | 4.9E-324 | -| literals/Literals.java:47:3:47:28 | 0x0.0_0000_0000_0001P-1022 | 4.9E-324 | 4.9E-324 | -| literals/Literals.java:48:3:48:13 | 0x1.0P-1074 | 4.9E-324 | 4.9E-324 | diff --git a/java/ql/test/library-tests/literals/literalFloat.expected b/java/ql/test/library-tests/literals/literalFloat.expected deleted file mode 100644 index f6221533fdb..00000000000 --- a/java/ql/test/library-tests/literals/literalFloat.expected +++ /dev/null @@ -1,16 +0,0 @@ -| literals/Literals.java:9:22:9:27 | 123.0F | 123.0 | 123.0 | -| literals/Literals.java:52:3:52:6 | 0.0f | 0.0 | 0.0 | -| literals/Literals.java:53:3:53:4 | 0f | 0.0 | 0.0 | -| literals/Literals.java:54:3:54:5 | .0f | 0.0 | 0.0 | -| literals/Literals.java:55:4:55:6 | 0.f | 0.0 | 0.0 | -| literals/Literals.java:56:4:56:6 | 0.f | 0.0 | 0.0 | -| literals/Literals.java:57:3:57:10 | 1_0_0.0f | 100.0 | 100.0 | -| literals/Literals.java:58:3:58:23 | 1.234567890123456789f | 1.2345679 | 1.2345679 | -| literals/Literals.java:59:3:59:25 | 1.55555555555555555555f | 1.5555556 | 1.5555556 | -| literals/Literals.java:61:3:61:6 | 1e1f | 10.0 | 10.0 | -| literals/Literals.java:62:3:62:15 | 3.4028235e38f | 3.4028235E38 | 3.4028235E38 | -| literals/Literals.java:63:4:63:16 | 3.4028235e38f | 3.4028235E38 | 3.4028235E38 | -| literals/Literals.java:64:3:64:18 | 0x1.fffffeP+127f | 3.4028235E38 | 3.4028235E38 | -| literals/Literals.java:65:3:65:10 | 1.4e-45f | 1.4E-45 | 1.4E-45 | -| literals/Literals.java:66:3:66:18 | 0x0.000002P-126f | 1.4E-45 | 1.4E-45 | -| literals/Literals.java:67:3:67:13 | 0x1.0P-149f | 1.4E-45 | 1.4E-45 | diff --git a/java/ql/test/library-tests/literals/literalInteger.expected b/java/ql/test/library-tests/literals/literalInteger.expected deleted file mode 100644 index 40c6a89cd98..00000000000 --- a/java/ql/test/library-tests/literals/literalInteger.expected +++ /dev/null @@ -1,20 +0,0 @@ -| literals/Literals.java:7:22:7:24 | 123 | 123 | 123 | -| literals/Literals.java:71:3:71:3 | 0 | 0 | 0 | -| literals/Literals.java:72:3:72:5 | 0_0 | 0 | 0 | -| literals/Literals.java:73:3:73:7 | 0___0 | 0 | 0 | -| literals/Literals.java:74:3:74:6 | 0_12 | 10 | 10 | -| literals/Literals.java:75:3:75:7 | 0X012 | 18 | 18 | -| literals/Literals.java:76:3:76:10 | 0xaBcDeF | 11259375 | 11259375 | -| literals/Literals.java:77:3:77:6 | 0B11 | 3 | 3 | -| literals/Literals.java:78:3:78:12 | 0x80000000 | -2147483648 | -2147483648 | -| literals/Literals.java:79:3:79:12 | 2147483647 | 2147483647 | 2147483647 | -| literals/Literals.java:80:3:80:13 | -2147483648 | -2147483648 | -2147483648 | -| literals/Literals.java:82:3:82:13 | 0x7fff_ffff | 2147483647 | 2147483647 | -| literals/Literals.java:83:3:83:16 | 0177_7777_7777 | 2147483647 | 2147483647 | -| literals/Literals.java:84:3:84:43 | 0b0111_1111_1111_1111_1111_1111_1111_1111 | 2147483647 | 2147483647 | -| literals/Literals.java:85:3:85:13 | 0x8000_0000 | -2147483648 | -2147483648 | -| literals/Literals.java:86:3:86:16 | 0200_0000_0000 | -2147483648 | -2147483648 | -| literals/Literals.java:87:3:87:43 | 0b1000_0000_0000_0000_0000_0000_0000_0000 | -2147483648 | -2147483648 | -| literals/Literals.java:88:3:88:13 | 0xffff_ffff | -1 | -1 | -| literals/Literals.java:89:3:89:16 | 0377_7777_7777 | -1 | -1 | -| literals/Literals.java:90:3:90:43 | 0b1111_1111_1111_1111_1111_1111_1111_1111 | -1 | -1 | diff --git a/java/ql/test/library-tests/literals/literalLong.expected b/java/ql/test/library-tests/literals/literalLong.expected deleted file mode 100644 index f46c55f9f37..00000000000 --- a/java/ql/test/library-tests/literals/literalLong.expected +++ /dev/null @@ -1,20 +0,0 @@ -| literals/Literals.java:8:22:8:25 | 456L | 456 | -| literals/Literals.java:94:3:94:4 | 0l | 0 | -| literals/Literals.java:95:3:95:4 | 0L | 0 | -| literals/Literals.java:96:3:96:6 | 0_0L | 0 | -| literals/Literals.java:97:3:97:8 | 0___0L | 0 | -| literals/Literals.java:98:3:98:7 | 0_12L | 10 | -| literals/Literals.java:99:3:99:8 | 0X012L | 18 | -| literals/Literals.java:100:3:100:11 | 0xaBcDeFL | 11259375 | -| literals/Literals.java:101:3:101:7 | 0B11L | 3 | -| literals/Literals.java:102:3:102:22 | 9223372036854775807L | 9223372036854775807 | -| literals/Literals.java:103:3:103:23 | -9223372036854775808L | -9223372036854775808 | -| literals/Literals.java:105:3:105:24 | 0x7fff_ffff_ffff_ffffL | 9223372036854775807 | -| literals/Literals.java:106:3:106:30 | 07_7777_7777_7777_7777_7777L | 9223372036854775807 | -| literals/Literals.java:107:3:107:84 | 0b0111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111L | 9223372036854775807 | -| literals/Literals.java:108:3:108:24 | 0x8000_0000_0000_0000L | -9223372036854775808 | -| literals/Literals.java:109:3:109:31 | 010_0000_0000_0000_0000_0000L | -9223372036854775808 | -| literals/Literals.java:110:3:110:84 | 0b1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000L | -9223372036854775808 | -| literals/Literals.java:111:3:111:24 | 0xffff_ffff_ffff_ffffL | -1 | -| literals/Literals.java:112:3:112:31 | 017_7777_7777_7777_7777_7777L | -1 | -| literals/Literals.java:113:3:113:84 | 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111L | -1 | diff --git a/java/ql/test/library-tests/literals/literalString.expected b/java/ql/test/library-tests/literals/literalString.expected deleted file mode 100644 index ff39e8085e1..00000000000 --- a/java/ql/test/library-tests/literals/literalString.expected +++ /dev/null @@ -1,20 +0,0 @@ -| literals/Literals.java:6:22:6:37 | "literal string" | literal string | literal string | -| literals/Literals.java:117:3:117:19 | "hello" + "world" | helloworld | helloworld | -| literals/Literals.java:118:3:118:17 | "hello,\\tworld" | hello,\tworld | hello,\tworld | -| literals/Literals.java:119:3:119:21 | "hello,\\u0009world" | hello,\tworld | hello,\tworld | -| literals/Literals.java:120:3:120:10 | "\\u0061" | a | a | -| literals/Literals.java:121:3:121:6 | "\\0" | \u0000 | \u0000 | -| literals/Literals.java:122:3:122:9 | "\\0000" | \u00000 | \u00000 | -| literals/Literals.java:123:3:123:6 | "\\"" | " | " | -| literals/Literals.java:124:3:124:6 | "\\'" | ' | ' | -| literals/Literals.java:125:3:125:6 | "\\n" | \n | \n | -| literals/Literals.java:126:3:126:6 | "\\\\" | \\ | \\ | -| literals/Literals.java:127:3:127:13 | "test \\123" | test S | test S | -| literals/Literals.java:128:3:128:9 | "\\1234" | S4 | S4 | -| literals/Literals.java:129:3:129:13 | "\\u0061567" | a567 | a567 | -| literals/Literals.java:130:3:130:13 | "\\u1234567" | \u1234567 | \u1234567 | -| literals/Literals.java:131:3:131:18 | "\\uaBcDeF\\u0aB1" | \uabcdeF\u0ab1 | \uabcdeF\u0ab1 | -| literals/Literals.java:132:3:132:16 | "\\uD800\\uDC00" | \ud800\udc00 | \ud800\udc00 | -| literals/Literals.java:134:3:134:10 | "\\uD800" | \ufffd | \ufffd | -| literals/Literals.java:135:3:135:10 | "\\uDC00" | \ufffd | \ufffd | -| literals/Literals.java:136:3:136:31 | "hello\\uD800hello\\uDC00world" | hello\ufffdhello\ufffdworld | hello\ufffdhello\ufffdworld | diff --git a/java/ql/test/library-tests/literals/literalString.ql b/java/ql/test/library-tests/literals/literalString.ql deleted file mode 100644 index e2128e54e0e..00000000000 --- a/java/ql/test/library-tests/literals/literalString.ql +++ /dev/null @@ -1,5 +0,0 @@ -import semmle.code.java.Expr - -from StringLiteral lit -where lit.getFile().(CompilationUnit).fromSource() -select lit, lit.getValue(), lit.getRepresentedString() diff --git a/java/ql/test/library-tests/literals-numeric/NumericLiterals.java b/java/ql/test/library-tests/literals/literals-numeric/NumericLiterals.java similarity index 95% rename from java/ql/test/library-tests/literals-numeric/NumericLiterals.java rename to java/ql/test/library-tests/literals/literals-numeric/NumericLiterals.java index 02f2fbfcc6b..776ac6215fc 100644 --- a/java/ql/test/library-tests/literals-numeric/NumericLiterals.java +++ b/java/ql/test/library-tests/literals/literals-numeric/NumericLiterals.java @@ -1,4 +1,4 @@ -class NumericLiterals { +class NumericLiterals { void negativeLiterals() { float f = -1f; double d = -1d; diff --git a/java/ql/test/library-tests/literals-numeric/negativeNumericLiteral.expected b/java/ql/test/library-tests/literals/literals-numeric/negativeNumericLiteral.expected similarity index 100% rename from java/ql/test/library-tests/literals-numeric/negativeNumericLiteral.expected rename to java/ql/test/library-tests/literals/literals-numeric/negativeNumericLiteral.expected diff --git a/java/ql/test/library-tests/literals/literals-numeric/negativeNumericLiterals.expected b/java/ql/test/library-tests/literals/literals-numeric/negativeNumericLiterals.expected new file mode 100644 index 00000000000..95100f259dd --- /dev/null +++ b/java/ql/test/library-tests/literals/literals-numeric/negativeNumericLiterals.expected @@ -0,0 +1,12 @@ +| NumericLiterals.java:3:14:3:15 | 1f | 1.0 | NumericLiterals.java:3:13:3:15 | -... | +| NumericLiterals.java:4:15:4:16 | 1d | 1.0 | NumericLiterals.java:4:14:4:16 | -... | +| NumericLiterals.java:5:13:5:22 | 2147483647 | 2147483647 | NumericLiterals.java:5:12:5:22 | -... | +| NumericLiterals.java:6:12:6:22 | -2147483648 | -2147483648 | NumericLiterals.java:6:7:6:22 | i2 | +| NumericLiterals.java:7:13:7:46 | 0b10000000000000000000000000000000 | -2147483648 | NumericLiterals.java:7:12:7:46 | -... | +| NumericLiterals.java:8:13:8:24 | 020000000000 | -2147483648 | NumericLiterals.java:8:12:8:24 | -... | +| NumericLiterals.java:9:13:9:22 | 0x80000000 | -2147483648 | NumericLiterals.java:9:12:9:22 | -... | +| NumericLiterals.java:10:14:10:33 | 9223372036854775807L | 9223372036854775807 | NumericLiterals.java:10:13:10:33 | -... | +| NumericLiterals.java:11:13:11:33 | -9223372036854775808L | -9223372036854775808 | NumericLiterals.java:11:8:11:33 | l2 | +| NumericLiterals.java:12:14:12:80 | 0b1000000000000000000000000000000000000000000000000000000000000000L | -9223372036854775808 | NumericLiterals.java:12:13:12:80 | -... | +| NumericLiterals.java:13:14:13:37 | 01000000000000000000000L | -9223372036854775808 | NumericLiterals.java:13:13:13:37 | -... | +| NumericLiterals.java:14:14:14:32 | 0x8000000000000000L | -9223372036854775808 | NumericLiterals.java:14:13:14:32 | -... | diff --git a/java/ql/test/library-tests/literals-numeric/negativeNumericLiteral.ql b/java/ql/test/library-tests/literals/literals-numeric/negativeNumericLiterals.ql similarity index 100% rename from java/ql/test/library-tests/literals-numeric/negativeNumericLiteral.ql rename to java/ql/test/library-tests/literals/literals-numeric/negativeNumericLiterals.ql diff --git a/java/ql/test/library-tests/literals/literals/Literals.java b/java/ql/test/library-tests/literals/literals/Literals.java deleted file mode 100644 index 082310acb80..00000000000 --- a/java/ql/test/library-tests/literals/literals/Literals.java +++ /dev/null @@ -1,138 +0,0 @@ -package literals; - -public class Literals { - public int notAliteral; - public void doStuff() { - System.out.println("literal string"); - System.out.println(123); - System.out.println(456L); - System.out.println(123.0F); - System.out.println(456.0D); - System.out.println(true); - System.out.println('x'); - } - - boolean[] booleans = { - true, - false - }; - - char[] chars = { - 'a', - '\u0061', // 'a' - '\u0000', - '\0', - '\n', - '\0', - '\\', - '\'', - '\123' // octal escape sequence for 'S' - }; - - double[] doubles = { - 0.0, - 0d, - .0d, - .0, - -0.d, - +0.d, - 1.234567890123456789, - 1.55555555555555555555, - // From the JLS - 1e1, - 1.7976931348623157E308, - -1.7976931348623157E308, - 0x1.f_ffff_ffff_ffffP+1023, - 4.9e-324, - 0x0.0_0000_0000_0001P-1022, - 0x1.0P-1074 - }; - - float[] floats = { - 0.0f, - 0f, - .0f, - -0.f, - +0.f, - 1_0_0.0f, - 1.234567890123456789f, - 1.55555555555555555555f, - // From the JLS - 1e1f, - 3.4028235e38f, - -3.4028235e38f, - 0x1.fffffeP+127f, - 1.4e-45f, - 0x0.000002P-126f, - 0x1.0P-149f - }; - - int[] ints = { - 0, - 0_0, - 0___0, - 0_12, // octal - 0X012, // hex - 0xaBcDeF, // hex - 0B11, // binary - 0x80000000, - 2147483647, - -2147483648, - // From the JLS - 0x7fff_ffff, - 0177_7777_7777, // octal - 0b0111_1111_1111_1111_1111_1111_1111_1111, // binary - 0x8000_0000, - 0200_0000_0000, - 0b1000_0000_0000_0000_0000_0000_0000_0000, - 0xffff_ffff, - 0377_7777_7777, - 0b1111_1111_1111_1111_1111_1111_1111_1111 - }; - - long[] longs = { - 0l, - 0L, - 0_0L, - 0___0L, - 0_12L, // octal - 0X012L, // hex - 0xaBcDeFL, // hex - 0B11L, // binary - 9223372036854775807L, - -9223372036854775808L, - // From the JLS - 0x7fff_ffff_ffff_ffffL, - 07_7777_7777_7777_7777_7777L, // octal - 0b0111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111L, // binary - 0x8000_0000_0000_0000L, - 010_0000_0000_0000_0000_0000L, - 0b1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000L, - 0xffff_ffff_ffff_ffffL, - 017_7777_7777_7777_7777_7777L, - 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111L - }; - - String[] strings = { - "hello" + "world", // two separate literals - "hello,\tworld", - "hello,\u0009world", - "\u0061", // 'a' - "\0", - "\0000", - "\"", - "\'", - "\n", - "\\", - "test \123", // octal escape sequence for 'S' - "\1234", // octal escape followed by '4' - "\u0061567", // escape sequence for 'a' followed by "567" - "\u1234567", // '\u1234' followed by "567" - "\uaBcDeF\u0aB1", // '\uABCD' followed by "eF" followed by '\u0AB1' - "\uD800\uDC00", // surrogate pair - // Unpaired surrogates - "\uD800", - "\uDC00", - "hello\uD800hello\uDC00world" - }; -} diff --git a/java/ql/test/library-tests/literals/longLiterals/LongLiterals.java b/java/ql/test/library-tests/literals/longLiterals/LongLiterals.java new file mode 100644 index 00000000000..7501300684b --- /dev/null +++ b/java/ql/test/library-tests/literals/longLiterals/LongLiterals.java @@ -0,0 +1,56 @@ +package longLiterals; + +public class LongLiterals { + long[] longs = { + 0l, + 0L, + 1L, + 0_0L, + 0___0L, + 0_12L, // octal + 0X012L, // hex + 0xaBcDeFL, // hex + 0B11L, // binary + 9223372036854775807L, + -9223372036854775808L, // special case: sign is part of the literal + // From the JLS + 0x7fff_ffff_ffff_ffffL, + 07_7777_7777_7777_7777_7777L, // octal + 0b0111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111L, // binary + 0x8000_0000_0000_0000L, + 010_0000_0000_0000_0000_0000L, + 0b1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000L, + 0xffff_ffff_ffff_ffffL, + 017_7777_7777_7777_7777_7777L, + 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111L, + // Using Unicode escapes (which are handled during pre-processing) + \u0030\u004C, // 0L + }; + + // + and - are not part of the literal + long[] longsWithSign = { + +0L, + -0L, + +1L, + -1L, + +9223372036854775807L, + }; + + // The operation expression (e.g. `+`) is not a literal + long[] numericOperations = { + 1L + 1L, + 0L / 0L, + }; + + Object[] longLongLiterals = { + "0L", + '0', + 0, + (long) 0, + 0.0, + (long) 0.0, + Long.MIN_VALUE, + }; + + long nonLiteral; +} diff --git a/java/ql/test/library-tests/literals/longLiterals/longLiterals.expected b/java/ql/test/library-tests/literals/longLiterals/longLiterals.expected new file mode 100644 index 00000000000..d020a8f1c87 --- /dev/null +++ b/java/ql/test/library-tests/literals/longLiterals/longLiterals.expected @@ -0,0 +1,30 @@ +| LongLiterals.java:5:3:5:4 | 0l | 0 | +| LongLiterals.java:6:3:6:4 | 0L | 0 | +| LongLiterals.java:7:3:7:4 | 1L | 1 | +| LongLiterals.java:8:3:8:6 | 0_0L | 0 | +| LongLiterals.java:9:3:9:8 | 0___0L | 0 | +| LongLiterals.java:10:3:10:7 | 0_12L | 10 | +| LongLiterals.java:11:3:11:8 | 0X012L | 18 | +| LongLiterals.java:12:3:12:11 | 0xaBcDeFL | 11259375 | +| LongLiterals.java:13:3:13:7 | 0B11L | 3 | +| LongLiterals.java:14:3:14:22 | 9223372036854775807L | 9223372036854775807 | +| LongLiterals.java:15:3:15:23 | -9223372036854775808L | -9223372036854775808 | +| LongLiterals.java:17:3:17:24 | 0x7fff_ffff_ffff_ffffL | 9223372036854775807 | +| LongLiterals.java:18:3:18:30 | 07_7777_7777_7777_7777_7777L | 9223372036854775807 | +| LongLiterals.java:19:3:19:84 | 0b0111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111L | 9223372036854775807 | +| LongLiterals.java:20:3:20:24 | 0x8000_0000_0000_0000L | -9223372036854775808 | +| LongLiterals.java:21:3:21:31 | 010_0000_0000_0000_0000_0000L | -9223372036854775808 | +| LongLiterals.java:22:3:22:84 | 0b1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000L | -9223372036854775808 | +| LongLiterals.java:23:3:23:24 | 0xffff_ffff_ffff_ffffL | -1 | +| LongLiterals.java:24:3:24:31 | 017_7777_7777_7777_7777_7777L | -1 | +| LongLiterals.java:25:3:25:84 | 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111L | -1 | +| LongLiterals.java:27:8:27:14 | 0\\u004C | 0 | +| LongLiterals.java:32:4:32:5 | 0L | 0 | +| LongLiterals.java:33:4:33:5 | 0L | 0 | +| LongLiterals.java:34:4:34:5 | 1L | 1 | +| LongLiterals.java:35:4:35:5 | 1L | 1 | +| LongLiterals.java:36:4:36:23 | 9223372036854775807L | 9223372036854775807 | +| LongLiterals.java:41:3:41:4 | 1L | 1 | +| LongLiterals.java:41:8:41:9 | 1L | 1 | +| LongLiterals.java:42:3:42:4 | 0L | 0 | +| LongLiterals.java:42:8:42:9 | 0L | 0 | diff --git a/java/ql/test/library-tests/literals/literalLong.ql b/java/ql/test/library-tests/literals/longLiterals/longLiterals.ql similarity index 100% rename from java/ql/test/library-tests/literals/literalLong.ql rename to java/ql/test/library-tests/literals/longLiterals/longLiterals.ql diff --git a/java/ql/test/library-tests/literals/nullLiterals/NullLiterals.java b/java/ql/test/library-tests/literals/nullLiterals/NullLiterals.java new file mode 100644 index 00000000000..f0042b2f11a --- /dev/null +++ b/java/ql/test/library-tests/literals/nullLiterals/NullLiterals.java @@ -0,0 +1,22 @@ +package nullLiterals; + +public class NullLiterals { + Object[] nulls = { + null, + // Using Unicode escapes (which are handled during pre-processing) + \u006E\u0075\u006C\u006C, // null + }; + + // The operation expressions (e.g. cast) are not a literal + Object[] operations = { + (Object) null, + }; + + Object[] nonNullLiterals = { + "null", + 0, + Boolean.FALSE, + }; + + Object nonLiteral; +} diff --git a/java/ql/test/library-tests/literals/nullLiterals/nullLiterals.expected b/java/ql/test/library-tests/literals/nullLiterals/nullLiterals.expected new file mode 100644 index 00000000000..0401876d07c --- /dev/null +++ b/java/ql/test/library-tests/literals/nullLiterals/nullLiterals.expected @@ -0,0 +1,3 @@ +| NullLiterals.java:5:3:5:6 | null | null | +| NullLiterals.java:7:8:7:26 | null | null | +| NullLiterals.java:12:12:12:15 | null | null | diff --git a/java/ql/test/library-tests/literals/nullLiterals/nullLiterals.ql b/java/ql/test/library-tests/literals/nullLiterals/nullLiterals.ql new file mode 100644 index 00000000000..4bfcf3c206b --- /dev/null +++ b/java/ql/test/library-tests/literals/nullLiterals/nullLiterals.ql @@ -0,0 +1,4 @@ +import semmle.code.java.Expr + +from NullLiteral lit +select lit, lit.getValue() diff --git a/java/ql/test/library-tests/literals/stringLiterals/StringLiterals.java b/java/ql/test/library-tests/literals/stringLiterals/StringLiterals.java new file mode 100644 index 00000000000..9c0c55b12e5 --- /dev/null +++ b/java/ql/test/library-tests/literals/stringLiterals/StringLiterals.java @@ -0,0 +1,113 @@ +package stringLiterals; + +import java.io.File; + +public class StringLiterals { + String[] strings = { + "", + "hello,\tworld", + "hello,\u0009world", + "\u0061", // 'a' + "\0", + "\uFFFF", + "\ufFfF", + "\"", + "\'", + "\n", + "\\", + "test \123", // octal escape sequence for 'S' + "\1234", // octal escape followed by '4' + "\0000", // octal escape \000 followed by '0' + "\u0061567", // escape sequence for 'a' followed by "567" + "\u1234567", // '\u1234' followed by "567" + "\uaBcDeF\u0aB1", // '\uABCD' followed by "eF" followed by '\u0AB1' + "\uD800\uDC00", // surrogate pair + "\uDBFF\uDFFF", // U+10FFFF + // Unpaired surrogates + "\uD800", + "\uDC00", + "hello\uD800hello\uDC00world", // malformed surrogates + // Using Unicode escapes (which are handled during pre-processing) + "\u005C\u0022", // escaped double quote ("\"") + \u0022\u0061\u0022, // "a" + }; + + String[] textBlocks = { + // trailing whitespaces after """ (will be ignored) + """ + test "text" and escaped \u0022 + """, + // Indentation tests + """ + indented + """, + """ + no indentation last line + """, // Line is blank, therefore not indented + """ + indentation last line + \s""", // Line is not blank therefore indented + """ + not-indented + """, + """ + indented + """, + """ + not-indented + """, + """ + spaces (only single space is trimmed) + tab + """, + """ + end on same line""", + """ + trailing spaces ignored: + not ignored: \s + """, + """ + 3 quotes:""\"""", + """ + line \ + continuation \ + """, + """ + Explicit line breaks:\n + \r\n + \r + """, + // Using Unicode escapes (which are handled during pre-processing) + // Currently not detected by StringLiteral.isTextBlock() + \uuu0022"\u0022 + test + \u0022\uu0022", + }; + + // The concatenation (`+`) is not a string literal + String[] stringConcatenation = { + // CodeQL erroneously reports this as one literal, see https://github.com/github/codeql/issues/5469 + "hello" + "world", + """ + hello""" + "world", + null + "a", + "a" + null, + "a" + 1, + 1 + "a", + "a" + true, + true + "a", + "a" + 'b', + 'b' + "a", + }; + + Object[] nonStringLiterals = { + 'a', + '"', + true, + null, + 0, + File.pathSeparator + }; + + String nonLiteral; +} diff --git a/java/ql/test/library-tests/literals/stringLiterals/options b/java/ql/test/library-tests/literals/stringLiterals/options new file mode 100644 index 00000000000..bb7dba26161 --- /dev/null +++ b/java/ql/test/library-tests/literals/stringLiterals/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -source 15 -target 15 diff --git a/java/ql/test/library-tests/literals/stringLiterals/stringLiterals.expected b/java/ql/test/library-tests/literals/stringLiterals/stringLiterals.expected new file mode 100644 index 00000000000..bf91a56e723 --- /dev/null +++ b/java/ql/test/library-tests/literals/stringLiterals/stringLiterals.expected @@ -0,0 +1,48 @@ +| StringLiterals.java:7:3:7:4 | "" | | | | +| StringLiterals.java:8:3:8:17 | "hello,\\tworld" | hello,\tworld | hello,\tworld | | +| StringLiterals.java:9:3:9:21 | "hello,\\u0009world" | hello,\tworld | hello,\tworld | | +| StringLiterals.java:10:3:10:10 | "\\u0061" | a | a | | +| StringLiterals.java:11:3:11:6 | "\\0" | \u0000 | \u0000 | | +| StringLiterals.java:12:3:12:10 | "\\uFFFF" | \uffff | \uffff | | +| StringLiterals.java:13:3:13:10 | "\\ufFfF" | \uffff | \uffff | | +| StringLiterals.java:14:3:14:6 | "\\"" | " | " | | +| StringLiterals.java:15:3:15:6 | "\\'" | ' | ' | | +| StringLiterals.java:16:3:16:6 | "\\n" | \n | \n | | +| StringLiterals.java:17:3:17:6 | "\\\\" | \\ | \\ | | +| StringLiterals.java:18:3:18:13 | "test \\123" | test S | test S | | +| StringLiterals.java:19:3:19:9 | "\\1234" | S4 | S4 | | +| StringLiterals.java:20:3:20:9 | "\\0000" | \u00000 | \u00000 | | +| StringLiterals.java:21:3:21:13 | "\\u0061567" | a567 | a567 | | +| StringLiterals.java:22:3:22:13 | "\\u1234567" | \u1234567 | \u1234567 | | +| StringLiterals.java:23:3:23:18 | "\\uaBcDeF\\u0aB1" | \uabcdeF\u0ab1 | \uabcdeF\u0ab1 | | +| StringLiterals.java:24:3:24:16 | "\\uD800\\uDC00" | \ud800\udc00 | \ud800\udc00 | | +| StringLiterals.java:25:3:25:16 | "\\uDBFF\\uDFFF" | \udbff\udfff | \udbff\udfff | | +| StringLiterals.java:27:3:27:10 | "\\uD800" | \ufffd | \ufffd | | +| StringLiterals.java:28:3:28:10 | "\\uDC00" | \ufffd | \ufffd | | +| StringLiterals.java:29:3:29:31 | "hello\\uD800hello\\uDC00world" | hello\ufffdhello\ufffdworld | hello\ufffdhello\ufffdworld | | +| StringLiterals.java:31:3:31:16 | "\\u005C\\u0022" | " | " | | +| StringLiterals.java:32:8:32:20 | 2\\u0061\\u0022 | a | a | | +| StringLiterals.java:37:3:39:5 | """ \t \n\t\ttest "text" and escaped \\u0022\n\t\t""" | test "text" and escaped "\n | test "text" and escaped "\n | text-block | +| StringLiterals.java:41:3:43:5 | """\n\t\t\tindented\n\t\t""" | \tindented\n | \tindented\n | text-block | +| StringLiterals.java:44:3:46:5 | """\n\tno indentation last line\n\t\t""" | no indentation last line\n | no indentation last line\n | text-block | +| StringLiterals.java:47:3:49:7 | """\n\tindentation last line\n\t\t\\s""" | indentation last line\n\t | indentation last line\n\t | text-block | +| StringLiterals.java:50:3:52:6 | """\n\t\t\tnot-indented\n\t\t\t""" | not-indented\n | not-indented\n | text-block | +| StringLiterals.java:53:3:55:4 | """\n\t\tindented\n\t""" | \tindented\n | \tindented\n | text-block | +| StringLiterals.java:56:4:58:5 | """\n\t\tnot-indented\n\t\t""" | not-indented\n | not-indented\n | text-block | +| StringLiterals.java:59:3:62:6 | """\n\t\t spaces (only single space is trimmed)\n\t\t\ttab\n\t\t\t""" | spaces (only single space is trimmed)\ntab\n | spaces (only single space is trimmed)\ntab\n | text-block | +| StringLiterals.java:63:3:64:22 | """\n\t\t\tend on same line""" | end on same line | end on same line | text-block | +| StringLiterals.java:65:3:68:5 | """\n\t\ttrailing spaces ignored: \t \n\t\tnot ignored: \t \\s\n\t\t""" | trailing spaces ignored:\nnot ignored: \t \n | trailing spaces ignored:\nnot ignored: \t \n | text-block | +| StringLiterals.java:69:3:70:18 | """\n\t\t3 quotes:""\\"""" | 3 quotes:""" | 3 quotes:""" | text-block | +| StringLiterals.java:71:3:74:5 | """\n\t\tline \\\n\t\tcontinuation \\\n\t\t""" | line continuation | line continuation | text-block | +| StringLiterals.java:75:3:79:5 | """\n\t\tExplicit line breaks:\\n\n\t\t\\r\\n\n\t\t\\r\n\t\t""" | Explicit line breaks:\n\n\r\n\n\r\n | Explicit line breaks:\n\n\r\n\n\r\n | text-block | +| StringLiterals.java:82:10:84:16 | 2"\\u0022\n\t\ttest\n\t\t\\u0022\\uu0022" | test\n | test\n | | +| StringLiterals.java:90:3:90:19 | "hello" + "world" | helloworld | helloworld | | +| StringLiterals.java:91:3:92:20 | """\n\t\thello""" + "world" | helloworld | helloworld | text-block | +| StringLiterals.java:93:10:93:12 | "a" | a | a | | +| StringLiterals.java:94:3:94:5 | "a" | a | a | | +| StringLiterals.java:95:3:95:5 | "a" | a | a | | +| StringLiterals.java:96:7:96:9 | "a" | a | a | | +| StringLiterals.java:97:3:97:5 | "a" | a | a | | +| StringLiterals.java:98:10:98:12 | "a" | a | a | | +| StringLiterals.java:99:3:99:5 | "a" | a | a | | +| StringLiterals.java:100:9:100:11 | "a" | a | a | | diff --git a/java/ql/test/library-tests/literals/stringLiterals/stringLiterals.ql b/java/ql/test/library-tests/literals/stringLiterals/stringLiterals.ql new file mode 100644 index 00000000000..4b754090ff5 --- /dev/null +++ b/java/ql/test/library-tests/literals/stringLiterals/stringLiterals.ql @@ -0,0 +1,7 @@ +import semmle.code.java.Expr + +from StringLiteral lit, string isTextBlock +where + lit.getFile().(CompilationUnit).fromSource() and + if lit.isTextBlock() then isTextBlock = "text-block" else isTextBlock = "" +select lit, lit.getValue(), lit.getRepresentedString(), isTextBlock diff --git a/java/ql/test/library-tests/optional/test.ql b/java/ql/test/library-tests/optional/test.ql index 1edfda2487a..a5a61097dde 100644 --- a/java/ql/test/library-tests/optional/test.ql +++ b/java/ql/test/library-tests/optional/test.ql @@ -3,10 +3,6 @@ import TestUtilities.InlineFlowTest class SummaryModelTest extends SummaryModelCsv { override predicate row(string row) { - row = - [ - //"package;type;overrides;name;signature;ext;inputspec;outputspec;kind", - "generatedtest;Test;false;getStreamElement;;;Element of Argument[0];ReturnValue;value" - ] + row = "generatedtest;Test;false;getStreamElement;;;Element of Argument[0];ReturnValue;value" } } diff --git a/java/ql/test/query-tests/security/CWE-079/semmle/tests/XSS.ql b/java/ql/test/query-tests/security/CWE-079/semmle/tests/XSS.ql index 1cd3e59fc6b..6bfde865e85 100644 --- a/java/ql/test/query-tests/security/CWE-079/semmle/tests/XSS.ql +++ b/java/ql/test/query-tests/security/CWE-079/semmle/tests/XSS.ql @@ -20,7 +20,7 @@ class XSSConfig extends TaintTracking::Configuration { class XssTest extends InlineExpectationsTest { XssTest() { this = "XssTest" } - override string getARelevantTag() { result = ["xss"] } + override string getARelevantTag() { result = "xss" } override predicate hasActualResult(Location location, string element, string tag, string value) { tag = "xss" and diff --git a/java/ql/test/query-tests/security/CWE-297/InsecureJakartaMailTest.java b/java/ql/test/query-tests/security/CWE-297/InsecureJakartaMailTest.java new file mode 100644 index 00000000000..2dd5cab08ec --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-297/InsecureJakartaMailTest.java @@ -0,0 +1,42 @@ +import java.util.Properties; + +import jakarta.mail.Authenticator; +import jakarta.mail.PasswordAuthentication; +import jakarta.mail.Session; + +class InsecureJakartaMailTest { + public void testJavaMail() { + final Properties properties = new Properties(); + properties.put("mail.transport.protocol", "protocol"); + properties.put("mail.smtp.host", "hostname"); + properties.put("mail.smtp.socketFactory.class", "classname"); + + final jakarta.mail.Authenticator authenticator = new jakarta.mail.Authenticator() { + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication("username", "password"); + } + }; + if (null != authenticator) { + properties.put("mail.smtp.auth", "true"); + } + final Session session = Session.getInstance(properties, authenticator); // $hasInsecureJavaMail + } + + public void testSecureJavaMail() { + final Properties properties = new Properties(); + properties.put("mail.transport.protocol", "protocol"); + properties.put("mail.smtp.host", "hostname"); + properties.put("mail.smtp.socketFactory.class", "classname"); + + final jakarta.mail.Authenticator authenticator = new jakarta.mail.Authenticator() { + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication("username", "password"); + } + }; + if (null != authenticator) { + properties.put("mail.smtp.auth", "true"); + properties.put("mail.smtp.ssl.checkserveridentity", "true"); + } + final Session session = Session.getInstance(properties, authenticator); // Safe + } +} diff --git a/java/ql/test/query-tests/security/CWE-297/InsecureJavaMailTest.expected b/java/ql/test/query-tests/security/CWE-297/InsecureJavaMailTest.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/java/ql/test/query-tests/security/CWE-297/InsecureJavaMailTest.java b/java/ql/test/query-tests/security/CWE-297/InsecureJavaMailTest.java new file mode 100644 index 00000000000..a9880c20339 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-297/InsecureJavaMailTest.java @@ -0,0 +1,44 @@ +import java.util.Properties; + +import javax.mail.Authenticator; +import javax.mail.PasswordAuthentication; +import javax.mail.Session; + +class InsecureJavaMailTest { + public void testJavaMail() { + final Properties properties = new Properties(); + properties.put("mail.transport.protocol", "protocol"); + properties.put("mail.smtp.host", "hostname"); + properties.put("mail.smtp.socketFactory.class", "classname"); + + final javax.mail.Authenticator authenticator = new javax.mail.Authenticator() { + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication("username", "password"); + } + }; + if (null != authenticator) { + properties.put("mail.smtp.auth", "true"); + } + final Session session = Session.getInstance(properties, authenticator); // $hasInsecureJavaMail + } + + public void testSecureJavaMail() { + final Properties properties = new Properties(); + properties.put("mail.transport.protocol", "protocol"); + properties.put("mail.smtp.host", "hostname"); + properties.put("mail.smtp.socketFactory.class", "classname"); + + final javax.mail.Authenticator authenticator = new javax.mail.Authenticator() { + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication("username", "password"); + } + }; + if (null != authenticator) { + properties.put("mail.smtp.auth", "true"); + properties.put("mail.smtp.ssl.checkserveridentity", "true"); + } + final Session session = Session.getInstance(properties, authenticator); // Safe + } + + +} diff --git a/java/ql/test/query-tests/security/CWE-297/InsecureJavaMailTest.ql b/java/ql/test/query-tests/security/CWE-297/InsecureJavaMailTest.ql new file mode 100644 index 00000000000..137dde369f9 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-297/InsecureJavaMailTest.ql @@ -0,0 +1,24 @@ +import java +import semmle.code.java.security.Mail +import TestUtilities.InlineExpectationsTest + +class InsecureJavaMailTest extends InlineExpectationsTest { + InsecureJavaMailTest() { this = "HasInsecureJavaMailTest" } + + override string getARelevantTag() { result = "hasInsecureJavaMail" } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "hasInsecureJavaMail" and + exists(MethodAccess ma | + ma.getLocation() = location and + element = ma.toString() and + value = "" + | + ma.getMethod() instanceof MailSessionGetInstanceMethod and + isInsecureMailPropertyConfig(ma.getArgument(0).(VarAccess).getVariable()) + or + enablesEmailSsl(ma) and + not hasSslCertificateCheck(ma.getQualifier().(VarAccess).getVariable()) + ) + } +} diff --git a/java/ql/test/query-tests/security/CWE-297/InsecureSimpleEmailTest.java b/java/ql/test/query-tests/security/CWE-297/InsecureSimpleEmailTest.java new file mode 100644 index 00000000000..5940dbbd457 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-297/InsecureSimpleEmailTest.java @@ -0,0 +1,62 @@ +import org.apache.commons.mail.DefaultAuthenticator; +import org.apache.commons.mail.Email; +import org.apache.commons.mail.SimpleEmail; + +public class InsecureSimpleEmailTest { + public void test() throws Exception { + // with setSSLOnConnect + { + Email email = new SimpleEmail(); + email.setHostName("config.hostName"); + email.setSmtpPort(25); + email.setAuthenticator(new DefaultAuthenticator("config.username", "config.password")); + email.setSSLOnConnect(true); // $hasInsecureJavaMail + email.setFrom("fromAddress"); + email.setSubject("subject"); + email.setMsg("body"); + email.addTo("toAddress"); + email.send(); + } + // with setStartTLSRequired + { + Email email = new SimpleEmail(); + email.setHostName("config.hostName"); + email.setSmtpPort(25); + email.setAuthenticator(new DefaultAuthenticator("config.username", "config.password")); + email.setStartTLSRequired(true); // $hasInsecureJavaMail + email.setFrom("fromAddress"); + email.setSubject("subject"); + email.setMsg("body"); + email.addTo("toAddress"); + email.send(); + } + // safe with setSSLOnConnect + { + Email email = new SimpleEmail(); + email.setHostName("config.hostName"); + email.setSmtpPort(25); + email.setAuthenticator(new DefaultAuthenticator("config.username", "config.password")); + email.setSSLOnConnect(true); // Safe + email.setSSLCheckServerIdentity(true); + email.setFrom("fromAddress"); + email.setSubject("subject"); + email.setMsg("body"); + email.addTo("toAddress"); + email.send(); + } + // safe with setStartTLSRequired + { + Email email = new SimpleEmail(); + email.setHostName("config.hostName"); + email.setSmtpPort(25); + email.setAuthenticator(new DefaultAuthenticator("config.username", "config.password")); + email.setStartTLSRequired(true); // Safe + email.setSSLCheckServerIdentity(true); + email.setFrom("fromAddress"); + email.setSubject("subject"); + email.setMsg("body"); + email.addTo("toAddress"); + email.send(); + } + } +} diff --git a/java/ql/test/query-tests/security/CWE-297/options b/java/ql/test/query-tests/security/CWE-297/options new file mode 100644 index 00000000000..6fcd80260e3 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-297/options @@ -0,0 +1 @@ +// semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/apache-commons-email-1.6.0:${testdir}/../../../stubs/javamail-api-1.6.2:${testdir}/../../../stubs/jakarta-mail-2.0.1 diff --git a/java/ql/test/query-tests/security/CWE-502/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-502/AndroidManifest.xml new file mode 100755 index 00000000000..6a9785b8da4 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-502/AndroidManifest.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + diff --git a/java/ql/test/query-tests/security/CWE-502/GsonActivity.java b/java/ql/test/query-tests/security/CWE-502/GsonActivity.java new file mode 100644 index 00000000000..a080924c6cd --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-502/GsonActivity.java @@ -0,0 +1,17 @@ +package com.example.app; + +import android.app.Activity; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import com.google.gson.Gson; + +public class GsonActivity extends Activity { + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(-1); + + ParcelableEntity entity = (ParcelableEntity) getIntent().getParcelableExtra("jsonEntity"); + } +} diff --git a/java/ql/test/query-tests/security/CWE-502/GsonServlet.java b/java/ql/test/query-tests/security/CWE-502/GsonServlet.java new file mode 100644 index 00000000000..47534d2d7a0 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-502/GsonServlet.java @@ -0,0 +1,77 @@ +import java.io.IOException; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.typeadapters.RuntimeTypeAdapterFactory; + +import com.example.User; +import com.thirdparty.Person; + +public class GsonServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + // GOOD: concrete class type specified + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + String json = req.getParameter("json"); + + Gson gson = new Gson(); + Object obj = gson.fromJson(json, User.class); + } + + @Override + // GOOD: concrete class type specified + public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException { + String json = req.getParameter("json"); + + Gson gson = new Gson(); + Object obj = gson.fromJson(json, Person.class); + } + + @Override + // BAD: allow class name to be controlled by remote source + public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { + String json = req.getParameter("json"); + String clazz = req.getParameter("class"); + + try { + Gson gson = new Gson(); + Object obj = gson.fromJson(json, Class.forName(clazz)); // $unsafeDeserialization + } catch (ClassNotFoundException cne) { + throw new IOException(cne.getMessage()); + } + } + + @Override + // BAD: allow class name to be controlled by remote source even with a type adapter factory + public void doHead(HttpServletRequest req, HttpServletResponse resp) throws IOException { + String json = req.getParameter("json"); + String clazz = req.getParameter("class"); + + try { + RuntimeTypeAdapterFactory runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory + .of(User.class, "type"); + Gson gson = new GsonBuilder().registerTypeAdapterFactory(runtimeTypeAdapterFactory).create(); + Object obj = gson.fromJson(json, Class.forName(clazz)); // $unsafeDeserialization + } catch (ClassNotFoundException cne) { + throw new IOException(cne.getMessage()); + } + } + + @Override + // GOOD: specify allowed class types without explicitly configured vulnerable subclass types + public void doTrace(HttpServletRequest req, HttpServletResponse resp) throws IOException { + String json = req.getParameter("json"); + String clazz = req.getParameter("class"); + + RuntimeTypeAdapterFactory runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory + .of(Person.class, "type"); + Gson gson = new GsonBuilder().registerTypeAdapterFactory(runtimeTypeAdapterFactory).create(); + Person obj = gson.fromJson(json, Person.class); + } +} \ No newline at end of file diff --git a/java/ql/test/query-tests/security/CWE-502/ParcelableEntity.java b/java/ql/test/query-tests/security/CWE-502/ParcelableEntity.java new file mode 100644 index 00000000000..a9cbcabd9d3 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-502/ParcelableEntity.java @@ -0,0 +1,45 @@ +package com.example.app; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +public class ParcelableEntity implements Parcelable { + private static final Gson GSON = new GsonBuilder().create(); + + public ParcelableEntity(Object obj) { + this.obj = obj; + } + + private Object obj; + + @Override + public void writeToParcel(Parcel parcel, int i) { + parcel.writeString(obj.getClass().getName()); + parcel.writeString(GSON.toJson(obj)); + } + + @Override + public int describeContents() { return 0; } + + public static final Parcelable.Creator CREATOR = new Creator() { + @Override + public ParcelableEntity createFromParcel(Parcel parcel) { + try { + Class clazz = Class.forName(parcel.readString()); + Object obj = GSON.fromJson(parcel.readString(), clazz); // $unsafeDeserialization + return new ParcelableEntity(obj); + } + catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + @Override + public ParcelableEntity[] newArray(int size) { + return new ParcelableEntity[size]; + } + }; +} diff --git a/java/ql/test/query-tests/security/CWE-502/options b/java/ql/test/query-tests/security/CWE-502/options index cdd0375684e..0e9cab2d100 100644 --- a/java/ql/test/query-tests/security/CWE-502/options +++ b/java/ql/test/query-tests/security/CWE-502/options @@ -1 +1 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/snakeyaml-1.21:${testdir}/../../../stubs/xstream-1.4.10:${testdir}/../../../stubs/kryo-4.0.2:${testdir}/../../../stubs/jsr311-api-1.1.1:${testdir}/../../../stubs/fastjson-1.2.74:${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/jyaml-1.3:${testdir}/../../../stubs/json-io-4.10.0:${testdir}/../../../stubs/yamlbeans-1.09:${testdir}/../../../stubs/hessian-4.0.38:${testdir}/../../../stubs/castor-1.4.1:${testdir}/../../../stubs/jackson-databind-2.12:${testdir}/../../../stubs/jackson-core-2.12:${testdir}/../../../stubs/jabsorb-1.3.2:${testdir}/../../../stubs/json-java-20210307:${testdir}/../../../stubs/joddjson-6.0.3:${testdir}/../../../stubs/flexjson-2.1 +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/snakeyaml-1.21:${testdir}/../../../stubs/xstream-1.4.10:${testdir}/../../../stubs/kryo-4.0.2:${testdir}/../../../stubs/jsr311-api-1.1.1:${testdir}/../../../stubs/fastjson-1.2.74:${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/jyaml-1.3:${testdir}/../../../stubs/json-io-4.10.0:${testdir}/../../../stubs/yamlbeans-1.09:${testdir}/../../../stubs/hessian-4.0.38:${testdir}/../../../stubs/castor-1.4.1:${testdir}/../../../stubs/jackson-databind-2.12:${testdir}/../../../stubs/jackson-core-2.12:${testdir}/../../../stubs/jabsorb-1.3.2:${testdir}/../../../stubs/json-java-20210307:${testdir}/../../../stubs/joddjson-6.0.3:${testdir}/../../../stubs/flexjson-2.1:${testdir}/../../../stubs/gson-2.8.6:${testdir}/../../../stubs/google-android-9.0.0 diff --git a/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedAzureCredentials.java b/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedAzureCredentials.java new file mode 100644 index 00000000000..d243e18d608 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedAzureCredentials.java @@ -0,0 +1,66 @@ +import com.azure.identity.ClientSecretCredential; +import com.azure.identity.ClientSecretCredentialBuilder; +import com.azure.identity.UsernamePasswordCredential; +import com.azure.identity.UsernamePasswordCredentialBuilder; +import com.azure.security.keyvault.secrets.SecretClient; +import com.azure.security.keyvault.secrets.SecretClientBuilder; + +public class HardcodedAzureCredentials { + private final String clientId = "81734019-15a3-50t8-3253-5abe78abc3a1"; + private final String username = "username@example.onmicrosoft.com"; + private final String clientSecret = "1n1.qAc~3Q-1t38aF79Xzv5AUEfR5-ct3_"; + private final String tenantId = "22f367ce-535x-357w-2179-a33517mn166h"; + + //BAD: hard-coded username/password credentials + public void testHardcodedUsernamePassword(String input) { + UsernamePasswordCredential usernamePasswordCredential = new UsernamePasswordCredentialBuilder() + .clientId(clientId) + .username(username) + .password(clientSecret) + .build(); + + SecretClient client = new SecretClientBuilder() + .vaultUrl("https://myKeyVault.vault.azure.net") + .credential(usernamePasswordCredential) + .buildClient(); + } + + //GOOD: username/password credentials stored as environment variables + public void testEnvironmentUsernamePassword(String input) { + UsernamePasswordCredential usernamePasswordCredential = new UsernamePasswordCredentialBuilder() + .clientId(clientId) + .username(System.getenv("myUsername")) + .password(System.getenv("mySuperSecurePass")) + .build(); + + SecretClient client = new SecretClientBuilder() + .vaultUrl("https://myKeyVault.vault.azure.net") + .credential(usernamePasswordCredential) + .buildClient(); + } + + //BAD: hard-coded client secret + public void testHardcodedClientSecret(String input) { + ClientSecretCredential defaultCredential = new ClientSecretCredentialBuilder() + .clientId(clientId) + .clientSecret(clientSecret) + .tenantId(tenantId) + .build(); + } + + //GOOD: client secret stored as environment variables + public void testEnvironmentClientSecret(String input) { + ClientSecretCredential defaultCredential = new ClientSecretCredentialBuilder() + .clientId(clientId) + .clientSecret(System.getenv("myClientSecret")) + .tenantId(tenantId) + .build(); + } + + public static void main(String[] args) { + new HardcodedAzureCredentials().testHardcodedUsernamePassword(args[0]); + new HardcodedAzureCredentials().testEnvironmentUsernamePassword(args[0]); + new HardcodedAzureCredentials().testHardcodedClientSecret(args[0]); + new HardcodedAzureCredentials().testEnvironmentClientSecret(args[0]); + } +} \ No newline at end of file diff --git a/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedCredentialsApiCall.expected b/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedCredentialsApiCall.expected index 9c51a24760d..33b8879d9e5 100644 --- a/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedCredentialsApiCall.expected +++ b/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedCredentialsApiCall.expected @@ -10,6 +10,25 @@ edges | FileCredentialTest.java:13:14:13:20 | "admin" : String | FileCredentialTest.java:19:13:19:13 | u : String | | FileCredentialTest.java:19:13:19:13 | u : String | FileCredentialTest.java:22:38:22:45 | v : String | | FileCredentialTest.java:22:38:22:45 | v : String | FileCredentialTest.java:23:36:23:36 | v | +| HardcodedAzureCredentials.java:8:14:8:38 | this <.method> [post update] [clientSecret] : String | HardcodedAzureCredentials.java:61:3:61:33 | new HardcodedAzureCredentials(...) [clientSecret] : String | +| HardcodedAzureCredentials.java:8:14:8:38 | this <.method> [post update] [clientSecret] : String | HardcodedAzureCredentials.java:63:3:63:33 | new HardcodedAzureCredentials(...) [clientSecret] : String | +| HardcodedAzureCredentials.java:8:14:8:38 | this <.method> [post update] [username] : String | HardcodedAzureCredentials.java:61:3:61:33 | new HardcodedAzureCredentials(...) [username] : String | +| HardcodedAzureCredentials.java:10:2:10:68 | this <.field> [post update] [username] : String | HardcodedAzureCredentials.java:8:14:8:38 | this <.method> [post update] [username] : String | +| HardcodedAzureCredentials.java:10:34:10:67 | "username@example.onmicrosoft.com" : String | HardcodedAzureCredentials.java:10:2:10:68 | this <.field> [post update] [username] : String | +| HardcodedAzureCredentials.java:11:2:11:74 | this <.field> [post update] [clientSecret] : String | HardcodedAzureCredentials.java:8:14:8:38 | this <.method> [post update] [clientSecret] : String | +| HardcodedAzureCredentials.java:11:38:11:73 | "1n1.qAc~3Q-1t38aF79Xzv5AUEfR5-ct3_" : String | HardcodedAzureCredentials.java:11:2:11:74 | this <.field> [post update] [clientSecret] : String | +| HardcodedAzureCredentials.java:15:14:15:42 | parameter this [clientSecret] : String | HardcodedAzureCredentials.java:19:13:19:24 | this <.field> [clientSecret] : String | +| HardcodedAzureCredentials.java:15:14:15:42 | parameter this [username] : String | HardcodedAzureCredentials.java:18:13:18:20 | this <.field> [username] : String | +| HardcodedAzureCredentials.java:18:13:18:20 | this <.field> [username] : String | HardcodedAzureCredentials.java:18:13:18:20 | username | +| HardcodedAzureCredentials.java:19:13:19:24 | this <.field> [clientSecret] : String | HardcodedAzureCredentials.java:19:13:19:24 | clientSecret | +| HardcodedAzureCredentials.java:43:14:43:38 | parameter this [clientSecret] : String | HardcodedAzureCredentials.java:46:17:46:28 | this <.field> [clientSecret] : String | +| HardcodedAzureCredentials.java:46:17:46:28 | this <.field> [clientSecret] : String | HardcodedAzureCredentials.java:46:17:46:28 | clientSecret | +| HardcodedAzureCredentials.java:61:3:61:33 | new HardcodedAzureCredentials(...) [clientSecret] : String | HardcodedAzureCredentials.java:15:14:15:42 | parameter this [clientSecret] : String | +| HardcodedAzureCredentials.java:61:3:61:33 | new HardcodedAzureCredentials(...) [username] : String | HardcodedAzureCredentials.java:15:14:15:42 | parameter this [username] : String | +| HardcodedAzureCredentials.java:63:3:63:33 | new HardcodedAzureCredentials(...) [clientSecret] : String | HardcodedAzureCredentials.java:43:14:43:38 | parameter this [clientSecret] : String | +| HardcodedShiroKey.java:9:46:9:54 | "TEST123" : String | HardcodedShiroKey.java:9:46:9:65 | getBytes(...) | +| HardcodedShiroKey.java:18:61:18:86 | "4AvVhmFLUs0KTA3Kprsdag==" : String | HardcodedShiroKey.java:18:46:18:87 | decode(...) | +| HardcodedShiroKey.java:26:83:26:108 | "6ZmI6I2j5Y+R5aSn5ZOlAA==" : String | HardcodedShiroKey.java:26:46:26:109 | decode(...) | | Test.java:9:16:9:22 | "admin" : String | Test.java:12:13:12:15 | usr : String | | Test.java:9:16:9:22 | "admin" : String | Test.java:15:36:15:38 | usr | | Test.java:9:16:9:22 | "admin" : String | Test.java:17:39:17:41 | usr | @@ -42,6 +61,30 @@ nodes | FileCredentialTest.java:23:36:23:36 | v | semmle.label | v | | HardcodedAWSCredentials.java:8:50:8:61 | "ACCESS_KEY" | semmle.label | "ACCESS_KEY" | | HardcodedAWSCredentials.java:8:64:8:75 | "SECRET_KEY" | semmle.label | "SECRET_KEY" | +| HardcodedAzureCredentials.java:8:14:8:38 | this <.method> [post update] [clientSecret] : String | semmle.label | this <.method> [post update] [clientSecret] : String | +| HardcodedAzureCredentials.java:8:14:8:38 | this <.method> [post update] [username] : String | semmle.label | this <.method> [post update] [username] : String | +| HardcodedAzureCredentials.java:10:2:10:68 | this <.field> [post update] [username] : String | semmle.label | this <.field> [post update] [username] : String | +| HardcodedAzureCredentials.java:10:34:10:67 | "username@example.onmicrosoft.com" : String | semmle.label | "username@example.onmicrosoft.com" : String | +| HardcodedAzureCredentials.java:11:2:11:74 | this <.field> [post update] [clientSecret] : String | semmle.label | this <.field> [post update] [clientSecret] : String | +| HardcodedAzureCredentials.java:11:38:11:73 | "1n1.qAc~3Q-1t38aF79Xzv5AUEfR5-ct3_" : String | semmle.label | "1n1.qAc~3Q-1t38aF79Xzv5AUEfR5-ct3_" : String | +| HardcodedAzureCredentials.java:15:14:15:42 | parameter this [clientSecret] : String | semmle.label | parameter this [clientSecret] : String | +| HardcodedAzureCredentials.java:15:14:15:42 | parameter this [username] : String | semmle.label | parameter this [username] : String | +| HardcodedAzureCredentials.java:18:13:18:20 | this <.field> [username] : String | semmle.label | this <.field> [username] : String | +| HardcodedAzureCredentials.java:18:13:18:20 | username | semmle.label | username | +| HardcodedAzureCredentials.java:19:13:19:24 | clientSecret | semmle.label | clientSecret | +| HardcodedAzureCredentials.java:19:13:19:24 | this <.field> [clientSecret] : String | semmle.label | this <.field> [clientSecret] : String | +| HardcodedAzureCredentials.java:43:14:43:38 | parameter this [clientSecret] : String | semmle.label | parameter this [clientSecret] : String | +| HardcodedAzureCredentials.java:46:17:46:28 | clientSecret | semmle.label | clientSecret | +| HardcodedAzureCredentials.java:46:17:46:28 | this <.field> [clientSecret] : String | semmle.label | this <.field> [clientSecret] : String | +| HardcodedAzureCredentials.java:61:3:61:33 | new HardcodedAzureCredentials(...) [clientSecret] : String | semmle.label | new HardcodedAzureCredentials(...) [clientSecret] : String | +| HardcodedAzureCredentials.java:61:3:61:33 | new HardcodedAzureCredentials(...) [username] : String | semmle.label | new HardcodedAzureCredentials(...) [username] : String | +| HardcodedAzureCredentials.java:63:3:63:33 | new HardcodedAzureCredentials(...) [clientSecret] : String | semmle.label | new HardcodedAzureCredentials(...) [clientSecret] : String | +| HardcodedShiroKey.java:9:46:9:54 | "TEST123" : String | semmle.label | "TEST123" : String | +| HardcodedShiroKey.java:9:46:9:65 | getBytes(...) | semmle.label | getBytes(...) | +| HardcodedShiroKey.java:18:46:18:87 | decode(...) | semmle.label | decode(...) | +| HardcodedShiroKey.java:18:61:18:86 | "4AvVhmFLUs0KTA3Kprsdag==" : String | semmle.label | "4AvVhmFLUs0KTA3Kprsdag==" : String | +| HardcodedShiroKey.java:26:46:26:109 | decode(...) | semmle.label | decode(...) | +| HardcodedShiroKey.java:26:83:26:108 | "6ZmI6I2j5Y+R5aSn5ZOlAA==" : String | semmle.label | "6ZmI6I2j5Y+R5aSn5ZOlAA==" : String | | Test.java:9:16:9:22 | "admin" : String | semmle.label | "admin" : String | | Test.java:10:17:10:24 | "123456" : String | semmle.label | "123456" : String | | Test.java:12:13:12:15 | usr : String | semmle.label | usr : String | @@ -73,6 +116,12 @@ subpaths | FileCredentialTest.java:18:35:18:41 | "admin" | FileCredentialTest.java:18:35:18:41 | "admin" | FileCredentialTest.java:18:35:18:41 | "admin" | Hard-coded value flows to $@. | FileCredentialTest.java:18:35:18:41 | "admin" | sensitive API call | | HardcodedAWSCredentials.java:8:50:8:61 | "ACCESS_KEY" | HardcodedAWSCredentials.java:8:50:8:61 | "ACCESS_KEY" | HardcodedAWSCredentials.java:8:50:8:61 | "ACCESS_KEY" | Hard-coded value flows to $@. | HardcodedAWSCredentials.java:8:50:8:61 | "ACCESS_KEY" | sensitive API call | | HardcodedAWSCredentials.java:8:64:8:75 | "SECRET_KEY" | HardcodedAWSCredentials.java:8:64:8:75 | "SECRET_KEY" | HardcodedAWSCredentials.java:8:64:8:75 | "SECRET_KEY" | Hard-coded value flows to $@. | HardcodedAWSCredentials.java:8:64:8:75 | "SECRET_KEY" | sensitive API call | +| HardcodedAzureCredentials.java:10:34:10:67 | "username@example.onmicrosoft.com" | HardcodedAzureCredentials.java:10:34:10:67 | "username@example.onmicrosoft.com" : String | HardcodedAzureCredentials.java:18:13:18:20 | username | Hard-coded value flows to $@. | HardcodedAzureCredentials.java:18:13:18:20 | username | sensitive API call | +| HardcodedAzureCredentials.java:11:38:11:73 | "1n1.qAc~3Q-1t38aF79Xzv5AUEfR5-ct3_" | HardcodedAzureCredentials.java:11:38:11:73 | "1n1.qAc~3Q-1t38aF79Xzv5AUEfR5-ct3_" : String | HardcodedAzureCredentials.java:19:13:19:24 | clientSecret | Hard-coded value flows to $@. | HardcodedAzureCredentials.java:19:13:19:24 | clientSecret | sensitive API call | +| HardcodedAzureCredentials.java:11:38:11:73 | "1n1.qAc~3Q-1t38aF79Xzv5AUEfR5-ct3_" | HardcodedAzureCredentials.java:11:38:11:73 | "1n1.qAc~3Q-1t38aF79Xzv5AUEfR5-ct3_" : String | HardcodedAzureCredentials.java:46:17:46:28 | clientSecret | Hard-coded value flows to $@. | HardcodedAzureCredentials.java:46:17:46:28 | clientSecret | sensitive API call | +| HardcodedShiroKey.java:9:46:9:54 | "TEST123" | HardcodedShiroKey.java:9:46:9:54 | "TEST123" : String | HardcodedShiroKey.java:9:46:9:65 | getBytes(...) | Hard-coded value flows to $@. | HardcodedShiroKey.java:9:46:9:65 | getBytes(...) | sensitive API call | +| HardcodedShiroKey.java:18:61:18:86 | "4AvVhmFLUs0KTA3Kprsdag==" | HardcodedShiroKey.java:18:61:18:86 | "4AvVhmFLUs0KTA3Kprsdag==" : String | HardcodedShiroKey.java:18:46:18:87 | decode(...) | Hard-coded value flows to $@. | HardcodedShiroKey.java:18:46:18:87 | decode(...) | sensitive API call | +| HardcodedShiroKey.java:26:83:26:108 | "6ZmI6I2j5Y+R5aSn5ZOlAA==" | HardcodedShiroKey.java:26:83:26:108 | "6ZmI6I2j5Y+R5aSn5ZOlAA==" : String | HardcodedShiroKey.java:26:46:26:109 | decode(...) | Hard-coded value flows to $@. | HardcodedShiroKey.java:26:46:26:109 | decode(...) | sensitive API call | | Test.java:9:16:9:22 | "admin" | Test.java:9:16:9:22 | "admin" : String | Test.java:15:36:15:38 | usr | Hard-coded value flows to $@. | Test.java:15:36:15:38 | usr | sensitive API call | | Test.java:9:16:9:22 | "admin" | Test.java:9:16:9:22 | "admin" : String | Test.java:17:39:17:41 | usr | Hard-coded value flows to $@. | Test.java:17:39:17:41 | usr | sensitive API call | | Test.java:9:16:9:22 | "admin" | Test.java:9:16:9:22 | "admin" : String | Test.java:18:39:18:41 | usr | Hard-coded value flows to $@. | Test.java:18:39:18:41 | usr | sensitive API call | diff --git a/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedCredentialsSourceCall.expected b/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedCredentialsSourceCall.expected index ff9380ee6ae..a960464f0d9 100644 --- a/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedCredentialsSourceCall.expected +++ b/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedCredentialsSourceCall.expected @@ -1,12 +1,40 @@ edges +| HardcodedAzureCredentials.java:8:14:8:38 | this <.method> [post update] [clientSecret] : String | HardcodedAzureCredentials.java:61:3:61:33 | new HardcodedAzureCredentials(...) [clientSecret] : String | +| HardcodedAzureCredentials.java:8:14:8:38 | this <.method> [post update] [username] : String | HardcodedAzureCredentials.java:61:3:61:33 | new HardcodedAzureCredentials(...) [username] : String | +| HardcodedAzureCredentials.java:10:2:10:68 | this <.field> [post update] [username] : String | HardcodedAzureCredentials.java:8:14:8:38 | this <.method> [post update] [username] : String | +| HardcodedAzureCredentials.java:10:34:10:67 | "username@example.onmicrosoft.com" : String | HardcodedAzureCredentials.java:10:2:10:68 | this <.field> [post update] [username] : String | +| HardcodedAzureCredentials.java:11:2:11:74 | this <.field> [post update] [clientSecret] : String | HardcodedAzureCredentials.java:8:14:8:38 | this <.method> [post update] [clientSecret] : String | +| HardcodedAzureCredentials.java:11:38:11:73 | "1n1.qAc~3Q-1t38aF79Xzv5AUEfR5-ct3_" : String | HardcodedAzureCredentials.java:11:2:11:74 | this <.field> [post update] [clientSecret] : String | +| HardcodedAzureCredentials.java:15:14:15:42 | parameter this [clientSecret] : String | HardcodedAzureCredentials.java:19:13:19:24 | this <.field> [clientSecret] : String | +| HardcodedAzureCredentials.java:15:14:15:42 | parameter this [username] : String | HardcodedAzureCredentials.java:18:13:18:20 | this <.field> [username] : String | +| HardcodedAzureCredentials.java:18:13:18:20 | this <.field> [username] : String | HardcodedAzureCredentials.java:18:13:18:20 | username | +| HardcodedAzureCredentials.java:19:13:19:24 | this <.field> [clientSecret] : String | HardcodedAzureCredentials.java:19:13:19:24 | clientSecret | +| HardcodedAzureCredentials.java:61:3:61:33 | new HardcodedAzureCredentials(...) [clientSecret] : String | HardcodedAzureCredentials.java:15:14:15:42 | parameter this [clientSecret] : String | +| HardcodedAzureCredentials.java:61:3:61:33 | new HardcodedAzureCredentials(...) [username] : String | HardcodedAzureCredentials.java:15:14:15:42 | parameter this [username] : String | | Test.java:10:17:10:24 | "123456" : String | Test.java:26:17:26:20 | pass | | User.java:2:43:2:50 | "123456" : String | User.java:5:15:5:24 | DEFAULT_PW | nodes +| HardcodedAzureCredentials.java:8:14:8:38 | this <.method> [post update] [clientSecret] : String | semmle.label | this <.method> [post update] [clientSecret] : String | +| HardcodedAzureCredentials.java:8:14:8:38 | this <.method> [post update] [username] : String | semmle.label | this <.method> [post update] [username] : String | +| HardcodedAzureCredentials.java:10:2:10:68 | this <.field> [post update] [username] : String | semmle.label | this <.field> [post update] [username] : String | +| HardcodedAzureCredentials.java:10:34:10:67 | "username@example.onmicrosoft.com" : String | semmle.label | "username@example.onmicrosoft.com" : String | +| HardcodedAzureCredentials.java:11:2:11:74 | this <.field> [post update] [clientSecret] : String | semmle.label | this <.field> [post update] [clientSecret] : String | +| HardcodedAzureCredentials.java:11:38:11:73 | "1n1.qAc~3Q-1t38aF79Xzv5AUEfR5-ct3_" : String | semmle.label | "1n1.qAc~3Q-1t38aF79Xzv5AUEfR5-ct3_" : String | +| HardcodedAzureCredentials.java:15:14:15:42 | parameter this [clientSecret] : String | semmle.label | parameter this [clientSecret] : String | +| HardcodedAzureCredentials.java:15:14:15:42 | parameter this [username] : String | semmle.label | parameter this [username] : String | +| HardcodedAzureCredentials.java:18:13:18:20 | this <.field> [username] : String | semmle.label | this <.field> [username] : String | +| HardcodedAzureCredentials.java:18:13:18:20 | username | semmle.label | username | +| HardcodedAzureCredentials.java:19:13:19:24 | clientSecret | semmle.label | clientSecret | +| HardcodedAzureCredentials.java:19:13:19:24 | this <.field> [clientSecret] : String | semmle.label | this <.field> [clientSecret] : String | +| HardcodedAzureCredentials.java:61:3:61:33 | new HardcodedAzureCredentials(...) [clientSecret] : String | semmle.label | new HardcodedAzureCredentials(...) [clientSecret] : String | +| HardcodedAzureCredentials.java:61:3:61:33 | new HardcodedAzureCredentials(...) [username] : String | semmle.label | new HardcodedAzureCredentials(...) [username] : String | | Test.java:10:17:10:24 | "123456" : String | semmle.label | "123456" : String | | Test.java:26:17:26:20 | pass | semmle.label | pass | | User.java:2:43:2:50 | "123456" : String | semmle.label | "123456" : String | | User.java:5:15:5:24 | DEFAULT_PW | semmle.label | DEFAULT_PW | subpaths #select +| HardcodedAzureCredentials.java:10:34:10:67 | "username@example.onmicrosoft.com" | HardcodedAzureCredentials.java:10:34:10:67 | "username@example.onmicrosoft.com" : String | HardcodedAzureCredentials.java:18:13:18:20 | username | Hard-coded value flows to $@. | HardcodedAzureCredentials.java:18:13:18:20 | username | sensitive call | +| HardcodedAzureCredentials.java:11:38:11:73 | "1n1.qAc~3Q-1t38aF79Xzv5AUEfR5-ct3_" | HardcodedAzureCredentials.java:11:38:11:73 | "1n1.qAc~3Q-1t38aF79Xzv5AUEfR5-ct3_" : String | HardcodedAzureCredentials.java:19:13:19:24 | clientSecret | Hard-coded value flows to $@. | HardcodedAzureCredentials.java:19:13:19:24 | clientSecret | sensitive call | | Test.java:10:17:10:24 | "123456" | Test.java:10:17:10:24 | "123456" : String | Test.java:26:17:26:20 | pass | Hard-coded value flows to $@. | Test.java:26:17:26:20 | pass | sensitive call | | User.java:2:43:2:50 | "123456" | User.java:2:43:2:50 | "123456" : String | User.java:5:15:5:24 | DEFAULT_PW | Hard-coded value flows to $@. | User.java:5:15:5:24 | DEFAULT_PW | sensitive call | diff --git a/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedShiroKey.java b/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedShiroKey.java new file mode 100644 index 00000000000..3647af01ed1 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-798/semmle/tests/HardcodedShiroKey.java @@ -0,0 +1,40 @@ +import org.apache.shiro.web.mgt.CookieRememberMeManager; + + +public class HardcodedShiroKey { + + //BAD: hard-coded shiro key + public void testHardcodedShiroKey(String input) { + CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); + cookieRememberMeManager.setCipherKey("TEST123".getBytes()); + + } + + + //BAD: hard-coded shiro key encoded by java.util.Base64 + public void testHardcodedbase64ShiroKey1(String input) { + CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); + java.util.Base64.Decoder decoder = java.util.Base64.getDecoder(); + cookieRememberMeManager.setCipherKey(decoder.decode("4AvVhmFLUs0KTA3Kprsdag==")); + + } + + + //BAD: hard-coded shiro key encoded by org.apache.shiro.codec.Base64 + public void testHardcodedbase64ShiroKey2(String input) { + CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); + cookieRememberMeManager.setCipherKey(org.apache.shiro.codec.Base64.decode("6ZmI6I2j5Y+R5aSn5ZOlAA==")); + + } + + //GOOD: random shiro key + public void testRandomShiroKey(String input) { + CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); + } + + + + + + +} \ No newline at end of file diff --git a/java/ql/test/query-tests/security/CWE-798/semmle/tests/options b/java/ql/test/query-tests/security/CWE-798/semmle/tests/options index e13da5319f0..2d21958e299 100644 --- a/java/ql/test/query-tests/security/CWE-798/semmle/tests/options +++ b/java/ql/test/query-tests/security/CWE-798/semmle/tests/options @@ -1 +1 @@ -// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/amazon-aws-sdk-1.11.700 +// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/amazon-aws-sdk-1.11.700:${testdir}/../../../../../stubs/azure-sdk-for-java:${testdir}/../../../../../stubs/shiro-core-1.4.0 diff --git a/java/ql/test/query-tests/security/CWE-927/SensitiveCommunication.expected b/java/ql/test/query-tests/security/CWE-927/SensitiveCommunication.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.java b/java/ql/test/query-tests/security/CWE-927/SensitiveCommunication.java similarity index 93% rename from java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.java rename to java/ql/test/query-tests/security/CWE-927/SensitiveCommunication.java index 0529cf3c421..f68f54601d8 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.java +++ b/java/ql/test/query-tests/security/CWE-927/SensitiveCommunication.java @@ -11,7 +11,7 @@ class SensitiveBroadcast { intent.setAction("com.example.custom_action"); intent.putExtra("token", token); intent.putExtra("refreshToken", refreshToken); - context.sendBroadcast(intent); + context.sendBroadcast(intent); // $ hasTaintFlow } // BAD - Tests broadcast of sensitive user information with intent extra. @@ -23,7 +23,7 @@ class SensitiveBroadcast { intent.setAction("com.example.custom_action"); intent.putExtra("name", userName); intent.putExtra("pwd", password); - context.sendBroadcast(intent); + context.sendBroadcast(intent); // $ hasTaintFlow } // BAD - Tests broadcast of email information with extra bundle. @@ -35,7 +35,7 @@ class SensitiveBroadcast { Bundle bundle = new Bundle(); bundle.putString("email", email); intent.putExtras(bundle); - context.sendBroadcast(intent); + context.sendBroadcast(intent); // $ hasTaintFlow } // BAD - Tests broadcast of sensitive user information with null permission. @@ -49,7 +49,7 @@ class SensitiveBroadcast { userinfo.add(username); userinfo.add(password); intent.putStringArrayListExtra("userinfo", userinfo); - context.sendBroadcast(intent, null); + context.sendBroadcast(intent, null); // $ hasTaintFlow } // GOOD - Tests broadcast of sensitive user information with permission using string literal. @@ -72,7 +72,7 @@ class SensitiveBroadcast { intent.setAction("com.example.custom_action"); intent.putExtra("ticket", ticket); String perm = "com.example.user_permission"; - context.sendBroadcast(intent, perm); + context.sendBroadcast(intent, perm); } // GOOD - Tests broadcast of sensitive user information to a specific application. @@ -95,7 +95,7 @@ class SensitiveBroadcast { Intent intent = new Intent(); intent.setAction("com.example.custom_action"); intent.putExtra("ticket", ticket); - context.sendBroadcastWithMultiplePermissions(intent, new String[]{}); + context.sendBroadcastWithMultiplePermissions(intent, new String[]{}); // $ hasTaintFlow } // BAD - Tests broadcast of sensitive user information with multiple permissions using empty array initialization through a variable. @@ -108,7 +108,7 @@ class SensitiveBroadcast { intent.putExtra("name", username); intent.putExtra("pwd", passcode); String[] perms = new String[0]; - context.sendBroadcastWithMultiplePermissions(intent, perms); + context.sendBroadcastWithMultiplePermissions(intent, perms); // $ hasTaintFlow } // GOOD - Tests broadcast of sensitive user information with multiple permissions. @@ -133,16 +133,15 @@ class SensitiveBroadcast { intent.setAction("com.example.custom_action"); Bundle bundle = new Bundle(); bundle.putString("name", username); - bundle.putString("pwd", passwd); + bundle.putString("pwd", passwd); intent.putExtras(bundle); String[] perms = new String[0]; String[] perms2 = perms; - context.sendBroadcastWithMultiplePermissions(intent, perms2); + context.sendBroadcastWithMultiplePermissions(intent, perms2); // $ hasTaintFlow } /** * BAD - Tests broadcast of sensitive user information with multiple permissions using empty array initialization through two variables and `intent.getExtras().putString()`. - * Note this case of `getExtras().putString(...)` is not yet detected thus is beyond what the query is capable of. */ public void sendBroadcast12(Context context) { String username = "test123"; @@ -156,7 +155,7 @@ class SensitiveBroadcast { intent.getExtras().putString("pwd", password); String[] perms = new String[0]; String[] perms2 = perms; - context.sendBroadcastWithMultiplePermissions(intent, perms2); + context.sendBroadcastWithMultiplePermissions(intent, perms2); // $ hasTaintFlow } // GOOD - Tests broadcast of sensitive user information with ordered broadcast. diff --git a/java/ql/test/query-tests/security/CWE-927/SensitiveCommunication.ql b/java/ql/test/query-tests/security/CWE-927/SensitiveCommunication.ql new file mode 100644 index 00000000000..ae632646c96 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-927/SensitiveCommunication.ql @@ -0,0 +1,12 @@ +import java +import semmle.code.java.security.AndroidSensitiveCommunicationQuery +import TestUtilities.InlineExpectationsTest +import TestUtilities.InlineFlowTest + +class HasFlowTest extends InlineFlowTest { + override DataFlow::Configuration getTaintFlowConfig() { + result = any(SensitiveCommunicationConfig c) + } + + override DataFlow::Configuration getValueFlowConfig() { none() } +} diff --git a/java/ql/test/query-tests/security/CWE-927/options b/java/ql/test/query-tests/security/CWE-927/options new file mode 100644 index 00000000000..f017f81ff2f --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-927/options @@ -0,0 +1 @@ +// semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/google-android-9.0.0 diff --git a/java/ql/test/stubs/android/android/accounts/Account.java b/java/ql/test/stubs/android/android/accounts/Account.java new file mode 100644 index 00000000000..806f076452e --- /dev/null +++ b/java/ql/test/stubs/android/android/accounts/Account.java @@ -0,0 +1,21 @@ +// Generated automatically from android.accounts.Account for testing purposes + +package android.accounts; + +import android.os.Parcel; +import android.os.Parcelable; + +public class Account implements Parcelable +{ + protected Account() {} + public Account(Parcel p0){} + public Account(String p0, String p1){} + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public final String name = null; + public final String type = null; + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/BroadcastReceiver.java b/java/ql/test/stubs/android/android/content/BroadcastReceiver.java index 1d73018c96d..93375c04d85 100644 --- a/java/ql/test/stubs/android/android/content/BroadcastReceiver.java +++ b/java/ql/test/stubs/android/android/content/BroadcastReceiver.java @@ -1,255 +1,45 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Generated automatically from android.content.BroadcastReceiver for testing purposes + package android.content; +import android.content.Context; +import android.content.Intent; import android.os.Bundle; -/** - * Base class for code that will receive intents sent by sendBroadcast(). - * - *

    If you don't need to send broadcasts across applications, consider using - * this class with {@link android.support.v4.content.LocalBroadcastManager} instead - * of the more general facilities described below. This will give you a much - * more efficient implementation (no cross-process communication needed) and allow - * you to avoid thinking about any security issues related to other applications - * being able to receive or send your broadcasts. - * - *

    You can either dynamically register an instance of this class with - * {@link Context#registerReceiver Context.registerReceiver()} - * or statically publish an implementation through the - * {@link android.R.styleable#AndroidManifestReceiver <receiver>} - * tag in your AndroidManifest.xml. - * - *

    Note: - *    If registering a receiver in your - * {@link android.app.Activity#onResume() Activity.onResume()} - * implementation, you should unregister it in - * {@link android.app.Activity#onPause() Activity.onPause()}. - * (You won't receive intents when paused, - * and this will cut down on unnecessary system overhead). Do not unregister in - * {@link android.app.Activity#onSaveInstanceState(android.os.Bundle) Activity.onSaveInstanceState()}, - * because this won't be called if the user moves back in the history - * stack. - * - *

    There are two major classes of broadcasts that can be received:

    - *
      - *
    • Normal broadcasts (sent with {@link Context#sendBroadcast(Intent) - * Context.sendBroadcast}) are completely asynchronous. All receivers of the - * broadcast are run in an undefined order, often at the same time. This is - * more efficient, but means that receivers cannot use the result or abort - * APIs included here. - *
    • Ordered broadcasts (sent with {@link Context#sendOrderedBroadcast(Intent, String) - * Context.sendOrderedBroadcast}) are delivered to one receiver at a time. - * As each receiver executes in turn, it can propagate a result to the next - * receiver, or it can completely abort the broadcast so that it won't be passed - * to other receivers. The order receivers run in can be controlled with the - * {@link android.R.styleable#AndroidManifestIntentFilter_priority - * android:priority} attribute of the matching intent-filter; receivers with - * the same priority will be run in an arbitrary order. - *
    - * - *

    Even in the case of normal broadcasts, the system may in some - * situations revert to delivering the broadcast one receiver at a time. In - * particular, for receivers that may require the creation of a process, only - * one will be run at a time to avoid overloading the system with new processes. - * In this situation, however, the non-ordered semantics hold: these receivers still - * cannot return results or abort their broadcast.

    - * - *

    Note that, although the Intent class is used for sending and receiving - * these broadcasts, the Intent broadcast mechanism here is completely separate - * from Intents that are used to start Activities with - * {@link Context#startActivity Context.startActivity()}. - * There is no way for a BroadcastReceiver - * to see or capture Intents used with startActivity(); likewise, when - * you broadcast an Intent, you will never find or start an Activity. - * These two operations are semantically very different: starting an - * Activity with an Intent is a foreground operation that modifies what the - * user is currently interacting with; broadcasting an Intent is a background - * operation that the user is not normally aware of. - * - *

    The BroadcastReceiver class (when launched as a component through - * a manifest's {@link android.R.styleable#AndroidManifestReceiver <receiver>} - * tag) is an important part of an - * application's overall lifecycle.

    - * - *

    Topics covered here: - *

      - *
    1. Security - *
    2. Receiver Lifecycle - *
    3. Process Lifecycle - *
    - * - *
    - *

    Developer Guides

    - *

    For information about how to use this class to receive and resolve intents, read the - * Intents and Intent Filters - * developer guide.

    - *
    - * - * - *

    Security

    - * - *

    Receivers used with the {@link Context} APIs are by their nature a - * cross-application facility, so you must consider how other applications - * may be able to abuse your use of them. Some things to consider are: - * - *

      - *
    • The Intent namespace is global. Make sure that Intent action names and - * other strings are written in a namespace you own, or else you may inadvertantly - * conflict with other applications. - *

    • When you use {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)}, - * any application may send broadcasts to that registered receiver. You can - * control who can send broadcasts to it through permissions described below. - *

    • When you publish a receiver in your application's manifest and specify - * intent-filters for it, any other application can send broadcasts to it regardless - * of the filters you specify. To prevent others from sending to it, make it - * unavailable to them with android:exported="false". - *

    • When you use {@link Context#sendBroadcast(Intent)} or related methods, - * normally any other application can receive these broadcasts. You can control who - * can receive such broadcasts through permissions described below. Alternatively, - * starting with {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, you - * can also safely restrict the broadcast to a single application with - * {@link Intent#setPackage(String) Intent.setPackage} - *

    - * - *

    None of these issues exist when using - * {@link android.support.v4.content.LocalBroadcastManager}, since intents - * broadcast it never go outside of the current process. - * - *

    Access permissions can be enforced by either the sender or receiver - * of a broadcast. - * - *

    To enforce a permission when sending, you supply a non-null - * permission argument to - * {@link Context#sendBroadcast(Intent, String)} or - * {@link Context#sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler, int, String, Bundle)}. - * Only receivers who have been granted this permission - * (by requesting it with the - * {@link android.R.styleable#AndroidManifestUsesPermission <uses-permission>} - * tag in their AndroidManifest.xml) will be able to receive - * the broadcast. - * - *

    To enforce a permission when receiving, you supply a non-null - * permission when registering your receiver -- either when calling - * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter, String, android.os.Handler)} - * or in the static - * {@link android.R.styleable#AndroidManifestReceiver <receiver>} - * tag in your AndroidManifest.xml. Only broadcasters who have - * been granted this permission (by requesting it with the - * {@link android.R.styleable#AndroidManifestUsesPermission <uses-permission>} - * tag in their AndroidManifest.xml) will be able to send an - * Intent to the receiver. - * - *

    See the Security and Permissions - * document for more information on permissions and security in general. - * - * - *

    Receiver Lifecycle

    - * - *

    A BroadcastReceiver object is only valid for the duration of the call - * to {@link #onReceive}. Once your code returns from this function, - * the system considers the object to be finished and no longer active. - * - *

    This has important repercussions to what you can do in an - * {@link #onReceive} implementation: anything that requires asynchronous - * operation is not available, because you will need to return from the - * function to handle the asynchronous operation, but at that point the - * BroadcastReceiver is no longer active and thus the system is free to kill - * its process before the asynchronous operation completes. - * - *

    In particular, you may not show a dialog or bind to a service from - * within a BroadcastReceiver. For the former, you should instead use the - * {@link android.app.NotificationManager} API. For the latter, you can - * use {@link android.content.Context#startService Context.startService()} to - * send a command to the service. - * - * - *

    Process Lifecycle

    - * - *

    A process that is currently executing a BroadcastReceiver (that is, - * currently running the code in its {@link #onReceive} method) is - * considered to be a foreground process and will be kept running by the - * system except under cases of extreme memory pressure. - * - *

    Once you return from onReceive(), the BroadcastReceiver is no longer - * active, and its hosting process is only as important as any other application - * components that are running in it. This is especially important because if - * that process was only hosting the BroadcastReceiver (a common case for - * applications that the user has never or not recently interacted with), then - * upon returning from onReceive() the system will consider its process - * to be empty and aggressively kill it so that resources are available for other - * more important processes. - * - *

    This means that for longer-running operations you will often use - * a {@link android.app.Service} in conjunction with a BroadcastReceiver to keep - * the containing process active for the entire time of your operation. - */ -public abstract class BroadcastReceiver { - - /** - * State for a result that is pending for a broadcast receiver. Returned - * by {@link BroadcastReceiver#goAsync() goAsync()} - * while in {@link BroadcastReceiver#onReceive BroadcastReceiver.onReceive()}. - * This allows you to return from onReceive() without having the broadcast - * terminate; you must call {@link #finish()} once you are done with the - * broadcast. This allows you to process the broadcast off of the main - * thread of your app. - * - *

    Note on threading: the state inside of this class is not itself - * thread-safe, however you can use it from any thread if you properly - * sure that you do not have races. Typically this means you will hand - * the entire object to another thread, which will be solely responsible - * for setting any results and finally calling {@link #finish()}. - */ - - public BroadcastReceiver() { +import android.os.IBinder; + +abstract public class BroadcastReceiver +{ + public BroadcastReceiver(){} + public IBinder peekService(Context p0, Intent p1){ return null; } + public abstract void onReceive(Context p0, Intent p1); + public final BroadcastReceiver.PendingResult goAsync(){ return null; } + public final Bundle getResultExtras(boolean p0){ return null; } + public final String getResultData(){ return null; } + public final boolean getAbortBroadcast(){ return false; } + public final boolean getDebugUnregister(){ return false; } + public final boolean isInitialStickyBroadcast(){ return false; } + public final boolean isOrderedBroadcast(){ return false; } + public final int getResultCode(){ return 0; } + public final void abortBroadcast(){} + public final void clearAbortBroadcast(){} + public final void setDebugUnregister(boolean p0){} + public final void setOrderedHint(boolean p0){} + public final void setResult(int p0, String p1, Bundle p2){} + public final void setResultCode(int p0){} + public final void setResultData(String p0){} + public final void setResultExtras(Bundle p0){} + static public class PendingResult + { + public final Bundle getResultExtras(boolean p0){ return null; } + public final String getResultData(){ return null; } + public final boolean getAbortBroadcast(){ return false; } + public final int getResultCode(){ return 0; } + public final void abortBroadcast(){} + public final void clearAbortBroadcast(){} + public final void finish(){} + public final void setResult(int p0, String p1, Bundle p2){} + public final void setResultCode(int p0){} + public final void setResultData(String p0){} + public final void setResultExtras(Bundle p0){} } - /** - * This method is called when the BroadcastReceiver is receiving an Intent - * broadcast. During this time you can use the other methods on - * BroadcastReceiver to view/modify the current result values. This method - * is always called within the main thread of its process, unless you - * explicitly asked for it to be scheduled on a different thread using - * {@link android.content.Context#registerReceiver(BroadcastReceiver, - * IntentFilter, String, android.os.Handler)}. When it runs on the main - * thread you should - * never perform long-running operations in it (there is a timeout of - * 10 seconds that the system allows before considering the receiver to - * be blocked and a candidate to be killed). You cannot launch a popup dialog - * in your implementation of onReceive(). - * - *

    If this BroadcastReceiver was launched through a <receiver> tag, - * then the object is no longer alive after returning from this - * function. This means you should not perform any operations that - * return a result to you asynchronously -- in particular, for interacting - * with services, you should use - * {@link Context#startService(Intent)} instead of - * {@link Context#bindService(Intent, ServiceConnection, int)}. If you wish - * to interact with a service that is already running, you can use - * {@link #peekService}. - * - *

    The Intent filters used in {@link android.content.Context#registerReceiver} - * and in application manifests are not guaranteed to be exclusive. They - * are hints to the operating system about how to find suitable recipients. It is - * possible for senders to force delivery to specific recipients, bypassing filter - * resolution. For this reason, {@link #onReceive(Context, Intent) onReceive()} - * implementations should respond only to known actions, ignoring any unexpected - * Intents that they may receive. - * - * @param context The Context in which the receiver is running. - * @param intent The Intent being received. - */ - public abstract void onReceive(Context context, Intent intent); -} \ No newline at end of file +} diff --git a/java/ql/test/stubs/android/android/content/ClipData.java b/java/ql/test/stubs/android/android/content/ClipData.java new file mode 100644 index 00000000000..490aff3323a --- /dev/null +++ b/java/ql/test/stubs/android/android/content/ClipData.java @@ -0,0 +1,51 @@ +// Generated automatically from android.content.ClipData for testing purposes + +package android.content; + +import android.content.ClipDescription; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +public class ClipData implements Parcelable +{ + protected ClipData() {} + public ClipData(CharSequence p0, String[] p1, ClipData.Item p2){} + public ClipData(ClipData p0){} + public ClipData(ClipDescription p0, ClipData.Item p1){} + public ClipData.Item getItemAt(int p0){ return null; } + public ClipDescription getDescription(){ return null; } + public String toString(){ return null; } + public int describeContents(){ return 0; } + public int getItemCount(){ return 0; } + public static ClipData newHtmlText(CharSequence p0, CharSequence p1, String p2){ return null; } + public static ClipData newIntent(CharSequence p0, Intent p1){ return null; } + public static ClipData newPlainText(CharSequence p0, CharSequence p1){ return null; } + public static ClipData newRawUri(CharSequence p0, Uri p1){ return null; } + public static ClipData newUri(ContentResolver p0, CharSequence p1, Uri p2){ return null; } + public static Parcelable.Creator CREATOR = null; + public void addItem(ClipData.Item p0){} + public void addItem(ContentResolver p0, ClipData.Item p1){} + public void writeToParcel(Parcel p0, int p1){} + static public class Item + { + protected Item() {} + public CharSequence coerceToStyledText(Context p0){ return null; } + public CharSequence coerceToText(Context p0){ return null; } + public CharSequence getText(){ return null; } + public Intent getIntent(){ return null; } + public Item(CharSequence p0){} + public Item(CharSequence p0, Intent p1, Uri p2){} + public Item(CharSequence p0, String p1){} + public Item(CharSequence p0, String p1, Intent p2, Uri p3){} + public Item(Intent p0){} + public Item(Uri p0){} + public String coerceToHtmlText(Context p0){ return null; } + public String getHtmlText(){ return null; } + public String toString(){ return null; } + public Uri getUri(){ return null; } + } +} diff --git a/java/ql/test/stubs/android/android/content/ClipDescription.java b/java/ql/test/stubs/android/android/content/ClipDescription.java new file mode 100644 index 00000000000..34fe5a59715 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/ClipDescription.java @@ -0,0 +1,32 @@ +// Generated automatically from android.content.ClipDescription for testing purposes + +package android.content; + +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PersistableBundle; + +public class ClipDescription implements Parcelable +{ + protected ClipDescription() {} + public CharSequence getLabel(){ return null; } + public ClipDescription(CharSequence p0, String[] p1){} + public ClipDescription(ClipDescription p0){} + public PersistableBundle getExtras(){ return null; } + public String getMimeType(int p0){ return null; } + public String toString(){ return null; } + public String[] filterMimeTypes(String p0){ return null; } + public boolean hasMimeType(String p0){ return false; } + public int describeContents(){ return 0; } + public int getMimeTypeCount(){ return 0; } + public long getTimestamp(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static String MIMETYPE_TEXT_HTML = null; + public static String MIMETYPE_TEXT_INTENT = null; + public static String MIMETYPE_TEXT_PLAIN = null; + public static String MIMETYPE_TEXT_URILIST = null; + public static String MIMETYPE_UNKNOWN = null; + public static boolean compareMimeTypes(String p0, String p1){ return false; } + public void setExtras(PersistableBundle p0){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/ComponentCallbacks.java b/java/ql/test/stubs/android/android/content/ComponentCallbacks.java new file mode 100644 index 00000000000..51726693d00 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/ComponentCallbacks.java @@ -0,0 +1,11 @@ +// Generated automatically from android.content.ComponentCallbacks for testing purposes + +package android.content; + +import android.content.res.Configuration; + +public interface ComponentCallbacks +{ + void onConfigurationChanged(Configuration p0); + void onLowMemory(); +} diff --git a/java/ql/test/stubs/android/android/content/ComponentCallbacks2.java b/java/ql/test/stubs/android/android/content/ComponentCallbacks2.java new file mode 100644 index 00000000000..f8c83ab104d --- /dev/null +++ b/java/ql/test/stubs/android/android/content/ComponentCallbacks2.java @@ -0,0 +1,17 @@ +// Generated automatically from android.content.ComponentCallbacks2 for testing purposes + +package android.content; + +import android.content.ComponentCallbacks; + +public interface ComponentCallbacks2 extends ComponentCallbacks +{ + static int TRIM_MEMORY_BACKGROUND = 0; + static int TRIM_MEMORY_COMPLETE = 0; + static int TRIM_MEMORY_MODERATE = 0; + static int TRIM_MEMORY_RUNNING_CRITICAL = 0; + static int TRIM_MEMORY_RUNNING_LOW = 0; + static int TRIM_MEMORY_RUNNING_MODERATE = 0; + static int TRIM_MEMORY_UI_HIDDEN = 0; + void onTrimMemory(int p0); +} diff --git a/java/ql/test/stubs/android/android/content/ComponentName.java b/java/ql/test/stubs/android/android/content/ComponentName.java new file mode 100644 index 00000000000..2c72a0a5125 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/ComponentName.java @@ -0,0 +1,35 @@ +// Generated automatically from android.content.ComponentName for testing purposes + +package android.content; + +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; + +public class ComponentName implements Cloneable, Comparable, Parcelable +{ + protected ComponentName() {} + public ComponentName clone(){ return null; } + public ComponentName(Context p0, Class p1){} + public ComponentName(Context p0, String p1){} + public ComponentName(Parcel p0){} + public ComponentName(String p0, String p1){} + public String flattenToShortString(){ return null; } + public String flattenToString(){ return null; } + public String getClassName(){ return null; } + public String getPackageName(){ return null; } + public String getShortClassName(){ return null; } + public String toShortString(){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public int compareTo(ComponentName p0){ return 0; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static ComponentName createRelative(Context p0, String p1){ return null; } + public static ComponentName createRelative(String p0, String p1){ return null; } + public static ComponentName readFromParcel(Parcel p0){ return null; } + public static ComponentName unflattenFromString(String p0){ return null; } + public static Parcelable.Creator CREATOR = null; + public static void writeToParcel(ComponentName p0, Parcel p1){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/ContentProvider.java b/java/ql/test/stubs/android/android/content/ContentProvider.java index dfbd61f2ae6..d0928e0e9d0 100644 --- a/java/ql/test/stubs/android/android/content/ContentProvider.java +++ b/java/ql/test/stubs/android/android/content/ContentProvider.java @@ -1,17 +1,82 @@ +// Generated automatically from android.content.ContentProvider for testing purposes + package android.content; +import android.content.ComponentCallbacks2; +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentValues; +import android.content.Context; +import android.content.pm.PathPermission; +import android.content.pm.ProviderInfo; +import android.content.res.AssetFileDescriptor; +import android.content.res.Configuration; import android.database.Cursor; import android.net.Uri; +import android.os.Bundle; import android.os.CancellationSignal; +import android.os.ParcelFileDescriptor; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; -public abstract class ContentProvider { - public abstract int delete(Uri uri, String selection, String[] selectionArgs); - - public abstract Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, - CancellationSignal cancellationSignal); - - public abstract Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder); - - public abstract int update(Uri uri, ContentValues values, String selection, String[] selectionArgs); - +abstract public class ContentProvider implements ComponentCallbacks2 +{ + protected boolean isTemporary(){ return false; } + protected final ParcelFileDescriptor openFileHelper(Uri p0, String p1){ return null; } + protected final void setPathPermissions(PathPermission[] p0){} + protected final void setReadPermission(String p0){} + protected final void setWritePermission(String p0){} + public ParcelFileDescriptor openPipeHelper(Uri p0, String p1, Bundle p2, T p3, ContentProvider.PipeDataWriter p4){ return null; } + public AssetFileDescriptor openAssetFile(Uri p0, String p1){ return null; } + public AssetFileDescriptor openAssetFile(Uri p0, String p1, CancellationSignal p2){ return null; } + public AssetFileDescriptor openTypedAssetFile(Uri p0, String p1, Bundle p2){ return null; } + public AssetFileDescriptor openTypedAssetFile(Uri p0, String p1, Bundle p2, CancellationSignal p3){ return null; } + public Bundle call(String p0, String p1, Bundle p2){ return null; } + public Bundle call(String p0, String p1, String p2, Bundle p3){ return null; } + public ContentProvider(){} + public ContentProviderResult[] applyBatch(ArrayList p0){ return null; } + public ContentProviderResult[] applyBatch(String p0, ArrayList p1){ return null; } + public Cursor query(Uri p0, String[] p1, Bundle p2, CancellationSignal p3){ return null; } + public Cursor query(Uri p0, String[] p1, String p2, String[] p3, String p4, CancellationSignal p5){ return null; } + public ParcelFileDescriptor openFile(Uri p0, String p1){ return null; } + public ParcelFileDescriptor openFile(Uri p0, String p1, CancellationSignal p2){ return null; } + public String[] getStreamTypes(Uri p0, String p1){ return null; } + public Uri canonicalize(Uri p0){ return null; } + public Uri insert(Uri p0, ContentValues p1, Bundle p2){ return null; } + public Uri uncanonicalize(Uri p0){ return null; } + public abstract Cursor query(Uri p0, String[] p1, String p2, String[] p3, String p4); + public abstract String getType(Uri p0); + public abstract Uri insert(Uri p0, ContentValues p1); + public abstract boolean onCreate(); + public abstract int delete(Uri p0, String p1, String[] p2); + public abstract int update(Uri p0, ContentValues p1, String p2, String[] p3); + public boolean refresh(Uri p0, Bundle p1, CancellationSignal p2){ return false; } + public class CallingIdentity + { + } + public final ContentProvider.CallingIdentity clearCallingIdentity(){ return null; } + public final Context getContext(){ return null; } + public final Context requireContext(){ return null; } + public final PathPermission[] getPathPermissions(){ return null; } + public final String getCallingAttributionTag(){ return null; } + public final String getCallingPackage(){ return null; } + public final String getCallingPackageUnchecked(){ return null; } + public final String getReadPermission(){ return null; } + public final String getWritePermission(){ return null; } + public final void restoreCallingIdentity(ContentProvider.CallingIdentity p0){} + public int bulkInsert(Uri p0, ContentValues[] p1){ return 0; } + public int delete(Uri p0, Bundle p1){ return 0; } + public int update(Uri p0, ContentValues p1, Bundle p2){ return 0; } + public void attachInfo(Context p0, ProviderInfo p1){} + public void dump(FileDescriptor p0, PrintWriter p1, String[] p2){} + public void onCallingPackageChanged(){} + public void onConfigurationChanged(Configuration p0){} + public void onLowMemory(){} + public void onTrimMemory(int p0){} + public void shutdown(){} + static public interface PipeDataWriter + { + void writeDataToPipe(ParcelFileDescriptor p0, Uri p1, String p2, Bundle p3, T p4); + } } diff --git a/java/ql/test/stubs/android/android/content/ContentProviderClient.java b/java/ql/test/stubs/android/android/content/ContentProviderClient.java new file mode 100644 index 00000000000..0cb6890ad1b --- /dev/null +++ b/java/ql/test/stubs/android/android/content/ContentProviderClient.java @@ -0,0 +1,49 @@ +// Generated automatically from android.content.ContentProviderClient for testing purposes + +package android.content; + +import android.content.ContentProvider; +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentValues; +import android.content.res.AssetFileDescriptor; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.os.CancellationSignal; +import android.os.ParcelFileDescriptor; +import java.util.ArrayList; + +public class ContentProviderClient implements AutoCloseable +{ + protected void finalize(){} + public AssetFileDescriptor openAssetFile(Uri p0, String p1){ return null; } + public AssetFileDescriptor openAssetFile(Uri p0, String p1, CancellationSignal p2){ return null; } + public Bundle call(String p0, String p1, Bundle p2){ return null; } + public Bundle call(String p0, String p1, String p2, Bundle p3){ return null; } + public ContentProvider getLocalContentProvider(){ return null; } + public ContentProviderResult[] applyBatch(ArrayList p0){ return null; } + public ContentProviderResult[] applyBatch(String p0, ArrayList p1){ return null; } + public Cursor query(Uri p0, String[] p1, Bundle p2, CancellationSignal p3){ return null; } + public Cursor query(Uri p0, String[] p1, String p2, String[] p3, String p4){ return null; } + public Cursor query(Uri p0, String[] p1, String p2, String[] p3, String p4, CancellationSignal p5){ return null; } + public ParcelFileDescriptor openFile(Uri p0, String p1){ return null; } + public ParcelFileDescriptor openFile(Uri p0, String p1, CancellationSignal p2){ return null; } + public String getType(Uri p0){ return null; } + public String[] getStreamTypes(Uri p0, String p1){ return null; } + public Uri insert(Uri p0, ContentValues p1){ return null; } + public Uri insert(Uri p0, ContentValues p1, Bundle p2){ return null; } + public boolean refresh(Uri p0, Bundle p1, CancellationSignal p2){ return false; } + public boolean release(){ return false; } + public final AssetFileDescriptor openTypedAssetFile(Uri p0, String p1, Bundle p2, CancellationSignal p3){ return null; } + public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri p0, String p1, Bundle p2){ return null; } + public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri p0, String p1, Bundle p2, CancellationSignal p3){ return null; } + public final Uri canonicalize(Uri p0){ return null; } + public final Uri uncanonicalize(Uri p0){ return null; } + public int bulkInsert(Uri p0, ContentValues[] p1){ return 0; } + public int delete(Uri p0, Bundle p1){ return 0; } + public int delete(Uri p0, String p1, String[] p2){ return 0; } + public int update(Uri p0, ContentValues p1, Bundle p2){ return 0; } + public int update(Uri p0, ContentValues p1, String p2, String[] p3){ return 0; } + public void close(){} +} diff --git a/java/ql/test/stubs/android/android/content/ContentProviderOperation.java b/java/ql/test/stubs/android/android/content/ContentProviderOperation.java new file mode 100644 index 00000000000..59fb8b366ca --- /dev/null +++ b/java/ql/test/stubs/android/android/content/ContentProviderOperation.java @@ -0,0 +1,58 @@ +// Generated automatically from android.content.ContentProviderOperation for testing purposes + +package android.content; + +import android.content.ContentProvider; +import android.content.ContentProviderResult; +import android.content.ContentValues; +import android.net.Uri; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +public class ContentProviderOperation implements Parcelable +{ + public Bundle resolveExtrasBackReferences(ContentProviderResult[] p0, int p1){ return null; } + public ContentProviderResult apply(ContentProvider p0, ContentProviderResult[] p1, int p2){ return null; } + public ContentValues resolveValueBackReferences(ContentProviderResult[] p0, int p1){ return null; } + public String toString(){ return null; } + public String[] resolveSelectionArgsBackReferences(ContentProviderResult[] p0, int p1){ return null; } + public Uri getUri(){ return null; } + public boolean isAssertQuery(){ return false; } + public boolean isCall(){ return false; } + public boolean isDelete(){ return false; } + public boolean isExceptionAllowed(){ return false; } + public boolean isInsert(){ return false; } + public boolean isReadOperation(){ return false; } + public boolean isUpdate(){ return false; } + public boolean isWriteOperation(){ return false; } + public boolean isYieldAllowed(){ return false; } + public int describeContents(){ return 0; } + public static ContentProviderOperation.Builder newAssertQuery(Uri p0){ return null; } + public static ContentProviderOperation.Builder newCall(Uri p0, String p1, String p2){ return null; } + public static ContentProviderOperation.Builder newDelete(Uri p0){ return null; } + public static ContentProviderOperation.Builder newInsert(Uri p0){ return null; } + public static ContentProviderOperation.Builder newUpdate(Uri p0){ return null; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} + static public class Builder + { + protected Builder() {} + public ContentProviderOperation build(){ return null; } + public ContentProviderOperation.Builder withExceptionAllowed(boolean p0){ return null; } + public ContentProviderOperation.Builder withExpectedCount(int p0){ return null; } + public ContentProviderOperation.Builder withExtra(String p0, Object p1){ return null; } + public ContentProviderOperation.Builder withExtraBackReference(String p0, int p1){ return null; } + public ContentProviderOperation.Builder withExtraBackReference(String p0, int p1, String p2){ return null; } + public ContentProviderOperation.Builder withExtras(Bundle p0){ return null; } + public ContentProviderOperation.Builder withSelection(String p0, String[] p1){ return null; } + public ContentProviderOperation.Builder withSelectionBackReference(int p0, int p1){ return null; } + public ContentProviderOperation.Builder withSelectionBackReference(int p0, int p1, String p2){ return null; } + public ContentProviderOperation.Builder withValue(String p0, Object p1){ return null; } + public ContentProviderOperation.Builder withValueBackReference(String p0, int p1){ return null; } + public ContentProviderOperation.Builder withValueBackReference(String p0, int p1, String p2){ return null; } + public ContentProviderOperation.Builder withValueBackReferences(ContentValues p0){ return null; } + public ContentProviderOperation.Builder withValues(ContentValues p0){ return null; } + public ContentProviderOperation.Builder withYieldAllowed(boolean p0){ return null; } + } +} diff --git a/java/ql/test/stubs/android/android/content/ContentProviderResult.java b/java/ql/test/stubs/android/android/content/ContentProviderResult.java new file mode 100644 index 00000000000..b0f285d5c92 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/ContentProviderResult.java @@ -0,0 +1,26 @@ +// Generated automatically from android.content.ContentProviderResult for testing purposes + +package android.content; + +import android.net.Uri; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +public class ContentProviderResult implements Parcelable +{ + protected ContentProviderResult() {} + public ContentProviderResult(Bundle p0){} + public ContentProviderResult(Parcel p0){} + public ContentProviderResult(Throwable p0){} + public ContentProviderResult(Uri p0){} + public ContentProviderResult(int p0){} + public String toString(){ return null; } + public final Bundle extras = null; + public final Integer count = null; + public final Throwable exception = null; + public final Uri uri = null; + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/ContentResolver.java b/java/ql/test/stubs/android/android/content/ContentResolver.java index 27308ee87be..1d03e20ecf9 100644 --- a/java/ql/test/stubs/android/android/content/ContentResolver.java +++ b/java/ql/test/stubs/android/android/content/ContentResolver.java @@ -1,17 +1,160 @@ +// Generated automatically from android.content.ContentResolver for testing purposes + package android.content; +import android.accounts.Account; +import android.content.ContentProvider; +import android.content.ContentProviderClient; +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentValues; +import android.content.Context; +import android.content.PeriodicSync; +import android.content.SyncAdapterType; +import android.content.SyncInfo; +import android.content.SyncRequest; +import android.content.SyncStatusObserver; +import android.content.UriPermission; +import android.content.res.AssetFileDescriptor; +import android.database.ContentObserver; import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.drawable.Icon; import android.net.Uri; +import android.os.Bundle; import android.os.CancellationSignal; +import android.os.ParcelFileDescriptor; +import android.util.Size; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; -public abstract class ContentResolver { - public abstract int delete(Uri uri, String selection, String[] selectionArgs); - - public abstract Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, - CancellationSignal cancellationSignal); - - public abstract Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder); - - public abstract int update(Uri uri, ContentValues values, String selection, String[] selectionArgs); - +abstract public class ContentResolver +{ + protected ContentResolver() {} + public Bitmap loadThumbnail(Uri p0, Size p1, CancellationSignal p2){ return null; } + public ContentProviderResult[] applyBatch(String p0, ArrayList p1){ return null; } + public ContentResolver(Context p0){} + public List getOutgoingPersistedUriPermissions(){ return null; } + public List getPersistedUriPermissions(){ return null; } + public String[] getStreamTypes(Uri p0, String p1){ return null; } + public final AssetFileDescriptor openAssetFile(Uri p0, String p1, CancellationSignal p2){ return null; } + public final AssetFileDescriptor openAssetFileDescriptor(Uri p0, String p1){ return null; } + public final AssetFileDescriptor openAssetFileDescriptor(Uri p0, String p1, CancellationSignal p2){ return null; } + public final AssetFileDescriptor openTypedAssetFile(Uri p0, String p1, Bundle p2, CancellationSignal p3){ return null; } + public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri p0, String p1, Bundle p2){ return null; } + public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri p0, String p1, Bundle p2, CancellationSignal p3){ return null; } + public final Bundle call(String p0, String p1, String p2, Bundle p3){ return null; } + public final Bundle call(Uri p0, String p1, String p2, Bundle p3){ return null; } + public final ContentProviderClient acquireContentProviderClient(String p0){ return null; } + public final ContentProviderClient acquireContentProviderClient(Uri p0){ return null; } + public final ContentProviderClient acquireUnstableContentProviderClient(String p0){ return null; } + public final ContentProviderClient acquireUnstableContentProviderClient(Uri p0){ return null; } + public final ContentResolver.MimeTypeInfo getTypeInfo(String p0){ return null; } + public final Cursor query(Uri p0, String[] p1, Bundle p2, CancellationSignal p3){ return null; } + public final Cursor query(Uri p0, String[] p1, String p2, String[] p3, String p4){ return null; } + public final Cursor query(Uri p0, String[] p1, String p2, String[] p3, String p4, CancellationSignal p5){ return null; } + public final InputStream openInputStream(Uri p0){ return null; } + public final OutputStream openOutputStream(Uri p0){ return null; } + public final OutputStream openOutputStream(Uri p0, String p1){ return null; } + public final ParcelFileDescriptor openFile(Uri p0, String p1, CancellationSignal p2){ return null; } + public final ParcelFileDescriptor openFileDescriptor(Uri p0, String p1){ return null; } + public final ParcelFileDescriptor openFileDescriptor(Uri p0, String p1, CancellationSignal p2){ return null; } + public final String getType(Uri p0){ return null; } + public final Uri canonicalize(Uri p0){ return null; } + public final Uri insert(Uri p0, ContentValues p1){ return null; } + public final Uri insert(Uri p0, ContentValues p1, Bundle p2){ return null; } + public final Uri uncanonicalize(Uri p0){ return null; } + public final boolean refresh(Uri p0, Bundle p1, CancellationSignal p2){ return false; } + public final int bulkInsert(Uri p0, ContentValues[] p1){ return 0; } + public final int delete(Uri p0, Bundle p1){ return 0; } + public final int delete(Uri p0, String p1, String[] p2){ return 0; } + public final int update(Uri p0, ContentValues p1, Bundle p2){ return 0; } + public final int update(Uri p0, ContentValues p1, String p2, String[] p3){ return 0; } + public final void registerContentObserver(Uri p0, boolean p1, ContentObserver p2){} + public final void unregisterContentObserver(ContentObserver p0){} + public static ContentResolver wrap(ContentProvider p0){ return null; } + public static ContentResolver wrap(ContentProviderClient p0){ return null; } + public static List getPeriodicSyncs(Account p0, String p1){ return null; } + public static List getCurrentSyncs(){ return null; } + public static Object addStatusChangeListener(int p0, SyncStatusObserver p1){ return null; } + public static String ANY_CURSOR_ITEM_TYPE = null; + public static String CURSOR_DIR_BASE_TYPE = null; + public static String CURSOR_ITEM_BASE_TYPE = null; + public static String EXTRA_HONORED_ARGS = null; + public static String EXTRA_REFRESH_SUPPORTED = null; + public static String EXTRA_SIZE = null; + public static String EXTRA_TOTAL_COUNT = null; + public static String QUERY_ARG_GROUP_COLUMNS = null; + public static String QUERY_ARG_LIMIT = null; + public static String QUERY_ARG_OFFSET = null; + public static String QUERY_ARG_SORT_COLLATION = null; + public static String QUERY_ARG_SORT_COLUMNS = null; + public static String QUERY_ARG_SORT_DIRECTION = null; + public static String QUERY_ARG_SORT_LOCALE = null; + public static String QUERY_ARG_SQL_GROUP_BY = null; + public static String QUERY_ARG_SQL_HAVING = null; + public static String QUERY_ARG_SQL_LIMIT = null; + public static String QUERY_ARG_SQL_SELECTION = null; + public static String QUERY_ARG_SQL_SELECTION_ARGS = null; + public static String QUERY_ARG_SQL_SORT_ORDER = null; + public static String SCHEME_ANDROID_RESOURCE = null; + public static String SCHEME_CONTENT = null; + public static String SCHEME_FILE = null; + public static String SYNC_EXTRAS_ACCOUNT = null; + public static String SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS = null; + public static String SYNC_EXTRAS_DO_NOT_RETRY = null; + public static String SYNC_EXTRAS_EXPEDITED = null; + public static String SYNC_EXTRAS_FORCE = null; + public static String SYNC_EXTRAS_IGNORE_BACKOFF = null; + public static String SYNC_EXTRAS_IGNORE_SETTINGS = null; + public static String SYNC_EXTRAS_INITIALIZE = null; + public static String SYNC_EXTRAS_MANUAL = null; + public static String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = null; + public static String SYNC_EXTRAS_REQUIRE_CHARGING = null; + public static String SYNC_EXTRAS_UPLOAD = null; + public static SyncAdapterType[] getSyncAdapterTypes(){ return null; } + public static SyncInfo getCurrentSync(){ return null; } + public static boolean getMasterSyncAutomatically(){ return false; } + public static boolean getSyncAutomatically(Account p0, String p1){ return false; } + public static boolean isSyncActive(Account p0, String p1){ return false; } + public static boolean isSyncPending(Account p0, String p1){ return false; } + public static int NOTIFY_DELETE = 0; + public static int NOTIFY_INSERT = 0; + public static int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 0; + public static int NOTIFY_SYNC_TO_NETWORK = 0; + public static int NOTIFY_UPDATE = 0; + public static int QUERY_SORT_DIRECTION_ASCENDING = 0; + public static int QUERY_SORT_DIRECTION_DESCENDING = 0; + public static int SYNC_OBSERVER_TYPE_ACTIVE = 0; + public static int SYNC_OBSERVER_TYPE_PENDING = 0; + public static int SYNC_OBSERVER_TYPE_SETTINGS = 0; + public static int getIsSyncable(Account p0, String p1){ return 0; } + public static void addPeriodicSync(Account p0, String p1, Bundle p2, long p3){} + public static void cancelSync(Account p0, String p1){} + public static void cancelSync(SyncRequest p0){} + public static void removePeriodicSync(Account p0, String p1, Bundle p2){} + public static void removeStatusChangeListener(Object p0){} + public static void requestSync(Account p0, String p1, Bundle p2){} + public static void requestSync(SyncRequest p0){} + public static void setIsSyncable(Account p0, String p1, int p2){} + public static void setMasterSyncAutomatically(boolean p0){} + public static void setSyncAutomatically(Account p0, String p1, boolean p2){} + public static void validateSyncExtrasBundle(Bundle p0){} + public void cancelSync(Uri p0){} + public void notifyChange(Collection p0, ContentObserver p1, int p2){} + public void notifyChange(Uri p0, ContentObserver p1){} + public void notifyChange(Uri p0, ContentObserver p1, boolean p2){} + public void notifyChange(Uri p0, ContentObserver p1, int p2){} + public void releasePersistableUriPermission(Uri p0, int p1){} + public void startSync(Uri p0, Bundle p1){} + public void takePersistableUriPermission(Uri p0, int p1){} + static public class MimeTypeInfo + { + public CharSequence getContentDescription(){ return null; } + public CharSequence getLabel(){ return null; } + public Icon getIcon(){ return null; } + } } diff --git a/java/ql/test/stubs/android/android/content/ContentValues.java b/java/ql/test/stubs/android/android/content/ContentValues.java index 105c3092d79..f85e1462717 100644 --- a/java/ql/test/stubs/android/android/content/ContentValues.java +++ b/java/ql/test/stubs/android/android/content/ContentValues.java @@ -1,5 +1,50 @@ +// Generated automatically from android.content.ContentValues for testing purposes + package android.content; -public class ContentValues { +import android.os.Parcel; +import android.os.Parcelable; +import java.util.Map; +import java.util.Set; +public class ContentValues implements Parcelable +{ + public Boolean getAsBoolean(String p0){ return null; } + public Byte getAsByte(String p0){ return null; } + public ContentValues(){} + public ContentValues(ContentValues p0){} + public ContentValues(int p0){} + public Double getAsDouble(String p0){ return null; } + public Float getAsFloat(String p0){ return null; } + public Integer getAsInteger(String p0){ return null; } + public Long getAsLong(String p0){ return null; } + public Object get(String p0){ return null; } + public Set> valueSet(){ return null; } + public Set keySet(){ return null; } + public Short getAsShort(String p0){ return null; } + public String getAsString(String p0){ return null; } + public String toString(){ return null; } + public boolean containsKey(String p0){ return false; } + public boolean equals(Object p0){ return false; } + public boolean isEmpty(){ return false; } + public byte[] getAsByteArray(String p0){ return null; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public int size(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static String TAG = null; + public void clear(){} + public void put(String p0, Boolean p1){} + public void put(String p0, Byte p1){} + public void put(String p0, Double p1){} + public void put(String p0, Float p1){} + public void put(String p0, Integer p1){} + public void put(String p0, Long p1){} + public void put(String p0, Short p1){} + public void put(String p0, String p1){} + public void put(String p0, byte[] p1){} + public void putAll(ContentValues p0){} + public void putNull(String p0){} + public void remove(String p0){} + public void writeToParcel(Parcel p0, int p1){} } diff --git a/java/ql/test/stubs/android/android/content/Context.java b/java/ql/test/stubs/android/android/content/Context.java index 118e170d788..7e507e89095 100644 --- a/java/ql/test/stubs/android/android/content/Context.java +++ b/java/ql/test/stubs/android/android/content/Context.java @@ -1,5 +1,265 @@ +// Generated automatically from android.content.Context for testing purposes + package android.content; -public class Context { +import android.content.BroadcastReceiver; +import android.content.ComponentCallbacks; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.IntentSender; +import android.content.ServiceConnection; +import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.res.AssetManager; +import android.content.res.ColorStateList; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.database.DatabaseErrorHandler; +import android.database.sqlite.SQLiteDatabase; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.util.AttributeSet; +import android.view.Display; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.util.concurrent.Executor; +abstract public class Context +{ + public Context createAttributionContext(String p0){ return null; } + public Context createWindowContext(int p0, Bundle p1){ return null; } + public Context(){} + public Display getDisplay(){ return null; } + public Executor getMainExecutor(){ return null; } + public String getAttributionTag(){ return null; } + public String getOpPackageName(){ return null; } + public abstract ApplicationInfo getApplicationInfo(); + public abstract AssetManager getAssets(); + public abstract ClassLoader getClassLoader(); + public abstract ComponentName startForegroundService(Intent p0); + public abstract ComponentName startService(Intent p0); + public abstract ContentResolver getContentResolver(); + public abstract Context createConfigurationContext(Configuration p0); + public abstract Context createContextForSplit(String p0); + public abstract Context createDeviceProtectedStorageContext(); + public abstract Context createDisplayContext(Display p0); + public abstract Context createPackageContext(String p0, int p1); + public abstract Context getApplicationContext(); + public abstract Drawable getWallpaper(); + public abstract Drawable peekWallpaper(); + public abstract File getCacheDir(); + public abstract File getCodeCacheDir(); + public abstract File getDataDir(); + public abstract File getDatabasePath(String p0); + public abstract File getDir(String p0, int p1); + public abstract File getExternalCacheDir(); + public abstract File getExternalFilesDir(String p0); + public abstract File getFileStreamPath(String p0); + public abstract File getFilesDir(); + public abstract File getNoBackupFilesDir(); + public abstract File getObbDir(); + public abstract FileInputStream openFileInput(String p0); + public abstract FileOutputStream openFileOutput(String p0, int p1); + public abstract File[] getExternalCacheDirs(); + public abstract File[] getExternalFilesDirs(String p0); + public abstract File[] getExternalMediaDirs(); + public abstract File[] getObbDirs(); + public abstract Intent registerReceiver(BroadcastReceiver p0, IntentFilter p1); + public abstract Intent registerReceiver(BroadcastReceiver p0, IntentFilter p1, String p2, Handler p3); + public abstract Intent registerReceiver(BroadcastReceiver p0, IntentFilter p1, String p2, Handler p3, int p4); + public abstract Intent registerReceiver(BroadcastReceiver p0, IntentFilter p1, int p2); + public abstract Looper getMainLooper(); + public abstract Object getSystemService(String p0); + public abstract PackageManager getPackageManager(); + public abstract Resources getResources(); + public abstract Resources.Theme getTheme(); + public abstract SQLiteDatabase openOrCreateDatabase(String p0, int p1, SQLiteDatabase.CursorFactory p2); + public abstract SQLiteDatabase openOrCreateDatabase(String p0, int p1, SQLiteDatabase.CursorFactory p2, DatabaseErrorHandler p3); + public abstract SharedPreferences getSharedPreferences(String p0, int p1); + public abstract String getPackageCodePath(); + public abstract String getPackageName(); + public abstract String getPackageResourcePath(); + public abstract String getSystemServiceName(Class p0); + public abstract String[] databaseList(); + public abstract String[] fileList(); + public abstract boolean bindService(Intent p0, ServiceConnection p1, int p2); + public abstract boolean deleteDatabase(String p0); + public abstract boolean deleteFile(String p0); + public abstract boolean deleteSharedPreferences(String p0); + public abstract boolean isDeviceProtectedStorage(); + public abstract boolean moveDatabaseFrom(Context p0, String p1); + public abstract boolean moveSharedPreferencesFrom(Context p0, String p1); + public abstract boolean startInstrumentation(ComponentName p0, String p1, Bundle p2); + public abstract boolean stopService(Intent p0); + public abstract int checkCallingOrSelfPermission(String p0); + public abstract int checkCallingOrSelfUriPermission(Uri p0, int p1); + public abstract int checkCallingPermission(String p0); + public abstract int checkCallingUriPermission(Uri p0, int p1); + public abstract int checkPermission(String p0, int p1, int p2); + public abstract int checkSelfPermission(String p0); + public abstract int checkUriPermission(Uri p0, String p1, String p2, int p3, int p4, int p5); + public abstract int checkUriPermission(Uri p0, int p1, int p2, int p3); + public abstract int getWallpaperDesiredMinimumHeight(); + public abstract int getWallpaperDesiredMinimumWidth(); + public abstract void clearWallpaper(); + public abstract void enforceCallingOrSelfPermission(String p0, String p1); + public abstract void enforceCallingOrSelfUriPermission(Uri p0, int p1, String p2); + public abstract void enforceCallingPermission(String p0, String p1); + public abstract void enforceCallingUriPermission(Uri p0, int p1, String p2); + public abstract void enforcePermission(String p0, int p1, int p2, String p3); + public abstract void enforceUriPermission(Uri p0, String p1, String p2, int p3, int p4, int p5, String p6); + public abstract void enforceUriPermission(Uri p0, int p1, int p2, int p3, String p4); + public abstract void grantUriPermission(String p0, Uri p1, int p2); + public abstract void removeStickyBroadcast(Intent p0); + public abstract void removeStickyBroadcastAsUser(Intent p0, UserHandle p1); + public abstract void revokeUriPermission(String p0, Uri p1, int p2); + public abstract void revokeUriPermission(Uri p0, int p1); + public abstract void sendBroadcast(Intent p0); + public abstract void sendBroadcast(Intent p0, String p1); + public abstract void sendBroadcastAsUser(Intent p0, UserHandle p1); + public abstract void sendBroadcastAsUser(Intent p0, UserHandle p1, String p2); + public abstract void sendOrderedBroadcast(Intent p0, String p1); + public abstract void sendOrderedBroadcast(Intent p0, String p1, BroadcastReceiver p2, Handler p3, int p4, String p5, Bundle p6); + public abstract void sendOrderedBroadcastAsUser(Intent p0, UserHandle p1, String p2, BroadcastReceiver p3, Handler p4, int p5, String p6, Bundle p7); + public abstract void sendStickyBroadcast(Intent p0); + public abstract void sendStickyBroadcastAsUser(Intent p0, UserHandle p1); + public abstract void sendStickyOrderedBroadcast(Intent p0, BroadcastReceiver p1, Handler p2, int p3, String p4, Bundle p5); + public abstract void sendStickyOrderedBroadcastAsUser(Intent p0, UserHandle p1, BroadcastReceiver p2, Handler p3, int p4, String p5, Bundle p6); + public abstract void setTheme(int p0); + public abstract void setWallpaper(Bitmap p0); + public abstract void setWallpaper(InputStream p0); + public abstract void startActivities(Intent[] p0); + public abstract void startActivities(Intent[] p0, Bundle p1); + public abstract void startActivity(Intent p0); + public abstract void startActivity(Intent p0, Bundle p1); + public abstract void startIntentSender(IntentSender p0, Intent p1, int p2, int p3, int p4); + public abstract void startIntentSender(IntentSender p0, Intent p1, int p2, int p3, int p4, Bundle p5); + public abstract void unbindService(ServiceConnection p0); + public abstract void unregisterReceiver(BroadcastReceiver p0); + public boolean bindIsolatedService(Intent p0, int p1, String p2, Executor p3, ServiceConnection p4){ return false; } + public boolean bindService(Intent p0, int p1, Executor p2, ServiceConnection p3){ return false; } + public boolean bindServiceAsUser(Intent p0, ServiceConnection p1, int p2, UserHandle p3){ return false; } + public boolean isRestricted(){ return false; } + public final T getSystemService(Class p0){ return null; } + public final CharSequence getText(int p0){ return null; } + public final ColorStateList getColorStateList(int p0){ return null; } + public final Drawable getDrawable(int p0){ return null; } + public final String getString(int p0){ return null; } + public final String getString(int p0, Object... p1){ return null; } + public final TypedArray obtainStyledAttributes(AttributeSet p0, int[] p1){ return null; } + public final TypedArray obtainStyledAttributes(AttributeSet p0, int[] p1, int p2, int p3){ return null; } + public final TypedArray obtainStyledAttributes(int p0, int[] p1){ return null; } + public final TypedArray obtainStyledAttributes(int[] p0){ return null; } + public final int getColor(int p0){ return 0; } + public static String ACCESSIBILITY_SERVICE = null; + public static String ACCOUNT_SERVICE = null; + public static String ACTIVITY_SERVICE = null; + public static String ALARM_SERVICE = null; + public static String APPWIDGET_SERVICE = null; + public static String APP_OPS_SERVICE = null; + public static String AUDIO_SERVICE = null; + public static String BATTERY_SERVICE = null; + public static String BIOMETRIC_SERVICE = null; + public static String BLOB_STORE_SERVICE = null; + public static String BLUETOOTH_SERVICE = null; + public static String CAMERA_SERVICE = null; + public static String CAPTIONING_SERVICE = null; + public static String CARRIER_CONFIG_SERVICE = null; + public static String CLIPBOARD_SERVICE = null; + public static String COMPANION_DEVICE_SERVICE = null; + public static String CONNECTIVITY_DIAGNOSTICS_SERVICE = null; + public static String CONNECTIVITY_SERVICE = null; + public static String CONSUMER_IR_SERVICE = null; + public static String CROSS_PROFILE_APPS_SERVICE = null; + public static String DEVICE_POLICY_SERVICE = null; + public static String DISPLAY_SERVICE = null; + public static String DOWNLOAD_SERVICE = null; + public static String DROPBOX_SERVICE = null; + public static String EUICC_SERVICE = null; + public static String FILE_INTEGRITY_SERVICE = null; + public static String FINGERPRINT_SERVICE = null; + public static String HARDWARE_PROPERTIES_SERVICE = null; + public static String INPUT_METHOD_SERVICE = null; + public static String INPUT_SERVICE = null; + public static String IPSEC_SERVICE = null; + public static String JOB_SCHEDULER_SERVICE = null; + public static String KEYGUARD_SERVICE = null; + public static String LAUNCHER_APPS_SERVICE = null; + public static String LAYOUT_INFLATER_SERVICE = null; + public static String LOCATION_SERVICE = null; + public static String MEDIA_PROJECTION_SERVICE = null; + public static String MEDIA_ROUTER_SERVICE = null; + public static String MEDIA_SESSION_SERVICE = null; + public static String MIDI_SERVICE = null; + public static String NETWORK_STATS_SERVICE = null; + public static String NFC_SERVICE = null; + public static String NOTIFICATION_SERVICE = null; + public static String NSD_SERVICE = null; + public static String POWER_SERVICE = null; + public static String PRINT_SERVICE = null; + public static String RESTRICTIONS_SERVICE = null; + public static String ROLE_SERVICE = null; + public static String SEARCH_SERVICE = null; + public static String SENSOR_SERVICE = null; + public static String SHORTCUT_SERVICE = null; + public static String STORAGE_SERVICE = null; + public static String STORAGE_STATS_SERVICE = null; + public static String SYSTEM_HEALTH_SERVICE = null; + public static String TELECOM_SERVICE = null; + public static String TELEPHONY_IMS_SERVICE = null; + public static String TELEPHONY_SERVICE = null; + public static String TELEPHONY_SUBSCRIPTION_SERVICE = null; + public static String TEXT_CLASSIFICATION_SERVICE = null; + public static String TEXT_SERVICES_MANAGER_SERVICE = null; + public static String TV_INPUT_SERVICE = null; + public static String UI_MODE_SERVICE = null; + public static String USAGE_STATS_SERVICE = null; + public static String USB_SERVICE = null; + public static String USER_SERVICE = null; + public static String VIBRATOR_SERVICE = null; + public static String VPN_MANAGEMENT_SERVICE = null; + public static String WALLPAPER_SERVICE = null; + public static String WIFI_AWARE_SERVICE = null; + public static String WIFI_P2P_SERVICE = null; + public static String WIFI_RTT_RANGING_SERVICE = null; + public static String WIFI_SERVICE = null; + public static String WINDOW_SERVICE = null; + public static int BIND_ABOVE_CLIENT = 0; + public static int BIND_ADJUST_WITH_ACTIVITY = 0; + public static int BIND_ALLOW_OOM_MANAGEMENT = 0; + public static int BIND_AUTO_CREATE = 0; + public static int BIND_DEBUG_UNBIND = 0; + public static int BIND_EXTERNAL_SERVICE = 0; + public static int BIND_IMPORTANT = 0; + public static int BIND_INCLUDE_CAPABILITIES = 0; + public static int BIND_NOT_FOREGROUND = 0; + public static int BIND_NOT_PERCEPTIBLE = 0; + public static int BIND_WAIVE_PRIORITY = 0; + public static int CONTEXT_IGNORE_SECURITY = 0; + public static int CONTEXT_INCLUDE_CODE = 0; + public static int CONTEXT_RESTRICTED = 0; + public static int MODE_APPEND = 0; + public static int MODE_ENABLE_WRITE_AHEAD_LOGGING = 0; + public static int MODE_MULTI_PROCESS = 0; + public static int MODE_NO_LOCALIZED_COLLATORS = 0; + public static int MODE_PRIVATE = 0; + public static int MODE_WORLD_READABLE = 0; + public static int MODE_WORLD_WRITEABLE = 0; + public static int RECEIVER_VISIBLE_TO_INSTANT_APPS = 0; + public void registerComponentCallbacks(ComponentCallbacks p0){} + public void sendBroadcastWithMultiplePermissions(Intent p0, String[] p1){} + public void sendOrderedBroadcast(Intent p0, String p1, String p2, BroadcastReceiver p3, Handler p4, int p5, String p6, Bundle p7){} + public void unregisterComponentCallbacks(ComponentCallbacks p0){} + public void updateServiceGroup(ServiceConnection p0, int p1, int p2){} } diff --git a/java/ql/test/stubs/android/android/content/Intent.java b/java/ql/test/stubs/android/android/content/Intent.java index 32b167509d3..d3dd30a8fc4 100644 --- a/java/ql/test/stubs/android/android/content/Intent.java +++ b/java/ql/test/stubs/android/android/content/Intent.java @@ -1,196 +1,461 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Generated automatically from android.content.Intent for testing purposes package android.content; +import android.content.ClipData; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.IntentSender; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.graphics.Rect; import android.net.Uri; import android.os.Bundle; - -import java.io.PrintWriter; -import java.net.URISyntaxException; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.AttributeSet; +import java.io.Serializable; +import java.util.ArrayList; import java.util.Set; +import org.xmlpull.v1.XmlPullParser; -public class Intent implements Cloneable { - - public static Intent createChooser(Intent target, CharSequence title) { - return null; - } - - public Intent() { - } - - public Intent(Intent o) { - } - - @Override - public Object clone() { - return null; - } - - public Intent(String action) { - } - - public Intent(String action, Uri uri) { - } - - public Intent(Context packageContext, Class cls) { - } - - public Intent(String action, Uri uri, Context packageContext, Class cls) { - } - - public static Intent makeMainSelectorActivity(String selectorAction, String selectorCategory) { - return null; - } - - public static Intent getIntent(String uri) throws URISyntaxException { - return null; - } - - public static Intent getIntentOld(String uri) throws URISyntaxException { - return null; - } - - public static void printIntentArgsHelp(PrintWriter pw, String prefix) { - } - - public boolean hasCategory(String category) { - return false; - } - - public Set getCategories() { - return null; - } - - public int getContentUserHint() { - return 0; - } - - public String getLaunchToken() { - return null; - } - - public void setLaunchToken(String launchToken) { - } - - public boolean hasExtra(String name) { - return false; - } - - public boolean hasFileDescriptors() { - return false; - } - - public void setAllowFds(boolean allowFds) { - } - - public void setDefusable(boolean defusable) { - } - - public Object getExtra(String name) { - return null; - } - - public boolean getBooleanExtra(String name, boolean defaultValue) { - return false; - } - - public byte getByteExtra(String name, byte defaultValue) { - return 0; - } - - public short getShortExtra(String name, short defaultValue) { - return 0; - } - - public char getCharExtra(String name, char defaultValue) { - return '0'; - } - - public int getIntExtra(String name, int defaultValue) { - return 0; - } - - public long getLongExtra(String name, long defaultValue) { - return 0; - } - - public float getFloatExtra(String name, float defaultValue) { - return 0; - } - - public double getDoubleExtra(String name, double defaultValue) { - return 0; - } - - public String getStringExtra(String string) { - return null; - } - - public void removeUnsafeExtras() { - } - - public boolean canStripForHistory() { - return false; - } - - public Intent maybeStripForHistory() { - return null; - } - - public boolean isExcludingStopped() { - return false; - } - - public void removeCategory(String category) { - } - - public void prepareToLeaveUser(int userId) { - } - - public boolean filterEquals(Intent other) { - return false; - } - - public int filterHashCode() { - return 0; - } - - @Override - public String toString() { - return null; - } - - public String toInsecureString() { - return null; - } - - public String toInsecureStringWithClip() { - return null; - } - - public String toShortString(boolean secure, boolean comp, boolean extras, boolean clip) { - return null; - } - - public void toShortString(StringBuilder b, boolean secure, boolean comp, boolean extras, boolean clip) { - } - - public Bundle getExtras() { - return null; - } - +public class Intent implements Cloneable, Parcelable +{ + public ArrayList getParcelableArrayListExtra(String p0){ return null; } + public T getParcelableExtra(String p0){ return null; } + public ActivityInfo resolveActivityInfo(PackageManager p0, int p1){ return null; } + public ArrayList getCharSequenceArrayListExtra(String p0){ return null; } + public ArrayList getIntegerArrayListExtra(String p0){ return null; } + public ArrayList getStringArrayListExtra(String p0){ return null; } + public Bundle getBundleExtra(String p0){ return null; } + public Bundle getExtras(){ return null; } + public CharSequence getCharSequenceExtra(String p0){ return null; } + public CharSequence[] getCharSequenceArrayExtra(String p0){ return null; } + public ClipData getClipData(){ return null; } + public ComponentName getComponent(){ return null; } + public ComponentName resolveActivity(PackageManager p0){ return null; } + public Intent addCategory(String p0){ return null; } + public Intent addFlags(int p0){ return null; } + public Intent cloneFilter(){ return null; } + public Intent getSelector(){ return null; } + public Intent putCharSequenceArrayListExtra(String p0, ArrayList p1){ return null; } + public Intent putExtra(String p0, Bundle p1){ return null; } + public Intent putExtra(String p0, CharSequence p1){ return null; } + public Intent putExtra(String p0, CharSequence[] p1){ return null; } + public Intent putExtra(String p0, Parcelable p1){ return null; } + public Intent putExtra(String p0, Parcelable[] p1){ return null; } + public Intent putExtra(String p0, Serializable p1){ return null; } + public Intent putExtra(String p0, String p1){ return null; } + public Intent putExtra(String p0, String[] p1){ return null; } + public Intent putExtra(String p0, boolean p1){ return null; } + public Intent putExtra(String p0, boolean[] p1){ return null; } + public Intent putExtra(String p0, byte p1){ return null; } + public Intent putExtra(String p0, byte[] p1){ return null; } + public Intent putExtra(String p0, char p1){ return null; } + public Intent putExtra(String p0, char[] p1){ return null; } + public Intent putExtra(String p0, double p1){ return null; } + public Intent putExtra(String p0, double[] p1){ return null; } + public Intent putExtra(String p0, float p1){ return null; } + public Intent putExtra(String p0, float[] p1){ return null; } + public Intent putExtra(String p0, int p1){ return null; } + public Intent putExtra(String p0, int[] p1){ return null; } + public Intent putExtra(String p0, long p1){ return null; } + public Intent putExtra(String p0, long[] p1){ return null; } + public Intent putExtra(String p0, short p1){ return null; } + public Intent putExtra(String p0, short[] p1){ return null; } + public Intent putExtras(Bundle p0){ return null; } + public Intent putExtras(Intent p0){ return null; } + public Intent putIntegerArrayListExtra(String p0, ArrayList p1){ return null; } + public Intent putParcelableArrayListExtra(String p0, ArrayList p1){ return null; } + public Intent putStringArrayListExtra(String p0, ArrayList p1){ return null; } + public Intent replaceExtras(Bundle p0){ return null; } + public Intent replaceExtras(Intent p0){ return null; } + public Intent setAction(String p0){ return null; } + public Intent setClass(Context p0, Class p1){ return null; } + public Intent setClassName(Context p0, String p1){ return null; } + public Intent setClassName(String p0, String p1){ return null; } + public Intent setComponent(ComponentName p0){ return null; } + public Intent setData(Uri p0){ return null; } + public Intent setDataAndNormalize(Uri p0){ return null; } + public Intent setDataAndType(Uri p0, String p1){ return null; } + public Intent setDataAndTypeAndNormalize(Uri p0, String p1){ return null; } + public Intent setFlags(int p0){ return null; } + public Intent setIdentifier(String p0){ return null; } + public Intent setPackage(String p0){ return null; } + public Intent setType(String p0){ return null; } + public Intent setTypeAndNormalize(String p0){ return null; } + public Intent(){} + public Intent(Context p0, Class p1){} + public Intent(Intent p0){} + public Intent(String p0){} + public Intent(String p0, Uri p1){} + public Intent(String p0, Uri p1, Context p2, Class p3){} + public Object clone(){ return null; } + public Parcelable[] getParcelableArrayExtra(String p0){ return null; } + public Rect getSourceBounds(){ return null; } + public Serializable getSerializableExtra(String p0){ return null; } + public Set getCategories(){ return null; } + public String getAction(){ return null; } + public String getDataString(){ return null; } + public String getIdentifier(){ return null; } + public String getPackage(){ return null; } + public String getScheme(){ return null; } + public String getStringExtra(String p0){ return null; } + public String getType(){ return null; } + public String resolveType(ContentResolver p0){ return null; } + public String resolveType(Context p0){ return null; } + public String resolveTypeIfNeeded(ContentResolver p0){ return null; } + public String toString(){ return null; } + public String toURI(){ return null; } + public String toUri(int p0){ return null; } + public String[] getStringArrayExtra(String p0){ return null; } + public Uri getData(){ return null; } + public boolean filterEquals(Intent p0){ return false; } + public boolean getBooleanExtra(String p0, boolean p1){ return false; } + public boolean hasCategory(String p0){ return false; } + public boolean hasExtra(String p0){ return false; } + public boolean hasFileDescriptors(){ return false; } + public boolean[] getBooleanArrayExtra(String p0){ return null; } + public byte getByteExtra(String p0, byte p1){ return 0; } + public byte[] getByteArrayExtra(String p0){ return null; } + public char getCharExtra(String p0, char p1){ return '0'; } + public char[] getCharArrayExtra(String p0){ return null; } + public double getDoubleExtra(String p0, double p1){ return 0; } + public double[] getDoubleArrayExtra(String p0){ return null; } + public float getFloatExtra(String p0, float p1){ return 0; } + public float[] getFloatArrayExtra(String p0){ return null; } + public int describeContents(){ return 0; } + public int fillIn(Intent p0, int p1){ return 0; } + public int filterHashCode(){ return 0; } + public int getFlags(){ return 0; } + public int getIntExtra(String p0, int p1){ return 0; } + public int[] getIntArrayExtra(String p0){ return null; } + public long getLongExtra(String p0, long p1){ return 0; } + public long[] getLongArrayExtra(String p0){ return null; } + public short getShortExtra(String p0, short p1){ return 0; } + public short[] getShortArrayExtra(String p0){ return null; } + public static Intent createChooser(Intent p0, CharSequence p1){ return null; } + public static Intent createChooser(Intent p0, CharSequence p1, IntentSender p2){ return null; } + public static Intent getIntent(String p0){ return null; } + public static Intent getIntentOld(String p0){ return null; } + public static Intent makeMainActivity(ComponentName p0){ return null; } + public static Intent makeMainSelectorActivity(String p0, String p1){ return null; } + public static Intent makeRestartActivityTask(ComponentName p0){ return null; } + public static Intent parseIntent(Resources p0, XmlPullParser p1, AttributeSet p2){ return null; } + public static Intent parseUri(String p0, int p1){ return null; } + public static Parcelable.Creator CREATOR = null; + public static String ACTION_AIRPLANE_MODE_CHANGED = null; + public static String ACTION_ALL_APPS = null; + public static String ACTION_ANSWER = null; + public static String ACTION_APPLICATION_PREFERENCES = null; + public static String ACTION_APPLICATION_RESTRICTIONS_CHANGED = null; + public static String ACTION_APP_ERROR = null; + public static String ACTION_ASSIST = null; + public static String ACTION_ATTACH_DATA = null; + public static String ACTION_AUTO_REVOKE_PERMISSIONS = null; + public static String ACTION_BATTERY_CHANGED = null; + public static String ACTION_BATTERY_LOW = null; + public static String ACTION_BATTERY_OKAY = null; + public static String ACTION_BOOT_COMPLETED = null; + public static String ACTION_BUG_REPORT = null; + public static String ACTION_CALL = null; + public static String ACTION_CALL_BUTTON = null; + public static String ACTION_CAMERA_BUTTON = null; + public static String ACTION_CARRIER_SETUP = null; + public static String ACTION_CHOOSER = null; + public static String ACTION_CLOSE_SYSTEM_DIALOGS = null; + public static String ACTION_CONFIGURATION_CHANGED = null; + public static String ACTION_CREATE_DOCUMENT = null; + public static String ACTION_CREATE_REMINDER = null; + public static String ACTION_CREATE_SHORTCUT = null; + public static String ACTION_DATE_CHANGED = null; + public static String ACTION_DEFAULT = null; + public static String ACTION_DEFINE = null; + public static String ACTION_DELETE = null; + public static String ACTION_DEVICE_STORAGE_LOW = null; + public static String ACTION_DEVICE_STORAGE_OK = null; + public static String ACTION_DIAL = null; + public static String ACTION_DOCK_EVENT = null; + public static String ACTION_DREAMING_STARTED = null; + public static String ACTION_DREAMING_STOPPED = null; + public static String ACTION_EDIT = null; + public static String ACTION_EXTERNAL_APPLICATIONS_AVAILABLE = null; + public static String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE = null; + public static String ACTION_FACTORY_TEST = null; + public static String ACTION_GET_CONTENT = null; + public static String ACTION_GET_RESTRICTION_ENTRIES = null; + public static String ACTION_GTALK_SERVICE_CONNECTED = null; + public static String ACTION_GTALK_SERVICE_DISCONNECTED = null; + public static String ACTION_HEADSET_PLUG = null; + public static String ACTION_INPUT_METHOD_CHANGED = null; + public static String ACTION_INSERT = null; + public static String ACTION_INSERT_OR_EDIT = null; + public static String ACTION_INSTALL_FAILURE = null; + public static String ACTION_INSTALL_PACKAGE = null; + public static String ACTION_LOCALE_CHANGED = null; + public static String ACTION_LOCKED_BOOT_COMPLETED = null; + public static String ACTION_MAIN = null; + public static String ACTION_MANAGED_PROFILE_ADDED = null; + public static String ACTION_MANAGED_PROFILE_AVAILABLE = null; + public static String ACTION_MANAGED_PROFILE_REMOVED = null; + public static String ACTION_MANAGED_PROFILE_UNAVAILABLE = null; + public static String ACTION_MANAGED_PROFILE_UNLOCKED = null; + public static String ACTION_MANAGE_NETWORK_USAGE = null; + public static String ACTION_MANAGE_PACKAGE_STORAGE = null; + public static String ACTION_MEDIA_BAD_REMOVAL = null; + public static String ACTION_MEDIA_BUTTON = null; + public static String ACTION_MEDIA_CHECKING = null; + public static String ACTION_MEDIA_EJECT = null; + public static String ACTION_MEDIA_MOUNTED = null; + public static String ACTION_MEDIA_NOFS = null; + public static String ACTION_MEDIA_REMOVED = null; + public static String ACTION_MEDIA_SCANNER_FINISHED = null; + public static String ACTION_MEDIA_SCANNER_SCAN_FILE = null; + public static String ACTION_MEDIA_SCANNER_STARTED = null; + public static String ACTION_MEDIA_SHARED = null; + public static String ACTION_MEDIA_UNMOUNTABLE = null; + public static String ACTION_MEDIA_UNMOUNTED = null; + public static String ACTION_MY_PACKAGE_REPLACED = null; + public static String ACTION_MY_PACKAGE_SUSPENDED = null; + public static String ACTION_MY_PACKAGE_UNSUSPENDED = null; + public static String ACTION_NEW_OUTGOING_CALL = null; + public static String ACTION_OPEN_DOCUMENT = null; + public static String ACTION_OPEN_DOCUMENT_TREE = null; + public static String ACTION_PACKAGES_SUSPENDED = null; + public static String ACTION_PACKAGES_UNSUSPENDED = null; + public static String ACTION_PACKAGE_ADDED = null; + public static String ACTION_PACKAGE_CHANGED = null; + public static String ACTION_PACKAGE_DATA_CLEARED = null; + public static String ACTION_PACKAGE_FIRST_LAUNCH = null; + public static String ACTION_PACKAGE_FULLY_REMOVED = null; + public static String ACTION_PACKAGE_INSTALL = null; + public static String ACTION_PACKAGE_NEEDS_VERIFICATION = null; + public static String ACTION_PACKAGE_REMOVED = null; + public static String ACTION_PACKAGE_REPLACED = null; + public static String ACTION_PACKAGE_RESTARTED = null; + public static String ACTION_PACKAGE_VERIFIED = null; + public static String ACTION_PASTE = null; + public static String ACTION_PICK = null; + public static String ACTION_PICK_ACTIVITY = null; + public static String ACTION_POWER_CONNECTED = null; + public static String ACTION_POWER_DISCONNECTED = null; + public static String ACTION_POWER_USAGE_SUMMARY = null; + public static String ACTION_PROCESS_TEXT = null; + public static String ACTION_PROVIDER_CHANGED = null; + public static String ACTION_QUICK_CLOCK = null; + public static String ACTION_QUICK_VIEW = null; + public static String ACTION_REBOOT = null; + public static String ACTION_RUN = null; + public static String ACTION_SCREEN_OFF = null; + public static String ACTION_SCREEN_ON = null; + public static String ACTION_SEARCH = null; + public static String ACTION_SEARCH_LONG_PRESS = null; + public static String ACTION_SEND = null; + public static String ACTION_SENDTO = null; + public static String ACTION_SEND_MULTIPLE = null; + public static String ACTION_SET_WALLPAPER = null; + public static String ACTION_SHOW_APP_INFO = null; + public static String ACTION_SHUTDOWN = null; + public static String ACTION_SYNC = null; + public static String ACTION_SYSTEM_TUTORIAL = null; + public static String ACTION_TIMEZONE_CHANGED = null; + public static String ACTION_TIME_CHANGED = null; + public static String ACTION_TIME_TICK = null; + public static String ACTION_TRANSLATE = null; + public static String ACTION_UID_REMOVED = null; + public static String ACTION_UMS_CONNECTED = null; + public static String ACTION_UMS_DISCONNECTED = null; + public static String ACTION_UNINSTALL_PACKAGE = null; + public static String ACTION_USER_BACKGROUND = null; + public static String ACTION_USER_FOREGROUND = null; + public static String ACTION_USER_INITIALIZE = null; + public static String ACTION_USER_PRESENT = null; + public static String ACTION_USER_UNLOCKED = null; + public static String ACTION_VIEW = null; + public static String ACTION_VIEW_LOCUS = null; + public static String ACTION_VIEW_PERMISSION_USAGE = null; + public static String ACTION_VOICE_COMMAND = null; + public static String ACTION_WALLPAPER_CHANGED = null; + public static String ACTION_WEB_SEARCH = null; + public static String CATEGORY_ACCESSIBILITY_SHORTCUT_TARGET = null; + public static String CATEGORY_ALTERNATIVE = null; + public static String CATEGORY_APP_BROWSER = null; + public static String CATEGORY_APP_CALCULATOR = null; + public static String CATEGORY_APP_CALENDAR = null; + public static String CATEGORY_APP_CONTACTS = null; + public static String CATEGORY_APP_EMAIL = null; + public static String CATEGORY_APP_FILES = null; + public static String CATEGORY_APP_GALLERY = null; + public static String CATEGORY_APP_MAPS = null; + public static String CATEGORY_APP_MARKET = null; + public static String CATEGORY_APP_MESSAGING = null; + public static String CATEGORY_APP_MUSIC = null; + public static String CATEGORY_BROWSABLE = null; + public static String CATEGORY_CAR_DOCK = null; + public static String CATEGORY_CAR_MODE = null; + public static String CATEGORY_DEFAULT = null; + public static String CATEGORY_DESK_DOCK = null; + public static String CATEGORY_DEVELOPMENT_PREFERENCE = null; + public static String CATEGORY_EMBED = null; + public static String CATEGORY_FRAMEWORK_INSTRUMENTATION_TEST = null; + public static String CATEGORY_HE_DESK_DOCK = null; + public static String CATEGORY_HOME = null; + public static String CATEGORY_INFO = null; + public static String CATEGORY_LAUNCHER = null; + public static String CATEGORY_LEANBACK_LAUNCHER = null; + public static String CATEGORY_LE_DESK_DOCK = null; + public static String CATEGORY_MONKEY = null; + public static String CATEGORY_OPENABLE = null; + public static String CATEGORY_PREFERENCE = null; + public static String CATEGORY_SAMPLE_CODE = null; + public static String CATEGORY_SECONDARY_HOME = null; + public static String CATEGORY_SELECTED_ALTERNATIVE = null; + public static String CATEGORY_TAB = null; + public static String CATEGORY_TEST = null; + public static String CATEGORY_TYPED_OPENABLE = null; + public static String CATEGORY_UNIT_TEST = null; + public static String CATEGORY_VOICE = null; + public static String CATEGORY_VR_HOME = null; + public static String EXTRA_ALARM_COUNT = null; + public static String EXTRA_ALLOW_MULTIPLE = null; + public static String EXTRA_ALLOW_REPLACE = null; + public static String EXTRA_ALTERNATE_INTENTS = null; + public static String EXTRA_ASSIST_CONTEXT = null; + public static String EXTRA_ASSIST_INPUT_DEVICE_ID = null; + public static String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = null; + public static String EXTRA_ASSIST_PACKAGE = null; + public static String EXTRA_ASSIST_UID = null; + public static String EXTRA_AUTO_LAUNCH_SINGLE_CHOICE = null; + public static String EXTRA_BCC = null; + public static String EXTRA_BUG_REPORT = null; + public static String EXTRA_CC = null; + public static String EXTRA_CHANGED_COMPONENT_NAME = null; + public static String EXTRA_CHANGED_COMPONENT_NAME_LIST = null; + public static String EXTRA_CHANGED_PACKAGE_LIST = null; + public static String EXTRA_CHANGED_UID_LIST = null; + public static String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = null; + public static String EXTRA_CHOOSER_TARGETS = null; + public static String EXTRA_CHOSEN_COMPONENT = null; + public static String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = null; + public static String EXTRA_COMPONENT_NAME = null; + public static String EXTRA_CONTENT_ANNOTATIONS = null; + public static String EXTRA_CONTENT_QUERY = null; + public static String EXTRA_DATA_REMOVED = null; + public static String EXTRA_DOCK_STATE = null; + public static String EXTRA_DONT_KILL_APP = null; + public static String EXTRA_DURATION_MILLIS = null; + public static String EXTRA_EMAIL = null; + public static String EXTRA_EXCLUDE_COMPONENTS = null; + public static String EXTRA_FROM_STORAGE = null; + public static String EXTRA_HTML_TEXT = null; + public static String EXTRA_INDEX = null; + public static String EXTRA_INITIAL_INTENTS = null; + public static String EXTRA_INSTALLER_PACKAGE_NAME = null; + public static String EXTRA_INTENT = null; + public static String EXTRA_KEY_EVENT = null; + public static String EXTRA_LOCAL_ONLY = null; + public static String EXTRA_LOCUS_ID = null; + public static String EXTRA_MIME_TYPES = null; + public static String EXTRA_NOT_UNKNOWN_SOURCE = null; + public static String EXTRA_ORIGINATING_URI = null; + public static String EXTRA_PACKAGE_NAME = null; + public static String EXTRA_PHONE_NUMBER = null; + public static String EXTRA_PROCESS_TEXT = null; + public static String EXTRA_PROCESS_TEXT_READONLY = null; + public static String EXTRA_QUICK_VIEW_FEATURES = null; + public static String EXTRA_QUIET_MODE = null; + public static String EXTRA_REFERRER = null; + public static String EXTRA_REFERRER_NAME = null; + public static String EXTRA_REMOTE_INTENT_TOKEN = null; + public static String EXTRA_REPLACEMENT_EXTRAS = null; + public static String EXTRA_REPLACING = null; + public static String EXTRA_RESTRICTIONS_BUNDLE = null; + public static String EXTRA_RESTRICTIONS_INTENT = null; + public static String EXTRA_RESTRICTIONS_LIST = null; + public static String EXTRA_RESULT_RECEIVER = null; + public static String EXTRA_RETURN_RESULT = null; + public static String EXTRA_SHORTCUT_ICON = null; + public static String EXTRA_SHORTCUT_ICON_RESOURCE = null; + public static String EXTRA_SHORTCUT_ID = null; + public static String EXTRA_SHORTCUT_INTENT = null; + public static String EXTRA_SHORTCUT_NAME = null; + public static String EXTRA_SHUTDOWN_USERSPACE_ONLY = null; + public static String EXTRA_SPLIT_NAME = null; + public static String EXTRA_STREAM = null; + public static String EXTRA_SUBJECT = null; + public static String EXTRA_SUSPENDED_PACKAGE_EXTRAS = null; + public static String EXTRA_TEMPLATE = null; + public static String EXTRA_TEXT = null; + public static String EXTRA_TIME = null; + public static String EXTRA_TIMEZONE = null; + public static String EXTRA_TITLE = null; + public static String EXTRA_UID = null; + public static String EXTRA_USER = null; + public static String METADATA_DOCK_HOME = null; + public static String normalizeMimeType(String p0){ return null; } + public static int EXTRA_DOCK_STATE_CAR = 0; + public static int EXTRA_DOCK_STATE_DESK = 0; + public static int EXTRA_DOCK_STATE_HE_DESK = 0; + public static int EXTRA_DOCK_STATE_LE_DESK = 0; + public static int EXTRA_DOCK_STATE_UNDOCKED = 0; + public static int FILL_IN_ACTION = 0; + public static int FILL_IN_CATEGORIES = 0; + public static int FILL_IN_CLIP_DATA = 0; + public static int FILL_IN_COMPONENT = 0; + public static int FILL_IN_DATA = 0; + public static int FILL_IN_IDENTIFIER = 0; + public static int FILL_IN_PACKAGE = 0; + public static int FILL_IN_SELECTOR = 0; + public static int FILL_IN_SOURCE_BOUNDS = 0; + public static int FLAG_ACTIVITY_BROUGHT_TO_FRONT = 0; + public static int FLAG_ACTIVITY_CLEAR_TASK = 0; + public static int FLAG_ACTIVITY_CLEAR_TOP = 0; + public static int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 0; + public static int FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS = 0; + public static int FLAG_ACTIVITY_FORWARD_RESULT = 0; + public static int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 0; + public static int FLAG_ACTIVITY_LAUNCH_ADJACENT = 0; + public static int FLAG_ACTIVITY_MATCH_EXTERNAL = 0; + public static int FLAG_ACTIVITY_MULTIPLE_TASK = 0; + public static int FLAG_ACTIVITY_NEW_DOCUMENT = 0; + public static int FLAG_ACTIVITY_NEW_TASK = 0; + public static int FLAG_ACTIVITY_NO_ANIMATION = 0; + public static int FLAG_ACTIVITY_NO_HISTORY = 0; + public static int FLAG_ACTIVITY_NO_USER_ACTION = 0; + public static int FLAG_ACTIVITY_PREVIOUS_IS_TOP = 0; + public static int FLAG_ACTIVITY_REORDER_TO_FRONT = 0; + public static int FLAG_ACTIVITY_REQUIRE_DEFAULT = 0; + public static int FLAG_ACTIVITY_REQUIRE_NON_BROWSER = 0; + public static int FLAG_ACTIVITY_RESET_TASK_IF_NEEDED = 0; + public static int FLAG_ACTIVITY_RETAIN_IN_RECENTS = 0; + public static int FLAG_ACTIVITY_SINGLE_TOP = 0; + public static int FLAG_ACTIVITY_TASK_ON_HOME = 0; + public static int FLAG_DEBUG_LOG_RESOLUTION = 0; + public static int FLAG_DIRECT_BOOT_AUTO = 0; + public static int FLAG_EXCLUDE_STOPPED_PACKAGES = 0; + public static int FLAG_FROM_BACKGROUND = 0; + public static int FLAG_GRANT_PERSISTABLE_URI_PERMISSION = 0; + public static int FLAG_GRANT_PREFIX_URI_PERMISSION = 0; + public static int FLAG_GRANT_READ_URI_PERMISSION = 0; + public static int FLAG_GRANT_WRITE_URI_PERMISSION = 0; + public static int FLAG_INCLUDE_STOPPED_PACKAGES = 0; + public static int FLAG_RECEIVER_FOREGROUND = 0; + public static int FLAG_RECEIVER_NO_ABORT = 0; + public static int FLAG_RECEIVER_REGISTERED_ONLY = 0; + public static int FLAG_RECEIVER_REPLACE_PENDING = 0; + public static int FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS = 0; + public static int URI_ALLOW_UNSAFE = 0; + public static int URI_ANDROID_APP_SCHEME = 0; + public static int URI_INTENT_SCHEME = 0; + public void readFromParcel(Parcel p0){} + public void removeCategory(String p0){} + public void removeExtra(String p0){} + public void removeFlags(int p0){} + public void setClipData(ClipData p0){} + public void setExtrasClassLoader(ClassLoader p0){} + public void setSelector(Intent p0){} + public void setSourceBounds(Rect p0){} + public void writeToParcel(Parcel p0, int p1){} } diff --git a/java/ql/test/stubs/android/android/content/IntentFilter.java b/java/ql/test/stubs/android/android/content/IntentFilter.java new file mode 100644 index 00000000000..99de8481e14 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/IntentFilter.java @@ -0,0 +1,104 @@ +// Generated automatically from android.content.IntentFilter for testing purposes + +package android.content; + +import android.content.ContentResolver; +import android.content.Intent; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PatternMatcher; +import android.util.AndroidException; +import android.util.Printer; +import java.util.Iterator; +import java.util.Set; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlSerializer; + +public class IntentFilter implements Parcelable +{ + public IntentFilter(){} + public IntentFilter(IntentFilter p0){} + public IntentFilter(String p0){} + public IntentFilter(String p0, String p1){} + public final IntentFilter.AuthorityEntry getDataAuthority(int p0){ return null; } + public final Iterator authoritiesIterator(){ return null; } + public final Iterator pathsIterator(){ return null; } + public final Iterator schemeSpecificPartsIterator(){ return null; } + public final Iterator actionsIterator(){ return null; } + public final Iterator categoriesIterator(){ return null; } + public final Iterator schemesIterator(){ return null; } + public final Iterator typesIterator(){ return null; } + public final PatternMatcher getDataPath(int p0){ return null; } + public final PatternMatcher getDataSchemeSpecificPart(int p0){ return null; } + public final String getAction(int p0){ return null; } + public final String getCategory(int p0){ return null; } + public final String getDataScheme(int p0){ return null; } + public final String getDataType(int p0){ return null; } + public final String matchCategories(Set p0){ return null; } + public final boolean hasAction(String p0){ return false; } + public final boolean hasCategory(String p0){ return false; } + public final boolean hasDataAuthority(Uri p0){ return false; } + public final boolean hasDataPath(String p0){ return false; } + public final boolean hasDataScheme(String p0){ return false; } + public final boolean hasDataSchemeSpecificPart(String p0){ return false; } + public final boolean hasDataType(String p0){ return false; } + public final boolean matchAction(String p0){ return false; } + public final int countActions(){ return 0; } + public final int countCategories(){ return 0; } + public final int countDataAuthorities(){ return 0; } + public final int countDataPaths(){ return 0; } + public final int countDataSchemeSpecificParts(){ return 0; } + public final int countDataSchemes(){ return 0; } + public final int countDataTypes(){ return 0; } + public final int describeContents(){ return 0; } + public final int getPriority(){ return 0; } + public final int match(ContentResolver p0, Intent p1, boolean p2, String p3){ return 0; } + public final int match(String p0, String p1, String p2, Uri p3, Set p4, String p5){ return 0; } + public final int matchData(String p0, String p1, Uri p2){ return 0; } + public final int matchDataAuthority(Uri p0){ return 0; } + public final void addAction(String p0){} + public final void addCategory(String p0){} + public final void addDataAuthority(String p0, String p1){} + public final void addDataPath(String p0, int p1){} + public final void addDataScheme(String p0){} + public final void addDataSchemeSpecificPart(String p0, int p1){} + public final void addDataType(String p0){} + public final void setPriority(int p0){} + public final void writeToParcel(Parcel p0, int p1){} + public static IntentFilter create(String p0, String p1){ return null; } + public static Parcelable.Creator CREATOR = null; + public static int MATCH_ADJUSTMENT_MASK = 0; + public static int MATCH_ADJUSTMENT_NORMAL = 0; + public static int MATCH_CATEGORY_EMPTY = 0; + public static int MATCH_CATEGORY_HOST = 0; + public static int MATCH_CATEGORY_MASK = 0; + public static int MATCH_CATEGORY_PATH = 0; + public static int MATCH_CATEGORY_PORT = 0; + public static int MATCH_CATEGORY_SCHEME = 0; + public static int MATCH_CATEGORY_SCHEME_SPECIFIC_PART = 0; + public static int MATCH_CATEGORY_TYPE = 0; + public static int NO_MATCH_ACTION = 0; + public static int NO_MATCH_CATEGORY = 0; + public static int NO_MATCH_DATA = 0; + public static int NO_MATCH_TYPE = 0; + public static int SYSTEM_HIGH_PRIORITY = 0; + public static int SYSTEM_LOW_PRIORITY = 0; + public void dump(Printer p0, String p1){} + public void readFromXml(XmlPullParser p0){} + public void writeToXml(XmlSerializer p0){} + static public class AuthorityEntry + { + protected AuthorityEntry() {} + public AuthorityEntry(String p0, String p1){} + public String getHost(){ return null; } + public boolean equals(Object p0){ return false; } + public int getPort(){ return 0; } + public int match(Uri p0){ return 0; } + } + static public class MalformedMimeTypeException extends AndroidException + { + public MalformedMimeTypeException(){} + public MalformedMimeTypeException(String p0){} + } +} diff --git a/java/ql/test/stubs/android/android/content/IntentSender.java b/java/ql/test/stubs/android/android/content/IntentSender.java new file mode 100644 index 00000000000..6008341e802 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/IntentSender.java @@ -0,0 +1,40 @@ +// Generated automatically from android.content.IntentSender for testing purposes + +package android.content; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.UserHandle; +import android.util.AndroidException; + +public class IntentSender implements Parcelable +{ + public String getCreatorPackage(){ return null; } + public String getTargetPackage(){ return null; } + public String toString(){ return null; } + public UserHandle getCreatorUserHandle(){ return null; } + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int getCreatorUid(){ return 0; } + public int hashCode(){ return 0; } + public static IntentSender readIntentSenderOrNullFromParcel(Parcel p0){ return null; } + public static Parcelable.Creator CREATOR = null; + public static void writeIntentSenderOrNullToParcel(IntentSender p0, Parcel p1){} + public void sendIntent(Context p0, int p1, Intent p2, IntentSender.OnFinished p3, Handler p4){} + public void sendIntent(Context p0, int p1, Intent p2, IntentSender.OnFinished p3, Handler p4, String p5){} + public void writeToParcel(Parcel p0, int p1){} + static public class SendIntentException extends AndroidException + { + public SendIntentException(){} + public SendIntentException(Exception p0){} + public SendIntentException(String p0){} + } + static public interface OnFinished + { + void onSendFinished(IntentSender p0, Intent p1, int p2, String p3, Bundle p4); + } +} diff --git a/java/ql/test/stubs/android/android/content/PeriodicSync.java b/java/ql/test/stubs/android/android/content/PeriodicSync.java new file mode 100644 index 00000000000..6842c06d22f --- /dev/null +++ b/java/ql/test/stubs/android/android/content/PeriodicSync.java @@ -0,0 +1,23 @@ +// Generated automatically from android.content.PeriodicSync for testing purposes + +package android.content; + +import android.accounts.Account; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +public class PeriodicSync implements Parcelable +{ + protected PeriodicSync() {} + public PeriodicSync(Account p0, String p1, Bundle p2, long p3){} + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public final Account account = null; + public final Bundle extras = null; + public final String authority = null; + public final long period = 0; + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/ServiceConnection.java b/java/ql/test/stubs/android/android/content/ServiceConnection.java new file mode 100644 index 00000000000..1b8534539c4 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/ServiceConnection.java @@ -0,0 +1,14 @@ +// Generated automatically from android.content.ServiceConnection for testing purposes + +package android.content; + +import android.content.ComponentName; +import android.os.IBinder; + +public interface ServiceConnection +{ + default void onBindingDied(ComponentName p0){} + default void onNullBinding(ComponentName p0){} + void onServiceConnected(ComponentName p0, IBinder p1); + void onServiceDisconnected(ComponentName p0); +} diff --git a/java/ql/test/stubs/android/android/content/SharedPreferences.java b/java/ql/test/stubs/android/android/content/SharedPreferences.java new file mode 100644 index 00000000000..90cdc242501 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/SharedPreferences.java @@ -0,0 +1,38 @@ +// Generated automatically from android.content.SharedPreferences for testing purposes + +package android.content; + +import java.util.Map; +import java.util.Set; + +public interface SharedPreferences +{ + Map getAll(); + Set getStringSet(String p0, Set p1); + SharedPreferences.Editor edit(); + String getString(String p0, String p1); + boolean contains(String p0); + boolean getBoolean(String p0, boolean p1); + float getFloat(String p0, float p1); + int getInt(String p0, int p1); + long getLong(String p0, long p1); + static public interface Editor + { + SharedPreferences.Editor clear(); + SharedPreferences.Editor putBoolean(String p0, boolean p1); + SharedPreferences.Editor putFloat(String p0, float p1); + SharedPreferences.Editor putInt(String p0, int p1); + SharedPreferences.Editor putLong(String p0, long p1); + SharedPreferences.Editor putString(String p0, String p1); + SharedPreferences.Editor putStringSet(String p0, Set p1); + SharedPreferences.Editor remove(String p0); + boolean commit(); + void apply(); + } + static public interface OnSharedPreferenceChangeListener + { + void onSharedPreferenceChanged(SharedPreferences p0, String p1); + } + void registerOnSharedPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener p0); + void unregisterOnSharedPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener p0); +} diff --git a/java/ql/test/stubs/android/android/content/SyncAdapterType.java b/java/ql/test/stubs/android/android/content/SyncAdapterType.java new file mode 100644 index 00000000000..b05e030a6c3 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/SyncAdapterType.java @@ -0,0 +1,28 @@ +// Generated automatically from android.content.SyncAdapterType for testing purposes + +package android.content; + +import android.os.Parcel; +import android.os.Parcelable; + +public class SyncAdapterType implements Parcelable +{ + protected SyncAdapterType() {} + public String getSettingsActivity(){ return null; } + public String toString(){ return null; } + public SyncAdapterType(Parcel p0){} + public SyncAdapterType(String p0, String p1, boolean p2, boolean p3){} + public boolean allowParallelSyncs(){ return false; } + public boolean equals(Object p0){ return false; } + public boolean isAlwaysSyncable(){ return false; } + public boolean isUserVisible(){ return false; } + public boolean supportsUploading(){ return false; } + public final String accountType = null; + public final String authority = null; + public final boolean isKey = false; + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static SyncAdapterType newKey(String p0, String p1){ return null; } + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/SyncInfo.java b/java/ql/test/stubs/android/android/content/SyncInfo.java new file mode 100644 index 00000000000..500c5bd97bc --- /dev/null +++ b/java/ql/test/stubs/android/android/content/SyncInfo.java @@ -0,0 +1,16 @@ +// Generated automatically from android.content.SyncInfo for testing purposes + +package android.content; + +import android.accounts.Account; +import android.os.Parcel; +import android.os.Parcelable; + +public class SyncInfo implements Parcelable +{ + public final Account account = null; + public final String authority = null; + public final long startTime = 0; + public int describeContents(){ return 0; } + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/SyncRequest.java b/java/ql/test/stubs/android/android/content/SyncRequest.java new file mode 100644 index 00000000000..29a07c4b5a7 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/SyncRequest.java @@ -0,0 +1,13 @@ +// Generated automatically from android.content.SyncRequest for testing purposes + +package android.content; + +import android.os.Parcel; +import android.os.Parcelable; + +public class SyncRequest implements Parcelable +{ + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/SyncStatusObserver.java b/java/ql/test/stubs/android/android/content/SyncStatusObserver.java new file mode 100644 index 00000000000..534cd549be0 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/SyncStatusObserver.java @@ -0,0 +1,9 @@ +// Generated automatically from android.content.SyncStatusObserver for testing purposes + +package android.content; + + +public interface SyncStatusObserver +{ + void onStatusChanged(int p0); +} diff --git a/java/ql/test/stubs/android/android/content/UriPermission.java b/java/ql/test/stubs/android/android/content/UriPermission.java new file mode 100644 index 00000000000..efa9f6e7b95 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/UriPermission.java @@ -0,0 +1,20 @@ +// Generated automatically from android.content.UriPermission for testing purposes + +package android.content; + +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +public class UriPermission implements Parcelable +{ + public String toString(){ return null; } + public Uri getUri(){ return null; } + public boolean isReadPermission(){ return false; } + public boolean isWritePermission(){ return false; } + public int describeContents(){ return 0; } + public long getPersistedTime(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static long INVALID_TIME = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/pm/ActivityInfo.java b/java/ql/test/stubs/android/android/content/pm/ActivityInfo.java new file mode 100644 index 00000000000..ed0debb0412 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/ActivityInfo.java @@ -0,0 +1,112 @@ +// Generated automatically from android.content.pm.ActivityInfo for testing purposes + +package android.content.pm; + +import android.content.pm.ComponentInfo; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Printer; + +public class ActivityInfo extends ComponentInfo implements Parcelable +{ + public ActivityInfo(){} + public ActivityInfo(ActivityInfo p0){} + public ActivityInfo.WindowLayout windowLayout = null; + public String parentActivityName = null; + public String permission = null; + public String targetActivity = null; + public String taskAffinity = null; + public String toString(){ return null; } + public final int getThemeResource(){ return 0; } + public int colorMode = 0; + public int configChanges = 0; + public int describeContents(){ return 0; } + public int documentLaunchMode = 0; + public int flags = 0; + public int launchMode = 0; + public int maxRecents = 0; + public int persistableMode = 0; + public int screenOrientation = 0; + public int softInputMode = 0; + public int theme = 0; + public int uiOptions = 0; + public static Parcelable.Creator CREATOR = null; + public static int COLOR_MODE_DEFAULT = 0; + public static int COLOR_MODE_HDR = 0; + public static int COLOR_MODE_WIDE_COLOR_GAMUT = 0; + public static int CONFIG_COLOR_MODE = 0; + public static int CONFIG_DENSITY = 0; + public static int CONFIG_FONT_SCALE = 0; + public static int CONFIG_KEYBOARD = 0; + public static int CONFIG_KEYBOARD_HIDDEN = 0; + public static int CONFIG_LAYOUT_DIRECTION = 0; + public static int CONFIG_LOCALE = 0; + public static int CONFIG_MCC = 0; + public static int CONFIG_MNC = 0; + public static int CONFIG_NAVIGATION = 0; + public static int CONFIG_ORIENTATION = 0; + public static int CONFIG_SCREEN_LAYOUT = 0; + public static int CONFIG_SCREEN_SIZE = 0; + public static int CONFIG_SMALLEST_SCREEN_SIZE = 0; + public static int CONFIG_TOUCHSCREEN = 0; + public static int CONFIG_UI_MODE = 0; + public static int DOCUMENT_LAUNCH_ALWAYS = 0; + public static int DOCUMENT_LAUNCH_INTO_EXISTING = 0; + public static int DOCUMENT_LAUNCH_NEVER = 0; + public static int DOCUMENT_LAUNCH_NONE = 0; + public static int FLAG_ALLOW_TASK_REPARENTING = 0; + public static int FLAG_ALWAYS_RETAIN_TASK_STATE = 0; + public static int FLAG_AUTO_REMOVE_FROM_RECENTS = 0; + public static int FLAG_CLEAR_TASK_ON_LAUNCH = 0; + public static int FLAG_ENABLE_VR_MODE = 0; + public static int FLAG_EXCLUDE_FROM_RECENTS = 0; + public static int FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS = 0; + public static int FLAG_FINISH_ON_TASK_LAUNCH = 0; + public static int FLAG_HARDWARE_ACCELERATED = 0; + public static int FLAG_IMMERSIVE = 0; + public static int FLAG_MULTIPROCESS = 0; + public static int FLAG_NO_HISTORY = 0; + public static int FLAG_PREFER_MINIMAL_POST_PROCESSING = 0; + public static int FLAG_RELINQUISH_TASK_IDENTITY = 0; + public static int FLAG_RESUME_WHILE_PAUSING = 0; + public static int FLAG_SINGLE_USER = 0; + public static int FLAG_STATE_NOT_NEEDED = 0; + public static int LAUNCH_MULTIPLE = 0; + public static int LAUNCH_SINGLE_INSTANCE = 0; + public static int LAUNCH_SINGLE_TASK = 0; + public static int LAUNCH_SINGLE_TOP = 0; + public static int PERSIST_ACROSS_REBOOTS = 0; + public static int PERSIST_NEVER = 0; + public static int PERSIST_ROOT_ONLY = 0; + public static int SCREEN_ORIENTATION_BEHIND = 0; + public static int SCREEN_ORIENTATION_FULL_SENSOR = 0; + public static int SCREEN_ORIENTATION_FULL_USER = 0; + public static int SCREEN_ORIENTATION_LANDSCAPE = 0; + public static int SCREEN_ORIENTATION_LOCKED = 0; + public static int SCREEN_ORIENTATION_NOSENSOR = 0; + public static int SCREEN_ORIENTATION_PORTRAIT = 0; + public static int SCREEN_ORIENTATION_REVERSE_LANDSCAPE = 0; + public static int SCREEN_ORIENTATION_REVERSE_PORTRAIT = 0; + public static int SCREEN_ORIENTATION_SENSOR = 0; + public static int SCREEN_ORIENTATION_SENSOR_LANDSCAPE = 0; + public static int SCREEN_ORIENTATION_SENSOR_PORTRAIT = 0; + public static int SCREEN_ORIENTATION_UNSPECIFIED = 0; + public static int SCREEN_ORIENTATION_USER = 0; + public static int SCREEN_ORIENTATION_USER_LANDSCAPE = 0; + public static int SCREEN_ORIENTATION_USER_PORTRAIT = 0; + public static int UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW = 0; + public void dump(Printer p0, String p1){} + public void writeToParcel(Parcel p0, int p1){} + static public class WindowLayout + { + protected WindowLayout() {} + public WindowLayout(int p0, float p1, int p2, float p3, int p4, int p5, int p6){} + public final float heightFraction = 0; + public final float widthFraction = 0; + public final int gravity = 0; + public final int height = 0; + public final int minHeight = 0; + public final int minWidth = 0; + public final int width = 0; + } +} diff --git a/java/ql/test/stubs/android/android/content/pm/ApplicationInfo.java b/java/ql/test/stubs/android/android/content/pm/ApplicationInfo.java new file mode 100644 index 00000000000..51fc3974933 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/ApplicationInfo.java @@ -0,0 +1,101 @@ +// Generated automatically from android.content.pm.ApplicationInfo for testing purposes + +package android.content.pm; + +import android.content.Context; +import android.content.pm.PackageItemInfo; +import android.content.pm.PackageManager; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Printer; +import java.util.UUID; + +public class ApplicationInfo extends PackageItemInfo implements Parcelable +{ + public ApplicationInfo(){} + public ApplicationInfo(ApplicationInfo p0){} + public CharSequence loadDescription(PackageManager p0){ return null; } + public String appComponentFactory = null; + public String backupAgentName = null; + public String className = null; + public String dataDir = null; + public String deviceProtectedDataDir = null; + public String manageSpaceActivityName = null; + public String nativeLibraryDir = null; + public String permission = null; + public String processName = null; + public String publicSourceDir = null; + public String sourceDir = null; + public String taskAffinity = null; + public String toString(){ return null; } + public String[] sharedLibraryFiles = null; + public String[] splitNames = null; + public String[] splitPublicSourceDirs = null; + public String[] splitSourceDirs = null; + public UUID storageUuid = null; + public boolean enabled = false; + public boolean isProfileableByShell(){ return false; } + public boolean isResourceOverlay(){ return false; } + public boolean isVirtualPreload(){ return false; } + public int category = 0; + public int compatibleWidthLimitDp = 0; + public int describeContents(){ return 0; } + public int descriptionRes = 0; + public int flags = 0; + public int getGwpAsanMode(){ return 0; } + public int largestWidthLimitDp = 0; + public int minSdkVersion = 0; + public int requiresSmallestWidthDp = 0; + public int targetSdkVersion = 0; + public int theme = 0; + public int uiOptions = 0; + public int uid = 0; + public static CharSequence getCategoryTitle(Context p0, int p1){ return null; } + public static Parcelable.Creator CREATOR = null; + public static int CATEGORY_AUDIO = 0; + public static int CATEGORY_GAME = 0; + public static int CATEGORY_IMAGE = 0; + public static int CATEGORY_MAPS = 0; + public static int CATEGORY_NEWS = 0; + public static int CATEGORY_PRODUCTIVITY = 0; + public static int CATEGORY_SOCIAL = 0; + public static int CATEGORY_UNDEFINED = 0; + public static int CATEGORY_VIDEO = 0; + public static int FLAG_ALLOW_BACKUP = 0; + public static int FLAG_ALLOW_CLEAR_USER_DATA = 0; + public static int FLAG_ALLOW_TASK_REPARENTING = 0; + public static int FLAG_DEBUGGABLE = 0; + public static int FLAG_EXTERNAL_STORAGE = 0; + public static int FLAG_EXTRACT_NATIVE_LIBS = 0; + public static int FLAG_FACTORY_TEST = 0; + public static int FLAG_FULL_BACKUP_ONLY = 0; + public static int FLAG_HARDWARE_ACCELERATED = 0; + public static int FLAG_HAS_CODE = 0; + public static int FLAG_INSTALLED = 0; + public static int FLAG_IS_DATA_ONLY = 0; + public static int FLAG_IS_GAME = 0; + public static int FLAG_KILL_AFTER_RESTORE = 0; + public static int FLAG_LARGE_HEAP = 0; + public static int FLAG_MULTIARCH = 0; + public static int FLAG_PERSISTENT = 0; + public static int FLAG_RESIZEABLE_FOR_SCREENS = 0; + public static int FLAG_RESTORE_ANY_VERSION = 0; + public static int FLAG_STOPPED = 0; + public static int FLAG_SUPPORTS_LARGE_SCREENS = 0; + public static int FLAG_SUPPORTS_NORMAL_SCREENS = 0; + public static int FLAG_SUPPORTS_RTL = 0; + public static int FLAG_SUPPORTS_SCREEN_DENSITIES = 0; + public static int FLAG_SUPPORTS_SMALL_SCREENS = 0; + public static int FLAG_SUPPORTS_XLARGE_SCREENS = 0; + public static int FLAG_SUSPENDED = 0; + public static int FLAG_SYSTEM = 0; + public static int FLAG_TEST_ONLY = 0; + public static int FLAG_UPDATED_SYSTEM_APP = 0; + public static int FLAG_USES_CLEARTEXT_TRAFFIC = 0; + public static int FLAG_VM_SAFE_MODE = 0; + public static int GWP_ASAN_ALWAYS = 0; + public static int GWP_ASAN_DEFAULT = 0; + public static int GWP_ASAN_NEVER = 0; + public void dump(Printer p0, String p1){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/pm/ChangedPackages.java b/java/ql/test/stubs/android/android/content/pm/ChangedPackages.java new file mode 100644 index 00000000000..08c10b13f3c --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/ChangedPackages.java @@ -0,0 +1,18 @@ +// Generated automatically from android.content.pm.ChangedPackages for testing purposes + +package android.content.pm; + +import android.os.Parcel; +import android.os.Parcelable; +import java.util.List; + +public class ChangedPackages implements Parcelable +{ + protected ChangedPackages() {} + public ChangedPackages(int p0, List p1){} + public List getPackageNames(){ return null; } + public int describeContents(){ return 0; } + public int getSequenceNumber(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/pm/ComponentInfo.java b/java/ql/test/stubs/android/android/content/pm/ComponentInfo.java new file mode 100644 index 00000000000..fb92a43adbe --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/ComponentInfo.java @@ -0,0 +1,29 @@ +// Generated automatically from android.content.pm.ComponentInfo for testing purposes + +package android.content.pm; + +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageItemInfo; +import android.os.Parcel; +import android.util.Printer; + +public class ComponentInfo extends PackageItemInfo +{ + protected ComponentInfo(Parcel p0){} + protected void dumpBack(Printer p0, String p1){} + protected void dumpFront(Printer p0, String p1){} + public ApplicationInfo applicationInfo = null; + public ComponentInfo(){} + public ComponentInfo(ComponentInfo p0){} + public String processName = null; + public String splitName = null; + public boolean directBootAware = false; + public boolean enabled = false; + public boolean exported = false; + public boolean isEnabled(){ return false; } + public final int getBannerResource(){ return 0; } + public final int getIconResource(){ return 0; } + public final int getLogoResource(){ return 0; } + public int descriptionRes = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/pm/ConfigurationInfo.java b/java/ql/test/stubs/android/android/content/pm/ConfigurationInfo.java new file mode 100644 index 00000000000..ad178b8c6a3 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/ConfigurationInfo.java @@ -0,0 +1,25 @@ +// Generated automatically from android.content.pm.ConfigurationInfo for testing purposes + +package android.content.pm; + +import android.os.Parcel; +import android.os.Parcelable; + +public class ConfigurationInfo implements Parcelable +{ + public ConfigurationInfo(){} + public ConfigurationInfo(ConfigurationInfo p0){} + public String getGlEsVersion(){ return null; } + public String toString(){ return null; } + public int describeContents(){ return 0; } + public int reqGlEsVersion = 0; + public int reqInputFeatures = 0; + public int reqKeyboardType = 0; + public int reqNavigation = 0; + public int reqTouchScreen = 0; + public static Parcelable.Creator CREATOR = null; + public static int GL_ES_VERSION_UNDEFINED = 0; + public static int INPUT_FEATURE_FIVE_WAY_NAV = 0; + public static int INPUT_FEATURE_HARD_KEYBOARD = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/pm/FeatureGroupInfo.java b/java/ql/test/stubs/android/android/content/pm/FeatureGroupInfo.java new file mode 100644 index 00000000000..9cf6eaa172a --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/FeatureGroupInfo.java @@ -0,0 +1,17 @@ +// Generated automatically from android.content.pm.FeatureGroupInfo for testing purposes + +package android.content.pm; + +import android.content.pm.FeatureInfo; +import android.os.Parcel; +import android.os.Parcelable; + +public class FeatureGroupInfo implements Parcelable +{ + public FeatureGroupInfo(){} + public FeatureGroupInfo(FeatureGroupInfo p0){} + public FeatureInfo[] features = null; + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/pm/FeatureInfo.java b/java/ql/test/stubs/android/android/content/pm/FeatureInfo.java new file mode 100644 index 00000000000..5512590828f --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/FeatureInfo.java @@ -0,0 +1,23 @@ +// Generated automatically from android.content.pm.FeatureInfo for testing purposes + +package android.content.pm; + +import android.os.Parcel; +import android.os.Parcelable; + +public class FeatureInfo implements Parcelable +{ + public FeatureInfo(){} + public FeatureInfo(FeatureInfo p0){} + public String getGlEsVersion(){ return null; } + public String name = null; + public String toString(){ return null; } + public int describeContents(){ return 0; } + public int flags = 0; + public int reqGlEsVersion = 0; + public int version = 0; + public static Parcelable.Creator CREATOR = null; + public static int FLAG_REQUIRED = 0; + public static int GL_ES_VERSION_UNDEFINED = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/pm/InstallSourceInfo.java b/java/ql/test/stubs/android/android/content/pm/InstallSourceInfo.java new file mode 100644 index 00000000000..85c503025d0 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/InstallSourceInfo.java @@ -0,0 +1,18 @@ +// Generated automatically from android.content.pm.InstallSourceInfo for testing purposes + +package android.content.pm; + +import android.content.pm.SigningInfo; +import android.os.Parcel; +import android.os.Parcelable; + +public class InstallSourceInfo implements Parcelable +{ + public SigningInfo getInitiatingPackageSigningInfo(){ return null; } + public String getInitiatingPackageName(){ return null; } + public String getInstallingPackageName(){ return null; } + public String getOriginatingPackageName(){ return null; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/pm/InstrumentationInfo.java b/java/ql/test/stubs/android/android/content/pm/InstrumentationInfo.java new file mode 100644 index 00000000000..70063a09744 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/InstrumentationInfo.java @@ -0,0 +1,27 @@ +// Generated automatically from android.content.pm.InstrumentationInfo for testing purposes + +package android.content.pm; + +import android.content.pm.PackageItemInfo; +import android.os.Parcel; +import android.os.Parcelable; + +public class InstrumentationInfo extends PackageItemInfo implements Parcelable +{ + public InstrumentationInfo(){} + public InstrumentationInfo(InstrumentationInfo p0){} + public String dataDir = null; + public String publicSourceDir = null; + public String sourceDir = null; + public String targetPackage = null; + public String targetProcesses = null; + public String toString(){ return null; } + public String[] splitNames = null; + public String[] splitPublicSourceDirs = null; + public String[] splitSourceDirs = null; + public boolean functionalTest = false; + public boolean handleProfiling = false; + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/pm/ModuleInfo.java b/java/ql/test/stubs/android/android/content/pm/ModuleInfo.java new file mode 100644 index 00000000000..c7aef5f7ac7 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/ModuleInfo.java @@ -0,0 +1,19 @@ +// Generated automatically from android.content.pm.ModuleInfo for testing purposes + +package android.content.pm; + +import android.os.Parcel; +import android.os.Parcelable; + +public class ModuleInfo implements Parcelable +{ + public CharSequence getName(){ return null; } + public String getPackageName(){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public boolean isHidden(){ return false; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/pm/PackageInfo.java b/java/ql/test/stubs/android/android/content/pm/PackageInfo.java new file mode 100644 index 00000000000..566e8e73fbf --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/PackageInfo.java @@ -0,0 +1,59 @@ +// Generated automatically from android.content.pm.PackageInfo for testing purposes + +package android.content.pm; + +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ConfigurationInfo; +import android.content.pm.FeatureGroupInfo; +import android.content.pm.FeatureInfo; +import android.content.pm.InstrumentationInfo; +import android.content.pm.PermissionInfo; +import android.content.pm.ProviderInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.Signature; +import android.content.pm.SigningInfo; +import android.os.Parcel; +import android.os.Parcelable; + +public class PackageInfo implements Parcelable +{ + public ActivityInfo[] activities = null; + public ActivityInfo[] receivers = null; + public ApplicationInfo applicationInfo = null; + public ConfigurationInfo[] configPreferences = null; + public FeatureGroupInfo[] featureGroups = null; + public FeatureInfo[] reqFeatures = null; + public InstrumentationInfo[] instrumentation = null; + public PackageInfo(){} + public PermissionInfo[] permissions = null; + public ProviderInfo[] providers = null; + public ServiceInfo[] services = null; + public Signature[] signatures = null; + public SigningInfo signingInfo = null; + public String packageName = null; + public String sharedUserId = null; + public String toString(){ return null; } + public String versionName = null; + public String[] requestedPermissions = null; + public String[] splitNames = null; + public boolean isApex = false; + public int baseRevisionCode = 0; + public int describeContents(){ return 0; } + public int installLocation = 0; + public int sharedUserLabel = 0; + public int versionCode = 0; + public int[] gids = null; + public int[] requestedPermissionsFlags = null; + public int[] splitRevisionCodes = null; + public long firstInstallTime = 0; + public long getLongVersionCode(){ return 0; } + public long lastUpdateTime = 0; + public static Parcelable.Creator CREATOR = null; + public static int INSTALL_LOCATION_AUTO = 0; + public static int INSTALL_LOCATION_INTERNAL_ONLY = 0; + public static int INSTALL_LOCATION_PREFER_EXTERNAL = 0; + public static int REQUESTED_PERMISSION_GRANTED = 0; + public void setLongVersionCode(long p0){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/pm/PackageInstaller.java b/java/ql/test/stubs/android/android/content/pm/PackageInstaller.java new file mode 100644 index 00000000000..46337e28f60 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/PackageInstaller.java @@ -0,0 +1,151 @@ +// Generated automatically from android.content.pm.PackageInstaller for testing purposes + +package android.content.pm; + +import android.content.Intent; +import android.content.IntentSender; +import android.content.pm.VersionedPackage; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Handler; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.UserHandle; +import java.io.Closeable; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; +import java.util.Set; + +public class PackageInstaller +{ + abstract static public class SessionCallback + { + public SessionCallback(){} + public abstract void onActiveChanged(int p0, boolean p1); + public abstract void onBadgingChanged(int p0); + public abstract void onCreated(int p0); + public abstract void onFinished(int p0, boolean p1); + public abstract void onProgressChanged(int p0, float p1); + } + public List getActiveStagedSessions(){ return null; } + public List getAllSessions(){ return null; } + public List getMySessions(){ return null; } + public List getStagedSessions(){ return null; } + public PackageInstaller.Session openSession(int p0){ return null; } + public PackageInstaller.SessionInfo getActiveStagedSession(){ return null; } + public PackageInstaller.SessionInfo getSessionInfo(int p0){ return null; } + public int createSession(PackageInstaller.SessionParams p0){ return 0; } + public static String ACTION_SESSION_COMMITTED = null; + public static String ACTION_SESSION_DETAILS = null; + public static String ACTION_SESSION_UPDATED = null; + public static String EXTRA_OTHER_PACKAGE_NAME = null; + public static String EXTRA_PACKAGE_NAME = null; + public static String EXTRA_SESSION = null; + public static String EXTRA_SESSION_ID = null; + public static String EXTRA_STATUS = null; + public static String EXTRA_STATUS_MESSAGE = null; + public static String EXTRA_STORAGE_PATH = null; + public static int STATUS_FAILURE = 0; + public static int STATUS_FAILURE_ABORTED = 0; + public static int STATUS_FAILURE_BLOCKED = 0; + public static int STATUS_FAILURE_CONFLICT = 0; + public static int STATUS_FAILURE_INCOMPATIBLE = 0; + public static int STATUS_FAILURE_INVALID = 0; + public static int STATUS_FAILURE_STORAGE = 0; + public static int STATUS_PENDING_USER_ACTION = 0; + public static int STATUS_SUCCESS = 0; + public void abandonSession(int p0){} + public void installExistingPackage(String p0, int p1, IntentSender p2){} + public void registerSessionCallback(PackageInstaller.SessionCallback p0){} + public void registerSessionCallback(PackageInstaller.SessionCallback p0, Handler p1){} + public void uninstall(String p0, IntentSender p1){} + public void uninstall(VersionedPackage p0, IntentSender p1){} + public void unregisterSessionCallback(PackageInstaller.SessionCallback p0){} + public void updateSessionAppIcon(int p0, Bitmap p1){} + public void updateSessionAppLabel(int p0, CharSequence p1){} + static public class Session implements Closeable + { + public InputStream openRead(String p0){ return null; } + public OutputStream openWrite(String p0, long p1, long p2){ return null; } + public String[] getNames(){ return null; } + public boolean isMultiPackage(){ return false; } + public boolean isStaged(){ return false; } + public int getParentSessionId(){ return 0; } + public int[] getChildSessionIds(){ return null; } + public void abandon(){} + public void addChildSessionId(int p0){} + public void close(){} + public void commit(IntentSender p0){} + public void fsync(OutputStream p0){} + public void removeChildSessionId(int p0){} + public void removeSplit(String p0){} + public void setStagingProgress(float p0){} + public void transfer(String p0){} + } + static public class SessionInfo implements Parcelable + { + public Bitmap getAppIcon(){ return null; } + public CharSequence getAppLabel(){ return null; } + public Intent createDetailsIntent(){ return null; } + public String getAppPackageName(){ return null; } + public String getInstallerPackageName(){ return null; } + public String getStagedSessionErrorMessage(){ return null; } + public Uri getOriginatingUri(){ return null; } + public Uri getReferrerUri(){ return null; } + public UserHandle getUser(){ return null; } + public boolean hasParentSessionId(){ return false; } + public boolean isActive(){ return false; } + public boolean isCommitted(){ return false; } + public boolean isMultiPackage(){ return false; } + public boolean isSealed(){ return false; } + public boolean isStaged(){ return false; } + public boolean isStagedSessionActive(){ return false; } + public boolean isStagedSessionApplied(){ return false; } + public boolean isStagedSessionFailed(){ return false; } + public boolean isStagedSessionReady(){ return false; } + public float getProgress(){ return 0; } + public int describeContents(){ return 0; } + public int getInstallLocation(){ return 0; } + public int getInstallReason(){ return 0; } + public int getMode(){ return 0; } + public int getOriginatingUid(){ return 0; } + public int getParentSessionId(){ return 0; } + public int getSessionId(){ return 0; } + public int getStagedSessionErrorCode(){ return 0; } + public int[] getChildSessionIds(){ return null; } + public long getCreatedMillis(){ return 0; } + public long getSize(){ return 0; } + public long getUpdatedMillis(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static int INVALID_ID = 0; + public static int STAGED_SESSION_ACTIVATION_FAILED = 0; + public static int STAGED_SESSION_NO_ERROR = 0; + public static int STAGED_SESSION_UNKNOWN = 0; + public static int STAGED_SESSION_VERIFICATION_FAILED = 0; + public void writeToParcel(Parcel p0, int p1){} + } + static public class SessionParams implements Parcelable + { + protected SessionParams() {} + public SessionParams(int p0){} + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static Set RESTRICTED_PERMISSIONS_ALL = null; + public static int MODE_FULL_INSTALL = 0; + public static int MODE_INHERIT_EXISTING = 0; + public void setAppIcon(Bitmap p0){} + public void setAppLabel(CharSequence p0){} + public void setAppPackageName(String p0){} + public void setAutoRevokePermissionsMode(boolean p0){} + public void setInstallLocation(int p0){} + public void setInstallReason(int p0){} + public void setMultiPackage(){} + public void setOriginatingUid(int p0){} + public void setOriginatingUri(Uri p0){} + public void setReferrerUri(Uri p0){} + public void setSize(long p0){} + public void setWhitelistedRestrictedPermissions(Set p0){} + public void writeToParcel(Parcel p0, int p1){} + } +} diff --git a/java/ql/test/stubs/android/android/content/pm/PackageItemInfo.java b/java/ql/test/stubs/android/android/content/pm/PackageItemInfo.java new file mode 100644 index 00000000000..1d35a31f52b --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/PackageItemInfo.java @@ -0,0 +1,34 @@ +// Generated automatically from android.content.pm.PackageItemInfo for testing purposes + +package android.content.pm; + +import android.content.pm.PackageManager; +import android.content.res.XmlResourceParser; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Parcel; +import android.util.Printer; + +public class PackageItemInfo +{ + protected PackageItemInfo(Parcel p0){} + protected void dumpBack(Printer p0, String p1){} + protected void dumpFront(Printer p0, String p1){} + public Bundle metaData = null; + public CharSequence loadLabel(PackageManager p0){ return null; } + public CharSequence nonLocalizedLabel = null; + public Drawable loadBanner(PackageManager p0){ return null; } + public Drawable loadIcon(PackageManager p0){ return null; } + public Drawable loadLogo(PackageManager p0){ return null; } + public Drawable loadUnbadgedIcon(PackageManager p0){ return null; } + public PackageItemInfo(){} + public PackageItemInfo(PackageItemInfo p0){} + public String name = null; + public String packageName = null; + public XmlResourceParser loadXmlMetaData(PackageManager p0, String p1){ return null; } + public int banner = 0; + public int icon = 0; + public int labelRes = 0; + public int logo = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/pm/PackageManager.java b/java/ql/test/stubs/android/android/content/pm/PackageManager.java new file mode 100644 index 00000000000..73e82024984 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/PackageManager.java @@ -0,0 +1,331 @@ +// Generated automatically from android.content.pm.PackageManager for testing purposes + +package android.content.pm; + +import android.content.ComponentName; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ChangedPackages; +import android.content.pm.FeatureInfo; +import android.content.pm.InstallSourceInfo; +import android.content.pm.InstrumentationInfo; +import android.content.pm.ModuleInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageInstaller; +import android.content.pm.PermissionGroupInfo; +import android.content.pm.PermissionInfo; +import android.content.pm.ProviderInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.SharedLibraryInfo; +import android.content.pm.VersionedPackage; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.UserHandle; +import android.util.AndroidException; +import java.util.List; +import java.util.Set; + +abstract public class PackageManager +{ + public Bundle getSuspendedPackageAppExtras(){ return null; } + public CharSequence getBackgroundPermissionOptionLabel(){ return null; } + public InstallSourceInfo getInstallSourceInfo(String p0){ return null; } + public List getInstalledModules(int p0){ return null; } + public ModuleInfo getModuleInfo(String p0, int p1){ return null; } + public PackageInfo getPackageArchiveInfo(String p0, int p1){ return null; } + public PackageManager(){} + public Set getMimeGroup(String p0){ return null; } + public Set getWhitelistedRestrictedPermissions(String p0, int p1){ return null; } + public abstract ActivityInfo getActivityInfo(ComponentName p0, int p1); + public abstract ActivityInfo getReceiverInfo(ComponentName p0, int p1); + public abstract ApplicationInfo getApplicationInfo(String p0, int p1); + public abstract ChangedPackages getChangedPackages(int p0); + public abstract CharSequence getApplicationLabel(ApplicationInfo p0); + public abstract CharSequence getText(String p0, int p1, ApplicationInfo p2); + public abstract CharSequence getUserBadgedLabel(CharSequence p0, UserHandle p1); + public abstract Drawable getActivityBanner(ComponentName p0); + public abstract Drawable getActivityBanner(Intent p0); + public abstract Drawable getActivityIcon(ComponentName p0); + public abstract Drawable getActivityIcon(Intent p0); + public abstract Drawable getActivityLogo(ComponentName p0); + public abstract Drawable getActivityLogo(Intent p0); + public abstract Drawable getApplicationBanner(ApplicationInfo p0); + public abstract Drawable getApplicationBanner(String p0); + public abstract Drawable getApplicationIcon(ApplicationInfo p0); + public abstract Drawable getApplicationIcon(String p0); + public abstract Drawable getApplicationLogo(ApplicationInfo p0); + public abstract Drawable getApplicationLogo(String p0); + public abstract Drawable getDefaultActivityIcon(); + public abstract Drawable getDrawable(String p0, int p1, ApplicationInfo p2); + public abstract Drawable getUserBadgedDrawableForDensity(Drawable p0, UserHandle p1, Rect p2, int p3); + public abstract Drawable getUserBadgedIcon(Drawable p0, UserHandle p1); + public abstract FeatureInfo[] getSystemAvailableFeatures(); + public abstract InstrumentationInfo getInstrumentationInfo(ComponentName p0, int p1); + public abstract Intent getLaunchIntentForPackage(String p0); + public abstract Intent getLeanbackLaunchIntentForPackage(String p0); + public abstract List getInstalledApplications(int p0); + public abstract List queryInstrumentation(String p0, int p1); + public abstract List getInstalledPackages(int p0); + public abstract List getPackagesHoldingPermissions(String[] p0, int p1); + public abstract List getPreferredPackages(int p0); + public abstract List getAllPermissionGroups(int p0); + public abstract List queryPermissionsByGroup(String p0, int p1); + public abstract List queryContentProviders(String p0, int p1, int p2); + public abstract List queryBroadcastReceivers(Intent p0, int p1); + public abstract List queryIntentActivities(Intent p0, int p1); + public abstract List queryIntentActivityOptions(ComponentName p0, Intent[] p1, Intent p2, int p3); + public abstract List queryIntentContentProviders(Intent p0, int p1); + public abstract List queryIntentServices(Intent p0, int p1); + public abstract List getSharedLibraries(int p0); + public abstract PackageInfo getPackageInfo(String p0, int p1); + public abstract PackageInfo getPackageInfo(VersionedPackage p0, int p1); + public abstract PackageInstaller getPackageInstaller(); + public abstract PermissionGroupInfo getPermissionGroupInfo(String p0, int p1); + public abstract PermissionInfo getPermissionInfo(String p0, int p1); + public abstract ProviderInfo getProviderInfo(ComponentName p0, int p1); + public abstract ProviderInfo resolveContentProvider(String p0, int p1); + public abstract ResolveInfo resolveActivity(Intent p0, int p1); + public abstract ResolveInfo resolveService(Intent p0, int p1); + public abstract Resources getResourcesForActivity(ComponentName p0); + public abstract Resources getResourcesForApplication(ApplicationInfo p0); + public abstract Resources getResourcesForApplication(String p0); + public abstract ServiceInfo getServiceInfo(ComponentName p0, int p1); + public abstract String getInstallerPackageName(String p0); + public abstract String getNameForUid(int p0); + public abstract String[] canonicalToCurrentPackageNames(String[] p0); + public abstract String[] currentToCanonicalPackageNames(String[] p0); + public abstract String[] getPackagesForUid(int p0); + public abstract String[] getSystemSharedLibraryNames(); + public abstract XmlResourceParser getXml(String p0, int p1, ApplicationInfo p2); + public abstract boolean addPermission(PermissionInfo p0); + public abstract boolean addPermissionAsync(PermissionInfo p0); + public abstract boolean canRequestPackageInstalls(); + public abstract boolean hasSystemFeature(String p0); + public abstract boolean hasSystemFeature(String p0, int p1); + public abstract boolean isInstantApp(); + public abstract boolean isInstantApp(String p0); + public abstract boolean isPermissionRevokedByPolicy(String p0, String p1); + public abstract boolean isSafeMode(); + public abstract byte[] getInstantAppCookie(); + public abstract int checkPermission(String p0, String p1); + public abstract int checkSignatures(String p0, String p1); + public abstract int checkSignatures(int p0, int p1); + public abstract int getApplicationEnabledSetting(String p0); + public abstract int getComponentEnabledSetting(ComponentName p0); + public abstract int getInstantAppCookieMaxBytes(); + public abstract int getPackageUid(String p0, int p1); + public abstract int getPreferredActivities(List p0, List p1, String p2); + public abstract int[] getPackageGids(String p0); + public abstract int[] getPackageGids(String p0, int p1); + public abstract void addPackageToPreferred(String p0); + public abstract void addPreferredActivity(IntentFilter p0, int p1, ComponentName[] p2, ComponentName p3); + public abstract void clearInstantAppCookie(); + public abstract void clearPackagePreferredActivities(String p0); + public abstract void extendVerificationTimeout(int p0, int p1, long p2); + public abstract void removePackageFromPreferred(String p0); + public abstract void removePermission(String p0); + public abstract void setApplicationCategoryHint(String p0, int p1); + public abstract void setApplicationEnabledSetting(String p0, int p1, int p2); + public abstract void setComponentEnabledSetting(ComponentName p0, int p1, int p2); + public abstract void setInstallerPackageName(String p0, String p1); + public abstract void updateInstantAppCookie(byte[] p0); + public abstract void verifyPendingInstall(int p0, int p1); + public boolean addWhitelistedRestrictedPermission(String p0, String p1, int p2){ return false; } + public boolean getSyntheticAppDetailsActivityEnabled(String p0){ return false; } + public boolean hasSigningCertificate(String p0, byte[] p1, int p2){ return false; } + public boolean hasSigningCertificate(int p0, byte[] p1, int p2){ return false; } + public boolean isAutoRevokeWhitelisted(){ return false; } + public boolean isAutoRevokeWhitelisted(String p0){ return false; } + public boolean isDefaultApplicationIcon(Drawable p0){ return false; } + public boolean isDeviceUpgrading(){ return false; } + public boolean isPackageSuspended(){ return false; } + public boolean isPackageSuspended(String p0){ return false; } + public boolean removeWhitelistedRestrictedPermission(String p0, String p1, int p2){ return false; } + public boolean setAutoRevokeWhitelisted(String p0, boolean p1){ return false; } + public static String EXTRA_VERIFICATION_ID = null; + public static String EXTRA_VERIFICATION_RESULT = null; + public static String FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS = null; + public static String FEATURE_APP_WIDGETS = null; + public static String FEATURE_AUDIO_LOW_LATENCY = null; + public static String FEATURE_AUDIO_OUTPUT = null; + public static String FEATURE_AUDIO_PRO = null; + public static String FEATURE_AUTOFILL = null; + public static String FEATURE_AUTOMOTIVE = null; + public static String FEATURE_BACKUP = null; + public static String FEATURE_BLUETOOTH = null; + public static String FEATURE_BLUETOOTH_LE = null; + public static String FEATURE_CAMERA = null; + public static String FEATURE_CAMERA_ANY = null; + public static String FEATURE_CAMERA_AR = null; + public static String FEATURE_CAMERA_AUTOFOCUS = null; + public static String FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING = null; + public static String FEATURE_CAMERA_CAPABILITY_MANUAL_SENSOR = null; + public static String FEATURE_CAMERA_CAPABILITY_RAW = null; + public static String FEATURE_CAMERA_CONCURRENT = null; + public static String FEATURE_CAMERA_EXTERNAL = null; + public static String FEATURE_CAMERA_FLASH = null; + public static String FEATURE_CAMERA_FRONT = null; + public static String FEATURE_CAMERA_LEVEL_FULL = null; + public static String FEATURE_CANT_SAVE_STATE = null; + public static String FEATURE_COMPANION_DEVICE_SETUP = null; + public static String FEATURE_CONNECTION_SERVICE = null; + public static String FEATURE_CONSUMER_IR = null; + public static String FEATURE_CONTROLS = null; + public static String FEATURE_DEVICE_ADMIN = null; + public static String FEATURE_EMBEDDED = null; + public static String FEATURE_ETHERNET = null; + public static String FEATURE_FACE = null; + public static String FEATURE_FAKETOUCH = null; + public static String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = null; + public static String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = null; + public static String FEATURE_FINGERPRINT = null; + public static String FEATURE_FREEFORM_WINDOW_MANAGEMENT = null; + public static String FEATURE_GAMEPAD = null; + public static String FEATURE_HIFI_SENSORS = null; + public static String FEATURE_HOME_SCREEN = null; + public static String FEATURE_INPUT_METHODS = null; + public static String FEATURE_IPSEC_TUNNELS = null; + public static String FEATURE_IRIS = null; + public static String FEATURE_LEANBACK = null; + public static String FEATURE_LEANBACK_ONLY = null; + public static String FEATURE_LIVE_TV = null; + public static String FEATURE_LIVE_WALLPAPER = null; + public static String FEATURE_LOCATION = null; + public static String FEATURE_LOCATION_GPS = null; + public static String FEATURE_LOCATION_NETWORK = null; + public static String FEATURE_MANAGED_USERS = null; + public static String FEATURE_MICROPHONE = null; + public static String FEATURE_MIDI = null; + public static String FEATURE_NFC = null; + public static String FEATURE_NFC_BEAM = null; + public static String FEATURE_NFC_HOST_CARD_EMULATION = null; + public static String FEATURE_NFC_HOST_CARD_EMULATION_NFCF = null; + public static String FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE = null; + public static String FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC = null; + public static String FEATURE_OPENGLES_EXTENSION_PACK = null; + public static String FEATURE_PC = null; + public static String FEATURE_PICTURE_IN_PICTURE = null; + public static String FEATURE_PRINTING = null; + public static String FEATURE_RAM_LOW = null; + public static String FEATURE_RAM_NORMAL = null; + public static String FEATURE_SCREEN_LANDSCAPE = null; + public static String FEATURE_SCREEN_PORTRAIT = null; + public static String FEATURE_SECURELY_REMOVES_USERS = null; + public static String FEATURE_SECURE_LOCK_SCREEN = null; + public static String FEATURE_SENSOR_ACCELEROMETER = null; + public static String FEATURE_SENSOR_AMBIENT_TEMPERATURE = null; + public static String FEATURE_SENSOR_BAROMETER = null; + public static String FEATURE_SENSOR_COMPASS = null; + public static String FEATURE_SENSOR_GYROSCOPE = null; + public static String FEATURE_SENSOR_HEART_RATE = null; + public static String FEATURE_SENSOR_HEART_RATE_ECG = null; + public static String FEATURE_SENSOR_HINGE_ANGLE = null; + public static String FEATURE_SENSOR_LIGHT = null; + public static String FEATURE_SENSOR_PROXIMITY = null; + public static String FEATURE_SENSOR_RELATIVE_HUMIDITY = null; + public static String FEATURE_SENSOR_STEP_COUNTER = null; + public static String FEATURE_SENSOR_STEP_DETECTOR = null; + public static String FEATURE_SE_OMAPI_ESE = null; + public static String FEATURE_SE_OMAPI_SD = null; + public static String FEATURE_SE_OMAPI_UICC = null; + public static String FEATURE_SIP = null; + public static String FEATURE_SIP_VOIP = null; + public static String FEATURE_STRONGBOX_KEYSTORE = null; + public static String FEATURE_TELEPHONY = null; + public static String FEATURE_TELEPHONY_CDMA = null; + public static String FEATURE_TELEPHONY_EUICC = null; + public static String FEATURE_TELEPHONY_GSM = null; + public static String FEATURE_TELEPHONY_IMS = null; + public static String FEATURE_TELEPHONY_MBMS = null; + public static String FEATURE_TELEVISION = null; + public static String FEATURE_TOUCHSCREEN = null; + public static String FEATURE_TOUCHSCREEN_MULTITOUCH = null; + public static String FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT = null; + public static String FEATURE_TOUCHSCREEN_MULTITOUCH_JAZZHAND = null; + public static String FEATURE_USB_ACCESSORY = null; + public static String FEATURE_USB_HOST = null; + public static String FEATURE_VERIFIED_BOOT = null; + public static String FEATURE_VR_HEADTRACKING = null; + public static String FEATURE_VR_MODE = null; + public static String FEATURE_VR_MODE_HIGH_PERFORMANCE = null; + public static String FEATURE_VULKAN_DEQP_LEVEL = null; + public static String FEATURE_VULKAN_HARDWARE_COMPUTE = null; + public static String FEATURE_VULKAN_HARDWARE_LEVEL = null; + public static String FEATURE_VULKAN_HARDWARE_VERSION = null; + public static String FEATURE_WATCH = null; + public static String FEATURE_WEBVIEW = null; + public static String FEATURE_WIFI = null; + public static String FEATURE_WIFI_AWARE = null; + public static String FEATURE_WIFI_DIRECT = null; + public static String FEATURE_WIFI_PASSPOINT = null; + public static String FEATURE_WIFI_RTT = null; + public static int CERT_INPUT_RAW_X509 = 0; + public static int CERT_INPUT_SHA256 = 0; + public static int COMPONENT_ENABLED_STATE_DEFAULT = 0; + public static int COMPONENT_ENABLED_STATE_DISABLED = 0; + public static int COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED = 0; + public static int COMPONENT_ENABLED_STATE_DISABLED_USER = 0; + public static int COMPONENT_ENABLED_STATE_ENABLED = 0; + public static int DONT_KILL_APP = 0; + public static int FLAG_PERMISSION_WHITELIST_INSTALLER = 0; + public static int FLAG_PERMISSION_WHITELIST_SYSTEM = 0; + public static int FLAG_PERMISSION_WHITELIST_UPGRADE = 0; + public static int GET_ACTIVITIES = 0; + public static int GET_CONFIGURATIONS = 0; + public static int GET_DISABLED_COMPONENTS = 0; + public static int GET_DISABLED_UNTIL_USED_COMPONENTS = 0; + public static int GET_GIDS = 0; + public static int GET_INSTRUMENTATION = 0; + public static int GET_INTENT_FILTERS = 0; + public static int GET_META_DATA = 0; + public static int GET_PERMISSIONS = 0; + public static int GET_PROVIDERS = 0; + public static int GET_RECEIVERS = 0; + public static int GET_RESOLVED_FILTER = 0; + public static int GET_SERVICES = 0; + public static int GET_SHARED_LIBRARY_FILES = 0; + public static int GET_SIGNATURES = 0; + public static int GET_SIGNING_CERTIFICATES = 0; + public static int GET_UNINSTALLED_PACKAGES = 0; + public static int GET_URI_PERMISSION_PATTERNS = 0; + public static int INSTALL_REASON_DEVICE_RESTORE = 0; + public static int INSTALL_REASON_DEVICE_SETUP = 0; + public static int INSTALL_REASON_POLICY = 0; + public static int INSTALL_REASON_UNKNOWN = 0; + public static int INSTALL_REASON_USER = 0; + public static int MATCH_ALL = 0; + public static int MATCH_APEX = 0; + public static int MATCH_DEFAULT_ONLY = 0; + public static int MATCH_DIRECT_BOOT_AUTO = 0; + public static int MATCH_DIRECT_BOOT_AWARE = 0; + public static int MATCH_DIRECT_BOOT_UNAWARE = 0; + public static int MATCH_DISABLED_COMPONENTS = 0; + public static int MATCH_DISABLED_UNTIL_USED_COMPONENTS = 0; + public static int MATCH_SYSTEM_ONLY = 0; + public static int MATCH_UNINSTALLED_PACKAGES = 0; + public static int PERMISSION_DENIED = 0; + public static int PERMISSION_GRANTED = 0; + public static int SIGNATURE_FIRST_NOT_SIGNED = 0; + public static int SIGNATURE_MATCH = 0; + public static int SIGNATURE_NEITHER_SIGNED = 0; + public static int SIGNATURE_NO_MATCH = 0; + public static int SIGNATURE_SECOND_NOT_SIGNED = 0; + public static int SIGNATURE_UNKNOWN_PACKAGE = 0; + public static int SYNCHRONOUS = 0; + public static int VERIFICATION_ALLOW = 0; + public static int VERIFICATION_REJECT = 0; + public static int VERSION_CODE_HIGHEST = 0; + public static long MAXIMUM_VERIFICATION_TIMEOUT = 0; + public void setMimeGroup(String p0, Set p1){} + static public class NameNotFoundException extends AndroidException + { + public NameNotFoundException(){} + public NameNotFoundException(String p0){} + } +} diff --git a/java/ql/test/stubs/android/android/content/pm/PathPermission.java b/java/ql/test/stubs/android/android/content/pm/PathPermission.java new file mode 100644 index 00000000000..455adfe5be7 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/PathPermission.java @@ -0,0 +1,18 @@ +// Generated automatically from android.content.pm.PathPermission for testing purposes + +package android.content.pm; + +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PatternMatcher; + +public class PathPermission extends PatternMatcher +{ + protected PathPermission() {} + public PathPermission(Parcel p0){} + public PathPermission(String p0, int p1, String p2, String p3){} + public String getReadPermission(){ return null; } + public String getWritePermission(){ return null; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/pm/PermissionGroupInfo.java b/java/ql/test/stubs/android/android/content/pm/PermissionGroupInfo.java new file mode 100644 index 00000000000..a74934de52f --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/PermissionGroupInfo.java @@ -0,0 +1,24 @@ +// Generated automatically from android.content.pm.PermissionGroupInfo for testing purposes + +package android.content.pm; + +import android.content.pm.PackageItemInfo; +import android.content.pm.PackageManager; +import android.os.Parcel; +import android.os.Parcelable; + +public class PermissionGroupInfo extends PackageItemInfo implements Parcelable +{ + public CharSequence loadDescription(PackageManager p0){ return null; } + public CharSequence nonLocalizedDescription = null; + public PermissionGroupInfo(){} + public PermissionGroupInfo(PermissionGroupInfo p0){} + public String toString(){ return null; } + public int describeContents(){ return 0; } + public int descriptionRes = 0; + public int flags = 0; + public int priority = 0; + public static Parcelable.Creator CREATOR = null; + public static int FLAG_PERSONAL_INFO = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/pm/PermissionInfo.java b/java/ql/test/stubs/android/android/content/pm/PermissionInfo.java new file mode 100644 index 00000000000..2c95bb9af7a --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/PermissionInfo.java @@ -0,0 +1,48 @@ +// Generated automatically from android.content.pm.PermissionInfo for testing purposes + +package android.content.pm; + +import android.content.pm.PackageItemInfo; +import android.content.pm.PackageManager; +import android.os.Parcel; +import android.os.Parcelable; + +public class PermissionInfo extends PackageItemInfo implements Parcelable +{ + public CharSequence loadDescription(PackageManager p0){ return null; } + public CharSequence nonLocalizedDescription = null; + public PermissionInfo(){} + public PermissionInfo(PermissionInfo p0){} + public String group = null; + public String toString(){ return null; } + public int describeContents(){ return 0; } + public int descriptionRes = 0; + public int flags = 0; + public int getProtection(){ return 0; } + public int getProtectionFlags(){ return 0; } + public int protectionLevel = 0; + public static Parcelable.Creator CREATOR = null; + public static int FLAG_COSTS_MONEY = 0; + public static int FLAG_HARD_RESTRICTED = 0; + public static int FLAG_IMMUTABLY_RESTRICTED = 0; + public static int FLAG_INSTALLED = 0; + public static int FLAG_SOFT_RESTRICTED = 0; + public static int PROTECTION_DANGEROUS = 0; + public static int PROTECTION_FLAG_APPOP = 0; + public static int PROTECTION_FLAG_DEVELOPMENT = 0; + public static int PROTECTION_FLAG_INSTALLER = 0; + public static int PROTECTION_FLAG_INSTANT = 0; + public static int PROTECTION_FLAG_PRE23 = 0; + public static int PROTECTION_FLAG_PREINSTALLED = 0; + public static int PROTECTION_FLAG_PRIVILEGED = 0; + public static int PROTECTION_FLAG_RUNTIME_ONLY = 0; + public static int PROTECTION_FLAG_SETUP = 0; + public static int PROTECTION_FLAG_SYSTEM = 0; + public static int PROTECTION_FLAG_VERIFIER = 0; + public static int PROTECTION_MASK_BASE = 0; + public static int PROTECTION_MASK_FLAGS = 0; + public static int PROTECTION_NORMAL = 0; + public static int PROTECTION_SIGNATURE = 0; + public static int PROTECTION_SIGNATURE_OR_SYSTEM = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/pm/ProviderInfo.java b/java/ql/test/stubs/android/android/content/pm/ProviderInfo.java new file mode 100644 index 00000000000..93b694cae59 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/ProviderInfo.java @@ -0,0 +1,33 @@ +// Generated automatically from android.content.pm.ProviderInfo for testing purposes + +package android.content.pm; + +import android.content.pm.ComponentInfo; +import android.content.pm.PathPermission; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PatternMatcher; +import android.util.Printer; + +public class ProviderInfo extends ComponentInfo implements Parcelable +{ + public PathPermission[] pathPermissions = null; + public PatternMatcher[] uriPermissionPatterns = null; + public ProviderInfo(){} + public ProviderInfo(ProviderInfo p0){} + public String authority = null; + public String readPermission = null; + public String toString(){ return null; } + public String writePermission = null; + public boolean forceUriPermissions = false; + public boolean grantUriPermissions = false; + public boolean isSyncable = false; + public boolean multiprocess = false; + public int describeContents(){ return 0; } + public int flags = 0; + public int initOrder = 0; + public static Parcelable.Creator CREATOR = null; + public static int FLAG_SINGLE_USER = 0; + public void dump(Printer p0, String p1){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/pm/ResolveInfo.java b/java/ql/test/stubs/android/android/content/pm/ResolveInfo.java new file mode 100644 index 00000000000..3ed20df0d38 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/ResolveInfo.java @@ -0,0 +1,42 @@ +// Generated automatically from android.content.pm.ResolveInfo for testing purposes + +package android.content.pm; + +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.ProviderInfo; +import android.content.pm.ServiceInfo; +import android.graphics.drawable.Drawable; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Printer; + +public class ResolveInfo implements Parcelable +{ + public ActivityInfo activityInfo = null; + public CharSequence loadLabel(PackageManager p0){ return null; } + public CharSequence nonLocalizedLabel = null; + public Drawable loadIcon(PackageManager p0){ return null; } + public IntentFilter filter = null; + public ProviderInfo providerInfo = null; + public ResolveInfo(){} + public ResolveInfo(ResolveInfo p0){} + public ServiceInfo serviceInfo = null; + public String resolvePackageName = null; + public String toString(){ return null; } + public boolean isCrossProfileIntentForwarderActivity(){ return false; } + public boolean isDefault = false; + public boolean isInstantAppAvailable = false; + public final int getIconResource(){ return 0; } + public int describeContents(){ return 0; } + public int icon = 0; + public int labelRes = 0; + public int match = 0; + public int preferredOrder = 0; + public int priority = 0; + public int specificIndex = 0; + public static Parcelable.Creator CREATOR = null; + public void dump(Printer p0, String p1){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/pm/ServiceInfo.java b/java/ql/test/stubs/android/android/content/pm/ServiceInfo.java new file mode 100644 index 00000000000..6044d4af7bb --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/ServiceInfo.java @@ -0,0 +1,37 @@ +// Generated automatically from android.content.pm.ServiceInfo for testing purposes + +package android.content.pm; + +import android.content.pm.ComponentInfo; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Printer; + +public class ServiceInfo extends ComponentInfo implements Parcelable +{ + public ServiceInfo(){} + public ServiceInfo(ServiceInfo p0){} + public String permission = null; + public String toString(){ return null; } + public int describeContents(){ return 0; } + public int flags = 0; + public int getForegroundServiceType(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static int FLAG_EXTERNAL_SERVICE = 0; + public static int FLAG_ISOLATED_PROCESS = 0; + public static int FLAG_SINGLE_USER = 0; + public static int FLAG_STOP_WITH_TASK = 0; + public static int FLAG_USE_APP_ZYGOTE = 0; + public static int FOREGROUND_SERVICE_TYPE_CAMERA = 0; + public static int FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE = 0; + public static int FOREGROUND_SERVICE_TYPE_DATA_SYNC = 0; + public static int FOREGROUND_SERVICE_TYPE_LOCATION = 0; + public static int FOREGROUND_SERVICE_TYPE_MANIFEST = 0; + public static int FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK = 0; + public static int FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION = 0; + public static int FOREGROUND_SERVICE_TYPE_MICROPHONE = 0; + public static int FOREGROUND_SERVICE_TYPE_NONE = 0; + public static int FOREGROUND_SERVICE_TYPE_PHONE_CALL = 0; + public void dump(Printer p0, String p1){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/pm/SharedLibraryInfo.java b/java/ql/test/stubs/android/android/content/pm/SharedLibraryInfo.java new file mode 100644 index 00000000000..10928af2a23 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/SharedLibraryInfo.java @@ -0,0 +1,26 @@ +// Generated automatically from android.content.pm.SharedLibraryInfo for testing purposes + +package android.content.pm; + +import android.content.pm.VersionedPackage; +import android.os.Parcel; +import android.os.Parcelable; +import java.util.List; + +public class SharedLibraryInfo implements Parcelable +{ + public List getDependentPackages(){ return null; } + public String getName(){ return null; } + public String toString(){ return null; } + public VersionedPackage getDeclaringPackage(){ return null; } + public int describeContents(){ return 0; } + public int getType(){ return 0; } + public int getVersion(){ return 0; } + public long getLongVersion(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static int TYPE_BUILTIN = 0; + public static int TYPE_DYNAMIC = 0; + public static int TYPE_STATIC = 0; + public static int VERSION_UNDEFINED = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/pm/Signature.java b/java/ql/test/stubs/android/android/content/pm/Signature.java new file mode 100644 index 00000000000..2f26fb9636e --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/Signature.java @@ -0,0 +1,22 @@ +// Generated automatically from android.content.pm.Signature for testing purposes + +package android.content.pm; + +import android.os.Parcel; +import android.os.Parcelable; + +public class Signature implements Parcelable +{ + protected Signature() {} + public Signature(String p0){} + public Signature(byte[] p0){} + public String toCharsString(){ return null; } + public boolean equals(Object p0){ return false; } + public byte[] toByteArray(){ return null; } + public char[] toChars(){ return null; } + public char[] toChars(char[] p0, int[] p1){ return null; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/pm/SigningInfo.java b/java/ql/test/stubs/android/android/content/pm/SigningInfo.java new file mode 100644 index 00000000000..56aadea159a --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/SigningInfo.java @@ -0,0 +1,20 @@ +// Generated automatically from android.content.pm.SigningInfo for testing purposes + +package android.content.pm; + +import android.content.pm.Signature; +import android.os.Parcel; +import android.os.Parcelable; + +public class SigningInfo implements Parcelable +{ + public Signature[] getApkContentsSigners(){ return null; } + public Signature[] getSigningCertificateHistory(){ return null; } + public SigningInfo(){} + public SigningInfo(SigningInfo p0){} + public boolean hasMultipleSigners(){ return false; } + public boolean hasPastSigningCertificates(){ return false; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/pm/VersionedPackage.java b/java/ql/test/stubs/android/android/content/pm/VersionedPackage.java new file mode 100644 index 00000000000..4d4e37f54bc --- /dev/null +++ b/java/ql/test/stubs/android/android/content/pm/VersionedPackage.java @@ -0,0 +1,22 @@ +// Generated automatically from android.content.pm.VersionedPackage for testing purposes + +package android.content.pm; + +import android.os.Parcel; +import android.os.Parcelable; + +public class VersionedPackage implements Parcelable +{ + protected VersionedPackage() {} + public String getPackageName(){ return null; } + public String toString(){ return null; } + public VersionedPackage(String p0, int p1){} + public VersionedPackage(String p0, long p1){} + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int getVersionCode(){ return 0; } + public int hashCode(){ return 0; } + public long getLongVersionCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/res/AssetFileDescriptor.java b/java/ql/test/stubs/android/android/content/res/AssetFileDescriptor.java new file mode 100644 index 00000000000..46e0266dd62 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/res/AssetFileDescriptor.java @@ -0,0 +1,33 @@ +// Generated automatically from android.content.res.AssetFileDescriptor for testing purposes + +package android.content.res; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.ParcelFileDescriptor; +import android.os.Parcelable; +import java.io.Closeable; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; + +public class AssetFileDescriptor implements Closeable, Parcelable +{ + protected AssetFileDescriptor() {} + public AssetFileDescriptor(ParcelFileDescriptor p0, long p1, long p2){} + public AssetFileDescriptor(ParcelFileDescriptor p0, long p1, long p2, Bundle p3){} + public Bundle getExtras(){ return null; } + public FileDescriptor getFileDescriptor(){ return null; } + public FileInputStream createInputStream(){ return null; } + public FileOutputStream createOutputStream(){ return null; } + public ParcelFileDescriptor getParcelFileDescriptor(){ return null; } + public String toString(){ return null; } + public int describeContents(){ return 0; } + public long getDeclaredLength(){ return 0; } + public long getLength(){ return 0; } + public long getStartOffset(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static long UNKNOWN_LENGTH = 0; + public void close(){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/res/AssetManager.java b/java/ql/test/stubs/android/android/content/res/AssetManager.java new file mode 100644 index 00000000000..37926aa5c61 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/res/AssetManager.java @@ -0,0 +1,26 @@ +// Generated automatically from android.content.res.AssetManager for testing purposes + +package android.content.res; + +import android.content.res.AssetFileDescriptor; +import android.content.res.XmlResourceParser; +import java.io.InputStream; + +public class AssetManager implements AutoCloseable +{ + protected void finalize(){} + public AssetFileDescriptor openFd(String p0){ return null; } + public AssetFileDescriptor openNonAssetFd(String p0){ return null; } + public AssetFileDescriptor openNonAssetFd(int p0, String p1){ return null; } + public InputStream open(String p0){ return null; } + public InputStream open(String p0, int p1){ return null; } + public String[] getLocales(){ return null; } + public String[] list(String p0){ return null; } + public XmlResourceParser openXmlResourceParser(String p0){ return null; } + public XmlResourceParser openXmlResourceParser(int p0, String p1){ return null; } + public static int ACCESS_BUFFER = 0; + public static int ACCESS_RANDOM = 0; + public static int ACCESS_STREAMING = 0; + public static int ACCESS_UNKNOWN = 0; + public void close(){} +} diff --git a/java/ql/test/stubs/android/android/content/res/ColorStateList.java b/java/ql/test/stubs/android/android/content/res/ColorStateList.java new file mode 100644 index 00000000000..cc6a1851767 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/res/ColorStateList.java @@ -0,0 +1,27 @@ +// Generated automatically from android.content.res.ColorStateList for testing purposes + +package android.content.res; + +import android.content.res.Resources; +import android.os.Parcel; +import android.os.Parcelable; +import org.xmlpull.v1.XmlPullParser; + +public class ColorStateList implements Parcelable +{ + protected ColorStateList() {} + public ColorStateList withAlpha(int p0){ return null; } + public ColorStateList(int[][] p0, int[] p1){} + public String toString(){ return null; } + public boolean isOpaque(){ return false; } + public boolean isStateful(){ return false; } + public int describeContents(){ return 0; } + public int getChangingConfigurations(){ return 0; } + public int getColorForState(int[] p0, int p1){ return 0; } + public int getDefaultColor(){ return 0; } + public static ColorStateList createFromXml(Resources p0, XmlPullParser p1){ return null; } + public static ColorStateList createFromXml(Resources p0, XmlPullParser p1, Resources.Theme p2){ return null; } + public static ColorStateList valueOf(int p0){ return null; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/res/Configuration.java b/java/ql/test/stubs/android/android/content/res/Configuration.java new file mode 100644 index 00000000000..01c3f2a7d15 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/res/Configuration.java @@ -0,0 +1,130 @@ +// Generated automatically from android.content.res.Configuration for testing purposes + +package android.content.res; + +import android.os.LocaleList; +import android.os.Parcel; +import android.os.Parcelable; +import java.util.Locale; + +public class Configuration implements Comparable, Parcelable +{ + public Configuration(){} + public Configuration(Configuration p0){} + public Locale locale = null; + public LocaleList getLocales(){ return null; } + public String toString(){ return null; } + public boolean equals(Configuration p0){ return false; } + public boolean equals(Object p0){ return false; } + public boolean isLayoutSizeAtLeast(int p0){ return false; } + public boolean isNightModeActive(){ return false; } + public boolean isScreenHdr(){ return false; } + public boolean isScreenRound(){ return false; } + public boolean isScreenWideColorGamut(){ return false; } + public float fontScale = 0; + public int colorMode = 0; + public int compareTo(Configuration p0){ return 0; } + public int densityDpi = 0; + public int describeContents(){ return 0; } + public int diff(Configuration p0){ return 0; } + public int getLayoutDirection(){ return 0; } + public int hardKeyboardHidden = 0; + public int hashCode(){ return 0; } + public int keyboard = 0; + public int keyboardHidden = 0; + public int mcc = 0; + public int mnc = 0; + public int navigation = 0; + public int navigationHidden = 0; + public int orientation = 0; + public int screenHeightDp = 0; + public int screenLayout = 0; + public int screenWidthDp = 0; + public int smallestScreenWidthDp = 0; + public int touchscreen = 0; + public int uiMode = 0; + public int updateFrom(Configuration p0){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static boolean needNewResources(int p0, int p1){ return false; } + public static int COLOR_MODE_HDR_MASK = 0; + public static int COLOR_MODE_HDR_NO = 0; + public static int COLOR_MODE_HDR_SHIFT = 0; + public static int COLOR_MODE_HDR_UNDEFINED = 0; + public static int COLOR_MODE_HDR_YES = 0; + public static int COLOR_MODE_UNDEFINED = 0; + public static int COLOR_MODE_WIDE_COLOR_GAMUT_MASK = 0; + public static int COLOR_MODE_WIDE_COLOR_GAMUT_NO = 0; + public static int COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED = 0; + public static int COLOR_MODE_WIDE_COLOR_GAMUT_YES = 0; + public static int DENSITY_DPI_UNDEFINED = 0; + public static int HARDKEYBOARDHIDDEN_NO = 0; + public static int HARDKEYBOARDHIDDEN_UNDEFINED = 0; + public static int HARDKEYBOARDHIDDEN_YES = 0; + public static int KEYBOARDHIDDEN_NO = 0; + public static int KEYBOARDHIDDEN_UNDEFINED = 0; + public static int KEYBOARDHIDDEN_YES = 0; + public static int KEYBOARD_12KEY = 0; + public static int KEYBOARD_NOKEYS = 0; + public static int KEYBOARD_QWERTY = 0; + public static int KEYBOARD_UNDEFINED = 0; + public static int MNC_ZERO = 0; + public static int NAVIGATIONHIDDEN_NO = 0; + public static int NAVIGATIONHIDDEN_UNDEFINED = 0; + public static int NAVIGATIONHIDDEN_YES = 0; + public static int NAVIGATION_DPAD = 0; + public static int NAVIGATION_NONAV = 0; + public static int NAVIGATION_TRACKBALL = 0; + public static int NAVIGATION_UNDEFINED = 0; + public static int NAVIGATION_WHEEL = 0; + public static int ORIENTATION_LANDSCAPE = 0; + public static int ORIENTATION_PORTRAIT = 0; + public static int ORIENTATION_SQUARE = 0; + public static int ORIENTATION_UNDEFINED = 0; + public static int SCREENLAYOUT_LAYOUTDIR_LTR = 0; + public static int SCREENLAYOUT_LAYOUTDIR_MASK = 0; + public static int SCREENLAYOUT_LAYOUTDIR_RTL = 0; + public static int SCREENLAYOUT_LAYOUTDIR_SHIFT = 0; + public static int SCREENLAYOUT_LAYOUTDIR_UNDEFINED = 0; + public static int SCREENLAYOUT_LONG_MASK = 0; + public static int SCREENLAYOUT_LONG_NO = 0; + public static int SCREENLAYOUT_LONG_UNDEFINED = 0; + public static int SCREENLAYOUT_LONG_YES = 0; + public static int SCREENLAYOUT_ROUND_MASK = 0; + public static int SCREENLAYOUT_ROUND_NO = 0; + public static int SCREENLAYOUT_ROUND_UNDEFINED = 0; + public static int SCREENLAYOUT_ROUND_YES = 0; + public static int SCREENLAYOUT_SIZE_LARGE = 0; + public static int SCREENLAYOUT_SIZE_MASK = 0; + public static int SCREENLAYOUT_SIZE_NORMAL = 0; + public static int SCREENLAYOUT_SIZE_SMALL = 0; + public static int SCREENLAYOUT_SIZE_UNDEFINED = 0; + public static int SCREENLAYOUT_SIZE_XLARGE = 0; + public static int SCREENLAYOUT_UNDEFINED = 0; + public static int SCREEN_HEIGHT_DP_UNDEFINED = 0; + public static int SCREEN_WIDTH_DP_UNDEFINED = 0; + public static int SMALLEST_SCREEN_WIDTH_DP_UNDEFINED = 0; + public static int TOUCHSCREEN_FINGER = 0; + public static int TOUCHSCREEN_NOTOUCH = 0; + public static int TOUCHSCREEN_STYLUS = 0; + public static int TOUCHSCREEN_UNDEFINED = 0; + public static int UI_MODE_NIGHT_MASK = 0; + public static int UI_MODE_NIGHT_NO = 0; + public static int UI_MODE_NIGHT_UNDEFINED = 0; + public static int UI_MODE_NIGHT_YES = 0; + public static int UI_MODE_TYPE_APPLIANCE = 0; + public static int UI_MODE_TYPE_CAR = 0; + public static int UI_MODE_TYPE_DESK = 0; + public static int UI_MODE_TYPE_MASK = 0; + public static int UI_MODE_TYPE_NORMAL = 0; + public static int UI_MODE_TYPE_TELEVISION = 0; + public static int UI_MODE_TYPE_UNDEFINED = 0; + public static int UI_MODE_TYPE_VR_HEADSET = 0; + public static int UI_MODE_TYPE_WATCH = 0; + public void readFromParcel(Parcel p0){} + public void setLayoutDirection(Locale p0){} + public void setLocale(Locale p0){} + public void setLocales(LocaleList p0){} + public void setTo(Configuration p0){} + public void setToDefaults(){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/content/res/Resources.java b/java/ql/test/stubs/android/android/content/res/Resources.java new file mode 100644 index 00000000000..9277c4ee765 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/res/Resources.java @@ -0,0 +1,105 @@ +// Generated automatically from android.content.res.Resources for testing purposes + +package android.content.res; + +import android.content.res.AssetFileDescriptor; +import android.content.res.AssetManager; +import android.content.res.ColorStateList; +import android.content.res.Configuration; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.content.res.loader.ResourcesLoader; +import android.graphics.Movie; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import java.io.InputStream; + +public class Resources +{ + protected Resources() {} + public AssetFileDescriptor openRawResourceFd(int p0){ return null; } + public CharSequence getQuantityText(int p0, int p1){ return null; } + public CharSequence getText(int p0){ return null; } + public CharSequence getText(int p0, CharSequence p1){ return null; } + public CharSequence[] getTextArray(int p0){ return null; } + public ColorStateList getColorStateList(int p0){ return null; } + public ColorStateList getColorStateList(int p0, Resources.Theme p1){ return null; } + public Configuration getConfiguration(){ return null; } + public DisplayMetrics getDisplayMetrics(){ return null; } + public Drawable getDrawable(int p0){ return null; } + public Drawable getDrawable(int p0, Resources.Theme p1){ return null; } + public Drawable getDrawableForDensity(int p0, int p1){ return null; } + public Drawable getDrawableForDensity(int p0, int p1, Resources.Theme p2){ return null; } + public InputStream openRawResource(int p0){ return null; } + public InputStream openRawResource(int p0, TypedValue p1){ return null; } + public Movie getMovie(int p0){ return null; } + public Resources(AssetManager p0, DisplayMetrics p1, Configuration p2){} + public String getQuantityString(int p0, int p1){ return null; } + public String getQuantityString(int p0, int p1, Object... p2){ return null; } + public String getResourceEntryName(int p0){ return null; } + public String getResourceName(int p0){ return null; } + public String getResourcePackageName(int p0){ return null; } + public String getResourceTypeName(int p0){ return null; } + public String getString(int p0){ return null; } + public String getString(int p0, Object... p1){ return null; } + public String[] getStringArray(int p0){ return null; } + public TypedArray obtainAttributes(AttributeSet p0, int[] p1){ return null; } + public TypedArray obtainTypedArray(int p0){ return null; } + public Typeface getFont(int p0){ return null; } + public XmlResourceParser getAnimation(int p0){ return null; } + public XmlResourceParser getLayout(int p0){ return null; } + public XmlResourceParser getXml(int p0){ return null; } + public boolean getBoolean(int p0){ return false; } + public class Theme + { + protected Theme() {} + public Drawable getDrawable(int p0){ return null; } + public Resources getResources(){ return null; } + public TypedArray obtainStyledAttributes(AttributeSet p0, int[] p1, int p2, int p3){ return null; } + public TypedArray obtainStyledAttributes(int p0, int[] p1){ return null; } + public TypedArray obtainStyledAttributes(int[] p0){ return null; } + public boolean resolveAttribute(int p0, TypedValue p1, boolean p2){ return false; } + public int getChangingConfigurations(){ return 0; } + public int getExplicitStyle(AttributeSet p0){ return 0; } + public int[] getAttributeResolutionStack(int p0, int p1, int p2){ return null; } + public void applyStyle(int p0, boolean p1){} + public void dump(int p0, String p1, String p2){} + public void rebase(){} + public void setTo(Resources.Theme p0){} + } + public final AssetManager getAssets(){ return null; } + public final Resources.Theme newTheme(){ return null; } + public final void finishPreloading(){} + public final void flushLayoutCache(){} + public float getDimension(int p0){ return 0; } + public float getFloat(int p0){ return 0; } + public float getFraction(int p0, int p1, int p2){ return 0; } + public int getColor(int p0){ return 0; } + public int getColor(int p0, Resources.Theme p1){ return 0; } + public int getDimensionPixelOffset(int p0){ return 0; } + public int getDimensionPixelSize(int p0){ return 0; } + public int getIdentifier(String p0, String p1, String p2){ return 0; } + public int getInteger(int p0){ return 0; } + public int[] getIntArray(int p0){ return null; } + public static Resources getSystem(){ return null; } + public static int ID_NULL = 0; + public static int getAttributeSetSourceResId(AttributeSet p0){ return 0; } + public void addLoaders(ResourcesLoader... p0){} + public void getValue(String p0, TypedValue p1, boolean p2){} + public void getValue(int p0, TypedValue p1, boolean p2){} + public void getValueForDensity(int p0, int p1, TypedValue p2, boolean p3){} + public void parseBundleExtra(String p0, AttributeSet p1, Bundle p2){} + public void parseBundleExtras(XmlResourceParser p0, Bundle p1){} + public void removeLoaders(ResourcesLoader... p0){} + public void updateConfiguration(Configuration p0, DisplayMetrics p1){} + static public class NotFoundException extends RuntimeException + { + public NotFoundException(){} + public NotFoundException(String p0){} + public NotFoundException(String p0, Exception p1){} + } +} diff --git a/java/ql/test/stubs/android/android/content/res/TypedArray.java b/java/ql/test/stubs/android/android/content/res/TypedArray.java new file mode 100644 index 00000000000..2b857671f88 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/res/TypedArray.java @@ -0,0 +1,46 @@ +// Generated automatically from android.content.res.TypedArray for testing purposes + +package android.content.res; + +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.util.TypedValue; + +public class TypedArray +{ + public CharSequence getText(int p0){ return null; } + public CharSequence[] getTextArray(int p0){ return null; } + public ColorStateList getColorStateList(int p0){ return null; } + public Drawable getDrawable(int p0){ return null; } + public Resources getResources(){ return null; } + public String getNonResourceString(int p0){ return null; } + public String getPositionDescription(){ return null; } + public String getString(int p0){ return null; } + public String toString(){ return null; } + public TypedValue peekValue(int p0){ return null; } + public Typeface getFont(int p0){ return null; } + public boolean getBoolean(int p0, boolean p1){ return false; } + public boolean getValue(int p0, TypedValue p1){ return false; } + public boolean hasValue(int p0){ return false; } + public boolean hasValueOrEmpty(int p0){ return false; } + public float getDimension(int p0, float p1){ return 0; } + public float getFloat(int p0, float p1){ return 0; } + public float getFraction(int p0, int p1, int p2, float p3){ return 0; } + public int getChangingConfigurations(){ return 0; } + public int getColor(int p0, int p1){ return 0; } + public int getDimensionPixelOffset(int p0, int p1){ return 0; } + public int getDimensionPixelSize(int p0, int p1){ return 0; } + public int getIndex(int p0){ return 0; } + public int getIndexCount(){ return 0; } + public int getInt(int p0, int p1){ return 0; } + public int getInteger(int p0, int p1){ return 0; } + public int getLayoutDimension(int p0, String p1){ return 0; } + public int getLayoutDimension(int p0, int p1){ return 0; } + public int getResourceId(int p0, int p1){ return 0; } + public int getSourceResourceId(int p0, int p1){ return 0; } + public int getType(int p0){ return 0; } + public int length(){ return 0; } + public void recycle(){} +} diff --git a/java/ql/test/stubs/android/android/content/res/XmlResourceParser.java b/java/ql/test/stubs/android/android/content/res/XmlResourceParser.java new file mode 100644 index 00000000000..61a50fe8bec --- /dev/null +++ b/java/ql/test/stubs/android/android/content/res/XmlResourceParser.java @@ -0,0 +1,12 @@ +// Generated automatically from android.content.res.XmlResourceParser for testing purposes + +package android.content.res; + +import android.util.AttributeSet; +import org.xmlpull.v1.XmlPullParser; + +public interface XmlResourceParser extends AttributeSet, AutoCloseable, XmlPullParser +{ + String getAttributeNamespace(int p0); + void close(); +} diff --git a/java/ql/test/stubs/android/android/content/res/loader/AssetsProvider.java b/java/ql/test/stubs/android/android/content/res/loader/AssetsProvider.java new file mode 100644 index 00000000000..bf99474dcb1 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/res/loader/AssetsProvider.java @@ -0,0 +1,10 @@ +// Generated automatically from android.content.res.loader.AssetsProvider for testing purposes + +package android.content.res.loader; + +import android.content.res.AssetFileDescriptor; + +public interface AssetsProvider +{ + default AssetFileDescriptor loadAssetFd(String p0, int p1){ return null; } +} diff --git a/java/ql/test/stubs/android/android/content/res/loader/ResourcesLoader.java b/java/ql/test/stubs/android/android/content/res/loader/ResourcesLoader.java new file mode 100644 index 00000000000..1ad577ab478 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/res/loader/ResourcesLoader.java @@ -0,0 +1,16 @@ +// Generated automatically from android.content.res.loader.ResourcesLoader for testing purposes + +package android.content.res.loader; + +import android.content.res.loader.ResourcesProvider; +import java.util.List; + +public class ResourcesLoader +{ + public List getProviders(){ return null; } + public ResourcesLoader(){} + public void addProvider(ResourcesProvider p0){} + public void clearProviders(){} + public void removeProvider(ResourcesProvider p0){} + public void setProviders(List p0){} +} diff --git a/java/ql/test/stubs/android/android/content/res/loader/ResourcesProvider.java b/java/ql/test/stubs/android/android/content/res/loader/ResourcesProvider.java new file mode 100644 index 00000000000..059b0961b59 --- /dev/null +++ b/java/ql/test/stubs/android/android/content/res/loader/ResourcesProvider.java @@ -0,0 +1,21 @@ +// Generated automatically from android.content.res.loader.ResourcesProvider for testing purposes + +package android.content.res.loader; + +import android.content.Context; +import android.content.res.loader.AssetsProvider; +import android.os.ParcelFileDescriptor; +import java.io.Closeable; + +public class ResourcesProvider implements AutoCloseable, Closeable +{ + protected ResourcesProvider() {} + protected void finalize(){} + public static ResourcesProvider empty(AssetsProvider p0){ return null; } + public static ResourcesProvider loadFromApk(ParcelFileDescriptor p0){ return null; } + public static ResourcesProvider loadFromApk(ParcelFileDescriptor p0, AssetsProvider p1){ return null; } + public static ResourcesProvider loadFromDirectory(String p0, AssetsProvider p1){ return null; } + public static ResourcesProvider loadFromSplit(Context p0, String p1){ return null; } + public static ResourcesProvider loadFromTable(ParcelFileDescriptor p0, AssetsProvider p1){ return null; } + public void close(){} +} diff --git a/java/ql/test/stubs/android/android/database/CharArrayBuffer.java b/java/ql/test/stubs/android/android/database/CharArrayBuffer.java new file mode 100644 index 00000000000..b75eff88239 --- /dev/null +++ b/java/ql/test/stubs/android/android/database/CharArrayBuffer.java @@ -0,0 +1,13 @@ +// Generated automatically from android.database.CharArrayBuffer for testing purposes + +package android.database; + + +public class CharArrayBuffer +{ + protected CharArrayBuffer() {} + public CharArrayBuffer(char[] p0){} + public CharArrayBuffer(int p0){} + public char[] data = null; + public int sizeCopied = 0; +} diff --git a/java/ql/test/stubs/android/android/database/ContentObserver.java b/java/ql/test/stubs/android/android/database/ContentObserver.java new file mode 100644 index 00000000000..f70e12dc20c --- /dev/null +++ b/java/ql/test/stubs/android/android/database/ContentObserver.java @@ -0,0 +1,22 @@ +// Generated automatically from android.database.ContentObserver for testing purposes + +package android.database; + +import android.net.Uri; +import android.os.Handler; +import java.util.Collection; + +abstract public class ContentObserver +{ + protected ContentObserver() {} + public ContentObserver(Handler p0){} + public boolean deliverSelfNotifications(){ return false; } + public final void dispatchChange(boolean p0){} + public final void dispatchChange(boolean p0, Collection p1, int p2){} + public final void dispatchChange(boolean p0, Uri p1){} + public final void dispatchChange(boolean p0, Uri p1, int p2){} + public void onChange(boolean p0){} + public void onChange(boolean p0, Collection p1, int p2){} + public void onChange(boolean p0, Uri p1){} + public void onChange(boolean p0, Uri p1, int p2){} +} diff --git a/java/ql/test/stubs/android/android/database/Cursor.java b/java/ql/test/stubs/android/android/database/Cursor.java index cc432b1ad06..e692f9be738 100644 --- a/java/ql/test/stubs/android/android/database/Cursor.java +++ b/java/ql/test/stubs/android/android/database/Cursor.java @@ -1,5 +1,64 @@ +// Generated automatically from android.database.Cursor for testing purposes + package android.database; -public interface Cursor { +import android.content.ContentResolver; +import android.database.CharArrayBuffer; +import android.database.ContentObserver; +import android.database.DataSetObserver; +import android.net.Uri; +import android.os.Bundle; +import java.io.Closeable; +import java.util.List; +public interface Cursor extends Closeable +{ + Bundle getExtras(); + Bundle respond(Bundle p0); + String getColumnName(int p0); + String getString(int p0); + String[] getColumnNames(); + Uri getNotificationUri(); + boolean getWantsAllOnMoveCalls(); + boolean isAfterLast(); + boolean isBeforeFirst(); + boolean isClosed(); + boolean isFirst(); + boolean isLast(); + boolean isNull(int p0); + boolean move(int p0); + boolean moveToFirst(); + boolean moveToLast(); + boolean moveToNext(); + boolean moveToPosition(int p0); + boolean moveToPrevious(); + boolean requery(); + byte[] getBlob(int p0); + default List getNotificationUris(){ return null; } + default void setNotificationUris(ContentResolver p0, List p1){} + double getDouble(int p0); + float getFloat(int p0); + int getColumnCount(); + int getColumnIndex(String p0); + int getColumnIndexOrThrow(String p0); + int getCount(); + int getInt(int p0); + int getPosition(); + int getType(int p0); + long getLong(int p0); + short getShort(int p0); + static int FIELD_TYPE_BLOB = 0; + static int FIELD_TYPE_FLOAT = 0; + static int FIELD_TYPE_INTEGER = 0; + static int FIELD_TYPE_NULL = 0; + static int FIELD_TYPE_STRING = 0; + void close(); + void copyStringToBuffer(int p0, CharArrayBuffer p1); + void deactivate(); + void registerContentObserver(ContentObserver p0); + void registerDataSetObserver(DataSetObserver p0); + void setExtras(Bundle p0); + void setNotificationUri(ContentResolver p0, Uri p1); + void unregisterContentObserver(ContentObserver p0); + void unregisterDataSetObserver(DataSetObserver p0); } diff --git a/java/ql/test/stubs/android/android/database/DataSetObserver.java b/java/ql/test/stubs/android/android/database/DataSetObserver.java new file mode 100644 index 00000000000..6ca449b2e95 --- /dev/null +++ b/java/ql/test/stubs/android/android/database/DataSetObserver.java @@ -0,0 +1,11 @@ +// Generated automatically from android.database.DataSetObserver for testing purposes + +package android.database; + + +abstract public class DataSetObserver +{ + public DataSetObserver(){} + public void onChanged(){} + public void onInvalidated(){} +} diff --git a/java/ql/test/stubs/android/android/database/DatabaseErrorHandler.java b/java/ql/test/stubs/android/android/database/DatabaseErrorHandler.java new file mode 100644 index 00000000000..c52eb9698a7 --- /dev/null +++ b/java/ql/test/stubs/android/android/database/DatabaseErrorHandler.java @@ -0,0 +1,10 @@ +// Generated automatically from android.database.DatabaseErrorHandler for testing purposes + +package android.database; + +import android.database.sqlite.SQLiteDatabase; + +public interface DatabaseErrorHandler +{ + void onCorruption(SQLiteDatabase p0); +} diff --git a/java/ql/test/stubs/android/android/database/sqlite/SQLiteClosable.java b/java/ql/test/stubs/android/android/database/sqlite/SQLiteClosable.java new file mode 100644 index 00000000000..8295e8d0d61 --- /dev/null +++ b/java/ql/test/stubs/android/android/database/sqlite/SQLiteClosable.java @@ -0,0 +1,16 @@ +// Generated automatically from android.database.sqlite.SQLiteClosable for testing purposes + +package android.database.sqlite; + +import java.io.Closeable; + +abstract public class SQLiteClosable implements Closeable +{ + protected abstract void onAllReferencesReleased(); + protected void onAllReferencesReleasedFromContainer(){} + public SQLiteClosable(){} + public void acquireReference(){} + public void close(){} + public void releaseReference(){} + public void releaseReferenceFromContainer(){} +} diff --git a/java/ql/test/stubs/android/android/database/sqlite/SQLiteCursorDriver.java b/java/ql/test/stubs/android/android/database/sqlite/SQLiteCursorDriver.java new file mode 100644 index 00000000000..3d5cb99a2d0 --- /dev/null +++ b/java/ql/test/stubs/android/android/database/sqlite/SQLiteCursorDriver.java @@ -0,0 +1,15 @@ +// Generated automatically from android.database.sqlite.SQLiteCursorDriver for testing purposes + +package android.database.sqlite; + +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +public interface SQLiteCursorDriver +{ + Cursor query(SQLiteDatabase.CursorFactory p0, String[] p1); + void cursorClosed(); + void cursorDeactivated(); + void cursorRequeried(Cursor p0); + void setBindArguments(String[] p0); +} diff --git a/java/ql/test/stubs/android/android/database/sqlite/SQLiteDatabase.java b/java/ql/test/stubs/android/android/database/sqlite/SQLiteDatabase.java index 6f5b6bd2eea..64b62e68d44 100644 --- a/java/ql/test/stubs/android/android/database/sqlite/SQLiteDatabase.java +++ b/java/ql/test/stubs/android/android/database/sqlite/SQLiteDatabase.java @@ -1,56 +1,127 @@ +// Generated automatically from android.database.sqlite.SQLiteDatabase for testing purposes + package android.database.sqlite; import android.content.ContentValues; +import android.database.Cursor; +import android.database.DatabaseErrorHandler; +import android.database.sqlite.SQLiteClosable; +import android.database.sqlite.SQLiteCursorDriver; +import android.database.sqlite.SQLiteQuery; +import android.database.sqlite.SQLiteStatement; +import android.database.sqlite.SQLiteTransactionListener; import android.os.CancellationSignal; +import android.util.Pair; +import java.io.File; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.function.BinaryOperator; +import java.util.function.UnaryOperator; -public abstract class SQLiteDatabase { - public class CursorFactory { - - } - - public abstract void execPerConnectionSQL(String sql, Object[] bindArgs); - - public abstract void execSQL(String sql); - - public abstract void execSQL(String sql, Object[] bindArgs); - - public abstract void query(boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, - String groupBy, String having, String orderBy, String limit); - - public abstract void query(boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, - String groupBy, String having, String orderBy, String limit, CancellationSignal cancellationSignal); - - public abstract void query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, - String having, String orderBy, String limit); - - public abstract void query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, - String having, String orderBy); - - public abstract void queryWithFactory(SQLiteDatabase.CursorFactory cursorFactory, boolean distinct, String table, - String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, - String limit, CancellationSignal cancellationSignal); - - public abstract void queryWithFactory(SQLiteDatabase.CursorFactory cursorFactory, boolean distinct, String table, - String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, - String limit); - - public abstract void rawQuery(String sql, String[] selectionArgs, CancellationSignal cancellationSignal); - - public abstract void rawQuery(String sql, String[] selectionArgs); - - public abstract void rawQueryWithFactory(SQLiteDatabase.CursorFactory cursorFactory, String sql, String[] selectionArgs, - String editTable, CancellationSignal cancellationSignal); - - public abstract void rawQueryWithFactory(SQLiteDatabase.CursorFactory cursorFactory, String sql, String[] selectionArgs, - String editTable); - - public abstract void compileStatement(String sql); - - public abstract void delete(String table, String whereClause, String[] whereArgs); - - public abstract void update(String table, ContentValues values, String whereClause, String[] whereArgs); - - public abstract void updateWithOnConflict(String table, ContentValues values, String whereClause, String[] whereArgs, - int conflictAlgorithm); - +public class SQLiteDatabase extends SQLiteClosable +{ + protected SQLiteDatabase() {} + protected void finalize(){} + protected void onAllReferencesReleased(){} + public Cursor query(String p0, String[] p1, String p2, String[] p3, String p4, String p5, String p6){ return null; } + public Cursor query(String p0, String[] p1, String p2, String[] p3, String p4, String p5, String p6, String p7){ return null; } + public Cursor query(boolean p0, String p1, String[] p2, String p3, String[] p4, String p5, String p6, String p7, String p8){ return null; } + public Cursor query(boolean p0, String p1, String[] p2, String p3, String[] p4, String p5, String p6, String p7, String p8, CancellationSignal p9){ return null; } + public Cursor queryWithFactory(SQLiteDatabase.CursorFactory p0, boolean p1, String p2, String[] p3, String p4, String[] p5, String p6, String p7, String p8, String p9){ return null; } + public Cursor queryWithFactory(SQLiteDatabase.CursorFactory p0, boolean p1, String p2, String[] p3, String p4, String[] p5, String p6, String p7, String p8, String p9, CancellationSignal p10){ return null; } + public Cursor rawQuery(String p0, String[] p1){ return null; } + public Cursor rawQuery(String p0, String[] p1, CancellationSignal p2){ return null; } + public Cursor rawQueryWithFactory(SQLiteDatabase.CursorFactory p0, String p1, String[] p2, String p3){ return null; } + public Cursor rawQueryWithFactory(SQLiteDatabase.CursorFactory p0, String p1, String[] p2, String p3, CancellationSignal p4){ return null; } + public List> getAttachedDbs(){ return null; } + public Map getSyncedTables(){ return null; } + public SQLiteStatement compileStatement(String p0){ return null; } + public String getPath(){ return null; } + public String toString(){ return null; } + public boolean enableWriteAheadLogging(){ return false; } + public boolean inTransaction(){ return false; } + public boolean isDatabaseIntegrityOk(){ return false; } + public boolean isDbLockedByCurrentThread(){ return false; } + public boolean isDbLockedByOtherThreads(){ return false; } + public boolean isOpen(){ return false; } + public boolean isReadOnly(){ return false; } + public boolean isWriteAheadLoggingEnabled(){ return false; } + public boolean needUpgrade(int p0){ return false; } + public boolean yieldIfContended(){ return false; } + public boolean yieldIfContendedSafely(){ return false; } + public boolean yieldIfContendedSafely(long p0){ return false; } + public int delete(String p0, String p1, String[] p2){ return 0; } + public int getVersion(){ return 0; } + public int update(String p0, ContentValues p1, String p2, String[] p3){ return 0; } + public int updateWithOnConflict(String p0, ContentValues p1, String p2, String[] p3, int p4){ return 0; } + public long getMaximumSize(){ return 0; } + public long getPageSize(){ return 0; } + public long insert(String p0, String p1, ContentValues p2){ return 0; } + public long insertOrThrow(String p0, String p1, ContentValues p2){ return 0; } + public long insertWithOnConflict(String p0, String p1, ContentValues p2, int p3){ return 0; } + public long replace(String p0, String p1, ContentValues p2){ return 0; } + public long replaceOrThrow(String p0, String p1, ContentValues p2){ return 0; } + public long setMaximumSize(long p0){ return 0; } + public static SQLiteDatabase create(SQLiteDatabase.CursorFactory p0){ return null; } + public static SQLiteDatabase createInMemory(SQLiteDatabase.OpenParams p0){ return null; } + public static SQLiteDatabase openDatabase(File p0, SQLiteDatabase.OpenParams p1){ return null; } + public static SQLiteDatabase openDatabase(String p0, SQLiteDatabase.CursorFactory p1, int p2){ return null; } + public static SQLiteDatabase openDatabase(String p0, SQLiteDatabase.CursorFactory p1, int p2, DatabaseErrorHandler p3){ return null; } + public static SQLiteDatabase openOrCreateDatabase(File p0, SQLiteDatabase.CursorFactory p1){ return null; } + public static SQLiteDatabase openOrCreateDatabase(String p0, SQLiteDatabase.CursorFactory p1){ return null; } + public static SQLiteDatabase openOrCreateDatabase(String p0, SQLiteDatabase.CursorFactory p1, DatabaseErrorHandler p2){ return null; } + public static String findEditTable(String p0){ return null; } + public static boolean deleteDatabase(File p0){ return false; } + public static int CONFLICT_ABORT = 0; + public static int CONFLICT_FAIL = 0; + public static int CONFLICT_IGNORE = 0; + public static int CONFLICT_NONE = 0; + public static int CONFLICT_REPLACE = 0; + public static int CONFLICT_ROLLBACK = 0; + public static int CREATE_IF_NECESSARY = 0; + public static int ENABLE_WRITE_AHEAD_LOGGING = 0; + public static int MAX_SQL_CACHE_SIZE = 0; + public static int NO_LOCALIZED_COLLATORS = 0; + public static int OPEN_READONLY = 0; + public static int OPEN_READWRITE = 0; + public static int SQLITE_MAX_LIKE_PATTERN_LENGTH = 0; + public static int releaseMemory(){ return 0; } + public void beginTransaction(){} + public void beginTransactionNonExclusive(){} + public void beginTransactionWithListener(SQLiteTransactionListener p0){} + public void beginTransactionWithListenerNonExclusive(SQLiteTransactionListener p0){} + public void disableWriteAheadLogging(){} + public void endTransaction(){} + public void execPerConnectionSQL(String p0, Object[] p1){} + public void execSQL(String p0){} + public void execSQL(String p0, Object[] p1){} + public void markTableSyncable(String p0, String p1){} + public void markTableSyncable(String p0, String p1, String p2){} + public void setCustomAggregateFunction(String p0, BinaryOperator p1){} + public void setCustomScalarFunction(String p0, UnaryOperator p1){} + public void setForeignKeyConstraintsEnabled(boolean p0){} + public void setLocale(Locale p0){} + public void setLockingEnabled(boolean p0){} + public void setMaxSqlCacheSize(int p0){} + public void setPageSize(long p0){} + public void setTransactionSuccessful(){} + public void setVersion(int p0){} + public void validateSql(String p0, CancellationSignal p1){} + static public class OpenParams + { + protected OpenParams() {} + public DatabaseErrorHandler getErrorHandler(){ return null; } + public SQLiteDatabase.CursorFactory getCursorFactory(){ return null; } + public String getJournalMode(){ return null; } + public String getSynchronousMode(){ return null; } + public int getLookasideSlotCount(){ return 0; } + public int getLookasideSlotSize(){ return 0; } + public int getOpenFlags(){ return 0; } + public long getIdleConnectionTimeout(){ return 0; } + } + static public interface CursorFactory + { + Cursor newCursor(SQLiteDatabase p0, SQLiteCursorDriver p1, String p2, SQLiteQuery p3); + } } diff --git a/java/ql/test/stubs/android/android/database/sqlite/SQLiteProgram.java b/java/ql/test/stubs/android/android/database/sqlite/SQLiteProgram.java new file mode 100644 index 00000000000..15f078a4c16 --- /dev/null +++ b/java/ql/test/stubs/android/android/database/sqlite/SQLiteProgram.java @@ -0,0 +1,18 @@ +// Generated automatically from android.database.sqlite.SQLiteProgram for testing purposes + +package android.database.sqlite; + +import android.database.sqlite.SQLiteClosable; + +abstract public class SQLiteProgram extends SQLiteClosable +{ + protected void onAllReferencesReleased(){} + public final int getUniqueId(){ return 0; } + public void bindAllArgsAsStrings(String[] p0){} + public void bindBlob(int p0, byte[] p1){} + public void bindDouble(int p0, double p1){} + public void bindLong(int p0, long p1){} + public void bindNull(int p0){} + public void bindString(int p0, String p1){} + public void clearBindings(){} +} diff --git a/java/ql/test/stubs/android/android/database/sqlite/SQLiteQuery.java b/java/ql/test/stubs/android/android/database/sqlite/SQLiteQuery.java new file mode 100644 index 00000000000..a2169bcf238 --- /dev/null +++ b/java/ql/test/stubs/android/android/database/sqlite/SQLiteQuery.java @@ -0,0 +1,10 @@ +// Generated automatically from android.database.sqlite.SQLiteQuery for testing purposes + +package android.database.sqlite; + +import android.database.sqlite.SQLiteProgram; + +public class SQLiteQuery extends SQLiteProgram +{ + public String toString(){ return null; } +} diff --git a/java/ql/test/stubs/android/android/database/sqlite/SQLiteStatement.java b/java/ql/test/stubs/android/android/database/sqlite/SQLiteStatement.java new file mode 100644 index 00000000000..e96a0d28725 --- /dev/null +++ b/java/ql/test/stubs/android/android/database/sqlite/SQLiteStatement.java @@ -0,0 +1,17 @@ +// Generated automatically from android.database.sqlite.SQLiteStatement for testing purposes + +package android.database.sqlite; + +import android.database.sqlite.SQLiteProgram; +import android.os.ParcelFileDescriptor; + +public class SQLiteStatement extends SQLiteProgram +{ + public ParcelFileDescriptor simpleQueryForBlobFileDescriptor(){ return null; } + public String simpleQueryForString(){ return null; } + public String toString(){ return null; } + public int executeUpdateDelete(){ return 0; } + public long executeInsert(){ return 0; } + public long simpleQueryForLong(){ return 0; } + public void execute(){} +} diff --git a/java/ql/test/stubs/android/android/database/sqlite/SQLiteTransactionListener.java b/java/ql/test/stubs/android/android/database/sqlite/SQLiteTransactionListener.java new file mode 100644 index 00000000000..31895691b11 --- /dev/null +++ b/java/ql/test/stubs/android/android/database/sqlite/SQLiteTransactionListener.java @@ -0,0 +1,11 @@ +// Generated automatically from android.database.sqlite.SQLiteTransactionListener for testing purposes + +package android.database.sqlite; + + +public interface SQLiteTransactionListener +{ + void onBegin(); + void onCommit(); + void onRollback(); +} diff --git a/java/ql/test/stubs/android/android/graphics/Bitmap.java b/java/ql/test/stubs/android/android/graphics/Bitmap.java new file mode 100644 index 00000000000..cf7a3b69176 --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/Bitmap.java @@ -0,0 +1,97 @@ +// Generated automatically from android.graphics.Bitmap for testing purposes + +package android.graphics; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorSpace; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Picture; +import android.hardware.HardwareBuffer; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.DisplayMetrics; +import java.io.OutputStream; +import java.nio.Buffer; + +public class Bitmap implements Parcelable +{ + public Bitmap copy(Bitmap.Config p0, boolean p1){ return null; } + public Bitmap extractAlpha(){ return null; } + public Bitmap extractAlpha(Paint p0, int[] p1){ return null; } + public Bitmap.Config getConfig(){ return null; } + public Color getColor(int p0, int p1){ return null; } + public ColorSpace getColorSpace(){ return null; } + public boolean compress(Bitmap.CompressFormat p0, int p1, OutputStream p2){ return false; } + public boolean hasAlpha(){ return false; } + public boolean hasMipMap(){ return false; } + public boolean isMutable(){ return false; } + public boolean isPremultiplied(){ return false; } + public boolean isRecycled(){ return false; } + public boolean sameAs(Bitmap p0){ return false; } + public byte[] getNinePatchChunk(){ return null; } + public int describeContents(){ return 0; } + public int getAllocationByteCount(){ return 0; } + public int getByteCount(){ return 0; } + public int getDensity(){ return 0; } + public int getGenerationId(){ return 0; } + public int getHeight(){ return 0; } + public int getPixel(int p0, int p1){ return 0; } + public int getRowBytes(){ return 0; } + public int getScaledHeight(Canvas p0){ return 0; } + public int getScaledHeight(DisplayMetrics p0){ return 0; } + public int getScaledHeight(int p0){ return 0; } + public int getScaledWidth(Canvas p0){ return 0; } + public int getScaledWidth(DisplayMetrics p0){ return 0; } + public int getScaledWidth(int p0){ return 0; } + public int getWidth(){ return 0; } + public static Bitmap createBitmap(Bitmap p0){ return null; } + public static Bitmap createBitmap(Bitmap p0, int p1, int p2, int p3, int p4){ return null; } + public static Bitmap createBitmap(Bitmap p0, int p1, int p2, int p3, int p4, Matrix p5, boolean p6){ return null; } + public static Bitmap createBitmap(DisplayMetrics p0, int p1, int p2, Bitmap.Config p3){ return null; } + public static Bitmap createBitmap(DisplayMetrics p0, int p1, int p2, Bitmap.Config p3, boolean p4){ return null; } + public static Bitmap createBitmap(DisplayMetrics p0, int p1, int p2, Bitmap.Config p3, boolean p4, ColorSpace p5){ return null; } + public static Bitmap createBitmap(DisplayMetrics p0, int[] p1, int p2, int p3, Bitmap.Config p4){ return null; } + public static Bitmap createBitmap(DisplayMetrics p0, int[] p1, int p2, int p3, int p4, int p5, Bitmap.Config p6){ return null; } + public static Bitmap createBitmap(Picture p0){ return null; } + public static Bitmap createBitmap(Picture p0, int p1, int p2, Bitmap.Config p3){ return null; } + public static Bitmap createBitmap(int p0, int p1, Bitmap.Config p2){ return null; } + public static Bitmap createBitmap(int p0, int p1, Bitmap.Config p2, boolean p3){ return null; } + public static Bitmap createBitmap(int p0, int p1, Bitmap.Config p2, boolean p3, ColorSpace p4){ return null; } + public static Bitmap createBitmap(int[] p0, int p1, int p2, Bitmap.Config p3){ return null; } + public static Bitmap createBitmap(int[] p0, int p1, int p2, int p3, int p4, Bitmap.Config p5){ return null; } + public static Bitmap createScaledBitmap(Bitmap p0, int p1, int p2, boolean p3){ return null; } + public static Bitmap wrapHardwareBuffer(HardwareBuffer p0, ColorSpace p1){ return null; } + public static Parcelable.Creator CREATOR = null; + public static int DENSITY_NONE = 0; + public void copyPixelsFromBuffer(Buffer p0){} + public void copyPixelsToBuffer(Buffer p0){} + public void eraseColor(int p0){} + public void eraseColor(long p0){} + public void getPixels(int[] p0, int p1, int p2, int p3, int p4, int p5, int p6){} + public void prepareToDraw(){} + public void reconfigure(int p0, int p1, Bitmap.Config p2){} + public void recycle(){} + public void setColorSpace(ColorSpace p0){} + public void setConfig(Bitmap.Config p0){} + public void setDensity(int p0){} + public void setHasAlpha(boolean p0){} + public void setHasMipMap(boolean p0){} + public void setHeight(int p0){} + public void setPixel(int p0, int p1, int p2){} + public void setPixels(int[] p0, int p1, int p2, int p3, int p4, int p5, int p6){} + public void setPremultiplied(boolean p0){} + public void setWidth(int p0){} + public void writeToParcel(Parcel p0, int p1){} + static public enum CompressFormat + { + JPEG, PNG, WEBP, WEBP_LOSSLESS, WEBP_LOSSY; + private CompressFormat() {} + } + static public enum Config + { + ALPHA_8, ARGB_4444, ARGB_8888, HARDWARE, RGBA_F16, RGB_565; + private Config() {} + } +} diff --git a/java/ql/test/stubs/android/android/graphics/BitmapFactory.java b/java/ql/test/stubs/android/android/graphics/BitmapFactory.java new file mode 100644 index 00000000000..f6e05c10de9 --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/BitmapFactory.java @@ -0,0 +1,54 @@ +// Generated automatically from android.graphics.BitmapFactory for testing purposes + +package android.graphics; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.ColorSpace; +import android.graphics.Rect; +import android.util.TypedValue; +import java.io.FileDescriptor; +import java.io.InputStream; + +public class BitmapFactory +{ + public BitmapFactory(){} + public static Bitmap decodeByteArray(byte[] p0, int p1, int p2){ return null; } + public static Bitmap decodeByteArray(byte[] p0, int p1, int p2, BitmapFactory.Options p3){ return null; } + public static Bitmap decodeFile(String p0){ return null; } + public static Bitmap decodeFile(String p0, BitmapFactory.Options p1){ return null; } + public static Bitmap decodeFileDescriptor(FileDescriptor p0){ return null; } + public static Bitmap decodeFileDescriptor(FileDescriptor p0, Rect p1, BitmapFactory.Options p2){ return null; } + public static Bitmap decodeResource(Resources p0, int p1){ return null; } + public static Bitmap decodeResource(Resources p0, int p1, BitmapFactory.Options p2){ return null; } + public static Bitmap decodeResourceStream(Resources p0, TypedValue p1, InputStream p2, Rect p3, BitmapFactory.Options p4){ return null; } + public static Bitmap decodeStream(InputStream p0){ return null; } + public static Bitmap decodeStream(InputStream p0, Rect p1, BitmapFactory.Options p2){ return null; } + static public class Options + { + public Bitmap inBitmap = null; + public Bitmap.Config inPreferredConfig = null; + public Bitmap.Config outConfig = null; + public ColorSpace inPreferredColorSpace = null; + public ColorSpace outColorSpace = null; + public Options(){} + public String outMimeType = null; + public boolean inDither = false; + public boolean inInputShareable = false; + public boolean inJustDecodeBounds = false; + public boolean inMutable = false; + public boolean inPreferQualityOverSpeed = false; + public boolean inPremultiplied = false; + public boolean inPurgeable = false; + public boolean inScaled = false; + public boolean mCancel = false; + public byte[] inTempStorage = null; + public int inDensity = 0; + public int inSampleSize = 0; + public int inScreenDensity = 0; + public int inTargetDensity = 0; + public int outHeight = 0; + public int outWidth = 0; + public void requestCancelDecode(){} + } +} diff --git a/java/ql/test/stubs/android/android/graphics/BlendMode.java b/java/ql/test/stubs/android/android/graphics/BlendMode.java new file mode 100644 index 00000000000..32510e36bdd --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/BlendMode.java @@ -0,0 +1,10 @@ +// Generated automatically from android.graphics.BlendMode for testing purposes + +package android.graphics; + + +public enum BlendMode +{ + CLEAR, COLOR, COLOR_BURN, COLOR_DODGE, DARKEN, DIFFERENCE, DST, DST_ATOP, DST_IN, DST_OUT, DST_OVER, EXCLUSION, HARD_LIGHT, HUE, LIGHTEN, LUMINOSITY, MODULATE, MULTIPLY, OVERLAY, PLUS, SATURATION, SCREEN, SOFT_LIGHT, SRC, SRC_ATOP, SRC_IN, SRC_OUT, SRC_OVER, XOR; + private BlendMode() {} +} diff --git a/java/ql/test/stubs/android/android/graphics/Canvas.java b/java/ql/test/stubs/android/android/graphics/Canvas.java new file mode 100644 index 00000000000..26c4536a2aa --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/Canvas.java @@ -0,0 +1,141 @@ +// Generated automatically from android.graphics.Canvas for testing purposes + +package android.graphics; + +import android.graphics.Bitmap; +import android.graphics.BlendMode; +import android.graphics.DrawFilter; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Picture; +import android.graphics.PorterDuff; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Region; +import android.graphics.RenderNode; +import android.graphics.text.MeasuredText; + +public class Canvas +{ + public Canvas(){} + public Canvas(Bitmap p0){} + public DrawFilter getDrawFilter(){ return null; } + public boolean clipOutPath(Path p0){ return false; } + public boolean clipOutRect(Rect p0){ return false; } + public boolean clipOutRect(RectF p0){ return false; } + public boolean clipOutRect(float p0, float p1, float p2, float p3){ return false; } + public boolean clipOutRect(int p0, int p1, int p2, int p3){ return false; } + public boolean clipPath(Path p0){ return false; } + public boolean clipPath(Path p0, Region.Op p1){ return false; } + public boolean clipRect(Rect p0){ return false; } + public boolean clipRect(Rect p0, Region.Op p1){ return false; } + public boolean clipRect(RectF p0){ return false; } + public boolean clipRect(RectF p0, Region.Op p1){ return false; } + public boolean clipRect(float p0, float p1, float p2, float p3){ return false; } + public boolean clipRect(float p0, float p1, float p2, float p3, Region.Op p4){ return false; } + public boolean clipRect(int p0, int p1, int p2, int p3){ return false; } + public boolean getClipBounds(Rect p0){ return false; } + public boolean isHardwareAccelerated(){ return false; } + public boolean isOpaque(){ return false; } + public boolean quickReject(Path p0){ return false; } + public boolean quickReject(Path p0, Canvas.EdgeType p1){ return false; } + public boolean quickReject(RectF p0){ return false; } + public boolean quickReject(RectF p0, Canvas.EdgeType p1){ return false; } + public boolean quickReject(float p0, float p1, float p2, float p3){ return false; } + public boolean quickReject(float p0, float p1, float p2, float p3, Canvas.EdgeType p4){ return false; } + public final Matrix getMatrix(){ return null; } + public final Rect getClipBounds(){ return null; } + public final void rotate(float p0, float p1, float p2){} + public final void scale(float p0, float p1, float p2, float p3){} + public int getDensity(){ return 0; } + public int getHeight(){ return 0; } + public int getMaximumBitmapHeight(){ return 0; } + public int getMaximumBitmapWidth(){ return 0; } + public int getSaveCount(){ return 0; } + public int getWidth(){ return 0; } + public int save(){ return 0; } + public int saveLayer(RectF p0, Paint p1){ return 0; } + public int saveLayer(RectF p0, Paint p1, int p2){ return 0; } + public int saveLayer(float p0, float p1, float p2, float p3, Paint p4){ return 0; } + public int saveLayer(float p0, float p1, float p2, float p3, Paint p4, int p5){ return 0; } + public int saveLayerAlpha(RectF p0, int p1){ return 0; } + public int saveLayerAlpha(RectF p0, int p1, int p2){ return 0; } + public int saveLayerAlpha(float p0, float p1, float p2, float p3, int p4){ return 0; } + public int saveLayerAlpha(float p0, float p1, float p2, float p3, int p4, int p5){ return 0; } + public static int ALL_SAVE_FLAG = 0; + public void concat(Matrix p0){} + public void disableZ(){} + public void drawARGB(int p0, int p1, int p2, int p3){} + public void drawArc(RectF p0, float p1, float p2, boolean p3, Paint p4){} + public void drawArc(float p0, float p1, float p2, float p3, float p4, float p5, boolean p6, Paint p7){} + public void drawBitmap(Bitmap p0, Matrix p1, Paint p2){} + public void drawBitmap(Bitmap p0, Rect p1, Rect p2, Paint p3){} + public void drawBitmap(Bitmap p0, Rect p1, RectF p2, Paint p3){} + public void drawBitmap(Bitmap p0, float p1, float p2, Paint p3){} + public void drawBitmap(int[] p0, int p1, int p2, float p3, float p4, int p5, int p6, boolean p7, Paint p8){} + public void drawBitmap(int[] p0, int p1, int p2, int p3, int p4, int p5, int p6, boolean p7, Paint p8){} + public void drawBitmapMesh(Bitmap p0, int p1, int p2, float[] p3, int p4, int[] p5, int p6, Paint p7){} + public void drawCircle(float p0, float p1, float p2, Paint p3){} + public void drawColor(int p0){} + public void drawColor(int p0, BlendMode p1){} + public void drawColor(int p0, PorterDuff.Mode p1){} + public void drawColor(long p0){} + public void drawColor(long p0, BlendMode p1){} + public void drawDoubleRoundRect(RectF p0, float p1, float p2, RectF p3, float p4, float p5, Paint p6){} + public void drawDoubleRoundRect(RectF p0, float[] p1, RectF p2, float[] p3, Paint p4){} + public void drawLine(float p0, float p1, float p2, float p3, Paint p4){} + public void drawLines(float[] p0, Paint p1){} + public void drawLines(float[] p0, int p1, int p2, Paint p3){} + public void drawOval(RectF p0, Paint p1){} + public void drawOval(float p0, float p1, float p2, float p3, Paint p4){} + public void drawPaint(Paint p0){} + public void drawPath(Path p0, Paint p1){} + public void drawPicture(Picture p0){} + public void drawPicture(Picture p0, Rect p1){} + public void drawPicture(Picture p0, RectF p1){} + public void drawPoint(float p0, float p1, Paint p2){} + public void drawPoints(float[] p0, Paint p1){} + public void drawPoints(float[] p0, int p1, int p2, Paint p3){} + public void drawPosText(String p0, float[] p1, Paint p2){} + public void drawPosText(char[] p0, int p1, int p2, float[] p3, Paint p4){} + public void drawRGB(int p0, int p1, int p2){} + public void drawRect(Rect p0, Paint p1){} + public void drawRect(RectF p0, Paint p1){} + public void drawRect(float p0, float p1, float p2, float p3, Paint p4){} + public void drawRenderNode(RenderNode p0){} + public void drawRoundRect(RectF p0, float p1, float p2, Paint p3){} + public void drawRoundRect(float p0, float p1, float p2, float p3, float p4, float p5, Paint p6){} + public void drawText(CharSequence p0, int p1, int p2, float p3, float p4, Paint p5){} + public void drawText(String p0, float p1, float p2, Paint p3){} + public void drawText(String p0, int p1, int p2, float p3, float p4, Paint p5){} + public void drawText(char[] p0, int p1, int p2, float p3, float p4, Paint p5){} + public void drawTextOnPath(String p0, Path p1, float p2, float p3, Paint p4){} + public void drawTextOnPath(char[] p0, int p1, int p2, Path p3, float p4, float p5, Paint p6){} + public void drawTextRun(CharSequence p0, int p1, int p2, int p3, int p4, float p5, float p6, boolean p7, Paint p8){} + public void drawTextRun(MeasuredText p0, int p1, int p2, int p3, int p4, float p5, float p6, boolean p7, Paint p8){} + public void drawTextRun(char[] p0, int p1, int p2, int p3, int p4, float p5, float p6, boolean p7, Paint p8){} + public void drawVertices(Canvas.VertexMode p0, int p1, float[] p2, int p3, float[] p4, int p5, int[] p6, int p7, short[] p8, int p9, int p10, Paint p11){} + public void enableZ(){} + public void getMatrix(Matrix p0){} + public void restore(){} + public void restoreToCount(int p0){} + public void rotate(float p0){} + public void scale(float p0, float p1){} + public void setBitmap(Bitmap p0){} + public void setDensity(int p0){} + public void setDrawFilter(DrawFilter p0){} + public void setMatrix(Matrix p0){} + public void skew(float p0, float p1){} + public void translate(float p0, float p1){} + static public enum EdgeType + { + AA, BW; + private EdgeType() {} + } + static public enum VertexMode + { + TRIANGLES, TRIANGLE_FAN, TRIANGLE_STRIP; + private VertexMode() {} + } +} diff --git a/java/ql/test/stubs/android/android/graphics/Color.java b/java/ql/test/stubs/android/android/graphics/Color.java new file mode 100644 index 00000000000..45f1e68548c --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/Color.java @@ -0,0 +1,80 @@ +// Generated automatically from android.graphics.Color for testing purposes + +package android.graphics; + +import android.graphics.ColorSpace; + +public class Color +{ + public Color convert(ColorSpace p0){ return null; } + public Color(){} + public ColorSpace getColorSpace(){ return null; } + public ColorSpace.Model getModel(){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public boolean isSrgb(){ return false; } + public boolean isWideGamut(){ return false; } + public float alpha(){ return 0; } + public float blue(){ return 0; } + public float getComponent(int p0){ return 0; } + public float green(){ return 0; } + public float luminance(){ return 0; } + public float red(){ return 0; } + public float[] getComponents(){ return null; } + public float[] getComponents(float[] p0){ return null; } + public int getComponentCount(){ return 0; } + public int hashCode(){ return 0; } + public int toArgb(){ return 0; } + public long pack(){ return 0; } + public static Color valueOf(float p0, float p1, float p2){ return null; } + public static Color valueOf(float p0, float p1, float p2, float p3){ return null; } + public static Color valueOf(float p0, float p1, float p2, float p3, ColorSpace p4){ return null; } + public static Color valueOf(float[] p0, ColorSpace p1){ return null; } + public static Color valueOf(int p0){ return null; } + public static Color valueOf(long p0){ return null; } + public static ColorSpace colorSpace(long p0){ return null; } + public static boolean isInColorSpace(long p0, ColorSpace p1){ return false; } + public static boolean isSrgb(long p0){ return false; } + public static boolean isWideGamut(long p0){ return false; } + public static float alpha(long p0){ return 0; } + public static float blue(long p0){ return 0; } + public static float green(long p0){ return 0; } + public static float luminance(int p0){ return 0; } + public static float luminance(long p0){ return 0; } + public static float red(long p0){ return 0; } + public static int BLACK = 0; + public static int BLUE = 0; + public static int CYAN = 0; + public static int DKGRAY = 0; + public static int GRAY = 0; + public static int GREEN = 0; + public static int HSVToColor(float[] p0){ return 0; } + public static int HSVToColor(int p0, float[] p1){ return 0; } + public static int LTGRAY = 0; + public static int MAGENTA = 0; + public static int RED = 0; + public static int TRANSPARENT = 0; + public static int WHITE = 0; + public static int YELLOW = 0; + public static int alpha(int p0){ return 0; } + public static int argb(float p0, float p1, float p2, float p3){ return 0; } + public static int argb(int p0, int p1, int p2, int p3){ return 0; } + public static int blue(int p0){ return 0; } + public static int green(int p0){ return 0; } + public static int parseColor(String p0){ return 0; } + public static int red(int p0){ return 0; } + public static int rgb(float p0, float p1, float p2){ return 0; } + public static int rgb(int p0, int p1, int p2){ return 0; } + public static int toArgb(long p0){ return 0; } + public static long convert(float p0, float p1, float p2, float p3, ColorSpace p4, ColorSpace p5){ return 0; } + public static long convert(float p0, float p1, float p2, float p3, ColorSpace.Connector p4){ return 0; } + public static long convert(int p0, ColorSpace p1){ return 0; } + public static long convert(long p0, ColorSpace p1){ return 0; } + public static long convert(long p0, ColorSpace.Connector p1){ return 0; } + public static long pack(float p0, float p1, float p2){ return 0; } + public static long pack(float p0, float p1, float p2, float p3){ return 0; } + public static long pack(float p0, float p1, float p2, float p3, ColorSpace p4){ return 0; } + public static long pack(int p0){ return 0; } + public static void RGBToHSV(int p0, int p1, int p2, float[] p3){} + public static void colorToHSV(int p0, float[] p1){} +} diff --git a/java/ql/test/stubs/android/android/graphics/ColorFilter.java b/java/ql/test/stubs/android/android/graphics/ColorFilter.java new file mode 100644 index 00000000000..06a565743de --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/ColorFilter.java @@ -0,0 +1,9 @@ +// Generated automatically from android.graphics.ColorFilter for testing purposes + +package android.graphics; + + +public class ColorFilter +{ + public ColorFilter(){} +} diff --git a/java/ql/test/stubs/android/android/graphics/ColorSpace.java b/java/ql/test/stubs/android/android/graphics/ColorSpace.java new file mode 100644 index 00000000000..80c1ebcbbe4 --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/ColorSpace.java @@ -0,0 +1,120 @@ +// Generated automatically from android.graphics.ColorSpace for testing purposes + +package android.graphics; + +import java.util.function.DoubleUnaryOperator; + +abstract public class ColorSpace +{ + public ColorSpace.Model getModel(){ return null; } + public String getName(){ return null; } + public String toString(){ return null; } + public abstract boolean isWideGamut(); + public abstract float getMaxValue(int p0); + public abstract float getMinValue(int p0); + public abstract float[] fromXyz(float[] p0); + public abstract float[] toXyz(float[] p0); + public boolean equals(Object p0){ return false; } + public boolean isSrgb(){ return false; } + public float[] fromXyz(float p0, float p1, float p2){ return null; } + public float[] toXyz(float p0, float p1, float p2){ return null; } + public int getComponentCount(){ return 0; } + public int getId(){ return 0; } + public int hashCode(){ return 0; } + public static ColorSpace adapt(ColorSpace p0, float[] p1){ return null; } + public static ColorSpace adapt(ColorSpace p0, float[] p1, ColorSpace.Adaptation p2){ return null; } + public static ColorSpace get(ColorSpace.Named p0){ return null; } + public static ColorSpace match(float[] p0, ColorSpace.Rgb.TransferParameters p1){ return null; } + public static ColorSpace.Connector connect(ColorSpace p0){ return null; } + public static ColorSpace.Connector connect(ColorSpace p0, ColorSpace p1){ return null; } + public static ColorSpace.Connector connect(ColorSpace p0, ColorSpace p1, ColorSpace.RenderIntent p2){ return null; } + public static ColorSpace.Connector connect(ColorSpace p0, ColorSpace.RenderIntent p1){ return null; } + public static float[] ILLUMINANT_A = null; + public static float[] ILLUMINANT_B = null; + public static float[] ILLUMINANT_C = null; + public static float[] ILLUMINANT_D50 = null; + public static float[] ILLUMINANT_D55 = null; + public static float[] ILLUMINANT_D60 = null; + public static float[] ILLUMINANT_D65 = null; + public static float[] ILLUMINANT_D75 = null; + public static float[] ILLUMINANT_E = null; + public static int MAX_ID = 0; + public static int MIN_ID = 0; + static public class Connector + { + public ColorSpace getDestination(){ return null; } + public ColorSpace getSource(){ return null; } + public ColorSpace.RenderIntent getRenderIntent(){ return null; } + public float[] transform(float p0, float p1, float p2){ return null; } + public float[] transform(float[] p0){ return null; } + } + static public class Rgb extends ColorSpace + { + protected Rgb() {} + public ColorSpace.Rgb.TransferParameters getTransferParameters(){ return null; } + public DoubleUnaryOperator getEotf(){ return null; } + public DoubleUnaryOperator getOetf(){ return null; } + public Rgb(String p0, float[] p1, ColorSpace.Rgb.TransferParameters p2){} + public Rgb(String p0, float[] p1, DoubleUnaryOperator p2, DoubleUnaryOperator p3){} + public Rgb(String p0, float[] p1, double p2){} + public Rgb(String p0, float[] p1, float[] p2, ColorSpace.Rgb.TransferParameters p3){} + public Rgb(String p0, float[] p1, float[] p2, DoubleUnaryOperator p3, DoubleUnaryOperator p4, float p5, float p6){} + public Rgb(String p0, float[] p1, float[] p2, double p3){} + public boolean equals(Object p0){ return false; } + public boolean isSrgb(){ return false; } + public boolean isWideGamut(){ return false; } + public float getMaxValue(int p0){ return 0; } + public float getMinValue(int p0){ return 0; } + public float[] fromLinear(float p0, float p1, float p2){ return null; } + public float[] fromLinear(float[] p0){ return null; } + public float[] fromXyz(float[] p0){ return null; } + public float[] getInverseTransform(){ return null; } + public float[] getInverseTransform(float[] p0){ return null; } + public float[] getPrimaries(){ return null; } + public float[] getPrimaries(float[] p0){ return null; } + public float[] getTransform(){ return null; } + public float[] getTransform(float[] p0){ return null; } + public float[] getWhitePoint(){ return null; } + public float[] getWhitePoint(float[] p0){ return null; } + public float[] toLinear(float p0, float p1, float p2){ return null; } + public float[] toLinear(float[] p0){ return null; } + public float[] toXyz(float[] p0){ return null; } + public int hashCode(){ return 0; } + static public class TransferParameters + { + protected TransferParameters() {} + public TransferParameters(double p0, double p1, double p2, double p3, double p4){} + public TransferParameters(double p0, double p1, double p2, double p3, double p4, double p5, double p6){} + public boolean equals(Object p0){ return false; } + public final double a = 0; + public final double b = 0; + public final double c = 0; + public final double d = 0; + public final double e = 0; + public final double f = 0; + public final double g = 0; + public int hashCode(){ return 0; } + } + } + static public enum Adaptation + { + BRADFORD, CIECAT02, VON_KRIES; + private Adaptation() {} + } + static public enum Model + { + CMYK, LAB, RGB, XYZ; + private Model() {} + public int getComponentCount(){ return 0; } + } + static public enum Named + { + ACES, ACESCG, ADOBE_RGB, BT2020, BT709, CIE_LAB, CIE_XYZ, DCI_P3, DISPLAY_P3, EXTENDED_SRGB, LINEAR_EXTENDED_SRGB, LINEAR_SRGB, NTSC_1953, PRO_PHOTO_RGB, SMPTE_C, SRGB; + private Named() {} + } + static public enum RenderIntent + { + ABSOLUTE, PERCEPTUAL, RELATIVE, SATURATION; + private RenderIntent() {} + } +} diff --git a/java/ql/test/stubs/android/android/graphics/DrawFilter.java b/java/ql/test/stubs/android/android/graphics/DrawFilter.java new file mode 100644 index 00000000000..ed760270a81 --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/DrawFilter.java @@ -0,0 +1,10 @@ +// Generated automatically from android.graphics.DrawFilter for testing purposes + +package android.graphics; + + +public class DrawFilter +{ + protected void finalize(){} + public DrawFilter(){} +} diff --git a/java/ql/test/stubs/android/android/graphics/Insets.java b/java/ql/test/stubs/android/android/graphics/Insets.java new file mode 100644 index 00000000000..3e61e03cd68 --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/Insets.java @@ -0,0 +1,29 @@ +// Generated automatically from android.graphics.Insets for testing purposes + +package android.graphics; + +import android.graphics.Rect; +import android.os.Parcel; +import android.os.Parcelable; + +public class Insets implements Parcelable +{ + protected Insets() {} + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public final int bottom = 0; + public final int left = 0; + public final int right = 0; + public final int top = 0; + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Insets NONE = null; + public static Insets add(Insets p0, Insets p1){ return null; } + public static Insets max(Insets p0, Insets p1){ return null; } + public static Insets min(Insets p0, Insets p1){ return null; } + public static Insets of(Rect p0){ return null; } + public static Insets of(int p0, int p1, int p2, int p3){ return null; } + public static Insets subtract(Insets p0, Insets p1){ return null; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/graphics/MaskFilter.java b/java/ql/test/stubs/android/android/graphics/MaskFilter.java new file mode 100644 index 00000000000..0355b5c1206 --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/MaskFilter.java @@ -0,0 +1,10 @@ +// Generated automatically from android.graphics.MaskFilter for testing purposes + +package android.graphics; + + +public class MaskFilter +{ + protected void finalize(){} + public MaskFilter(){} +} diff --git a/java/ql/test/stubs/android/android/graphics/Matrix.java b/java/ql/test/stubs/android/android/graphics/Matrix.java new file mode 100644 index 00000000000..fc003bac004 --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/Matrix.java @@ -0,0 +1,74 @@ +// Generated automatically from android.graphics.Matrix for testing purposes + +package android.graphics; + +import android.graphics.RectF; + +public class Matrix +{ + public Matrix(){} + public Matrix(Matrix p0){} + public String toShortString(){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public boolean invert(Matrix p0){ return false; } + public boolean isAffine(){ return false; } + public boolean isIdentity(){ return false; } + public boolean mapRect(RectF p0){ return false; } + public boolean mapRect(RectF p0, RectF p1){ return false; } + public boolean postConcat(Matrix p0){ return false; } + public boolean postRotate(float p0){ return false; } + public boolean postRotate(float p0, float p1, float p2){ return false; } + public boolean postScale(float p0, float p1){ return false; } + public boolean postScale(float p0, float p1, float p2, float p3){ return false; } + public boolean postSkew(float p0, float p1){ return false; } + public boolean postSkew(float p0, float p1, float p2, float p3){ return false; } + public boolean postTranslate(float p0, float p1){ return false; } + public boolean preConcat(Matrix p0){ return false; } + public boolean preRotate(float p0){ return false; } + public boolean preRotate(float p0, float p1, float p2){ return false; } + public boolean preScale(float p0, float p1){ return false; } + public boolean preScale(float p0, float p1, float p2, float p3){ return false; } + public boolean preSkew(float p0, float p1){ return false; } + public boolean preSkew(float p0, float p1, float p2, float p3){ return false; } + public boolean preTranslate(float p0, float p1){ return false; } + public boolean rectStaysRect(){ return false; } + public boolean setConcat(Matrix p0, Matrix p1){ return false; } + public boolean setPolyToPoly(float[] p0, int p1, float[] p2, int p3, int p4){ return false; } + public boolean setRectToRect(RectF p0, RectF p1, Matrix.ScaleToFit p2){ return false; } + public float mapRadius(float p0){ return 0; } + public int hashCode(){ return 0; } + public static int MPERSP_0 = 0; + public static int MPERSP_1 = 0; + public static int MPERSP_2 = 0; + public static int MSCALE_X = 0; + public static int MSCALE_Y = 0; + public static int MSKEW_X = 0; + public static int MSKEW_Y = 0; + public static int MTRANS_X = 0; + public static int MTRANS_Y = 0; + public void getValues(float[] p0){} + public void mapPoints(float[] p0){} + public void mapPoints(float[] p0, float[] p1){} + public void mapPoints(float[] p0, int p1, float[] p2, int p3, int p4){} + public void mapVectors(float[] p0){} + public void mapVectors(float[] p0, float[] p1){} + public void mapVectors(float[] p0, int p1, float[] p2, int p3, int p4){} + public void reset(){} + public void set(Matrix p0){} + public void setRotate(float p0){} + public void setRotate(float p0, float p1, float p2){} + public void setScale(float p0, float p1){} + public void setScale(float p0, float p1, float p2, float p3){} + public void setSinCos(float p0, float p1){} + public void setSinCos(float p0, float p1, float p2, float p3){} + public void setSkew(float p0, float p1){} + public void setSkew(float p0, float p1, float p2, float p3){} + public void setTranslate(float p0, float p1){} + public void setValues(float[] p0){} + static public enum ScaleToFit + { + CENTER, END, FILL, START; + private ScaleToFit() {} + } +} diff --git a/java/ql/test/stubs/android/android/graphics/Movie.java b/java/ql/test/stubs/android/android/graphics/Movie.java new file mode 100644 index 00000000000..dd531ba7e04 --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/Movie.java @@ -0,0 +1,23 @@ +// Generated automatically from android.graphics.Movie for testing purposes + +package android.graphics; + +import android.graphics.Canvas; +import android.graphics.Paint; +import java.io.InputStream; + +public class Movie +{ + protected Movie() {} + protected void finalize(){} + public boolean isOpaque(){ return false; } + public boolean setTime(int p0){ return false; } + public int duration(){ return 0; } + public int height(){ return 0; } + public int width(){ return 0; } + public static Movie decodeByteArray(byte[] p0, int p1, int p2){ return null; } + public static Movie decodeFile(String p0){ return null; } + public static Movie decodeStream(InputStream p0){ return null; } + public void draw(Canvas p0, float p1, float p2){} + public void draw(Canvas p0, float p1, float p2, Paint p3){} +} diff --git a/java/ql/test/stubs/android/android/graphics/NinePatch.java b/java/ql/test/stubs/android/android/graphics/NinePatch.java new file mode 100644 index 00000000000..0fa7bc2eb69 --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/NinePatch.java @@ -0,0 +1,31 @@ +// Generated automatically from android.graphics.NinePatch for testing purposes + +package android.graphics; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Region; + +public class NinePatch +{ + protected NinePatch() {} + protected void finalize(){} + public Bitmap getBitmap(){ return null; } + public NinePatch(Bitmap p0, byte[] p1){} + public NinePatch(Bitmap p0, byte[] p1, String p2){} + public Paint getPaint(){ return null; } + public String getName(){ return null; } + public final Region getTransparentRegion(Rect p0){ return null; } + public final boolean hasAlpha(){ return false; } + public int getDensity(){ return 0; } + public int getHeight(){ return 0; } + public int getWidth(){ return 0; } + public static boolean isNinePatchChunk(byte[] p0){ return false; } + public void draw(Canvas p0, Rect p1){} + public void draw(Canvas p0, Rect p1, Paint p2){} + public void draw(Canvas p0, RectF p1){} + public void setPaint(Paint p0){} +} diff --git a/java/ql/test/stubs/android/android/graphics/Outline.java b/java/ql/test/stubs/android/android/graphics/Outline.java new file mode 100644 index 00000000000..4367be91cdd --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/Outline.java @@ -0,0 +1,29 @@ +// Generated automatically from android.graphics.Outline for testing purposes + +package android.graphics; + +import android.graphics.Path; +import android.graphics.Rect; + +public class Outline +{ + public Outline(){} + public Outline(Outline p0){} + public boolean canClip(){ return false; } + public boolean getRect(Rect p0){ return false; } + public boolean isEmpty(){ return false; } + public float getAlpha(){ return 0; } + public float getRadius(){ return 0; } + public void offset(int p0, int p1){} + public void set(Outline p0){} + public void setAlpha(float p0){} + public void setConvexPath(Path p0){} + public void setEmpty(){} + public void setOval(Rect p0){} + public void setOval(int p0, int p1, int p2, int p3){} + public void setPath(Path p0){} + public void setRect(Rect p0){} + public void setRect(int p0, int p1, int p2, int p3){} + public void setRoundRect(Rect p0, float p1){} + public void setRoundRect(int p0, int p1, int p2, int p3, float p4){} +} diff --git a/java/ql/test/stubs/android/android/graphics/Paint.java b/java/ql/test/stubs/android/android/graphics/Paint.java new file mode 100644 index 00000000000..810aecc3617 --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/Paint.java @@ -0,0 +1,212 @@ +// Generated automatically from android.graphics.Paint for testing purposes + +package android.graphics; + +import android.graphics.BlendMode; +import android.graphics.ColorFilter; +import android.graphics.MaskFilter; +import android.graphics.Path; +import android.graphics.PathEffect; +import android.graphics.Rect; +import android.graphics.Shader; +import android.graphics.Typeface; +import android.graphics.Xfermode; +import android.os.LocaleList; +import java.util.Locale; + +public class Paint +{ + public BlendMode getBlendMode(){ return null; } + public ColorFilter getColorFilter(){ return null; } + public ColorFilter setColorFilter(ColorFilter p0){ return null; } + public Locale getTextLocale(){ return null; } + public LocaleList getTextLocales(){ return null; } + public MaskFilter getMaskFilter(){ return null; } + public MaskFilter setMaskFilter(MaskFilter p0){ return null; } + public Paint(){} + public Paint(Paint p0){} + public Paint(int p0){} + public Paint.Align getTextAlign(){ return null; } + public Paint.Cap getStrokeCap(){ return null; } + public Paint.FontMetrics getFontMetrics(){ return null; } + public Paint.FontMetricsInt getFontMetricsInt(){ return null; } + public Paint.Join getStrokeJoin(){ return null; } + public Paint.Style getStyle(){ return null; } + public PathEffect getPathEffect(){ return null; } + public PathEffect setPathEffect(PathEffect p0){ return null; } + public Shader getShader(){ return null; } + public Shader setShader(Shader p0){ return null; } + public String getFontFeatureSettings(){ return null; } + public String getFontVariationSettings(){ return null; } + public Typeface getTypeface(){ return null; } + public Typeface setTypeface(Typeface p0){ return null; } + public Xfermode getXfermode(){ return null; } + public Xfermode setXfermode(Xfermode p0){ return null; } + public boolean equalsForTextMeasurement(Paint p0){ return false; } + public boolean getFillPath(Path p0, Path p1){ return false; } + public boolean hasGlyph(String p0){ return false; } + public boolean isElegantTextHeight(){ return false; } + public boolean setFontVariationSettings(String p0){ return false; } + public final boolean isAntiAlias(){ return false; } + public final boolean isDither(){ return false; } + public final boolean isFakeBoldText(){ return false; } + public final boolean isFilterBitmap(){ return false; } + public final boolean isLinearText(){ return false; } + public final boolean isStrikeThruText(){ return false; } + public final boolean isSubpixelText(){ return false; } + public final boolean isUnderlineText(){ return false; } + public float ascent(){ return 0; } + public float descent(){ return 0; } + public float getFontMetrics(Paint.FontMetrics p0){ return 0; } + public float getFontSpacing(){ return 0; } + public float getLetterSpacing(){ return 0; } + public float getRunAdvance(CharSequence p0, int p1, int p2, int p3, int p4, boolean p5, int p6){ return 0; } + public float getRunAdvance(char[] p0, int p1, int p2, int p3, int p4, boolean p5, int p6){ return 0; } + public float getShadowLayerDx(){ return 0; } + public float getShadowLayerDy(){ return 0; } + public float getShadowLayerRadius(){ return 0; } + public float getStrikeThruPosition(){ return 0; } + public float getStrikeThruThickness(){ return 0; } + public float getStrokeMiter(){ return 0; } + public float getStrokeWidth(){ return 0; } + public float getTextRunAdvances(char[] p0, int p1, int p2, int p3, int p4, boolean p5, float[] p6, int p7){ return 0; } + public float getTextScaleX(){ return 0; } + public float getTextSize(){ return 0; } + public float getTextSkewX(){ return 0; } + public float getUnderlinePosition(){ return 0; } + public float getUnderlineThickness(){ return 0; } + public float getWordSpacing(){ return 0; } + public float measureText(CharSequence p0, int p1, int p2){ return 0; } + public float measureText(String p0){ return 0; } + public float measureText(String p0, int p1, int p2){ return 0; } + public float measureText(char[] p0, int p1, int p2){ return 0; } + public int breakText(CharSequence p0, int p1, int p2, boolean p3, float p4, float[] p5){ return 0; } + public int breakText(String p0, boolean p1, float p2, float[] p3){ return 0; } + public int breakText(char[] p0, int p1, int p2, float p3, float[] p4){ return 0; } + public int getAlpha(){ return 0; } + public int getColor(){ return 0; } + public int getEndHyphenEdit(){ return 0; } + public int getFlags(){ return 0; } + public int getFontMetricsInt(Paint.FontMetricsInt p0){ return 0; } + public int getHinting(){ return 0; } + public int getOffsetForAdvance(CharSequence p0, int p1, int p2, int p3, int p4, boolean p5, float p6){ return 0; } + public int getOffsetForAdvance(char[] p0, int p1, int p2, int p3, int p4, boolean p5, float p6){ return 0; } + public int getShadowLayerColor(){ return 0; } + public int getStartHyphenEdit(){ return 0; } + public int getTextRunCursor(CharSequence p0, int p1, int p2, boolean p3, int p4, int p5){ return 0; } + public int getTextRunCursor(char[] p0, int p1, int p2, boolean p3, int p4, int p5){ return 0; } + public int getTextWidths(CharSequence p0, int p1, int p2, float[] p3){ return 0; } + public int getTextWidths(String p0, float[] p1){ return 0; } + public int getTextWidths(String p0, int p1, int p2, float[] p3){ return 0; } + public int getTextWidths(char[] p0, int p1, int p2, float[] p3){ return 0; } + public long getColorLong(){ return 0; } + public long getShadowLayerColorLong(){ return 0; } + public static int ANTI_ALIAS_FLAG = 0; + public static int CURSOR_AFTER = 0; + public static int CURSOR_AT = 0; + public static int CURSOR_AT_OR_AFTER = 0; + public static int CURSOR_AT_OR_BEFORE = 0; + public static int CURSOR_BEFORE = 0; + public static int DEV_KERN_TEXT_FLAG = 0; + public static int DITHER_FLAG = 0; + public static int EMBEDDED_BITMAP_TEXT_FLAG = 0; + public static int END_HYPHEN_EDIT_INSERT_ARMENIAN_HYPHEN = 0; + public static int END_HYPHEN_EDIT_INSERT_HYPHEN = 0; + public static int END_HYPHEN_EDIT_INSERT_MAQAF = 0; + public static int END_HYPHEN_EDIT_INSERT_UCAS_HYPHEN = 0; + public static int END_HYPHEN_EDIT_INSERT_ZWJ_AND_HYPHEN = 0; + public static int END_HYPHEN_EDIT_NO_EDIT = 0; + public static int END_HYPHEN_EDIT_REPLACE_WITH_HYPHEN = 0; + public static int FAKE_BOLD_TEXT_FLAG = 0; + public static int FILTER_BITMAP_FLAG = 0; + public static int HINTING_OFF = 0; + public static int HINTING_ON = 0; + public static int LINEAR_TEXT_FLAG = 0; + public static int START_HYPHEN_EDIT_INSERT_HYPHEN = 0; + public static int START_HYPHEN_EDIT_INSERT_ZWJ = 0; + public static int START_HYPHEN_EDIT_NO_EDIT = 0; + public static int STRIKE_THRU_TEXT_FLAG = 0; + public static int SUBPIXEL_TEXT_FLAG = 0; + public static int UNDERLINE_TEXT_FLAG = 0; + public void clearShadowLayer(){} + public void getTextBounds(CharSequence p0, int p1, int p2, Rect p3){} + public void getTextBounds(String p0, int p1, int p2, Rect p3){} + public void getTextBounds(char[] p0, int p1, int p2, Rect p3){} + public void getTextPath(String p0, int p1, int p2, float p3, float p4, Path p5){} + public void getTextPath(char[] p0, int p1, int p2, float p3, float p4, Path p5){} + public void reset(){} + public void set(Paint p0){} + public void setARGB(int p0, int p1, int p2, int p3){} + public void setAlpha(int p0){} + public void setAntiAlias(boolean p0){} + public void setBlendMode(BlendMode p0){} + public void setColor(int p0){} + public void setColor(long p0){} + public void setDither(boolean p0){} + public void setElegantTextHeight(boolean p0){} + public void setEndHyphenEdit(int p0){} + public void setFakeBoldText(boolean p0){} + public void setFilterBitmap(boolean p0){} + public void setFlags(int p0){} + public void setFontFeatureSettings(String p0){} + public void setHinting(int p0){} + public void setLetterSpacing(float p0){} + public void setLinearText(boolean p0){} + public void setShadowLayer(float p0, float p1, float p2, int p3){} + public void setShadowLayer(float p0, float p1, float p2, long p3){} + public void setStartHyphenEdit(int p0){} + public void setStrikeThruText(boolean p0){} + public void setStrokeCap(Paint.Cap p0){} + public void setStrokeJoin(Paint.Join p0){} + public void setStrokeMiter(float p0){} + public void setStrokeWidth(float p0){} + public void setStyle(Paint.Style p0){} + public void setSubpixelText(boolean p0){} + public void setTextAlign(Paint.Align p0){} + public void setTextLocale(Locale p0){} + public void setTextLocales(LocaleList p0){} + public void setTextScaleX(float p0){} + public void setTextSize(float p0){} + public void setTextSkewX(float p0){} + public void setUnderlineText(boolean p0){} + public void setWordSpacing(float p0){} + static public class FontMetrics + { + public FontMetrics(){} + public float ascent = 0; + public float bottom = 0; + public float descent = 0; + public float leading = 0; + public float top = 0; + } + static public class FontMetricsInt + { + public FontMetricsInt(){} + public String toString(){ return null; } + public int ascent = 0; + public int bottom = 0; + public int descent = 0; + public int leading = 0; + public int top = 0; + } + static public enum Align + { + CENTER, LEFT, RIGHT; + private Align() {} + } + static public enum Cap + { + BUTT, ROUND, SQUARE; + private Cap() {} + } + static public enum Join + { + BEVEL, MITER, ROUND; + private Join() {} + } + static public enum Style + { + FILL, FILL_AND_STROKE, STROKE; + private Style() {} + } +} diff --git a/java/ql/test/stubs/android/android/graphics/Path.java b/java/ql/test/stubs/android/android/graphics/Path.java new file mode 100644 index 00000000000..c7164e37b01 --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/Path.java @@ -0,0 +1,73 @@ +// Generated automatically from android.graphics.Path for testing purposes + +package android.graphics; + +import android.graphics.Matrix; +import android.graphics.RectF; + +public class Path +{ + public Path(){} + public Path(Path p0){} + public Path.FillType getFillType(){ return null; } + public boolean isConvex(){ return false; } + public boolean isEmpty(){ return false; } + public boolean isInverseFillType(){ return false; } + public boolean isRect(RectF p0){ return false; } + public boolean op(Path p0, Path p1, Path.Op p2){ return false; } + public boolean op(Path p0, Path.Op p1){ return false; } + public float[] approximate(float p0){ return null; } + public void addArc(RectF p0, float p1, float p2){} + public void addArc(float p0, float p1, float p2, float p3, float p4, float p5){} + public void addCircle(float p0, float p1, float p2, Path.Direction p3){} + public void addOval(RectF p0, Path.Direction p1){} + public void addOval(float p0, float p1, float p2, float p3, Path.Direction p4){} + public void addPath(Path p0){} + public void addPath(Path p0, Matrix p1){} + public void addPath(Path p0, float p1, float p2){} + public void addRect(RectF p0, Path.Direction p1){} + public void addRect(float p0, float p1, float p2, float p3, Path.Direction p4){} + public void addRoundRect(RectF p0, float p1, float p2, Path.Direction p3){} + public void addRoundRect(RectF p0, float[] p1, Path.Direction p2){} + public void addRoundRect(float p0, float p1, float p2, float p3, float p4, float p5, Path.Direction p6){} + public void addRoundRect(float p0, float p1, float p2, float p3, float[] p4, Path.Direction p5){} + public void arcTo(RectF p0, float p1, float p2){} + public void arcTo(RectF p0, float p1, float p2, boolean p3){} + public void arcTo(float p0, float p1, float p2, float p3, float p4, float p5, boolean p6){} + public void close(){} + public void computeBounds(RectF p0, boolean p1){} + public void cubicTo(float p0, float p1, float p2, float p3, float p4, float p5){} + public void incReserve(int p0){} + public void lineTo(float p0, float p1){} + public void moveTo(float p0, float p1){} + public void offset(float p0, float p1){} + public void offset(float p0, float p1, Path p2){} + public void quadTo(float p0, float p1, float p2, float p3){} + public void rCubicTo(float p0, float p1, float p2, float p3, float p4, float p5){} + public void rLineTo(float p0, float p1){} + public void rMoveTo(float p0, float p1){} + public void rQuadTo(float p0, float p1, float p2, float p3){} + public void reset(){} + public void rewind(){} + public void set(Path p0){} + public void setFillType(Path.FillType p0){} + public void setLastPoint(float p0, float p1){} + public void toggleInverseFillType(){} + public void transform(Matrix p0){} + public void transform(Matrix p0, Path p1){} + static public enum Direction + { + CCW, CW; + private Direction() {} + } + static public enum FillType + { + EVEN_ODD, INVERSE_EVEN_ODD, INVERSE_WINDING, WINDING; + private FillType() {} + } + static public enum Op + { + DIFFERENCE, INTERSECT, REVERSE_DIFFERENCE, UNION, XOR; + private Op() {} + } +} diff --git a/java/ql/test/stubs/android/android/graphics/PathEffect.java b/java/ql/test/stubs/android/android/graphics/PathEffect.java new file mode 100644 index 00000000000..c9ca9aeae86 --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/PathEffect.java @@ -0,0 +1,10 @@ +// Generated automatically from android.graphics.PathEffect for testing purposes + +package android.graphics; + + +public class PathEffect +{ + protected void finalize(){} + public PathEffect(){} +} diff --git a/java/ql/test/stubs/android/android/graphics/Picture.java b/java/ql/test/stubs/android/android/graphics/Picture.java new file mode 100644 index 00000000000..328321a2139 --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/Picture.java @@ -0,0 +1,18 @@ +// Generated automatically from android.graphics.Picture for testing purposes + +package android.graphics; + +import android.graphics.Canvas; + +public class Picture +{ + protected void finalize(){} + public Canvas beginRecording(int p0, int p1){ return null; } + public Picture(){} + public Picture(Picture p0){} + public boolean requiresHardwareAcceleration(){ return false; } + public int getHeight(){ return 0; } + public int getWidth(){ return 0; } + public void draw(Canvas p0){} + public void endRecording(){} +} diff --git a/java/ql/test/stubs/android/android/graphics/Point.java b/java/ql/test/stubs/android/android/graphics/Point.java new file mode 100644 index 00000000000..0e97248f44b --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/Point.java @@ -0,0 +1,26 @@ +// Generated automatically from android.graphics.Point for testing purposes + +package android.graphics; + +import android.os.Parcel; +import android.os.Parcelable; + +public class Point implements Parcelable +{ + public Point(){} + public Point(Point p0){} + public Point(int p0, int p1){} + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public final boolean equals(int p0, int p1){ return false; } + public final void negate(){} + public final void offset(int p0, int p1){} + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public int x = 0; + public int y = 0; + public static Parcelable.Creator CREATOR = null; + public void readFromParcel(Parcel p0){} + public void set(int p0, int p1){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/graphics/PorterDuff.java b/java/ql/test/stubs/android/android/graphics/PorterDuff.java new file mode 100644 index 00000000000..39a82e655d6 --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/PorterDuff.java @@ -0,0 +1,14 @@ +// Generated automatically from android.graphics.PorterDuff for testing purposes + +package android.graphics; + + +public class PorterDuff +{ + public PorterDuff(){} + static public enum Mode + { + ADD, CLEAR, DARKEN, DST, DST_ATOP, DST_IN, DST_OUT, DST_OVER, LIGHTEN, MULTIPLY, OVERLAY, SCREEN, SRC, SRC_ATOP, SRC_IN, SRC_OUT, SRC_OVER, XOR; + private Mode() {} + } +} diff --git a/java/ql/test/stubs/android/android/graphics/RecordingCanvas.java b/java/ql/test/stubs/android/android/graphics/RecordingCanvas.java new file mode 100644 index 00000000000..1d992a53ec0 --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/RecordingCanvas.java @@ -0,0 +1,82 @@ +// Generated automatically from android.graphics.RecordingCanvas for testing purposes + +package android.graphics; + +import android.graphics.Bitmap; +import android.graphics.BlendMode; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.NinePatch; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Picture; +import android.graphics.PorterDuff; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.RenderNode; +import android.graphics.text.MeasuredText; + +public class RecordingCanvas extends Canvas +{ + public boolean isHardwareAccelerated(){ return false; } + public boolean isOpaque(){ return false; } + public final void drawARGB(int p0, int p1, int p2, int p3){} + public final void drawArc(RectF p0, float p1, float p2, boolean p3, Paint p4){} + public final void drawArc(float p0, float p1, float p2, float p3, float p4, float p5, boolean p6, Paint p7){} + public final void drawBitmap(Bitmap p0, Matrix p1, Paint p2){} + public final void drawBitmap(Bitmap p0, Rect p1, Rect p2, Paint p3){} + public final void drawBitmap(Bitmap p0, Rect p1, RectF p2, Paint p3){} + public final void drawBitmap(Bitmap p0, float p1, float p2, Paint p3){} + public final void drawBitmap(int[] p0, int p1, int p2, float p3, float p4, int p5, int p6, boolean p7, Paint p8){} + public final void drawBitmap(int[] p0, int p1, int p2, int p3, int p4, int p5, int p6, boolean p7, Paint p8){} + public final void drawBitmapMesh(Bitmap p0, int p1, int p2, float[] p3, int p4, int[] p5, int p6, Paint p7){} + public final void drawCircle(float p0, float p1, float p2, Paint p3){} + public final void drawColor(int p0){} + public final void drawColor(int p0, BlendMode p1){} + public final void drawColor(int p0, PorterDuff.Mode p1){} + public final void drawColor(long p0, BlendMode p1){} + public final void drawDoubleRoundRect(RectF p0, float p1, float p2, RectF p3, float p4, float p5, Paint p6){} + public final void drawDoubleRoundRect(RectF p0, float[] p1, RectF p2, float[] p3, Paint p4){} + public final void drawLine(float p0, float p1, float p2, float p3, Paint p4){} + public final void drawLines(float[] p0, Paint p1){} + public final void drawLines(float[] p0, int p1, int p2, Paint p3){} + public final void drawOval(RectF p0, Paint p1){} + public final void drawOval(float p0, float p1, float p2, float p3, Paint p4){} + public final void drawPaint(Paint p0){} + public final void drawPatch(NinePatch p0, Rect p1, Paint p2){} + public final void drawPatch(NinePatch p0, RectF p1, Paint p2){} + public final void drawPath(Path p0, Paint p1){} + public final void drawPicture(Picture p0){} + public final void drawPicture(Picture p0, Rect p1){} + public final void drawPicture(Picture p0, RectF p1){} + public final void drawPoint(float p0, float p1, Paint p2){} + public final void drawPoints(float[] p0, Paint p1){} + public final void drawPoints(float[] p0, int p1, int p2, Paint p3){} + public final void drawPosText(String p0, float[] p1, Paint p2){} + public final void drawPosText(char[] p0, int p1, int p2, float[] p3, Paint p4){} + public final void drawRGB(int p0, int p1, int p2){} + public final void drawRect(Rect p0, Paint p1){} + public final void drawRect(RectF p0, Paint p1){} + public final void drawRect(float p0, float p1, float p2, float p3, Paint p4){} + public final void drawRoundRect(RectF p0, float p1, float p2, Paint p3){} + public final void drawRoundRect(float p0, float p1, float p2, float p3, float p4, float p5, Paint p6){} + public final void drawText(CharSequence p0, int p1, int p2, float p3, float p4, Paint p5){} + public final void drawText(String p0, float p1, float p2, Paint p3){} + public final void drawText(String p0, int p1, int p2, float p3, float p4, Paint p5){} + public final void drawText(char[] p0, int p1, int p2, float p3, float p4, Paint p5){} + public final void drawTextOnPath(String p0, Path p1, float p2, float p3, Paint p4){} + public final void drawTextOnPath(char[] p0, int p1, int p2, Path p3, float p4, float p5, Paint p6){} + public final void drawTextRun(CharSequence p0, int p1, int p2, int p3, int p4, float p5, float p6, boolean p7, Paint p8){} + public final void drawTextRun(char[] p0, int p1, int p2, int p3, int p4, float p5, float p6, boolean p7, Paint p8){} + public final void drawVertices(Canvas.VertexMode p0, int p1, float[] p2, int p3, float[] p4, int p5, int[] p6, int p7, short[] p8, int p9, int p10, Paint p11){} + public int getHeight(){ return 0; } + public int getMaximumBitmapHeight(){ return 0; } + public int getMaximumBitmapWidth(){ return 0; } + public int getWidth(){ return 0; } + public void disableZ(){} + public void drawRenderNode(RenderNode p0){} + public void drawTextRun(MeasuredText p0, int p1, int p2, int p3, int p4, float p5, float p6, boolean p7, Paint p8){} + public void enableZ(){} + public void setBitmap(Bitmap p0){} + public void setDensity(int p0){} +} diff --git a/java/ql/test/stubs/android/android/graphics/Rect.java b/java/ql/test/stubs/android/android/graphics/Rect.java new file mode 100644 index 00000000000..c29950cb31d --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/Rect.java @@ -0,0 +1,52 @@ +// Generated automatically from android.graphics.Rect for testing purposes + +package android.graphics; + +import android.os.Parcel; +import android.os.Parcelable; + +public class Rect implements Parcelable +{ + public Rect(){} + public Rect(Rect p0){} + public Rect(int p0, int p1, int p2, int p3){} + public String flattenToString(){ return null; } + public String toShortString(){ return null; } + public String toString(){ return null; } + public boolean contains(Rect p0){ return false; } + public boolean contains(int p0, int p1){ return false; } + public boolean contains(int p0, int p1, int p2, int p3){ return false; } + public boolean equals(Object p0){ return false; } + public boolean intersect(Rect p0){ return false; } + public boolean intersect(int p0, int p1, int p2, int p3){ return false; } + public boolean intersects(int p0, int p1, int p2, int p3){ return false; } + public boolean isEmpty(){ return false; } + public boolean setIntersect(Rect p0, Rect p1){ return false; } + public float exactCenterX(){ return 0; } + public float exactCenterY(){ return 0; } + public int bottom = 0; + public int centerX(){ return 0; } + public int centerY(){ return 0; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public int height(){ return 0; } + public int left = 0; + public int right = 0; + public int top = 0; + public int width(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static Rect unflattenFromString(String p0){ return null; } + public static boolean intersects(Rect p0, Rect p1){ return false; } + public void inset(int p0, int p1){} + public void offset(int p0, int p1){} + public void offsetTo(int p0, int p1){} + public void readFromParcel(Parcel p0){} + public void set(Rect p0){} + public void set(int p0, int p1, int p2, int p3){} + public void setEmpty(){} + public void sort(){} + public void union(Rect p0){} + public void union(int p0, int p1){} + public void union(int p0, int p1, int p2, int p3){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/graphics/RectF.java b/java/ql/test/stubs/android/android/graphics/RectF.java new file mode 100644 index 00000000000..59489dd3f17 --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/RectF.java @@ -0,0 +1,53 @@ +// Generated automatically from android.graphics.RectF for testing purposes + +package android.graphics; + +import android.graphics.Rect; +import android.os.Parcel; +import android.os.Parcelable; + +public class RectF implements Parcelable +{ + public RectF(){} + public RectF(Rect p0){} + public RectF(RectF p0){} + public RectF(float p0, float p1, float p2, float p3){} + public String toShortString(){ return null; } + public String toString(){ return null; } + public boolean contains(RectF p0){ return false; } + public boolean contains(float p0, float p1){ return false; } + public boolean contains(float p0, float p1, float p2, float p3){ return false; } + public boolean equals(Object p0){ return false; } + public boolean intersect(RectF p0){ return false; } + public boolean intersect(float p0, float p1, float p2, float p3){ return false; } + public boolean intersects(float p0, float p1, float p2, float p3){ return false; } + public boolean setIntersect(RectF p0, RectF p1){ return false; } + public final boolean isEmpty(){ return false; } + public final float centerX(){ return 0; } + public final float centerY(){ return 0; } + public final float height(){ return 0; } + public final float width(){ return 0; } + public float bottom = 0; + public float left = 0; + public float right = 0; + public float top = 0; + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static boolean intersects(RectF p0, RectF p1){ return false; } + public void inset(float p0, float p1){} + public void offset(float p0, float p1){} + public void offsetTo(float p0, float p1){} + public void readFromParcel(Parcel p0){} + public void round(Rect p0){} + public void roundOut(Rect p0){} + public void set(Rect p0){} + public void set(RectF p0){} + public void set(float p0, float p1, float p2, float p3){} + public void setEmpty(){} + public void sort(){} + public void union(RectF p0){} + public void union(float p0, float p1){} + public void union(float p0, float p1, float p2, float p3){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/graphics/Region.java b/java/ql/test/stubs/android/android/graphics/Region.java new file mode 100644 index 00000000000..7fbd2b4dafc --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/Region.java @@ -0,0 +1,53 @@ +// Generated automatically from android.graphics.Region for testing purposes + +package android.graphics; + +import android.graphics.Path; +import android.graphics.Rect; +import android.os.Parcel; +import android.os.Parcelable; + +public class Region implements Parcelable +{ + protected void finalize(){} + public Path getBoundaryPath(){ return null; } + public Rect getBounds(){ return null; } + public Region(){} + public Region(Rect p0){} + public Region(Region p0){} + public Region(int p0, int p1, int p2, int p3){} + public String toString(){ return null; } + public boolean contains(int p0, int p1){ return false; } + public boolean equals(Object p0){ return false; } + public boolean getBoundaryPath(Path p0){ return false; } + public boolean getBounds(Rect p0){ return false; } + public boolean isComplex(){ return false; } + public boolean isEmpty(){ return false; } + public boolean isRect(){ return false; } + public boolean op(Rect p0, Region p1, Region.Op p2){ return false; } + public boolean op(Rect p0, Region.Op p1){ return false; } + public boolean op(Region p0, Region p1, Region.Op p2){ return false; } + public boolean op(Region p0, Region.Op p1){ return false; } + public boolean op(int p0, int p1, int p2, int p3, Region.Op p4){ return false; } + public boolean quickContains(Rect p0){ return false; } + public boolean quickContains(int p0, int p1, int p2, int p3){ return false; } + public boolean quickReject(Rect p0){ return false; } + public boolean quickReject(Region p0){ return false; } + public boolean quickReject(int p0, int p1, int p2, int p3){ return false; } + public boolean set(Rect p0){ return false; } + public boolean set(Region p0){ return false; } + public boolean set(int p0, int p1, int p2, int p3){ return false; } + public boolean setPath(Path p0, Region p1){ return false; } + public final boolean union(Rect p0){ return false; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void setEmpty(){} + public void translate(int p0, int p1){} + public void translate(int p0, int p1, Region p2){} + public void writeToParcel(Parcel p0, int p1){} + static public enum Op + { + DIFFERENCE, INTERSECT, REPLACE, REVERSE_DIFFERENCE, UNION, XOR; + private Op() {} + } +} diff --git a/java/ql/test/stubs/android/android/graphics/RenderNode.java b/java/ql/test/stubs/android/android/graphics/RenderNode.java new file mode 100644 index 00000000000..9384cd4cd4e --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/RenderNode.java @@ -0,0 +1,82 @@ +// Generated automatically from android.graphics.RenderNode for testing purposes + +package android.graphics; + +import android.graphics.Matrix; +import android.graphics.Outline; +import android.graphics.Paint; +import android.graphics.RecordingCanvas; +import android.graphics.Rect; + +public class RenderNode +{ + protected RenderNode() {} + public RecordingCanvas beginRecording(){ return null; } + public RecordingCanvas beginRecording(int p0, int p1){ return null; } + public RenderNode(String p0){} + public boolean getClipToBounds(){ return false; } + public boolean getClipToOutline(){ return false; } + public boolean getUseCompositingLayer(){ return false; } + public boolean hasDisplayList(){ return false; } + public boolean hasIdentityMatrix(){ return false; } + public boolean hasOverlappingRendering(){ return false; } + public boolean hasShadow(){ return false; } + public boolean isForceDarkAllowed(){ return false; } + public boolean isPivotExplicitlySet(){ return false; } + public boolean offsetLeftAndRight(int p0){ return false; } + public boolean offsetTopAndBottom(int p0){ return false; } + public boolean resetPivot(){ return false; } + public boolean setAlpha(float p0){ return false; } + public boolean setAmbientShadowColor(int p0){ return false; } + public boolean setCameraDistance(float p0){ return false; } + public boolean setClipRect(Rect p0){ return false; } + public boolean setClipToBounds(boolean p0){ return false; } + public boolean setClipToOutline(boolean p0){ return false; } + public boolean setElevation(float p0){ return false; } + public boolean setForceDarkAllowed(boolean p0){ return false; } + public boolean setHasOverlappingRendering(boolean p0){ return false; } + public boolean setOutline(Outline p0){ return false; } + public boolean setPivotX(float p0){ return false; } + public boolean setPivotY(float p0){ return false; } + public boolean setPosition(Rect p0){ return false; } + public boolean setPosition(int p0, int p1, int p2, int p3){ return false; } + public boolean setProjectBackwards(boolean p0){ return false; } + public boolean setProjectionReceiver(boolean p0){ return false; } + public boolean setRotationX(float p0){ return false; } + public boolean setRotationY(float p0){ return false; } + public boolean setRotationZ(float p0){ return false; } + public boolean setScaleX(float p0){ return false; } + public boolean setScaleY(float p0){ return false; } + public boolean setSpotShadowColor(int p0){ return false; } + public boolean setTranslationX(float p0){ return false; } + public boolean setTranslationY(float p0){ return false; } + public boolean setTranslationZ(float p0){ return false; } + public boolean setUseCompositingLayer(boolean p0, Paint p1){ return false; } + public float getAlpha(){ return 0; } + public float getCameraDistance(){ return 0; } + public float getElevation(){ return 0; } + public float getPivotX(){ return 0; } + public float getPivotY(){ return 0; } + public float getRotationX(){ return 0; } + public float getRotationY(){ return 0; } + public float getRotationZ(){ return 0; } + public float getScaleX(){ return 0; } + public float getScaleY(){ return 0; } + public float getTranslationX(){ return 0; } + public float getTranslationY(){ return 0; } + public float getTranslationZ(){ return 0; } + public int getAmbientShadowColor(){ return 0; } + public int getBottom(){ return 0; } + public int getHeight(){ return 0; } + public int getLeft(){ return 0; } + public int getRight(){ return 0; } + public int getSpotShadowColor(){ return 0; } + public int getTop(){ return 0; } + public int getWidth(){ return 0; } + public long computeApproximateMemoryUsage(){ return 0; } + public long getUniqueId(){ return 0; } + public void discardDisplayList(){} + public void endRecording(){} + public void getInverseMatrix(Matrix p0){} + public void getMatrix(Matrix p0){} +} diff --git a/java/ql/test/stubs/android/android/graphics/Shader.java b/java/ql/test/stubs/android/android/graphics/Shader.java new file mode 100644 index 00000000000..3e845aa0c0a --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/Shader.java @@ -0,0 +1,12 @@ +// Generated automatically from android.graphics.Shader for testing purposes + +package android.graphics; + +import android.graphics.Matrix; + +public class Shader +{ + public Shader(){} + public boolean getLocalMatrix(Matrix p0){ return false; } + public void setLocalMatrix(Matrix p0){} +} diff --git a/java/ql/test/stubs/android/android/graphics/Typeface.java b/java/ql/test/stubs/android/android/graphics/Typeface.java new file mode 100644 index 00000000000..a2ab630e8d0 --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/Typeface.java @@ -0,0 +1,33 @@ +// Generated automatically from android.graphics.Typeface for testing purposes + +package android.graphics; + +import android.content.res.AssetManager; +import java.io.File; + +public class Typeface +{ + protected Typeface() {} + public boolean equals(Object p0){ return false; } + public final boolean isBold(){ return false; } + public final boolean isItalic(){ return false; } + public int getStyle(){ return 0; } + public int getWeight(){ return 0; } + public int hashCode(){ return 0; } + public static Typeface DEFAULT = null; + public static Typeface DEFAULT_BOLD = null; + public static Typeface MONOSPACE = null; + public static Typeface SANS_SERIF = null; + public static Typeface SERIF = null; + public static Typeface create(String p0, int p1){ return null; } + public static Typeface create(Typeface p0, int p1){ return null; } + public static Typeface create(Typeface p0, int p1, boolean p2){ return null; } + public static Typeface createFromAsset(AssetManager p0, String p1){ return null; } + public static Typeface createFromFile(File p0){ return null; } + public static Typeface createFromFile(String p0){ return null; } + public static Typeface defaultFromStyle(int p0){ return null; } + public static int BOLD = 0; + public static int BOLD_ITALIC = 0; + public static int ITALIC = 0; + public static int NORMAL = 0; +} diff --git a/java/ql/test/stubs/android/android/graphics/Xfermode.java b/java/ql/test/stubs/android/android/graphics/Xfermode.java new file mode 100644 index 00000000000..7b6e5ade001 --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/Xfermode.java @@ -0,0 +1,9 @@ +// Generated automatically from android.graphics.Xfermode for testing purposes + +package android.graphics; + + +public class Xfermode +{ + public Xfermode(){} +} diff --git a/java/ql/test/stubs/android/android/graphics/drawable/Drawable.java b/java/ql/test/stubs/android/android/graphics/drawable/Drawable.java new file mode 100644 index 00000000000..5bb5b00f898 --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/drawable/Drawable.java @@ -0,0 +1,111 @@ +// Generated automatically from android.graphics.drawable.Drawable for testing purposes + +package android.graphics.drawable; + +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.graphics.BitmapFactory; +import android.graphics.BlendMode; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Insets; +import android.graphics.Outline; +import android.graphics.PorterDuff; +import android.graphics.Rect; +import android.graphics.Region; +import android.util.AttributeSet; +import android.util.TypedValue; +import java.io.InputStream; +import org.xmlpull.v1.XmlPullParser; + +abstract public class Drawable +{ + abstract static public class ConstantState + { + public ConstantState(){} + public Drawable newDrawable(Resources p0){ return null; } + public Drawable newDrawable(Resources p0, Resources.Theme p1){ return null; } + public abstract Drawable newDrawable(); + public abstract int getChangingConfigurations(); + public boolean canApplyTheme(){ return false; } + } + protected boolean onLevelChange(int p0){ return false; } + protected boolean onStateChange(int[] p0){ return false; } + protected void onBoundsChange(Rect p0){} + public ColorFilter getColorFilter(){ return null; } + public Drawable getCurrent(){ return null; } + public Drawable mutate(){ return null; } + public Drawable(){} + public Drawable.Callback getCallback(){ return null; } + public Drawable.ConstantState getConstantState(){ return null; } + public Insets getOpticalInsets(){ return null; } + public Rect getDirtyBounds(){ return null; } + public Region getTransparentRegion(){ return null; } + public abstract int getOpacity(); + public abstract void draw(Canvas p0); + public abstract void setAlpha(int p0); + public abstract void setColorFilter(ColorFilter p0); + public boolean canApplyTheme(){ return false; } + public boolean getPadding(Rect p0){ return false; } + public boolean isAutoMirrored(){ return false; } + public boolean isFilterBitmap(){ return false; } + public boolean isProjected(){ return false; } + public boolean isStateful(){ return false; } + public boolean onLayoutDirectionChanged(int p0){ return false; } + public boolean setState(int[] p0){ return false; } + public boolean setVisible(boolean p0, boolean p1){ return false; } + public final Rect copyBounds(){ return null; } + public final Rect getBounds(){ return null; } + public final boolean isVisible(){ return false; } + public final boolean setLayoutDirection(int p0){ return false; } + public final boolean setLevel(int p0){ return false; } + public final int getLevel(){ return 0; } + public final void copyBounds(Rect p0){} + public final void setCallback(Drawable.Callback p0){} + public int getAlpha(){ return 0; } + public int getChangingConfigurations(){ return 0; } + public int getIntrinsicHeight(){ return 0; } + public int getIntrinsicWidth(){ return 0; } + public int getLayoutDirection(){ return 0; } + public int getMinimumHeight(){ return 0; } + public int getMinimumWidth(){ return 0; } + public int[] getState(){ return null; } + public static Drawable createFromPath(String p0){ return null; } + public static Drawable createFromResourceStream(Resources p0, TypedValue p1, InputStream p2, String p3){ return null; } + public static Drawable createFromResourceStream(Resources p0, TypedValue p1, InputStream p2, String p3, BitmapFactory.Options p4){ return null; } + public static Drawable createFromStream(InputStream p0, String p1){ return null; } + public static Drawable createFromXml(Resources p0, XmlPullParser p1){ return null; } + public static Drawable createFromXml(Resources p0, XmlPullParser p1, Resources.Theme p2){ return null; } + public static Drawable createFromXmlInner(Resources p0, XmlPullParser p1, AttributeSet p2){ return null; } + public static Drawable createFromXmlInner(Resources p0, XmlPullParser p1, AttributeSet p2, Resources.Theme p3){ return null; } + public static int resolveOpacity(int p0, int p1){ return 0; } + public void applyTheme(Resources.Theme p0){} + public void clearColorFilter(){} + public void getHotspotBounds(Rect p0){} + public void getOutline(Outline p0){} + public void inflate(Resources p0, XmlPullParser p1, AttributeSet p2){} + public void inflate(Resources p0, XmlPullParser p1, AttributeSet p2, Resources.Theme p3){} + public void invalidateSelf(){} + public void jumpToCurrentState(){} + public void scheduleSelf(Runnable p0, long p1){} + public void setAutoMirrored(boolean p0){} + public void setBounds(Rect p0){} + public void setBounds(int p0, int p1, int p2, int p3){} + public void setChangingConfigurations(int p0){} + public void setColorFilter(int p0, PorterDuff.Mode p1){} + public void setDither(boolean p0){} + public void setFilterBitmap(boolean p0){} + public void setHotspot(float p0, float p1){} + public void setHotspotBounds(int p0, int p1, int p2, int p3){} + public void setTint(int p0){} + public void setTintBlendMode(BlendMode p0){} + public void setTintList(ColorStateList p0){} + public void setTintMode(PorterDuff.Mode p0){} + public void unscheduleSelf(Runnable p0){} + static public interface Callback + { + void invalidateDrawable(Drawable p0); + void scheduleDrawable(Drawable p0, Runnable p1, long p2); + void unscheduleDrawable(Drawable p0, Runnable p1); + } +} diff --git a/java/ql/test/stubs/android/android/graphics/drawable/Icon.java b/java/ql/test/stubs/android/android/graphics/drawable/Icon.java new file mode 100644 index 00000000000..fcfeb2b7109 --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/drawable/Icon.java @@ -0,0 +1,55 @@ +// Generated automatically from android.graphics.drawable.Icon for testing purposes + +package android.graphics.drawable; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Bitmap; +import android.graphics.BlendMode; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Handler; +import android.os.Message; +import android.os.Parcel; +import android.os.Parcelable; + +public class Icon implements Parcelable +{ + protected Icon() {} + public Drawable loadDrawable(Context p0){ return null; } + public Icon setTint(int p0){ return null; } + public Icon setTintBlendMode(BlendMode p0){ return null; } + public Icon setTintList(ColorStateList p0){ return null; } + public Icon setTintMode(PorterDuff.Mode p0){ return null; } + public String getResPackage(){ return null; } + public String toString(){ return null; } + public Uri getUri(){ return null; } + public int describeContents(){ return 0; } + public int getResId(){ return 0; } + public int getType(){ return 0; } + public static Icon createWithAdaptiveBitmap(Bitmap p0){ return null; } + public static Icon createWithAdaptiveBitmapContentUri(String p0){ return null; } + public static Icon createWithAdaptiveBitmapContentUri(Uri p0){ return null; } + public static Icon createWithBitmap(Bitmap p0){ return null; } + public static Icon createWithContentUri(String p0){ return null; } + public static Icon createWithContentUri(Uri p0){ return null; } + public static Icon createWithData(byte[] p0, int p1, int p2){ return null; } + public static Icon createWithFilePath(String p0){ return null; } + public static Icon createWithResource(Context p0, int p1){ return null; } + public static Icon createWithResource(String p0, int p1){ return null; } + public static Parcelable.Creator CREATOR = null; + public static int TYPE_ADAPTIVE_BITMAP = 0; + public static int TYPE_BITMAP = 0; + public static int TYPE_DATA = 0; + public static int TYPE_RESOURCE = 0; + public static int TYPE_URI = 0; + public static int TYPE_URI_ADAPTIVE_BITMAP = 0; + public void loadDrawableAsync(Context p0, Icon.OnDrawableLoadedListener p1, Handler p2){} + public void loadDrawableAsync(Context p0, Message p1){} + public void writeToParcel(Parcel p0, int p1){} + static public interface OnDrawableLoadedListener + { + void onDrawableLoaded(Drawable p0); + } +} diff --git a/java/ql/test/stubs/android/android/graphics/text/MeasuredText.java b/java/ql/test/stubs/android/android/graphics/text/MeasuredText.java new file mode 100644 index 00000000000..c737bd5dfc1 --- /dev/null +++ b/java/ql/test/stubs/android/android/graphics/text/MeasuredText.java @@ -0,0 +1,13 @@ +// Generated automatically from android.graphics.text.MeasuredText for testing purposes + +package android.graphics.text; + +import android.graphics.Rect; + +public class MeasuredText +{ + protected MeasuredText() {} + public float getCharWidthAt(int p0){ return 0; } + public float getWidth(int p0, int p1){ return 0; } + public void getBounds(int p0, int p1, Rect p2){} +} diff --git a/java/ql/test/stubs/android/android/hardware/HardwareBuffer.java b/java/ql/test/stubs/android/android/hardware/HardwareBuffer.java new file mode 100644 index 00000000000..0f4e5888a21 --- /dev/null +++ b/java/ql/test/stubs/android/android/hardware/HardwareBuffer.java @@ -0,0 +1,50 @@ +// Generated automatically from android.hardware.HardwareBuffer for testing purposes + +package android.hardware; + +import android.os.Parcel; +import android.os.Parcelable; + +public class HardwareBuffer implements AutoCloseable, Parcelable +{ + protected HardwareBuffer() {} + protected void finalize(){} + public boolean isClosed(){ return false; } + public int describeContents(){ return 0; } + public int getFormat(){ return 0; } + public int getHeight(){ return 0; } + public int getLayers(){ return 0; } + public int getWidth(){ return 0; } + public long getUsage(){ return 0; } + public static HardwareBuffer create(int p0, int p1, int p2, int p3, long p4){ return null; } + public static Parcelable.Creator CREATOR = null; + public static boolean isSupported(int p0, int p1, int p2, int p3, long p4){ return false; } + public static int BLOB = 0; + public static int DS_24UI8 = 0; + public static int DS_FP32UI8 = 0; + public static int D_16 = 0; + public static int D_24 = 0; + public static int D_FP32 = 0; + public static int RGBA_1010102 = 0; + public static int RGBA_8888 = 0; + public static int RGBA_FP16 = 0; + public static int RGBX_8888 = 0; + public static int RGB_565 = 0; + public static int RGB_888 = 0; + public static int S_UI8 = 0; + public static int YCBCR_420_888 = 0; + public static long USAGE_CPU_READ_OFTEN = 0; + public static long USAGE_CPU_READ_RARELY = 0; + public static long USAGE_CPU_WRITE_OFTEN = 0; + public static long USAGE_CPU_WRITE_RARELY = 0; + public static long USAGE_GPU_COLOR_OUTPUT = 0; + public static long USAGE_GPU_CUBE_MAP = 0; + public static long USAGE_GPU_DATA_BUFFER = 0; + public static long USAGE_GPU_MIPMAP_COMPLETE = 0; + public static long USAGE_GPU_SAMPLED_IMAGE = 0; + public static long USAGE_PROTECTED_CONTENT = 0; + public static long USAGE_SENSOR_DIRECT_DATA = 0; + public static long USAGE_VIDEO_ENCODE = 0; + public void close(){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/icu/util/ULocale.java b/java/ql/test/stubs/android/android/icu/util/ULocale.java new file mode 100644 index 00000000000..1e1ea662c22 --- /dev/null +++ b/java/ql/test/stubs/android/android/icu/util/ULocale.java @@ -0,0 +1,138 @@ +// Generated automatically from android.icu.util.ULocale for testing purposes + +package android.icu.util; + +import java.io.Serializable; +import java.util.Iterator; +import java.util.Locale; +import java.util.Set; + +public class ULocale implements Comparable, Serializable +{ + protected ULocale() {} + public Iterator getKeywords(){ return null; } + public Locale toLocale(){ return null; } + public Object clone(){ return null; } + public Set getExtensionKeys(){ return null; } + public Set getUnicodeLocaleAttributes(){ return null; } + public Set getUnicodeLocaleKeys(){ return null; } + public String getBaseName(){ return null; } + public String getCharacterOrientation(){ return null; } + public String getCountry(){ return null; } + public String getDisplayCountry(){ return null; } + public String getDisplayCountry(ULocale p0){ return null; } + public String getDisplayKeywordValue(String p0){ return null; } + public String getDisplayKeywordValue(String p0, ULocale p1){ return null; } + public String getDisplayLanguage(){ return null; } + public String getDisplayLanguage(ULocale p0){ return null; } + public String getDisplayLanguageWithDialect(){ return null; } + public String getDisplayLanguageWithDialect(ULocale p0){ return null; } + public String getDisplayName(){ return null; } + public String getDisplayName(ULocale p0){ return null; } + public String getDisplayNameWithDialect(){ return null; } + public String getDisplayNameWithDialect(ULocale p0){ return null; } + public String getDisplayScript(){ return null; } + public String getDisplayScript(ULocale p0){ return null; } + public String getDisplayVariant(){ return null; } + public String getDisplayVariant(ULocale p0){ return null; } + public String getExtension(char p0){ return null; } + public String getISO3Country(){ return null; } + public String getISO3Language(){ return null; } + public String getKeywordValue(String p0){ return null; } + public String getLanguage(){ return null; } + public String getLineOrientation(){ return null; } + public String getName(){ return null; } + public String getScript(){ return null; } + public String getUnicodeLocaleType(String p0){ return null; } + public String getVariant(){ return null; } + public String toLanguageTag(){ return null; } + public String toString(){ return null; } + public ULocale getFallback(){ return null; } + public ULocale setKeywordValue(String p0, String p1){ return null; } + public ULocale(String p0){} + public ULocale(String p0, String p1){} + public ULocale(String p0, String p1, String p2){} + public boolean equals(Object p0){ return false; } + public boolean isRightToLeft(){ return false; } + public int compareTo(ULocale p0){ return 0; } + public int hashCode(){ return 0; } + public static Iterator getKeywords(String p0){ return null; } + public static String canonicalize(String p0){ return null; } + public static String getBaseName(String p0){ return null; } + public static String getCountry(String p0){ return null; } + public static String getDisplayCountry(String p0, String p1){ return null; } + public static String getDisplayCountry(String p0, ULocale p1){ return null; } + public static String getDisplayKeyword(String p0){ return null; } + public static String getDisplayKeyword(String p0, String p1){ return null; } + public static String getDisplayKeyword(String p0, ULocale p1){ return null; } + public static String getDisplayKeywordValue(String p0, String p1, String p2){ return null; } + public static String getDisplayKeywordValue(String p0, String p1, ULocale p2){ return null; } + public static String getDisplayLanguage(String p0, String p1){ return null; } + public static String getDisplayLanguage(String p0, ULocale p1){ return null; } + public static String getDisplayLanguageWithDialect(String p0, String p1){ return null; } + public static String getDisplayLanguageWithDialect(String p0, ULocale p1){ return null; } + public static String getDisplayName(String p0, String p1){ return null; } + public static String getDisplayName(String p0, ULocale p1){ return null; } + public static String getDisplayNameWithDialect(String p0, String p1){ return null; } + public static String getDisplayNameWithDialect(String p0, ULocale p1){ return null; } + public static String getDisplayScript(String p0, String p1){ return null; } + public static String getDisplayScript(String p0, ULocale p1){ return null; } + public static String getDisplayVariant(String p0, String p1){ return null; } + public static String getDisplayVariant(String p0, ULocale p1){ return null; } + public static String getFallback(String p0){ return null; } + public static String getISO3Country(String p0){ return null; } + public static String getISO3Language(String p0){ return null; } + public static String getKeywordValue(String p0, String p1){ return null; } + public static String getLanguage(String p0){ return null; } + public static String getName(String p0){ return null; } + public static String getScript(String p0){ return null; } + public static String getVariant(String p0){ return null; } + public static String setKeywordValue(String p0, String p1, String p2){ return null; } + public static String toLegacyKey(String p0){ return null; } + public static String toLegacyType(String p0, String p1){ return null; } + public static String toUnicodeLocaleKey(String p0){ return null; } + public static String toUnicodeLocaleType(String p0, String p1){ return null; } + public static String[] getISOCountries(){ return null; } + public static String[] getISOLanguages(){ return null; } + public static ULocale CANADA = null; + public static ULocale CANADA_FRENCH = null; + public static ULocale CHINA = null; + public static ULocale CHINESE = null; + public static ULocale ENGLISH = null; + public static ULocale FRANCE = null; + public static ULocale FRENCH = null; + public static ULocale GERMAN = null; + public static ULocale GERMANY = null; + public static ULocale ITALIAN = null; + public static ULocale ITALY = null; + public static ULocale JAPAN = null; + public static ULocale JAPANESE = null; + public static ULocale KOREA = null; + public static ULocale KOREAN = null; + public static ULocale PRC = null; + public static ULocale ROOT = null; + public static ULocale SIMPLIFIED_CHINESE = null; + public static ULocale TAIWAN = null; + public static ULocale TRADITIONAL_CHINESE = null; + public static ULocale UK = null; + public static ULocale US = null; + public static ULocale acceptLanguage(String p0, ULocale[] p1, boolean[] p2){ return null; } + public static ULocale acceptLanguage(String p0, boolean[] p1){ return null; } + public static ULocale acceptLanguage(ULocale[] p0, ULocale[] p1, boolean[] p2){ return null; } + public static ULocale acceptLanguage(ULocale[] p0, boolean[] p1){ return null; } + public static ULocale addLikelySubtags(ULocale p0){ return null; } + public static ULocale createCanonical(String p0){ return null; } + public static ULocale forLanguageTag(String p0){ return null; } + public static ULocale forLocale(Locale p0){ return null; } + public static ULocale getDefault(){ return null; } + public static ULocale getDefault(ULocale.Category p0){ return null; } + public static ULocale minimizeSubtags(ULocale p0){ return null; } + public static ULocale[] getAvailableLocales(){ return null; } + public static char PRIVATE_USE_EXTENSION = '0'; + public static char UNICODE_LOCALE_EXTENSION = '0'; + static public enum Category + { + DISPLAY, FORMAT; + private Category() {} + } +} diff --git a/java/ql/test/stubs/android/android/net/Uri.java b/java/ql/test/stubs/android/android/net/Uri.java index 371dab3cc97..ec3e601b7bb 100644 --- a/java/ql/test/stubs/android/android/net/Uri.java +++ b/java/ql/test/stubs/android/android/net/Uri.java @@ -1,5 +1,76 @@ +// Generated automatically from android.net.Uri for testing purposes + package android.net; -public class Uri { +import android.os.Parcel; +import android.os.Parcelable; +import java.io.File; +import java.util.List; +import java.util.Set; +abstract public class Uri implements Comparable, Parcelable +{ + protected Uri() {} + public List getQueryParameters(String p0){ return null; } + public Set getQueryParameterNames(){ return null; } + public String getQueryParameter(String p0){ return null; } + public Uri normalizeScheme(){ return null; } + public abstract List getPathSegments(); + public abstract String getAuthority(); + public abstract String getEncodedAuthority(); + public abstract String getEncodedFragment(); + public abstract String getEncodedPath(); + public abstract String getEncodedQuery(); + public abstract String getEncodedSchemeSpecificPart(); + public abstract String getEncodedUserInfo(); + public abstract String getFragment(); + public abstract String getHost(); + public abstract String getLastPathSegment(); + public abstract String getPath(); + public abstract String getQuery(); + public abstract String getScheme(); + public abstract String getSchemeSpecificPart(); + public abstract String getUserInfo(); + public abstract String toString(); + public abstract Uri.Builder buildUpon(); + public abstract boolean isHierarchical(); + public abstract boolean isRelative(); + public abstract int getPort(); + public boolean equals(Object p0){ return false; } + public boolean getBooleanQueryParameter(String p0, boolean p1){ return false; } + public boolean isAbsolute(){ return false; } + public boolean isOpaque(){ return false; } + public int compareTo(Uri p0){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static String decode(String p0){ return null; } + public static String encode(String p0){ return null; } + public static String encode(String p0, String p1){ return null; } + public static Uri EMPTY = null; + public static Uri fromFile(File p0){ return null; } + public static Uri fromParts(String p0, String p1, String p2){ return null; } + public static Uri parse(String p0){ return null; } + public static Uri withAppendedPath(Uri p0, String p1){ return null; } + public static void writeToParcel(Parcel p0, Uri p1){} + static public class Builder + { + public Builder(){} + public String toString(){ return null; } + public Uri build(){ return null; } + public Uri.Builder appendEncodedPath(String p0){ return null; } + public Uri.Builder appendPath(String p0){ return null; } + public Uri.Builder appendQueryParameter(String p0, String p1){ return null; } + public Uri.Builder authority(String p0){ return null; } + public Uri.Builder clearQuery(){ return null; } + public Uri.Builder encodedAuthority(String p0){ return null; } + public Uri.Builder encodedFragment(String p0){ return null; } + public Uri.Builder encodedOpaquePart(String p0){ return null; } + public Uri.Builder encodedPath(String p0){ return null; } + public Uri.Builder encodedQuery(String p0){ return null; } + public Uri.Builder fragment(String p0){ return null; } + public Uri.Builder opaquePart(String p0){ return null; } + public Uri.Builder path(String p0){ return null; } + public Uri.Builder query(String p0){ return null; } + public Uri.Builder scheme(String p0){ return null; } + } } diff --git a/java/ql/test/stubs/android/android/os/BaseBundle.java b/java/ql/test/stubs/android/android/os/BaseBundle.java new file mode 100644 index 00000000000..3cc3c310c38 --- /dev/null +++ b/java/ql/test/stubs/android/android/os/BaseBundle.java @@ -0,0 +1,43 @@ +// Generated automatically from android.os.BaseBundle for testing purposes + +package android.os; + +import android.os.PersistableBundle; +import java.util.Set; + +public class BaseBundle +{ + public Object get(String p0){ return null; } + public Set keySet(){ return null; } + public String getString(String p0){ return null; } + public String getString(String p0, String p1){ return null; } + public String[] getStringArray(String p0){ return null; } + public boolean containsKey(String p0){ return false; } + public boolean getBoolean(String p0){ return false; } + public boolean getBoolean(String p0, boolean p1){ return false; } + public boolean isEmpty(){ return false; } + public boolean[] getBooleanArray(String p0){ return null; } + public double getDouble(String p0){ return 0; } + public double getDouble(String p0, double p1){ return 0; } + public double[] getDoubleArray(String p0){ return null; } + public int getInt(String p0){ return 0; } + public int getInt(String p0, int p1){ return 0; } + public int size(){ return 0; } + public int[] getIntArray(String p0){ return null; } + public long getLong(String p0){ return 0; } + public long getLong(String p0, long p1){ return 0; } + public long[] getLongArray(String p0){ return null; } + public void clear(){} + public void putAll(PersistableBundle p0){} + public void putBoolean(String p0, boolean p1){} + public void putBooleanArray(String p0, boolean[] p1){} + public void putDouble(String p0, double p1){} + public void putDoubleArray(String p0, double[] p1){} + public void putInt(String p0, int p1){} + public void putIntArray(String p0, int[] p1){} + public void putLong(String p0, long p1){} + public void putLongArray(String p0, long[] p1){} + public void putString(String p0, String p1){} + public void putStringArray(String p0, String[] p1){} + public void remove(String p0){} +} diff --git a/java/ql/test/stubs/android/android/os/Bundle.java b/java/ql/test/stubs/android/android/os/Bundle.java index fa49a640f30..4beb1cf5dee 100644 --- a/java/ql/test/stubs/android/android/os/Bundle.java +++ b/java/ql/test/stubs/android/android/os/Bundle.java @@ -1,87 +1,86 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Generated automatically from android.os.Bundle for testing purposes package android.os; -public final class Bundle { - public Bundle() { - } - - public Bundle(ClassLoader loader) { - } - - public Bundle(int capacity) { - } - - public Bundle(Bundle b) { - } - - public static Bundle forPair(String key, String value) { - return null; - } - - public boolean setAllowFds(boolean allowFds) { - return false; - } - - public void setDefusable(boolean defusable) { - } - - public static Bundle setDefusable(Bundle bundle, boolean defusable) { - return null; - } - - @Override - public Object clone() { - return null; - } - - public Bundle deepCopy() { - return null; - } - - public void remove(String key) { - } - - public void putAll(Bundle bundle) { - } - - public int getSize() { - return 0; - } - - public boolean hasFileDescriptors() { - return false; - } - - public Bundle filterValues() { - return null; - } - - @Override - public synchronized String toString() { - return null; - } - - public synchronized String toShortString() { - return null; - } - - public String getString(String string) { - return null; - } +import android.os.BaseBundle; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PersistableBundle; +import android.util.Size; +import android.util.SizeF; +import android.util.SparseArray; +import java.io.Serializable; +import java.util.ArrayList; +public class Bundle extends BaseBundle implements Cloneable, Parcelable +{ + public ArrayList getParcelableArrayList(String p0){ return null; } + public SparseArray getSparseParcelableArray(String p0){ return null; } + public T getParcelable(String p0){ return null; } + public ArrayList getCharSequenceArrayList(String p0){ return null; } + public ArrayList getIntegerArrayList(String p0){ return null; } + public ArrayList getStringArrayList(String p0){ return null; } + public Bundle deepCopy(){ return null; } + public Bundle getBundle(String p0){ return null; } + public Bundle(){} + public Bundle(Bundle p0){} + public Bundle(ClassLoader p0){} + public Bundle(PersistableBundle p0){} + public Bundle(int p0){} + public Byte getByte(String p0, byte p1){ return null; } + public CharSequence getCharSequence(String p0){ return null; } + public CharSequence getCharSequence(String p0, CharSequence p1){ return null; } + public CharSequence[] getCharSequenceArray(String p0){ return null; } + public ClassLoader getClassLoader(){ return null; } + public IBinder getBinder(String p0){ return null; } + public Object clone(){ return null; } + public Parcelable[] getParcelableArray(String p0){ return null; } + public Serializable getSerializable(String p0){ return null; } + public Size getSize(String p0){ return null; } + public SizeF getSizeF(String p0){ return null; } + public String toString(){ return null; } + public boolean hasFileDescriptors(){ return false; } + public byte getByte(String p0){ return 0; } + public byte[] getByteArray(String p0){ return null; } + public char getChar(String p0){ return '0'; } + public char getChar(String p0, char p1){ return '0'; } + public char[] getCharArray(String p0){ return null; } + public float getFloat(String p0){ return 0; } + public float getFloat(String p0, float p1){ return 0; } + public float[] getFloatArray(String p0){ return null; } + public int describeContents(){ return 0; } + public short getShort(String p0){ return 0; } + public short getShort(String p0, short p1){ return 0; } + public short[] getShortArray(String p0){ return null; } + public static Bundle EMPTY = null; + public static Parcelable.Creator CREATOR = null; + public void clear(){} + public void putAll(Bundle p0){} + public void putBinder(String p0, IBinder p1){} + public void putBundle(String p0, Bundle p1){} + public void putByte(String p0, byte p1){} + public void putByteArray(String p0, byte[] p1){} + public void putChar(String p0, char p1){} + public void putCharArray(String p0, char[] p1){} + public void putCharSequence(String p0, CharSequence p1){} + public void putCharSequenceArray(String p0, CharSequence[] p1){} + public void putCharSequenceArrayList(String p0, ArrayList p1){} + public void putFloat(String p0, float p1){} + public void putFloatArray(String p0, float[] p1){} + public void putIntegerArrayList(String p0, ArrayList p1){} + public void putParcelable(String p0, Parcelable p1){} + public void putParcelableArray(String p0, Parcelable[] p1){} + public void putParcelableArrayList(String p0, ArrayList p1){} + public void putSerializable(String p0, Serializable p1){} + public void putShort(String p0, short p1){} + public void putShortArray(String p0, short[] p1){} + public void putSize(String p0, Size p1){} + public void putSizeF(String p0, SizeF p1){} + public void putSparseParcelableArray(String p0, SparseArray p1){} + public void putStringArrayList(String p0, ArrayList p1){} + public void readFromParcel(Parcel p0){} + public void remove(String p0){} + public void setClassLoader(ClassLoader p0){} + public void writeToParcel(Parcel p0, int p1){} } diff --git a/java/ql/test/stubs/android/android/os/CancellationSignal.java b/java/ql/test/stubs/android/android/os/CancellationSignal.java index 212e605a819..5112eb46595 100644 --- a/java/ql/test/stubs/android/android/os/CancellationSignal.java +++ b/java/ql/test/stubs/android/android/os/CancellationSignal.java @@ -1,5 +1,17 @@ +// Generated automatically from android.os.CancellationSignal for testing purposes + package android.os; -public class CancellationSignal { +public class CancellationSignal +{ + public CancellationSignal(){} + public boolean isCanceled(){ return false; } + public void cancel(){} + public void setOnCancelListener(CancellationSignal.OnCancelListener p0){} + public void throwIfCanceled(){} + static public interface OnCancelListener + { + void onCancel(); + } } diff --git a/java/ql/test/stubs/android/android/os/Handler.java b/java/ql/test/stubs/android/android/os/Handler.java new file mode 100644 index 00000000000..5d1ae91db88 --- /dev/null +++ b/java/ql/test/stubs/android/android/os/Handler.java @@ -0,0 +1,53 @@ +// Generated automatically from android.os.Handler for testing purposes + +package android.os; + +import android.os.Looper; +import android.os.Message; +import android.util.Printer; + +public class Handler +{ + public Handler(){} + public Handler(Handler.Callback p0){} + public Handler(Looper p0){} + public Handler(Looper p0, Handler.Callback p1){} + public String getMessageName(Message p0){ return null; } + public String toString(){ return null; } + public boolean sendMessageAtTime(Message p0, long p1){ return false; } + public final Looper getLooper(){ return null; } + public final Message obtainMessage(){ return null; } + public final Message obtainMessage(int p0){ return null; } + public final Message obtainMessage(int p0, Object p1){ return null; } + public final Message obtainMessage(int p0, int p1, int p2){ return null; } + public final Message obtainMessage(int p0, int p1, int p2, Object p3){ return null; } + public final boolean hasCallbacks(Runnable p0){ return false; } + public final boolean hasMessages(int p0){ return false; } + public final boolean hasMessages(int p0, Object p1){ return false; } + public final boolean post(Runnable p0){ return false; } + public final boolean postAtFrontOfQueue(Runnable p0){ return false; } + public final boolean postAtTime(Runnable p0, Object p1, long p2){ return false; } + public final boolean postAtTime(Runnable p0, long p1){ return false; } + public final boolean postDelayed(Runnable p0, Object p1, long p2){ return false; } + public final boolean postDelayed(Runnable p0, long p1){ return false; } + public final boolean sendEmptyMessage(int p0){ return false; } + public final boolean sendEmptyMessageAtTime(int p0, long p1){ return false; } + public final boolean sendEmptyMessageDelayed(int p0, long p1){ return false; } + public final boolean sendMessage(Message p0){ return false; } + public final boolean sendMessageAtFrontOfQueue(Message p0){ return false; } + public final boolean sendMessageDelayed(Message p0, long p1){ return false; } + public final void dump(Printer p0, String p1){} + public final void removeCallbacks(Runnable p0){} + public final void removeCallbacks(Runnable p0, Object p1){} + public final void removeCallbacksAndMessages(Object p0){} + public final void removeMessages(int p0){} + public final void removeMessages(int p0, Object p1){} + public static Handler createAsync(Looper p0){ return null; } + public static Handler createAsync(Looper p0, Handler.Callback p1){ return null; } + public void dispatchMessage(Message p0){} + public void handleMessage(Message p0){} + static public interface Callback + { + boolean handleMessage(Message p0); + } +} diff --git a/java/ql/test/stubs/android/android/os/IBinder.java b/java/ql/test/stubs/android/android/os/IBinder.java new file mode 100644 index 00000000000..4441468b00f --- /dev/null +++ b/java/ql/test/stubs/android/android/os/IBinder.java @@ -0,0 +1,33 @@ +// Generated automatically from android.os.IBinder for testing purposes + +package android.os; + +import android.os.IInterface; +import android.os.Parcel; +import java.io.FileDescriptor; + +public interface IBinder +{ + IInterface queryLocalInterface(String p0); + String getInterfaceDescriptor(); + boolean isBinderAlive(); + boolean pingBinder(); + boolean transact(int p0, Parcel p1, Parcel p2, int p3); + boolean unlinkToDeath(IBinder.DeathRecipient p0, int p1); + static int DUMP_TRANSACTION = 0; + static int FIRST_CALL_TRANSACTION = 0; + static int FLAG_ONEWAY = 0; + static int INTERFACE_TRANSACTION = 0; + static int LAST_CALL_TRANSACTION = 0; + static int LIKE_TRANSACTION = 0; + static int PING_TRANSACTION = 0; + static int TWEET_TRANSACTION = 0; + static int getSuggestedMaxIpcSizeBytes(){ return 0; } + static public interface DeathRecipient + { + void binderDied(); + } + void dump(FileDescriptor p0, String[] p1); + void dumpAsync(FileDescriptor p0, String[] p1); + void linkToDeath(IBinder.DeathRecipient p0, int p1); +} diff --git a/java/ql/test/stubs/android/android/os/IInterface.java b/java/ql/test/stubs/android/android/os/IInterface.java new file mode 100644 index 00000000000..ccc3ae0a62d --- /dev/null +++ b/java/ql/test/stubs/android/android/os/IInterface.java @@ -0,0 +1,10 @@ +// Generated automatically from android.os.IInterface for testing purposes + +package android.os; + +import android.os.IBinder; + +public interface IInterface +{ + IBinder asBinder(); +} diff --git a/java/ql/test/stubs/android/android/os/LocaleList.java b/java/ql/test/stubs/android/android/os/LocaleList.java new file mode 100644 index 00000000000..113f5910a49 --- /dev/null +++ b/java/ql/test/stubs/android/android/os/LocaleList.java @@ -0,0 +1,32 @@ +// Generated automatically from android.os.LocaleList for testing purposes + +package android.os; + +import android.icu.util.ULocale; +import android.os.Parcel; +import android.os.Parcelable; +import java.util.Locale; + +public class LocaleList implements Parcelable +{ + protected LocaleList() {} + public Locale get(int p0){ return null; } + public Locale getFirstMatch(String[] p0){ return null; } + public LocaleList(Locale... p0){} + public String toLanguageTags(){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public boolean isEmpty(){ return false; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public int indexOf(Locale p0){ return 0; } + public int size(){ return 0; } + public static LocaleList forLanguageTags(String p0){ return null; } + public static LocaleList getAdjustedDefault(){ return null; } + public static LocaleList getDefault(){ return null; } + public static LocaleList getEmptyLocaleList(){ return null; } + public static Parcelable.Creator CREATOR = null; + public static boolean isPseudoLocale(ULocale p0){ return false; } + public static void setDefault(LocaleList p0){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/os/Looper.java b/java/ql/test/stubs/android/android/os/Looper.java new file mode 100644 index 00000000000..bc7076c2125 --- /dev/null +++ b/java/ql/test/stubs/android/android/os/Looper.java @@ -0,0 +1,25 @@ +// Generated automatically from android.os.Looper for testing purposes + +package android.os; + +import android.os.MessageQueue; +import android.util.Printer; + +public class Looper +{ + protected Looper() {} + public MessageQueue getQueue(){ return null; } + public String toString(){ return null; } + public Thread getThread(){ return null; } + public boolean isCurrentThread(){ return false; } + public static Looper getMainLooper(){ return null; } + public static Looper myLooper(){ return null; } + public static MessageQueue myQueue(){ return null; } + public static void loop(){} + public static void prepare(){} + public static void prepareMainLooper(){} + public void dump(Printer p0, String p1){} + public void quit(){} + public void quitSafely(){} + public void setMessageLogging(Printer p0){} +} diff --git a/java/ql/test/stubs/android/android/os/Message.java b/java/ql/test/stubs/android/android/os/Message.java new file mode 100644 index 00000000000..b6f98d43430 --- /dev/null +++ b/java/ql/test/stubs/android/android/os/Message.java @@ -0,0 +1,44 @@ +// Generated automatically from android.os.Message for testing purposes + +package android.os; + +import android.os.Bundle; +import android.os.Handler; +import android.os.Messenger; +import android.os.Parcel; +import android.os.Parcelable; + +public class Message implements Parcelable +{ + public Bundle getData(){ return null; } + public Bundle peekData(){ return null; } + public Handler getTarget(){ return null; } + public Message(){} + public Messenger replyTo = null; + public Object obj = null; + public Runnable getCallback(){ return null; } + public String toString(){ return null; } + public boolean isAsynchronous(){ return false; } + public int arg1 = 0; + public int arg2 = 0; + public int describeContents(){ return 0; } + public int sendingUid = 0; + public int what = 0; + public long getWhen(){ return 0; } + public static Message obtain(){ return null; } + public static Message obtain(Handler p0){ return null; } + public static Message obtain(Handler p0, Runnable p1){ return null; } + public static Message obtain(Handler p0, int p1){ return null; } + public static Message obtain(Handler p0, int p1, Object p2){ return null; } + public static Message obtain(Handler p0, int p1, int p2, int p3){ return null; } + public static Message obtain(Handler p0, int p1, int p2, int p3, Object p4){ return null; } + public static Message obtain(Message p0){ return null; } + public static Parcelable.Creator CREATOR = null; + public void copyFrom(Message p0){} + public void recycle(){} + public void sendToTarget(){} + public void setAsynchronous(boolean p0){} + public void setData(Bundle p0){} + public void setTarget(Handler p0){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/os/MessageQueue.java b/java/ql/test/stubs/android/android/os/MessageQueue.java new file mode 100644 index 00000000000..71d1e4b9fdd --- /dev/null +++ b/java/ql/test/stubs/android/android/os/MessageQueue.java @@ -0,0 +1,26 @@ +// Generated automatically from android.os.MessageQueue for testing purposes + +package android.os; + +import java.io.FileDescriptor; + +public class MessageQueue +{ + protected void finalize(){} + public boolean isIdle(){ return false; } + public void addIdleHandler(MessageQueue.IdleHandler p0){} + public void addOnFileDescriptorEventListener(FileDescriptor p0, int p1, MessageQueue.OnFileDescriptorEventListener p2){} + public void removeIdleHandler(MessageQueue.IdleHandler p0){} + public void removeOnFileDescriptorEventListener(FileDescriptor p0){} + static public interface IdleHandler + { + boolean queueIdle(); + } + static public interface OnFileDescriptorEventListener + { + int onFileDescriptorEvents(FileDescriptor p0, int p1); + static int EVENT_ERROR = 0; + static int EVENT_INPUT = 0; + static int EVENT_OUTPUT = 0; + } +} diff --git a/java/ql/test/stubs/android/android/os/Messenger.java b/java/ql/test/stubs/android/android/os/Messenger.java new file mode 100644 index 00000000000..08d2a975153 --- /dev/null +++ b/java/ql/test/stubs/android/android/os/Messenger.java @@ -0,0 +1,25 @@ +// Generated automatically from android.os.Messenger for testing purposes + +package android.os; + +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.Parcel; +import android.os.Parcelable; + +public class Messenger implements Parcelable +{ + protected Messenger() {} + public IBinder getBinder(){ return null; } + public Messenger(Handler p0){} + public Messenger(IBinder p0){} + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Messenger readMessengerOrNullFromParcel(Parcel p0){ return null; } + public static Parcelable.Creator CREATOR = null; + public static void writeMessengerOrNullToParcel(Messenger p0, Parcel p1){} + public void send(Message p0){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/os/Parcel.java b/java/ql/test/stubs/android/android/os/Parcel.java new file mode 100644 index 00000000000..ef6dcdeb085 --- /dev/null +++ b/java/ql/test/stubs/android/android/os/Parcel.java @@ -0,0 +1,146 @@ +// Generated automatically from android.os.Parcel for testing purposes + +package android.os; + +import android.os.Bundle; +import android.os.IBinder; +import android.os.IInterface; +import android.os.ParcelFileDescriptor; +import android.os.Parcelable; +import android.os.PersistableBundle; +import android.util.ArrayMap; +import android.util.Size; +import android.util.SizeF; +import android.util.SparseArray; +import android.util.SparseBooleanArray; +import java.io.FileDescriptor; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Parcel +{ + protected Parcel() {} + protected void finalize(){} + public ArrayMap createTypedArrayMap(Parcelable.Creator p0){ return null; } + public List readParcelableList(List p0, ClassLoader p1){ return null; } + public SparseArray createTypedSparseArray(Parcelable.Creator p0){ return null; } + public T readParcelable(ClassLoader p0){ return null; } + public void writeParcelableArray(T[] p0, int p1){} + public void writeParcelableList(List p0, int p1){} + public void writeTypedArray(T[] p0, int p1){} + public void writeTypedArrayMap(ArrayMap p0, int p1){} + public void writeTypedList(List p0){} + public void writeTypedObject(T p0, int p1){} + public void writeTypedSparseArray(SparseArray p0, int p1){} + public ArrayList createTypedArrayList(Parcelable.Creator p0){ return null; } + public SparseArray readSparseArray(ClassLoader p0){ return null; } + public T readTypedObject(Parcelable.Creator p0){ return null; } + public T[] createTypedArray(Parcelable.Creator p0){ return null; } + public void readTypedArray(T[] p0, Parcelable.Creator p1){} + public void readTypedList(List p0, Parcelable.Creator p1){} + public void writeSparseArray(SparseArray p0){} + public ArrayList readArrayList(ClassLoader p0){ return null; } + public ArrayList createBinderArrayList(){ return null; } + public ArrayList createStringArrayList(){ return null; } + public Bundle readBundle(){ return null; } + public Bundle readBundle(ClassLoader p0){ return null; } + public HashMap readHashMap(ClassLoader p0){ return null; } + public IBinder readStrongBinder(){ return null; } + public IBinder[] createBinderArray(){ return null; } + public Object readValue(ClassLoader p0){ return null; } + public Object[] readArray(ClassLoader p0){ return null; } + public ParcelFileDescriptor readFileDescriptor(){ return null; } + public Parcelable.Creator readParcelableCreator(ClassLoader p0){ return null; } + public Parcelable[] readParcelableArray(ClassLoader p0){ return null; } + public PersistableBundle readPersistableBundle(){ return null; } + public PersistableBundle readPersistableBundle(ClassLoader p0){ return null; } + public Serializable readSerializable(){ return null; } + public Size readSize(){ return null; } + public SizeF readSizeF(){ return null; } + public SparseBooleanArray readSparseBooleanArray(){ return null; } + public String readString(){ return null; } + public String[] createStringArray(){ return null; } + public boolean hasFileDescriptors(){ return false; } + public boolean readBoolean(){ return false; } + public boolean[] createBooleanArray(){ return null; } + public byte readByte(){ return 0; } + public byte[] createByteArray(){ return null; } + public byte[] marshall(){ return null; } + public char[] createCharArray(){ return null; } + public double readDouble(){ return 0; } + public double[] createDoubleArray(){ return null; } + public float readFloat(){ return 0; } + public float[] createFloatArray(){ return null; } + public int dataAvail(){ return 0; } + public int dataCapacity(){ return 0; } + public int dataPosition(){ return 0; } + public int dataSize(){ return 0; } + public int readInt(){ return 0; } + public int[] createIntArray(){ return null; } + public long readLong(){ return 0; } + public long[] createLongArray(){ return null; } + public static Parcel obtain(){ return null; } + public static Parcelable.Creator STRING_CREATOR = null; + public void appendFrom(Parcel p0, int p1, int p2){} + public void enforceInterface(String p0){} + public void readBinderArray(IBinder[] p0){} + public void readBinderList(List p0){} + public void readBooleanArray(boolean[] p0){} + public void readByteArray(byte[] p0){} + public void readCharArray(char[] p0){} + public void readDoubleArray(double[] p0){} + public void readException(){} + public void readException(int p0, String p1){} + public void readFloatArray(float[] p0){} + public void readIntArray(int[] p0){} + public void readList(List p0, ClassLoader p1){} + public void readLongArray(long[] p0){} + public void readMap(Map p0, ClassLoader p1){} + public void readStringArray(String[] p0){} + public void readStringList(List p0){} + public void recycle(){} + public void setDataCapacity(int p0){} + public void setDataPosition(int p0){} + public void setDataSize(int p0){} + public void unmarshall(byte[] p0, int p1, int p2){} + public void writeArray(Object[] p0){} + public void writeBinderArray(IBinder[] p0){} + public void writeBinderList(List p0){} + public void writeBoolean(boolean p0){} + public void writeBooleanArray(boolean[] p0){} + public void writeBundle(Bundle p0){} + public void writeByte(byte p0){} + public void writeByteArray(byte[] p0){} + public void writeByteArray(byte[] p0, int p1, int p2){} + public void writeCharArray(char[] p0){} + public void writeDouble(double p0){} + public void writeDoubleArray(double[] p0){} + public void writeException(Exception p0){} + public void writeFileDescriptor(FileDescriptor p0){} + public void writeFloat(float p0){} + public void writeFloatArray(float[] p0){} + public void writeInt(int p0){} + public void writeIntArray(int[] p0){} + public void writeInterfaceToken(String p0){} + public void writeList(List p0){} + public void writeLong(long p0){} + public void writeLongArray(long[] p0){} + public void writeMap(Map p0){} + public void writeNoException(){} + public void writeParcelable(Parcelable p0, int p1){} + public void writeParcelableCreator(Parcelable p0){} + public void writePersistableBundle(PersistableBundle p0){} + public void writeSerializable(Serializable p0){} + public void writeSize(Size p0){} + public void writeSizeF(SizeF p0){} + public void writeSparseBooleanArray(SparseBooleanArray p0){} + public void writeString(String p0){} + public void writeStringArray(String[] p0){} + public void writeStringList(List p0){} + public void writeStrongBinder(IBinder p0){} + public void writeStrongInterface(IInterface p0){} + public void writeValue(Object p0){} +} diff --git a/java/ql/test/stubs/android/android/os/ParcelFileDescriptor.java b/java/ql/test/stubs/android/android/os/ParcelFileDescriptor.java index 757872bb457..05b41873d41 100644 --- a/java/ql/test/stubs/android/android/os/ParcelFileDescriptor.java +++ b/java/ql/test/stubs/android/android/os/ParcelFileDescriptor.java @@ -1,5 +1,58 @@ +// Generated automatically from android.os.ParcelFileDescriptor for testing purposes + package android.os; -public class ParcelFileDescriptor { +import android.os.Handler; +import android.os.Parcel; +import android.os.Parcelable; +import java.io.Closeable; +import java.io.File; +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.DatagramSocket; +import java.net.Socket; +public class ParcelFileDescriptor implements Closeable, Parcelable +{ + protected ParcelFileDescriptor() {} + protected void finalize(){} + public FileDescriptor getFileDescriptor(){ return null; } + public ParcelFileDescriptor dup(){ return null; } + public ParcelFileDescriptor(ParcelFileDescriptor p0){} + public String toString(){ return null; } + public boolean canDetectErrors(){ return false; } + public int describeContents(){ return 0; } + public int detachFd(){ return 0; } + public int getFd(){ return 0; } + public long getStatSize(){ return 0; } + public static ParcelFileDescriptor adoptFd(int p0){ return null; } + public static ParcelFileDescriptor dup(FileDescriptor p0){ return null; } + public static ParcelFileDescriptor fromDatagramSocket(DatagramSocket p0){ return null; } + public static ParcelFileDescriptor fromFd(int p0){ return null; } + public static ParcelFileDescriptor fromSocket(Socket p0){ return null; } + public static ParcelFileDescriptor open(File p0, int p1){ return null; } + public static ParcelFileDescriptor open(File p0, int p1, Handler p2, ParcelFileDescriptor.OnCloseListener p3){ return null; } + public static ParcelFileDescriptor wrap(ParcelFileDescriptor p0, Handler p1, ParcelFileDescriptor.OnCloseListener p2){ return null; } + public static ParcelFileDescriptor[] createPipe(){ return null; } + public static ParcelFileDescriptor[] createReliablePipe(){ return null; } + public static ParcelFileDescriptor[] createReliableSocketPair(){ return null; } + public static ParcelFileDescriptor[] createSocketPair(){ return null; } + public static Parcelable.Creator CREATOR = null; + public static int MODE_APPEND = 0; + public static int MODE_CREATE = 0; + public static int MODE_READ_ONLY = 0; + public static int MODE_READ_WRITE = 0; + public static int MODE_TRUNCATE = 0; + public static int MODE_WORLD_READABLE = 0; + public static int MODE_WORLD_WRITEABLE = 0; + public static int MODE_WRITE_ONLY = 0; + public static int parseMode(String p0){ return 0; } + public void checkError(){} + public void close(){} + public void closeWithError(String p0){} + public void writeToParcel(Parcel p0, int p1){} + static public interface OnCloseListener + { + void onClose(IOException p0); + } } diff --git a/java/ql/test/stubs/android/android/os/Parcelable.java b/java/ql/test/stubs/android/android/os/Parcelable.java new file mode 100644 index 00000000000..626061a6799 --- /dev/null +++ b/java/ql/test/stubs/android/android/os/Parcelable.java @@ -0,0 +1,18 @@ +// Generated automatically from android.os.Parcelable for testing purposes + +package android.os; + +import android.os.Parcel; + +public interface Parcelable +{ + int describeContents(); + static int CONTENTS_FILE_DESCRIPTOR = 0; + static int PARCELABLE_WRITE_RETURN_VALUE = 0; + static public interface Creator + { + T createFromParcel(Parcel p0); + T[] newArray(int p0); + } + void writeToParcel(Parcel p0, int p1); +} diff --git a/java/ql/test/stubs/android/android/os/PatternMatcher.java b/java/ql/test/stubs/android/android/os/PatternMatcher.java new file mode 100644 index 00000000000..d23c55aefd1 --- /dev/null +++ b/java/ql/test/stubs/android/android/os/PatternMatcher.java @@ -0,0 +1,24 @@ +// Generated automatically from android.os.PatternMatcher for testing purposes + +package android.os; + +import android.os.Parcel; +import android.os.Parcelable; + +public class PatternMatcher implements Parcelable +{ + protected PatternMatcher() {} + public PatternMatcher(Parcel p0){} + public PatternMatcher(String p0, int p1){} + public String toString(){ return null; } + public boolean match(String p0){ return false; } + public final String getPath(){ return null; } + public final int getType(){ return 0; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static int PATTERN_ADVANCED_GLOB = 0; + public static int PATTERN_LITERAL = 0; + public static int PATTERN_PREFIX = 0; + public static int PATTERN_SIMPLE_GLOB = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/os/PersistableBundle.java b/java/ql/test/stubs/android/android/os/PersistableBundle.java new file mode 100644 index 00000000000..45354eb5b53 --- /dev/null +++ b/java/ql/test/stubs/android/android/os/PersistableBundle.java @@ -0,0 +1,27 @@ +// Generated automatically from android.os.PersistableBundle for testing purposes + +package android.os; + +import android.os.BaseBundle; +import android.os.Parcel; +import android.os.Parcelable; +import java.io.InputStream; +import java.io.OutputStream; + +public class PersistableBundle extends BaseBundle implements Cloneable, Parcelable +{ + public Object clone(){ return null; } + public PersistableBundle deepCopy(){ return null; } + public PersistableBundle getPersistableBundle(String p0){ return null; } + public PersistableBundle(){} + public PersistableBundle(PersistableBundle p0){} + public PersistableBundle(int p0){} + public String toString(){ return null; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static PersistableBundle EMPTY = null; + public static PersistableBundle readFromStream(InputStream p0){ return null; } + public void putPersistableBundle(String p0, PersistableBundle p1){} + public void writeToParcel(Parcel p0, int p1){} + public void writeToStream(OutputStream p0){} +} diff --git a/java/ql/test/stubs/android/android/os/UserHandle.java b/java/ql/test/stubs/android/android/os/UserHandle.java new file mode 100644 index 00000000000..a2e4d596054 --- /dev/null +++ b/java/ql/test/stubs/android/android/os/UserHandle.java @@ -0,0 +1,21 @@ +// Generated automatically from android.os.UserHandle for testing purposes + +package android.os; + +import android.os.Parcel; +import android.os.Parcelable; + +public class UserHandle implements Parcelable +{ + protected UserHandle() {} + public String toString(){ return null; } + public UserHandle(Parcel p0){} + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static UserHandle getUserHandleForUid(int p0){ return null; } + public static UserHandle readFromParcel(Parcel p0){ return null; } + public static void writeToParcel(UserHandle p0, Parcel p1){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/android/android/util/AndroidException.java b/java/ql/test/stubs/android/android/util/AndroidException.java new file mode 100644 index 00000000000..aee2fef29f7 --- /dev/null +++ b/java/ql/test/stubs/android/android/util/AndroidException.java @@ -0,0 +1,12 @@ +// Generated automatically from android.util.AndroidException for testing purposes + +package android.util; + + +public class AndroidException extends Exception +{ + public AndroidException(){} + public AndroidException(Exception p0){} + public AndroidException(String p0){} + public AndroidException(String p0, Throwable p1){} +} diff --git a/java/ql/test/stubs/android/android/util/ArrayMap.java b/java/ql/test/stubs/android/android/util/ArrayMap.java new file mode 100644 index 00000000000..55c7adebea7 --- /dev/null +++ b/java/ql/test/stubs/android/android/util/ArrayMap.java @@ -0,0 +1,40 @@ +// Generated automatically from android.util.ArrayMap for testing purposes + +package android.util; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +public class ArrayMap implements Map +{ + public ArrayMap(){} + public ArrayMap(ArrayMap p0){} + public ArrayMap(int p0){} + public Collection values(){ return null; } + public K keyAt(int p0){ return null; } + public Set keySet(){ return null; } + public Set> entrySet(){ return null; } + public String toString(){ return null; } + public V get(Object p0){ return null; } + public V put(K p0, V p1){ return null; } + public V remove(Object p0){ return null; } + public V removeAt(int p0){ return null; } + public V setValueAt(int p0, V p1){ return null; } + public V valueAt(int p0){ return null; } + public boolean containsAll(Collection p0){ return false; } + public boolean containsKey(Object p0){ return false; } + public boolean containsValue(Object p0){ return false; } + public boolean equals(Object p0){ return false; } + public boolean isEmpty(){ return false; } + public boolean removeAll(Collection p0){ return false; } + public boolean retainAll(Collection p0){ return false; } + public int hashCode(){ return 0; } + public int indexOfKey(Object p0){ return 0; } + public int indexOfValue(Object p0){ return 0; } + public int size(){ return 0; } + public void clear(){} + public void ensureCapacity(int p0){} + public void putAll(ArrayMap p0){} + public void putAll(Map p0){} +} diff --git a/java/ql/test/stubs/android/android/util/AttributeSet.java b/java/ql/test/stubs/android/android/util/AttributeSet.java index 27159b91485..04bdbe6b8d2 100644 --- a/java/ql/test/stubs/android/android/util/AttributeSet.java +++ b/java/ql/test/stubs/android/android/util/AttributeSet.java @@ -1,74 +1,31 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Generated automatically from android.util.AttributeSet for testing purposes package android.util; -public interface AttributeSet { - public int getAttributeCount(); - - default String getAttributeNamespace (int index) { - return null; - } - - public String getAttributeName(int index); - - public String getAttributeValue(int index); - - public String getAttributeValue(String namespace, String name); - - public String getPositionDescription(); - - public int getAttributeNameResource(int index); - - public int getAttributeListValue(String namespace, String attribute, - String[] options, int defaultValue); - - public boolean getAttributeBooleanValue(String namespace, String attribute, - boolean defaultValue); - - public int getAttributeResourceValue(String namespace, String attribute, - int defaultValue); - - public int getAttributeIntValue(String namespace, String attribute, - int defaultValue); - - public int getAttributeUnsignedIntValue(String namespace, String attribute, - int defaultValue); - - public float getAttributeFloatValue(String namespace, String attribute, - float defaultValue); - - public int getAttributeListValue(int index, String[] options, int defaultValue); - - public boolean getAttributeBooleanValue(int index, boolean defaultValue); - - public int getAttributeResourceValue(int index, int defaultValue); - - public int getAttributeIntValue(int index, int defaultValue); - - public int getAttributeUnsignedIntValue(int index, int defaultValue); - - public float getAttributeFloatValue(int index, float defaultValue); - - public String getIdAttribute(); - - public String getClassAttribute(); - - public int getIdAttributeResourceValue(int defaultValue); - - public int getStyleAttribute(); +public interface AttributeSet +{ + String getAttributeName(int p0); + String getAttributeValue(String p0, String p1); + String getAttributeValue(int p0); + String getClassAttribute(); + String getIdAttribute(); + String getPositionDescription(); + boolean getAttributeBooleanValue(String p0, String p1, boolean p2); + boolean getAttributeBooleanValue(int p0, boolean p1); + default String getAttributeNamespace(int p0){ return null; } + float getAttributeFloatValue(String p0, String p1, float p2); + float getAttributeFloatValue(int p0, float p1); + int getAttributeCount(); + int getAttributeIntValue(String p0, String p1, int p2); + int getAttributeIntValue(int p0, int p1); + int getAttributeListValue(String p0, String p1, String[] p2, int p3); + int getAttributeListValue(int p0, String[] p1, int p2); + int getAttributeNameResource(int p0); + int getAttributeResourceValue(String p0, String p1, int p2); + int getAttributeResourceValue(int p0, int p1); + int getAttributeUnsignedIntValue(String p0, String p1, int p2); + int getAttributeUnsignedIntValue(int p0, int p1); + int getIdAttributeResourceValue(int p0); + int getStyleAttribute(); } diff --git a/java/ql/test/stubs/android/android/util/DisplayMetrics.java b/java/ql/test/stubs/android/android/util/DisplayMetrics.java new file mode 100644 index 00000000000..fe64b670921 --- /dev/null +++ b/java/ql/test/stubs/android/android/util/DisplayMetrics.java @@ -0,0 +1,46 @@ +// Generated automatically from android.util.DisplayMetrics for testing purposes + +package android.util; + + +public class DisplayMetrics +{ + public DisplayMetrics(){} + public String toString(){ return null; } + public boolean equals(DisplayMetrics p0){ return false; } + public boolean equals(Object p0){ return false; } + public float density = 0; + public float scaledDensity = 0; + public float xdpi = 0; + public float ydpi = 0; + public int densityDpi = 0; + public int hashCode(){ return 0; } + public int heightPixels = 0; + public int widthPixels = 0; + public static int DENSITY_140 = 0; + public static int DENSITY_180 = 0; + public static int DENSITY_200 = 0; + public static int DENSITY_220 = 0; + public static int DENSITY_260 = 0; + public static int DENSITY_280 = 0; + public static int DENSITY_300 = 0; + public static int DENSITY_340 = 0; + public static int DENSITY_360 = 0; + public static int DENSITY_400 = 0; + public static int DENSITY_420 = 0; + public static int DENSITY_440 = 0; + public static int DENSITY_450 = 0; + public static int DENSITY_560 = 0; + public static int DENSITY_600 = 0; + public static int DENSITY_DEFAULT = 0; + public static int DENSITY_DEVICE_STABLE = 0; + public static int DENSITY_HIGH = 0; + public static int DENSITY_LOW = 0; + public static int DENSITY_MEDIUM = 0; + public static int DENSITY_TV = 0; + public static int DENSITY_XHIGH = 0; + public static int DENSITY_XXHIGH = 0; + public static int DENSITY_XXXHIGH = 0; + public void setTo(DisplayMetrics p0){} + public void setToDefaults(){} +} diff --git a/java/ql/test/stubs/android/android/util/Pair.java b/java/ql/test/stubs/android/android/util/Pair.java new file mode 100644 index 00000000000..84a73730e05 --- /dev/null +++ b/java/ql/test/stubs/android/android/util/Pair.java @@ -0,0 +1,16 @@ +// Generated automatically from android.util.Pair for testing purposes + +package android.util; + + +public class Pair +{ + protected Pair() {} + public Pair(F p0, S p1){} + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public final F first = null; + public final S second = null; + public int hashCode(){ return 0; } + public static Pair create(A p0, B p1){ return null; } +} diff --git a/java/ql/test/stubs/android/android/util/Printer.java b/java/ql/test/stubs/android/android/util/Printer.java new file mode 100644 index 00000000000..e6b67a13572 --- /dev/null +++ b/java/ql/test/stubs/android/android/util/Printer.java @@ -0,0 +1,9 @@ +// Generated automatically from android.util.Printer for testing purposes + +package android.util; + + +public interface Printer +{ + void println(String p0); +} diff --git a/java/ql/test/stubs/android/android/util/Size.java b/java/ql/test/stubs/android/android/util/Size.java new file mode 100644 index 00000000000..9cd6edc8555 --- /dev/null +++ b/java/ql/test/stubs/android/android/util/Size.java @@ -0,0 +1,16 @@ +// Generated automatically from android.util.Size for testing purposes + +package android.util; + + +public class Size +{ + protected Size() {} + public Size(int p0, int p1){} + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public int getHeight(){ return 0; } + public int getWidth(){ return 0; } + public int hashCode(){ return 0; } + public static Size parseSize(String p0){ return null; } +} diff --git a/java/ql/test/stubs/android/android/util/SizeF.java b/java/ql/test/stubs/android/android/util/SizeF.java new file mode 100644 index 00000000000..16558fd17e6 --- /dev/null +++ b/java/ql/test/stubs/android/android/util/SizeF.java @@ -0,0 +1,16 @@ +// Generated automatically from android.util.SizeF for testing purposes + +package android.util; + + +public class SizeF +{ + protected SizeF() {} + public SizeF(float p0, float p1){} + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public float getHeight(){ return 0; } + public float getWidth(){ return 0; } + public int hashCode(){ return 0; } + public static SizeF parseSizeF(String p0){ return null; } +} diff --git a/java/ql/test/stubs/android/android/util/SparseArray.java b/java/ql/test/stubs/android/android/util/SparseArray.java new file mode 100644 index 00000000000..11f3ffa5427 --- /dev/null +++ b/java/ql/test/stubs/android/android/util/SparseArray.java @@ -0,0 +1,28 @@ +// Generated automatically from android.util.SparseArray for testing purposes + +package android.util; + + +public class SparseArray implements Cloneable +{ + public E get(int p0){ return null; } + public E get(int p0, E p1){ return null; } + public E valueAt(int p0){ return null; } + public SparseArray(){} + public SparseArray(int p0){} + public SparseArray clone(){ return null; } + public String toString(){ return null; } + public boolean contains(int p0){ return false; } + public int indexOfKey(int p0){ return 0; } + public int indexOfValue(E p0){ return 0; } + public int keyAt(int p0){ return 0; } + public int size(){ return 0; } + public void append(int p0, E p1){} + public void clear(){} + public void delete(int p0){} + public void put(int p0, E p1){} + public void remove(int p0){} + public void removeAt(int p0){} + public void removeAtRange(int p0, int p1){} + public void setValueAt(int p0, E p1){} +} diff --git a/java/ql/test/stubs/android/android/util/SparseBooleanArray.java b/java/ql/test/stubs/android/android/util/SparseBooleanArray.java new file mode 100644 index 00000000000..3c84c18e71b --- /dev/null +++ b/java/ql/test/stubs/android/android/util/SparseBooleanArray.java @@ -0,0 +1,27 @@ +// Generated automatically from android.util.SparseBooleanArray for testing purposes + +package android.util; + + +public class SparseBooleanArray implements Cloneable +{ + public SparseBooleanArray clone(){ return null; } + public SparseBooleanArray(){} + public SparseBooleanArray(int p0){} + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public boolean get(int p0){ return false; } + public boolean get(int p0, boolean p1){ return false; } + public boolean valueAt(int p0){ return false; } + public int hashCode(){ return 0; } + public int indexOfKey(int p0){ return 0; } + public int indexOfValue(boolean p0){ return 0; } + public int keyAt(int p0){ return 0; } + public int size(){ return 0; } + public void append(int p0, boolean p1){} + public void clear(){} + public void delete(int p0){} + public void put(int p0, boolean p1){} + public void removeAt(int p0){} + public void setValueAt(int p0, boolean p1){} +} diff --git a/java/ql/test/stubs/android/android/util/TypedValue.java b/java/ql/test/stubs/android/android/util/TypedValue.java new file mode 100644 index 00000000000..269ab8bcf67 --- /dev/null +++ b/java/ql/test/stubs/android/android/util/TypedValue.java @@ -0,0 +1,73 @@ +// Generated automatically from android.util.TypedValue for testing purposes + +package android.util; + +import android.util.DisplayMetrics; + +public class TypedValue +{ + public CharSequence string = null; + public String toString(){ return null; } + public TypedValue(){} + public boolean isColorType(){ return false; } + public final CharSequence coerceToString(){ return null; } + public final float getFloat(){ return 0; } + public float getDimension(DisplayMetrics p0){ return 0; } + public float getFraction(float p0, float p1){ return 0; } + public int assetCookie = 0; + public int changingConfigurations = 0; + public int data = 0; + public int density = 0; + public int getComplexUnit(){ return 0; } + public int resourceId = 0; + public int sourceResourceId = 0; + public int type = 0; + public static String coerceToString(int p0, int p1){ return null; } + public static float applyDimension(int p0, float p1, DisplayMetrics p2){ return 0; } + public static float complexToDimension(int p0, DisplayMetrics p1){ return 0; } + public static float complexToFloat(int p0){ return 0; } + public static float complexToFraction(int p0, float p1, float p2){ return 0; } + public static int COMPLEX_MANTISSA_MASK = 0; + public static int COMPLEX_MANTISSA_SHIFT = 0; + public static int COMPLEX_RADIX_0p23 = 0; + public static int COMPLEX_RADIX_16p7 = 0; + public static int COMPLEX_RADIX_23p0 = 0; + public static int COMPLEX_RADIX_8p15 = 0; + public static int COMPLEX_RADIX_MASK = 0; + public static int COMPLEX_RADIX_SHIFT = 0; + public static int COMPLEX_UNIT_DIP = 0; + public static int COMPLEX_UNIT_FRACTION = 0; + public static int COMPLEX_UNIT_FRACTION_PARENT = 0; + public static int COMPLEX_UNIT_IN = 0; + public static int COMPLEX_UNIT_MASK = 0; + public static int COMPLEX_UNIT_MM = 0; + public static int COMPLEX_UNIT_PT = 0; + public static int COMPLEX_UNIT_PX = 0; + public static int COMPLEX_UNIT_SHIFT = 0; + public static int COMPLEX_UNIT_SP = 0; + public static int DATA_NULL_EMPTY = 0; + public static int DATA_NULL_UNDEFINED = 0; + public static int DENSITY_DEFAULT = 0; + public static int DENSITY_NONE = 0; + public static int TYPE_ATTRIBUTE = 0; + public static int TYPE_DIMENSION = 0; + public static int TYPE_FIRST_COLOR_INT = 0; + public static int TYPE_FIRST_INT = 0; + public static int TYPE_FLOAT = 0; + public static int TYPE_FRACTION = 0; + public static int TYPE_INT_BOOLEAN = 0; + public static int TYPE_INT_COLOR_ARGB4 = 0; + public static int TYPE_INT_COLOR_ARGB8 = 0; + public static int TYPE_INT_COLOR_RGB4 = 0; + public static int TYPE_INT_COLOR_RGB8 = 0; + public static int TYPE_INT_DEC = 0; + public static int TYPE_INT_HEX = 0; + public static int TYPE_LAST_COLOR_INT = 0; + public static int TYPE_LAST_INT = 0; + public static int TYPE_NULL = 0; + public static int TYPE_REFERENCE = 0; + public static int TYPE_STRING = 0; + public static int complexToDimensionPixelOffset(int p0, DisplayMetrics p1){ return 0; } + public static int complexToDimensionPixelSize(int p0, DisplayMetrics p1){ return 0; } + public void setTo(TypedValue p0){} +} diff --git a/java/ql/test/stubs/android/android/view/Display.java b/java/ql/test/stubs/android/android/view/Display.java new file mode 100644 index 00000000000..8c8061f00cb --- /dev/null +++ b/java/ql/test/stubs/android/android/view/Display.java @@ -0,0 +1,89 @@ +// Generated automatically from android.view.Display for testing purposes + +package android.view; + +import android.graphics.ColorSpace; +import android.graphics.Point; +import android.graphics.Rect; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.DisplayMetrics; +import android.view.DisplayCutout; + +public class Display +{ + public ColorSpace getPreferredWideGamutColorSpace(){ return null; } + public Display.HdrCapabilities getHdrCapabilities(){ return null; } + public Display.Mode getMode(){ return null; } + public Display.Mode[] getSupportedModes(){ return null; } + public DisplayCutout getCutout(){ return null; } + public String getName(){ return null; } + public String toString(){ return null; } + public boolean isHdr(){ return false; } + public boolean isMinimalPostProcessingSupported(){ return false; } + public boolean isValid(){ return false; } + public boolean isWideColorGamut(){ return false; } + public float getRefreshRate(){ return 0; } + public float[] getSupportedRefreshRates(){ return null; } + public int getDisplayId(){ return 0; } + public int getFlags(){ return 0; } + public int getHeight(){ return 0; } + public int getOrientation(){ return 0; } + public int getPixelFormat(){ return 0; } + public int getRotation(){ return 0; } + public int getState(){ return 0; } + public int getWidth(){ return 0; } + public long getAppVsyncOffsetNanos(){ return 0; } + public long getPresentationDeadlineNanos(){ return 0; } + public static int DEFAULT_DISPLAY = 0; + public static int FLAG_PRESENTATION = 0; + public static int FLAG_PRIVATE = 0; + public static int FLAG_ROUND = 0; + public static int FLAG_SECURE = 0; + public static int FLAG_SUPPORTS_PROTECTED_BUFFERS = 0; + public static int INVALID_DISPLAY = 0; + public static int STATE_DOZE = 0; + public static int STATE_DOZE_SUSPEND = 0; + public static int STATE_OFF = 0; + public static int STATE_ON = 0; + public static int STATE_ON_SUSPEND = 0; + public static int STATE_UNKNOWN = 0; + public static int STATE_VR = 0; + public void getCurrentSizeRange(Point p0, Point p1){} + public void getMetrics(DisplayMetrics p0){} + public void getRealMetrics(DisplayMetrics p0){} + public void getRealSize(Point p0){} + public void getRectSize(Rect p0){} + public void getSize(Point p0){} + static public class HdrCapabilities implements Parcelable + { + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public float getDesiredMaxAverageLuminance(){ return 0; } + public float getDesiredMaxLuminance(){ return 0; } + public float getDesiredMinLuminance(){ return 0; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public int[] getSupportedHdrTypes(){ return null; } + public static Parcelable.Creator CREATOR = null; + public static float INVALID_LUMINANCE = 0; + public static int HDR_TYPE_DOLBY_VISION = 0; + public static int HDR_TYPE_HDR10 = 0; + public static int HDR_TYPE_HDR10_PLUS = 0; + public static int HDR_TYPE_HLG = 0; + public void writeToParcel(Parcel p0, int p1){} + } + static public class Mode implements Parcelable + { + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public float getRefreshRate(){ return 0; } + public int describeContents(){ return 0; } + public int getModeId(){ return 0; } + public int getPhysicalHeight(){ return 0; } + public int getPhysicalWidth(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} + } +} diff --git a/java/ql/test/stubs/android/android/view/DisplayCutout.java b/java/ql/test/stubs/android/android/view/DisplayCutout.java new file mode 100644 index 00000000000..90d3ecf0b44 --- /dev/null +++ b/java/ql/test/stubs/android/android/view/DisplayCutout.java @@ -0,0 +1,28 @@ +// Generated automatically from android.view.DisplayCutout for testing purposes + +package android.view; + +import android.graphics.Insets; +import android.graphics.Rect; +import java.util.List; + +public class DisplayCutout +{ + protected DisplayCutout() {} + public DisplayCutout(Insets p0, Rect p1, Rect p2, Rect p3, Rect p4){} + public DisplayCutout(Insets p0, Rect p1, Rect p2, Rect p3, Rect p4, Insets p5){} + public DisplayCutout(Rect p0, List p1){} + public Insets getWaterfallInsets(){ return null; } + public List getBoundingRects(){ return null; } + public Rect getBoundingRectBottom(){ return null; } + public Rect getBoundingRectLeft(){ return null; } + public Rect getBoundingRectRight(){ return null; } + public Rect getBoundingRectTop(){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public int getSafeInsetBottom(){ return 0; } + public int getSafeInsetLeft(){ return 0; } + public int getSafeInsetRight(){ return 0; } + public int getSafeInsetTop(){ return 0; } + public int hashCode(){ return 0; } +} diff --git a/java/ql/test/stubs/android/org/xmlpull/v1/XmlPullParser.java b/java/ql/test/stubs/android/org/xmlpull/v1/XmlPullParser.java new file mode 100644 index 00000000000..c693a5f6289 --- /dev/null +++ b/java/ql/test/stubs/android/org/xmlpull/v1/XmlPullParser.java @@ -0,0 +1,64 @@ +// Generated automatically from org.xmlpull.v1.XmlPullParser for testing purposes + +package org.xmlpull.v1; + +import java.io.InputStream; +import java.io.Reader; + +public interface XmlPullParser +{ + Object getProperty(String p0); + String getAttributeName(int p0); + String getAttributeNamespace(int p0); + String getAttributePrefix(int p0); + String getAttributeType(int p0); + String getAttributeValue(String p0, String p1); + String getAttributeValue(int p0); + String getInputEncoding(); + String getName(); + String getNamespace(); + String getNamespace(String p0); + String getNamespacePrefix(int p0); + String getNamespaceUri(int p0); + String getPositionDescription(); + String getPrefix(); + String getText(); + String nextText(); + boolean getFeature(String p0); + boolean isAttributeDefault(int p0); + boolean isEmptyElementTag(); + boolean isWhitespace(); + char[] getTextCharacters(int[] p0); + int getAttributeCount(); + int getColumnNumber(); + int getDepth(); + int getEventType(); + int getLineNumber(); + int getNamespaceCount(int p0); + int next(); + int nextTag(); + int nextToken(); + static String FEATURE_PROCESS_DOCDECL = null; + static String FEATURE_PROCESS_NAMESPACES = null; + static String FEATURE_REPORT_NAMESPACE_ATTRIBUTES = null; + static String FEATURE_VALIDATION = null; + static String NO_NAMESPACE = null; + static String[] TYPES = null; + static int CDSECT = 0; + static int COMMENT = 0; + static int DOCDECL = 0; + static int END_DOCUMENT = 0; + static int END_TAG = 0; + static int ENTITY_REF = 0; + static int IGNORABLE_WHITESPACE = 0; + static int PROCESSING_INSTRUCTION = 0; + static int START_DOCUMENT = 0; + static int START_TAG = 0; + static int TEXT = 0; + void defineEntityReplacementText(String p0, String p1); + void require(int p0, String p1, String p2); + void setFeature(String p0, boolean p1); + void setInput(InputStream p0, String p1); + void setInput(Reader p0); + void setProperty(String p0, Object p1); +} diff --git a/java/ql/test/stubs/android/org/xmlpull/v1/XmlSerializer.java b/java/ql/test/stubs/android/org/xmlpull/v1/XmlSerializer.java new file mode 100644 index 00000000000..f0a985adebe --- /dev/null +++ b/java/ql/test/stubs/android/org/xmlpull/v1/XmlSerializer.java @@ -0,0 +1,35 @@ +// Generated automatically from org.xmlpull.v1.XmlSerializer for testing purposes + +package org.xmlpull.v1; + +import java.io.OutputStream; +import java.io.Writer; + +public interface XmlSerializer +{ + Object getProperty(String p0); + String getName(); + String getNamespace(); + String getPrefix(String p0, boolean p1); + XmlSerializer attribute(String p0, String p1, String p2); + XmlSerializer endTag(String p0, String p1); + XmlSerializer startTag(String p0, String p1); + XmlSerializer text(String p0); + XmlSerializer text(char[] p0, int p1, int p2); + boolean getFeature(String p0); + int getDepth(); + void cdsect(String p0); + void comment(String p0); + void docdecl(String p0); + void endDocument(); + void entityRef(String p0); + void flush(); + void ignorableWhitespace(String p0); + void processingInstruction(String p0); + void setFeature(String p0, boolean p1); + void setOutput(OutputStream p0, String p1); + void setOutput(Writer p0); + void setPrefix(String p0, String p1); + void setProperty(String p0, Object p1); + void startDocument(String p0, Boolean p1); +} diff --git a/java/ql/test/stubs/azure-sdk-for-java/com/azure/core/credential/TokenCredential.java b/java/ql/test/stubs/azure-sdk-for-java/com/azure/core/credential/TokenCredential.java new file mode 100644 index 00000000000..bacaa46a251 --- /dev/null +++ b/java/ql/test/stubs/azure-sdk-for-java/com/azure/core/credential/TokenCredential.java @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.core.credential; + +/** + * The interface for credentials that can provide a token. + */ +public interface TokenCredential { +} \ No newline at end of file diff --git a/java/ql/test/stubs/azure-sdk-for-java/com/azure/identity/AadCredentialBuilderBase.java b/java/ql/test/stubs/azure-sdk-for-java/com/azure/identity/AadCredentialBuilderBase.java new file mode 100644 index 00000000000..bedaab87e69 --- /dev/null +++ b/java/ql/test/stubs/azure-sdk-for-java/com/azure/identity/AadCredentialBuilderBase.java @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.identity; + +/** + * The base class for credential builders that allow specifying a client ID and tenant ID for an Azure Active Directory. + * @param the type of the credential builder + */ +public abstract class AadCredentialBuilderBase> extends CredentialBuilderBase { + + /** + * Specifies the Azure Active Directory endpoint to acquire tokens. + * @param authorityHost the Azure Active Directory endpoint + * @return An updated instance of this builder with the authority host set as specified. + */ + @SuppressWarnings("unchecked") + public T authorityHost(String authorityHost) { + return null; + } + + /** + * Sets the client ID of the application. + * + * @param clientId the client ID of the application. + * @return An updated instance of this builder with the client id set as specified. + */ + @SuppressWarnings("unchecked") + public T clientId(String clientId) { + return null; + } + + /** + * Sets the tenant ID of the application. + * + * @param tenantId the tenant ID of the application. + * @return An updated instance of this builder with the tenant id set as specified. + */ + @SuppressWarnings("unchecked") + public T tenantId(String tenantId) { + return null; + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/azure-sdk-for-java/com/azure/identity/ClientSecretCredential.java b/java/ql/test/stubs/azure-sdk-for-java/com/azure/identity/ClientSecretCredential.java new file mode 100644 index 00000000000..9b8182451b8 --- /dev/null +++ b/java/ql/test/stubs/azure-sdk-for-java/com/azure/identity/ClientSecretCredential.java @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.identity; + +import com.azure.core.credential.TokenCredential; + +/** + * An AAD credential that acquires a token with a client secret for an AAD application. + * + *

    Sample: Construct a simple ClientSecretCredential

    + * {@codesnippet com.azure.identity.credential.clientsecretcredential.construct} + * + *

    Sample: Construct a ClientSecretCredential behind a proxy

    + * {@codesnippet com.azure.identity.credential.clientsecretcredential.constructwithproxy} + */ +public class ClientSecretCredential implements TokenCredential { +} diff --git a/java/ql/test/stubs/azure-sdk-for-java/com/azure/identity/ClientSecretCredentialBuilder.java b/java/ql/test/stubs/azure-sdk-for-java/com/azure/identity/ClientSecretCredentialBuilder.java new file mode 100644 index 00000000000..85ab73c3060 --- /dev/null +++ b/java/ql/test/stubs/azure-sdk-for-java/com/azure/identity/ClientSecretCredentialBuilder.java @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.identity; + +/** + * Fluent credential builder for instantiating a {@link ClientSecretCredential}. + * + * @see ClientSecretCredential + */ +public class ClientSecretCredentialBuilder extends AadCredentialBuilderBase { + /** + * Sets the client secret for the authentication. + * @param clientSecret the secret value of the AAD application. + * @return An updated instance of this builder. + */ + public ClientSecretCredentialBuilder clientSecret(String clientSecret) { + return null; + } + + /** + * Enables the shared token cache which is disabled by default. If enabled, the credential will store tokens + * in a cache persisted to the machine, protected to the current user, which can be shared by other credentials + * and processes. + * + * @return An updated instance of this builder. + */ + ClientSecretCredentialBuilder enablePersistentCache() { + return null; + } + + /** + * Allows to use an unprotected file specified by cacheFileLocation() instead of + * Gnome keyring on Linux. This is restricted by default. + * + * @return An updated instance of this builder. + */ + ClientSecretCredentialBuilder allowUnencryptedCache() { + return null; + } + + /** + * Configures the persistent shared token cache options and enables the persistent token cache which is disabled + * by default. If configured, the credential will store tokens in a cache persisted to the machine, protected to + * the current user, which can be shared by other credentials and processes. + * + * @param tokenCachePersistenceOptions the token cache configuration options + * @return An updated instance of this builder with the token cache options configured. + */ + public ClientSecretCredentialBuilder tokenCachePersistenceOptions(TokenCachePersistenceOptions + tokenCachePersistenceOptions) { + return null; + } + + /** + * Creates a new {@link ClientCertificateCredential} with the current configurations. + * + * @return a {@link ClientSecretCredentialBuilder} with the current configurations. + */ + public ClientSecretCredential build() { + return null; + } +} diff --git a/java/ql/test/stubs/azure-sdk-for-java/com/azure/identity/CredentialBuilderBase.java b/java/ql/test/stubs/azure-sdk-for-java/com/azure/identity/CredentialBuilderBase.java new file mode 100644 index 00000000000..c1210942ea0 --- /dev/null +++ b/java/ql/test/stubs/azure-sdk-for-java/com/azure/identity/CredentialBuilderBase.java @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.identity; + +/** + * The base class for all the credential builders. + * @param the type of the credential builder + */ +public abstract class CredentialBuilderBase> { + CredentialBuilderBase() { + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/azure-sdk-for-java/com/azure/identity/TokenCachePersistenceOptions.java b/java/ql/test/stubs/azure-sdk-for-java/com/azure/identity/TokenCachePersistenceOptions.java new file mode 100644 index 00000000000..d5580fb05f6 --- /dev/null +++ b/java/ql/test/stubs/azure-sdk-for-java/com/azure/identity/TokenCachePersistenceOptions.java @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.identity; + +/** + * Represents the Persistence Token Cache options used to setup the persistent access token cache. + */ +public final class TokenCachePersistenceOptions { + + /** + * Allows to use an unprotected file specified by cacheFileLocation() instead of + * Gnome keyring on Linux. This is restricted by default. For other platforms this setting currently doesn't apply. + * + * @param unencryptedStorageAllowed The flag indicating if unencrypted storage is allowed for the cache or not. + * @return An updated instance of the options bag. + */ + public TokenCachePersistenceOptions setUnencryptedStorageAllowed(boolean unencryptedStorageAllowed) { + return null; + } + + /** + * Gets the status whether unencrypted storage is allowed for the persistent token cache. + * + * @return The status indicating if unencrypted storage is allowed for the persistent token cache. + */ + public boolean isUnencryptedStorageAllowed() { + return false; + } + + /** + * Set the name uniquely identifying the cache. + * + * @param name the name of the cache + * @return the updated instance of the cache. + */ + public TokenCachePersistenceOptions setName(String name) { + return null; + } + + /** + * Get the name uniquely identifying the cache. + * + * @return the name of the cache. + */ + public String getName() { + return null; + } +} diff --git a/java/ql/test/stubs/azure-sdk-for-java/com/azure/identity/UsernamePasswordCredential.java b/java/ql/test/stubs/azure-sdk-for-java/com/azure/identity/UsernamePasswordCredential.java new file mode 100644 index 00000000000..5cb9463eb95 --- /dev/null +++ b/java/ql/test/stubs/azure-sdk-for-java/com/azure/identity/UsernamePasswordCredential.java @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.identity; + +import com.azure.core.credential.TokenCredential; + +/** + * An AAD credential that acquires a token with a username and a password. Users with 2FA/MFA (Multi-factored auth) + * turned on will not be able to use this credential. Please use {@link DeviceCodeCredential} or {@link + * InteractiveBrowserCredential} instead, or create a service principal if you want to authenticate silently. + */ +public class UsernamePasswordCredential implements TokenCredential { +} diff --git a/java/ql/test/stubs/azure-sdk-for-java/com/azure/identity/UsernamePasswordCredentialBuilder.java b/java/ql/test/stubs/azure-sdk-for-java/com/azure/identity/UsernamePasswordCredentialBuilder.java new file mode 100644 index 00000000000..b6aa411ed84 --- /dev/null +++ b/java/ql/test/stubs/azure-sdk-for-java/com/azure/identity/UsernamePasswordCredentialBuilder.java @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.identity; + +import com.azure.security.keyvault.secrets.SecretClient; + +/** + * Fluent credential builder for instantiating a {@link UsernamePasswordCredential}. + * + * @see UsernamePasswordCredential + */ +public class UsernamePasswordCredentialBuilder extends AadCredentialBuilderBase { + /** + * Sets the username of the user. + * @param username the username of the user + * @return the UserCredentialBuilder itself + */ + public UsernamePasswordCredentialBuilder username(String username) { + return null; + } + + /** + * Sets the password of the user. + * @param password the password of the user + * @return the UserCredentialBuilder itself + */ + public UsernamePasswordCredentialBuilder password(String password) { + return null; + } + + /** + * Configures the persistent shared token cache options and enables the persistent token cache which is disabled + * by default. If configured, the credential will store tokens in a cache persisted to the machine, protected to + * the current user, which can be shared by other credentials and processes. + * + * @param tokenCachePersistenceOptions the token cache configuration options + * @return An updated instance of this builder with the token cache options configured. + */ + public UsernamePasswordCredentialBuilder tokenCachePersistenceOptions(TokenCachePersistenceOptions + tokenCachePersistenceOptions) { + return null; + } + + /** + * Allows to use an unprotected file specified by cacheFileLocation() instead of + * Gnome keyring on Linux. This is restricted by default. + * + * @return An updated instance of this builder. + */ + UsernamePasswordCredentialBuilder allowUnencryptedCache() { + return null; + } + + /** + * Enables the shared token cache which is disabled by default. If enabled, the credential will store tokens + * in a cache persisted to the machine, protected to the current user, which can be shared by other credentials + * and processes. + * + * @return An updated instance of this builder with if the shared token cache enabled specified. + */ + UsernamePasswordCredentialBuilder enablePersistentCache() { + return null; + } + + /** + * Creates a new {@link UsernamePasswordCredential} with the current configurations. + * + * @return a {@link UsernamePasswordCredential} with the current configurations. + */ + public UsernamePasswordCredential build() { + return null; + } +} diff --git a/java/ql/test/stubs/azure-sdk-for-java/com/azure/security/keyvault/secrets/SecretClient.java b/java/ql/test/stubs/azure-sdk-for-java/com/azure/security/keyvault/secrets/SecretClient.java new file mode 100644 index 00000000000..94cdc7d1be7 --- /dev/null +++ b/java/ql/test/stubs/azure-sdk-for-java/com/azure/security/keyvault/secrets/SecretClient.java @@ -0,0 +1,155 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.secrets; + +import com.azure.security.keyvault.secrets.models.KeyVaultSecret; +import com.azure.security.keyvault.secrets.models.SecretProperties; + +/** + * The SecretClient provides synchronous methods to manage {@link KeyVaultSecret secrets} in the Azure Key Vault. The client + * supports creating, retrieving, updating, deleting, purging, backing up, restoring, and listing the {@link KeyVaultSecret + * secrets}. The client also supports listing {@link DeletedSecret deleted secrets} for a soft-delete enabled Azure Key + * Vault. + * + *

    Construct the sync client

    + * {@codesnippet com.azure.security.keyvault.secretclient.sync.construct} + * + * @see SecretClientBuilder + * @see PagedIterable + */ +public final class SecretClient { + + /** + * Gets the vault endpoint url to which service requests are sent to. + * @return the vault endpoint url. + */ + public String getVaultUrl() { + return null; + } + + /** + * Adds a secret to the key vault if it does not exist. If the named secret exists, a new version of the secret is + * created. This operation requires the {@code secrets/set} permission. + * + *

    The {@link SecretProperties#getExpiresOn() expires}, {@link SecretProperties#getContentType() contentType}, + * and {@link SecretProperties#getNotBefore() notBefore} values in {@code secret} are optional. + * If not specified, {@link SecretProperties#isEnabled() enabled} is set to true by key vault.

    + * + *

    Code sample

    + *

    Creates a new secret in the key vault. Prints out the details of the newly created secret returned in the + * response.

    + * {@codesnippet com.azure.security.keyvault.secretclient.setSecret#secret} + * + * @param secret The Secret object containing information about the secret and its properties. The properties + * {@link KeyVaultSecret#getName() secret.name} and {@link KeyVaultSecret#getValue() secret.value} cannot be + * null. + * @return The {@link KeyVaultSecret created secret}. + * @throws NullPointerException if {@code secret} is {@code null}. + * @throws ResourceModifiedException if {@code secret} is malformed. + * @throws HttpResponseException if {@link KeyVaultSecret#getName() name} or {@link KeyVaultSecret#getValue() value} + * is an empty string. + */ + public KeyVaultSecret setSecret(KeyVaultSecret secret) { + return null; + } + + /** + * Adds a secret to the key vault if it does not exist. If the named secret exists, a new version of the secret is + * created. This operation requires the {@code secrets/set} permission. + * + *

    Code sample

    + *

    Creates a new secret in the key vault. Prints out the details of the newly created secret returned in the + * response.

    + * {@codesnippet com.azure.security.keyvault.secretclient.setSecret#string-string} + * + * @param name The name of the secret. It is required and cannot be null. + * @param value The value of the secret. It is required and cannot be null. + * @return The {@link KeyVaultSecret created secret}. + * @throws ResourceModifiedException if invalid {@code name} or {@code value} is specified. + * @throws HttpResponseException if {@code name} or {@code value} is empty string. + */ + public KeyVaultSecret setSecret(String name, String value) { + return null; + } + + /** + * Gets the specified secret with specified version from the key vault. This operation requires the + * {@code secrets/get} permission. + * + *

    Code sample

    + *

    Gets a specific version of the secret in the key vault. Prints out the details of the returned secret.

    + * {@codesnippet com.azure.security.keyvault.secretclient.getSecret#string-string} + * + * @param name The name of the secret, cannot be null. + * @param version The version of the secret to retrieve. If this is an empty string or null, this call is + * equivalent to calling {@link #getSecret(String)}, with the latest version being retrieved. + * @return The requested {@link KeyVaultSecret secret}. + * @throws ResourceNotFoundException when a secret with {@code name} and {@code version} doesn't exist in the + * key vault. + * @throws HttpResponseException if {@code name} or {@code version} is empty string. + */ + public KeyVaultSecret getSecret(String name, String version) { + return null; + } + + /** + * Gets the latest version of the specified secret from the key vault. + * This operation requires the {@code secrets/get} permission. + * + *

    Code sample

    + *

    Gets the latest version of the secret in the key vault. Prints out the details of the returned secret.

    + * {@codesnippet com.azure.security.keyvault.secretclient.getSecret#string} + * + * @param name The name of the secret. + * @return The requested {@link KeyVaultSecret}. + * @throws ResourceNotFoundException when a secret with {@code name} doesn't exist in the key vault. + * @throws HttpResponseException if {@code name} is empty string. + */ + public KeyVaultSecret getSecret(String name) { + return null; + } + + /** + * Updates the attributes associated with the secret. The value of the secret in the key vault cannot be changed. + * Only attributes populated in {@code secretProperties} are changed. Attributes not specified in the request are + * not changed. This operation requires the {@code secrets/set} permission. + * + *

    The {@code secret} is required and its fields {@link SecretProperties#getName() name} and + * {@link SecretProperties#getVersion() version} cannot be null.

    + * + *

    Code sample

    + *

    Gets the latest version of the secret, changes its expiry time, and the updates the secret in the key + * vault.

    + * {@codesnippet com.azure.security.keyvault.secretclient.updateSecretProperties#secretProperties} + * + * @param secretProperties The {@link SecretProperties secret properties} object with updated properties. + * @return The {@link SecretProperties updated secret}. + * @throws NullPointerException if {@code secret} is {@code null}. + * @throws ResourceNotFoundException when a secret with {@link SecretProperties#getName() name} and {@link + * SecretProperties#getVersion() version} doesn't exist in the key vault. + * @throws HttpResponseException if {@link SecretProperties#getName() name} or {@link SecretProperties#getVersion() version} is + * empty string. + */ + public SecretProperties updateSecretProperties(SecretProperties secretProperties) { + return null; + } + + /** + * Requests a backup of the secret be downloaded to the client. All versions of the secret will be downloaded. + * This operation requires the {@code secrets/backup} permission. + * + *

    Code sample

    + *

    Backs up the secret from the key vault and prints out the length of the secret's backup byte array returned in + * the response

    + * {@codesnippet com.azure.security.keyvault.secretclient.backupSecret#string} + * + * @param name The name of the secret. + * @return A {@link Response} whose {@link Response#getValue() value} contains the backed up secret blob. + * @throws ResourceNotFoundException when a secret with {@code name} doesn't exist in the key vault. + * @throws HttpResponseException when a secret with {@code name} is empty string. + */ + public byte[] backupSecret(String name) { + return null; + } +} diff --git a/java/ql/test/stubs/azure-sdk-for-java/com/azure/security/keyvault/secrets/SecretClientBuilder.java b/java/ql/test/stubs/azure-sdk-for-java/com/azure/security/keyvault/secrets/SecretClientBuilder.java new file mode 100644 index 00000000000..0a90f44f8dd --- /dev/null +++ b/java/ql/test/stubs/azure-sdk-for-java/com/azure/security/keyvault/secrets/SecretClientBuilder.java @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.secrets; + +import com.azure.core.credential.TokenCredential; + +/** + * This class provides a fluent builder API to help aid the configuration and instantiation of the {@link + * SecretAsyncClient secret async client} and {@link SecretClient secret client}, + * by calling {@link SecretClientBuilder#buildAsyncClient() buildAsyncClient} and {@link + * SecretClientBuilder#buildClient() buildClient} respectively. + * It constructs an instance of the desired client. + * + *

    The minimal configuration options required by {@link SecretClientBuilder secretClientBuilder} to build + * {@link SecretAsyncClient} are {@link String vaultUrl} and {@link TokenCredential credential}.

    + * + * {@codesnippet com.azure.security.keyvault.secrets.async.secretclient.construct} + * + *

    Samples to construct the sync client

    + * {@codesnippet com.azure.security.keyvault.secretclient.sync.construct} + * + *

    The {@link HttpLogDetailLevel log detail level}, multiple custom {@link HttpLoggingPolicy policies} and custom + * {@link HttpClient http client} can be optionally configured in the {@link SecretClientBuilder}.

    + * + * {@codesnippet com.azure.security.keyvault.secrets.async.secretclient.withhttpclient.instantiation} + * + *

    Alternatively, custom {@link HttpPipeline http pipeline} with custom {@link HttpPipelinePolicy} policies and + * {@link String vaultUrl} + * can be specified. It provides finer control over the construction of {@link SecretAsyncClient client}

    + * + * {@codesnippet com.azure.security.keyvault.secrets.async.secretclient.pipeline.instantiation} + * + * @see SecretClient + * @see SecretAsyncClient + */ +public final class SecretClientBuilder { + /** + * The constructor with defaults. + */ + public SecretClientBuilder() { + } + + /** + * Creates a {@link SecretClient} based on options set in the builder. + * Every time {@code buildClient()} is called, a new instance of {@link SecretClient} is created. + * + *

    If {@link SecretClientBuilder#pipeline(HttpPipeline) pipeline} is set, then the {@code pipeline} and + * {@link SecretClientBuilder#vaultUrl(String) serviceEndpoint} are used to create the + * {@link SecretClientBuilder client}. All other builder settings are ignored. If {@code pipeline} is not set, + * then {@link SecretClientBuilder#credential(TokenCredential) key vault credential}, and + * {@link SecretClientBuilder#vaultUrl(String)} key vault url are required to build the {@link SecretClient + * client}.

    + * + * @return A {@link SecretClient} with the options set from the builder. + * + * @throws IllegalStateException If {@link SecretClientBuilder#credential(TokenCredential)} or + * {@link SecretClientBuilder#vaultUrl(String)} have not been set. + */ + public SecretClient buildClient() { + return null; + } + + /** + * Sets the vault URL to send HTTP requests to. + * + * @param vaultUrl The vault url is used as destination on Azure to send requests to. If you have a secret + * identifier, create a new {@link KeyVaultSecretIdentifier} to parse it and obtain the {@code vaultUrl} and + * other information. + * + * @return The updated {@link SecretClientBuilder} object. + * + * @throws IllegalArgumentException If {@code vaultUrl} is null or it cannot be parsed into a valid URL. + * @throws NullPointerException If {@code vaultUrl} is {@code null}. + */ + public SecretClientBuilder vaultUrl(String vaultUrl) { + return null; + } + + /** + * Sets the credential to use when authenticating HTTP requests. + * + * @param credential The credential to use for authenticating HTTP requests. + * + * @return The updated {@link SecretClientBuilder} object. + * + * @throws NullPointerException If {@code credential} is {@code null}. + */ + public SecretClientBuilder credential(TokenCredential credential) { + return null; + } +} diff --git a/java/ql/test/stubs/azure-sdk-for-java/com/azure/security/keyvault/secrets/models/KeyVaultSecret.java b/java/ql/test/stubs/azure-sdk-for-java/com/azure/security/keyvault/secrets/models/KeyVaultSecret.java new file mode 100644 index 00000000000..1f2f252a323 --- /dev/null +++ b/java/ql/test/stubs/azure-sdk-for-java/com/azure/security/keyvault/secrets/models/KeyVaultSecret.java @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.secrets.models; + +import com.azure.security.keyvault.secrets.SecretClient; + +import java.util.Map; +import java.util.Objects; + +/** + * Secret is the resource consisting of name, value and its attributes specified in {@link SecretProperties}. + * It is managed by Secret Service. + * + * @see SecretClient + * @see SecretAsyncClient + */ +public class KeyVaultSecret { + /** + * Creates an empty instance of the Secret. + */ + KeyVaultSecret() { + } + + /** + * Creates a Secret with {@code name} and {@code value}. + * + * @param name The name of the secret. + * @param value the value of the secret. + */ + public KeyVaultSecret(String name, String value) { + } + + /** + * Get the value of the secret. + * + * @return the secret value + */ + public String getValue() { + return null; + } + + /** + * Get the secret identifier. + * + * @return the secret identifier. + */ + public String getId() { + return null; + } + + /** + * Get the secret name. + * + * @return the secret name. + */ + public String getName() { + return null; + } + + /** + * Get the secret properties + * @return the Secret properties + */ + public SecretProperties getProperties() { + return null; + } + + /** + * Set the secret properties + * @param properties The Secret properties + * @throws NullPointerException if {@code properties} is null. + * @return the updated secret object + */ + public KeyVaultSecret setProperties(SecretProperties properties) { + return null; + } +} diff --git a/java/ql/test/stubs/azure-sdk-for-java/com/azure/security/keyvault/secrets/models/SecretProperties.java b/java/ql/test/stubs/azure-sdk-for-java/com/azure/security/keyvault/secrets/models/SecretProperties.java new file mode 100644 index 00000000000..750db3ff177 --- /dev/null +++ b/java/ql/test/stubs/azure-sdk-for-java/com/azure/security/keyvault/secrets/models/SecretProperties.java @@ -0,0 +1,145 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.security.keyvault.secrets.models; + +import java.util.Map; + +import com.azure.security.keyvault.secrets.SecretClient; + +/** + * SecretProperties is the resource containing all the properties of the secret except its value. + * It is managed by the Secret Service. + * + * @see SecretClient + * @see SecretAsyncClient + */ +public class SecretProperties { + SecretProperties(String secretName) { + } + + /** + * Creates empty instance of SecretProperties. + */ + public SecretProperties() { } + + /** + * Get the secret name. + * + * @return the name of the secret. + */ + public String getName() { + return null; + } + + /** + * Get the recovery level of the secret. + + * @return the recoveryLevel of the secret. + */ + public String getRecoveryLevel() { + return null; + } + + /** + * Get the enabled value. + * + * @return the enabled value + */ + public Boolean isEnabled() { + return false; + } + + /** + * Set the enabled value. + * + * @param enabled The enabled value to set + * @throws NullPointerException if {@code enabled} is null. + * @return the SecretProperties object itself. + */ + public SecretProperties setEnabled(Boolean enabled) { + return null; + } + + /** + * Get the secret identifier. + * + * @return the secret identifier. + */ + public String getId() { + return null; + } + + /** + * Get the content type. + * + * @return the content type. + */ + public String getContentType() { + return null; + } + + /** + * Set the contentType. + * + * @param contentType The contentType to set + * @return the updated SecretProperties object itself. + */ + public SecretProperties setContentType(String contentType) { + return null; + } + + /** + * Get the tags associated with the secret. + * + * @return the value of the tags. + */ + public Map getTags() { + return null; + } + + /** + * Set the tags to be associated with the secret. + * + * @param tags The tags to set + * @return the updated SecretProperties object itself. + */ + public SecretProperties setTags(Map tags) { + return null; + } + + /** + * Get the keyId identifier. + * + * @return the keyId identifier. + */ + public String getKeyId() { + return null; + } + + /** + * Get the managed value. + * + * @return the managed value + */ + public Boolean isManaged() { + return null; + } + + /** + * Get the version of the secret. + * + * @return the version of the secret. + */ + public String getVersion() { + return null; + } + + /** + * Gets the number of days a secret is retained before being deleted for a soft delete-enabled Key Vault. + * @return the recoverable days. + */ + public Integer getRecoverableDays() { + return null; + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/accounts/Account.java b/java/ql/test/stubs/google-android-9.0.0/android/accounts/Account.java new file mode 100644 index 00000000000..806f076452e --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/accounts/Account.java @@ -0,0 +1,21 @@ +// Generated automatically from android.accounts.Account for testing purposes + +package android.accounts; + +import android.os.Parcel; +import android.os.Parcelable; + +public class Account implements Parcelable +{ + protected Account() {} + public Account(Parcel p0){} + public Account(String p0, String p1){} + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public final String name = null; + public final String type = null; + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/annotation/StyleRes.java b/java/ql/test/stubs/google-android-9.0.0/android/annotation/StyleRes.java new file mode 100644 index 00000000000..2a75e140347 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/annotation/StyleRes.java @@ -0,0 +1,38 @@ + +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +/** + * Denotes that a integer parameter, field or method return value is expected + * to be a style resource reference (e.g. {@link android.R.style#TextAppearance}). + * + * {@hide} + */ +@Documented +@Retention(SOURCE) +@Target({METHOD, PARAMETER, FIELD}) +public @interface StyleRes { +} \ No newline at end of file diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java b/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java index e04f68a203d..9e31a58a260 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java @@ -16,8 +16,9 @@ package android.app; import android.content.Intent; +import android.content.ComponentCallbacks2; import android.os.Bundle; -import android.view.View; +import android.view.*; /** * An activity is a single, focused thing that the user can do. Almost all @@ -187,7 +188,7 @@ import android.view.View; *

    * *

    - * + * *
      * public class Activity extends ApplicationContext {
      *     protected void onCreate(Bundle savedInstanceState);
    @@ -675,7 +676,7 @@ import android.view.View;
      * upload, independent of whether the original activity is paused, stopped, or
      * finished.
      */
    -public class Activity {
    +public class Activity extends ContextThemeWrapper {
         /** Return the intent that started this activity. */
         public Intent getIntent() {
             return null;
    @@ -1142,4 +1143,8 @@ public class Activity {
          */
         public void startActivities(Intent[] intents, Bundle options) {
         }
    +
    +    protected void onActivityResult(int requestCode, int resultCode, Intent data) { }
    +
    +    public static final int RESULT_OK = -1;
     }
    diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/Service.java b/java/ql/test/stubs/google-android-9.0.0/android/app/Service.java
    new file mode 100644
    index 00000000000..370d7c5e4fe
    --- /dev/null
    +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/Service.java
    @@ -0,0 +1,344 @@
    +/*
    + * Copyright (C) 2006 The Android Open Source Project
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + *      http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package android.app;
    +
    +import android.content.Intent;
    +import android.content.ContextWrapper;
    +
    +/**
    + * A Service is an application component representing either an application's desire
    + * to perform a longer-running operation while not interacting with the user
    + * or to supply functionality for other applications to use.  Each service
    + * class must have a corresponding
    + * {@link android.R.styleable#AndroidManifestService <service>}
    + * declaration in its package's AndroidManifest.xml.  Services
    + * can be started with
    + * {@link android.content.Context#startService Context.startService()} and
    + * {@link android.content.Context#bindService Context.bindService()}.
    + * 
    + * 

    Note that services, like other application objects, run in the main + * thread of their hosting process. This means that, if your service is going + * to do any CPU intensive (such as MP3 playback) or blocking (such as + * networking) operations, it should spawn its own thread in which to do that + * work. More information on this can be found in + * Processes and + * Threads. The {@link IntentService} class is available + * as a standard implementation of Service that has its own thread where it + * schedules its work to be done.

    + * + *

    Topics covered here: + *

      + *
    1. What is a Service? + *
    2. Service Lifecycle + *
    3. Permissions + *
    4. Process Lifecycle + *
    5. Local Service Sample + *
    6. Remote Messenger Service Sample + *
    + * + *
    + *

    Developer Guides

    + *

    For a detailed discussion about how to create services, read the + * Services developer guide.

    + *
    + * + * + *

    What is a Service?

    + * + *

    Most confusion about the Service class actually revolves around what + * it is not:

    + * + *
      + *
    • A Service is not a separate process. The Service object itself + * does not imply it is running in its own process; unless otherwise specified, + * it runs in the same process as the application it is part of. + *
    • A Service is not a thread. It is not a means itself to do work off + * of the main thread (to avoid Application Not Responding errors). + *
    + * + *

    Thus a Service itself is actually very simple, providing two main features:

    + * + *
      + *
    • A facility for the application to tell the system about + * something it wants to be doing in the background (even when the user is not + * directly interacting with the application). This corresponds to calls to + * {@link android.content.Context#startService Context.startService()}, which + * ask the system to schedule work for the service, to be run until the service + * or someone else explicitly stop it. + *
    • A facility for an application to expose some of its functionality to + * other applications. This corresponds to calls to + * {@link android.content.Context#bindService Context.bindService()}, which + * allows a long-standing connection to be made to the service in order to + * interact with it. + *
    + * + *

    When a Service component is actually created, for either of these reasons, + * all that the system actually does is instantiate the component + * and call its {@link #onCreate} and any other appropriate callbacks on the + * main thread. It is up to the Service to implement these with the appropriate + * behavior, such as creating a secondary thread in which it does its work.

    + * + *

    Note that because Service itself is so simple, you can make your + * interaction with it as simple or complicated as you want: from treating it + * as a local Java object that you make direct method calls on (as illustrated + * by Local Service Sample), to providing + * a full remoteable interface using AIDL.

    + * + * + *

    Service Lifecycle

    + * + *

    There are two reasons that a service can be run by the system. If someone + * calls {@link android.content.Context#startService Context.startService()} then the system will + * retrieve the service (creating it and calling its {@link #onCreate} method + * if needed) and then call its {@link #onStartCommand} method with the + * arguments supplied by the client. The service will at this point continue + * running until {@link android.content.Context#stopService Context.stopService()} or + * {@link #stopSelf()} is called. Note that multiple calls to + * Context.startService() do not nest (though they do result in multiple corresponding + * calls to onStartCommand()), so no matter how many times it is started a service + * will be stopped once Context.stopService() or stopSelf() is called; however, + * services can use their {@link #stopSelf(int)} method to ensure the service is + * not stopped until started intents have been processed. + * + *

    For started services, there are two additional major modes of operation + * they can decide to run in, depending on the value they return from + * onStartCommand(): {@link #START_STICKY} is used for services that are + * explicitly started and stopped as needed, while {@link #START_NOT_STICKY} + * or {@link #START_REDELIVER_INTENT} are used for services that should only + * remain running while processing any commands sent to them. See the linked + * documentation for more detail on the semantics. + * + *

    Clients can also use {@link android.content.Context#bindService Context.bindService()} to + * obtain a persistent connection to a service. This likewise creates the + * service if it is not already running (calling {@link #onCreate} while + * doing so), but does not call onStartCommand(). The client will receive the + * {@link android.os.IBinder} object that the service returns from its + * {@link #onBind} method, allowing the client to then make calls back + * to the service. The service will remain running as long as the connection + * is established (whether or not the client retains a reference on the + * service's IBinder). Usually the IBinder returned is for a complex + * interface that has been written + * in aidl. + * + *

    A service can be both started and have connections bound to it. In such + * a case, the system will keep the service running as long as either it is + * started or there are one or more connections to it with the + * {@link android.content.Context#BIND_AUTO_CREATE Context.BIND_AUTO_CREATE} + * flag. Once neither + * of these situations hold, the service's {@link #onDestroy} method is called + * and the service is effectively terminated. All cleanup (stopping threads, + * unregistering receivers) should be complete upon returning from onDestroy(). + * + * + *

    Permissions

    + * + *

    Global access to a service can be enforced when it is declared in its + * manifest's {@link android.R.styleable#AndroidManifestService <service>} + * tag. By doing so, other applications will need to declare a corresponding + * {@link android.R.styleable#AndroidManifestUsesPermission <uses-permission>} + * element in their own manifest to be able to start, stop, or bind to + * the service. + * + *

    As of {@link android.os.Build.VERSION_CODES#GINGERBREAD}, when using + * {@link Context#startService(Intent) Context.startService(Intent)}, you can + * also set {@link Intent#FLAG_GRANT_READ_URI_PERMISSION + * Intent.FLAG_GRANT_READ_URI_PERMISSION} and/or {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION + * Intent.FLAG_GRANT_WRITE_URI_PERMISSION} on the Intent. This will grant the + * Service temporary access to the specific URIs in the Intent. Access will + * remain until the Service has called {@link #stopSelf(int)} for that start + * command or a later one, or until the Service has been completely stopped. + * This works for granting access to the other apps that have not requested + * the permission protecting the Service, or even when the Service is not + * exported at all. + * + *

    In addition, a service can protect individual IPC calls into it with + * permissions, by calling the + * {@link #checkCallingPermission} + * method before executing the implementation of that call. + * + *

    See the Security and Permissions + * document for more information on permissions and security in general. + * + * + *

    Process Lifecycle

    + * + *

    The Android system will attempt to keep the process hosting a service + * around as long as the service has been started or has clients bound to it. + * When running low on memory and needing to kill existing processes, the + * priority of a process hosting the service will be the higher of the + * following possibilities: + * + *

      + *
    • If the service is currently executing code in its + * {@link #onCreate onCreate()}, {@link #onStartCommand onStartCommand()}, + * or {@link #onDestroy onDestroy()} methods, then the hosting process will + * be a foreground process to ensure this code can execute without + * being killed. + *

    • If the service has been started, then its hosting process is considered + * to be less important than any processes that are currently visible to the + * user on-screen, but more important than any process not visible. Because + * only a few processes are generally visible to the user, this means that + * the service should not be killed except in low memory conditions. However, since + * the user is not directly aware of a background service, in that state it is + * considered a valid candidate to kill, and you should be prepared for this to + * happen. In particular, long-running services will be increasingly likely to + * kill and are guaranteed to be killed (and restarted if appropriate) if they + * remain started long enough. + *

    • If there are clients bound to the service, then the service's hosting + * process is never less important than the most important client. That is, + * if one of its clients is visible to the user, then the service itself is + * considered to be visible. The way a client's importance impacts the service's + * importance can be adjusted through {@link Context#BIND_ABOVE_CLIENT}, + * {@link Context#BIND_ALLOW_OOM_MANAGEMENT}, {@link Context#BIND_WAIVE_PRIORITY}, + * {@link Context#BIND_IMPORTANT}, and {@link Context#BIND_ADJUST_WITH_ACTIVITY}. + *

    • A started service can use the {@link #startForeground(int, Notification)} + * API to put the service in a foreground state, where the system considers + * it to be something the user is actively aware of and thus not a candidate + * for killing when low on memory. (It is still theoretically possible for + * the service to be killed under extreme memory pressure from the current + * foreground application, but in practice this should not be a concern.) + *

    + * + *

    Note this means that most of the time your service is running, it may + * be killed by the system if it is under heavy memory pressure. If this + * happens, the system will later try to restart the service. An important + * consequence of this is that if you implement {@link #onStartCommand onStartCommand()} + * to schedule work to be done asynchronously or in another thread, then you + * may want to use {@link #START_FLAG_REDELIVERY} to have the system + * re-deliver an Intent for you so that it does not get lost if your service + * is killed while processing it. + * + *

    Other application components running in the same process as the service + * (such as an {@link android.app.Activity}) can, of course, increase the + * importance of the overall + * process beyond just the importance of the service itself. + * + * + *

    Local Service Sample

    + * + *

    One of the most common uses of a Service is as a secondary component + * running alongside other parts of an application, in the same process as + * the rest of the components. All components of an .apk run in the same + * process unless explicitly stated otherwise, so this is a typical situation. + * + *

    When used in this way, by assuming the + * components are in the same process, you can greatly simplify the interaction + * between them: clients of the service can simply cast the IBinder they + * receive from it to a concrete class published by the service. + * + *

    An example of this use of a Service is shown here. First is the Service + * itself, publishing a custom class when bound: + * + * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/LocalService.java + * service} + * + *

    With that done, one can now write client code that directly accesses the + * running service, such as: + * + * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/LocalServiceActivities.java + * bind} + * + * + *

    Remote Messenger Service Sample

    + * + *

    If you need to be able to write a Service that can perform complicated + * communication with clients in remote processes (beyond simply the use of + * {@link Context#startService(Intent) Context.startService} to send + * commands to it), then you can use the {@link android.os.Messenger} class + * instead of writing full AIDL files. + * + *

    An example of a Service that uses Messenger as its client interface + * is shown here. First is the Service itself, publishing a Messenger to + * an internal Handler when bound: + * + * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/MessengerService.java + * service} + * + *

    If we want to make this service run in a remote process (instead of the + * standard one for its .apk), we can use android:process in its + * manifest tag to specify one: + * + * {@sample development/samples/ApiDemos/AndroidManifest.xml remote_service_declaration} + * + *

    Note that the name "remote" chosen here is arbitrary, and you can use + * other names if you want additional processes. The ':' prefix appends the + * name to your package's standard process name. + * + *

    With that done, clients can now bind to the service and send messages + * to it. Note that this allows clients to register with it to receive + * messages back as well: + * + * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/MessengerServiceActivities.java + * bind} + */ +public abstract class Service extends ContextWrapper { + /** + * Called by the system when the service is first created. Do not call this method directly. + */ + public void onCreate() { + } + + /** + * @deprecated Implement {@link #onStartCommand(Intent, int, int)} instead. + */ + @Deprecated + public void onStart(Intent intent, int startId) { + } + + /** + * Called by the system every time a client explicitly starts the service by calling + * {@link android.content.Context#startService}, providing the arguments it supplied and a + * unique integer token representing the start request. Do not call this method directly. + * + *

    For backwards compatibility, the default implementation calls + * {@link #onStart} and returns either {@link #START_STICKY} + * or {@link #START_STICKY_COMPATIBILITY}. + * + *

    Note that the system calls this on your + * service's main thread. A service's main thread is the same + * thread where UI operations take place for Activities running in the + * same process. You should always avoid stalling the main + * thread's event loop. When doing long-running operations, + * network calls, or heavy disk I/O, you should kick off a new + * thread, or use {@link android.os.AsyncTask}.

    + * + * @param intent The Intent supplied to {@link android.content.Context#startService}, + * as given. This may be null if the service is being restarted after + * its process has gone away, and it had previously returned anything + * except {@link #START_STICKY_COMPATIBILITY}. + * @param flags Additional data about this start request. + * @param startId A unique integer representing this specific request to + * start. Use with {@link #stopSelfResult(int)}. + * + * @return The return value indicates what semantics the system should + * use for the service's current started state. It may be one of the + * constants associated with the {@link #START_CONTINUATION_MASK} bits. + * + * @see #stopSelfResult(int) + */ + public int onStartCommand(Intent intent, int flags, int startId) { + return -1; + } + + /** + * Called by the system to notify a Service that it is no longer used and is being removed. The + * service should clean up any resources it holds (threads, registered + * receivers, etc) at this point. Upon return, there will be no more calls + * in to this Service object and it is effectively dead. Do not call this method directly. + */ + public void onDestroy() { + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/BroadcastReceiver.java b/java/ql/test/stubs/google-android-9.0.0/android/content/BroadcastReceiver.java index 1d73018c96d..f199fcccc75 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/content/BroadcastReceiver.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/BroadcastReceiver.java @@ -1,255 +1,46 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Generated automatically from android.content.BroadcastReceiver for testing purposes + package android.content; +import android.content.Context; +import android.content.Intent; import android.os.Bundle; -/** - * Base class for code that will receive intents sent by sendBroadcast(). - * - *

    If you don't need to send broadcasts across applications, consider using - * this class with {@link android.support.v4.content.LocalBroadcastManager} instead - * of the more general facilities described below. This will give you a much - * more efficient implementation (no cross-process communication needed) and allow - * you to avoid thinking about any security issues related to other applications - * being able to receive or send your broadcasts. - * - *

    You can either dynamically register an instance of this class with - * {@link Context#registerReceiver Context.registerReceiver()} - * or statically publish an implementation through the - * {@link android.R.styleable#AndroidManifestReceiver <receiver>} - * tag in your AndroidManifest.xml. - * - *

    Note: - *    If registering a receiver in your - * {@link android.app.Activity#onResume() Activity.onResume()} - * implementation, you should unregister it in - * {@link android.app.Activity#onPause() Activity.onPause()}. - * (You won't receive intents when paused, - * and this will cut down on unnecessary system overhead). Do not unregister in - * {@link android.app.Activity#onSaveInstanceState(android.os.Bundle) Activity.onSaveInstanceState()}, - * because this won't be called if the user moves back in the history - * stack. - * - *

    There are two major classes of broadcasts that can be received:

    - *
      - *
    • Normal broadcasts (sent with {@link Context#sendBroadcast(Intent) - * Context.sendBroadcast}) are completely asynchronous. All receivers of the - * broadcast are run in an undefined order, often at the same time. This is - * more efficient, but means that receivers cannot use the result or abort - * APIs included here. - *
    • Ordered broadcasts (sent with {@link Context#sendOrderedBroadcast(Intent, String) - * Context.sendOrderedBroadcast}) are delivered to one receiver at a time. - * As each receiver executes in turn, it can propagate a result to the next - * receiver, or it can completely abort the broadcast so that it won't be passed - * to other receivers. The order receivers run in can be controlled with the - * {@link android.R.styleable#AndroidManifestIntentFilter_priority - * android:priority} attribute of the matching intent-filter; receivers with - * the same priority will be run in an arbitrary order. - *
    - * - *

    Even in the case of normal broadcasts, the system may in some - * situations revert to delivering the broadcast one receiver at a time. In - * particular, for receivers that may require the creation of a process, only - * one will be run at a time to avoid overloading the system with new processes. - * In this situation, however, the non-ordered semantics hold: these receivers still - * cannot return results or abort their broadcast.

    - * - *

    Note that, although the Intent class is used for sending and receiving - * these broadcasts, the Intent broadcast mechanism here is completely separate - * from Intents that are used to start Activities with - * {@link Context#startActivity Context.startActivity()}. - * There is no way for a BroadcastReceiver - * to see or capture Intents used with startActivity(); likewise, when - * you broadcast an Intent, you will never find or start an Activity. - * These two operations are semantically very different: starting an - * Activity with an Intent is a foreground operation that modifies what the - * user is currently interacting with; broadcasting an Intent is a background - * operation that the user is not normally aware of. - * - *

    The BroadcastReceiver class (when launched as a component through - * a manifest's {@link android.R.styleable#AndroidManifestReceiver <receiver>} - * tag) is an important part of an - * application's overall lifecycle.

    - * - *

    Topics covered here: - *

      - *
    1. Security - *
    2. Receiver Lifecycle - *
    3. Process Lifecycle - *
    - * - *
    - *

    Developer Guides

    - *

    For information about how to use this class to receive and resolve intents, read the - * Intents and Intent Filters - * developer guide.

    - *
    - * - * - *

    Security

    - * - *

    Receivers used with the {@link Context} APIs are by their nature a - * cross-application facility, so you must consider how other applications - * may be able to abuse your use of them. Some things to consider are: - * - *

      - *
    • The Intent namespace is global. Make sure that Intent action names and - * other strings are written in a namespace you own, or else you may inadvertantly - * conflict with other applications. - *

    • When you use {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)}, - * any application may send broadcasts to that registered receiver. You can - * control who can send broadcasts to it through permissions described below. - *

    • When you publish a receiver in your application's manifest and specify - * intent-filters for it, any other application can send broadcasts to it regardless - * of the filters you specify. To prevent others from sending to it, make it - * unavailable to them with android:exported="false". - *

    • When you use {@link Context#sendBroadcast(Intent)} or related methods, - * normally any other application can receive these broadcasts. You can control who - * can receive such broadcasts through permissions described below. Alternatively, - * starting with {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, you - * can also safely restrict the broadcast to a single application with - * {@link Intent#setPackage(String) Intent.setPackage} - *

    - * - *

    None of these issues exist when using - * {@link android.support.v4.content.LocalBroadcastManager}, since intents - * broadcast it never go outside of the current process. - * - *

    Access permissions can be enforced by either the sender or receiver - * of a broadcast. - * - *

    To enforce a permission when sending, you supply a non-null - * permission argument to - * {@link Context#sendBroadcast(Intent, String)} or - * {@link Context#sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler, int, String, Bundle)}. - * Only receivers who have been granted this permission - * (by requesting it with the - * {@link android.R.styleable#AndroidManifestUsesPermission <uses-permission>} - * tag in their AndroidManifest.xml) will be able to receive - * the broadcast. - * - *

    To enforce a permission when receiving, you supply a non-null - * permission when registering your receiver -- either when calling - * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter, String, android.os.Handler)} - * or in the static - * {@link android.R.styleable#AndroidManifestReceiver <receiver>} - * tag in your AndroidManifest.xml. Only broadcasters who have - * been granted this permission (by requesting it with the - * {@link android.R.styleable#AndroidManifestUsesPermission <uses-permission>} - * tag in their AndroidManifest.xml) will be able to send an - * Intent to the receiver. - * - *

    See the Security and Permissions - * document for more information on permissions and security in general. - * - * - *

    Receiver Lifecycle

    - * - *

    A BroadcastReceiver object is only valid for the duration of the call - * to {@link #onReceive}. Once your code returns from this function, - * the system considers the object to be finished and no longer active. - * - *

    This has important repercussions to what you can do in an - * {@link #onReceive} implementation: anything that requires asynchronous - * operation is not available, because you will need to return from the - * function to handle the asynchronous operation, but at that point the - * BroadcastReceiver is no longer active and thus the system is free to kill - * its process before the asynchronous operation completes. - * - *

    In particular, you may not show a dialog or bind to a service from - * within a BroadcastReceiver. For the former, you should instead use the - * {@link android.app.NotificationManager} API. For the latter, you can - * use {@link android.content.Context#startService Context.startService()} to - * send a command to the service. - * - * - *

    Process Lifecycle

    - * - *

    A process that is currently executing a BroadcastReceiver (that is, - * currently running the code in its {@link #onReceive} method) is - * considered to be a foreground process and will be kept running by the - * system except under cases of extreme memory pressure. - * - *

    Once you return from onReceive(), the BroadcastReceiver is no longer - * active, and its hosting process is only as important as any other application - * components that are running in it. This is especially important because if - * that process was only hosting the BroadcastReceiver (a common case for - * applications that the user has never or not recently interacted with), then - * upon returning from onReceive() the system will consider its process - * to be empty and aggressively kill it so that resources are available for other - * more important processes. - * - *

    This means that for longer-running operations you will often use - * a {@link android.app.Service} in conjunction with a BroadcastReceiver to keep - * the containing process active for the entire time of your operation. - */ -public abstract class BroadcastReceiver { - - /** - * State for a result that is pending for a broadcast receiver. Returned - * by {@link BroadcastReceiver#goAsync() goAsync()} - * while in {@link BroadcastReceiver#onReceive BroadcastReceiver.onReceive()}. - * This allows you to return from onReceive() without having the broadcast - * terminate; you must call {@link #finish()} once you are done with the - * broadcast. This allows you to process the broadcast off of the main - * thread of your app. - * - *

    Note on threading: the state inside of this class is not itself - * thread-safe, however you can use it from any thread if you properly - * sure that you do not have races. Typically this means you will hand - * the entire object to another thread, which will be solely responsible - * for setting any results and finally calling {@link #finish()}. - */ - - public BroadcastReceiver() { +import android.os.IBinder; + +abstract public class BroadcastReceiver +{ + public BroadcastReceiver(){} + public IBinder peekService(Context p0, Intent p1){ return null; } + public abstract void onReceive(Context p0, Intent p1); + public final BroadcastReceiver.PendingResult goAsync(){ return null; } + public final Bundle getResultExtras(boolean p0){ return null; } + public final String getResultData(){ return null; } + public final boolean getAbortBroadcast(){ return false; } + public final boolean getDebugUnregister(){ return false; } + public final boolean isInitialStickyBroadcast(){ return false; } + public final boolean isOrderedBroadcast(){ return false; } + public final int getResultCode(){ return 0; } + public final void abortBroadcast(){} + public final void clearAbortBroadcast(){} + public final void setDebugUnregister(boolean p0){} + public final void setOrderedHint(boolean p0){} + public final void setResult(int p0, String p1, Bundle p2){} + public final void setResultCode(int p0){} + public final void setResultData(String p0){} + public final void setResultExtras(Bundle p0){} + static public class PendingResult + { + protected PendingResult() {} + public final Bundle getResultExtras(boolean p0){ return null; } + public final String getResultData(){ return null; } + public final boolean getAbortBroadcast(){ return false; } + public final int getResultCode(){ return 0; } + public final void abortBroadcast(){} + public final void clearAbortBroadcast(){} + public final void finish(){} + public final void setResult(int p0, String p1, Bundle p2){} + public final void setResultCode(int p0){} + public final void setResultData(String p0){} + public final void setResultExtras(Bundle p0){} } - /** - * This method is called when the BroadcastReceiver is receiving an Intent - * broadcast. During this time you can use the other methods on - * BroadcastReceiver to view/modify the current result values. This method - * is always called within the main thread of its process, unless you - * explicitly asked for it to be scheduled on a different thread using - * {@link android.content.Context#registerReceiver(BroadcastReceiver, - * IntentFilter, String, android.os.Handler)}. When it runs on the main - * thread you should - * never perform long-running operations in it (there is a timeout of - * 10 seconds that the system allows before considering the receiver to - * be blocked and a candidate to be killed). You cannot launch a popup dialog - * in your implementation of onReceive(). - * - *

    If this BroadcastReceiver was launched through a <receiver> tag, - * then the object is no longer alive after returning from this - * function. This means you should not perform any operations that - * return a result to you asynchronously -- in particular, for interacting - * with services, you should use - * {@link Context#startService(Intent)} instead of - * {@link Context#bindService(Intent, ServiceConnection, int)}. If you wish - * to interact with a service that is already running, you can use - * {@link #peekService}. - * - *

    The Intent filters used in {@link android.content.Context#registerReceiver} - * and in application manifests are not guaranteed to be exclusive. They - * are hints to the operating system about how to find suitable recipients. It is - * possible for senders to force delivery to specific recipients, bypassing filter - * resolution. For this reason, {@link #onReceive(Context, Intent) onReceive()} - * implementations should respond only to known actions, ignoring any unexpected - * Intents that they may receive. - * - * @param context The Context in which the receiver is running. - * @param intent The Intent being received. - */ - public abstract void onReceive(Context context, Intent intent); -} \ No newline at end of file +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/ClipData.java b/java/ql/test/stubs/google-android-9.0.0/android/content/ClipData.java new file mode 100644 index 00000000000..490aff3323a --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/ClipData.java @@ -0,0 +1,51 @@ +// Generated automatically from android.content.ClipData for testing purposes + +package android.content; + +import android.content.ClipDescription; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +public class ClipData implements Parcelable +{ + protected ClipData() {} + public ClipData(CharSequence p0, String[] p1, ClipData.Item p2){} + public ClipData(ClipData p0){} + public ClipData(ClipDescription p0, ClipData.Item p1){} + public ClipData.Item getItemAt(int p0){ return null; } + public ClipDescription getDescription(){ return null; } + public String toString(){ return null; } + public int describeContents(){ return 0; } + public int getItemCount(){ return 0; } + public static ClipData newHtmlText(CharSequence p0, CharSequence p1, String p2){ return null; } + public static ClipData newIntent(CharSequence p0, Intent p1){ return null; } + public static ClipData newPlainText(CharSequence p0, CharSequence p1){ return null; } + public static ClipData newRawUri(CharSequence p0, Uri p1){ return null; } + public static ClipData newUri(ContentResolver p0, CharSequence p1, Uri p2){ return null; } + public static Parcelable.Creator CREATOR = null; + public void addItem(ClipData.Item p0){} + public void addItem(ContentResolver p0, ClipData.Item p1){} + public void writeToParcel(Parcel p0, int p1){} + static public class Item + { + protected Item() {} + public CharSequence coerceToStyledText(Context p0){ return null; } + public CharSequence coerceToText(Context p0){ return null; } + public CharSequence getText(){ return null; } + public Intent getIntent(){ return null; } + public Item(CharSequence p0){} + public Item(CharSequence p0, Intent p1, Uri p2){} + public Item(CharSequence p0, String p1){} + public Item(CharSequence p0, String p1, Intent p2, Uri p3){} + public Item(Intent p0){} + public Item(Uri p0){} + public String coerceToHtmlText(Context p0){ return null; } + public String getHtmlText(){ return null; } + public String toString(){ return null; } + public Uri getUri(){ return null; } + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/ClipDescription.java b/java/ql/test/stubs/google-android-9.0.0/android/content/ClipDescription.java new file mode 100644 index 00000000000..95598cc40cd --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/ClipDescription.java @@ -0,0 +1,31 @@ +// Generated automatically from android.content.ClipDescription for testing purposes + +package android.content; + +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PersistableBundle; + +public class ClipDescription implements Parcelable +{ + protected ClipDescription() {} + public CharSequence getLabel(){ return null; } + public ClipDescription(CharSequence p0, String[] p1){} + public ClipDescription(ClipDescription p0){} + public PersistableBundle getExtras(){ return null; } + public String getMimeType(int p0){ return null; } + public String toString(){ return null; } + public String[] filterMimeTypes(String p0){ return null; } + public boolean hasMimeType(String p0){ return false; } + public int describeContents(){ return 0; } + public int getMimeTypeCount(){ return 0; } + public long getTimestamp(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static String MIMETYPE_TEXT_HTML = null; + public static String MIMETYPE_TEXT_INTENT = null; + public static String MIMETYPE_TEXT_PLAIN = null; + public static String MIMETYPE_TEXT_URILIST = null; + public static boolean compareMimeTypes(String p0, String p1){ return false; } + public void setExtras(PersistableBundle p0){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/ComponentCallbacks.java b/java/ql/test/stubs/google-android-9.0.0/android/content/ComponentCallbacks.java new file mode 100644 index 00000000000..51726693d00 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/ComponentCallbacks.java @@ -0,0 +1,11 @@ +// Generated automatically from android.content.ComponentCallbacks for testing purposes + +package android.content; + +import android.content.res.Configuration; + +public interface ComponentCallbacks +{ + void onConfigurationChanged(Configuration p0); + void onLowMemory(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/ComponentCallbacks2.java b/java/ql/test/stubs/google-android-9.0.0/android/content/ComponentCallbacks2.java new file mode 100644 index 00000000000..f8c83ab104d --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/ComponentCallbacks2.java @@ -0,0 +1,17 @@ +// Generated automatically from android.content.ComponentCallbacks2 for testing purposes + +package android.content; + +import android.content.ComponentCallbacks; + +public interface ComponentCallbacks2 extends ComponentCallbacks +{ + static int TRIM_MEMORY_BACKGROUND = 0; + static int TRIM_MEMORY_COMPLETE = 0; + static int TRIM_MEMORY_MODERATE = 0; + static int TRIM_MEMORY_RUNNING_CRITICAL = 0; + static int TRIM_MEMORY_RUNNING_LOW = 0; + static int TRIM_MEMORY_RUNNING_MODERATE = 0; + static int TRIM_MEMORY_UI_HIDDEN = 0; + void onTrimMemory(int p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/ComponentName.java b/java/ql/test/stubs/google-android-9.0.0/android/content/ComponentName.java new file mode 100644 index 00000000000..2c72a0a5125 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/ComponentName.java @@ -0,0 +1,35 @@ +// Generated automatically from android.content.ComponentName for testing purposes + +package android.content; + +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; + +public class ComponentName implements Cloneable, Comparable, Parcelable +{ + protected ComponentName() {} + public ComponentName clone(){ return null; } + public ComponentName(Context p0, Class p1){} + public ComponentName(Context p0, String p1){} + public ComponentName(Parcel p0){} + public ComponentName(String p0, String p1){} + public String flattenToShortString(){ return null; } + public String flattenToString(){ return null; } + public String getClassName(){ return null; } + public String getPackageName(){ return null; } + public String getShortClassName(){ return null; } + public String toShortString(){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public int compareTo(ComponentName p0){ return 0; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static ComponentName createRelative(Context p0, String p1){ return null; } + public static ComponentName createRelative(String p0, String p1){ return null; } + public static ComponentName readFromParcel(Parcel p0){ return null; } + public static ComponentName unflattenFromString(String p0){ return null; } + public static Parcelable.Creator CREATOR = null; + public static void writeToParcel(ComponentName p0, Parcel p1){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/ContentProviderClient.java b/java/ql/test/stubs/google-android-9.0.0/android/content/ContentProviderClient.java new file mode 100644 index 00000000000..b1b171f7649 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/ContentProviderClient.java @@ -0,0 +1,46 @@ +// Generated automatically from android.content.ContentProviderClient for testing purposes + +package android.content; + +import android.content.ContentProvider; +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentValues; +import android.content.res.AssetFileDescriptor; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.os.CancellationSignal; +import android.os.ParcelFileDescriptor; +import java.util.ArrayList; + +public class ContentProviderClient implements AutoCloseable +{ + protected void finalize(){} + public AssetFileDescriptor openAssetFile(Uri p0, String p1){ return null; } + public AssetFileDescriptor openAssetFile(Uri p0, String p1, CancellationSignal p2){ return null; } + public Bundle call(String p0, String p1, Bundle p2){ return null; } + public Bundle call(String p0, String p1, String p2, Bundle p3){ return null; } + public ContentProvider getLocalContentProvider(){ return null; } + public ContentProviderResult[] applyBatch(ArrayList p0){ return null; } + public ContentProviderResult[] applyBatch(String p0, ArrayList p1){ return null; } + public Cursor query(Uri p0, String[] p1, Bundle p2, CancellationSignal p3){ return null; } + public Cursor query(Uri p0, String[] p1, String p2, String[] p3, String p4){ return null; } + public Cursor query(Uri p0, String[] p1, String p2, String[] p3, String p4, CancellationSignal p5){ return null; } + public ParcelFileDescriptor openFile(Uri p0, String p1){ return null; } + public ParcelFileDescriptor openFile(Uri p0, String p1, CancellationSignal p2){ return null; } + public String getType(Uri p0){ return null; } + public String[] getStreamTypes(Uri p0, String p1){ return null; } + public Uri insert(Uri p0, ContentValues p1){ return null; } + public boolean refresh(Uri p0, Bundle p1, CancellationSignal p2){ return false; } + public boolean release(){ return false; } + public final AssetFileDescriptor openTypedAssetFile(Uri p0, String p1, Bundle p2, CancellationSignal p3){ return null; } + public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri p0, String p1, Bundle p2){ return null; } + public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri p0, String p1, Bundle p2, CancellationSignal p3){ return null; } + public final Uri canonicalize(Uri p0){ return null; } + public final Uri uncanonicalize(Uri p0){ return null; } + public int bulkInsert(Uri p0, ContentValues[] p1){ return 0; } + public int delete(Uri p0, String p1, String[] p2){ return 0; } + public int update(Uri p0, ContentValues p1, String p2, String[] p3){ return 0; } + public void close(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/ContentProviderOperation.java b/java/ql/test/stubs/google-android-9.0.0/android/content/ContentProviderOperation.java new file mode 100644 index 00000000000..168b33fd7d6 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/ContentProviderOperation.java @@ -0,0 +1,47 @@ +// Generated automatically from android.content.ContentProviderOperation for testing purposes + +package android.content; + +import android.content.ContentProvider; +import android.content.ContentProviderResult; +import android.content.ContentValues; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +public class ContentProviderOperation implements Parcelable +{ + protected ContentProviderOperation() {} + public ContentProviderResult apply(ContentProvider p0, ContentProviderResult[] p1, int p2){ return null; } + public ContentValues resolveValueBackReferences(ContentProviderResult[] p0, int p1){ return null; } + public String toString(){ return null; } + public String[] resolveSelectionArgsBackReferences(ContentProviderResult[] p0, int p1){ return null; } + public Uri getUri(){ return null; } + public boolean isAssertQuery(){ return false; } + public boolean isDelete(){ return false; } + public boolean isInsert(){ return false; } + public boolean isReadOperation(){ return false; } + public boolean isUpdate(){ return false; } + public boolean isWriteOperation(){ return false; } + public boolean isYieldAllowed(){ return false; } + public int describeContents(){ return 0; } + public static ContentProviderOperation.Builder newAssertQuery(Uri p0){ return null; } + public static ContentProviderOperation.Builder newDelete(Uri p0){ return null; } + public static ContentProviderOperation.Builder newInsert(Uri p0){ return null; } + public static ContentProviderOperation.Builder newUpdate(Uri p0){ return null; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} + static public class Builder + { + protected Builder() {} + public ContentProviderOperation build(){ return null; } + public ContentProviderOperation.Builder withExpectedCount(int p0){ return null; } + public ContentProviderOperation.Builder withSelection(String p0, String[] p1){ return null; } + public ContentProviderOperation.Builder withSelectionBackReference(int p0, int p1){ return null; } + public ContentProviderOperation.Builder withValue(String p0, Object p1){ return null; } + public ContentProviderOperation.Builder withValueBackReference(String p0, int p1){ return null; } + public ContentProviderOperation.Builder withValueBackReferences(ContentValues p0){ return null; } + public ContentProviderOperation.Builder withValues(ContentValues p0){ return null; } + public ContentProviderOperation.Builder withYieldAllowed(boolean p0){ return null; } + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/ContentProviderResult.java b/java/ql/test/stubs/google-android-9.0.0/android/content/ContentProviderResult.java new file mode 100644 index 00000000000..e29865ee4c1 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/ContentProviderResult.java @@ -0,0 +1,21 @@ +// Generated automatically from android.content.ContentProviderResult for testing purposes + +package android.content; + +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +public class ContentProviderResult implements Parcelable +{ + protected ContentProviderResult() {} + public ContentProviderResult(Parcel p0){} + public ContentProviderResult(Uri p0){} + public ContentProviderResult(int p0){} + public String toString(){ return null; } + public final Integer count = null; + public final Uri uri = null; + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/ContentResolver.java b/java/ql/test/stubs/google-android-9.0.0/android/content/ContentResolver.java new file mode 100644 index 00000000000..a5b7f1b0b46 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/ContentResolver.java @@ -0,0 +1,148 @@ +// Generated automatically from android.content.ContentResolver for testing purposes + +package android.content; + +import android.accounts.Account; +import android.content.ContentProvider; +import android.content.ContentProviderClient; +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentValues; +import android.content.Context; +import android.content.PeriodicSync; +import android.content.SyncAdapterType; +import android.content.SyncInfo; +import android.content.SyncRequest; +import android.content.SyncStatusObserver; +import android.content.UriPermission; +import android.content.res.AssetFileDescriptor; +import android.database.ContentObserver; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.drawable.Icon; +import android.net.Uri; +import android.os.Bundle; +import android.os.CancellationSignal; +import android.os.ParcelFileDescriptor; +import android.util.Size; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +abstract public class ContentResolver +{ + protected ContentResolver() {} + public Bitmap loadThumbnail(Uri p0, Size p1, CancellationSignal p2){ return null; } + public ContentProviderResult[] applyBatch(String p0, ArrayList p1){ return null; } + public ContentResolver(Context p0){} + public List getOutgoingPersistedUriPermissions(){ return null; } + public List getPersistedUriPermissions(){ return null; } + public String[] getStreamTypes(Uri p0, String p1){ return null; } + public final AssetFileDescriptor openAssetFile(Uri p0, String p1, CancellationSignal p2){ return null; } + public final AssetFileDescriptor openAssetFileDescriptor(Uri p0, String p1){ return null; } + public final AssetFileDescriptor openAssetFileDescriptor(Uri p0, String p1, CancellationSignal p2){ return null; } + public final AssetFileDescriptor openTypedAssetFile(Uri p0, String p1, Bundle p2, CancellationSignal p3){ return null; } + public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri p0, String p1, Bundle p2){ return null; } + public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri p0, String p1, Bundle p2, CancellationSignal p3){ return null; } + public final Bundle call(String p0, String p1, String p2, Bundle p3){ return null; } + public final Bundle call(Uri p0, String p1, String p2, Bundle p3){ return null; } + public final ContentProviderClient acquireContentProviderClient(String p0){ return null; } + public final ContentProviderClient acquireContentProviderClient(Uri p0){ return null; } + public final ContentProviderClient acquireUnstableContentProviderClient(String p0){ return null; } + public final ContentProviderClient acquireUnstableContentProviderClient(Uri p0){ return null; } + public final ContentResolver.MimeTypeInfo getTypeInfo(String p0){ return null; } + public final Cursor query(Uri p0, String[] p1, Bundle p2, CancellationSignal p3){ return null; } + public final Cursor query(Uri p0, String[] p1, String p2, String[] p3, String p4){ return null; } + public final Cursor query(Uri p0, String[] p1, String p2, String[] p3, String p4, CancellationSignal p5){ return null; } + public final InputStream openInputStream(Uri p0){ return null; } + public final OutputStream openOutputStream(Uri p0){ return null; } + public final OutputStream openOutputStream(Uri p0, String p1){ return null; } + public final ParcelFileDescriptor openFile(Uri p0, String p1, CancellationSignal p2){ return null; } + public final ParcelFileDescriptor openFileDescriptor(Uri p0, String p1){ return null; } + public final ParcelFileDescriptor openFileDescriptor(Uri p0, String p1, CancellationSignal p2){ return null; } + public final String getType(Uri p0){ return null; } + public final Uri canonicalize(Uri p0){ return null; } + public final Uri insert(Uri p0, ContentValues p1){ return null; } + public final Uri uncanonicalize(Uri p0){ return null; } + public final boolean refresh(Uri p0, Bundle p1, CancellationSignal p2){ return false; } + public final int bulkInsert(Uri p0, ContentValues[] p1){ return 0; } + public final int delete(Uri p0, String p1, String[] p2){ return 0; } + public final int update(Uri p0, ContentValues p1, String p2, String[] p3){ return 0; } + public final void registerContentObserver(Uri p0, boolean p1, ContentObserver p2){} + public final void unregisterContentObserver(ContentObserver p0){} + public static ContentResolver wrap(ContentProvider p0){ return null; } + public static ContentResolver wrap(ContentProviderClient p0){ return null; } + public static List getPeriodicSyncs(Account p0, String p1){ return null; } + public static List getCurrentSyncs(){ return null; } + public static Object addStatusChangeListener(int p0, SyncStatusObserver p1){ return null; } + public static String ANY_CURSOR_ITEM_TYPE = null; + public static String CURSOR_DIR_BASE_TYPE = null; + public static String CURSOR_ITEM_BASE_TYPE = null; + public static String EXTRA_HONORED_ARGS = null; + public static String EXTRA_REFRESH_SUPPORTED = null; + public static String EXTRA_SIZE = null; + public static String EXTRA_TOTAL_COUNT = null; + public static String QUERY_ARG_LIMIT = null; + public static String QUERY_ARG_OFFSET = null; + public static String QUERY_ARG_SORT_COLLATION = null; + public static String QUERY_ARG_SORT_COLUMNS = null; + public static String QUERY_ARG_SORT_DIRECTION = null; + public static String QUERY_ARG_SQL_SELECTION = null; + public static String QUERY_ARG_SQL_SELECTION_ARGS = null; + public static String QUERY_ARG_SQL_SORT_ORDER = null; + public static String SCHEME_ANDROID_RESOURCE = null; + public static String SCHEME_CONTENT = null; + public static String SCHEME_FILE = null; + public static String SYNC_EXTRAS_ACCOUNT = null; + public static String SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS = null; + public static String SYNC_EXTRAS_DO_NOT_RETRY = null; + public static String SYNC_EXTRAS_EXPEDITED = null; + public static String SYNC_EXTRAS_FORCE = null; + public static String SYNC_EXTRAS_IGNORE_BACKOFF = null; + public static String SYNC_EXTRAS_IGNORE_SETTINGS = null; + public static String SYNC_EXTRAS_INITIALIZE = null; + public static String SYNC_EXTRAS_MANUAL = null; + public static String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = null; + public static String SYNC_EXTRAS_REQUIRE_CHARGING = null; + public static String SYNC_EXTRAS_UPLOAD = null; + public static SyncAdapterType[] getSyncAdapterTypes(){ return null; } + public static SyncInfo getCurrentSync(){ return null; } + public static boolean getMasterSyncAutomatically(){ return false; } + public static boolean getSyncAutomatically(Account p0, String p1){ return false; } + public static boolean isSyncActive(Account p0, String p1){ return false; } + public static boolean isSyncPending(Account p0, String p1){ return false; } + public static int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 0; + public static int NOTIFY_SYNC_TO_NETWORK = 0; + public static int QUERY_SORT_DIRECTION_ASCENDING = 0; + public static int QUERY_SORT_DIRECTION_DESCENDING = 0; + public static int SYNC_OBSERVER_TYPE_ACTIVE = 0; + public static int SYNC_OBSERVER_TYPE_PENDING = 0; + public static int SYNC_OBSERVER_TYPE_SETTINGS = 0; + public static int getIsSyncable(Account p0, String p1){ return 0; } + public static void addPeriodicSync(Account p0, String p1, Bundle p2, long p3){} + public static void cancelSync(Account p0, String p1){} + public static void cancelSync(SyncRequest p0){} + public static void removePeriodicSync(Account p0, String p1, Bundle p2){} + public static void removeStatusChangeListener(Object p0){} + public static void requestSync(Account p0, String p1, Bundle p2){} + public static void requestSync(SyncRequest p0){} + public static void setIsSyncable(Account p0, String p1, int p2){} + public static void setMasterSyncAutomatically(boolean p0){} + public static void setSyncAutomatically(Account p0, String p1, boolean p2){} + public static void validateSyncExtrasBundle(Bundle p0){} + public void cancelSync(Uri p0){} + public void notifyChange(Uri p0, ContentObserver p1){} + public void notifyChange(Uri p0, ContentObserver p1, boolean p2){} + public void notifyChange(Uri p0, ContentObserver p1, int p2){} + public void releasePersistableUriPermission(Uri p0, int p1){} + public void startSync(Uri p0, Bundle p1){} + public void takePersistableUriPermission(Uri p0, int p1){} + static public class MimeTypeInfo + { + protected MimeTypeInfo() {} + public CharSequence getContentDescription(){ return null; } + public CharSequence getLabel(){ return null; } + public Icon getIcon(){ return null; } + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java b/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java index 6507cbd371d..a857f97e20f 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java @@ -1,964 +1,255 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Generated automatically from android.content.Context for testing purposes + package android.content; -import java.io.File; +import android.content.BroadcastReceiver; +import android.content.ComponentCallbacks; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.IntentSender; +import android.content.ServiceConnection; +import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.res.AssetManager; +import android.content.res.ColorStateList; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.database.DatabaseErrorHandler; +import android.database.sqlite.SQLiteDatabase; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.util.AttributeSet; +import android.view.Display; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.util.concurrent.Executor; -/** - * Interface to global information about an application environment. This is an - * abstract class whose implementation is provided by the Android system. It - * allows access to application-specific resources and classes, as well as - * up-calls for application-level operations such as launching activities, - * broadcasting and receiving intents, etc. - */ -public abstract class Context { - /** - * File creation mode: the default mode, where the created file can only - * be accessed by the calling application (or all applications sharing the - * same user ID). - * @see #MODE_WORLD_READABLE - * @see #MODE_WORLD_WRITEABLE - */ - public static final int MODE_PRIVATE = 0x0000; - - /** - * @deprecated Creating world-readable files is very dangerous, and likely - * to cause security holes in applications. It is strongly discouraged; - * instead, applications should use more formal mechanism for interactions - * such as {@link ContentProvider}, {@link BroadcastReceiver}, and - * {@link android.app.Service}. There are no guarantees that this - * access mode will remain on a file, such as when it goes through a - * backup and restore. - * File creation mode: allow all other applications to have read access - * to the created file. - * @see #MODE_PRIVATE - * @see #MODE_WORLD_WRITEABLE - */ - @Deprecated - public static final int MODE_WORLD_READABLE = 0x0001; - - /** - * @deprecated Creating world-writable files is very dangerous, and likely - * to cause security holes in applications. It is strongly discouraged; - * instead, applications should use more formal mechanism for interactions - * such as {@link ContentProvider}, {@link BroadcastReceiver}, and - * {@link android.app.Service}. There are no guarantees that this - * access mode will remain on a file, such as when it goes through a - * backup and restore. - * File creation mode: allow all other applications to have write access - * to the created file. - * @see #MODE_PRIVATE - * @see #MODE_WORLD_READABLE - */ - @Deprecated - public static final int MODE_WORLD_WRITEABLE = 0x0002; - - /** - * File creation mode: for use with {@link #openFileOutput}, if the file - * already exists then write data to the end of the existing file - * instead of erasing it. - * @see #openFileOutput - */ - public static final int MODE_APPEND = 0x8000; - - /** - * SharedPreference loading flag: when set, the file on disk will - * be checked for modification even if the shared preferences - * instance is already loaded in this process. This behavior is - * sometimes desired in cases where the application has multiple - * processes, all writing to the same SharedPreferences file. - * Generally there are better forms of communication between - * processes, though. - * - *

    This was the legacy (but undocumented) behavior in and - * before Gingerbread (Android 2.3) and this flag is implied when - * targetting such releases. For applications targetting SDK - * versions greater than Android 2.3, this flag must be - * explicitly set if desired. - * - * @see #getSharedPreferences - */ - public static final int MODE_MULTI_PROCESS = 0x0004; - - /** - * Database open flag: when set, the database is opened with write-ahead - * logging enabled by default. - * - * @see #openOrCreateDatabase(String, int, CursorFactory) - * @see #openOrCreateDatabase(String, int, CursorFactory, DatabaseErrorHandler) - * @see SQLiteDatabase#enableWriteAheadLogging - */ - public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 0x0008; - - /** - * Return the context of the single, global Application object of the current - * process. This generally should only be used if you need a Context whose - * lifecycle is separate from the current context, that is tied to the lifetime - * of the process rather than the current component. - * - *

    - * Consider for example how this interacts with - * {@link #registerReceiver(BroadcastReceiver, IntentFilter)}: - *

      - *
    • - *

      - * If used from an Activity context, the receiver is being registered within - * that activity. This means that you are expected to unregister before the - * activity is done being destroyed; in fact if you do not do so, the framework - * will clean up your leaked registration as it removes the activity and log an - * error. Thus, if you use the Activity context to register a receiver that is - * static (global to the process, not associated with an Activity instance) then - * that registration will be removed on you at whatever point the activity you - * used is destroyed. - *

    • - *

      - * If used from the Context returned here, the receiver is being registered with - * the global state associated with your application. Thus it will never be - * unregistered for you. This is necessary if the receiver is associated with - * static data, not a particular component. However using the ApplicationContext - * elsewhere can easily lead to serious leaks if you forget to unregister, - * unbind, etc. - *

    - */ +abstract public class Context +{ + public Context(){} + public Executor getMainExecutor(){ return null; } + public String getOpPackageName(){ return null; } + public abstract ApplicationInfo getApplicationInfo(); + public abstract AssetManager getAssets(); + public abstract ClassLoader getClassLoader(); + public abstract ComponentName startForegroundService(Intent p0); + public abstract ComponentName startService(Intent p0); + public abstract ContentResolver getContentResolver(); + public abstract Context createConfigurationContext(Configuration p0); + public abstract Context createContextForSplit(String p0); + public abstract Context createDeviceProtectedStorageContext(); + public abstract Context createDisplayContext(Display p0); + public abstract Context createPackageContext(String p0, int p1); public abstract Context getApplicationContext(); - - /** - * Returns the absolute path on the filesystem where a file created with - * {@link #openFileOutput} is stored. - *

    - * The returned path may change over time if the calling app is moved to an - * adopted storage device, so only relative paths should be persisted. - * - * @param name The name of the file for which you would like to get its path. - * - * @return An absolute path to the given file. - * - * @see #openFileOutput - * @see #getFilesDir - * @see #getDir - */ - public abstract File getFileStreamPath(String name); - - /** - * {@hide} - * Return the full path to the shared prefs file for the given prefs group name. - * - *

    Note: this is not generally useful for applications, since they should - * not be directly accessing the file system. - */ - public abstract File getSharedPrefsFile(String name); - - /** - * Retrieve and hold the contents of the preferences file 'name', returning - * a SharedPreferences through which you can retrieve and modify its - * values. Only one instance of the SharedPreferences object is returned - * to any callers for the same name, meaning they will see each other's - * edits as soon as they are made. - * - * @param name Desired preferences file. If a preferences file by this name - * does not exist, it will be created when you retrieve an - * editor (SharedPreferences.edit()) and then commit changes (Editor.commit()). - * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the - * default operation, {@link #MODE_WORLD_READABLE} - * and {@link #MODE_WORLD_WRITEABLE} to control permissions. The bit - * {@link #MODE_MULTI_PROCESS} can also be used if multiple processes - * are mutating the same SharedPreferences file. {@link #MODE_MULTI_PROCESS} - * is always on in apps targetting Gingerbread (Android 2.3) and below, and - * off by default in later versions. - * - * @return Returns the single SharedPreferences instance that can be used - * to retrieve and modify the preference values. - * - * @see #MODE_PRIVATE - * @see #MODE_WORLD_READABLE - * @see #MODE_WORLD_WRITEABLE - * @see #MODE_MULTI_PROCESS - */ - public abstract SharedPreferences getSharedPreferences(String name, - int mode); - - /** - * Returns the absolute path to the directory on the filesystem where all - * private files belonging to this app are stored. Apps should not use this path - * directly; they should instead use {@link #getFilesDir()}, - * {@link #getCacheDir()}, {@link #getDir(String, int)}, or other storage APIs - * on this class. - *

    - * The returned path may change over time if the calling app is moved to an - * adopted storage device, so only relative paths should be persisted. - *

    - * No additional permissions are required for the calling app to read or write - * files under the returned path. - * - * @see ApplicationInfo#dataDir - */ - public abstract File getDataDir(); - - /** - * Returns the absolute path to the directory on the filesystem where files - * created with {@link #openFileOutput} are stored. - *

    - * The returned path may change over time if the calling app is moved to an - * adopted storage device, so only relative paths should be persisted. - *

    - * No additional permissions are required for the calling app to read or write - * files under the returned path. - * - * @return The path of the directory holding application files. - * @see #openFileOutput - * @see #getFileStreamPath - * @see #getDir - */ - public abstract File getFilesDir(); - - /** - * Returns the absolute path to the directory on the filesystem similar to - * {@link #getFilesDir()}. The difference is that files placed under this - * directory will be excluded from automatic backup to remote storage. See - * {@link android.app.backup.BackupAgent BackupAgent} for a full discussion of - * the automatic backup mechanism in Android. - *

    - * The returned path may change over time if the calling app is moved to an - * adopted storage device, so only relative paths should be persisted. - *

    - * No additional permissions are required for the calling app to read or write - * files under the returned path. - * - * @return The path of the directory holding application files that will not be - * automatically backed up to remote storage. - * @see #openFileOutput - * @see #getFileStreamPath - * @see #getDir - * @see android.app.backup.BackupAgent - */ - public abstract File getNoBackupFilesDir(); - - /** - * Returns the absolute path to the directory on the primary shared/external - * storage device where the application can place persistent files it owns. - * These files are internal to the applications, and not typically visible to - * the user as media. - *

    - * This is like {@link #getFilesDir()} in that these files will be deleted when - * the application is uninstalled, however there are some important differences: - *

      - *
    • Shared storage may not always be available, since removable media can be - * ejected by the user. Media state can be checked using - * {@link Environment#getExternalStorageState(File)}. - *
    • There is no security enforced with these files. For example, any - * application holding - * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to these - * files. - *
    - *

    - * If a shared storage device is emulated (as determined by - * {@link Environment#isExternalStorageEmulated(File)}), it's contents are - * backed by a private user data partition, which means there is little benefit - * to storing data here instead of the private directories returned by - * {@link #getFilesDir()}, etc. - *

    - * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions are - * required to read or write to the returned path; it's always accessible to the - * calling app. This only applies to paths generated for package name of the - * calling application. To access paths belonging to other packages, - * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} and/or - * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required. - *

    - * On devices with multiple users (as described by {@link UserManager}), each - * user has their own isolated shared storage. Applications only have access to - * the shared storage for the user they're running as. - *

    - * The returned path may change over time if different shared storage media is - * inserted, so only relative paths should be persisted. - *

    - * Here is an example of typical code to manipulate a file in an application's - * shared storage: - *

    - * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java - * private_file} - *

    - * If you supply a non-null type to this function, the returned file - * will be a path to a sub-directory of the given type. Though these files are - * not automatically scanned by the media scanner, you can explicitly add them - * to the media database with - * {@link android.media.MediaScannerConnection#scanFile(Context, String[], String[], android.media.MediaScannerConnection.OnScanCompletedListener) - * MediaScannerConnection.scanFile}. Note that this is not the same as - * {@link android.os.Environment#getExternalStoragePublicDirectory - * Environment.getExternalStoragePublicDirectory()}, which provides directories - * of media shared by all applications. The directories returned here are owned - * by the application, and their contents will be removed when the application - * is uninstalled. Unlike - * {@link android.os.Environment#getExternalStoragePublicDirectory - * Environment.getExternalStoragePublicDirectory()}, the directory returned here - * will be automatically created for you. - *

    - * Here is an example of typical code to manipulate a picture in an - * application's shared storage and add it to the media database: - *

    - * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java - * private_picture} - * - * @param type The type of files directory to return. May be {@code null} for - * the root of the files directory or one of the following constants - * for a subdirectory: - * {@link android.os.Environment#DIRECTORY_MUSIC}, - * {@link android.os.Environment#DIRECTORY_PODCASTS}, - * {@link android.os.Environment#DIRECTORY_RINGTONES}, - * {@link android.os.Environment#DIRECTORY_ALARMS}, - * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS}, - * {@link android.os.Environment#DIRECTORY_PICTURES}, or - * {@link android.os.Environment#DIRECTORY_MOVIES}. - * @return the absolute path to application-specific directory. May return - * {@code null} if shared storage is not currently available. - * @see #getFilesDir - * @see #getExternalFilesDirs(String) - * @see Environment#getExternalStorageState(File) - * @see Environment#isExternalStorageEmulated(File) - * @see Environment#isExternalStorageRemovable(File) - */ - public abstract File getExternalFilesDir(String type); - - /** - * Returns absolute paths to application-specific directories on all - * shared/external storage devices where the application can place persistent - * files it owns. These files are internal to the application, and not typically - * visible to the user as media. - *

    - * This is like {@link #getFilesDir()} in that these files will be deleted when - * the application is uninstalled, however there are some important differences: - *

      - *
    • Shared storage may not always be available, since removable media can be - * ejected by the user. Media state can be checked using - * {@link Environment#getExternalStorageState(File)}. - *
    • There is no security enforced with these files. For example, any - * application holding - * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to these - * files. - *
    - *

    - * If a shared storage device is emulated (as determined by - * {@link Environment#isExternalStorageEmulated(File)}), it's contents are - * backed by a private user data partition, which means there is little benefit - * to storing data here instead of the private directories returned by - * {@link #getFilesDir()}, etc. - *

    - * Shared storage devices returned here are considered a stable part of the - * device, including physical media slots under a protective cover. The returned - * paths do not include transient devices, such as USB flash drives connected to - * handheld devices. - *

    - * An application may store data on any or all of the returned devices. For - * example, an app may choose to store large files on the device with the most - * available space, as measured by {@link StatFs}. - *

    - * No additional permissions are required for the calling app to read or write - * files under the returned path. Write access outside of these paths on - * secondary external storage devices is not available. - *

    - * The returned path may change over time if different shared storage media is - * inserted, so only relative paths should be persisted. - * - * @param type The type of files directory to return. May be {@code null} for - * the root of the files directory or one of the following constants - * for a subdirectory: - * {@link android.os.Environment#DIRECTORY_MUSIC}, - * {@link android.os.Environment#DIRECTORY_PODCASTS}, - * {@link android.os.Environment#DIRECTORY_RINGTONES}, - * {@link android.os.Environment#DIRECTORY_ALARMS}, - * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS}, - * {@link android.os.Environment#DIRECTORY_PICTURES}, or - * {@link android.os.Environment#DIRECTORY_MOVIES}. - * @return the absolute paths to application-specific directories. Some - * individual paths may be {@code null} if that shared storage is not - * currently available. The first path returned is the same as - * {@link #getExternalFilesDir(String)}. - * @see #getExternalFilesDir(String) - * @see Environment#getExternalStorageState(File) - * @see Environment#isExternalStorageEmulated(File) - * @see Environment#isExternalStorageRemovable(File) - */ - public abstract File[] getExternalFilesDirs(String type); - - /** - * Return the primary shared/external storage directory where this application's - * OBB files (if there are any) can be found. Note if the application does not - * have any OBB files, this directory may not exist. - *

    - * This is like {@link #getFilesDir()} in that these files will be deleted when - * the application is uninstalled, however there are some important differences: - *

      - *
    • Shared storage may not always be available, since removable media can be - * ejected by the user. Media state can be checked using - * {@link Environment#getExternalStorageState(File)}. - *
    • There is no security enforced with these files. For example, any - * application holding - * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to these - * files. - *
    - *

    - * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions are - * required to read or write to the path that this method returns. However, - * starting from {@link android.os.Build.VERSION_CODES#M}, to read the OBB - * expansion files, you must declare the - * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission in the - * app manifest and ask for permission at runtime as follows: - *

    - *

    - * {@code } - *

    - *

    - * Starting from {@link android.os.Build.VERSION_CODES#N}, - * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission is not - * required, so don't ask for this permission at runtime. To handle both cases, - * your app must first try to read the OBB file, and if it fails, you must - * request {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission - * at runtime. - *

    - * - *

    - * The following code snippet shows how to do this: - *

    - * - *
    -     * File obb = new File(obb_filename);
    -     * boolean open_failed = false;
    -     *
    -     * try {
    -     *     BufferedReader br = new BufferedReader(new FileReader(obb));
    -     *     open_failed = false;
    -     *     ReadObbFile(br);
    -     * } catch (IOException e) {
    -     *     open_failed = true;
    -     * }
    -     *
    -     * if (open_failed) {
    -     *     // request READ_EXTERNAL_STORAGE permission before reading OBB file
    -     *     ReadObbFileWithPermission();
    -     * }
    -     * 
    - * - * On devices with multiple users (as described by {@link UserManager}), - * multiple users may share the same OBB storage location. Applications should - * ensure that multiple instances running under different users don't interfere - * with each other. - * - * @return the absolute path to application-specific directory. May return - * {@code null} if shared storage is not currently available. - * @see #getObbDirs() - * @see Environment#getExternalStorageState(File) - * @see Environment#isExternalStorageEmulated(File) - * @see Environment#isExternalStorageRemovable(File) - */ - public abstract File getObbDir(); - - /** - * Returns absolute paths to application-specific directories on all - * shared/external storage devices where the application's OBB files (if there - * are any) can be found. Note if the application does not have any OBB files, - * these directories may not exist. - *

    - * This is like {@link #getFilesDir()} in that these files will be deleted when - * the application is uninstalled, however there are some important differences: - *

      - *
    • Shared storage may not always be available, since removable media can be - * ejected by the user. Media state can be checked using - * {@link Environment#getExternalStorageState(File)}. - *
    • There is no security enforced with these files. For example, any - * application holding - * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to these - * files. - *
    - *

    - * Shared storage devices returned here are considered a stable part of the - * device, including physical media slots under a protective cover. The returned - * paths do not include transient devices, such as USB flash drives connected to - * handheld devices. - *

    - * An application may store data on any or all of the returned devices. For - * example, an app may choose to store large files on the device with the most - * available space, as measured by {@link StatFs}. - *

    - * No additional permissions are required for the calling app to read or write - * files under the returned path. Write access outside of these paths on - * secondary external storage devices is not available. - * - * @return the absolute paths to application-specific directories. Some - * individual paths may be {@code null} if that shared storage is not - * currently available. The first path returned is the same as - * {@link #getObbDir()} - * @see #getObbDir() - * @see Environment#getExternalStorageState(File) - * @see Environment#isExternalStorageEmulated(File) - * @see Environment#isExternalStorageRemovable(File) - */ - public abstract File[] getObbDirs(); - - /** - * Returns the absolute path to the application specific cache directory on the - * filesystem. - *

    - * The system will automatically delete files in this directory as disk space is - * needed elsewhere on the device. The system will always delete older files - * first, as reported by {@link File#lastModified()}. If desired, you can exert - * more control over how files are deleted using - * {@link StorageManager#setCacheBehaviorGroup(File, boolean)} and - * {@link StorageManager#setCacheBehaviorTombstone(File, boolean)}. - *

    - * Apps are strongly encouraged to keep their usage of cache space below the - * quota returned by {@link StorageManager#getCacheQuotaBytes(java.util.UUID)}. - * If your app goes above this quota, your cached files will be some of the - * first to be deleted when additional disk space is needed. Conversely, if your - * app stays under this quota, your cached files will be some of the last to be - * deleted when additional disk space is needed. - *

    - * Note that your cache quota will change over time depending on how frequently - * the user interacts with your app, and depending on how much system-wide disk - * space is used. - *

    - * The returned path may change over time if the calling app is moved to an - * adopted storage device, so only relative paths should be persisted. - *

    - * Apps require no extra permissions to read or write to the returned path, - * since this path lives in their private storage. - * - * @return The path of the directory holding application cache files. - * @see #openFileOutput - * @see #getFileStreamPath - * @see #getDir - * @see #getExternalCacheDir - */ + public abstract Drawable getWallpaper(); + public abstract Drawable peekWallpaper(); public abstract File getCacheDir(); - - /** - * Returns the absolute path to the application specific cache directory on the - * filesystem designed for storing cached code. - *

    - * The system will delete any files stored in this location both when your - * specific application is upgraded, and when the entire platform is upgraded. - *

    - * This location is optimal for storing compiled or optimized code generated by - * your application at runtime. - *

    - * The returned path may change over time if the calling app is moved to an - * adopted storage device, so only relative paths should be persisted. - *

    - * Apps require no extra permissions to read or write to the returned path, - * since this path lives in their private storage. - * - * @return The path of the directory holding application code cache files. - */ public abstract File getCodeCacheDir(); - - /** - * Returns absolute path to application-specific directory on the primary - * shared/external storage device where the application can place cache files it - * owns. These files are internal to the application, and not typically visible - * to the user as media. - *

    - * This is like {@link #getCacheDir()} in that these files will be deleted when - * the application is uninstalled, however there are some important differences: - *

      - *
    • The platform does not always monitor the space available in shared - * storage, and thus may not automatically delete these files. Apps should - * always manage the maximum space used in this location. Currently the only - * time files here will be deleted by the platform is when running on - * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} or later and - * {@link Environment#isExternalStorageEmulated(File)} returns true. - *
    • Shared storage may not always be available, since removable media can be - * ejected by the user. Media state can be checked using - * {@link Environment#getExternalStorageState(File)}. - *
    • There is no security enforced with these files. For example, any - * application holding - * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to these - * files. - *
    - *

    - * If a shared storage device is emulated (as determined by - * {@link Environment#isExternalStorageEmulated(File)}), its contents are backed - * by a private user data partition, which means there is little benefit to - * storing data here instead of the private directory returned by - * {@link #getCacheDir()}. - *

    - * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions are - * required to read or write to the returned path; it's always accessible to the - * calling app. This only applies to paths generated for package name of the - * calling application. To access paths belonging to other packages, - * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} and/or - * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required. - *

    - * On devices with multiple users (as described by {@link UserManager}), each - * user has their own isolated shared storage. Applications only have access to - * the shared storage for the user they're running as. - *

    - * The returned path may change over time if different shared storage media is - * inserted, so only relative paths should be persisted. - * - * @return the absolute path to application-specific directory. May return - * {@code null} if shared storage is not currently available. - * @see #getCacheDir - * @see #getExternalCacheDirs() - * @see Environment#getExternalStorageState(File) - * @see Environment#isExternalStorageEmulated(File) - * @see Environment#isExternalStorageRemovable(File) - */ + public abstract File getDataDir(); + public abstract File getDatabasePath(String p0); + public abstract File getDir(String p0, int p1); public abstract File getExternalCacheDir(); - - /** - * Returns absolute path to application-specific directory in the preloaded - * cache. - *

    - * Files stored in the cache directory can be deleted when the device runs low - * on storage. There is no guarantee when these files will be deleted. - * - * @hide - */ - public abstract File getPreloadsFileCache(); - - /** - * Returns absolute paths to application-specific directories on all - * shared/external storage devices where the application can place cache files - * it owns. These files are internal to the application, and not typically - * visible to the user as media. - *

    - * This is like {@link #getCacheDir()} in that these files will be deleted when - * the application is uninstalled, however there are some important differences: - *

      - *
    • The platform does not always monitor the space available in shared - * storage, and thus may not automatically delete these files. Apps should - * always manage the maximum space used in this location. Currently the only - * time files here will be deleted by the platform is when running on - * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} or later and - * {@link Environment#isExternalStorageEmulated(File)} returns true. - *
    • Shared storage may not always be available, since removable media can be - * ejected by the user. Media state can be checked using - * {@link Environment#getExternalStorageState(File)}. - *
    • There is no security enforced with these files. For example, any - * application holding - * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to these - * files. - *
    - *

    - * If a shared storage device is emulated (as determined by - * {@link Environment#isExternalStorageEmulated(File)}), it's contents are - * backed by a private user data partition, which means there is little benefit - * to storing data here instead of the private directory returned by - * {@link #getCacheDir()}. - *

    - * Shared storage devices returned here are considered a stable part of the - * device, including physical media slots under a protective cover. The returned - * paths do not include transient devices, such as USB flash drives connected to - * handheld devices. - *

    - * An application may store data on any or all of the returned devices. For - * example, an app may choose to store large files on the device with the most - * available space, as measured by {@link StatFs}. - *

    - * No additional permissions are required for the calling app to read or write - * files under the returned path. Write access outside of these paths on - * secondary external storage devices is not available. - *

    - * The returned paths may change over time if different shared storage media is - * inserted, so only relative paths should be persisted. - * - * @return the absolute paths to application-specific directories. Some - * individual paths may be {@code null} if that shared storage is not - * currently available. The first path returned is the same as - * {@link #getExternalCacheDir()}. - * @see #getExternalCacheDir() - * @see Environment#getExternalStorageState(File) - * @see Environment#isExternalStorageEmulated(File) - * @see Environment#isExternalStorageRemovable(File) - */ + public abstract File getExternalFilesDir(String p0); + public abstract File getFileStreamPath(String p0); + public abstract File getFilesDir(); + public abstract File getNoBackupFilesDir(); + public abstract File getObbDir(); + public abstract FileInputStream openFileInput(String p0); + public abstract FileOutputStream openFileOutput(String p0, int p1); public abstract File[] getExternalCacheDirs(); - - /** - * Returns absolute paths to application-specific directories on all - * shared/external storage devices where the application can place media files. - * These files are scanned and made available to other apps through - * {@link MediaStore}. - *

    - * This is like {@link #getExternalFilesDirs} in that these files will be - * deleted when the application is uninstalled, however there are some important - * differences: - *

      - *
    • Shared storage may not always be available, since removable media can be - * ejected by the user. Media state can be checked using - * {@link Environment#getExternalStorageState(File)}. - *
    • There is no security enforced with these files. For example, any - * application holding - * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to these - * files. - *
    - *

    - * Shared storage devices returned here are considered a stable part of the - * device, including physical media slots under a protective cover. The returned - * paths do not include transient devices, such as USB flash drives connected to - * handheld devices. - *

    - * An application may store data on any or all of the returned devices. For - * example, an app may choose to store large files on the device with the most - * available space, as measured by {@link StatFs}. - *

    - * No additional permissions are required for the calling app to read or write - * files under the returned path. Write access outside of these paths on - * secondary external storage devices is not available. - *

    - * The returned paths may change over time if different shared storage media is - * inserted, so only relative paths should be persisted. - * - * @return the absolute paths to application-specific directories. Some - * individual paths may be {@code null} if that shared storage is not - * currently available. - * @see Environment#getExternalStorageState(File) - * @see Environment#isExternalStorageEmulated(File) - * @see Environment#isExternalStorageRemovable(File) - */ + public abstract File[] getExternalFilesDirs(String p0); public abstract File[] getExternalMediaDirs(); - - /** - * Returns an array of strings naming the private files associated with this - * Context's application package. - * - * @return Array of strings naming the private files. - * - * @see #openFileInput - * @see #openFileOutput - * @see #deleteFile - */ + public abstract File[] getObbDirs(); + public abstract Intent registerReceiver(BroadcastReceiver p0, IntentFilter p1); + public abstract Intent registerReceiver(BroadcastReceiver p0, IntentFilter p1, String p2, Handler p3); + public abstract Intent registerReceiver(BroadcastReceiver p0, IntentFilter p1, String p2, Handler p3, int p4); + public abstract Intent registerReceiver(BroadcastReceiver p0, IntentFilter p1, int p2); + public abstract Looper getMainLooper(); + public abstract Object getSystemService(String p0); + public abstract PackageManager getPackageManager(); + public abstract Resources getResources(); + public abstract Resources.Theme getTheme(); + public abstract SQLiteDatabase openOrCreateDatabase(String p0, int p1, SQLiteDatabase.CursorFactory p2); + public abstract SQLiteDatabase openOrCreateDatabase(String p0, int p1, SQLiteDatabase.CursorFactory p2, DatabaseErrorHandler p3); + public abstract SharedPreferences getSharedPreferences(String p0, int p1); + public abstract String getPackageCodePath(); + public abstract String getPackageName(); + public abstract String getPackageResourcePath(); + public abstract String getSystemServiceName(Class p0); + public abstract String[] databaseList(); public abstract String[] fileList(); - - /** - * Retrieve, creating if needed, a new directory in which the application can - * place its own custom data files. You can use the returned File object to - * create and access files in this directory. Note that files created through a - * File object will only be accessible by your own application; you can only set - * the mode of the entire directory, not of individual files. - *

    - * The returned path may change over time if the calling app is moved to an - * adopted storage device, so only relative paths should be persisted. - *

    - * Apps require no extra permissions to read or write to the returned path, - * since this path lives in their private storage. - * - * @param name Name of the directory to retrieve. This is a directory that is - * created as part of your application data. - * @param mode Operating mode. - * - * @return A {@link File} object for the requested directory. The directory will - * have been created if it does not already exist. - * - * @see #openFileOutput(String, int) - */ - public abstract File getDir(String name, int mode); - - /** - * Same as {@link #startActivity(Intent, Bundle)} with no options specified. - * - * @param intent The description of the activity to start. - * - * @throws ActivityNotFoundException   ` - * @see #startActivity(Intent, Bundle) - * @see PackageManager#resolveActivity - */ - public abstract void startActivity(Intent intent); - - /** - * Launch a new activity. You will not receive any information about when the - * activity exits. - * - *

    - * Note that if this method is being called from outside of an - * {@link android.app.Activity} Context, then the Intent must include the - * {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag. This is because, without - * being started from an existing Activity, there is no existing task in which - * to place the new activity and thus it needs to be placed in its own separate - * task. - * - *

    - * This method throws {@link ActivityNotFoundException} if there was no Activity - * found to run the given Intent. - * - * @param intent The description of the activity to start. - * @param options Additional options for how the Activity should be started. May - * be null if there are no options. See - * {@link android.app.ActivityOptions} for how to build the - * Bundle supplied here; there are no supported definitions for - * building it manually. - * - * @throws ActivityNotFoundException   - * - * @see #startActivity(Intent) - * @see PackageManager#resolveActivity - */ - public abstract void startActivity(Intent intent, Bundle options); - - /** - * Identifies whether this Context instance will be able to process calls to - * {@link #startActivityForResult(String, Intent, int, Bundle)}. - * - * @hide - */ - public boolean canStartActivityForResult() { - return false; - } - - /** - * Same as {@link #startActivities(Intent[], Bundle)} with no options specified. - * - * @param intents An array of Intents to be started. - * - * @throws ActivityNotFoundException   - * - * @see #startActivities(Intent[], Bundle) - * @see PackageManager#resolveActivity - */ - public abstract void startActivities(Intent[] intents); - - /** - * Launch multiple new activities. This is generally the same as calling - * {@link #startActivity(Intent)} for the first Intent in the array, that - * activity during its creation calling {@link #startActivity(Intent)} for the - * second entry, etc. Note that unlike that approach, generally none of the - * activities except the last in the array will be created at this point, but - * rather will be created when the user first visits them (due to pressing back - * from the activity on top). - * - *

    - * This method throws {@link ActivityNotFoundException} if there was no Activity - * found for any given Intent. In this case the state of the activity - * stack is undefined (some Intents in the list may be on it, some not), so you - * probably want to avoid such situations. - * - * @param intents An array of Intents to be started. - * @param options Additional options for how the Activity should be started. See - * {@link android.content.Context#startActivity(Intent, Bundle)} - * Context.startActivity(Intent, Bundle)} for more details. - * - * @throws ActivityNotFoundException   - * - * @see #startActivities(Intent[]) - * @see PackageManager#resolveActivity - */ - public abstract void startActivities(Intent[] intents, Bundle options); - - /** - * Broadcast the given intent to all interested BroadcastReceivers. This call is - * asynchronous; it returns immediately, and you will continue executing while - * the receivers are run. No results are propagated from receivers and receivers - * can not abort the broadcast. If you want to allow receivers to propagate - * results or abort the broadcast, you must send an ordered broadcast using - * {@link #sendOrderedBroadcast(Intent, String)}. - * - *

    - * See {@link BroadcastReceiver} for more information on Intent broadcasts. - * - * @param intent The Intent to broadcast; all receivers matching this Intent - * will receive the broadcast. - * - * @see android.content.BroadcastReceiver - * @see #registerReceiver - * @see #sendBroadcast(Intent, String) - * @see #sendOrderedBroadcast(Intent, String) - * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, - * String, Bundle) - */ - public abstract void sendBroadcast(Intent intent); - - /** - * Broadcast the given intent to all interested BroadcastReceivers, allowing an - * optional required permission to be enforced. This call is asynchronous; it - * returns immediately, and you will continue executing while the receivers are - * run. No results are propagated from receivers and receivers can not abort the - * broadcast. If you want to allow receivers to propagate results or abort the - * broadcast, you must send an ordered broadcast using - * {@link #sendOrderedBroadcast(Intent, String)}. - * - *

    - * See {@link BroadcastReceiver} for more information on Intent broadcasts. - * - * @param intent The Intent to broadcast; all receivers matching - * this Intent will receive the broadcast. - * @param receiverPermission (optional) String naming a permission that a - * receiver must hold in order to receive your - * broadcast. If null, no permission is required. - * - * @see android.content.BroadcastReceiver - * @see #registerReceiver - * @see #sendBroadcast(Intent) - * @see #sendOrderedBroadcast(Intent, String) - * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, - * String, Bundle) - */ - public abstract void sendBroadcast(Intent intent, String receiverPermission); - - /** - * Like {@link #sendBroadcast(Intent, String)}, but also allows specification of - * an associated app op as per {@link android.app.AppOpsManager}. - * - * @hide - */ - public abstract void sendBroadcast(Intent intent, String receiverPermission, int appOp); - - /** - * Broadcast the given intent to all interested BroadcastReceivers, allowing - * an array of required permissions to be enforced. This call is asynchronous; it returns - * immediately, and you will continue executing while the receivers are run. No results are - * propagated from receivers and receivers can not abort the broadcast. If you want to allow - * receivers to propagate results or abort the broadcast, you must send an ordered broadcast - * using {@link #sendOrderedBroadcast(Intent, String)}. - * - *

    See {@link BroadcastReceiver} for more information on Intent broadcasts. - * - * @param intent The Intent to broadcast; all receivers matching this - * Intent will receive the broadcast. - * @param receiverPermissions Array of names of permissions that a receiver must hold - * in order to receive your broadcast. - * If empty, no permissions are required. - * - * @see android.content.BroadcastReceiver - * @see #registerReceiver - * @see #sendBroadcast(Intent) - * @see #sendOrderedBroadcast(Intent, String) - * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) - * @hide - */ - public abstract void sendBroadcastWithMultiplePermissions (Intent intent, String[] receiverPermissions); - - /** - * Broadcast the given intent to all interested BroadcastReceivers, delivering - * them one at a time to allow more preferred receivers to consume the - * broadcast before it is delivered to less preferred receivers. This - * call is asynchronous; it returns immediately, and you will continue - * executing while the receivers are run. - * - *

    See {@link BroadcastReceiver} for more information on Intent broadcasts. - * - * @param intent The Intent to broadcast; all receivers matching this - * Intent will receive the broadcast. - * @param receiverPermission (optional) String naming a permissions that - * a receiver must hold in order to receive your broadcast. - * If null, no permission is required. - * - * @see android.content.BroadcastReceiver - * @see #registerReceiver - * @see #sendBroadcast(Intent) - * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) - */ - public abstract void sendOrderedBroadcast(Intent intent, String receiverPermission); -} \ No newline at end of file + public abstract boolean bindService(Intent p0, ServiceConnection p1, int p2); + public abstract boolean deleteDatabase(String p0); + public abstract boolean deleteFile(String p0); + public abstract boolean deleteSharedPreferences(String p0); + public abstract boolean isDeviceProtectedStorage(); + public abstract boolean moveDatabaseFrom(Context p0, String p1); + public abstract boolean moveSharedPreferencesFrom(Context p0, String p1); + public abstract boolean startInstrumentation(ComponentName p0, String p1, Bundle p2); + public abstract boolean stopService(Intent p0); + public abstract int checkCallingOrSelfPermission(String p0); + public abstract int checkCallingOrSelfUriPermission(Uri p0, int p1); + public abstract int checkCallingPermission(String p0); + public abstract int checkCallingUriPermission(Uri p0, int p1); + public abstract int checkPermission(String p0, int p1, int p2); + public abstract int checkSelfPermission(String p0); + public abstract int checkUriPermission(Uri p0, String p1, String p2, int p3, int p4, int p5); + public abstract int checkUriPermission(Uri p0, int p1, int p2, int p3); + public abstract int getWallpaperDesiredMinimumHeight(); + public abstract int getWallpaperDesiredMinimumWidth(); + public abstract void clearWallpaper(); + public abstract void enforceCallingOrSelfPermission(String p0, String p1); + public abstract void enforceCallingOrSelfUriPermission(Uri p0, int p1, String p2); + public abstract void enforceCallingPermission(String p0, String p1); + public abstract void enforceCallingUriPermission(Uri p0, int p1, String p2); + public abstract void enforcePermission(String p0, int p1, int p2, String p3); + public abstract void enforceUriPermission(Uri p0, String p1, String p2, int p3, int p4, int p5, String p6); + public abstract void enforceUriPermission(Uri p0, int p1, int p2, int p3, String p4); + public abstract void grantUriPermission(String p0, Uri p1, int p2); + public abstract void removeStickyBroadcast(Intent p0); + public abstract void removeStickyBroadcastAsUser(Intent p0, UserHandle p1); + public abstract void revokeUriPermission(String p0, Uri p1, int p2); + public abstract void revokeUriPermission(Uri p0, int p1); + public abstract void sendBroadcast(Intent p0); + public abstract void sendBroadcast(Intent p0, String p1); + public abstract void sendBroadcastAsUser(Intent p0, UserHandle p1); + public abstract void sendBroadcastAsUser(Intent p0, UserHandle p1, String p2); + // Slight cheat: this is an Android 11 function which shouldn't really be present in this Android 9 stub. + public abstract void sendBroadcastWithMultiplePermissions(Intent p1, String[] p2); + public abstract void sendOrderedBroadcast(Intent p0, String p1); + public abstract void sendOrderedBroadcast(Intent p0, String p1, BroadcastReceiver p2, Handler p3, int p4, String p5, Bundle p6); + public abstract void sendOrderedBroadcastAsUser(Intent p0, UserHandle p1, String p2, BroadcastReceiver p3, Handler p4, int p5, String p6, Bundle p7); + public abstract void sendStickyBroadcast(Intent p0); + public abstract void sendStickyBroadcastAsUser(Intent p0, UserHandle p1); + public abstract void sendStickyOrderedBroadcast(Intent p0, BroadcastReceiver p1, Handler p2, int p3, String p4, Bundle p5); + public abstract void sendStickyOrderedBroadcastAsUser(Intent p0, UserHandle p1, BroadcastReceiver p2, Handler p3, int p4, String p5, Bundle p6); + public abstract void setTheme(int p0); + public abstract void setWallpaper(Bitmap p0); + public abstract void setWallpaper(InputStream p0); + public abstract void startActivities(Intent[] p0); + public abstract void startActivities(Intent[] p0, Bundle p1); + public abstract void startActivity(Intent p0); + public abstract void startActivity(Intent p0, Bundle p1); + public abstract void startIntentSender(IntentSender p0, Intent p1, int p2, int p3, int p4); + public abstract void startIntentSender(IntentSender p0, Intent p1, int p2, int p3, int p4, Bundle p5); + public abstract void unbindService(ServiceConnection p0); + public abstract void unregisterReceiver(BroadcastReceiver p0); + public boolean bindIsolatedService(Intent p0, int p1, String p2, Executor p3, ServiceConnection p4){ return false; } + public boolean bindService(Intent p0, int p1, Executor p2, ServiceConnection p3){ return false; } + public boolean isRestricted(){ return false; } + public final T getSystemService(Class p0){ return null; } + public final CharSequence getText(int p0){ return null; } + public final ColorStateList getColorStateList(int p0){ return null; } + public final Drawable getDrawable(int p0){ return null; } + public final String getString(int p0){ return null; } + public final String getString(int p0, Object... p1){ return null; } + public final TypedArray obtainStyledAttributes(AttributeSet p0, int[] p1){ return null; } + public final TypedArray obtainStyledAttributes(AttributeSet p0, int[] p1, int p2, int p3){ return null; } + public final TypedArray obtainStyledAttributes(int p0, int[] p1){ return null; } + public final TypedArray obtainStyledAttributes(int[] p0){ return null; } + public final int getColor(int p0){ return 0; } + public static String ACCESSIBILITY_SERVICE = null; + public static String ACCOUNT_SERVICE = null; + public static String ACTIVITY_SERVICE = null; + public static String ALARM_SERVICE = null; + public static String APPWIDGET_SERVICE = null; + public static String APP_OPS_SERVICE = null; + public static String AUDIO_SERVICE = null; + public static String BATTERY_SERVICE = null; + public static String BIOMETRIC_SERVICE = null; + public static String BLUETOOTH_SERVICE = null; + public static String CAMERA_SERVICE = null; + public static String CAPTIONING_SERVICE = null; + public static String CARRIER_CONFIG_SERVICE = null; + public static String CLIPBOARD_SERVICE = null; + public static String COMPANION_DEVICE_SERVICE = null; + public static String CONNECTIVITY_SERVICE = null; + public static String CONSUMER_IR_SERVICE = null; + public static String CROSS_PROFILE_APPS_SERVICE = null; + public static String DEVICE_POLICY_SERVICE = null; + public static String DISPLAY_SERVICE = null; + public static String DOWNLOAD_SERVICE = null; + public static String DROPBOX_SERVICE = null; + public static String EUICC_SERVICE = null; + public static String FINGERPRINT_SERVICE = null; + public static String HARDWARE_PROPERTIES_SERVICE = null; + public static String INPUT_METHOD_SERVICE = null; + public static String INPUT_SERVICE = null; + public static String IPSEC_SERVICE = null; + public static String JOB_SCHEDULER_SERVICE = null; + public static String KEYGUARD_SERVICE = null; + public static String LAUNCHER_APPS_SERVICE = null; + public static String LAYOUT_INFLATER_SERVICE = null; + public static String LOCATION_SERVICE = null; + public static String MEDIA_PROJECTION_SERVICE = null; + public static String MEDIA_ROUTER_SERVICE = null; + public static String MEDIA_SESSION_SERVICE = null; + public static String MIDI_SERVICE = null; + public static String NETWORK_STATS_SERVICE = null; + public static String NFC_SERVICE = null; + public static String NOTIFICATION_SERVICE = null; + public static String NSD_SERVICE = null; + public static String POWER_SERVICE = null; + public static String PRINT_SERVICE = null; + public static String RESTRICTIONS_SERVICE = null; + public static String ROLE_SERVICE = null; + public static String SEARCH_SERVICE = null; + public static String SENSOR_SERVICE = null; + public static String SHORTCUT_SERVICE = null; + public static String STORAGE_SERVICE = null; + public static String STORAGE_STATS_SERVICE = null; + public static String SYSTEM_HEALTH_SERVICE = null; + public static String TELECOM_SERVICE = null; + public static String TELEPHONY_SERVICE = null; + public static String TELEPHONY_SUBSCRIPTION_SERVICE = null; + public static String TEXT_CLASSIFICATION_SERVICE = null; + public static String TEXT_SERVICES_MANAGER_SERVICE = null; + public static String TV_INPUT_SERVICE = null; + public static String UI_MODE_SERVICE = null; + public static String USAGE_STATS_SERVICE = null; + public static String USB_SERVICE = null; + public static String USER_SERVICE = null; + public static String VIBRATOR_SERVICE = null; + public static String WALLPAPER_SERVICE = null; + public static String WIFI_AWARE_SERVICE = null; + public static String WIFI_P2P_SERVICE = null; + public static String WIFI_RTT_RANGING_SERVICE = null; + public static String WIFI_SERVICE = null; + public static String WINDOW_SERVICE = null; + public static int BIND_ABOVE_CLIENT = 0; + public static int BIND_ADJUST_WITH_ACTIVITY = 0; + public static int BIND_ALLOW_OOM_MANAGEMENT = 0; + public static int BIND_AUTO_CREATE = 0; + public static int BIND_DEBUG_UNBIND = 0; + public static int BIND_EXTERNAL_SERVICE = 0; + public static int BIND_IMPORTANT = 0; + public static int BIND_INCLUDE_CAPABILITIES = 0; + public static int BIND_NOT_FOREGROUND = 0; + public static int BIND_NOT_PERCEPTIBLE = 0; + public static int BIND_WAIVE_PRIORITY = 0; + public static int CONTEXT_IGNORE_SECURITY = 0; + public static int CONTEXT_INCLUDE_CODE = 0; + public static int CONTEXT_RESTRICTED = 0; + public static int MODE_APPEND = 0; + public static int MODE_ENABLE_WRITE_AHEAD_LOGGING = 0; + public static int MODE_MULTI_PROCESS = 0; + public static int MODE_NO_LOCALIZED_COLLATORS = 0; + public static int MODE_PRIVATE = 0; + public static int MODE_WORLD_READABLE = 0; + public static int MODE_WORLD_WRITEABLE = 0; + public static int RECEIVER_VISIBLE_TO_INSTANT_APPS = 0; + public void registerComponentCallbacks(ComponentCallbacks p0){} + public void unregisterComponentCallbacks(ComponentCallbacks p0){} + public void updateServiceGroup(ServiceConnection p0, int p1, int p2){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/ContextWrapper.java b/java/ql/test/stubs/google-android-9.0.0/android/content/ContextWrapper.java new file mode 100644 index 00000000000..bffda8f1956 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/ContextWrapper.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.content; + +import android.content.BroadcastReceiver; +import android.content.ComponentCallbacks; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.IntentSender; +import android.content.ServiceConnection; +import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.res.AssetManager; +import android.content.res.ColorStateList; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.database.DatabaseErrorHandler; +import android.database.sqlite.SQLiteDatabase; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.util.AttributeSet; +import android.view.Display; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.util.concurrent.Executor; + +/** + * Proxying implementation of Context that simply delegates all of its calls to + * another Context. Can be subclassed to modify behavior without changing + * the original Context. + */ +public class ContextWrapper extends Context { + public ContextWrapper() { + } + + public ContextWrapper(Context base) { + } + + /** @hide **/ + public void startActivityForResult( + String who, Intent intent, int requestCode, Bundle options) { + } + + /** @hide **/ + public boolean canStartActivityForResult() { + return false; + } + + @Override public ApplicationInfo getApplicationInfo() { return null; } + @Override public AssetManager getAssets() { return null; } + @Override public ClassLoader getClassLoader() { return null; } + @Override public ComponentName startForegroundService(Intent p0) { return null; } + @Override public ComponentName startService(Intent p0) { return null; } + @Override public ContentResolver getContentResolver() { return null; } + @Override public Context createConfigurationContext(Configuration p0) { return null; } + @Override public Context createContextForSplit(String p0) { return null; } + @Override public Context createDeviceProtectedStorageContext() { return null; } + @Override public Context createDisplayContext(Display p0) { return null; } + @Override public Context createPackageContext(String p0, int p1) { return null; } + @Override public Context getApplicationContext() { return null; } + @Override public Drawable getWallpaper() { return null; } + @Override public Drawable peekWallpaper() { return null; } + @Override public File getCacheDir() { return null; } + @Override public File getCodeCacheDir() { return null; } + @Override public File getDataDir() { return null; } + @Override public File getDatabasePath(String p0) { return null; } + @Override public File getDir(String p0, int p1) { return null; } + @Override public File getExternalCacheDir() { return null; } + @Override public File getExternalFilesDir(String p0) { return null; } + @Override public File getFileStreamPath(String p0) { return null; } + @Override public File getFilesDir() { return null; } + @Override public File getNoBackupFilesDir() { return null; } + @Override public File getObbDir() { return null; } + @Override public FileInputStream openFileInput(String p0) { return null; } + @Override public FileOutputStream openFileOutput(String p0, int p1) { return null; } + @Override public File[] getExternalCacheDirs() { return null; } + @Override public File[] getExternalFilesDirs(String p0) { return null; } + @Override public File[] getExternalMediaDirs() { return null; } + @Override public File[] getObbDirs() { return null; } + @Override public Intent registerReceiver(BroadcastReceiver p0, IntentFilter p1) { return null; } + @Override public Intent registerReceiver(BroadcastReceiver p0, IntentFilter p1, String p2, Handler p3) { return null; } + @Override public Intent registerReceiver(BroadcastReceiver p0, IntentFilter p1, String p2, Handler p3, int p4) { return null; } + @Override public Intent registerReceiver(BroadcastReceiver p0, IntentFilter p1, int p2) { return null; } + @Override public Looper getMainLooper() { return null; } + @Override public Object getSystemService(String p0) { return null; } + @Override public PackageManager getPackageManager() { return null; } + @Override public Resources getResources() { return null; } + @Override public Resources.Theme getTheme() { return null; } + @Override public SQLiteDatabase openOrCreateDatabase(String p0, int p1, SQLiteDatabase.CursorFactory p2) { return null; } + @Override public SQLiteDatabase openOrCreateDatabase(String p0, int p1, SQLiteDatabase.CursorFactory p2, DatabaseErrorHandler p3) { return null; } + @Override public SharedPreferences getSharedPreferences(String p0, int p1) { return null; } + @Override public String getPackageCodePath() { return null; } + @Override public String getPackageName() { return null; } + @Override public String getPackageResourcePath() { return null; } + @Override public String getSystemServiceName(Class p0) { return null; } + @Override public String[] databaseList() { return null; } + @Override public String[] fileList() { return null; } + @Override public boolean bindService(Intent p0, ServiceConnection p1, int p2) { return false; } + @Override public boolean deleteDatabase(String p0) { return false; } + @Override public boolean deleteFile(String p0) { return false; } + @Override public boolean deleteSharedPreferences(String p0) { return false; } + @Override public boolean isDeviceProtectedStorage() { return false; } + @Override public boolean moveDatabaseFrom(Context p0, String p1) { return false; } + @Override public boolean moveSharedPreferencesFrom(Context p0, String p1) { return false; } + @Override public boolean startInstrumentation(ComponentName p0, String p1, Bundle p2) { return false; } + @Override public boolean stopService(Intent p0) { return false; } + @Override public int checkCallingOrSelfPermission(String p0) { return 0; } + @Override public int checkCallingOrSelfUriPermission(Uri p0, int p1) { return 0; } + @Override public int checkCallingPermission(String p0) { return 0; } + @Override public int checkCallingUriPermission(Uri p0, int p1) { return 0; } + @Override public int checkPermission(String p0, int p1, int p2) { return 0; } + @Override public int checkSelfPermission(String p0) { return 0; } + @Override public int checkUriPermission(Uri p0, String p1, String p2, int p3, int p4, int p5) { return 0; } + @Override public int checkUriPermission(Uri p0, int p1, int p2, int p3) { return 0; } + @Override public int getWallpaperDesiredMinimumHeight() { return 0; } + @Override public int getWallpaperDesiredMinimumWidth() { return 0; } + @Override public void clearWallpaper() { } + @Override public void enforceCallingOrSelfPermission(String p0, String p1) { } + @Override public void enforceCallingOrSelfUriPermission(Uri p0, int p1, String p2) { } + @Override public void enforceCallingPermission(String p0, String p1) { } + @Override public void enforceCallingUriPermission(Uri p0, int p1, String p2) { } + @Override public void enforcePermission(String p0, int p1, int p2, String p3) { } + @Override public void enforceUriPermission(Uri p0, String p1, String p2, int p3, int p4, int p5, String p6) { } + @Override public void enforceUriPermission(Uri p0, int p1, int p2, int p3, String p4) { } + @Override public void grantUriPermission(String p0, Uri p1, int p2) { } + @Override public void removeStickyBroadcast(Intent p0) { } + @Override public void removeStickyBroadcastAsUser(Intent p0, UserHandle p1) { } + @Override public void revokeUriPermission(String p0, Uri p1, int p2) { } + @Override public void revokeUriPermission(Uri p0, int p1) { } + @Override public void sendBroadcast(Intent p0) { } + @Override public void sendBroadcast(Intent p0, String p1) { } + @Override public void sendBroadcastAsUser(Intent p0, UserHandle p1) { } + @Override public void sendBroadcastAsUser(Intent p0, UserHandle p1, String p2) { } + // Slight cheat: this is an Android 11 function which shouldn't really be present in this Android 9 stub. + @Override public void sendBroadcastWithMultiplePermissions(Intent p1, String[] p2) { } + @Override public void sendOrderedBroadcast(Intent p0, String p1) { } + @Override public void sendOrderedBroadcast(Intent p0, String p1, BroadcastReceiver p2, Handler p3, int p4, String p5, Bundle p6) { } + @Override public void sendOrderedBroadcastAsUser(Intent p0, UserHandle p1, String p2, BroadcastReceiver p3, Handler p4, int p5, String p6, Bundle p7) { } + @Override public void sendStickyBroadcast(Intent p0) { } + @Override public void sendStickyBroadcastAsUser(Intent p0, UserHandle p1) { } + @Override public void sendStickyOrderedBroadcast(Intent p0, BroadcastReceiver p1, Handler p2, int p3, String p4, Bundle p5) { } + @Override public void sendStickyOrderedBroadcastAsUser(Intent p0, UserHandle p1, BroadcastReceiver p2, Handler p3, int p4, String p5, Bundle p6) { } + @Override public void setTheme(int p0) { } + @Override public void setWallpaper(Bitmap p0) { } + @Override public void setWallpaper(InputStream p0) { } + @Override public void startActivities(Intent[] p0) { } + @Override public void startActivities(Intent[] p0, Bundle p1) { } + @Override public void startActivity(Intent p0) { } + @Override public void startActivity(Intent p0, Bundle p1) { } + @Override public void startIntentSender(IntentSender p0, Intent p1, int p2, int p3, int p4) { } + @Override public void startIntentSender(IntentSender p0, Intent p1, int p2, int p3, int p4, Bundle p5) { } + @Override public void unbindService(ServiceConnection p0) { } + @Override public void unregisterReceiver(BroadcastReceiver p0) { } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/Intent.java b/java/ql/test/stubs/google-android-9.0.0/android/content/Intent.java index 09422c7a48a..fe8f9f5a942 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/content/Intent.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/Intent.java @@ -1,2074 +1,454 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Generated automatically from android.content.Intent for testing purposes + package android.content; +import android.content.ClipData; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.IntentSender; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.graphics.Rect; import android.net.Uri; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; - -import java.io.IOException; -import java.io.PrintWriter; +import android.util.AttributeSet; import java.io.Serializable; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.net.URISyntaxException; import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Objects; import java.util.Set; - -/** - * An intent is an abstract description of an operation to be performed. It can - * be used with {@link Context#startActivity(Intent) startActivity} to launch an - * {@link android.app.Activity}, - * {@link android.content.Context#sendBroadcast(Intent) broadcastIntent} to send - * it to any interested {@link BroadcastReceiver BroadcastReceiver} components, - * and {@link android.content.Context#startService} or - * {@link android.content.Context#bindService} to communicate with a background - * {@link android.app.Service}. - * - *

    - * An Intent provides a facility for performing late runtime binding between the - * code in different applications. Its most significant use is in the launching - * of activities, where it can be thought of as the glue between activities. It - * is basically a passive data structure holding an abstract description of an - * action to be performed. - *

    - * - *
    - *

    Developer Guides

    - *

    - * For information about how to create and resolve intents, read the - * Intents and - * Intent Filters developer guide. - *

    - *
    - * - * - *

    Intent Structure

    - *

    - * The primary pieces of information in an intent are: - *

    - * - *
      - *
    • - *

      - * action -- The general action to be performed, such as - * {@link #ACTION_VIEW}, {@link #ACTION_EDIT}, {@link #ACTION_MAIN}, etc. - *

      - *
    • - *
    • - *

      - * data -- The data to operate on, such as a person record in the - * contacts database, expressed as a {@link android.net.Uri}. - *

      - *
    • - *
    - * - * - *

    - * Some examples of action/data pairs are: - *

    - * - *
      - *
    • - *

      - * {@link #ACTION_VIEW} content://contacts/people/1 -- Display - * information about the person whose identifier is "1". - *

      - *
    • - *
    • - *

      - * {@link #ACTION_DIAL} content://contacts/people/1 -- Display the - * phone dialer with the person filled in. - *

      - *
    • - *
    • - *

      - * {@link #ACTION_VIEW} tel:123 -- Display the phone dialer with - * the given number filled in. Note how the VIEW action does what is considered - * the most reasonable thing for a particular URI. - *

      - *
    • - *
    • - *

      - * {@link #ACTION_DIAL} tel:123 -- Display the phone dialer with - * the given number filled in. - *

      - *
    • - *
    • - *

      - * {@link #ACTION_EDIT} content://contacts/people/1 -- Edit - * information about the person whose identifier is "1". - *

      - *
    • - *
    • - *

      - * {@link #ACTION_VIEW} content://contacts/people/ -- Display a - * list of people, which the user can browse through. This example is a typical - * top-level entry into the Contacts application, showing you the list of - * people. Selecting a particular person to view would result in a new intent { - * {@link #ACTION_VIEW} content://contacts/people/N } being used - * to start an activity to display that person. - *

      - *
    • - *
    - * - *

    - * In addition to these primary attributes, there are a number of secondary - * attributes that you can also include with an intent: - *

    - * - *
      - *
    • - *

      - * category -- Gives additional information about the action to execute. - * For example, {@link #CATEGORY_LAUNCHER} means it should appear in the - * Launcher as a top-level application, while {@link #CATEGORY_ALTERNATIVE} - * means it should be included in a list of alternative actions the user can - * perform on a piece of data. - *

      - *
    • - *

      - * type -- Specifies an explicit type (a MIME type) of the intent data. - * Normally the type is inferred from the data itself. By setting this - * attribute, you disable that evaluation and force an explicit type. - *

      - *
    • - *

      - * component -- Specifies an explicit name of a component class to use - * for the intent. Normally this is determined by looking at the other - * information in the intent (the action, data/type, and categories) and - * matching that with a component that can handle it. If this attribute is set - * then none of the evaluation is performed, and this component is used exactly - * as is. By specifying this attribute, all of the other Intent attributes - * become optional. - *

      - *
    • - *

      - * extras -- This is a {@link Bundle} of any additional information. This - * can be used to provide extended information to the component. For example, if - * we have a action to send an e-mail message, we could also include extra - * pieces of data here to supply a subject, body, etc. - *

      - *
    - * - *

    - * Here are some examples of other operations you can specify as intents using - * these additional parameters: - *

    - * - *
      - *
    • - *

      - * {@link #ACTION_MAIN} with category {@link #CATEGORY_HOME} -- Launch - * the home screen. - *

      - *
    • - *
    • - *

      - * {@link #ACTION_GET_CONTENT} with MIME type - * {@link android.provider.Contacts.Phones#CONTENT_URI - * vnd.android.cursor.item/phone} -- Display the list of people's phone - * numbers, allowing the user to browse through them and pick one and return it - * to the parent activity. - *

      - *
    • - *
    • - *

      - * {@link #ACTION_GET_CONTENT} with MIME type *{@literal /}* and - * category {@link #CATEGORY_OPENABLE} -- Display all pickers for data that - * can be opened with {@link ContentResolver#openInputStream(Uri) - * ContentResolver.openInputStream()}, allowing the user to pick one of them and - * then some data inside of it and returning the resulting URI to the caller. - * This can be used, for example, in an e-mail application to allow the user to - * pick some data to include as an attachment. - *

      - *
    • - *
    - * - *

    - * There are a variety of standard Intent action and category constants defined - * in the Intent class, but applications can also define their own. These - * strings use Java-style scoping, to ensure they are unique -- for example, the - * standard {@link #ACTION_VIEW} is called "android.intent.action.VIEW". - *

    - * - *

    - * Put together, the set of actions, data types, categories, and extra data - * defines a language for the system allowing for the expression of phrases such - * as "call john smith's cell". As applications are added to the system, they - * can extend this language by adding new actions, types, and categories, or - * they can modify the behavior of existing phrases by supplying their own - * activities that handle them. - *

    - * - * - *

    Intent Resolution

    - * - *

    - * There are two primary forms of intents you will use. - * - *

      - *
    • - *

      - * Explicit Intents have specified a component (via {@link #setComponent} - * or {@link #setClass}), which provides the exact class to be run. Often these - * will not include any other information, simply being a way for an application - * to launch various internal activities it has as the user interacts with the - * application. - * - *

    • - *

      - * Implicit Intents have not specified a component; instead, they must - * include enough information for the system to determine which of the available - * components is best to run for that intent. - *

    - * - *

    - * When using implicit intents, given such an arbitrary intent we need to know - * what to do with it. This is handled by the process of Intent - * resolution, which maps an Intent to an {@link android.app.Activity}, - * {@link BroadcastReceiver}, or {@link android.app.Service} (or sometimes two - * or more activities/receivers) that can handle it. - *

    - * - *

    - * The intent resolution mechanism basically revolves around matching an Intent - * against all of the <intent-filter> descriptions in the installed - * application packages. (Plus, in the case of broadcasts, any - * {@link BroadcastReceiver} objects explicitly registered with - * {@link Context#registerReceiver}.) More details on this can be found in the - * documentation on the {@link IntentFilter} class. - *

    - * - *

    - * There are three pieces of information in the Intent that are used for - * resolution: the action, type, and category. Using this information, a query - * is done on the {@link PackageManager} for a component that can handle the - * intent. The appropriate component is determined based on the intent - * information supplied in the AndroidManifest.xml file as follows: - *

    - * - *
      - *
    • - *

      - * The action, if given, must be listed by the component as one it - * handles. - *

      - *
    • - *

      - * The type is retrieved from the Intent's data, if not already supplied - * in the Intent. Like the action, if a type is included in the intent (either - * explicitly or implicitly in its data), then this must be listed by the - * component as one it handles. - *

      - *
    • For data that is not a content: URI and where no explicit - * type is included in the Intent, instead the scheme of the intent data - * (such as http: or mailto:) is considered. Again - * like the action, if we are matching a scheme it must be listed by the - * component as one it can handle. - *
    • - *

      - * The categories, if supplied, must all be listed by the - * activity as categories it handles. That is, if you include the categories - * {@link #CATEGORY_LAUNCHER} and {@link #CATEGORY_ALTERNATIVE}, then you will - * only resolve to components with an intent that lists both of those - * categories. Activities will very often need to support the - * {@link #CATEGORY_DEFAULT} so that they can be found by - * {@link Context#startActivity Context.startActivity()}. - *

      - *
    - * - *

    - * For example, consider the Note Pad sample application that allows a user to - * browse through a list of notes data and view details about individual items. - * Text in italics indicates places where you would replace a name with one - * specific to your own package. - *

    - * - *
    - *  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    - *       package="com.android.notepad">
    - *     <application android:icon="@drawable/app_notes"
    - *             android:label="@string/app_name">
    - *
    - *         <provider class=".NotePadProvider"
    - *                 android:authorities="com.google.provider.NotePad" />
    - *
    - *         <activity class=".NotesList" android:label="@string/title_notes_list">
    - *             <intent-filter>
    - *                 <action android:name="android.intent.action.MAIN" />
    - *                 <category android:name="android.intent.category.LAUNCHER" />
    - *             </intent-filter>
    - *             <intent-filter>
    - *                 <action android:name="android.intent.action.VIEW" />
    - *                 <action android:name="android.intent.action.EDIT" />
    - *                 <action android:name="android.intent.action.PICK" />
    - *                 <category android:name="android.intent.category.DEFAULT" />
    - *                 <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
    - *             </intent-filter>
    - *             <intent-filter>
    - *                 <action android:name="android.intent.action.GET_CONTENT" />
    - *                 <category android:name="android.intent.category.DEFAULT" />
    - *                 <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
    - *             </intent-filter>
    - *         </activity>
    - *
    - *         <activity class=".NoteEditor" android:label="@string/title_note">
    - *             <intent-filter android:label="@string/resolve_edit">
    - *                 <action android:name="android.intent.action.VIEW" />
    - *                 <action android:name="android.intent.action.EDIT" />
    - *                 <category android:name="android.intent.category.DEFAULT" />
    - *                 <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
    - *             </intent-filter>
    - *
    - *             <intent-filter>
    - *                 <action android:name="android.intent.action.INSERT" />
    - *                 <category android:name="android.intent.category.DEFAULT" />
    - *                 <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
    - *             </intent-filter>
    - *
    - *         </activity>
    - *
    - *         <activity class=".TitleEditor" android:label="@string/title_edit_title"
    - *                 android:theme="@android:style/Theme.Dialog">
    - *             <intent-filter android:label="@string/resolve_title">
    - *                 <action android:name="com.android.notepad.action.EDIT_TITLE" />
    - *                 <category android:name="android.intent.category.DEFAULT" />
    - *                 <category android:name="android.intent.category.ALTERNATIVE" />
    - *                 <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
    - *                 <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
    - *             </intent-filter>
    - *         </activity>
    - *
    - *     </application>
    - * </manifest>
    - * 
    - * - *

    - * The first activity, com.android.notepad.NotesList, serves as our - * main entry into the app. It can do three things as described by its three - * intent templates: - *

      - *
    1. - * - *
      - * <intent-filter>
      - *     <action android:name="{@link #ACTION_MAIN android.intent.action.MAIN}" />
      - *     <category android:name="{@link #CATEGORY_LAUNCHER android.intent.category.LAUNCHER}" />
      - * </intent-filter>
      - * 
      - *

      - * This provides a top-level entry into the NotePad application: the standard - * MAIN action is a main entry point (not requiring any other information in the - * Intent), and the LAUNCHER category says that this entry point should be - * listed in the application launcher. - *

      - *
    2. - * - *
      - * <intent-filter>
      - *     <action android:name="{@link #ACTION_VIEW android.intent.action.VIEW}" />
      - *     <action android:name="{@link #ACTION_EDIT android.intent.action.EDIT}" />
      - *     <action android:name="{@link #ACTION_PICK android.intent.action.PICK}" />
      - *     <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" />
      - *     <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
      - * </intent-filter>
      - * 
      - *

      - * This declares the things that the activity can do on a directory of notes. - * The type being supported is given with the <type> tag, where - * vnd.android.cursor.dir/vnd.google.note is a URI from which a - * Cursor of zero or more items (vnd.android.cursor.dir) can be - * retrieved which holds our note pad data (vnd.google.note). The - * activity allows the user to view or edit the directory of data (via the VIEW - * and EDIT actions), or to pick a particular note and return it to the caller - * (via the PICK action). Note also the DEFAULT category supplied here: this is - * required for the {@link Context#startActivity Context.startActivity} - * method to resolve your activity when its component name is not explicitly - * specified. - *

      - *
    3. - * - *
      - * <intent-filter>
      - *     <action android:name="{@link #ACTION_GET_CONTENT android.intent.action.GET_CONTENT}" />
      - *     <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" />
      - *     <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
      - * </intent-filter>
      - * 
      - *

      - * This filter describes the ability to return to the caller a note selected by - * the user without needing to know where it came from. The data type - * vnd.android.cursor.item/vnd.google.note is a URI from which a - * Cursor of exactly one (vnd.android.cursor.item) item can be - * retrieved which contains our note pad data (vnd.google.note). - * The GET_CONTENT action is similar to the PICK action, where the activity will - * return to its caller a piece of data selected by the user. Here, however, the - * caller specifies the type of data they desire instead of the type of data the - * user will be picking from. - *

      - *
    - * - *

    - * Given these capabilities, the following intents will resolve to the NotesList - * activity: - *

    - * - *
      - *
    • - *

      - * { action=android.app.action.MAIN } matches all of the activities that - * can be used as top-level entry points into an application. - *

      - *
    • - *

      - * { action=android.app.action.MAIN, category=android.app.category.LAUNCHER - * } is the actual intent used by the Launcher to populate its top-level - * list. - *

      - *
    • - *

      - * { action=android.intent.action.VIEW - * data=content://com.google.provider.NotePad/notes } displays a list of all - * the notes under "content://com.google.provider.NotePad/notes", which the user - * can browse through and see the details on. - *

      - *
    • - *

      - * { action=android.app.action.PICK - * data=content://com.google.provider.NotePad/notes } provides a list of the - * notes under "content://com.google.provider.NotePad/notes", from which the - * user can pick a note whose data URL is returned back to the caller. - *

      - *
    • - *

      - * { action=android.app.action.GET_CONTENT - * type=vnd.android.cursor.item/vnd.google.note } is similar to the pick - * action, but allows the caller to specify the kind of data they want back so - * that the system can find the appropriate activity to pick something of that - * data type. - *

      - *
    - * - *

    - * The second activity, com.android.notepad.NoteEditor, shows the - * user a single note entry and allows them to edit it. It can do two things as - * described by its two intent templates: - *

      - *
    1. - * - *
      - * <intent-filter android:label="@string/resolve_edit">
      - *     <action android:name="{@link #ACTION_VIEW android.intent.action.VIEW}" />
      - *     <action android:name="{@link #ACTION_EDIT android.intent.action.EDIT}" />
      - *     <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" />
      - *     <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
      - * </intent-filter>
      - * 
      - *

      - * The first, primary, purpose of this activity is to let the user interact with - * a single note, as decribed by the MIME type - * vnd.android.cursor.item/vnd.google.note. The activity can either - * VIEW a note or allow the user to EDIT it. Again we support the DEFAULT - * category to allow the activity to be launched without explicitly specifying - * its component. - *

      - *
    2. - * - *
      - * <intent-filter>
      - *     <action android:name="{@link #ACTION_INSERT android.intent.action.INSERT}" />
      - *     <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" />
      - *     <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
      - * </intent-filter>
      - * 
      - *

      - * The secondary use of this activity is to insert a new note entry into an - * existing directory of notes. This is used when the user creates a new note: - * the INSERT action is executed on the directory of notes, causing this - * activity to run and have the user create the new note data which it then adds - * to the content provider. - *

      - *
    - * - *

    - * Given these capabilities, the following intents will resolve to the - * NoteEditor activity: - *

    - * - *
      - *
    • - *

      - * { action=android.intent.action.VIEW - * data=content://com.google.provider.NotePad/notes/{ID} } shows - * the user the content of note {ID}. - *

      - *
    • - *

      - * { action=android.app.action.EDIT - * data=content://com.google.provider.NotePad/notes/{ID} } allows - * the user to edit the content of note {ID}. - *

      - *
    • - *

      - * { action=android.app.action.INSERT - * data=content://com.google.provider.NotePad/notes } creates a new, empty - * note in the notes list at "content://com.google.provider.NotePad/notes" and - * allows the user to edit it. If they keep their changes, the URI of the newly - * created note is returned to the caller. - *

      - *
    - * - *

    - * The last activity, com.android.notepad.TitleEditor, allows the - * user to edit the title of a note. This could be implemented as a class that - * the application directly invokes (by explicitly setting its component in the - * Intent), but here we show a way you can publish alternative operations on - * existing data: - *

    - * - *
    - * <intent-filter android:label="@string/resolve_title">
    - *     <action android:name="com.android.notepad.action.EDIT_TITLE" />
    - *     <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" />
    - *     <category android:name="{@link #CATEGORY_ALTERNATIVE android.intent.category.ALTERNATIVE}" />
    - *     <category android:name="{@link #CATEGORY_SELECTED_ALTERNATIVE android.intent.category.SELECTED_ALTERNATIVE}" />
    - *     <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
    - * </intent-filter>
    - * 
    - * - *

    - * In the single intent template here, we have created our own private action - * called com.android.notepad.action.EDIT_TITLE which means to edit - * the title of a note. It must be invoked on a specific note (data type - * vnd.android.cursor.item/vnd.google.note) like the previous view - * and edit actions, but here displays and edits the title contained in the note - * data. - * - *

    - * In addition to supporting the default category as usual, our title editor - * also supports two other standard categories: ALTERNATIVE and - * SELECTED_ALTERNATIVE. Implementing these categories allows others to find the - * special action it provides without directly knowing about it, through the - * {@link android.content.pm.PackageManager#queryIntentActivityOptions} method, - * or more often to build dynamic menu items with - * {@link android.view.Menu#addIntentOptions}. Note that in the intent template - * here was also supply an explicit name for the template (via - * android:label="@string/resolve_title") to better control what - * the user sees when presented with this activity as an alternative action to - * the data they are viewing. - * - *

    - * Given these capabilities, the following intent will resolve to the - * TitleEditor activity: - *

    - * - *
      - *
    • - *

      - * { action=com.android.notepad.action.EDIT_TITLE - * data=content://com.google.provider.NotePad/notes/{ID} } - * displays and allows the user to edit the title associated with note - * {ID}. - *

      - *
    - * - *

    Standard Activity Actions

    - * - *

    - * These are the current standard actions that Intent defines for launching - * activities (usually through {@link Context#startActivity}. The most - * important, and by far most frequently used, are {@link #ACTION_MAIN} and - * {@link #ACTION_EDIT}. - * - *

      - *
    • {@link #ACTION_MAIN} - *
    • {@link #ACTION_VIEW} - *
    • {@link #ACTION_ATTACH_DATA} - *
    • {@link #ACTION_EDIT} - *
    • {@link #ACTION_PICK} - *
    • {@link #ACTION_CHOOSER} - *
    • {@link #ACTION_GET_CONTENT} - *
    • {@link #ACTION_DIAL} - *
    • {@link #ACTION_CALL} - *
    • {@link #ACTION_SEND} - *
    • {@link #ACTION_SENDTO} - *
    • {@link #ACTION_ANSWER} - *
    • {@link #ACTION_INSERT} - *
    • {@link #ACTION_DELETE} - *
    • {@link #ACTION_RUN} - *
    • {@link #ACTION_SYNC} - *
    • {@link #ACTION_PICK_ACTIVITY} - *
    • {@link #ACTION_SEARCH} - *
    • {@link #ACTION_WEB_SEARCH} - *
    • {@link #ACTION_FACTORY_TEST} - *
    - * - *

    Standard Broadcast Actions

    - * - *

    - * These are the current standard actions that Intent defines for receiving - * broadcasts (usually through {@link Context#registerReceiver} or a - * <receiver> tag in a manifest). - * - *

      - *
    • {@link #ACTION_TIME_TICK} - *
    • {@link #ACTION_TIME_CHANGED} - *
    • {@link #ACTION_TIMEZONE_CHANGED} - *
    • {@link #ACTION_BOOT_COMPLETED} - *
    • {@link #ACTION_PACKAGE_ADDED} - *
    • {@link #ACTION_PACKAGE_CHANGED} - *
    • {@link #ACTION_PACKAGE_REMOVED} - *
    • {@link #ACTION_PACKAGE_RESTARTED} - *
    • {@link #ACTION_PACKAGE_DATA_CLEARED} - *
    • {@link #ACTION_PACKAGES_SUSPENDED} - *
    • {@link #ACTION_PACKAGES_UNSUSPENDED} - *
    • {@link #ACTION_UID_REMOVED} - *
    • {@link #ACTION_BATTERY_CHANGED} - *
    • {@link #ACTION_POWER_CONNECTED} - *
    • {@link #ACTION_POWER_DISCONNECTED} - *
    • {@link #ACTION_SHUTDOWN} - *
    - * - *

    Standard Categories

    - * - *

    - * These are the current standard categories that can be used to further clarify - * an Intent via {@link #addCategory}. - * - *

      - *
    • {@link #CATEGORY_DEFAULT} - *
    • {@link #CATEGORY_BROWSABLE} - *
    • {@link #CATEGORY_TAB} - *
    • {@link #CATEGORY_ALTERNATIVE} - *
    • {@link #CATEGORY_SELECTED_ALTERNATIVE} - *
    • {@link #CATEGORY_LAUNCHER} - *
    • {@link #CATEGORY_INFO} - *
    • {@link #CATEGORY_HOME} - *
    • {@link #CATEGORY_PREFERENCE} - *
    • {@link #CATEGORY_TEST} - *
    • {@link #CATEGORY_CAR_DOCK} - *
    • {@link #CATEGORY_DESK_DOCK} - *
    • {@link #CATEGORY_LE_DESK_DOCK} - *
    • {@link #CATEGORY_HE_DESK_DOCK} - *
    • {@link #CATEGORY_CAR_MODE} - *
    • {@link #CATEGORY_APP_MARKET} - *
    • {@link #CATEGORY_VR_HOME} - *
    - * - *

    Standard Extra Data

    - * - *

    - * These are the current standard fields that can be used as extra data via - * {@link #putExtra}. - * - *

      - *
    • {@link #EXTRA_ALARM_COUNT} - *
    • {@link #EXTRA_BCC} - *
    • {@link #EXTRA_CC} - *
    • {@link #EXTRA_CHANGED_COMPONENT_NAME} - *
    • {@link #EXTRA_DATA_REMOVED} - *
    • {@link #EXTRA_DOCK_STATE} - *
    • {@link #EXTRA_DOCK_STATE_HE_DESK} - *
    • {@link #EXTRA_DOCK_STATE_LE_DESK} - *
    • {@link #EXTRA_DOCK_STATE_CAR} - *
    • {@link #EXTRA_DOCK_STATE_DESK} - *
    • {@link #EXTRA_DOCK_STATE_UNDOCKED} - *
    • {@link #EXTRA_DONT_KILL_APP} - *
    • {@link #EXTRA_EMAIL} - *
    • {@link #EXTRA_INITIAL_INTENTS} - *
    • {@link #EXTRA_INTENT} - *
    • {@link #EXTRA_KEY_EVENT} - *
    • {@link #EXTRA_ORIGINATING_URI} - *
    • {@link #EXTRA_PHONE_NUMBER} - *
    • {@link #EXTRA_REFERRER} - *
    • {@link #EXTRA_REMOTE_INTENT_TOKEN} - *
    • {@link #EXTRA_REPLACING} - *
    • {@link #EXTRA_SHORTCUT_ICON} - *
    • {@link #EXTRA_SHORTCUT_ICON_RESOURCE} - *
    • {@link #EXTRA_SHORTCUT_INTENT} - *
    • {@link #EXTRA_STREAM} - *
    • {@link #EXTRA_SHORTCUT_NAME} - *
    • {@link #EXTRA_SUBJECT} - *
    • {@link #EXTRA_TEMPLATE} - *
    • {@link #EXTRA_TEXT} - *
    • {@link #EXTRA_TITLE} - *
    • {@link #EXTRA_UID} - *
    - * - *

    Flags

    - * - *

    - * These are the possible flags that can be used in the Intent via - * {@link #setFlags} and {@link #addFlags}. See {@link #setFlags} for a list of - * all possible flags. - */ -public class Intent implements Parcelable, Cloneable { - - /** - * Create an empty intent. - */ - public Intent() { - } - - /** - * Copy constructor. - */ - public Intent(Intent o) { - } - - /** - * Create an intent with a given action. All other fields (data, type, class) - * are null. Note that the action must be in a namespace because - * Intents are used globally in the system -- for example the system VIEW action - * is android.intent.action.VIEW; an application's custom action would be - * something like com.google.app.myapp.CUSTOM_ACTION. - * - * @param action The Intent action, such as ACTION_VIEW. - */ - public Intent(String action) { - } - - /** - * Create an intent with a given action and for a given data url. Note that the - * action must be in a namespace because Intents are used globally in - * the system -- for example the system VIEW action is - * android.intent.action.VIEW; an application's custom action would be something - * like com.google.app.myapp.CUSTOM_ACTION. - * - *

    - * Note: scheme and host name matching in the Android framework is - * case-sensitive, unlike the formal RFC. As a result, you should always ensure - * that you write your Uri with these elements using lower case letters, and - * normalize any Uris you receive from outside of Android to ensure the scheme - * and host is lower case. - *

    - * - * @param action The Intent action, such as ACTION_VIEW. - * @param uri The Intent data URI. - */ - public Intent(String action, Uri uri) { - } - - /** - * Create an intent for a specific component. All other fields (action, data, - * type, class) are null, though they can be modified later with explicit calls. - * This provides a convenient way to create an intent that is intended to - * execute a hard-coded class name, rather than relying on the system to find an - * appropriate class for you; see {@link #setComponent} for more information on - * the repercussions of this. - * - * @param packageContext A Context of the application package implementing this - * class. - * @param cls The component class that is to be used for the intent. - * - * @see #setClass - * @see #setComponent - * @see #Intent(String, android.net.Uri , Context, Class) - */ - public Intent(Context packageContext, Class cls) { - } - - /** - * Create an intent for a specific component with a specified action and data. - * This is equivalent to using {@link #Intent(String, android.net.Uri)} to - * construct the Intent and then calling {@link #setClass} to set its class. - * - *

    - * Note: scheme and host name matching in the Android framework is - * case-sensitive, unlike the formal RFC. As a result, you should always ensure - * that you write your Uri with these elements using lower case letters, and - * normalize any Uris you receive from outside of Android to ensure the scheme - * and host is lower case. - *

    - * - * @param action The Intent action, such as ACTION_VIEW. - * @param uri The Intent data URI. - * @param packageContext A Context of the application package implementing this - * class. - * @param cls The component class that is to be used for the intent. - * - * @see #Intent(String, android.net.Uri) - * @see #Intent(Context, Class) - * @see #setClass - * @see #setComponent - */ - public Intent(String action, Uri uri, Context packageContext, Class cls) { - } - - /** - * Call {@link #parseUri} with 0 flags. - * - * @deprecated Use {@link #parseUri} instead. - */ - @Deprecated - public static Intent getIntent(String uri) { - return null; - } - - /** - * Create an intent from a URI. This URI may encode the action, category, and - * other intent fields, if it was returned by {@link #toUri}. If the Intent was - * not generate by toUri(), its data will be the entire URI and its action will - * be ACTION_VIEW. - * - *

    - * The URI given here must not be relative -- that is, it must include the - * scheme and full path. - * - * @param uri The URI to turn into an Intent. - * @param flags Additional processing flags. - * - * @return Intent The newly created Intent object. - * - * @throws URISyntaxException Throws URISyntaxError if the basic URI syntax it - * bad (as parsed by the Uri class) or the Intent - * data within the URI is invalid. - * - * @see #toUri - */ - public static Intent parseUri(String uri, int flags) { - return null; - } - - /** - * Retrieve the general action to be performed, such as {@link #ACTION_VIEW}. - * The action describes the general way the rest of the information in the - * intent should be interpreted -- most importantly, what to do with the data - * returned by {@link #getData}. - * - * @return The action of this intent or null if none is specified. - * - * @see #setAction - */ - public String getAction() { - return null; - } - - /** - * Retrieve data this intent is operating on. This URI specifies the name of the - * data; often it uses the content: scheme, specifying data in a content - * provider. Other schemes may be handled by specific activities, such as http: - * by the web browser. - * - * @return The URI of the data this intent is targeting or null. - * - * @see #getScheme - * @see #setData - */ - public Uri getData() { - return null; - } - - /** - * The same as {@link #getData()}, but returns the URI as an encoded String. - */ - public String getDataString() { - return null; - } - - /** - * Return the scheme portion of the intent's data. If the data is null or does - * not include a scheme, null is returned. Otherwise, the scheme prefix without - * the final ':' is returned, i.e. "http". - * - *

    - * This is the same as calling getData().getScheme() (and checking for null - * data). - * - * @return The scheme of this intent. - * - * @see #getData - */ - public String getScheme() { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * - * @return the value of an item previously added with putExtra(), or null if - * none was found. - * - * @deprecated - * @hide - */ - @Deprecated - public Object getExtra(String name) { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * @param defaultValue the value to be returned if no value of the desired type - * is stored with the given name. - * - * @return the value of an item previously added with putExtra(), or the default - * value if none was found. - * - * @see #putExtra(String, boolean) - */ - public boolean getBooleanExtra(String name, boolean defaultValue) { - return false; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * @param defaultValue the value to be returned if no value of the desired type - * is stored with the given name. - * - * @return the value of an item previously added with putExtra(), or the default - * value if none was found. - * - * @see #putExtra(String, byte) - */ - public byte getByteExtra(String name, byte defaultValue) { - return -1; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * @param defaultValue the value to be returned if no value of the desired type - * is stored with the given name. - * - * @return the value of an item previously added with putExtra(), or the default - * value if none was found. - * - * @see #putExtra(String, short) - */ - public short getShortExtra(String name, short defaultValue) { - return -1; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * @param defaultValue the value to be returned if no value of the desired type - * is stored with the given name. - * - * @return the value of an item previously added with putExtra(), or the default - * value if none was found. - * - * @see #putExtra(String, char) - */ - public char getCharExtra(String name, char defaultValue) { - return 'a'; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * @param defaultValue the value to be returned if no value of the desired type - * is stored with the given name. - * - * @return the value of an item previously added with putExtra(), or the default - * value if none was found. - * - * @see #putExtra(String, int) - */ - public int getIntExtra(String name, int defaultValue) { - return -1; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * @param defaultValue the value to be returned if no value of the desired type - * is stored with the given name. - * - * @return the value of an item previously added with putExtra(), or the default - * value if none was found. - * - * @see #putExtra(String, long) - */ - public long getLongExtra(String name, long defaultValue) { - return -1; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * @param defaultValue the value to be returned if no value of the desired type - * is stored with the given name. - * - * @return the value of an item previously added with putExtra(), or the default - * value if no such item is present - * - * @see #putExtra(String, float) - */ - public float getFloatExtra(String name, float defaultValue) { - return -1; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * @param defaultValue the value to be returned if no value of the desired type - * is stored with the given name. - * - * @return the value of an item previously added with putExtra(), or the default - * value if none was found. - * - * @see #putExtra(String, double) - */ - public double getDoubleExtra(String name, double defaultValue) { - return -1; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * - * @return the value of an item previously added with putExtra(), or null if no - * String value was found. - * - * @see #putExtra(String, String) - */ - public String getStringExtra(String name) { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * - * @return the value of an item previously added with putExtra(), or null if no - * CharSequence value was found. - * - * @see #putExtra(String, CharSequence) - */ - public CharSequence getCharSequenceExtra(String name) { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * - * @return the value of an item previously added with putExtra(), or null if no - * Parcelable value was found. - * - * @see #putExtra(String, Parcelable) - */ - public T getParcelableExtra(String name) { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * - * @return the value of an item previously added with putExtra(), or null if no - * Parcelable[] value was found. - * - * @see #putExtra(String, Parcelable[]) - */ - public Parcelable[] getParcelableArrayExtra(String name) { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * - * @return the value of an item previously added with - * putParcelableArrayListExtra(), or null if no ArrayList - * value was found. - * - * @see #putParcelableArrayListExtra(String, ArrayList) - */ - public ArrayList getParcelableArrayListExtra(String name) { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * - * @return the value of an item previously added with putExtra(), or null if no - * Serializable value was found. - * - * @see #putExtra(String, Serializable) - */ - public Serializable getSerializableExtra(String name) { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * - * @return the value of an item previously added with - * putIntegerArrayListExtra(), or null if no ArrayList value - * was found. - * - * @see #putIntegerArrayListExtra(String, ArrayList) - */ - public ArrayList getIntegerArrayListExtra(String name) { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * - * @return the value of an item previously added with putStringArrayListExtra(), - * or null if no ArrayList value was found. - * - * @see #putStringArrayListExtra(String, ArrayList) - */ - public ArrayList getStringArrayListExtra(String name) { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * - * @return the value of an item previously added with - * putCharSequenceArrayListExtra, or null if no ArrayList - * value was found. - * - * @see #putCharSequenceArrayListExtra(String, ArrayList) - */ - public ArrayList getCharSequenceArrayListExtra(String name) { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * - * @return the value of an item previously added with putExtra(), or null if no - * boolean array value was found. - * - * @see #putExtra(String, boolean[]) - */ - public boolean[] getBooleanArrayExtra(String name) { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * - * @return the value of an item previously added with putExtra(), or null if no - * byte array value was found. - * - * @see #putExtra(String, byte[]) - */ - public byte[] getByteArrayExtra(String name) { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * - * @return the value of an item previously added with putExtra(), or null if no - * short array value was found. - * - * @see #putExtra(String, short[]) - */ - public short[] getShortArrayExtra(String name) { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * - * @return the value of an item previously added with putExtra(), or null if no - * char array value was found. - * - * @see #putExtra(String, char[]) - */ - public char[] getCharArrayExtra(String name) { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * - * @return the value of an item previously added with putExtra(), or null if no - * int array value was found. - * - * @see #putExtra(String, int[]) - */ - public int[] getIntArrayExtra(String name) { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * - * @return the value of an item previously added with putExtra(), or null if no - * long array value was found. - * - * @see #putExtra(String, long[]) - */ - public long[] getLongArrayExtra(String name) { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * - * @return the value of an item previously added with putExtra(), or null if no - * float array value was found. - * - * @see #putExtra(String, float[]) - */ - public float[] getFloatArrayExtra(String name) { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * - * @return the value of an item previously added with putExtra(), or null if no - * double array value was found. - * - * @see #putExtra(String, double[]) - */ - public double[] getDoubleArrayExtra(String name) { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * - * @return the value of an item previously added with putExtra(), or null if no - * String array value was found. - * - * @see #putExtra(String, String[]) - */ - public String[] getStringArrayExtra(String name) { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * - * @return the value of an item previously added with putExtra(), or null if no - * CharSequence array value was found. - * - * @see #putExtra(String, CharSequence[]) - */ - public CharSequence[] getCharSequenceArrayExtra(String name) { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * - * @return the value of an item previously added with putExtra(), or null if no - * Bundle value was found. - * - * @see #putExtra(String, Bundle) - */ - public Bundle getBundleExtra(String name) { - return null; - } - - /** - * Retrieve extended data from the intent. - * - * @param name The name of the desired item. - * @param defaultValue The default value to return in case no item is associated - * with the key 'name' - * - * @return the value of an item previously added with putExtra(), or - * defaultValue if none was found. - * - * @see #putExtra - * - * @deprecated - * @hide - */ - @Deprecated - public Object getExtra(String name, Object defaultValue) { - return null; - } - - /** - * Retrieves a map of extended data from the intent. - * - * @return the map of all extras previously added with putExtra(), or null if - * none have been added. - */ - public Bundle getExtras() { - return null; - } - - /** - * Filter extras to only basic types. - * - * @hide - */ - public void removeUnsafeExtras() { - } - - /** - * Set the general action to be performed. - * - * @param action An action name, such as ACTION_VIEW. Application-specific - * actions should be prefixed with the vendor's package name. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #getAction - */ - public Intent setAction(String action) { - return null; - } - - /** - * Set the data this intent is operating on. This method automatically clears - * any type that was previously set by {@link #setType} or - * {@link #setTypeAndNormalize}. - * - *

    - * Note: scheme matching in the Android framework is case-sensitive, unlike - * the formal RFC. As a result, you should always write your Uri with a lower - * case scheme, or use {@link Uri#normalizeScheme} or - * {@link #setDataAndNormalize} to ensure that the scheme is converted to lower - * case. - * - * @param data The Uri of the data this intent is now targeting. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #getData - * @see #setDataAndNormalize - * @see android.net.Uri#normalizeScheme() - */ - public Intent setData(Uri data) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The boolean data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getBooleanExtra(String, boolean) - */ - public Intent putExtra(String name, boolean value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The byte data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getByteExtra(String, byte) - */ - public Intent putExtra(String name, byte value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The char data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getCharExtra(String, char) - */ - public Intent putExtra(String name, char value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The short data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getShortExtra(String, short) - */ - public Intent putExtra(String name, short value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The integer data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getIntExtra(String, int) - */ - public Intent putExtra(String name, int value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The long data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getLongExtra(String, long) - */ - public Intent putExtra(String name, long value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The float data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getFloatExtra(String, float) - */ - public Intent putExtra(String name, float value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The double data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getDoubleExtra(String, double) - */ - public Intent putExtra(String name, double value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The String data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getStringExtra(String) - */ - public Intent putExtra(String name, String value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The CharSequence data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getCharSequenceExtra(String) - */ - public Intent putExtra(String name, CharSequence value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The Parcelable data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getParcelableExtra(String) - */ - public Intent putExtra(String name, Parcelable value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The Parcelable[] data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getParcelableArrayExtra(String) - */ - public Intent putExtra(String name, Parcelable[] value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The ArrayList data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getParcelableArrayListExtra(String) - */ - public Intent putParcelableArrayListExtra(String name, ArrayList value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The ArrayList data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getIntegerArrayListExtra(String) - */ - public Intent putIntegerArrayListExtra(String name, ArrayList value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The ArrayList data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getStringArrayListExtra(String) - */ - public Intent putStringArrayListExtra(String name, ArrayList value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The ArrayList data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getCharSequenceArrayListExtra(String) - */ - public Intent putCharSequenceArrayListExtra(String name, ArrayList value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The Serializable data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getSerializableExtra(String) - */ - public Intent putExtra(String name, Serializable value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The boolean array data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getBooleanArrayExtra(String) - */ - public Intent putExtra(String name, boolean[] value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The byte array data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getByteArrayExtra(String) - */ - public Intent putExtra(String name, byte[] value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The short array data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getShortArrayExtra(String) - */ - public Intent putExtra(String name, short[] value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The char array data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getCharArrayExtra(String) - */ - public Intent putExtra(String name, char[] value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The int array data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getIntArrayExtra(String) - */ - public Intent putExtra(String name, int[] value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The byte array data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getLongArrayExtra(String) - */ - public Intent putExtra(String name, long[] value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The float array data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getFloatArrayExtra(String) - */ - public Intent putExtra(String name, float[] value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The double array data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getDoubleArrayExtra(String) - */ - public Intent putExtra(String name, double[] value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The String array data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getStringArrayExtra(String) - */ - public Intent putExtra(String name, String[] value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The CharSequence array data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getCharSequenceArrayExtra(String) - */ - public Intent putExtra(String name, CharSequence[] value) { - return null; - } - - /** - * Add extended data to the intent. The name must include a package prefix, for - * example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param name The name of the extra data, with package prefix. - * @param value The Bundle data value. - * - * @return Returns the same Intent object, for chaining multiple calls into a - * single statement. - * - * @see #putExtras - * @see #removeExtra - * @see #getBundleExtra(String) - */ - public Intent putExtra(String name, Bundle value) { - return null; - } - - /** - * Copy all extras in 'src' in to this intent. - * - * @param src Contains the extras to copy. - * - * @see #putExtra - */ - public Intent putExtras(Intent src) { - return null; - } - - /** - * Add a set of extended data to the intent. The keys must include a package - * prefix, for example the app com.android.contacts would use names like - * "com.android.contacts.ShowAll". - * - * @param extras The Bundle of extras to add to this intent. - * - * @see #putExtra - * @see #removeExtra - */ - public Intent putExtras(Bundle extras) { - return null; - } - - /** - * Completely replace the extras in the Intent with the extras in the given - * Intent. - * - * @param src The exact extras contained in this Intent are copied into the - * target intent, replacing any that were previously there. - */ - public Intent replaceExtras(Intent src) { - return null; - } - - /** - * Completely replace the extras in the Intent with the given Bundle of extras. - * - * @param extras The new set of extras in the Intent, or null to erase all - * extras. - */ - public Intent replaceExtras(Bundle extras) { - return null; - } - - /** - * Remove extended data from the intent. - * - * @see #putExtra - */ - public void removeExtra(String name) { - } - - public void writeToParcel(Parcel out, int flags) { - } - - public void readFromParcel(Parcel in) { - } - - /** - * Retrieve the application package name this Intent is limited to. When - * resolving an Intent, if non-null this limits the resolution to only - * components in the given application package. - * - * @return The name of the application package for the Intent. - * - * @see #resolveActivity - * @see #setPackage - */ - public String getPackage() { - return null; - } - - /** - * (Usually optional) Set an explicit application package name that limits - * the components this Intent will resolve to. If left to the default - * value of null, all components in all applications will considered. - * If non-null, the Intent can only match the components in the given - * application package. - * - * @param packageName The name of the application package to handle the - * intent, or null to allow any application package. - * - * @return Returns the same Intent object, for chaining multiple calls - * into a single statement. - * - * @see #getPackage - * @see #resolveActivity - */ - public Intent setPackage(String packageName) { - return null; - } - - /** - * Convenience for calling {@link #setComponent} with an - * explicit class name. - * - * @param packageContext A Context of the application package implementing - * this class. - * @param className The name of a class inside of the application package - * that will be used as the component for this Intent. - * - * @return Returns the same Intent object, for chaining multiple calls - * into a single statement. - * - * @see #setComponent - * @see #setClass - */ - public Intent setClassName(Context packageContext, String className) { - return null; - } - - /** - * Convenience for calling {@link #setComponent} with an - * explicit application package name and class name. - * - * @param packageName The name of the package implementing the desired - * component. - * @param className The name of a class inside of the application package - * that will be used as the component for this Intent. - * - * @return Returns the same Intent object, for chaining multiple calls - * into a single statement. - * - * @see #setComponent - * @see #setClass - */ - public Intent setClassName(String packageName, String className) { - return null; - } - +import org.xmlpull.v1.XmlPullParser; + +public class Intent implements Cloneable, Parcelable +{ + public ArrayList getParcelableArrayListExtra(String p0){ return null; } + public T getParcelableExtra(String p0){ return null; } + public ActivityInfo resolveActivityInfo(PackageManager p0, int p1){ return null; } + public ArrayList getCharSequenceArrayListExtra(String p0){ return null; } + public ArrayList getIntegerArrayListExtra(String p0){ return null; } + public ArrayList getStringArrayListExtra(String p0){ return null; } + public Bundle getBundleExtra(String p0){ return null; } + public Bundle getExtras(){ return null; } + public CharSequence getCharSequenceExtra(String p0){ return null; } + public CharSequence[] getCharSequenceArrayExtra(String p0){ return null; } + public ClipData getClipData(){ return null; } + public ComponentName getComponent(){ return null; } + public ComponentName resolveActivity(PackageManager p0){ return null; } + public Intent addCategory(String p0){ return null; } + public Intent addFlags(int p0){ return null; } + public Intent cloneFilter(){ return null; } + public Intent getSelector(){ return null; } + public Intent putCharSequenceArrayListExtra(String p0, ArrayList p1){ return null; } + public Intent putExtra(String p0, Bundle p1){ return null; } + public Intent putExtra(String p0, CharSequence p1){ return null; } + public Intent putExtra(String p0, CharSequence[] p1){ return null; } + public Intent putExtra(String p0, Parcelable p1){ return null; } + public Intent putExtra(String p0, Parcelable[] p1){ return null; } + public Intent putExtra(String p0, Serializable p1){ return null; } + public Intent putExtra(String p0, String p1){ return null; } + public Intent putExtra(String p0, String[] p1){ return null; } + public Intent putExtra(String p0, boolean p1){ return null; } + public Intent putExtra(String p0, boolean[] p1){ return null; } + public Intent putExtra(String p0, byte p1){ return null; } + public Intent putExtra(String p0, byte[] p1){ return null; } + public Intent putExtra(String p0, char p1){ return null; } + public Intent putExtra(String p0, char[] p1){ return null; } + public Intent putExtra(String p0, double p1){ return null; } + public Intent putExtra(String p0, double[] p1){ return null; } + public Intent putExtra(String p0, float p1){ return null; } + public Intent putExtra(String p0, float[] p1){ return null; } + public Intent putExtra(String p0, int p1){ return null; } + public Intent putExtra(String p0, int[] p1){ return null; } + public Intent putExtra(String p0, long p1){ return null; } + public Intent putExtra(String p0, long[] p1){ return null; } + public Intent putExtra(String p0, short p1){ return null; } + public Intent putExtra(String p0, short[] p1){ return null; } + public Intent putExtras(Bundle p0){ return null; } + public Intent putExtras(Intent p0){ return null; } + public Intent putIntegerArrayListExtra(String p0, ArrayList p1){ return null; } + public Intent putParcelableArrayListExtra(String p0, ArrayList p1){ return null; } + public Intent putStringArrayListExtra(String p0, ArrayList p1){ return null; } + public Intent replaceExtras(Bundle p0){ return null; } + public Intent replaceExtras(Intent p0){ return null; } + public Intent setAction(String p0){ return null; } + public Intent setClass(Context p0, Class p1){ return null; } + public Intent setClassName(Context p0, String p1){ return null; } + public Intent setClassName(String p0, String p1){ return null; } + public Intent setComponent(ComponentName p0){ return null; } + public Intent setData(Uri p0){ return null; } + public Intent setDataAndNormalize(Uri p0){ return null; } + public Intent setDataAndType(Uri p0, String p1){ return null; } + public Intent setDataAndTypeAndNormalize(Uri p0, String p1){ return null; } + public Intent setFlags(int p0){ return null; } + public Intent setIdentifier(String p0){ return null; } + public Intent setPackage(String p0){ return null; } + public Intent setType(String p0){ return null; } + public Intent setTypeAndNormalize(String p0){ return null; } + public Intent(){} + public Intent(Context p0, Class p1){} + public Intent(Intent p0){} + public Intent(String p0){} + public Intent(String p0, Uri p1){} + public Intent(String p0, Uri p1, Context p2, Class p3){} + public Object clone(){ return null; } + public Parcelable[] getParcelableArrayExtra(String p0){ return null; } + public Rect getSourceBounds(){ return null; } + public Serializable getSerializableExtra(String p0){ return null; } + public Set getCategories(){ return null; } + public String getAction(){ return null; } + public String getDataString(){ return null; } + public String getIdentifier(){ return null; } + public String getPackage(){ return null; } + public String getScheme(){ return null; } + public String getStringExtra(String p0){ return null; } + public String getType(){ return null; } + public String resolveType(ContentResolver p0){ return null; } + public String resolveType(Context p0){ return null; } + public String resolveTypeIfNeeded(ContentResolver p0){ return null; } + public String toString(){ return null; } + public String toURI(){ return null; } + public String toUri(int p0){ return null; } + public String[] getStringArrayExtra(String p0){ return null; } + public Uri getData(){ return null; } + public boolean filterEquals(Intent p0){ return false; } + public boolean getBooleanExtra(String p0, boolean p1){ return false; } + public boolean hasCategory(String p0){ return false; } + public boolean hasExtra(String p0){ return false; } + public boolean hasFileDescriptors(){ return false; } + public boolean[] getBooleanArrayExtra(String p0){ return null; } + public byte getByteExtra(String p0, byte p1){ return 0; } + public byte[] getByteArrayExtra(String p0){ return null; } + public char getCharExtra(String p0, char p1){ return '0'; } + public char[] getCharArrayExtra(String p0){ return null; } + public double getDoubleExtra(String p0, double p1){ return 0; } + public double[] getDoubleArrayExtra(String p0){ return null; } + public float getFloatExtra(String p0, float p1){ return 0; } + public float[] getFloatArrayExtra(String p0){ return null; } + public int describeContents(){ return 0; } + public int fillIn(Intent p0, int p1){ return 0; } + public int filterHashCode(){ return 0; } + public int getFlags(){ return 0; } + public int getIntExtra(String p0, int p1){ return 0; } + public int[] getIntArrayExtra(String p0){ return null; } + public long getLongExtra(String p0, long p1){ return 0; } + public long[] getLongArrayExtra(String p0){ return null; } + public short getShortExtra(String p0, short p1){ return 0; } + public short[] getShortArrayExtra(String p0){ return null; } + public static Intent createChooser(Intent p0, CharSequence p1){ return null; } + public static Intent createChooser(Intent p0, CharSequence p1, IntentSender p2){ return null; } + public static Intent getIntent(String p0){ return null; } + public static Intent getIntentOld(String p0){ return null; } + public static Intent makeMainActivity(ComponentName p0){ return null; } + public static Intent makeMainSelectorActivity(String p0, String p1){ return null; } + public static Intent makeRestartActivityTask(ComponentName p0){ return null; } + public static Intent parseIntent(Resources p0, XmlPullParser p1, AttributeSet p2){ return null; } + public static Intent parseUri(String p0, int p1){ return null; } + public static Parcelable.Creator CREATOR = null; + public static String ACTION_AIRPLANE_MODE_CHANGED = null; + public static String ACTION_ALL_APPS = null; + public static String ACTION_ANSWER = null; + public static String ACTION_APPLICATION_PREFERENCES = null; + public static String ACTION_APPLICATION_RESTRICTIONS_CHANGED = null; + public static String ACTION_APP_ERROR = null; + public static String ACTION_ASSIST = null; + public static String ACTION_ATTACH_DATA = null; + public static String ACTION_BATTERY_CHANGED = null; + public static String ACTION_BATTERY_LOW = null; + public static String ACTION_BATTERY_OKAY = null; + public static String ACTION_BOOT_COMPLETED = null; + public static String ACTION_BUG_REPORT = null; + public static String ACTION_CALL = null; + public static String ACTION_CALL_BUTTON = null; + public static String ACTION_CAMERA_BUTTON = null; + public static String ACTION_CARRIER_SETUP = null; + public static String ACTION_CHOOSER = null; + public static String ACTION_CLOSE_SYSTEM_DIALOGS = null; + public static String ACTION_CONFIGURATION_CHANGED = null; + public static String ACTION_CREATE_DOCUMENT = null; + public static String ACTION_CREATE_SHORTCUT = null; + public static String ACTION_DATE_CHANGED = null; + public static String ACTION_DEFAULT = null; + public static String ACTION_DEFINE = null; + public static String ACTION_DELETE = null; + public static String ACTION_DEVICE_STORAGE_LOW = null; + public static String ACTION_DEVICE_STORAGE_OK = null; + public static String ACTION_DIAL = null; + public static String ACTION_DOCK_EVENT = null; + public static String ACTION_DREAMING_STARTED = null; + public static String ACTION_DREAMING_STOPPED = null; + public static String ACTION_EDIT = null; + public static String ACTION_EXTERNAL_APPLICATIONS_AVAILABLE = null; + public static String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE = null; + public static String ACTION_FACTORY_TEST = null; + public static String ACTION_GET_CONTENT = null; + public static String ACTION_GET_RESTRICTION_ENTRIES = null; + public static String ACTION_GTALK_SERVICE_CONNECTED = null; + public static String ACTION_GTALK_SERVICE_DISCONNECTED = null; + public static String ACTION_HEADSET_PLUG = null; + public static String ACTION_INPUT_METHOD_CHANGED = null; + public static String ACTION_INSERT = null; + public static String ACTION_INSERT_OR_EDIT = null; + public static String ACTION_INSTALL_FAILURE = null; + public static String ACTION_INSTALL_PACKAGE = null; + public static String ACTION_LOCALE_CHANGED = null; + public static String ACTION_LOCKED_BOOT_COMPLETED = null; + public static String ACTION_MAIN = null; + public static String ACTION_MANAGED_PROFILE_ADDED = null; + public static String ACTION_MANAGED_PROFILE_AVAILABLE = null; + public static String ACTION_MANAGED_PROFILE_REMOVED = null; + public static String ACTION_MANAGED_PROFILE_UNAVAILABLE = null; + public static String ACTION_MANAGED_PROFILE_UNLOCKED = null; + public static String ACTION_MANAGE_NETWORK_USAGE = null; + public static String ACTION_MANAGE_PACKAGE_STORAGE = null; + public static String ACTION_MEDIA_BAD_REMOVAL = null; + public static String ACTION_MEDIA_BUTTON = null; + public static String ACTION_MEDIA_CHECKING = null; + public static String ACTION_MEDIA_EJECT = null; + public static String ACTION_MEDIA_MOUNTED = null; + public static String ACTION_MEDIA_NOFS = null; + public static String ACTION_MEDIA_REMOVED = null; + public static String ACTION_MEDIA_SCANNER_FINISHED = null; + public static String ACTION_MEDIA_SCANNER_SCAN_FILE = null; + public static String ACTION_MEDIA_SCANNER_STARTED = null; + public static String ACTION_MEDIA_SHARED = null; + public static String ACTION_MEDIA_UNMOUNTABLE = null; + public static String ACTION_MEDIA_UNMOUNTED = null; + public static String ACTION_MY_PACKAGE_REPLACED = null; + public static String ACTION_MY_PACKAGE_SUSPENDED = null; + public static String ACTION_MY_PACKAGE_UNSUSPENDED = null; + public static String ACTION_NEW_OUTGOING_CALL = null; + public static String ACTION_OPEN_DOCUMENT = null; + public static String ACTION_OPEN_DOCUMENT_TREE = null; + public static String ACTION_PACKAGES_SUSPENDED = null; + public static String ACTION_PACKAGES_UNSUSPENDED = null; + public static String ACTION_PACKAGE_ADDED = null; + public static String ACTION_PACKAGE_CHANGED = null; + public static String ACTION_PACKAGE_DATA_CLEARED = null; + public static String ACTION_PACKAGE_FIRST_LAUNCH = null; + public static String ACTION_PACKAGE_FULLY_REMOVED = null; + public static String ACTION_PACKAGE_INSTALL = null; + public static String ACTION_PACKAGE_NEEDS_VERIFICATION = null; + public static String ACTION_PACKAGE_REMOVED = null; + public static String ACTION_PACKAGE_REPLACED = null; + public static String ACTION_PACKAGE_RESTARTED = null; + public static String ACTION_PACKAGE_VERIFIED = null; + public static String ACTION_PASTE = null; + public static String ACTION_PICK = null; + public static String ACTION_PICK_ACTIVITY = null; + public static String ACTION_POWER_CONNECTED = null; + public static String ACTION_POWER_DISCONNECTED = null; + public static String ACTION_POWER_USAGE_SUMMARY = null; + public static String ACTION_PROCESS_TEXT = null; + public static String ACTION_PROVIDER_CHANGED = null; + public static String ACTION_QUICK_CLOCK = null; + public static String ACTION_QUICK_VIEW = null; + public static String ACTION_REBOOT = null; + public static String ACTION_RUN = null; + public static String ACTION_SCREEN_OFF = null; + public static String ACTION_SCREEN_ON = null; + public static String ACTION_SEARCH = null; + public static String ACTION_SEARCH_LONG_PRESS = null; + public static String ACTION_SEND = null; + public static String ACTION_SENDTO = null; + public static String ACTION_SEND_MULTIPLE = null; + public static String ACTION_SET_WALLPAPER = null; + public static String ACTION_SHOW_APP_INFO = null; + public static String ACTION_SHUTDOWN = null; + public static String ACTION_SYNC = null; + public static String ACTION_SYSTEM_TUTORIAL = null; + public static String ACTION_TIMEZONE_CHANGED = null; + public static String ACTION_TIME_CHANGED = null; + public static String ACTION_TIME_TICK = null; + public static String ACTION_TRANSLATE = null; + public static String ACTION_UID_REMOVED = null; + public static String ACTION_UMS_CONNECTED = null; + public static String ACTION_UMS_DISCONNECTED = null; + public static String ACTION_UNINSTALL_PACKAGE = null; + public static String ACTION_USER_BACKGROUND = null; + public static String ACTION_USER_FOREGROUND = null; + public static String ACTION_USER_INITIALIZE = null; + public static String ACTION_USER_PRESENT = null; + public static String ACTION_USER_UNLOCKED = null; + public static String ACTION_VIEW = null; + public static String ACTION_VIEW_LOCUS = null; + public static String ACTION_VIEW_PERMISSION_USAGE = null; + public static String ACTION_VOICE_COMMAND = null; + public static String ACTION_WALLPAPER_CHANGED = null; + public static String ACTION_WEB_SEARCH = null; + public static String CATEGORY_ALTERNATIVE = null; + public static String CATEGORY_APP_BROWSER = null; + public static String CATEGORY_APP_CALCULATOR = null; + public static String CATEGORY_APP_CALENDAR = null; + public static String CATEGORY_APP_CONTACTS = null; + public static String CATEGORY_APP_EMAIL = null; + public static String CATEGORY_APP_FILES = null; + public static String CATEGORY_APP_GALLERY = null; + public static String CATEGORY_APP_MAPS = null; + public static String CATEGORY_APP_MARKET = null; + public static String CATEGORY_APP_MESSAGING = null; + public static String CATEGORY_APP_MUSIC = null; + public static String CATEGORY_BROWSABLE = null; + public static String CATEGORY_CAR_DOCK = null; + public static String CATEGORY_CAR_MODE = null; + public static String CATEGORY_DEFAULT = null; + public static String CATEGORY_DESK_DOCK = null; + public static String CATEGORY_DEVELOPMENT_PREFERENCE = null; + public static String CATEGORY_EMBED = null; + public static String CATEGORY_FRAMEWORK_INSTRUMENTATION_TEST = null; + public static String CATEGORY_HE_DESK_DOCK = null; + public static String CATEGORY_HOME = null; + public static String CATEGORY_INFO = null; + public static String CATEGORY_LAUNCHER = null; + public static String CATEGORY_LEANBACK_LAUNCHER = null; + public static String CATEGORY_LE_DESK_DOCK = null; + public static String CATEGORY_MONKEY = null; + public static String CATEGORY_OPENABLE = null; + public static String CATEGORY_PREFERENCE = null; + public static String CATEGORY_SAMPLE_CODE = null; + public static String CATEGORY_SECONDARY_HOME = null; + public static String CATEGORY_SELECTED_ALTERNATIVE = null; + public static String CATEGORY_TAB = null; + public static String CATEGORY_TEST = null; + public static String CATEGORY_TYPED_OPENABLE = null; + public static String CATEGORY_UNIT_TEST = null; + public static String CATEGORY_VOICE = null; + public static String CATEGORY_VR_HOME = null; + public static String EXTRA_ALARM_COUNT = null; + public static String EXTRA_ALLOW_MULTIPLE = null; + public static String EXTRA_ALLOW_REPLACE = null; + public static String EXTRA_ALTERNATE_INTENTS = null; + public static String EXTRA_ASSIST_CONTEXT = null; + public static String EXTRA_ASSIST_INPUT_DEVICE_ID = null; + public static String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = null; + public static String EXTRA_ASSIST_PACKAGE = null; + public static String EXTRA_ASSIST_UID = null; + public static String EXTRA_AUTO_LAUNCH_SINGLE_CHOICE = null; + public static String EXTRA_BCC = null; + public static String EXTRA_BUG_REPORT = null; + public static String EXTRA_CC = null; + public static String EXTRA_CHANGED_COMPONENT_NAME = null; + public static String EXTRA_CHANGED_COMPONENT_NAME_LIST = null; + public static String EXTRA_CHANGED_PACKAGE_LIST = null; + public static String EXTRA_CHANGED_UID_LIST = null; + public static String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = null; + public static String EXTRA_CHOOSER_TARGETS = null; + public static String EXTRA_CHOSEN_COMPONENT = null; + public static String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = null; + public static String EXTRA_COMPONENT_NAME = null; + public static String EXTRA_CONTENT_ANNOTATIONS = null; + public static String EXTRA_CONTENT_QUERY = null; + public static String EXTRA_DATA_REMOVED = null; + public static String EXTRA_DOCK_STATE = null; + public static String EXTRA_DONT_KILL_APP = null; + public static String EXTRA_DURATION_MILLIS = null; + public static String EXTRA_EMAIL = null; + public static String EXTRA_EXCLUDE_COMPONENTS = null; + public static String EXTRA_FROM_STORAGE = null; + public static String EXTRA_HTML_TEXT = null; + public static String EXTRA_INDEX = null; + public static String EXTRA_INITIAL_INTENTS = null; + public static String EXTRA_INSTALLER_PACKAGE_NAME = null; + public static String EXTRA_INTENT = null; + public static String EXTRA_KEY_EVENT = null; + public static String EXTRA_LOCAL_ONLY = null; + public static String EXTRA_LOCUS_ID = null; + public static String EXTRA_MIME_TYPES = null; + public static String EXTRA_NOT_UNKNOWN_SOURCE = null; + public static String EXTRA_ORIGINATING_URI = null; + public static String EXTRA_PACKAGE_NAME = null; + public static String EXTRA_PHONE_NUMBER = null; + public static String EXTRA_PROCESS_TEXT = null; + public static String EXTRA_PROCESS_TEXT_READONLY = null; + public static String EXTRA_QUICK_VIEW_FEATURES = null; + public static String EXTRA_QUIET_MODE = null; + public static String EXTRA_REFERRER = null; + public static String EXTRA_REFERRER_NAME = null; + public static String EXTRA_REMOTE_INTENT_TOKEN = null; + public static String EXTRA_REPLACEMENT_EXTRAS = null; + public static String EXTRA_REPLACING = null; + public static String EXTRA_RESTRICTIONS_BUNDLE = null; + public static String EXTRA_RESTRICTIONS_INTENT = null; + public static String EXTRA_RESTRICTIONS_LIST = null; + public static String EXTRA_RESULT_RECEIVER = null; + public static String EXTRA_RETURN_RESULT = null; + public static String EXTRA_SHORTCUT_ICON = null; + public static String EXTRA_SHORTCUT_ICON_RESOURCE = null; + public static String EXTRA_SHORTCUT_ID = null; + public static String EXTRA_SHORTCUT_INTENT = null; + public static String EXTRA_SHORTCUT_NAME = null; + public static String EXTRA_SHUTDOWN_USERSPACE_ONLY = null; + public static String EXTRA_SPLIT_NAME = null; + public static String EXTRA_STREAM = null; + public static String EXTRA_SUBJECT = null; + public static String EXTRA_SUSPENDED_PACKAGE_EXTRAS = null; + public static String EXTRA_TEMPLATE = null; + public static String EXTRA_TEXT = null; + public static String EXTRA_TITLE = null; + public static String EXTRA_UID = null; + public static String EXTRA_USER = null; + public static String METADATA_DOCK_HOME = null; + public static String normalizeMimeType(String p0){ return null; } + public static int EXTRA_DOCK_STATE_CAR = 0; + public static int EXTRA_DOCK_STATE_DESK = 0; + public static int EXTRA_DOCK_STATE_HE_DESK = 0; + public static int EXTRA_DOCK_STATE_LE_DESK = 0; + public static int EXTRA_DOCK_STATE_UNDOCKED = 0; + public static int FILL_IN_ACTION = 0; + public static int FILL_IN_CATEGORIES = 0; + public static int FILL_IN_CLIP_DATA = 0; + public static int FILL_IN_COMPONENT = 0; + public static int FILL_IN_DATA = 0; + public static int FILL_IN_IDENTIFIER = 0; + public static int FILL_IN_PACKAGE = 0; + public static int FILL_IN_SELECTOR = 0; + public static int FILL_IN_SOURCE_BOUNDS = 0; + public static int FLAG_ACTIVITY_BROUGHT_TO_FRONT = 0; + public static int FLAG_ACTIVITY_CLEAR_TASK = 0; + public static int FLAG_ACTIVITY_CLEAR_TOP = 0; + public static int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 0; + public static int FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS = 0; + public static int FLAG_ACTIVITY_FORWARD_RESULT = 0; + public static int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 0; + public static int FLAG_ACTIVITY_LAUNCH_ADJACENT = 0; + public static int FLAG_ACTIVITY_MATCH_EXTERNAL = 0; + public static int FLAG_ACTIVITY_MULTIPLE_TASK = 0; + public static int FLAG_ACTIVITY_NEW_DOCUMENT = 0; + public static int FLAG_ACTIVITY_NEW_TASK = 0; + public static int FLAG_ACTIVITY_NO_ANIMATION = 0; + public static int FLAG_ACTIVITY_NO_HISTORY = 0; + public static int FLAG_ACTIVITY_NO_USER_ACTION = 0; + public static int FLAG_ACTIVITY_PREVIOUS_IS_TOP = 0; + public static int FLAG_ACTIVITY_REORDER_TO_FRONT = 0; + public static int FLAG_ACTIVITY_RESET_TASK_IF_NEEDED = 0; + public static int FLAG_ACTIVITY_RETAIN_IN_RECENTS = 0; + public static int FLAG_ACTIVITY_SINGLE_TOP = 0; + public static int FLAG_ACTIVITY_TASK_ON_HOME = 0; + public static int FLAG_DEBUG_LOG_RESOLUTION = 0; + public static int FLAG_DIRECT_BOOT_AUTO = 0; + public static int FLAG_EXCLUDE_STOPPED_PACKAGES = 0; + public static int FLAG_FROM_BACKGROUND = 0; + public static int FLAG_GRANT_PERSISTABLE_URI_PERMISSION = 0; + public static int FLAG_GRANT_PREFIX_URI_PERMISSION = 0; + public static int FLAG_GRANT_READ_URI_PERMISSION = 0; + public static int FLAG_GRANT_WRITE_URI_PERMISSION = 0; + public static int FLAG_INCLUDE_STOPPED_PACKAGES = 0; + public static int FLAG_RECEIVER_FOREGROUND = 0; + public static int FLAG_RECEIVER_NO_ABORT = 0; + public static int FLAG_RECEIVER_REGISTERED_ONLY = 0; + public static int FLAG_RECEIVER_REPLACE_PENDING = 0; + public static int FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS = 0; + public static int URI_ALLOW_UNSAFE = 0; + public static int URI_ANDROID_APP_SCHEME = 0; + public static int URI_INTENT_SCHEME = 0; + public void readFromParcel(Parcel p0){} + public void removeCategory(String p0){} + public void removeExtra(String p0){} + public void removeFlags(int p0){} + public void setClipData(ClipData p0){} + public void setExtrasClassLoader(ClassLoader p0){} + public void setSelector(Intent p0){} + public void setSourceBounds(Rect p0){} + public void writeToParcel(Parcel p0, int p1){} } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/IntentFilter.java b/java/ql/test/stubs/google-android-9.0.0/android/content/IntentFilter.java new file mode 100644 index 00000000000..99de8481e14 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/IntentFilter.java @@ -0,0 +1,104 @@ +// Generated automatically from android.content.IntentFilter for testing purposes + +package android.content; + +import android.content.ContentResolver; +import android.content.Intent; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PatternMatcher; +import android.util.AndroidException; +import android.util.Printer; +import java.util.Iterator; +import java.util.Set; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlSerializer; + +public class IntentFilter implements Parcelable +{ + public IntentFilter(){} + public IntentFilter(IntentFilter p0){} + public IntentFilter(String p0){} + public IntentFilter(String p0, String p1){} + public final IntentFilter.AuthorityEntry getDataAuthority(int p0){ return null; } + public final Iterator authoritiesIterator(){ return null; } + public final Iterator pathsIterator(){ return null; } + public final Iterator schemeSpecificPartsIterator(){ return null; } + public final Iterator actionsIterator(){ return null; } + public final Iterator categoriesIterator(){ return null; } + public final Iterator schemesIterator(){ return null; } + public final Iterator typesIterator(){ return null; } + public final PatternMatcher getDataPath(int p0){ return null; } + public final PatternMatcher getDataSchemeSpecificPart(int p0){ return null; } + public final String getAction(int p0){ return null; } + public final String getCategory(int p0){ return null; } + public final String getDataScheme(int p0){ return null; } + public final String getDataType(int p0){ return null; } + public final String matchCategories(Set p0){ return null; } + public final boolean hasAction(String p0){ return false; } + public final boolean hasCategory(String p0){ return false; } + public final boolean hasDataAuthority(Uri p0){ return false; } + public final boolean hasDataPath(String p0){ return false; } + public final boolean hasDataScheme(String p0){ return false; } + public final boolean hasDataSchemeSpecificPart(String p0){ return false; } + public final boolean hasDataType(String p0){ return false; } + public final boolean matchAction(String p0){ return false; } + public final int countActions(){ return 0; } + public final int countCategories(){ return 0; } + public final int countDataAuthorities(){ return 0; } + public final int countDataPaths(){ return 0; } + public final int countDataSchemeSpecificParts(){ return 0; } + public final int countDataSchemes(){ return 0; } + public final int countDataTypes(){ return 0; } + public final int describeContents(){ return 0; } + public final int getPriority(){ return 0; } + public final int match(ContentResolver p0, Intent p1, boolean p2, String p3){ return 0; } + public final int match(String p0, String p1, String p2, Uri p3, Set p4, String p5){ return 0; } + public final int matchData(String p0, String p1, Uri p2){ return 0; } + public final int matchDataAuthority(Uri p0){ return 0; } + public final void addAction(String p0){} + public final void addCategory(String p0){} + public final void addDataAuthority(String p0, String p1){} + public final void addDataPath(String p0, int p1){} + public final void addDataScheme(String p0){} + public final void addDataSchemeSpecificPart(String p0, int p1){} + public final void addDataType(String p0){} + public final void setPriority(int p0){} + public final void writeToParcel(Parcel p0, int p1){} + public static IntentFilter create(String p0, String p1){ return null; } + public static Parcelable.Creator CREATOR = null; + public static int MATCH_ADJUSTMENT_MASK = 0; + public static int MATCH_ADJUSTMENT_NORMAL = 0; + public static int MATCH_CATEGORY_EMPTY = 0; + public static int MATCH_CATEGORY_HOST = 0; + public static int MATCH_CATEGORY_MASK = 0; + public static int MATCH_CATEGORY_PATH = 0; + public static int MATCH_CATEGORY_PORT = 0; + public static int MATCH_CATEGORY_SCHEME = 0; + public static int MATCH_CATEGORY_SCHEME_SPECIFIC_PART = 0; + public static int MATCH_CATEGORY_TYPE = 0; + public static int NO_MATCH_ACTION = 0; + public static int NO_MATCH_CATEGORY = 0; + public static int NO_MATCH_DATA = 0; + public static int NO_MATCH_TYPE = 0; + public static int SYSTEM_HIGH_PRIORITY = 0; + public static int SYSTEM_LOW_PRIORITY = 0; + public void dump(Printer p0, String p1){} + public void readFromXml(XmlPullParser p0){} + public void writeToXml(XmlSerializer p0){} + static public class AuthorityEntry + { + protected AuthorityEntry() {} + public AuthorityEntry(String p0, String p1){} + public String getHost(){ return null; } + public boolean equals(Object p0){ return false; } + public int getPort(){ return 0; } + public int match(Uri p0){ return 0; } + } + static public class MalformedMimeTypeException extends AndroidException + { + public MalformedMimeTypeException(){} + public MalformedMimeTypeException(String p0){} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/IntentSender.java b/java/ql/test/stubs/google-android-9.0.0/android/content/IntentSender.java new file mode 100644 index 00000000000..378c92e0d0b --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/IntentSender.java @@ -0,0 +1,41 @@ +// Generated automatically from android.content.IntentSender for testing purposes + +package android.content; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.UserHandle; +import android.util.AndroidException; + +public class IntentSender implements Parcelable +{ + protected IntentSender() {} + public String getCreatorPackage(){ return null; } + public String getTargetPackage(){ return null; } + public String toString(){ return null; } + public UserHandle getCreatorUserHandle(){ return null; } + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int getCreatorUid(){ return 0; } + public int hashCode(){ return 0; } + public static IntentSender readIntentSenderOrNullFromParcel(Parcel p0){ return null; } + public static Parcelable.Creator CREATOR = null; + public static void writeIntentSenderOrNullToParcel(IntentSender p0, Parcel p1){} + public void sendIntent(Context p0, int p1, Intent p2, IntentSender.OnFinished p3, Handler p4){} + public void sendIntent(Context p0, int p1, Intent p2, IntentSender.OnFinished p3, Handler p4, String p5){} + public void writeToParcel(Parcel p0, int p1){} + static public class SendIntentException extends AndroidException + { + public SendIntentException(){} + public SendIntentException(Exception p0){} + public SendIntentException(String p0){} + } + static public interface OnFinished + { + void onSendFinished(IntentSender p0, Intent p1, int p2, String p3, Bundle p4); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/PeriodicSync.java b/java/ql/test/stubs/google-android-9.0.0/android/content/PeriodicSync.java new file mode 100644 index 00000000000..6842c06d22f --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/PeriodicSync.java @@ -0,0 +1,23 @@ +// Generated automatically from android.content.PeriodicSync for testing purposes + +package android.content; + +import android.accounts.Account; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +public class PeriodicSync implements Parcelable +{ + protected PeriodicSync() {} + public PeriodicSync(Account p0, String p1, Bundle p2, long p3){} + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public final Account account = null; + public final Bundle extras = null; + public final String authority = null; + public final long period = 0; + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/ServiceConnection.java b/java/ql/test/stubs/google-android-9.0.0/android/content/ServiceConnection.java new file mode 100644 index 00000000000..1b8534539c4 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/ServiceConnection.java @@ -0,0 +1,14 @@ +// Generated automatically from android.content.ServiceConnection for testing purposes + +package android.content; + +import android.content.ComponentName; +import android.os.IBinder; + +public interface ServiceConnection +{ + default void onBindingDied(ComponentName p0){} + default void onNullBinding(ComponentName p0){} + void onServiceConnected(ComponentName p0, IBinder p1); + void onServiceDisconnected(ComponentName p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/SharedPreferences.java b/java/ql/test/stubs/google-android-9.0.0/android/content/SharedPreferences.java index faac2f1d41e..90cdc242501 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/content/SharedPreferences.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/SharedPreferences.java @@ -1,362 +1,38 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Generated automatically from android.content.SharedPreferences for testing purposes + package android.content; import java.util.Map; import java.util.Set; -/** - * Interface for accessing and modifying preference data returned by {@link - * Context#getSharedPreferences}. For any particular set of preferences, - * there is a single instance of this class that all clients share. - * Modifications to the preferences must go through an {@link Editor} object - * to ensure the preference values remain in a consistent state and control - * when they are committed to storage. Objects that are returned from the - * various get methods must be treated as immutable by the application. - * - *

    Note: currently this class does not support use across multiple - * processes. This will be added later. - * - *

    - *

    Developer Guides

    - *

    For more information about using SharedPreferences, read the - * Data Storage - * developer guide.

    - * - * @see Context#getSharedPreferences - */ -public interface SharedPreferences { - /** - * Interface definition for a callback to be invoked when a shared - * preference is changed. - */ - public interface OnSharedPreferenceChangeListener { - /** - * Called when a shared preference is changed, added, or removed. This - * may be called even if a preference is set to its existing value. - * - *

    This callback will be run on your main thread. - * - * @param sharedPreferences The {@link SharedPreferences} that received - * the change. - * @param key The key of the preference that was changed, added, or - * removed. - */ - void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key); - } - - /** - * Interface used for modifying values in a {@link SharedPreferences} - * object. All changes you make in an editor are batched, and not copied - * back to the original {@link SharedPreferences} until you call {@link #commit} - * or {@link #apply} - */ - public interface Editor { - /** - * Set a String value in the preferences editor, to be written back once - * {@link #commit} or {@link #apply} are called. - * - * @param key The name of the preference to modify. - * @param value The new value for the preference. Supplying {@code null} - * as the value is equivalent to calling {@link #remove(String)} with - * this key. - * - * @return Returns a reference to the same Editor object, so you can - * chain put calls together. - */ - Editor putString(String key, String value); - - /** - * Set a set of String values in the preferences editor, to be written - * back once {@link #commit} is called. - * - * @param key The name of the preference to modify. - * @param values The set of new values for the preference. Passing {@code null} - * for this argument is equivalent to calling {@link #remove(String)} with - * this key. - * @return Returns a reference to the same Editor object, so you can - * chain put calls together. - */ - Editor putStringSet(String key, Set values); - - /** - * Set an int value in the preferences editor, to be written back once - * {@link #commit} or {@link #apply} are called. - * - * @param key The name of the preference to modify. - * @param value The new value for the preference. - * - * @return Returns a reference to the same Editor object, so you can - * chain put calls together. - */ - Editor putInt(String key, int value); - - /** - * Set a long value in the preferences editor, to be written back once - * {@link #commit} or {@link #apply} are called. - * - * @param key The name of the preference to modify. - * @param value The new value for the preference. - * - * @return Returns a reference to the same Editor object, so you can - * chain put calls together. - */ - Editor putLong(String key, long value); - - /** - * Set a float value in the preferences editor, to be written back once - * {@link #commit} or {@link #apply} are called. - * - * @param key The name of the preference to modify. - * @param value The new value for the preference. - * - * @return Returns a reference to the same Editor object, so you can - * chain put calls together. - */ - Editor putFloat(String key, float value); - - /** - * Set a boolean value in the preferences editor, to be written back - * once {@link #commit} or {@link #apply} are called. - * - * @param key The name of the preference to modify. - * @param value The new value for the preference. - * - * @return Returns a reference to the same Editor object, so you can - * chain put calls together. - */ - Editor putBoolean(String key, boolean value); - /** - * Mark in the editor that a preference value should be removed, which - * will be done in the actual preferences once {@link #commit} is - * called. - * - *

    Note that when committing back to the preferences, all removals - * are done first, regardless of whether you called remove before - * or after put methods on this editor. - * - * @param key The name of the preference to remove. - * - * @return Returns a reference to the same Editor object, so you can - * chain put calls together. - */ - Editor remove(String key); - /** - * Mark in the editor to remove all values from the - * preferences. Once commit is called, the only remaining preferences - * will be any that you have defined in this editor. - * - *

    Note that when committing back to the preferences, the clear - * is done first, regardless of whether you called clear before - * or after put methods on this editor. - * - * @return Returns a reference to the same Editor object, so you can - * chain put calls together. - */ - Editor clear(); - /** - * Commit your preferences changes back from this Editor to the - * {@link SharedPreferences} object it is editing. This atomically - * performs the requested modifications, replacing whatever is currently - * in the SharedPreferences. - * - *

    Note that when two editors are modifying preferences at the same - * time, the last one to call commit wins. - * - *

    If you don't care about the return value and you're - * using this from your application's main thread, consider - * using {@link #apply} instead. - * - * @return Returns true if the new values were successfully written - * to persistent storage. - */ + +public interface SharedPreferences +{ + Map getAll(); + Set getStringSet(String p0, Set p1); + SharedPreferences.Editor edit(); + String getString(String p0, String p1); + boolean contains(String p0); + boolean getBoolean(String p0, boolean p1); + float getFloat(String p0, float p1); + int getInt(String p0, int p1); + long getLong(String p0, long p1); + static public interface Editor + { + SharedPreferences.Editor clear(); + SharedPreferences.Editor putBoolean(String p0, boolean p1); + SharedPreferences.Editor putFloat(String p0, float p1); + SharedPreferences.Editor putInt(String p0, int p1); + SharedPreferences.Editor putLong(String p0, long p1); + SharedPreferences.Editor putString(String p0, String p1); + SharedPreferences.Editor putStringSet(String p0, Set p1); + SharedPreferences.Editor remove(String p0); boolean commit(); - /** - * Commit your preferences changes back from this Editor to the - * {@link SharedPreferences} object it is editing. This atomically - * performs the requested modifications, replacing whatever is currently - * in the SharedPreferences. - * - *

    Note that when two editors are modifying preferences at the same - * time, the last one to call apply wins. - * - *

    Unlike {@link #commit}, which writes its preferences out - * to persistent storage synchronously, {@link #apply} - * commits its changes to the in-memory - * {@link SharedPreferences} immediately but starts an - * asynchronous commit to disk and you won't be notified of - * any failures. If another editor on this - * {@link SharedPreferences} does a regular {@link #commit} - * while a {@link #apply} is still outstanding, the - * {@link #commit} will block until all async commits are - * completed as well as the commit itself. - * - *

    As {@link SharedPreferences} instances are singletons within - * a process, it's safe to replace any instance of {@link #commit} with - * {@link #apply} if you were already ignoring the return value. - * - *

    You don't need to worry about Android component - * lifecycles and their interaction with apply() - * writing to disk. The framework makes sure in-flight disk - * writes from apply() complete before switching - * states. - * - *

    The SharedPreferences.Editor interface - * isn't expected to be implemented directly. However, if you - * previously did implement it and are now getting errors - * about missing apply(), you can simply call - * {@link #commit} from apply(). - */ void apply(); } - /** - * Retrieve all values from the preferences. - * - *

    Note that you must not modify the collection returned - * by this method, or alter any of its contents. The consistency of your - * stored data is not guaranteed if you do. - * - * @return Returns a map containing a list of pairs key/value representing - * the preferences. - * - * @throws NullPointerException - */ - Map getAll(); - /** - * Retrieve a String value from the preferences. - * - * @param key The name of the preference to retrieve. - * @param defValue Value to return if this preference does not exist. - * - * @return Returns the preference value if it exists, or defValue. Throws - * ClassCastException if there is a preference with this name that is not - * a String. - * - * @throws ClassCastException - */ - String getString(String key, String defValue); - - /** - * Retrieve a set of String values from the preferences. - * - *

    Note that you must not modify the set instance returned - * by this call. The consistency of the stored data is not guaranteed - * if you do, nor is your ability to modify the instance at all. - * - * @param key The name of the preference to retrieve. - * @param defValues Values to return if this preference does not exist. - * - * @return Returns the preference values if they exist, or defValues. - * Throws ClassCastException if there is a preference with this name - * that is not a Set. - * - * @throws ClassCastException - */ - Set getStringSet(String key, Set defValues); - - /** - * Retrieve an int value from the preferences. - * - * @param key The name of the preference to retrieve. - * @param defValue Value to return if this preference does not exist. - * - * @return Returns the preference value if it exists, or defValue. Throws - * ClassCastException if there is a preference with this name that is not - * an int. - * - * @throws ClassCastException - */ - int getInt(String key, int defValue); - - /** - * Retrieve a long value from the preferences. - * - * @param key The name of the preference to retrieve. - * @param defValue Value to return if this preference does not exist. - * - * @return Returns the preference value if it exists, or defValue. Throws - * ClassCastException if there is a preference with this name that is not - * a long. - * - * @throws ClassCastException - */ - long getLong(String key, long defValue); - - /** - * Retrieve a float value from the preferences. - * - * @param key The name of the preference to retrieve. - * @param defValue Value to return if this preference does not exist. - * - * @return Returns the preference value if it exists, or defValue. Throws - * ClassCastException if there is a preference with this name that is not - * a float. - * - * @throws ClassCastException - */ - float getFloat(String key, float defValue); - - /** - * Retrieve a boolean value from the preferences. - * - * @param key The name of the preference to retrieve. - * @param defValue Value to return if this preference does not exist. - * - * @return Returns the preference value if it exists, or defValue. Throws - * ClassCastException if there is a preference with this name that is not - * a boolean. - * - * @throws ClassCastException - */ - boolean getBoolean(String key, boolean defValue); - /** - * Checks whether the preferences contains a preference. - * - * @param key The name of the preference to check. - * @return Returns true if the preference exists in the preferences, - * otherwise false. - */ - boolean contains(String key); - - /** - * Create a new Editor for these preferences, through which you can make - * modifications to the data in the preferences and atomically commit those - * changes back to the SharedPreferences object. - * - *

    Note that you must call {@link Editor#commit} to have any - * changes you perform in the Editor actually show up in the - * SharedPreferences. - * - * @return Returns a new instance of the {@link Editor} interface, allowing - * you to modify the values in this SharedPreferences object. - */ - Editor edit(); - - /** - * Registers a callback to be invoked when a change happens to a preference. - * - * @param listener The callback that will run. - * @see #unregisterOnSharedPreferenceChangeListener - */ - void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener); - - /** - * Unregisters a previous callback. - * - * @param listener The callback that should be unregistered. - * @see #registerOnSharedPreferenceChangeListener - */ - void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener); + static public interface OnSharedPreferenceChangeListener + { + void onSharedPreferenceChanged(SharedPreferences p0, String p1); + } + void registerOnSharedPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener p0); + void unregisterOnSharedPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener p0); } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/SyncAdapterType.java b/java/ql/test/stubs/google-android-9.0.0/android/content/SyncAdapterType.java new file mode 100644 index 00000000000..bc450b6c353 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/SyncAdapterType.java @@ -0,0 +1,3 @@ +package android.content; + +interface SyncAdapterType { } \ No newline at end of file diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/SyncInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/content/SyncInfo.java new file mode 100644 index 00000000000..e7e1da41477 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/SyncInfo.java @@ -0,0 +1,17 @@ +// Generated automatically from android.content.SyncInfo for testing purposes + +package android.content; + +import android.accounts.Account; +import android.os.Parcel; +import android.os.Parcelable; + +public class SyncInfo implements Parcelable +{ + protected SyncInfo() {} + public final Account account = null; + public final String authority = null; + public final long startTime = 0; + public int describeContents(){ return 0; } + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/SyncRequest.java b/java/ql/test/stubs/google-android-9.0.0/android/content/SyncRequest.java new file mode 100644 index 00000000000..2340b67b1cf --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/SyncRequest.java @@ -0,0 +1,14 @@ +// Generated automatically from android.content.SyncRequest for testing purposes + +package android.content; + +import android.os.Parcel; +import android.os.Parcelable; + +public class SyncRequest implements Parcelable +{ + protected SyncRequest() {} + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/SyncStatusObserver.java b/java/ql/test/stubs/google-android-9.0.0/android/content/SyncStatusObserver.java new file mode 100644 index 00000000000..534cd549be0 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/SyncStatusObserver.java @@ -0,0 +1,9 @@ +// Generated automatically from android.content.SyncStatusObserver for testing purposes + +package android.content; + + +public interface SyncStatusObserver +{ + void onStatusChanged(int p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/UriPermission.java b/java/ql/test/stubs/google-android-9.0.0/android/content/UriPermission.java new file mode 100644 index 00000000000..600a483a589 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/UriPermission.java @@ -0,0 +1,21 @@ +// Generated automatically from android.content.UriPermission for testing purposes + +package android.content; + +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +public class UriPermission implements Parcelable +{ + protected UriPermission() {} + public String toString(){ return null; } + public Uri getUri(){ return null; } + public boolean isReadPermission(){ return false; } + public boolean isWritePermission(){ return false; } + public int describeContents(){ return 0; } + public long getPersistedTime(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static long INVALID_TIME = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ActivityInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ActivityInfo.java new file mode 100644 index 00000000000..2fea1ef0771 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ActivityInfo.java @@ -0,0 +1,111 @@ +// Generated automatically from android.content.pm.ActivityInfo for testing purposes + +package android.content.pm; + +import android.content.pm.ComponentInfo; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Printer; + +public class ActivityInfo extends ComponentInfo implements Parcelable +{ + public ActivityInfo(){} + public ActivityInfo(ActivityInfo p0){} + public ActivityInfo.WindowLayout windowLayout = null; + public String parentActivityName = null; + public String permission = null; + public String targetActivity = null; + public String taskAffinity = null; + public String toString(){ return null; } + public final int getThemeResource(){ return 0; } + public int colorMode = 0; + public int configChanges = 0; + public int describeContents(){ return 0; } + public int documentLaunchMode = 0; + public int flags = 0; + public int launchMode = 0; + public int maxRecents = 0; + public int persistableMode = 0; + public int screenOrientation = 0; + public int softInputMode = 0; + public int theme = 0; + public int uiOptions = 0; + public static Parcelable.Creator CREATOR = null; + public static int COLOR_MODE_DEFAULT = 0; + public static int COLOR_MODE_HDR = 0; + public static int COLOR_MODE_WIDE_COLOR_GAMUT = 0; + public static int CONFIG_COLOR_MODE = 0; + public static int CONFIG_DENSITY = 0; + public static int CONFIG_FONT_SCALE = 0; + public static int CONFIG_KEYBOARD = 0; + public static int CONFIG_KEYBOARD_HIDDEN = 0; + public static int CONFIG_LAYOUT_DIRECTION = 0; + public static int CONFIG_LOCALE = 0; + public static int CONFIG_MCC = 0; + public static int CONFIG_MNC = 0; + public static int CONFIG_NAVIGATION = 0; + public static int CONFIG_ORIENTATION = 0; + public static int CONFIG_SCREEN_LAYOUT = 0; + public static int CONFIG_SCREEN_SIZE = 0; + public static int CONFIG_SMALLEST_SCREEN_SIZE = 0; + public static int CONFIG_TOUCHSCREEN = 0; + public static int CONFIG_UI_MODE = 0; + public static int DOCUMENT_LAUNCH_ALWAYS = 0; + public static int DOCUMENT_LAUNCH_INTO_EXISTING = 0; + public static int DOCUMENT_LAUNCH_NEVER = 0; + public static int DOCUMENT_LAUNCH_NONE = 0; + public static int FLAG_ALLOW_TASK_REPARENTING = 0; + public static int FLAG_ALWAYS_RETAIN_TASK_STATE = 0; + public static int FLAG_AUTO_REMOVE_FROM_RECENTS = 0; + public static int FLAG_CLEAR_TASK_ON_LAUNCH = 0; + public static int FLAG_ENABLE_VR_MODE = 0; + public static int FLAG_EXCLUDE_FROM_RECENTS = 0; + public static int FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS = 0; + public static int FLAG_FINISH_ON_TASK_LAUNCH = 0; + public static int FLAG_HARDWARE_ACCELERATED = 0; + public static int FLAG_IMMERSIVE = 0; + public static int FLAG_MULTIPROCESS = 0; + public static int FLAG_NO_HISTORY = 0; + public static int FLAG_RELINQUISH_TASK_IDENTITY = 0; + public static int FLAG_RESUME_WHILE_PAUSING = 0; + public static int FLAG_SINGLE_USER = 0; + public static int FLAG_STATE_NOT_NEEDED = 0; + public static int LAUNCH_MULTIPLE = 0; + public static int LAUNCH_SINGLE_INSTANCE = 0; + public static int LAUNCH_SINGLE_TASK = 0; + public static int LAUNCH_SINGLE_TOP = 0; + public static int PERSIST_ACROSS_REBOOTS = 0; + public static int PERSIST_NEVER = 0; + public static int PERSIST_ROOT_ONLY = 0; + public static int SCREEN_ORIENTATION_BEHIND = 0; + public static int SCREEN_ORIENTATION_FULL_SENSOR = 0; + public static int SCREEN_ORIENTATION_FULL_USER = 0; + public static int SCREEN_ORIENTATION_LANDSCAPE = 0; + public static int SCREEN_ORIENTATION_LOCKED = 0; + public static int SCREEN_ORIENTATION_NOSENSOR = 0; + public static int SCREEN_ORIENTATION_PORTRAIT = 0; + public static int SCREEN_ORIENTATION_REVERSE_LANDSCAPE = 0; + public static int SCREEN_ORIENTATION_REVERSE_PORTRAIT = 0; + public static int SCREEN_ORIENTATION_SENSOR = 0; + public static int SCREEN_ORIENTATION_SENSOR_LANDSCAPE = 0; + public static int SCREEN_ORIENTATION_SENSOR_PORTRAIT = 0; + public static int SCREEN_ORIENTATION_UNSPECIFIED = 0; + public static int SCREEN_ORIENTATION_USER = 0; + public static int SCREEN_ORIENTATION_USER_LANDSCAPE = 0; + public static int SCREEN_ORIENTATION_USER_PORTRAIT = 0; + public static int UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW = 0; + public void dump(Printer p0, String p1){} + public void writeToParcel(Parcel p0, int p1){} + static public class WindowLayout + { + protected WindowLayout() {} + public WindowLayout(int p0, float p1, int p2, float p3, int p4, int p5, int p6){} + public final float heightFraction = 0; + public final float widthFraction = 0; + public final int gravity = 0; + public final int height = 0; + public final int minHeight = 0; + public final int minWidth = 0; + public final int width = 0; + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ApplicationInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ApplicationInfo.java new file mode 100644 index 00000000000..7212b09588b --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ApplicationInfo.java @@ -0,0 +1,97 @@ +// Generated automatically from android.content.pm.ApplicationInfo for testing purposes + +package android.content.pm; + +import android.content.Context; +import android.content.pm.PackageItemInfo; +import android.content.pm.PackageManager; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Printer; +import java.util.UUID; + +public class ApplicationInfo extends PackageItemInfo implements Parcelable +{ + public ApplicationInfo(){} + public ApplicationInfo(ApplicationInfo p0){} + public CharSequence loadDescription(PackageManager p0){ return null; } + public String appComponentFactory = null; + public String backupAgentName = null; + public String className = null; + public String dataDir = null; + public String deviceProtectedDataDir = null; + public String manageSpaceActivityName = null; + public String nativeLibraryDir = null; + public String permission = null; + public String processName = null; + public String publicSourceDir = null; + public String sourceDir = null; + public String taskAffinity = null; + public String toString(){ return null; } + public String[] sharedLibraryFiles = null; + public String[] splitNames = null; + public String[] splitPublicSourceDirs = null; + public String[] splitSourceDirs = null; + public UUID storageUuid = null; + public boolean enabled = false; + public boolean isProfileableByShell(){ return false; } + public boolean isResourceOverlay(){ return false; } + public boolean isVirtualPreload(){ return false; } + public int category = 0; + public int compatibleWidthLimitDp = 0; + public int describeContents(){ return 0; } + public int descriptionRes = 0; + public int flags = 0; + public int largestWidthLimitDp = 0; + public int minSdkVersion = 0; + public int requiresSmallestWidthDp = 0; + public int targetSdkVersion = 0; + public int theme = 0; + public int uiOptions = 0; + public int uid = 0; + public static CharSequence getCategoryTitle(Context p0, int p1){ return null; } + public static Parcelable.Creator CREATOR = null; + public static int CATEGORY_AUDIO = 0; + public static int CATEGORY_GAME = 0; + public static int CATEGORY_IMAGE = 0; + public static int CATEGORY_MAPS = 0; + public static int CATEGORY_NEWS = 0; + public static int CATEGORY_PRODUCTIVITY = 0; + public static int CATEGORY_SOCIAL = 0; + public static int CATEGORY_UNDEFINED = 0; + public static int CATEGORY_VIDEO = 0; + public static int FLAG_ALLOW_BACKUP = 0; + public static int FLAG_ALLOW_CLEAR_USER_DATA = 0; + public static int FLAG_ALLOW_TASK_REPARENTING = 0; + public static int FLAG_DEBUGGABLE = 0; + public static int FLAG_EXTERNAL_STORAGE = 0; + public static int FLAG_EXTRACT_NATIVE_LIBS = 0; + public static int FLAG_FACTORY_TEST = 0; + public static int FLAG_FULL_BACKUP_ONLY = 0; + public static int FLAG_HARDWARE_ACCELERATED = 0; + public static int FLAG_HAS_CODE = 0; + public static int FLAG_INSTALLED = 0; + public static int FLAG_IS_DATA_ONLY = 0; + public static int FLAG_IS_GAME = 0; + public static int FLAG_KILL_AFTER_RESTORE = 0; + public static int FLAG_LARGE_HEAP = 0; + public static int FLAG_MULTIARCH = 0; + public static int FLAG_PERSISTENT = 0; + public static int FLAG_RESIZEABLE_FOR_SCREENS = 0; + public static int FLAG_RESTORE_ANY_VERSION = 0; + public static int FLAG_STOPPED = 0; + public static int FLAG_SUPPORTS_LARGE_SCREENS = 0; + public static int FLAG_SUPPORTS_NORMAL_SCREENS = 0; + public static int FLAG_SUPPORTS_RTL = 0; + public static int FLAG_SUPPORTS_SCREEN_DENSITIES = 0; + public static int FLAG_SUPPORTS_SMALL_SCREENS = 0; + public static int FLAG_SUPPORTS_XLARGE_SCREENS = 0; + public static int FLAG_SUSPENDED = 0; + public static int FLAG_SYSTEM = 0; + public static int FLAG_TEST_ONLY = 0; + public static int FLAG_UPDATED_SYSTEM_APP = 0; + public static int FLAG_USES_CLEARTEXT_TRAFFIC = 0; + public static int FLAG_VM_SAFE_MODE = 0; + public void dump(Printer p0, String p1){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ChangedPackages.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ChangedPackages.java new file mode 100644 index 00000000000..08c10b13f3c --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ChangedPackages.java @@ -0,0 +1,18 @@ +// Generated automatically from android.content.pm.ChangedPackages for testing purposes + +package android.content.pm; + +import android.os.Parcel; +import android.os.Parcelable; +import java.util.List; + +public class ChangedPackages implements Parcelable +{ + protected ChangedPackages() {} + public ChangedPackages(int p0, List p1){} + public List getPackageNames(){ return null; } + public int describeContents(){ return 0; } + public int getSequenceNumber(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ComponentInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ComponentInfo.java new file mode 100644 index 00000000000..fb92a43adbe --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ComponentInfo.java @@ -0,0 +1,29 @@ +// Generated automatically from android.content.pm.ComponentInfo for testing purposes + +package android.content.pm; + +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageItemInfo; +import android.os.Parcel; +import android.util.Printer; + +public class ComponentInfo extends PackageItemInfo +{ + protected ComponentInfo(Parcel p0){} + protected void dumpBack(Printer p0, String p1){} + protected void dumpFront(Printer p0, String p1){} + public ApplicationInfo applicationInfo = null; + public ComponentInfo(){} + public ComponentInfo(ComponentInfo p0){} + public String processName = null; + public String splitName = null; + public boolean directBootAware = false; + public boolean enabled = false; + public boolean exported = false; + public boolean isEnabled(){ return false; } + public final int getBannerResource(){ return 0; } + public final int getIconResource(){ return 0; } + public final int getLogoResource(){ return 0; } + public int descriptionRes = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ConfigurationInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ConfigurationInfo.java new file mode 100644 index 00000000000..43fc1954d8c --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ConfigurationInfo.java @@ -0,0 +1,3 @@ +package android.content.pm; + +interface ConfigurationInfo { } \ No newline at end of file diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/FeatureGroupInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/FeatureGroupInfo.java new file mode 100644 index 00000000000..402fdc55391 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/FeatureGroupInfo.java @@ -0,0 +1,3 @@ +package android.content.pm; + +interface FeatureGroupInfo { } \ No newline at end of file diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/FeatureInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/FeatureInfo.java new file mode 100644 index 00000000000..05c2028d40e --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/FeatureInfo.java @@ -0,0 +1,4 @@ + +package android.content.pm; + +interface FeatureInfo { } \ No newline at end of file diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/InstrumentationInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/InstrumentationInfo.java new file mode 100644 index 00000000000..70063a09744 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/InstrumentationInfo.java @@ -0,0 +1,27 @@ +// Generated automatically from android.content.pm.InstrumentationInfo for testing purposes + +package android.content.pm; + +import android.content.pm.PackageItemInfo; +import android.os.Parcel; +import android.os.Parcelable; + +public class InstrumentationInfo extends PackageItemInfo implements Parcelable +{ + public InstrumentationInfo(){} + public InstrumentationInfo(InstrumentationInfo p0){} + public String dataDir = null; + public String publicSourceDir = null; + public String sourceDir = null; + public String targetPackage = null; + public String targetProcesses = null; + public String toString(){ return null; } + public String[] splitNames = null; + public String[] splitPublicSourceDirs = null; + public String[] splitSourceDirs = null; + public boolean functionalTest = false; + public boolean handleProfiling = false; + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ModuleInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ModuleInfo.java new file mode 100644 index 00000000000..c7aef5f7ac7 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ModuleInfo.java @@ -0,0 +1,19 @@ +// Generated automatically from android.content.pm.ModuleInfo for testing purposes + +package android.content.pm; + +import android.os.Parcel; +import android.os.Parcelable; + +public class ModuleInfo implements Parcelable +{ + public CharSequence getName(){ return null; } + public String getPackageName(){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public boolean isHidden(){ return false; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/PackageInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/PackageInfo.java new file mode 100644 index 00000000000..566e8e73fbf --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/PackageInfo.java @@ -0,0 +1,59 @@ +// Generated automatically from android.content.pm.PackageInfo for testing purposes + +package android.content.pm; + +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ConfigurationInfo; +import android.content.pm.FeatureGroupInfo; +import android.content.pm.FeatureInfo; +import android.content.pm.InstrumentationInfo; +import android.content.pm.PermissionInfo; +import android.content.pm.ProviderInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.Signature; +import android.content.pm.SigningInfo; +import android.os.Parcel; +import android.os.Parcelable; + +public class PackageInfo implements Parcelable +{ + public ActivityInfo[] activities = null; + public ActivityInfo[] receivers = null; + public ApplicationInfo applicationInfo = null; + public ConfigurationInfo[] configPreferences = null; + public FeatureGroupInfo[] featureGroups = null; + public FeatureInfo[] reqFeatures = null; + public InstrumentationInfo[] instrumentation = null; + public PackageInfo(){} + public PermissionInfo[] permissions = null; + public ProviderInfo[] providers = null; + public ServiceInfo[] services = null; + public Signature[] signatures = null; + public SigningInfo signingInfo = null; + public String packageName = null; + public String sharedUserId = null; + public String toString(){ return null; } + public String versionName = null; + public String[] requestedPermissions = null; + public String[] splitNames = null; + public boolean isApex = false; + public int baseRevisionCode = 0; + public int describeContents(){ return 0; } + public int installLocation = 0; + public int sharedUserLabel = 0; + public int versionCode = 0; + public int[] gids = null; + public int[] requestedPermissionsFlags = null; + public int[] splitRevisionCodes = null; + public long firstInstallTime = 0; + public long getLongVersionCode(){ return 0; } + public long lastUpdateTime = 0; + public static Parcelable.Creator CREATOR = null; + public static int INSTALL_LOCATION_AUTO = 0; + public static int INSTALL_LOCATION_INTERNAL_ONLY = 0; + public static int INSTALL_LOCATION_PREFER_EXTERNAL = 0; + public static int REQUESTED_PERMISSION_GRANTED = 0; + public void setLongVersionCode(long p0){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/PackageInstaller.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/PackageInstaller.java new file mode 100644 index 00000000000..c836a4bfab9 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/PackageInstaller.java @@ -0,0 +1,146 @@ +// Generated automatically from android.content.pm.PackageInstaller for testing purposes + +package android.content.pm; + +import android.content.Intent; +import android.content.IntentSender; +import android.content.pm.VersionedPackage; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Handler; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.UserHandle; +import java.io.Closeable; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; +import java.util.Set; + +public class PackageInstaller +{ + abstract static public class SessionCallback + { + public SessionCallback(){} + public abstract void onActiveChanged(int p0, boolean p1); + public abstract void onBadgingChanged(int p0); + public abstract void onCreated(int p0); + public abstract void onFinished(int p0, boolean p1); + public abstract void onProgressChanged(int p0, float p1); + } + public List getAllSessions(){ return null; } + public List getMySessions(){ return null; } + public List getStagedSessions(){ return null; } + public PackageInstaller.Session openSession(int p0){ return null; } + public PackageInstaller.SessionInfo getActiveStagedSession(){ return null; } + public PackageInstaller.SessionInfo getSessionInfo(int p0){ return null; } + public int createSession(PackageInstaller.SessionParams p0){ return 0; } + public static String ACTION_SESSION_COMMITTED = null; + public static String ACTION_SESSION_DETAILS = null; + public static String ACTION_SESSION_UPDATED = null; + public static String EXTRA_OTHER_PACKAGE_NAME = null; + public static String EXTRA_PACKAGE_NAME = null; + public static String EXTRA_SESSION = null; + public static String EXTRA_SESSION_ID = null; + public static String EXTRA_STATUS = null; + public static String EXTRA_STATUS_MESSAGE = null; + public static String EXTRA_STORAGE_PATH = null; + public static int STATUS_FAILURE = 0; + public static int STATUS_FAILURE_ABORTED = 0; + public static int STATUS_FAILURE_BLOCKED = 0; + public static int STATUS_FAILURE_CONFLICT = 0; + public static int STATUS_FAILURE_INCOMPATIBLE = 0; + public static int STATUS_FAILURE_INVALID = 0; + public static int STATUS_FAILURE_STORAGE = 0; + public static int STATUS_PENDING_USER_ACTION = 0; + public static int STATUS_SUCCESS = 0; + public void abandonSession(int p0){} + public void installExistingPackage(String p0, int p1, IntentSender p2){} + public void registerSessionCallback(PackageInstaller.SessionCallback p0){} + public void registerSessionCallback(PackageInstaller.SessionCallback p0, Handler p1){} + public void uninstall(String p0, IntentSender p1){} + public void uninstall(VersionedPackage p0, IntentSender p1){} + public void unregisterSessionCallback(PackageInstaller.SessionCallback p0){} + public void updateSessionAppIcon(int p0, Bitmap p1){} + public void updateSessionAppLabel(int p0, CharSequence p1){} + static public class Session implements Closeable + { + public InputStream openRead(String p0){ return null; } + public OutputStream openWrite(String p0, long p1, long p2){ return null; } + public String[] getNames(){ return null; } + public boolean isMultiPackage(){ return false; } + public boolean isStaged(){ return false; } + public int getParentSessionId(){ return 0; } + public int[] getChildSessionIds(){ return null; } + public void abandon(){} + public void addChildSessionId(int p0){} + public void close(){} + public void commit(IntentSender p0){} + public void fsync(OutputStream p0){} + public void removeChildSessionId(int p0){} + public void removeSplit(String p0){} + public void setStagingProgress(float p0){} + public void transfer(String p0){} + } + static public class SessionInfo implements Parcelable + { + public Bitmap getAppIcon(){ return null; } + public CharSequence getAppLabel(){ return null; } + public Intent createDetailsIntent(){ return null; } + public String getAppPackageName(){ return null; } + public String getInstallerPackageName(){ return null; } + public String getStagedSessionErrorMessage(){ return null; } + public Uri getOriginatingUri(){ return null; } + public Uri getReferrerUri(){ return null; } + public UserHandle getUser(){ return null; } + public boolean isActive(){ return false; } + public boolean isCommitted(){ return false; } + public boolean isMultiPackage(){ return false; } + public boolean isSealed(){ return false; } + public boolean isStaged(){ return false; } + public boolean isStagedSessionApplied(){ return false; } + public boolean isStagedSessionFailed(){ return false; } + public boolean isStagedSessionReady(){ return false; } + public float getProgress(){ return 0; } + public int describeContents(){ return 0; } + public int getInstallLocation(){ return 0; } + public int getInstallReason(){ return 0; } + public int getMode(){ return 0; } + public int getOriginatingUid(){ return 0; } + public int getParentSessionId(){ return 0; } + public int getSessionId(){ return 0; } + public int getStagedSessionErrorCode(){ return 0; } + public int[] getChildSessionIds(){ return null; } + public long getSize(){ return 0; } + public long getUpdatedMillis(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static int INVALID_ID = 0; + public static int STAGED_SESSION_ACTIVATION_FAILED = 0; + public static int STAGED_SESSION_NO_ERROR = 0; + public static int STAGED_SESSION_UNKNOWN = 0; + public static int STAGED_SESSION_VERIFICATION_FAILED = 0; + public void writeToParcel(Parcel p0, int p1){} + } + static public class SessionParams implements Parcelable + { + protected SessionParams() {} + public SessionParams(int p0){} + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static Set RESTRICTED_PERMISSIONS_ALL = null; + public static int MODE_FULL_INSTALL = 0; + public static int MODE_INHERIT_EXISTING = 0; + public void setAppIcon(Bitmap p0){} + public void setAppLabel(CharSequence p0){} + public void setAppPackageName(String p0){} + public void setInstallLocation(int p0){} + public void setInstallReason(int p0){} + public void setMultiPackage(){} + public void setOriginatingUid(int p0){} + public void setOriginatingUri(Uri p0){} + public void setReferrerUri(Uri p0){} + public void setSize(long p0){} + public void setWhitelistedRestrictedPermissions(Set p0){} + public void writeToParcel(Parcel p0, int p1){} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/PackageItemInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/PackageItemInfo.java new file mode 100644 index 00000000000..1d35a31f52b --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/PackageItemInfo.java @@ -0,0 +1,34 @@ +// Generated automatically from android.content.pm.PackageItemInfo for testing purposes + +package android.content.pm; + +import android.content.pm.PackageManager; +import android.content.res.XmlResourceParser; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Parcel; +import android.util.Printer; + +public class PackageItemInfo +{ + protected PackageItemInfo(Parcel p0){} + protected void dumpBack(Printer p0, String p1){} + protected void dumpFront(Printer p0, String p1){} + public Bundle metaData = null; + public CharSequence loadLabel(PackageManager p0){ return null; } + public CharSequence nonLocalizedLabel = null; + public Drawable loadBanner(PackageManager p0){ return null; } + public Drawable loadIcon(PackageManager p0){ return null; } + public Drawable loadLogo(PackageManager p0){ return null; } + public Drawable loadUnbadgedIcon(PackageManager p0){ return null; } + public PackageItemInfo(){} + public PackageItemInfo(PackageItemInfo p0){} + public String name = null; + public String packageName = null; + public XmlResourceParser loadXmlMetaData(PackageManager p0, String p1){ return null; } + public int banner = 0; + public int icon = 0; + public int labelRes = 0; + public int logo = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/PackageManager.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/PackageManager.java new file mode 100644 index 00000000000..e3116359cbb --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/PackageManager.java @@ -0,0 +1,314 @@ +// Generated automatically from android.content.pm.PackageManager for testing purposes + +package android.content.pm; + +import android.content.ComponentName; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ChangedPackages; +import android.content.pm.FeatureInfo; +import android.content.pm.InstrumentationInfo; +import android.content.pm.ModuleInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageInstaller; +import android.content.pm.PermissionGroupInfo; +import android.content.pm.PermissionInfo; +import android.content.pm.ProviderInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.SharedLibraryInfo; +import android.content.pm.VersionedPackage; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.UserHandle; +import android.util.AndroidException; +import java.util.List; +import java.util.Set; + +abstract public class PackageManager +{ + public Bundle getSuspendedPackageAppExtras(){ return null; } + public List getInstalledModules(int p0){ return null; } + public ModuleInfo getModuleInfo(String p0, int p1){ return null; } + public PackageInfo getPackageArchiveInfo(String p0, int p1){ return null; } + public PackageManager(){} + public Set getWhitelistedRestrictedPermissions(String p0, int p1){ return null; } + public abstract ActivityInfo getActivityInfo(ComponentName p0, int p1); + public abstract ActivityInfo getReceiverInfo(ComponentName p0, int p1); + public abstract ApplicationInfo getApplicationInfo(String p0, int p1); + public abstract ChangedPackages getChangedPackages(int p0); + public abstract CharSequence getApplicationLabel(ApplicationInfo p0); + public abstract CharSequence getText(String p0, int p1, ApplicationInfo p2); + public abstract CharSequence getUserBadgedLabel(CharSequence p0, UserHandle p1); + public abstract Drawable getActivityBanner(ComponentName p0); + public abstract Drawable getActivityBanner(Intent p0); + public abstract Drawable getActivityIcon(ComponentName p0); + public abstract Drawable getActivityIcon(Intent p0); + public abstract Drawable getActivityLogo(ComponentName p0); + public abstract Drawable getActivityLogo(Intent p0); + public abstract Drawable getApplicationBanner(ApplicationInfo p0); + public abstract Drawable getApplicationBanner(String p0); + public abstract Drawable getApplicationIcon(ApplicationInfo p0); + public abstract Drawable getApplicationIcon(String p0); + public abstract Drawable getApplicationLogo(ApplicationInfo p0); + public abstract Drawable getApplicationLogo(String p0); + public abstract Drawable getDefaultActivityIcon(); + public abstract Drawable getDrawable(String p0, int p1, ApplicationInfo p2); + public abstract Drawable getUserBadgedDrawableForDensity(Drawable p0, UserHandle p1, Rect p2, int p3); + public abstract Drawable getUserBadgedIcon(Drawable p0, UserHandle p1); + public abstract FeatureInfo[] getSystemAvailableFeatures(); + public abstract InstrumentationInfo getInstrumentationInfo(ComponentName p0, int p1); + public abstract Intent getLaunchIntentForPackage(String p0); + public abstract Intent getLeanbackLaunchIntentForPackage(String p0); + public abstract List getInstalledApplications(int p0); + public abstract List queryInstrumentation(String p0, int p1); + public abstract List getInstalledPackages(int p0); + public abstract List getPackagesHoldingPermissions(String[] p0, int p1); + public abstract List getPreferredPackages(int p0); + public abstract List getAllPermissionGroups(int p0); + public abstract List queryPermissionsByGroup(String p0, int p1); + public abstract List queryContentProviders(String p0, int p1, int p2); + public abstract List queryBroadcastReceivers(Intent p0, int p1); + public abstract List queryIntentActivities(Intent p0, int p1); + public abstract List queryIntentActivityOptions(ComponentName p0, Intent[] p1, Intent p2, int p3); + public abstract List queryIntentContentProviders(Intent p0, int p1); + public abstract List queryIntentServices(Intent p0, int p1); + public abstract List getSharedLibraries(int p0); + public abstract PackageInfo getPackageInfo(String p0, int p1); + public abstract PackageInfo getPackageInfo(VersionedPackage p0, int p1); + public abstract PackageInstaller getPackageInstaller(); + public abstract PermissionGroupInfo getPermissionGroupInfo(String p0, int p1); + public abstract PermissionInfo getPermissionInfo(String p0, int p1); + public abstract ProviderInfo getProviderInfo(ComponentName p0, int p1); + public abstract ProviderInfo resolveContentProvider(String p0, int p1); + public abstract ResolveInfo resolveActivity(Intent p0, int p1); + public abstract ResolveInfo resolveService(Intent p0, int p1); + public abstract Resources getResourcesForActivity(ComponentName p0); + public abstract Resources getResourcesForApplication(ApplicationInfo p0); + public abstract Resources getResourcesForApplication(String p0); + public abstract ServiceInfo getServiceInfo(ComponentName p0, int p1); + public abstract String getInstallerPackageName(String p0); + public abstract String getNameForUid(int p0); + public abstract String[] canonicalToCurrentPackageNames(String[] p0); + public abstract String[] currentToCanonicalPackageNames(String[] p0); + public abstract String[] getPackagesForUid(int p0); + public abstract String[] getSystemSharedLibraryNames(); + public abstract XmlResourceParser getXml(String p0, int p1, ApplicationInfo p2); + public abstract boolean addPermission(PermissionInfo p0); + public abstract boolean addPermissionAsync(PermissionInfo p0); + public abstract boolean canRequestPackageInstalls(); + public abstract boolean hasSystemFeature(String p0); + public abstract boolean hasSystemFeature(String p0, int p1); + public abstract boolean isInstantApp(); + public abstract boolean isInstantApp(String p0); + public abstract boolean isPermissionRevokedByPolicy(String p0, String p1); + public abstract boolean isSafeMode(); + public abstract byte[] getInstantAppCookie(); + public abstract int checkPermission(String p0, String p1); + public abstract int checkSignatures(String p0, String p1); + public abstract int checkSignatures(int p0, int p1); + public abstract int getApplicationEnabledSetting(String p0); + public abstract int getComponentEnabledSetting(ComponentName p0); + public abstract int getInstantAppCookieMaxBytes(); + public abstract int getPackageUid(String p0, int p1); + public abstract int getPreferredActivities(List p0, List p1, String p2); + public abstract int[] getPackageGids(String p0); + public abstract int[] getPackageGids(String p0, int p1); + public abstract void addPackageToPreferred(String p0); + public abstract void addPreferredActivity(IntentFilter p0, int p1, ComponentName[] p2, ComponentName p3); + public abstract void clearInstantAppCookie(); + public abstract void clearPackagePreferredActivities(String p0); + public abstract void extendVerificationTimeout(int p0, int p1, long p2); + public abstract void removePackageFromPreferred(String p0); + public abstract void removePermission(String p0); + public abstract void setApplicationCategoryHint(String p0, int p1); + public abstract void setApplicationEnabledSetting(String p0, int p1, int p2); + public abstract void setComponentEnabledSetting(ComponentName p0, int p1, int p2); + public abstract void setInstallerPackageName(String p0, String p1); + public abstract void updateInstantAppCookie(byte[] p0); + public abstract void verifyPendingInstall(int p0, int p1); + public boolean addWhitelistedRestrictedPermission(String p0, String p1, int p2){ return false; } + public boolean getSyntheticAppDetailsActivityEnabled(String p0){ return false; } + public boolean hasSigningCertificate(String p0, byte[] p1, int p2){ return false; } + public boolean hasSigningCertificate(int p0, byte[] p1, int p2){ return false; } + public boolean isDeviceUpgrading(){ return false; } + public boolean isPackageSuspended(){ return false; } + public boolean isPackageSuspended(String p0){ return false; } + public boolean removeWhitelistedRestrictedPermission(String p0, String p1, int p2){ return false; } + public static String EXTRA_VERIFICATION_ID = null; + public static String EXTRA_VERIFICATION_RESULT = null; + public static String FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS = null; + public static String FEATURE_APP_WIDGETS = null; + public static String FEATURE_AUDIO_LOW_LATENCY = null; + public static String FEATURE_AUDIO_OUTPUT = null; + public static String FEATURE_AUDIO_PRO = null; + public static String FEATURE_AUTOFILL = null; + public static String FEATURE_AUTOMOTIVE = null; + public static String FEATURE_BACKUP = null; + public static String FEATURE_BLUETOOTH = null; + public static String FEATURE_BLUETOOTH_LE = null; + public static String FEATURE_CAMERA = null; + public static String FEATURE_CAMERA_ANY = null; + public static String FEATURE_CAMERA_AR = null; + public static String FEATURE_CAMERA_AUTOFOCUS = null; + public static String FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING = null; + public static String FEATURE_CAMERA_CAPABILITY_MANUAL_SENSOR = null; + public static String FEATURE_CAMERA_CAPABILITY_RAW = null; + public static String FEATURE_CAMERA_EXTERNAL = null; + public static String FEATURE_CAMERA_FLASH = null; + public static String FEATURE_CAMERA_FRONT = null; + public static String FEATURE_CAMERA_LEVEL_FULL = null; + public static String FEATURE_CANT_SAVE_STATE = null; + public static String FEATURE_COMPANION_DEVICE_SETUP = null; + public static String FEATURE_CONNECTION_SERVICE = null; + public static String FEATURE_CONSUMER_IR = null; + public static String FEATURE_DEVICE_ADMIN = null; + public static String FEATURE_EMBEDDED = null; + public static String FEATURE_ETHERNET = null; + public static String FEATURE_FACE = null; + public static String FEATURE_FAKETOUCH = null; + public static String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = null; + public static String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = null; + public static String FEATURE_FINGERPRINT = null; + public static String FEATURE_FREEFORM_WINDOW_MANAGEMENT = null; + public static String FEATURE_GAMEPAD = null; + public static String FEATURE_HIFI_SENSORS = null; + public static String FEATURE_HOME_SCREEN = null; + public static String FEATURE_INPUT_METHODS = null; + public static String FEATURE_IPSEC_TUNNELS = null; + public static String FEATURE_IRIS = null; + public static String FEATURE_LEANBACK = null; + public static String FEATURE_LEANBACK_ONLY = null; + public static String FEATURE_LIVE_TV = null; + public static String FEATURE_LIVE_WALLPAPER = null; + public static String FEATURE_LOCATION = null; + public static String FEATURE_LOCATION_GPS = null; + public static String FEATURE_LOCATION_NETWORK = null; + public static String FEATURE_MANAGED_USERS = null; + public static String FEATURE_MICROPHONE = null; + public static String FEATURE_MIDI = null; + public static String FEATURE_NFC = null; + public static String FEATURE_NFC_BEAM = null; + public static String FEATURE_NFC_HOST_CARD_EMULATION = null; + public static String FEATURE_NFC_HOST_CARD_EMULATION_NFCF = null; + public static String FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE = null; + public static String FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC = null; + public static String FEATURE_OPENGLES_EXTENSION_PACK = null; + public static String FEATURE_PC = null; + public static String FEATURE_PICTURE_IN_PICTURE = null; + public static String FEATURE_PRINTING = null; + public static String FEATURE_RAM_LOW = null; + public static String FEATURE_RAM_NORMAL = null; + public static String FEATURE_SCREEN_LANDSCAPE = null; + public static String FEATURE_SCREEN_PORTRAIT = null; + public static String FEATURE_SECURELY_REMOVES_USERS = null; + public static String FEATURE_SECURE_LOCK_SCREEN = null; + public static String FEATURE_SENSOR_ACCELEROMETER = null; + public static String FEATURE_SENSOR_AMBIENT_TEMPERATURE = null; + public static String FEATURE_SENSOR_BAROMETER = null; + public static String FEATURE_SENSOR_COMPASS = null; + public static String FEATURE_SENSOR_GYROSCOPE = null; + public static String FEATURE_SENSOR_HEART_RATE = null; + public static String FEATURE_SENSOR_HEART_RATE_ECG = null; + public static String FEATURE_SENSOR_LIGHT = null; + public static String FEATURE_SENSOR_PROXIMITY = null; + public static String FEATURE_SENSOR_RELATIVE_HUMIDITY = null; + public static String FEATURE_SENSOR_STEP_COUNTER = null; + public static String FEATURE_SENSOR_STEP_DETECTOR = null; + public static String FEATURE_SIP = null; + public static String FEATURE_SIP_VOIP = null; + public static String FEATURE_STRONGBOX_KEYSTORE = null; + public static String FEATURE_TELEPHONY = null; + public static String FEATURE_TELEPHONY_CDMA = null; + public static String FEATURE_TELEPHONY_EUICC = null; + public static String FEATURE_TELEPHONY_GSM = null; + public static String FEATURE_TELEPHONY_IMS = null; + public static String FEATURE_TELEPHONY_MBMS = null; + public static String FEATURE_TELEVISION = null; + public static String FEATURE_TOUCHSCREEN = null; + public static String FEATURE_TOUCHSCREEN_MULTITOUCH = null; + public static String FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT = null; + public static String FEATURE_TOUCHSCREEN_MULTITOUCH_JAZZHAND = null; + public static String FEATURE_USB_ACCESSORY = null; + public static String FEATURE_USB_HOST = null; + public static String FEATURE_VERIFIED_BOOT = null; + public static String FEATURE_VR_HEADTRACKING = null; + public static String FEATURE_VR_MODE = null; + public static String FEATURE_VR_MODE_HIGH_PERFORMANCE = null; + public static String FEATURE_VULKAN_HARDWARE_COMPUTE = null; + public static String FEATURE_VULKAN_HARDWARE_LEVEL = null; + public static String FEATURE_VULKAN_HARDWARE_VERSION = null; + public static String FEATURE_WATCH = null; + public static String FEATURE_WEBVIEW = null; + public static String FEATURE_WIFI = null; + public static String FEATURE_WIFI_AWARE = null; + public static String FEATURE_WIFI_DIRECT = null; + public static String FEATURE_WIFI_PASSPOINT = null; + public static String FEATURE_WIFI_RTT = null; + public static int CERT_INPUT_RAW_X509 = 0; + public static int CERT_INPUT_SHA256 = 0; + public static int COMPONENT_ENABLED_STATE_DEFAULT = 0; + public static int COMPONENT_ENABLED_STATE_DISABLED = 0; + public static int COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED = 0; + public static int COMPONENT_ENABLED_STATE_DISABLED_USER = 0; + public static int COMPONENT_ENABLED_STATE_ENABLED = 0; + public static int DONT_KILL_APP = 0; + public static int FLAG_PERMISSION_WHITELIST_INSTALLER = 0; + public static int FLAG_PERMISSION_WHITELIST_SYSTEM = 0; + public static int FLAG_PERMISSION_WHITELIST_UPGRADE = 0; + public static int GET_ACTIVITIES = 0; + public static int GET_CONFIGURATIONS = 0; + public static int GET_DISABLED_COMPONENTS = 0; + public static int GET_DISABLED_UNTIL_USED_COMPONENTS = 0; + public static int GET_GIDS = 0; + public static int GET_INSTRUMENTATION = 0; + public static int GET_INTENT_FILTERS = 0; + public static int GET_META_DATA = 0; + public static int GET_PERMISSIONS = 0; + public static int GET_PROVIDERS = 0; + public static int GET_RECEIVERS = 0; + public static int GET_RESOLVED_FILTER = 0; + public static int GET_SERVICES = 0; + public static int GET_SHARED_LIBRARY_FILES = 0; + public static int GET_SIGNATURES = 0; + public static int GET_SIGNING_CERTIFICATES = 0; + public static int GET_UNINSTALLED_PACKAGES = 0; + public static int GET_URI_PERMISSION_PATTERNS = 0; + public static int INSTALL_REASON_DEVICE_RESTORE = 0; + public static int INSTALL_REASON_DEVICE_SETUP = 0; + public static int INSTALL_REASON_POLICY = 0; + public static int INSTALL_REASON_UNKNOWN = 0; + public static int INSTALL_REASON_USER = 0; + public static int MATCH_ALL = 0; + public static int MATCH_APEX = 0; + public static int MATCH_DEFAULT_ONLY = 0; + public static int MATCH_DIRECT_BOOT_AUTO = 0; + public static int MATCH_DIRECT_BOOT_AWARE = 0; + public static int MATCH_DIRECT_BOOT_UNAWARE = 0; + public static int MATCH_DISABLED_COMPONENTS = 0; + public static int MATCH_DISABLED_UNTIL_USED_COMPONENTS = 0; + public static int MATCH_SYSTEM_ONLY = 0; + public static int MATCH_UNINSTALLED_PACKAGES = 0; + public static int PERMISSION_DENIED = 0; + public static int PERMISSION_GRANTED = 0; + public static int SIGNATURE_FIRST_NOT_SIGNED = 0; + public static int SIGNATURE_MATCH = 0; + public static int SIGNATURE_NEITHER_SIGNED = 0; + public static int SIGNATURE_NO_MATCH = 0; + public static int SIGNATURE_SECOND_NOT_SIGNED = 0; + public static int SIGNATURE_UNKNOWN_PACKAGE = 0; + public static int VERIFICATION_ALLOW = 0; + public static int VERIFICATION_REJECT = 0; + public static int VERSION_CODE_HIGHEST = 0; + public static long MAXIMUM_VERIFICATION_TIMEOUT = 0; + static public class NameNotFoundException extends AndroidException + { + public NameNotFoundException(){} + public NameNotFoundException(String p0){} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/PermissionGroupInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/PermissionGroupInfo.java new file mode 100644 index 00000000000..a74934de52f --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/PermissionGroupInfo.java @@ -0,0 +1,24 @@ +// Generated automatically from android.content.pm.PermissionGroupInfo for testing purposes + +package android.content.pm; + +import android.content.pm.PackageItemInfo; +import android.content.pm.PackageManager; +import android.os.Parcel; +import android.os.Parcelable; + +public class PermissionGroupInfo extends PackageItemInfo implements Parcelable +{ + public CharSequence loadDescription(PackageManager p0){ return null; } + public CharSequence nonLocalizedDescription = null; + public PermissionGroupInfo(){} + public PermissionGroupInfo(PermissionGroupInfo p0){} + public String toString(){ return null; } + public int describeContents(){ return 0; } + public int descriptionRes = 0; + public int flags = 0; + public int priority = 0; + public static Parcelable.Creator CREATOR = null; + public static int FLAG_PERSONAL_INFO = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/PermissionInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/PermissionInfo.java new file mode 100644 index 00000000000..2c95bb9af7a --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/PermissionInfo.java @@ -0,0 +1,48 @@ +// Generated automatically from android.content.pm.PermissionInfo for testing purposes + +package android.content.pm; + +import android.content.pm.PackageItemInfo; +import android.content.pm.PackageManager; +import android.os.Parcel; +import android.os.Parcelable; + +public class PermissionInfo extends PackageItemInfo implements Parcelable +{ + public CharSequence loadDescription(PackageManager p0){ return null; } + public CharSequence nonLocalizedDescription = null; + public PermissionInfo(){} + public PermissionInfo(PermissionInfo p0){} + public String group = null; + public String toString(){ return null; } + public int describeContents(){ return 0; } + public int descriptionRes = 0; + public int flags = 0; + public int getProtection(){ return 0; } + public int getProtectionFlags(){ return 0; } + public int protectionLevel = 0; + public static Parcelable.Creator CREATOR = null; + public static int FLAG_COSTS_MONEY = 0; + public static int FLAG_HARD_RESTRICTED = 0; + public static int FLAG_IMMUTABLY_RESTRICTED = 0; + public static int FLAG_INSTALLED = 0; + public static int FLAG_SOFT_RESTRICTED = 0; + public static int PROTECTION_DANGEROUS = 0; + public static int PROTECTION_FLAG_APPOP = 0; + public static int PROTECTION_FLAG_DEVELOPMENT = 0; + public static int PROTECTION_FLAG_INSTALLER = 0; + public static int PROTECTION_FLAG_INSTANT = 0; + public static int PROTECTION_FLAG_PRE23 = 0; + public static int PROTECTION_FLAG_PREINSTALLED = 0; + public static int PROTECTION_FLAG_PRIVILEGED = 0; + public static int PROTECTION_FLAG_RUNTIME_ONLY = 0; + public static int PROTECTION_FLAG_SETUP = 0; + public static int PROTECTION_FLAG_SYSTEM = 0; + public static int PROTECTION_FLAG_VERIFIER = 0; + public static int PROTECTION_MASK_BASE = 0; + public static int PROTECTION_MASK_FLAGS = 0; + public static int PROTECTION_NORMAL = 0; + public static int PROTECTION_SIGNATURE = 0; + public static int PROTECTION_SIGNATURE_OR_SYSTEM = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ProviderInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ProviderInfo.java new file mode 100644 index 00000000000..93b694cae59 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ProviderInfo.java @@ -0,0 +1,33 @@ +// Generated automatically from android.content.pm.ProviderInfo for testing purposes + +package android.content.pm; + +import android.content.pm.ComponentInfo; +import android.content.pm.PathPermission; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PatternMatcher; +import android.util.Printer; + +public class ProviderInfo extends ComponentInfo implements Parcelable +{ + public PathPermission[] pathPermissions = null; + public PatternMatcher[] uriPermissionPatterns = null; + public ProviderInfo(){} + public ProviderInfo(ProviderInfo p0){} + public String authority = null; + public String readPermission = null; + public String toString(){ return null; } + public String writePermission = null; + public boolean forceUriPermissions = false; + public boolean grantUriPermissions = false; + public boolean isSyncable = false; + public boolean multiprocess = false; + public int describeContents(){ return 0; } + public int flags = 0; + public int initOrder = 0; + public static Parcelable.Creator CREATOR = null; + public static int FLAG_SINGLE_USER = 0; + public void dump(Printer p0, String p1){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ResolveInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ResolveInfo.java new file mode 100644 index 00000000000..a06bcbdccc6 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ResolveInfo.java @@ -0,0 +1,41 @@ +// Generated automatically from android.content.pm.ResolveInfo for testing purposes + +package android.content.pm; + +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.ProviderInfo; +import android.content.pm.ServiceInfo; +import android.graphics.drawable.Drawable; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Printer; + +public class ResolveInfo implements Parcelable +{ + public ActivityInfo activityInfo = null; + public CharSequence loadLabel(PackageManager p0){ return null; } + public CharSequence nonLocalizedLabel = null; + public Drawable loadIcon(PackageManager p0){ return null; } + public IntentFilter filter = null; + public ProviderInfo providerInfo = null; + public ResolveInfo(){} + public ResolveInfo(ResolveInfo p0){} + public ServiceInfo serviceInfo = null; + public String resolvePackageName = null; + public String toString(){ return null; } + public boolean isDefault = false; + public boolean isInstantAppAvailable = false; + public final int getIconResource(){ return 0; } + public int describeContents(){ return 0; } + public int icon = 0; + public int labelRes = 0; + public int match = 0; + public int preferredOrder = 0; + public int priority = 0; + public int specificIndex = 0; + public static Parcelable.Creator CREATOR = null; + public void dump(Printer p0, String p1){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ServiceInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ServiceInfo.java new file mode 100644 index 00000000000..e7fdb2c49f1 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/ServiceInfo.java @@ -0,0 +1,35 @@ +// Generated automatically from android.content.pm.ServiceInfo for testing purposes + +package android.content.pm; + +import android.content.pm.ComponentInfo; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Printer; + +public class ServiceInfo extends ComponentInfo implements Parcelable +{ + public ServiceInfo(){} + public ServiceInfo(ServiceInfo p0){} + public String permission = null; + public String toString(){ return null; } + public int describeContents(){ return 0; } + public int flags = 0; + public int getForegroundServiceType(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static int FLAG_EXTERNAL_SERVICE = 0; + public static int FLAG_ISOLATED_PROCESS = 0; + public static int FLAG_SINGLE_USER = 0; + public static int FLAG_STOP_WITH_TASK = 0; + public static int FLAG_USE_APP_ZYGOTE = 0; + public static int FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE = 0; + public static int FOREGROUND_SERVICE_TYPE_DATA_SYNC = 0; + public static int FOREGROUND_SERVICE_TYPE_LOCATION = 0; + public static int FOREGROUND_SERVICE_TYPE_MANIFEST = 0; + public static int FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK = 0; + public static int FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION = 0; + public static int FOREGROUND_SERVICE_TYPE_NONE = 0; + public static int FOREGROUND_SERVICE_TYPE_PHONE_CALL = 0; + public void dump(Printer p0, String p1){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/SharedLibraryInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/SharedLibraryInfo.java new file mode 100644 index 00000000000..33c1173420d --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/SharedLibraryInfo.java @@ -0,0 +1,27 @@ +// Generated automatically from android.content.pm.SharedLibraryInfo for testing purposes + +package android.content.pm; + +import android.content.pm.VersionedPackage; +import android.os.Parcel; +import android.os.Parcelable; +import java.util.List; + +public class SharedLibraryInfo implements Parcelable +{ + protected SharedLibraryInfo() {} + public List getDependentPackages(){ return null; } + public String getName(){ return null; } + public String toString(){ return null; } + public VersionedPackage getDeclaringPackage(){ return null; } + public int describeContents(){ return 0; } + public int getType(){ return 0; } + public int getVersion(){ return 0; } + public long getLongVersion(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static int TYPE_BUILTIN = 0; + public static int TYPE_DYNAMIC = 0; + public static int TYPE_STATIC = 0; + public static int VERSION_UNDEFINED = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/Signature.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/Signature.java new file mode 100644 index 00000000000..c7d3a4dd055 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/Signature.java @@ -0,0 +1,3 @@ +package android.content.pm; + +interface Signature { } \ No newline at end of file diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/SigningInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/SigningInfo.java new file mode 100644 index 00000000000..56aadea159a --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/SigningInfo.java @@ -0,0 +1,20 @@ +// Generated automatically from android.content.pm.SigningInfo for testing purposes + +package android.content.pm; + +import android.content.pm.Signature; +import android.os.Parcel; +import android.os.Parcelable; + +public class SigningInfo implements Parcelable +{ + public Signature[] getApkContentsSigners(){ return null; } + public Signature[] getSigningCertificateHistory(){ return null; } + public SigningInfo(){} + public SigningInfo(SigningInfo p0){} + public boolean hasMultipleSigners(){ return false; } + public boolean hasPastSigningCertificates(){ return false; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/pm/VersionedPackage.java b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/VersionedPackage.java new file mode 100644 index 00000000000..d20014d0fcc --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/pm/VersionedPackage.java @@ -0,0 +1,20 @@ +// Generated automatically from android.content.pm.VersionedPackage for testing purposes + +package android.content.pm; + +import android.os.Parcel; +import android.os.Parcelable; + +public class VersionedPackage implements Parcelable +{ + protected VersionedPackage() {} + public String getPackageName(){ return null; } + public String toString(){ return null; } + public VersionedPackage(String p0, int p1){} + public VersionedPackage(String p0, long p1){} + public int describeContents(){ return 0; } + public int getVersionCode(){ return 0; } + public long getLongVersionCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/res/AssetManager.java b/java/ql/test/stubs/google-android-9.0.0/android/content/res/AssetManager.java new file mode 100644 index 00000000000..37926aa5c61 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/res/AssetManager.java @@ -0,0 +1,26 @@ +// Generated automatically from android.content.res.AssetManager for testing purposes + +package android.content.res; + +import android.content.res.AssetFileDescriptor; +import android.content.res.XmlResourceParser; +import java.io.InputStream; + +public class AssetManager implements AutoCloseable +{ + protected void finalize(){} + public AssetFileDescriptor openFd(String p0){ return null; } + public AssetFileDescriptor openNonAssetFd(String p0){ return null; } + public AssetFileDescriptor openNonAssetFd(int p0, String p1){ return null; } + public InputStream open(String p0){ return null; } + public InputStream open(String p0, int p1){ return null; } + public String[] getLocales(){ return null; } + public String[] list(String p0){ return null; } + public XmlResourceParser openXmlResourceParser(String p0){ return null; } + public XmlResourceParser openXmlResourceParser(int p0, String p1){ return null; } + public static int ACCESS_BUFFER = 0; + public static int ACCESS_RANDOM = 0; + public static int ACCESS_STREAMING = 0; + public static int ACCESS_UNKNOWN = 0; + public void close(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/res/ColorStateList.java b/java/ql/test/stubs/google-android-9.0.0/android/content/res/ColorStateList.java new file mode 100644 index 00000000000..cc6a1851767 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/res/ColorStateList.java @@ -0,0 +1,27 @@ +// Generated automatically from android.content.res.ColorStateList for testing purposes + +package android.content.res; + +import android.content.res.Resources; +import android.os.Parcel; +import android.os.Parcelable; +import org.xmlpull.v1.XmlPullParser; + +public class ColorStateList implements Parcelable +{ + protected ColorStateList() {} + public ColorStateList withAlpha(int p0){ return null; } + public ColorStateList(int[][] p0, int[] p1){} + public String toString(){ return null; } + public boolean isOpaque(){ return false; } + public boolean isStateful(){ return false; } + public int describeContents(){ return 0; } + public int getChangingConfigurations(){ return 0; } + public int getColorForState(int[] p0, int p1){ return 0; } + public int getDefaultColor(){ return 0; } + public static ColorStateList createFromXml(Resources p0, XmlPullParser p1){ return null; } + public static ColorStateList createFromXml(Resources p0, XmlPullParser p1, Resources.Theme p2){ return null; } + public static ColorStateList valueOf(int p0){ return null; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/res/Configuration.java b/java/ql/test/stubs/google-android-9.0.0/android/content/res/Configuration.java new file mode 100644 index 00000000000..2215536a74e --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/res/Configuration.java @@ -0,0 +1,129 @@ +// Generated automatically from android.content.res.Configuration for testing purposes + +package android.content.res; + +import android.os.LocaleList; +import android.os.Parcel; +import android.os.Parcelable; +import java.util.Locale; + +public class Configuration implements Comparable, Parcelable +{ + public Configuration(){} + public Configuration(Configuration p0){} + public Locale locale = null; + public LocaleList getLocales(){ return null; } + public String toString(){ return null; } + public boolean equals(Configuration p0){ return false; } + public boolean equals(Object p0){ return false; } + public boolean isLayoutSizeAtLeast(int p0){ return false; } + public boolean isScreenHdr(){ return false; } + public boolean isScreenRound(){ return false; } + public boolean isScreenWideColorGamut(){ return false; } + public float fontScale = 0; + public int colorMode = 0; + public int compareTo(Configuration p0){ return 0; } + public int densityDpi = 0; + public int describeContents(){ return 0; } + public int diff(Configuration p0){ return 0; } + public int getLayoutDirection(){ return 0; } + public int hardKeyboardHidden = 0; + public int hashCode(){ return 0; } + public int keyboard = 0; + public int keyboardHidden = 0; + public int mcc = 0; + public int mnc = 0; + public int navigation = 0; + public int navigationHidden = 0; + public int orientation = 0; + public int screenHeightDp = 0; + public int screenLayout = 0; + public int screenWidthDp = 0; + public int smallestScreenWidthDp = 0; + public int touchscreen = 0; + public int uiMode = 0; + public int updateFrom(Configuration p0){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static boolean needNewResources(int p0, int p1){ return false; } + public static int COLOR_MODE_HDR_MASK = 0; + public static int COLOR_MODE_HDR_NO = 0; + public static int COLOR_MODE_HDR_SHIFT = 0; + public static int COLOR_MODE_HDR_UNDEFINED = 0; + public static int COLOR_MODE_HDR_YES = 0; + public static int COLOR_MODE_UNDEFINED = 0; + public static int COLOR_MODE_WIDE_COLOR_GAMUT_MASK = 0; + public static int COLOR_MODE_WIDE_COLOR_GAMUT_NO = 0; + public static int COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED = 0; + public static int COLOR_MODE_WIDE_COLOR_GAMUT_YES = 0; + public static int DENSITY_DPI_UNDEFINED = 0; + public static int HARDKEYBOARDHIDDEN_NO = 0; + public static int HARDKEYBOARDHIDDEN_UNDEFINED = 0; + public static int HARDKEYBOARDHIDDEN_YES = 0; + public static int KEYBOARDHIDDEN_NO = 0; + public static int KEYBOARDHIDDEN_UNDEFINED = 0; + public static int KEYBOARDHIDDEN_YES = 0; + public static int KEYBOARD_12KEY = 0; + public static int KEYBOARD_NOKEYS = 0; + public static int KEYBOARD_QWERTY = 0; + public static int KEYBOARD_UNDEFINED = 0; + public static int MNC_ZERO = 0; + public static int NAVIGATIONHIDDEN_NO = 0; + public static int NAVIGATIONHIDDEN_UNDEFINED = 0; + public static int NAVIGATIONHIDDEN_YES = 0; + public static int NAVIGATION_DPAD = 0; + public static int NAVIGATION_NONAV = 0; + public static int NAVIGATION_TRACKBALL = 0; + public static int NAVIGATION_UNDEFINED = 0; + public static int NAVIGATION_WHEEL = 0; + public static int ORIENTATION_LANDSCAPE = 0; + public static int ORIENTATION_PORTRAIT = 0; + public static int ORIENTATION_SQUARE = 0; + public static int ORIENTATION_UNDEFINED = 0; + public static int SCREENLAYOUT_LAYOUTDIR_LTR = 0; + public static int SCREENLAYOUT_LAYOUTDIR_MASK = 0; + public static int SCREENLAYOUT_LAYOUTDIR_RTL = 0; + public static int SCREENLAYOUT_LAYOUTDIR_SHIFT = 0; + public static int SCREENLAYOUT_LAYOUTDIR_UNDEFINED = 0; + public static int SCREENLAYOUT_LONG_MASK = 0; + public static int SCREENLAYOUT_LONG_NO = 0; + public static int SCREENLAYOUT_LONG_UNDEFINED = 0; + public static int SCREENLAYOUT_LONG_YES = 0; + public static int SCREENLAYOUT_ROUND_MASK = 0; + public static int SCREENLAYOUT_ROUND_NO = 0; + public static int SCREENLAYOUT_ROUND_UNDEFINED = 0; + public static int SCREENLAYOUT_ROUND_YES = 0; + public static int SCREENLAYOUT_SIZE_LARGE = 0; + public static int SCREENLAYOUT_SIZE_MASK = 0; + public static int SCREENLAYOUT_SIZE_NORMAL = 0; + public static int SCREENLAYOUT_SIZE_SMALL = 0; + public static int SCREENLAYOUT_SIZE_UNDEFINED = 0; + public static int SCREENLAYOUT_SIZE_XLARGE = 0; + public static int SCREENLAYOUT_UNDEFINED = 0; + public static int SCREEN_HEIGHT_DP_UNDEFINED = 0; + public static int SCREEN_WIDTH_DP_UNDEFINED = 0; + public static int SMALLEST_SCREEN_WIDTH_DP_UNDEFINED = 0; + public static int TOUCHSCREEN_FINGER = 0; + public static int TOUCHSCREEN_NOTOUCH = 0; + public static int TOUCHSCREEN_STYLUS = 0; + public static int TOUCHSCREEN_UNDEFINED = 0; + public static int UI_MODE_NIGHT_MASK = 0; + public static int UI_MODE_NIGHT_NO = 0; + public static int UI_MODE_NIGHT_UNDEFINED = 0; + public static int UI_MODE_NIGHT_YES = 0; + public static int UI_MODE_TYPE_APPLIANCE = 0; + public static int UI_MODE_TYPE_CAR = 0; + public static int UI_MODE_TYPE_DESK = 0; + public static int UI_MODE_TYPE_MASK = 0; + public static int UI_MODE_TYPE_NORMAL = 0; + public static int UI_MODE_TYPE_TELEVISION = 0; + public static int UI_MODE_TYPE_UNDEFINED = 0; + public static int UI_MODE_TYPE_VR_HEADSET = 0; + public static int UI_MODE_TYPE_WATCH = 0; + public void readFromParcel(Parcel p0){} + public void setLayoutDirection(Locale p0){} + public void setLocale(Locale p0){} + public void setLocales(LocaleList p0){} + public void setTo(Configuration p0){} + public void setToDefaults(){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/res/Resources.java b/java/ql/test/stubs/google-android-9.0.0/android/content/res/Resources.java new file mode 100644 index 00000000000..96847810672 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/res/Resources.java @@ -0,0 +1,101 @@ +// Generated automatically from android.content.res.Resources for testing purposes + +package android.content.res; + +import android.content.res.AssetFileDescriptor; +import android.content.res.AssetManager; +import android.content.res.ColorStateList; +import android.content.res.Configuration; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.graphics.Movie; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import java.io.InputStream; + +public class Resources +{ + protected Resources() {} + public AssetFileDescriptor openRawResourceFd(int p0){ return null; } + public CharSequence getQuantityText(int p0, int p1){ return null; } + public CharSequence getText(int p0){ return null; } + public CharSequence getText(int p0, CharSequence p1){ return null; } + public CharSequence[] getTextArray(int p0){ return null; } + public ColorStateList getColorStateList(int p0){ return null; } + public ColorStateList getColorStateList(int p0, Resources.Theme p1){ return null; } + public Configuration getConfiguration(){ return null; } + public DisplayMetrics getDisplayMetrics(){ return null; } + public Drawable getDrawable(int p0){ return null; } + public Drawable getDrawable(int p0, Resources.Theme p1){ return null; } + public Drawable getDrawableForDensity(int p0, int p1){ return null; } + public Drawable getDrawableForDensity(int p0, int p1, Resources.Theme p2){ return null; } + public InputStream openRawResource(int p0){ return null; } + public InputStream openRawResource(int p0, TypedValue p1){ return null; } + public Movie getMovie(int p0){ return null; } + public Resources(AssetManager p0, DisplayMetrics p1, Configuration p2){} + public String getQuantityString(int p0, int p1){ return null; } + public String getQuantityString(int p0, int p1, Object... p2){ return null; } + public String getResourceEntryName(int p0){ return null; } + public String getResourceName(int p0){ return null; } + public String getResourcePackageName(int p0){ return null; } + public String getResourceTypeName(int p0){ return null; } + public String getString(int p0){ return null; } + public String getString(int p0, Object... p1){ return null; } + public String[] getStringArray(int p0){ return null; } + public TypedArray obtainAttributes(AttributeSet p0, int[] p1){ return null; } + public TypedArray obtainTypedArray(int p0){ return null; } + public Typeface getFont(int p0){ return null; } + public XmlResourceParser getAnimation(int p0){ return null; } + public XmlResourceParser getLayout(int p0){ return null; } + public XmlResourceParser getXml(int p0){ return null; } + public boolean getBoolean(int p0){ return false; } + public class Theme + { + public Drawable getDrawable(int p0){ return null; } + public Resources getResources(){ return null; } + public TypedArray obtainStyledAttributes(AttributeSet p0, int[] p1, int p2, int p3){ return null; } + public TypedArray obtainStyledAttributes(int p0, int[] p1){ return null; } + public TypedArray obtainStyledAttributes(int[] p0){ return null; } + public boolean resolveAttribute(int p0, TypedValue p1, boolean p2){ return false; } + public int getChangingConfigurations(){ return 0; } + public int getExplicitStyle(AttributeSet p0){ return 0; } + public int[] getAttributeResolutionStack(int p0, int p1, int p2){ return null; } + public void applyStyle(int p0, boolean p1){} + public void dump(int p0, String p1, String p2){} + public void rebase(){} + public void setTo(Resources.Theme p0){} + } + public final AssetManager getAssets(){ return null; } + public final Resources.Theme newTheme(){ return null; } + public final void finishPreloading(){} + public final void flushLayoutCache(){} + public float getDimension(int p0){ return 0; } + public float getFloat(int p0){ return 0; } + public float getFraction(int p0, int p1, int p2){ return 0; } + public int getColor(int p0){ return 0; } + public int getColor(int p0, Resources.Theme p1){ return 0; } + public int getDimensionPixelOffset(int p0){ return 0; } + public int getDimensionPixelSize(int p0){ return 0; } + public int getIdentifier(String p0, String p1, String p2){ return 0; } + public int getInteger(int p0){ return 0; } + public int[] getIntArray(int p0){ return null; } + public static Resources getSystem(){ return null; } + public static int ID_NULL = 0; + public static int getAttributeSetSourceResId(AttributeSet p0){ return 0; } + public void getValue(String p0, TypedValue p1, boolean p2){} + public void getValue(int p0, TypedValue p1, boolean p2){} + public void getValueForDensity(int p0, int p1, TypedValue p2, boolean p3){} + public void parseBundleExtra(String p0, AttributeSet p1, Bundle p2){} + public void parseBundleExtras(XmlResourceParser p0, Bundle p1){} + public void updateConfiguration(Configuration p0, DisplayMetrics p1){} + static public class NotFoundException extends RuntimeException + { + public NotFoundException(){} + public NotFoundException(String p0){} + public NotFoundException(String p0, Exception p1){} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/res/TypedArray.java b/java/ql/test/stubs/google-android-9.0.0/android/content/res/TypedArray.java new file mode 100644 index 00000000000..3578fd23292 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/res/TypedArray.java @@ -0,0 +1,47 @@ +// Generated automatically from android.content.res.TypedArray for testing purposes + +package android.content.res; + +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.util.TypedValue; + +public class TypedArray +{ + protected TypedArray() {} + public CharSequence getText(int p0){ return null; } + public CharSequence[] getTextArray(int p0){ return null; } + public ColorStateList getColorStateList(int p0){ return null; } + public Drawable getDrawable(int p0){ return null; } + public Resources getResources(){ return null; } + public String getNonResourceString(int p0){ return null; } + public String getPositionDescription(){ return null; } + public String getString(int p0){ return null; } + public String toString(){ return null; } + public TypedValue peekValue(int p0){ return null; } + public Typeface getFont(int p0){ return null; } + public boolean getBoolean(int p0, boolean p1){ return false; } + public boolean getValue(int p0, TypedValue p1){ return false; } + public boolean hasValue(int p0){ return false; } + public boolean hasValueOrEmpty(int p0){ return false; } + public float getDimension(int p0, float p1){ return 0; } + public float getFloat(int p0, float p1){ return 0; } + public float getFraction(int p0, int p1, int p2, float p3){ return 0; } + public int getChangingConfigurations(){ return 0; } + public int getColor(int p0, int p1){ return 0; } + public int getDimensionPixelOffset(int p0, int p1){ return 0; } + public int getDimensionPixelSize(int p0, int p1){ return 0; } + public int getIndex(int p0){ return 0; } + public int getIndexCount(){ return 0; } + public int getInt(int p0, int p1){ return 0; } + public int getInteger(int p0, int p1){ return 0; } + public int getLayoutDimension(int p0, String p1){ return 0; } + public int getLayoutDimension(int p0, int p1){ return 0; } + public int getResourceId(int p0, int p1){ return 0; } + public int getSourceResourceId(int p0, int p1){ return 0; } + public int getType(int p0){ return 0; } + public int length(){ return 0; } + public void recycle(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/res/XmlResourceParser.java b/java/ql/test/stubs/google-android-9.0.0/android/content/res/XmlResourceParser.java new file mode 100644 index 00000000000..61a50fe8bec --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/res/XmlResourceParser.java @@ -0,0 +1,12 @@ +// Generated automatically from android.content.res.XmlResourceParser for testing purposes + +package android.content.res; + +import android.util.AttributeSet; +import org.xmlpull.v1.XmlPullParser; + +public interface XmlResourceParser extends AttributeSet, AutoCloseable, XmlPullParser +{ + String getAttributeNamespace(int p0); + void close(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/database/CharArrayBuffer.java b/java/ql/test/stubs/google-android-9.0.0/android/database/CharArrayBuffer.java new file mode 100644 index 00000000000..b75eff88239 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/database/CharArrayBuffer.java @@ -0,0 +1,13 @@ +// Generated automatically from android.database.CharArrayBuffer for testing purposes + +package android.database; + + +public class CharArrayBuffer +{ + protected CharArrayBuffer() {} + public CharArrayBuffer(char[] p0){} + public CharArrayBuffer(int p0){} + public char[] data = null; + public int sizeCopied = 0; +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/database/ContentObserver.java b/java/ql/test/stubs/google-android-9.0.0/android/database/ContentObserver.java new file mode 100644 index 00000000000..4ba6000e93d --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/database/ContentObserver.java @@ -0,0 +1,17 @@ +// Generated automatically from android.database.ContentObserver for testing purposes + +package android.database; + +import android.net.Uri; +import android.os.Handler; + +abstract public class ContentObserver +{ + protected ContentObserver() {} + public ContentObserver(Handler p0){} + public boolean deliverSelfNotifications(){ return false; } + public final void dispatchChange(boolean p0){} + public final void dispatchChange(boolean p0, Uri p1){} + public void onChange(boolean p0){} + public void onChange(boolean p0, Uri p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/database/DataSetObserver.java b/java/ql/test/stubs/google-android-9.0.0/android/database/DataSetObserver.java new file mode 100644 index 00000000000..6ca449b2e95 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/database/DataSetObserver.java @@ -0,0 +1,11 @@ +// Generated automatically from android.database.DataSetObserver for testing purposes + +package android.database; + + +abstract public class DataSetObserver +{ + public DataSetObserver(){} + public void onChanged(){} + public void onInvalidated(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/database/DatabaseErrorHandler.java b/java/ql/test/stubs/google-android-9.0.0/android/database/DatabaseErrorHandler.java new file mode 100644 index 00000000000..c52eb9698a7 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/database/DatabaseErrorHandler.java @@ -0,0 +1,10 @@ +// Generated automatically from android.database.DatabaseErrorHandler for testing purposes + +package android.database; + +import android.database.sqlite.SQLiteDatabase; + +public interface DatabaseErrorHandler +{ + void onCorruption(SQLiteDatabase p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteClosable.java b/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteClosable.java new file mode 100644 index 00000000000..8295e8d0d61 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteClosable.java @@ -0,0 +1,16 @@ +// Generated automatically from android.database.sqlite.SQLiteClosable for testing purposes + +package android.database.sqlite; + +import java.io.Closeable; + +abstract public class SQLiteClosable implements Closeable +{ + protected abstract void onAllReferencesReleased(); + protected void onAllReferencesReleasedFromContainer(){} + public SQLiteClosable(){} + public void acquireReference(){} + public void close(){} + public void releaseReference(){} + public void releaseReferenceFromContainer(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteCursorDriver.java b/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteCursorDriver.java new file mode 100644 index 00000000000..3d5cb99a2d0 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteCursorDriver.java @@ -0,0 +1,15 @@ +// Generated automatically from android.database.sqlite.SQLiteCursorDriver for testing purposes + +package android.database.sqlite; + +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +public interface SQLiteCursorDriver +{ + Cursor query(SQLiteDatabase.CursorFactory p0, String[] p1); + void cursorClosed(); + void cursorDeactivated(); + void cursorRequeried(Cursor p0); + void setBindArguments(String[] p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteDatabase.java b/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteDatabase.java new file mode 100644 index 00000000000..5e6895f0c53 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteDatabase.java @@ -0,0 +1,122 @@ +// Generated automatically from android.database.sqlite.SQLiteDatabase for testing purposes + +package android.database.sqlite; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.DatabaseErrorHandler; +import android.database.sqlite.SQLiteClosable; +import android.database.sqlite.SQLiteCursorDriver; +import android.database.sqlite.SQLiteQuery; +import android.database.sqlite.SQLiteStatement; +import android.database.sqlite.SQLiteTransactionListener; +import android.os.CancellationSignal; +import android.util.Pair; +import java.io.File; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +public class SQLiteDatabase extends SQLiteClosable +{ + protected SQLiteDatabase() {} + protected void finalize(){} + protected void onAllReferencesReleased(){} + public Cursor query(String p0, String[] p1, String p2, String[] p3, String p4, String p5, String p6){ return null; } + public Cursor query(String p0, String[] p1, String p2, String[] p3, String p4, String p5, String p6, String p7){ return null; } + public Cursor query(boolean p0, String p1, String[] p2, String p3, String[] p4, String p5, String p6, String p7, String p8){ return null; } + public Cursor query(boolean p0, String p1, String[] p2, String p3, String[] p4, String p5, String p6, String p7, String p8, CancellationSignal p9){ return null; } + public Cursor queryWithFactory(SQLiteDatabase.CursorFactory p0, boolean p1, String p2, String[] p3, String p4, String[] p5, String p6, String p7, String p8, String p9){ return null; } + public Cursor queryWithFactory(SQLiteDatabase.CursorFactory p0, boolean p1, String p2, String[] p3, String p4, String[] p5, String p6, String p7, String p8, String p9, CancellationSignal p10){ return null; } + public Cursor rawQuery(String p0, String[] p1){ return null; } + public Cursor rawQuery(String p0, String[] p1, CancellationSignal p2){ return null; } + public Cursor rawQueryWithFactory(SQLiteDatabase.CursorFactory p0, String p1, String[] p2, String p3){ return null; } + public Cursor rawQueryWithFactory(SQLiteDatabase.CursorFactory p0, String p1, String[] p2, String p3, CancellationSignal p4){ return null; } + public List> getAttachedDbs(){ return null; } + public Map getSyncedTables(){ return null; } + public SQLiteStatement compileStatement(String p0){ return null; } + public String getPath(){ return null; } + public String toString(){ return null; } + public boolean enableWriteAheadLogging(){ return false; } + public boolean inTransaction(){ return false; } + public boolean isDatabaseIntegrityOk(){ return false; } + public boolean isDbLockedByCurrentThread(){ return false; } + public boolean isDbLockedByOtherThreads(){ return false; } + public boolean isOpen(){ return false; } + public boolean isReadOnly(){ return false; } + public boolean isWriteAheadLoggingEnabled(){ return false; } + public boolean needUpgrade(int p0){ return false; } + public boolean yieldIfContended(){ return false; } + public boolean yieldIfContendedSafely(){ return false; } + public boolean yieldIfContendedSafely(long p0){ return false; } + public int delete(String p0, String p1, String[] p2){ return 0; } + public int getVersion(){ return 0; } + public int update(String p0, ContentValues p1, String p2, String[] p3){ return 0; } + public int updateWithOnConflict(String p0, ContentValues p1, String p2, String[] p3, int p4){ return 0; } + public long getMaximumSize(){ return 0; } + public long getPageSize(){ return 0; } + public long insert(String p0, String p1, ContentValues p2){ return 0; } + public long insertOrThrow(String p0, String p1, ContentValues p2){ return 0; } + public long insertWithOnConflict(String p0, String p1, ContentValues p2, int p3){ return 0; } + public long replace(String p0, String p1, ContentValues p2){ return 0; } + public long replaceOrThrow(String p0, String p1, ContentValues p2){ return 0; } + public long setMaximumSize(long p0){ return 0; } + public static SQLiteDatabase create(SQLiteDatabase.CursorFactory p0){ return null; } + public static SQLiteDatabase createInMemory(SQLiteDatabase.OpenParams p0){ return null; } + public static SQLiteDatabase openDatabase(File p0, SQLiteDatabase.OpenParams p1){ return null; } + public static SQLiteDatabase openDatabase(String p0, SQLiteDatabase.CursorFactory p1, int p2){ return null; } + public static SQLiteDatabase openDatabase(String p0, SQLiteDatabase.CursorFactory p1, int p2, DatabaseErrorHandler p3){ return null; } + public static SQLiteDatabase openOrCreateDatabase(File p0, SQLiteDatabase.CursorFactory p1){ return null; } + public static SQLiteDatabase openOrCreateDatabase(String p0, SQLiteDatabase.CursorFactory p1){ return null; } + public static SQLiteDatabase openOrCreateDatabase(String p0, SQLiteDatabase.CursorFactory p1, DatabaseErrorHandler p2){ return null; } + public static String findEditTable(String p0){ return null; } + public static boolean deleteDatabase(File p0){ return false; } + public static int CONFLICT_ABORT = 0; + public static int CONFLICT_FAIL = 0; + public static int CONFLICT_IGNORE = 0; + public static int CONFLICT_NONE = 0; + public static int CONFLICT_REPLACE = 0; + public static int CONFLICT_ROLLBACK = 0; + public static int CREATE_IF_NECESSARY = 0; + public static int ENABLE_WRITE_AHEAD_LOGGING = 0; + public static int MAX_SQL_CACHE_SIZE = 0; + public static int NO_LOCALIZED_COLLATORS = 0; + public static int OPEN_READONLY = 0; + public static int OPEN_READWRITE = 0; + public static int SQLITE_MAX_LIKE_PATTERN_LENGTH = 0; + public static int releaseMemory(){ return 0; } + public void beginTransaction(){} + public void beginTransactionNonExclusive(){} + public void beginTransactionWithListener(SQLiteTransactionListener p0){} + public void beginTransactionWithListenerNonExclusive(SQLiteTransactionListener p0){} + public void disableWriteAheadLogging(){} + public void endTransaction(){} + public void execSQL(String p0){} + public void execSQL(String p0, Object[] p1){} + public void markTableSyncable(String p0, String p1){} + public void markTableSyncable(String p0, String p1, String p2){} + public void setForeignKeyConstraintsEnabled(boolean p0){} + public void setLocale(Locale p0){} + public void setLockingEnabled(boolean p0){} + public void setMaxSqlCacheSize(int p0){} + public void setPageSize(long p0){} + public void setTransactionSuccessful(){} + public void setVersion(int p0){} + public void validateSql(String p0, CancellationSignal p1){} + static public class OpenParams + { + protected OpenParams() {} + public DatabaseErrorHandler getErrorHandler(){ return null; } + public SQLiteDatabase.CursorFactory getCursorFactory(){ return null; } + public String getJournalMode(){ return null; } + public String getSynchronousMode(){ return null; } + public int getLookasideSlotCount(){ return 0; } + public int getLookasideSlotSize(){ return 0; } + public int getOpenFlags(){ return 0; } + public long getIdleConnectionTimeout(){ return 0; } + } + static public interface CursorFactory + { + Cursor newCursor(SQLiteDatabase p0, SQLiteCursorDriver p1, String p2, SQLiteQuery p3); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteProgram.java b/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteProgram.java new file mode 100644 index 00000000000..ca78c8d99f6 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteProgram.java @@ -0,0 +1,19 @@ +// Generated automatically from android.database.sqlite.SQLiteProgram for testing purposes + +package android.database.sqlite; + +import android.database.sqlite.SQLiteClosable; + +abstract public class SQLiteProgram extends SQLiteClosable +{ + protected SQLiteProgram() {} + protected void onAllReferencesReleased(){} + public final int getUniqueId(){ return 0; } + public void bindAllArgsAsStrings(String[] p0){} + public void bindBlob(int p0, byte[] p1){} + public void bindDouble(int p0, double p1){} + public void bindLong(int p0, long p1){} + public void bindNull(int p0){} + public void bindString(int p0, String p1){} + public void clearBindings(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteQuery.java b/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteQuery.java new file mode 100644 index 00000000000..80ff0c84998 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteQuery.java @@ -0,0 +1,11 @@ +// Generated automatically from android.database.sqlite.SQLiteQuery for testing purposes + +package android.database.sqlite; + +import android.database.sqlite.SQLiteProgram; + +public class SQLiteQuery extends SQLiteProgram +{ + protected SQLiteQuery() {} + public String toString(){ return null; } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteStatement.java b/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteStatement.java new file mode 100644 index 00000000000..d7b96baf0f3 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteStatement.java @@ -0,0 +1,18 @@ +// Generated automatically from android.database.sqlite.SQLiteStatement for testing purposes + +package android.database.sqlite; + +import android.database.sqlite.SQLiteProgram; +import android.os.ParcelFileDescriptor; + +public class SQLiteStatement extends SQLiteProgram +{ + protected SQLiteStatement() {} + public ParcelFileDescriptor simpleQueryForBlobFileDescriptor(){ return null; } + public String simpleQueryForString(){ return null; } + public String toString(){ return null; } + public int executeUpdateDelete(){ return 0; } + public long executeInsert(){ return 0; } + public long simpleQueryForLong(){ return 0; } + public void execute(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteTransactionListener.java b/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteTransactionListener.java new file mode 100644 index 00000000000..31895691b11 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/database/sqlite/SQLiteTransactionListener.java @@ -0,0 +1,11 @@ +// Generated automatically from android.database.sqlite.SQLiteTransactionListener for testing purposes + +package android.database.sqlite; + + +public interface SQLiteTransactionListener +{ + void onBegin(); + void onCommit(); + void onRollback(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/Bitmap.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Bitmap.java new file mode 100644 index 00000000000..991895a6ed3 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Bitmap.java @@ -0,0 +1,97 @@ +// Generated automatically from android.graphics.Bitmap for testing purposes + +package android.graphics; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorSpace; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Picture; +import android.hardware.HardwareBuffer; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.DisplayMetrics; +import java.io.OutputStream; +import java.nio.Buffer; + +public class Bitmap implements Parcelable +{ + public Bitmap copy(Bitmap.Config p0, boolean p1){ return null; } + public Bitmap extractAlpha(){ return null; } + public Bitmap extractAlpha(Paint p0, int[] p1){ return null; } + public Bitmap.Config getConfig(){ return null; } + public Color getColor(int p0, int p1){ return null; } + public ColorSpace getColorSpace(){ return null; } + public boolean compress(Bitmap.CompressFormat p0, int p1, OutputStream p2){ return false; } + public boolean hasAlpha(){ return false; } + public boolean hasMipMap(){ return false; } + public boolean isMutable(){ return false; } + public boolean isPremultiplied(){ return false; } + public boolean isRecycled(){ return false; } + public boolean sameAs(Bitmap p0){ return false; } + public byte[] getNinePatchChunk(){ return null; } + public int describeContents(){ return 0; } + public int getAllocationByteCount(){ return 0; } + public int getByteCount(){ return 0; } + public int getDensity(){ return 0; } + public int getGenerationId(){ return 0; } + public int getHeight(){ return 0; } + public int getPixel(int p0, int p1){ return 0; } + public int getRowBytes(){ return 0; } + public int getScaledHeight(Canvas p0){ return 0; } + public int getScaledHeight(DisplayMetrics p0){ return 0; } + public int getScaledHeight(int p0){ return 0; } + public int getScaledWidth(Canvas p0){ return 0; } + public int getScaledWidth(DisplayMetrics p0){ return 0; } + public int getScaledWidth(int p0){ return 0; } + public int getWidth(){ return 0; } + public static Bitmap createBitmap(Bitmap p0){ return null; } + public static Bitmap createBitmap(Bitmap p0, int p1, int p2, int p3, int p4){ return null; } + public static Bitmap createBitmap(Bitmap p0, int p1, int p2, int p3, int p4, Matrix p5, boolean p6){ return null; } + public static Bitmap createBitmap(DisplayMetrics p0, int p1, int p2, Bitmap.Config p3){ return null; } + public static Bitmap createBitmap(DisplayMetrics p0, int p1, int p2, Bitmap.Config p3, boolean p4){ return null; } + public static Bitmap createBitmap(DisplayMetrics p0, int p1, int p2, Bitmap.Config p3, boolean p4, ColorSpace p5){ return null; } + public static Bitmap createBitmap(DisplayMetrics p0, int[] p1, int p2, int p3, Bitmap.Config p4){ return null; } + public static Bitmap createBitmap(DisplayMetrics p0, int[] p1, int p2, int p3, int p4, int p5, Bitmap.Config p6){ return null; } + public static Bitmap createBitmap(Picture p0){ return null; } + public static Bitmap createBitmap(Picture p0, int p1, int p2, Bitmap.Config p3){ return null; } + public static Bitmap createBitmap(int p0, int p1, Bitmap.Config p2){ return null; } + public static Bitmap createBitmap(int p0, int p1, Bitmap.Config p2, boolean p3){ return null; } + public static Bitmap createBitmap(int p0, int p1, Bitmap.Config p2, boolean p3, ColorSpace p4){ return null; } + public static Bitmap createBitmap(int[] p0, int p1, int p2, Bitmap.Config p3){ return null; } + public static Bitmap createBitmap(int[] p0, int p1, int p2, int p3, int p4, Bitmap.Config p5){ return null; } + public static Bitmap createScaledBitmap(Bitmap p0, int p1, int p2, boolean p3){ return null; } + public static Bitmap wrapHardwareBuffer(HardwareBuffer p0, ColorSpace p1){ return null; } + public static Parcelable.Creator CREATOR = null; + public static int DENSITY_NONE = 0; + public void copyPixelsFromBuffer(Buffer p0){} + public void copyPixelsToBuffer(Buffer p0){} + public void eraseColor(int p0){} + public void eraseColor(long p0){} + public void getPixels(int[] p0, int p1, int p2, int p3, int p4, int p5, int p6){} + public void prepareToDraw(){} + public void reconfigure(int p0, int p1, Bitmap.Config p2){} + public void recycle(){} + public void setColorSpace(ColorSpace p0){} + public void setConfig(Bitmap.Config p0){} + public void setDensity(int p0){} + public void setHasAlpha(boolean p0){} + public void setHasMipMap(boolean p0){} + public void setHeight(int p0){} + public void setPixel(int p0, int p1, int p2){} + public void setPixels(int[] p0, int p1, int p2, int p3, int p4, int p5, int p6){} + public void setPremultiplied(boolean p0){} + public void setWidth(int p0){} + public void writeToParcel(Parcel p0, int p1){} + static public enum CompressFormat + { + JPEG, PNG, WEBP; + private CompressFormat() {} + } + static public enum Config + { + ALPHA_8, ARGB_4444, ARGB_8888, HARDWARE, RGBA_F16, RGB_565; + private Config() {} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/BitmapFactory.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/BitmapFactory.java new file mode 100644 index 00000000000..f6e05c10de9 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/BitmapFactory.java @@ -0,0 +1,54 @@ +// Generated automatically from android.graphics.BitmapFactory for testing purposes + +package android.graphics; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.ColorSpace; +import android.graphics.Rect; +import android.util.TypedValue; +import java.io.FileDescriptor; +import java.io.InputStream; + +public class BitmapFactory +{ + public BitmapFactory(){} + public static Bitmap decodeByteArray(byte[] p0, int p1, int p2){ return null; } + public static Bitmap decodeByteArray(byte[] p0, int p1, int p2, BitmapFactory.Options p3){ return null; } + public static Bitmap decodeFile(String p0){ return null; } + public static Bitmap decodeFile(String p0, BitmapFactory.Options p1){ return null; } + public static Bitmap decodeFileDescriptor(FileDescriptor p0){ return null; } + public static Bitmap decodeFileDescriptor(FileDescriptor p0, Rect p1, BitmapFactory.Options p2){ return null; } + public static Bitmap decodeResource(Resources p0, int p1){ return null; } + public static Bitmap decodeResource(Resources p0, int p1, BitmapFactory.Options p2){ return null; } + public static Bitmap decodeResourceStream(Resources p0, TypedValue p1, InputStream p2, Rect p3, BitmapFactory.Options p4){ return null; } + public static Bitmap decodeStream(InputStream p0){ return null; } + public static Bitmap decodeStream(InputStream p0, Rect p1, BitmapFactory.Options p2){ return null; } + static public class Options + { + public Bitmap inBitmap = null; + public Bitmap.Config inPreferredConfig = null; + public Bitmap.Config outConfig = null; + public ColorSpace inPreferredColorSpace = null; + public ColorSpace outColorSpace = null; + public Options(){} + public String outMimeType = null; + public boolean inDither = false; + public boolean inInputShareable = false; + public boolean inJustDecodeBounds = false; + public boolean inMutable = false; + public boolean inPreferQualityOverSpeed = false; + public boolean inPremultiplied = false; + public boolean inPurgeable = false; + public boolean inScaled = false; + public boolean mCancel = false; + public byte[] inTempStorage = null; + public int inDensity = 0; + public int inSampleSize = 0; + public int inScreenDensity = 0; + public int inTargetDensity = 0; + public int outHeight = 0; + public int outWidth = 0; + public void requestCancelDecode(){} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/BlendMode.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/BlendMode.java new file mode 100644 index 00000000000..32510e36bdd --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/BlendMode.java @@ -0,0 +1,10 @@ +// Generated automatically from android.graphics.BlendMode for testing purposes + +package android.graphics; + + +public enum BlendMode +{ + CLEAR, COLOR, COLOR_BURN, COLOR_DODGE, DARKEN, DIFFERENCE, DST, DST_ATOP, DST_IN, DST_OUT, DST_OVER, EXCLUSION, HARD_LIGHT, HUE, LIGHTEN, LUMINOSITY, MODULATE, MULTIPLY, OVERLAY, PLUS, SATURATION, SCREEN, SOFT_LIGHT, SRC, SRC_ATOP, SRC_IN, SRC_OUT, SRC_OVER, XOR; + private BlendMode() {} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/Canvas.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Canvas.java new file mode 100644 index 00000000000..128cf70d32d --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Canvas.java @@ -0,0 +1,138 @@ +// Generated automatically from android.graphics.Canvas for testing purposes + +package android.graphics; + +import android.graphics.Bitmap; +import android.graphics.BlendMode; +import android.graphics.DrawFilter; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Picture; +import android.graphics.PorterDuff; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Region; +import android.graphics.RenderNode; +import android.graphics.text.MeasuredText; + +public class Canvas +{ + public Canvas(){} + public Canvas(Bitmap p0){} + public DrawFilter getDrawFilter(){ return null; } + public boolean clipOutPath(Path p0){ return false; } + public boolean clipOutRect(Rect p0){ return false; } + public boolean clipOutRect(RectF p0){ return false; } + public boolean clipOutRect(float p0, float p1, float p2, float p3){ return false; } + public boolean clipOutRect(int p0, int p1, int p2, int p3){ return false; } + public boolean clipPath(Path p0){ return false; } + public boolean clipPath(Path p0, Region.Op p1){ return false; } + public boolean clipRect(Rect p0){ return false; } + public boolean clipRect(Rect p0, Region.Op p1){ return false; } + public boolean clipRect(RectF p0){ return false; } + public boolean clipRect(RectF p0, Region.Op p1){ return false; } + public boolean clipRect(float p0, float p1, float p2, float p3){ return false; } + public boolean clipRect(float p0, float p1, float p2, float p3, Region.Op p4){ return false; } + public boolean clipRect(int p0, int p1, int p2, int p3){ return false; } + public boolean getClipBounds(Rect p0){ return false; } + public boolean isHardwareAccelerated(){ return false; } + public boolean isOpaque(){ return false; } + public boolean quickReject(Path p0, Canvas.EdgeType p1){ return false; } + public boolean quickReject(RectF p0, Canvas.EdgeType p1){ return false; } + public boolean quickReject(float p0, float p1, float p2, float p3, Canvas.EdgeType p4){ return false; } + public final Matrix getMatrix(){ return null; } + public final Rect getClipBounds(){ return null; } + public final void rotate(float p0, float p1, float p2){} + public final void scale(float p0, float p1, float p2, float p3){} + public int getDensity(){ return 0; } + public int getHeight(){ return 0; } + public int getMaximumBitmapHeight(){ return 0; } + public int getMaximumBitmapWidth(){ return 0; } + public int getSaveCount(){ return 0; } + public int getWidth(){ return 0; } + public int save(){ return 0; } + public int saveLayer(RectF p0, Paint p1){ return 0; } + public int saveLayer(RectF p0, Paint p1, int p2){ return 0; } + public int saveLayer(float p0, float p1, float p2, float p3, Paint p4){ return 0; } + public int saveLayer(float p0, float p1, float p2, float p3, Paint p4, int p5){ return 0; } + public int saveLayerAlpha(RectF p0, int p1){ return 0; } + public int saveLayerAlpha(RectF p0, int p1, int p2){ return 0; } + public int saveLayerAlpha(float p0, float p1, float p2, float p3, int p4){ return 0; } + public int saveLayerAlpha(float p0, float p1, float p2, float p3, int p4, int p5){ return 0; } + public static int ALL_SAVE_FLAG = 0; + public void concat(Matrix p0){} + public void disableZ(){} + public void drawARGB(int p0, int p1, int p2, int p3){} + public void drawArc(RectF p0, float p1, float p2, boolean p3, Paint p4){} + public void drawArc(float p0, float p1, float p2, float p3, float p4, float p5, boolean p6, Paint p7){} + public void drawBitmap(Bitmap p0, Matrix p1, Paint p2){} + public void drawBitmap(Bitmap p0, Rect p1, Rect p2, Paint p3){} + public void drawBitmap(Bitmap p0, Rect p1, RectF p2, Paint p3){} + public void drawBitmap(Bitmap p0, float p1, float p2, Paint p3){} + public void drawBitmap(int[] p0, int p1, int p2, float p3, float p4, int p5, int p6, boolean p7, Paint p8){} + public void drawBitmap(int[] p0, int p1, int p2, int p3, int p4, int p5, int p6, boolean p7, Paint p8){} + public void drawBitmapMesh(Bitmap p0, int p1, int p2, float[] p3, int p4, int[] p5, int p6, Paint p7){} + public void drawCircle(float p0, float p1, float p2, Paint p3){} + public void drawColor(int p0){} + public void drawColor(int p0, BlendMode p1){} + public void drawColor(int p0, PorterDuff.Mode p1){} + public void drawColor(long p0){} + public void drawColor(long p0, BlendMode p1){} + public void drawDoubleRoundRect(RectF p0, float p1, float p2, RectF p3, float p4, float p5, Paint p6){} + public void drawDoubleRoundRect(RectF p0, float[] p1, RectF p2, float[] p3, Paint p4){} + public void drawLine(float p0, float p1, float p2, float p3, Paint p4){} + public void drawLines(float[] p0, Paint p1){} + public void drawLines(float[] p0, int p1, int p2, Paint p3){} + public void drawOval(RectF p0, Paint p1){} + public void drawOval(float p0, float p1, float p2, float p3, Paint p4){} + public void drawPaint(Paint p0){} + public void drawPath(Path p0, Paint p1){} + public void drawPicture(Picture p0){} + public void drawPicture(Picture p0, Rect p1){} + public void drawPicture(Picture p0, RectF p1){} + public void drawPoint(float p0, float p1, Paint p2){} + public void drawPoints(float[] p0, Paint p1){} + public void drawPoints(float[] p0, int p1, int p2, Paint p3){} + public void drawPosText(String p0, float[] p1, Paint p2){} + public void drawPosText(char[] p0, int p1, int p2, float[] p3, Paint p4){} + public void drawRGB(int p0, int p1, int p2){} + public void drawRect(Rect p0, Paint p1){} + public void drawRect(RectF p0, Paint p1){} + public void drawRect(float p0, float p1, float p2, float p3, Paint p4){} + public void drawRenderNode(RenderNode p0){} + public void drawRoundRect(RectF p0, float p1, float p2, Paint p3){} + public void drawRoundRect(float p0, float p1, float p2, float p3, float p4, float p5, Paint p6){} + public void drawText(CharSequence p0, int p1, int p2, float p3, float p4, Paint p5){} + public void drawText(String p0, float p1, float p2, Paint p3){} + public void drawText(String p0, int p1, int p2, float p3, float p4, Paint p5){} + public void drawText(char[] p0, int p1, int p2, float p3, float p4, Paint p5){} + public void drawTextOnPath(String p0, Path p1, float p2, float p3, Paint p4){} + public void drawTextOnPath(char[] p0, int p1, int p2, Path p3, float p4, float p5, Paint p6){} + public void drawTextRun(CharSequence p0, int p1, int p2, int p3, int p4, float p5, float p6, boolean p7, Paint p8){} + public void drawTextRun(MeasuredText p0, int p1, int p2, int p3, int p4, float p5, float p6, boolean p7, Paint p8){} + public void drawTextRun(char[] p0, int p1, int p2, int p3, int p4, float p5, float p6, boolean p7, Paint p8){} + public void drawVertices(Canvas.VertexMode p0, int p1, float[] p2, int p3, float[] p4, int p5, int[] p6, int p7, short[] p8, int p9, int p10, Paint p11){} + public void enableZ(){} + public void getMatrix(Matrix p0){} + public void restore(){} + public void restoreToCount(int p0){} + public void rotate(float p0){} + public void scale(float p0, float p1){} + public void setBitmap(Bitmap p0){} + public void setDensity(int p0){} + public void setDrawFilter(DrawFilter p0){} + public void setMatrix(Matrix p0){} + public void skew(float p0, float p1){} + public void translate(float p0, float p1){} + static public enum EdgeType + { + AA, BW; + private EdgeType() {} + } + static public enum VertexMode + { + TRIANGLES, TRIANGLE_FAN, TRIANGLE_STRIP; + private VertexMode() {} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/Color.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Color.java new file mode 100644 index 00000000000..45f1e68548c --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Color.java @@ -0,0 +1,80 @@ +// Generated automatically from android.graphics.Color for testing purposes + +package android.graphics; + +import android.graphics.ColorSpace; + +public class Color +{ + public Color convert(ColorSpace p0){ return null; } + public Color(){} + public ColorSpace getColorSpace(){ return null; } + public ColorSpace.Model getModel(){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public boolean isSrgb(){ return false; } + public boolean isWideGamut(){ return false; } + public float alpha(){ return 0; } + public float blue(){ return 0; } + public float getComponent(int p0){ return 0; } + public float green(){ return 0; } + public float luminance(){ return 0; } + public float red(){ return 0; } + public float[] getComponents(){ return null; } + public float[] getComponents(float[] p0){ return null; } + public int getComponentCount(){ return 0; } + public int hashCode(){ return 0; } + public int toArgb(){ return 0; } + public long pack(){ return 0; } + public static Color valueOf(float p0, float p1, float p2){ return null; } + public static Color valueOf(float p0, float p1, float p2, float p3){ return null; } + public static Color valueOf(float p0, float p1, float p2, float p3, ColorSpace p4){ return null; } + public static Color valueOf(float[] p0, ColorSpace p1){ return null; } + public static Color valueOf(int p0){ return null; } + public static Color valueOf(long p0){ return null; } + public static ColorSpace colorSpace(long p0){ return null; } + public static boolean isInColorSpace(long p0, ColorSpace p1){ return false; } + public static boolean isSrgb(long p0){ return false; } + public static boolean isWideGamut(long p0){ return false; } + public static float alpha(long p0){ return 0; } + public static float blue(long p0){ return 0; } + public static float green(long p0){ return 0; } + public static float luminance(int p0){ return 0; } + public static float luminance(long p0){ return 0; } + public static float red(long p0){ return 0; } + public static int BLACK = 0; + public static int BLUE = 0; + public static int CYAN = 0; + public static int DKGRAY = 0; + public static int GRAY = 0; + public static int GREEN = 0; + public static int HSVToColor(float[] p0){ return 0; } + public static int HSVToColor(int p0, float[] p1){ return 0; } + public static int LTGRAY = 0; + public static int MAGENTA = 0; + public static int RED = 0; + public static int TRANSPARENT = 0; + public static int WHITE = 0; + public static int YELLOW = 0; + public static int alpha(int p0){ return 0; } + public static int argb(float p0, float p1, float p2, float p3){ return 0; } + public static int argb(int p0, int p1, int p2, int p3){ return 0; } + public static int blue(int p0){ return 0; } + public static int green(int p0){ return 0; } + public static int parseColor(String p0){ return 0; } + public static int red(int p0){ return 0; } + public static int rgb(float p0, float p1, float p2){ return 0; } + public static int rgb(int p0, int p1, int p2){ return 0; } + public static int toArgb(long p0){ return 0; } + public static long convert(float p0, float p1, float p2, float p3, ColorSpace p4, ColorSpace p5){ return 0; } + public static long convert(float p0, float p1, float p2, float p3, ColorSpace.Connector p4){ return 0; } + public static long convert(int p0, ColorSpace p1){ return 0; } + public static long convert(long p0, ColorSpace p1){ return 0; } + public static long convert(long p0, ColorSpace.Connector p1){ return 0; } + public static long pack(float p0, float p1, float p2){ return 0; } + public static long pack(float p0, float p1, float p2, float p3){ return 0; } + public static long pack(float p0, float p1, float p2, float p3, ColorSpace p4){ return 0; } + public static long pack(int p0){ return 0; } + public static void RGBToHSV(int p0, int p1, int p2, float[] p3){} + public static void colorToHSV(int p0, float[] p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/ColorFilter.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/ColorFilter.java new file mode 100644 index 00000000000..06a565743de --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/ColorFilter.java @@ -0,0 +1,9 @@ +// Generated automatically from android.graphics.ColorFilter for testing purposes + +package android.graphics; + + +public class ColorFilter +{ + public ColorFilter(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/ColorSpace.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/ColorSpace.java new file mode 100644 index 00000000000..06993b43be5 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/ColorSpace.java @@ -0,0 +1,122 @@ +// Generated automatically from android.graphics.ColorSpace for testing purposes + +package android.graphics; + +import java.util.function.DoubleUnaryOperator; + +abstract public class ColorSpace +{ + protected ColorSpace() {} + public ColorSpace.Model getModel(){ return null; } + public String getName(){ return null; } + public String toString(){ return null; } + public abstract boolean isWideGamut(); + public abstract float getMaxValue(int p0); + public abstract float getMinValue(int p0); + public abstract float[] fromXyz(float[] p0); + public abstract float[] toXyz(float[] p0); + public boolean equals(Object p0){ return false; } + public boolean isSrgb(){ return false; } + public float[] fromXyz(float p0, float p1, float p2){ return null; } + public float[] toXyz(float p0, float p1, float p2){ return null; } + public int getComponentCount(){ return 0; } + public int getId(){ return 0; } + public int hashCode(){ return 0; } + public static ColorSpace adapt(ColorSpace p0, float[] p1){ return null; } + public static ColorSpace adapt(ColorSpace p0, float[] p1, ColorSpace.Adaptation p2){ return null; } + public static ColorSpace get(ColorSpace.Named p0){ return null; } + public static ColorSpace match(float[] p0, ColorSpace.Rgb.TransferParameters p1){ return null; } + public static ColorSpace.Connector connect(ColorSpace p0){ return null; } + public static ColorSpace.Connector connect(ColorSpace p0, ColorSpace p1){ return null; } + public static ColorSpace.Connector connect(ColorSpace p0, ColorSpace p1, ColorSpace.RenderIntent p2){ return null; } + public static ColorSpace.Connector connect(ColorSpace p0, ColorSpace.RenderIntent p1){ return null; } + public static float[] ILLUMINANT_A = null; + public static float[] ILLUMINANT_B = null; + public static float[] ILLUMINANT_C = null; + public static float[] ILLUMINANT_D50 = null; + public static float[] ILLUMINANT_D55 = null; + public static float[] ILLUMINANT_D60 = null; + public static float[] ILLUMINANT_D65 = null; + public static float[] ILLUMINANT_D75 = null; + public static float[] ILLUMINANT_E = null; + public static int MAX_ID = 0; + public static int MIN_ID = 0; + static public class Connector + { + protected Connector() {} + public ColorSpace getDestination(){ return null; } + public ColorSpace getSource(){ return null; } + public ColorSpace.RenderIntent getRenderIntent(){ return null; } + public float[] transform(float p0, float p1, float p2){ return null; } + public float[] transform(float[] p0){ return null; } + } + static public class Rgb extends ColorSpace + { + protected Rgb() {} + public ColorSpace.Rgb.TransferParameters getTransferParameters(){ return null; } + public DoubleUnaryOperator getEotf(){ return null; } + public DoubleUnaryOperator getOetf(){ return null; } + public Rgb(String p0, float[] p1, ColorSpace.Rgb.TransferParameters p2){} + public Rgb(String p0, float[] p1, DoubleUnaryOperator p2, DoubleUnaryOperator p3){} + public Rgb(String p0, float[] p1, double p2){} + public Rgb(String p0, float[] p1, float[] p2, ColorSpace.Rgb.TransferParameters p3){} + public Rgb(String p0, float[] p1, float[] p2, DoubleUnaryOperator p3, DoubleUnaryOperator p4, float p5, float p6){} + public Rgb(String p0, float[] p1, float[] p2, double p3){} + public boolean equals(Object p0){ return false; } + public boolean isSrgb(){ return false; } + public boolean isWideGamut(){ return false; } + public float getMaxValue(int p0){ return 0; } + public float getMinValue(int p0){ return 0; } + public float[] fromLinear(float p0, float p1, float p2){ return null; } + public float[] fromLinear(float[] p0){ return null; } + public float[] fromXyz(float[] p0){ return null; } + public float[] getInverseTransform(){ return null; } + public float[] getInverseTransform(float[] p0){ return null; } + public float[] getPrimaries(){ return null; } + public float[] getPrimaries(float[] p0){ return null; } + public float[] getTransform(){ return null; } + public float[] getTransform(float[] p0){ return null; } + public float[] getWhitePoint(){ return null; } + public float[] getWhitePoint(float[] p0){ return null; } + public float[] toLinear(float p0, float p1, float p2){ return null; } + public float[] toLinear(float[] p0){ return null; } + public float[] toXyz(float[] p0){ return null; } + public int hashCode(){ return 0; } + static public class TransferParameters + { + protected TransferParameters() {} + public TransferParameters(double p0, double p1, double p2, double p3, double p4){} + public TransferParameters(double p0, double p1, double p2, double p3, double p4, double p5, double p6){} + public boolean equals(Object p0){ return false; } + public final double a = 0; + public final double b = 0; + public final double c = 0; + public final double d = 0; + public final double e = 0; + public final double f = 0; + public final double g = 0; + public int hashCode(){ return 0; } + } + } + static public enum Adaptation + { + BRADFORD, CIECAT02, VON_KRIES; + private Adaptation() {} + } + static public enum Model + { + CMYK, LAB, RGB, XYZ; + private Model() {} + public int getComponentCount(){ return 0; } + } + static public enum Named + { + ACES, ACESCG, ADOBE_RGB, BT2020, BT709, CIE_LAB, CIE_XYZ, DCI_P3, DISPLAY_P3, EXTENDED_SRGB, LINEAR_EXTENDED_SRGB, LINEAR_SRGB, NTSC_1953, PRO_PHOTO_RGB, SMPTE_C, SRGB; + private Named() {} + } + static public enum RenderIntent + { + ABSOLUTE, PERCEPTUAL, RELATIVE, SATURATION; + private RenderIntent() {} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/DrawFilter.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/DrawFilter.java new file mode 100644 index 00000000000..ed760270a81 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/DrawFilter.java @@ -0,0 +1,10 @@ +// Generated automatically from android.graphics.DrawFilter for testing purposes + +package android.graphics; + + +public class DrawFilter +{ + protected void finalize(){} + public DrawFilter(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/Insets.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Insets.java new file mode 100644 index 00000000000..3e61e03cd68 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Insets.java @@ -0,0 +1,29 @@ +// Generated automatically from android.graphics.Insets for testing purposes + +package android.graphics; + +import android.graphics.Rect; +import android.os.Parcel; +import android.os.Parcelable; + +public class Insets implements Parcelable +{ + protected Insets() {} + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public final int bottom = 0; + public final int left = 0; + public final int right = 0; + public final int top = 0; + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Insets NONE = null; + public static Insets add(Insets p0, Insets p1){ return null; } + public static Insets max(Insets p0, Insets p1){ return null; } + public static Insets min(Insets p0, Insets p1){ return null; } + public static Insets of(Rect p0){ return null; } + public static Insets of(int p0, int p1, int p2, int p3){ return null; } + public static Insets subtract(Insets p0, Insets p1){ return null; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/MaskFilter.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/MaskFilter.java new file mode 100644 index 00000000000..0355b5c1206 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/MaskFilter.java @@ -0,0 +1,10 @@ +// Generated automatically from android.graphics.MaskFilter for testing purposes + +package android.graphics; + + +public class MaskFilter +{ + protected void finalize(){} + public MaskFilter(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/Matrix.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Matrix.java new file mode 100644 index 00000000000..fc003bac004 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Matrix.java @@ -0,0 +1,74 @@ +// Generated automatically from android.graphics.Matrix for testing purposes + +package android.graphics; + +import android.graphics.RectF; + +public class Matrix +{ + public Matrix(){} + public Matrix(Matrix p0){} + public String toShortString(){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public boolean invert(Matrix p0){ return false; } + public boolean isAffine(){ return false; } + public boolean isIdentity(){ return false; } + public boolean mapRect(RectF p0){ return false; } + public boolean mapRect(RectF p0, RectF p1){ return false; } + public boolean postConcat(Matrix p0){ return false; } + public boolean postRotate(float p0){ return false; } + public boolean postRotate(float p0, float p1, float p2){ return false; } + public boolean postScale(float p0, float p1){ return false; } + public boolean postScale(float p0, float p1, float p2, float p3){ return false; } + public boolean postSkew(float p0, float p1){ return false; } + public boolean postSkew(float p0, float p1, float p2, float p3){ return false; } + public boolean postTranslate(float p0, float p1){ return false; } + public boolean preConcat(Matrix p0){ return false; } + public boolean preRotate(float p0){ return false; } + public boolean preRotate(float p0, float p1, float p2){ return false; } + public boolean preScale(float p0, float p1){ return false; } + public boolean preScale(float p0, float p1, float p2, float p3){ return false; } + public boolean preSkew(float p0, float p1){ return false; } + public boolean preSkew(float p0, float p1, float p2, float p3){ return false; } + public boolean preTranslate(float p0, float p1){ return false; } + public boolean rectStaysRect(){ return false; } + public boolean setConcat(Matrix p0, Matrix p1){ return false; } + public boolean setPolyToPoly(float[] p0, int p1, float[] p2, int p3, int p4){ return false; } + public boolean setRectToRect(RectF p0, RectF p1, Matrix.ScaleToFit p2){ return false; } + public float mapRadius(float p0){ return 0; } + public int hashCode(){ return 0; } + public static int MPERSP_0 = 0; + public static int MPERSP_1 = 0; + public static int MPERSP_2 = 0; + public static int MSCALE_X = 0; + public static int MSCALE_Y = 0; + public static int MSKEW_X = 0; + public static int MSKEW_Y = 0; + public static int MTRANS_X = 0; + public static int MTRANS_Y = 0; + public void getValues(float[] p0){} + public void mapPoints(float[] p0){} + public void mapPoints(float[] p0, float[] p1){} + public void mapPoints(float[] p0, int p1, float[] p2, int p3, int p4){} + public void mapVectors(float[] p0){} + public void mapVectors(float[] p0, float[] p1){} + public void mapVectors(float[] p0, int p1, float[] p2, int p3, int p4){} + public void reset(){} + public void set(Matrix p0){} + public void setRotate(float p0){} + public void setRotate(float p0, float p1, float p2){} + public void setScale(float p0, float p1){} + public void setScale(float p0, float p1, float p2, float p3){} + public void setSinCos(float p0, float p1){} + public void setSinCos(float p0, float p1, float p2, float p3){} + public void setSkew(float p0, float p1){} + public void setSkew(float p0, float p1, float p2, float p3){} + public void setTranslate(float p0, float p1){} + public void setValues(float[] p0){} + static public enum ScaleToFit + { + CENTER, END, FILL, START; + private ScaleToFit() {} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/Movie.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Movie.java new file mode 100644 index 00000000000..dd531ba7e04 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Movie.java @@ -0,0 +1,23 @@ +// Generated automatically from android.graphics.Movie for testing purposes + +package android.graphics; + +import android.graphics.Canvas; +import android.graphics.Paint; +import java.io.InputStream; + +public class Movie +{ + protected Movie() {} + protected void finalize(){} + public boolean isOpaque(){ return false; } + public boolean setTime(int p0){ return false; } + public int duration(){ return 0; } + public int height(){ return 0; } + public int width(){ return 0; } + public static Movie decodeByteArray(byte[] p0, int p1, int p2){ return null; } + public static Movie decodeFile(String p0){ return null; } + public static Movie decodeStream(InputStream p0){ return null; } + public void draw(Canvas p0, float p1, float p2){} + public void draw(Canvas p0, float p1, float p2, Paint p3){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/NinePatch.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/NinePatch.java new file mode 100644 index 00000000000..0fa7bc2eb69 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/NinePatch.java @@ -0,0 +1,31 @@ +// Generated automatically from android.graphics.NinePatch for testing purposes + +package android.graphics; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Region; + +public class NinePatch +{ + protected NinePatch() {} + protected void finalize(){} + public Bitmap getBitmap(){ return null; } + public NinePatch(Bitmap p0, byte[] p1){} + public NinePatch(Bitmap p0, byte[] p1, String p2){} + public Paint getPaint(){ return null; } + public String getName(){ return null; } + public final Region getTransparentRegion(Rect p0){ return null; } + public final boolean hasAlpha(){ return false; } + public int getDensity(){ return 0; } + public int getHeight(){ return 0; } + public int getWidth(){ return 0; } + public static boolean isNinePatchChunk(byte[] p0){ return false; } + public void draw(Canvas p0, Rect p1){} + public void draw(Canvas p0, Rect p1, Paint p2){} + public void draw(Canvas p0, RectF p1){} + public void setPaint(Paint p0){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/Outline.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Outline.java new file mode 100644 index 00000000000..806c40a9807 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Outline.java @@ -0,0 +1,28 @@ +// Generated automatically from android.graphics.Outline for testing purposes + +package android.graphics; + +import android.graphics.Path; +import android.graphics.Rect; + +public class Outline +{ + public Outline(){} + public Outline(Outline p0){} + public boolean canClip(){ return false; } + public boolean getRect(Rect p0){ return false; } + public boolean isEmpty(){ return false; } + public float getAlpha(){ return 0; } + public float getRadius(){ return 0; } + public void offset(int p0, int p1){} + public void set(Outline p0){} + public void setAlpha(float p0){} + public void setConvexPath(Path p0){} + public void setEmpty(){} + public void setOval(Rect p0){} + public void setOval(int p0, int p1, int p2, int p3){} + public void setRect(Rect p0){} + public void setRect(int p0, int p1, int p2, int p3){} + public void setRoundRect(Rect p0, float p1){} + public void setRoundRect(int p0, int p1, int p2, int p3, float p4){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/Paint.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Paint.java new file mode 100644 index 00000000000..810aecc3617 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Paint.java @@ -0,0 +1,212 @@ +// Generated automatically from android.graphics.Paint for testing purposes + +package android.graphics; + +import android.graphics.BlendMode; +import android.graphics.ColorFilter; +import android.graphics.MaskFilter; +import android.graphics.Path; +import android.graphics.PathEffect; +import android.graphics.Rect; +import android.graphics.Shader; +import android.graphics.Typeface; +import android.graphics.Xfermode; +import android.os.LocaleList; +import java.util.Locale; + +public class Paint +{ + public BlendMode getBlendMode(){ return null; } + public ColorFilter getColorFilter(){ return null; } + public ColorFilter setColorFilter(ColorFilter p0){ return null; } + public Locale getTextLocale(){ return null; } + public LocaleList getTextLocales(){ return null; } + public MaskFilter getMaskFilter(){ return null; } + public MaskFilter setMaskFilter(MaskFilter p0){ return null; } + public Paint(){} + public Paint(Paint p0){} + public Paint(int p0){} + public Paint.Align getTextAlign(){ return null; } + public Paint.Cap getStrokeCap(){ return null; } + public Paint.FontMetrics getFontMetrics(){ return null; } + public Paint.FontMetricsInt getFontMetricsInt(){ return null; } + public Paint.Join getStrokeJoin(){ return null; } + public Paint.Style getStyle(){ return null; } + public PathEffect getPathEffect(){ return null; } + public PathEffect setPathEffect(PathEffect p0){ return null; } + public Shader getShader(){ return null; } + public Shader setShader(Shader p0){ return null; } + public String getFontFeatureSettings(){ return null; } + public String getFontVariationSettings(){ return null; } + public Typeface getTypeface(){ return null; } + public Typeface setTypeface(Typeface p0){ return null; } + public Xfermode getXfermode(){ return null; } + public Xfermode setXfermode(Xfermode p0){ return null; } + public boolean equalsForTextMeasurement(Paint p0){ return false; } + public boolean getFillPath(Path p0, Path p1){ return false; } + public boolean hasGlyph(String p0){ return false; } + public boolean isElegantTextHeight(){ return false; } + public boolean setFontVariationSettings(String p0){ return false; } + public final boolean isAntiAlias(){ return false; } + public final boolean isDither(){ return false; } + public final boolean isFakeBoldText(){ return false; } + public final boolean isFilterBitmap(){ return false; } + public final boolean isLinearText(){ return false; } + public final boolean isStrikeThruText(){ return false; } + public final boolean isSubpixelText(){ return false; } + public final boolean isUnderlineText(){ return false; } + public float ascent(){ return 0; } + public float descent(){ return 0; } + public float getFontMetrics(Paint.FontMetrics p0){ return 0; } + public float getFontSpacing(){ return 0; } + public float getLetterSpacing(){ return 0; } + public float getRunAdvance(CharSequence p0, int p1, int p2, int p3, int p4, boolean p5, int p6){ return 0; } + public float getRunAdvance(char[] p0, int p1, int p2, int p3, int p4, boolean p5, int p6){ return 0; } + public float getShadowLayerDx(){ return 0; } + public float getShadowLayerDy(){ return 0; } + public float getShadowLayerRadius(){ return 0; } + public float getStrikeThruPosition(){ return 0; } + public float getStrikeThruThickness(){ return 0; } + public float getStrokeMiter(){ return 0; } + public float getStrokeWidth(){ return 0; } + public float getTextRunAdvances(char[] p0, int p1, int p2, int p3, int p4, boolean p5, float[] p6, int p7){ return 0; } + public float getTextScaleX(){ return 0; } + public float getTextSize(){ return 0; } + public float getTextSkewX(){ return 0; } + public float getUnderlinePosition(){ return 0; } + public float getUnderlineThickness(){ return 0; } + public float getWordSpacing(){ return 0; } + public float measureText(CharSequence p0, int p1, int p2){ return 0; } + public float measureText(String p0){ return 0; } + public float measureText(String p0, int p1, int p2){ return 0; } + public float measureText(char[] p0, int p1, int p2){ return 0; } + public int breakText(CharSequence p0, int p1, int p2, boolean p3, float p4, float[] p5){ return 0; } + public int breakText(String p0, boolean p1, float p2, float[] p3){ return 0; } + public int breakText(char[] p0, int p1, int p2, float p3, float[] p4){ return 0; } + public int getAlpha(){ return 0; } + public int getColor(){ return 0; } + public int getEndHyphenEdit(){ return 0; } + public int getFlags(){ return 0; } + public int getFontMetricsInt(Paint.FontMetricsInt p0){ return 0; } + public int getHinting(){ return 0; } + public int getOffsetForAdvance(CharSequence p0, int p1, int p2, int p3, int p4, boolean p5, float p6){ return 0; } + public int getOffsetForAdvance(char[] p0, int p1, int p2, int p3, int p4, boolean p5, float p6){ return 0; } + public int getShadowLayerColor(){ return 0; } + public int getStartHyphenEdit(){ return 0; } + public int getTextRunCursor(CharSequence p0, int p1, int p2, boolean p3, int p4, int p5){ return 0; } + public int getTextRunCursor(char[] p0, int p1, int p2, boolean p3, int p4, int p5){ return 0; } + public int getTextWidths(CharSequence p0, int p1, int p2, float[] p3){ return 0; } + public int getTextWidths(String p0, float[] p1){ return 0; } + public int getTextWidths(String p0, int p1, int p2, float[] p3){ return 0; } + public int getTextWidths(char[] p0, int p1, int p2, float[] p3){ return 0; } + public long getColorLong(){ return 0; } + public long getShadowLayerColorLong(){ return 0; } + public static int ANTI_ALIAS_FLAG = 0; + public static int CURSOR_AFTER = 0; + public static int CURSOR_AT = 0; + public static int CURSOR_AT_OR_AFTER = 0; + public static int CURSOR_AT_OR_BEFORE = 0; + public static int CURSOR_BEFORE = 0; + public static int DEV_KERN_TEXT_FLAG = 0; + public static int DITHER_FLAG = 0; + public static int EMBEDDED_BITMAP_TEXT_FLAG = 0; + public static int END_HYPHEN_EDIT_INSERT_ARMENIAN_HYPHEN = 0; + public static int END_HYPHEN_EDIT_INSERT_HYPHEN = 0; + public static int END_HYPHEN_EDIT_INSERT_MAQAF = 0; + public static int END_HYPHEN_EDIT_INSERT_UCAS_HYPHEN = 0; + public static int END_HYPHEN_EDIT_INSERT_ZWJ_AND_HYPHEN = 0; + public static int END_HYPHEN_EDIT_NO_EDIT = 0; + public static int END_HYPHEN_EDIT_REPLACE_WITH_HYPHEN = 0; + public static int FAKE_BOLD_TEXT_FLAG = 0; + public static int FILTER_BITMAP_FLAG = 0; + public static int HINTING_OFF = 0; + public static int HINTING_ON = 0; + public static int LINEAR_TEXT_FLAG = 0; + public static int START_HYPHEN_EDIT_INSERT_HYPHEN = 0; + public static int START_HYPHEN_EDIT_INSERT_ZWJ = 0; + public static int START_HYPHEN_EDIT_NO_EDIT = 0; + public static int STRIKE_THRU_TEXT_FLAG = 0; + public static int SUBPIXEL_TEXT_FLAG = 0; + public static int UNDERLINE_TEXT_FLAG = 0; + public void clearShadowLayer(){} + public void getTextBounds(CharSequence p0, int p1, int p2, Rect p3){} + public void getTextBounds(String p0, int p1, int p2, Rect p3){} + public void getTextBounds(char[] p0, int p1, int p2, Rect p3){} + public void getTextPath(String p0, int p1, int p2, float p3, float p4, Path p5){} + public void getTextPath(char[] p0, int p1, int p2, float p3, float p4, Path p5){} + public void reset(){} + public void set(Paint p0){} + public void setARGB(int p0, int p1, int p2, int p3){} + public void setAlpha(int p0){} + public void setAntiAlias(boolean p0){} + public void setBlendMode(BlendMode p0){} + public void setColor(int p0){} + public void setColor(long p0){} + public void setDither(boolean p0){} + public void setElegantTextHeight(boolean p0){} + public void setEndHyphenEdit(int p0){} + public void setFakeBoldText(boolean p0){} + public void setFilterBitmap(boolean p0){} + public void setFlags(int p0){} + public void setFontFeatureSettings(String p0){} + public void setHinting(int p0){} + public void setLetterSpacing(float p0){} + public void setLinearText(boolean p0){} + public void setShadowLayer(float p0, float p1, float p2, int p3){} + public void setShadowLayer(float p0, float p1, float p2, long p3){} + public void setStartHyphenEdit(int p0){} + public void setStrikeThruText(boolean p0){} + public void setStrokeCap(Paint.Cap p0){} + public void setStrokeJoin(Paint.Join p0){} + public void setStrokeMiter(float p0){} + public void setStrokeWidth(float p0){} + public void setStyle(Paint.Style p0){} + public void setSubpixelText(boolean p0){} + public void setTextAlign(Paint.Align p0){} + public void setTextLocale(Locale p0){} + public void setTextLocales(LocaleList p0){} + public void setTextScaleX(float p0){} + public void setTextSize(float p0){} + public void setTextSkewX(float p0){} + public void setUnderlineText(boolean p0){} + public void setWordSpacing(float p0){} + static public class FontMetrics + { + public FontMetrics(){} + public float ascent = 0; + public float bottom = 0; + public float descent = 0; + public float leading = 0; + public float top = 0; + } + static public class FontMetricsInt + { + public FontMetricsInt(){} + public String toString(){ return null; } + public int ascent = 0; + public int bottom = 0; + public int descent = 0; + public int leading = 0; + public int top = 0; + } + static public enum Align + { + CENTER, LEFT, RIGHT; + private Align() {} + } + static public enum Cap + { + BUTT, ROUND, SQUARE; + private Cap() {} + } + static public enum Join + { + BEVEL, MITER, ROUND; + private Join() {} + } + static public enum Style + { + FILL, FILL_AND_STROKE, STROKE; + private Style() {} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/Path.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Path.java new file mode 100644 index 00000000000..c7164e37b01 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Path.java @@ -0,0 +1,73 @@ +// Generated automatically from android.graphics.Path for testing purposes + +package android.graphics; + +import android.graphics.Matrix; +import android.graphics.RectF; + +public class Path +{ + public Path(){} + public Path(Path p0){} + public Path.FillType getFillType(){ return null; } + public boolean isConvex(){ return false; } + public boolean isEmpty(){ return false; } + public boolean isInverseFillType(){ return false; } + public boolean isRect(RectF p0){ return false; } + public boolean op(Path p0, Path p1, Path.Op p2){ return false; } + public boolean op(Path p0, Path.Op p1){ return false; } + public float[] approximate(float p0){ return null; } + public void addArc(RectF p0, float p1, float p2){} + public void addArc(float p0, float p1, float p2, float p3, float p4, float p5){} + public void addCircle(float p0, float p1, float p2, Path.Direction p3){} + public void addOval(RectF p0, Path.Direction p1){} + public void addOval(float p0, float p1, float p2, float p3, Path.Direction p4){} + public void addPath(Path p0){} + public void addPath(Path p0, Matrix p1){} + public void addPath(Path p0, float p1, float p2){} + public void addRect(RectF p0, Path.Direction p1){} + public void addRect(float p0, float p1, float p2, float p3, Path.Direction p4){} + public void addRoundRect(RectF p0, float p1, float p2, Path.Direction p3){} + public void addRoundRect(RectF p0, float[] p1, Path.Direction p2){} + public void addRoundRect(float p0, float p1, float p2, float p3, float p4, float p5, Path.Direction p6){} + public void addRoundRect(float p0, float p1, float p2, float p3, float[] p4, Path.Direction p5){} + public void arcTo(RectF p0, float p1, float p2){} + public void arcTo(RectF p0, float p1, float p2, boolean p3){} + public void arcTo(float p0, float p1, float p2, float p3, float p4, float p5, boolean p6){} + public void close(){} + public void computeBounds(RectF p0, boolean p1){} + public void cubicTo(float p0, float p1, float p2, float p3, float p4, float p5){} + public void incReserve(int p0){} + public void lineTo(float p0, float p1){} + public void moveTo(float p0, float p1){} + public void offset(float p0, float p1){} + public void offset(float p0, float p1, Path p2){} + public void quadTo(float p0, float p1, float p2, float p3){} + public void rCubicTo(float p0, float p1, float p2, float p3, float p4, float p5){} + public void rLineTo(float p0, float p1){} + public void rMoveTo(float p0, float p1){} + public void rQuadTo(float p0, float p1, float p2, float p3){} + public void reset(){} + public void rewind(){} + public void set(Path p0){} + public void setFillType(Path.FillType p0){} + public void setLastPoint(float p0, float p1){} + public void toggleInverseFillType(){} + public void transform(Matrix p0){} + public void transform(Matrix p0, Path p1){} + static public enum Direction + { + CCW, CW; + private Direction() {} + } + static public enum FillType + { + EVEN_ODD, INVERSE_EVEN_ODD, INVERSE_WINDING, WINDING; + private FillType() {} + } + static public enum Op + { + DIFFERENCE, INTERSECT, REVERSE_DIFFERENCE, UNION, XOR; + private Op() {} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/PathEffect.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/PathEffect.java new file mode 100644 index 00000000000..c9ca9aeae86 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/PathEffect.java @@ -0,0 +1,10 @@ +// Generated automatically from android.graphics.PathEffect for testing purposes + +package android.graphics; + + +public class PathEffect +{ + protected void finalize(){} + public PathEffect(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/Picture.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Picture.java new file mode 100644 index 00000000000..328321a2139 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Picture.java @@ -0,0 +1,18 @@ +// Generated automatically from android.graphics.Picture for testing purposes + +package android.graphics; + +import android.graphics.Canvas; + +public class Picture +{ + protected void finalize(){} + public Canvas beginRecording(int p0, int p1){ return null; } + public Picture(){} + public Picture(Picture p0){} + public boolean requiresHardwareAcceleration(){ return false; } + public int getHeight(){ return 0; } + public int getWidth(){ return 0; } + public void draw(Canvas p0){} + public void endRecording(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/Point.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Point.java new file mode 100644 index 00000000000..0e97248f44b --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Point.java @@ -0,0 +1,26 @@ +// Generated automatically from android.graphics.Point for testing purposes + +package android.graphics; + +import android.os.Parcel; +import android.os.Parcelable; + +public class Point implements Parcelable +{ + public Point(){} + public Point(Point p0){} + public Point(int p0, int p1){} + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public final boolean equals(int p0, int p1){ return false; } + public final void negate(){} + public final void offset(int p0, int p1){} + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public int x = 0; + public int y = 0; + public static Parcelable.Creator CREATOR = null; + public void readFromParcel(Parcel p0){} + public void set(int p0, int p1){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/PorterDuff.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/PorterDuff.java new file mode 100644 index 00000000000..39a82e655d6 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/PorterDuff.java @@ -0,0 +1,14 @@ +// Generated automatically from android.graphics.PorterDuff for testing purposes + +package android.graphics; + + +public class PorterDuff +{ + public PorterDuff(){} + static public enum Mode + { + ADD, CLEAR, DARKEN, DST, DST_ATOP, DST_IN, DST_OUT, DST_OVER, LIGHTEN, MULTIPLY, OVERLAY, SCREEN, SRC, SRC_ATOP, SRC_IN, SRC_OUT, SRC_OVER, XOR; + private Mode() {} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/RecordingCanvas.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/RecordingCanvas.java new file mode 100644 index 00000000000..3616f4275fe --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/RecordingCanvas.java @@ -0,0 +1,83 @@ +// Generated automatically from android.graphics.RecordingCanvas for testing purposes + +package android.graphics; + +import android.graphics.Bitmap; +import android.graphics.BlendMode; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.NinePatch; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Picture; +import android.graphics.PorterDuff; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.RenderNode; +import android.graphics.text.MeasuredText; + +public class RecordingCanvas extends Canvas +{ + protected RecordingCanvas() {} + public boolean isHardwareAccelerated(){ return false; } + public boolean isOpaque(){ return false; } + public final void drawARGB(int p0, int p1, int p2, int p3){} + public final void drawArc(RectF p0, float p1, float p2, boolean p3, Paint p4){} + public final void drawArc(float p0, float p1, float p2, float p3, float p4, float p5, boolean p6, Paint p7){} + public final void drawBitmap(Bitmap p0, Matrix p1, Paint p2){} + public final void drawBitmap(Bitmap p0, Rect p1, Rect p2, Paint p3){} + public final void drawBitmap(Bitmap p0, Rect p1, RectF p2, Paint p3){} + public final void drawBitmap(Bitmap p0, float p1, float p2, Paint p3){} + public final void drawBitmap(int[] p0, int p1, int p2, float p3, float p4, int p5, int p6, boolean p7, Paint p8){} + public final void drawBitmap(int[] p0, int p1, int p2, int p3, int p4, int p5, int p6, boolean p7, Paint p8){} + public final void drawBitmapMesh(Bitmap p0, int p1, int p2, float[] p3, int p4, int[] p5, int p6, Paint p7){} + public final void drawCircle(float p0, float p1, float p2, Paint p3){} + public final void drawColor(int p0){} + public final void drawColor(int p0, BlendMode p1){} + public final void drawColor(int p0, PorterDuff.Mode p1){} + public final void drawColor(long p0, BlendMode p1){} + public final void drawDoubleRoundRect(RectF p0, float p1, float p2, RectF p3, float p4, float p5, Paint p6){} + public final void drawDoubleRoundRect(RectF p0, float[] p1, RectF p2, float[] p3, Paint p4){} + public final void drawLine(float p0, float p1, float p2, float p3, Paint p4){} + public final void drawLines(float[] p0, Paint p1){} + public final void drawLines(float[] p0, int p1, int p2, Paint p3){} + public final void drawOval(RectF p0, Paint p1){} + public final void drawOval(float p0, float p1, float p2, float p3, Paint p4){} + public final void drawPaint(Paint p0){} + public final void drawPatch(NinePatch p0, Rect p1, Paint p2){} + public final void drawPatch(NinePatch p0, RectF p1, Paint p2){} + public final void drawPath(Path p0, Paint p1){} + public final void drawPicture(Picture p0){} + public final void drawPicture(Picture p0, Rect p1){} + public final void drawPicture(Picture p0, RectF p1){} + public final void drawPoint(float p0, float p1, Paint p2){} + public final void drawPoints(float[] p0, Paint p1){} + public final void drawPoints(float[] p0, int p1, int p2, Paint p3){} + public final void drawPosText(String p0, float[] p1, Paint p2){} + public final void drawPosText(char[] p0, int p1, int p2, float[] p3, Paint p4){} + public final void drawRGB(int p0, int p1, int p2){} + public final void drawRect(Rect p0, Paint p1){} + public final void drawRect(RectF p0, Paint p1){} + public final void drawRect(float p0, float p1, float p2, float p3, Paint p4){} + public final void drawRoundRect(RectF p0, float p1, float p2, Paint p3){} + public final void drawRoundRect(float p0, float p1, float p2, float p3, float p4, float p5, Paint p6){} + public final void drawText(CharSequence p0, int p1, int p2, float p3, float p4, Paint p5){} + public final void drawText(String p0, float p1, float p2, Paint p3){} + public final void drawText(String p0, int p1, int p2, float p3, float p4, Paint p5){} + public final void drawText(char[] p0, int p1, int p2, float p3, float p4, Paint p5){} + public final void drawTextOnPath(String p0, Path p1, float p2, float p3, Paint p4){} + public final void drawTextOnPath(char[] p0, int p1, int p2, Path p3, float p4, float p5, Paint p6){} + public final void drawTextRun(CharSequence p0, int p1, int p2, int p3, int p4, float p5, float p6, boolean p7, Paint p8){} + public final void drawTextRun(char[] p0, int p1, int p2, int p3, int p4, float p5, float p6, boolean p7, Paint p8){} + public final void drawVertices(Canvas.VertexMode p0, int p1, float[] p2, int p3, float[] p4, int p5, int[] p6, int p7, short[] p8, int p9, int p10, Paint p11){} + public int getHeight(){ return 0; } + public int getMaximumBitmapHeight(){ return 0; } + public int getMaximumBitmapWidth(){ return 0; } + public int getWidth(){ return 0; } + public void disableZ(){} + public void drawRenderNode(RenderNode p0){} + public void drawTextRun(MeasuredText p0, int p1, int p2, int p3, int p4, float p5, float p6, boolean p7, Paint p8){} + public void enableZ(){} + public void setBitmap(Bitmap p0){} + public void setDensity(int p0){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/Rect.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Rect.java new file mode 100644 index 00000000000..c29950cb31d --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Rect.java @@ -0,0 +1,52 @@ +// Generated automatically from android.graphics.Rect for testing purposes + +package android.graphics; + +import android.os.Parcel; +import android.os.Parcelable; + +public class Rect implements Parcelable +{ + public Rect(){} + public Rect(Rect p0){} + public Rect(int p0, int p1, int p2, int p3){} + public String flattenToString(){ return null; } + public String toShortString(){ return null; } + public String toString(){ return null; } + public boolean contains(Rect p0){ return false; } + public boolean contains(int p0, int p1){ return false; } + public boolean contains(int p0, int p1, int p2, int p3){ return false; } + public boolean equals(Object p0){ return false; } + public boolean intersect(Rect p0){ return false; } + public boolean intersect(int p0, int p1, int p2, int p3){ return false; } + public boolean intersects(int p0, int p1, int p2, int p3){ return false; } + public boolean isEmpty(){ return false; } + public boolean setIntersect(Rect p0, Rect p1){ return false; } + public float exactCenterX(){ return 0; } + public float exactCenterY(){ return 0; } + public int bottom = 0; + public int centerX(){ return 0; } + public int centerY(){ return 0; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public int height(){ return 0; } + public int left = 0; + public int right = 0; + public int top = 0; + public int width(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static Rect unflattenFromString(String p0){ return null; } + public static boolean intersects(Rect p0, Rect p1){ return false; } + public void inset(int p0, int p1){} + public void offset(int p0, int p1){} + public void offsetTo(int p0, int p1){} + public void readFromParcel(Parcel p0){} + public void set(Rect p0){} + public void set(int p0, int p1, int p2, int p3){} + public void setEmpty(){} + public void sort(){} + public void union(Rect p0){} + public void union(int p0, int p1){} + public void union(int p0, int p1, int p2, int p3){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/RectF.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/RectF.java new file mode 100644 index 00000000000..59489dd3f17 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/RectF.java @@ -0,0 +1,53 @@ +// Generated automatically from android.graphics.RectF for testing purposes + +package android.graphics; + +import android.graphics.Rect; +import android.os.Parcel; +import android.os.Parcelable; + +public class RectF implements Parcelable +{ + public RectF(){} + public RectF(Rect p0){} + public RectF(RectF p0){} + public RectF(float p0, float p1, float p2, float p3){} + public String toShortString(){ return null; } + public String toString(){ return null; } + public boolean contains(RectF p0){ return false; } + public boolean contains(float p0, float p1){ return false; } + public boolean contains(float p0, float p1, float p2, float p3){ return false; } + public boolean equals(Object p0){ return false; } + public boolean intersect(RectF p0){ return false; } + public boolean intersect(float p0, float p1, float p2, float p3){ return false; } + public boolean intersects(float p0, float p1, float p2, float p3){ return false; } + public boolean setIntersect(RectF p0, RectF p1){ return false; } + public final boolean isEmpty(){ return false; } + public final float centerX(){ return 0; } + public final float centerY(){ return 0; } + public final float height(){ return 0; } + public final float width(){ return 0; } + public float bottom = 0; + public float left = 0; + public float right = 0; + public float top = 0; + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static boolean intersects(RectF p0, RectF p1){ return false; } + public void inset(float p0, float p1){} + public void offset(float p0, float p1){} + public void offsetTo(float p0, float p1){} + public void readFromParcel(Parcel p0){} + public void round(Rect p0){} + public void roundOut(Rect p0){} + public void set(Rect p0){} + public void set(RectF p0){} + public void set(float p0, float p1, float p2, float p3){} + public void setEmpty(){} + public void sort(){} + public void union(RectF p0){} + public void union(float p0, float p1){} + public void union(float p0, float p1, float p2, float p3){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/Region.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Region.java new file mode 100644 index 00000000000..7fbd2b4dafc --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Region.java @@ -0,0 +1,53 @@ +// Generated automatically from android.graphics.Region for testing purposes + +package android.graphics; + +import android.graphics.Path; +import android.graphics.Rect; +import android.os.Parcel; +import android.os.Parcelable; + +public class Region implements Parcelable +{ + protected void finalize(){} + public Path getBoundaryPath(){ return null; } + public Rect getBounds(){ return null; } + public Region(){} + public Region(Rect p0){} + public Region(Region p0){} + public Region(int p0, int p1, int p2, int p3){} + public String toString(){ return null; } + public boolean contains(int p0, int p1){ return false; } + public boolean equals(Object p0){ return false; } + public boolean getBoundaryPath(Path p0){ return false; } + public boolean getBounds(Rect p0){ return false; } + public boolean isComplex(){ return false; } + public boolean isEmpty(){ return false; } + public boolean isRect(){ return false; } + public boolean op(Rect p0, Region p1, Region.Op p2){ return false; } + public boolean op(Rect p0, Region.Op p1){ return false; } + public boolean op(Region p0, Region p1, Region.Op p2){ return false; } + public boolean op(Region p0, Region.Op p1){ return false; } + public boolean op(int p0, int p1, int p2, int p3, Region.Op p4){ return false; } + public boolean quickContains(Rect p0){ return false; } + public boolean quickContains(int p0, int p1, int p2, int p3){ return false; } + public boolean quickReject(Rect p0){ return false; } + public boolean quickReject(Region p0){ return false; } + public boolean quickReject(int p0, int p1, int p2, int p3){ return false; } + public boolean set(Rect p0){ return false; } + public boolean set(Region p0){ return false; } + public boolean set(int p0, int p1, int p2, int p3){ return false; } + public boolean setPath(Path p0, Region p1){ return false; } + public final boolean union(Rect p0){ return false; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void setEmpty(){} + public void translate(int p0, int p1){} + public void translate(int p0, int p1, Region p2){} + public void writeToParcel(Parcel p0, int p1){} + static public enum Op + { + DIFFERENCE, INTERSECT, REPLACE, REVERSE_DIFFERENCE, UNION, XOR; + private Op() {} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/RenderNode.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/RenderNode.java new file mode 100644 index 00000000000..9384cd4cd4e --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/RenderNode.java @@ -0,0 +1,82 @@ +// Generated automatically from android.graphics.RenderNode for testing purposes + +package android.graphics; + +import android.graphics.Matrix; +import android.graphics.Outline; +import android.graphics.Paint; +import android.graphics.RecordingCanvas; +import android.graphics.Rect; + +public class RenderNode +{ + protected RenderNode() {} + public RecordingCanvas beginRecording(){ return null; } + public RecordingCanvas beginRecording(int p0, int p1){ return null; } + public RenderNode(String p0){} + public boolean getClipToBounds(){ return false; } + public boolean getClipToOutline(){ return false; } + public boolean getUseCompositingLayer(){ return false; } + public boolean hasDisplayList(){ return false; } + public boolean hasIdentityMatrix(){ return false; } + public boolean hasOverlappingRendering(){ return false; } + public boolean hasShadow(){ return false; } + public boolean isForceDarkAllowed(){ return false; } + public boolean isPivotExplicitlySet(){ return false; } + public boolean offsetLeftAndRight(int p0){ return false; } + public boolean offsetTopAndBottom(int p0){ return false; } + public boolean resetPivot(){ return false; } + public boolean setAlpha(float p0){ return false; } + public boolean setAmbientShadowColor(int p0){ return false; } + public boolean setCameraDistance(float p0){ return false; } + public boolean setClipRect(Rect p0){ return false; } + public boolean setClipToBounds(boolean p0){ return false; } + public boolean setClipToOutline(boolean p0){ return false; } + public boolean setElevation(float p0){ return false; } + public boolean setForceDarkAllowed(boolean p0){ return false; } + public boolean setHasOverlappingRendering(boolean p0){ return false; } + public boolean setOutline(Outline p0){ return false; } + public boolean setPivotX(float p0){ return false; } + public boolean setPivotY(float p0){ return false; } + public boolean setPosition(Rect p0){ return false; } + public boolean setPosition(int p0, int p1, int p2, int p3){ return false; } + public boolean setProjectBackwards(boolean p0){ return false; } + public boolean setProjectionReceiver(boolean p0){ return false; } + public boolean setRotationX(float p0){ return false; } + public boolean setRotationY(float p0){ return false; } + public boolean setRotationZ(float p0){ return false; } + public boolean setScaleX(float p0){ return false; } + public boolean setScaleY(float p0){ return false; } + public boolean setSpotShadowColor(int p0){ return false; } + public boolean setTranslationX(float p0){ return false; } + public boolean setTranslationY(float p0){ return false; } + public boolean setTranslationZ(float p0){ return false; } + public boolean setUseCompositingLayer(boolean p0, Paint p1){ return false; } + public float getAlpha(){ return 0; } + public float getCameraDistance(){ return 0; } + public float getElevation(){ return 0; } + public float getPivotX(){ return 0; } + public float getPivotY(){ return 0; } + public float getRotationX(){ return 0; } + public float getRotationY(){ return 0; } + public float getRotationZ(){ return 0; } + public float getScaleX(){ return 0; } + public float getScaleY(){ return 0; } + public float getTranslationX(){ return 0; } + public float getTranslationY(){ return 0; } + public float getTranslationZ(){ return 0; } + public int getAmbientShadowColor(){ return 0; } + public int getBottom(){ return 0; } + public int getHeight(){ return 0; } + public int getLeft(){ return 0; } + public int getRight(){ return 0; } + public int getSpotShadowColor(){ return 0; } + public int getTop(){ return 0; } + public int getWidth(){ return 0; } + public long computeApproximateMemoryUsage(){ return 0; } + public long getUniqueId(){ return 0; } + public void discardDisplayList(){} + public void endRecording(){} + public void getInverseMatrix(Matrix p0){} + public void getMatrix(Matrix p0){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/Shader.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Shader.java new file mode 100644 index 00000000000..3e845aa0c0a --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Shader.java @@ -0,0 +1,12 @@ +// Generated automatically from android.graphics.Shader for testing purposes + +package android.graphics; + +import android.graphics.Matrix; + +public class Shader +{ + public Shader(){} + public boolean getLocalMatrix(Matrix p0){ return false; } + public void setLocalMatrix(Matrix p0){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/Typeface.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Typeface.java new file mode 100644 index 00000000000..a2ab630e8d0 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Typeface.java @@ -0,0 +1,33 @@ +// Generated automatically from android.graphics.Typeface for testing purposes + +package android.graphics; + +import android.content.res.AssetManager; +import java.io.File; + +public class Typeface +{ + protected Typeface() {} + public boolean equals(Object p0){ return false; } + public final boolean isBold(){ return false; } + public final boolean isItalic(){ return false; } + public int getStyle(){ return 0; } + public int getWeight(){ return 0; } + public int hashCode(){ return 0; } + public static Typeface DEFAULT = null; + public static Typeface DEFAULT_BOLD = null; + public static Typeface MONOSPACE = null; + public static Typeface SANS_SERIF = null; + public static Typeface SERIF = null; + public static Typeface create(String p0, int p1){ return null; } + public static Typeface create(Typeface p0, int p1){ return null; } + public static Typeface create(Typeface p0, int p1, boolean p2){ return null; } + public static Typeface createFromAsset(AssetManager p0, String p1){ return null; } + public static Typeface createFromFile(File p0){ return null; } + public static Typeface createFromFile(String p0){ return null; } + public static Typeface defaultFromStyle(int p0){ return null; } + public static int BOLD = 0; + public static int BOLD_ITALIC = 0; + public static int ITALIC = 0; + public static int NORMAL = 0; +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/Xfermode.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Xfermode.java new file mode 100644 index 00000000000..7b6e5ade001 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/Xfermode.java @@ -0,0 +1,9 @@ +// Generated automatically from android.graphics.Xfermode for testing purposes + +package android.graphics; + + +public class Xfermode +{ + public Xfermode(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/drawable/Drawable.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/drawable/Drawable.java new file mode 100644 index 00000000000..5bb5b00f898 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/drawable/Drawable.java @@ -0,0 +1,111 @@ +// Generated automatically from android.graphics.drawable.Drawable for testing purposes + +package android.graphics.drawable; + +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.graphics.BitmapFactory; +import android.graphics.BlendMode; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Insets; +import android.graphics.Outline; +import android.graphics.PorterDuff; +import android.graphics.Rect; +import android.graphics.Region; +import android.util.AttributeSet; +import android.util.TypedValue; +import java.io.InputStream; +import org.xmlpull.v1.XmlPullParser; + +abstract public class Drawable +{ + abstract static public class ConstantState + { + public ConstantState(){} + public Drawable newDrawable(Resources p0){ return null; } + public Drawable newDrawable(Resources p0, Resources.Theme p1){ return null; } + public abstract Drawable newDrawable(); + public abstract int getChangingConfigurations(); + public boolean canApplyTheme(){ return false; } + } + protected boolean onLevelChange(int p0){ return false; } + protected boolean onStateChange(int[] p0){ return false; } + protected void onBoundsChange(Rect p0){} + public ColorFilter getColorFilter(){ return null; } + public Drawable getCurrent(){ return null; } + public Drawable mutate(){ return null; } + public Drawable(){} + public Drawable.Callback getCallback(){ return null; } + public Drawable.ConstantState getConstantState(){ return null; } + public Insets getOpticalInsets(){ return null; } + public Rect getDirtyBounds(){ return null; } + public Region getTransparentRegion(){ return null; } + public abstract int getOpacity(); + public abstract void draw(Canvas p0); + public abstract void setAlpha(int p0); + public abstract void setColorFilter(ColorFilter p0); + public boolean canApplyTheme(){ return false; } + public boolean getPadding(Rect p0){ return false; } + public boolean isAutoMirrored(){ return false; } + public boolean isFilterBitmap(){ return false; } + public boolean isProjected(){ return false; } + public boolean isStateful(){ return false; } + public boolean onLayoutDirectionChanged(int p0){ return false; } + public boolean setState(int[] p0){ return false; } + public boolean setVisible(boolean p0, boolean p1){ return false; } + public final Rect copyBounds(){ return null; } + public final Rect getBounds(){ return null; } + public final boolean isVisible(){ return false; } + public final boolean setLayoutDirection(int p0){ return false; } + public final boolean setLevel(int p0){ return false; } + public final int getLevel(){ return 0; } + public final void copyBounds(Rect p0){} + public final void setCallback(Drawable.Callback p0){} + public int getAlpha(){ return 0; } + public int getChangingConfigurations(){ return 0; } + public int getIntrinsicHeight(){ return 0; } + public int getIntrinsicWidth(){ return 0; } + public int getLayoutDirection(){ return 0; } + public int getMinimumHeight(){ return 0; } + public int getMinimumWidth(){ return 0; } + public int[] getState(){ return null; } + public static Drawable createFromPath(String p0){ return null; } + public static Drawable createFromResourceStream(Resources p0, TypedValue p1, InputStream p2, String p3){ return null; } + public static Drawable createFromResourceStream(Resources p0, TypedValue p1, InputStream p2, String p3, BitmapFactory.Options p4){ return null; } + public static Drawable createFromStream(InputStream p0, String p1){ return null; } + public static Drawable createFromXml(Resources p0, XmlPullParser p1){ return null; } + public static Drawable createFromXml(Resources p0, XmlPullParser p1, Resources.Theme p2){ return null; } + public static Drawable createFromXmlInner(Resources p0, XmlPullParser p1, AttributeSet p2){ return null; } + public static Drawable createFromXmlInner(Resources p0, XmlPullParser p1, AttributeSet p2, Resources.Theme p3){ return null; } + public static int resolveOpacity(int p0, int p1){ return 0; } + public void applyTheme(Resources.Theme p0){} + public void clearColorFilter(){} + public void getHotspotBounds(Rect p0){} + public void getOutline(Outline p0){} + public void inflate(Resources p0, XmlPullParser p1, AttributeSet p2){} + public void inflate(Resources p0, XmlPullParser p1, AttributeSet p2, Resources.Theme p3){} + public void invalidateSelf(){} + public void jumpToCurrentState(){} + public void scheduleSelf(Runnable p0, long p1){} + public void setAutoMirrored(boolean p0){} + public void setBounds(Rect p0){} + public void setBounds(int p0, int p1, int p2, int p3){} + public void setChangingConfigurations(int p0){} + public void setColorFilter(int p0, PorterDuff.Mode p1){} + public void setDither(boolean p0){} + public void setFilterBitmap(boolean p0){} + public void setHotspot(float p0, float p1){} + public void setHotspotBounds(int p0, int p1, int p2, int p3){} + public void setTint(int p0){} + public void setTintBlendMode(BlendMode p0){} + public void setTintList(ColorStateList p0){} + public void setTintMode(PorterDuff.Mode p0){} + public void unscheduleSelf(Runnable p0){} + static public interface Callback + { + void invalidateDrawable(Drawable p0); + void scheduleDrawable(Drawable p0, Runnable p1, long p2); + void unscheduleDrawable(Drawable p0, Runnable p1); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/drawable/Icon.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/drawable/Icon.java new file mode 100644 index 00000000000..69faa775482 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/drawable/Icon.java @@ -0,0 +1,52 @@ +// Generated automatically from android.graphics.drawable.Icon for testing purposes + +package android.graphics.drawable; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Bitmap; +import android.graphics.BlendMode; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Handler; +import android.os.Message; +import android.os.Parcel; +import android.os.Parcelable; + +public class Icon implements Parcelable +{ + protected Icon() {} + public Drawable loadDrawable(Context p0){ return null; } + public Icon setTint(int p0){ return null; } + public Icon setTintBlendMode(BlendMode p0){ return null; } + public Icon setTintList(ColorStateList p0){ return null; } + public Icon setTintMode(PorterDuff.Mode p0){ return null; } + public String getResPackage(){ return null; } + public String toString(){ return null; } + public Uri getUri(){ return null; } + public int describeContents(){ return 0; } + public int getResId(){ return 0; } + public int getType(){ return 0; } + public static Icon createWithAdaptiveBitmap(Bitmap p0){ return null; } + public static Icon createWithBitmap(Bitmap p0){ return null; } + public static Icon createWithContentUri(String p0){ return null; } + public static Icon createWithContentUri(Uri p0){ return null; } + public static Icon createWithData(byte[] p0, int p1, int p2){ return null; } + public static Icon createWithFilePath(String p0){ return null; } + public static Icon createWithResource(Context p0, int p1){ return null; } + public static Icon createWithResource(String p0, int p1){ return null; } + public static Parcelable.Creator CREATOR = null; + public static int TYPE_ADAPTIVE_BITMAP = 0; + public static int TYPE_BITMAP = 0; + public static int TYPE_DATA = 0; + public static int TYPE_RESOURCE = 0; + public static int TYPE_URI = 0; + public void loadDrawableAsync(Context p0, Icon.OnDrawableLoadedListener p1, Handler p2){} + public void loadDrawableAsync(Context p0, Message p1){} + public void writeToParcel(Parcel p0, int p1){} + static public interface OnDrawableLoadedListener + { + void onDrawableLoaded(Drawable p0); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/graphics/text/MeasuredText.java b/java/ql/test/stubs/google-android-9.0.0/android/graphics/text/MeasuredText.java new file mode 100644 index 00000000000..c737bd5dfc1 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/graphics/text/MeasuredText.java @@ -0,0 +1,13 @@ +// Generated automatically from android.graphics.text.MeasuredText for testing purposes + +package android.graphics.text; + +import android.graphics.Rect; + +public class MeasuredText +{ + protected MeasuredText() {} + public float getCharWidthAt(int p0){ return 0; } + public float getWidth(int p0, int p1){ return 0; } + public void getBounds(int p0, int p1, Rect p2){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/hardware/HardwareBuffer.java b/java/ql/test/stubs/google-android-9.0.0/android/hardware/HardwareBuffer.java new file mode 100644 index 00000000000..d2998f52be9 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/hardware/HardwareBuffer.java @@ -0,0 +1,49 @@ +// Generated automatically from android.hardware.HardwareBuffer for testing purposes + +package android.hardware; + +import android.os.Parcel; +import android.os.Parcelable; + +public class HardwareBuffer implements AutoCloseable, Parcelable +{ + protected HardwareBuffer() {} + protected void finalize(){} + public boolean isClosed(){ return false; } + public int describeContents(){ return 0; } + public int getFormat(){ return 0; } + public int getHeight(){ return 0; } + public int getLayers(){ return 0; } + public int getWidth(){ return 0; } + public long getUsage(){ return 0; } + public static HardwareBuffer create(int p0, int p1, int p2, int p3, long p4){ return null; } + public static Parcelable.Creator CREATOR = null; + public static boolean isSupported(int p0, int p1, int p2, int p3, long p4){ return false; } + public static int BLOB = 0; + public static int DS_24UI8 = 0; + public static int DS_FP32UI8 = 0; + public static int D_16 = 0; + public static int D_24 = 0; + public static int D_FP32 = 0; + public static int RGBA_1010102 = 0; + public static int RGBA_8888 = 0; + public static int RGBA_FP16 = 0; + public static int RGBX_8888 = 0; + public static int RGB_565 = 0; + public static int RGB_888 = 0; + public static int S_UI8 = 0; + public static long USAGE_CPU_READ_OFTEN = 0; + public static long USAGE_CPU_READ_RARELY = 0; + public static long USAGE_CPU_WRITE_OFTEN = 0; + public static long USAGE_CPU_WRITE_RARELY = 0; + public static long USAGE_GPU_COLOR_OUTPUT = 0; + public static long USAGE_GPU_CUBE_MAP = 0; + public static long USAGE_GPU_DATA_BUFFER = 0; + public static long USAGE_GPU_MIPMAP_COMPLETE = 0; + public static long USAGE_GPU_SAMPLED_IMAGE = 0; + public static long USAGE_PROTECTED_CONTENT = 0; + public static long USAGE_SENSOR_DIRECT_DATA = 0; + public static long USAGE_VIDEO_ENCODE = 0; + public void close(){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/icu/util/ULocale.java b/java/ql/test/stubs/google-android-9.0.0/android/icu/util/ULocale.java new file mode 100644 index 00000000000..1e1ea662c22 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/icu/util/ULocale.java @@ -0,0 +1,138 @@ +// Generated automatically from android.icu.util.ULocale for testing purposes + +package android.icu.util; + +import java.io.Serializable; +import java.util.Iterator; +import java.util.Locale; +import java.util.Set; + +public class ULocale implements Comparable, Serializable +{ + protected ULocale() {} + public Iterator getKeywords(){ return null; } + public Locale toLocale(){ return null; } + public Object clone(){ return null; } + public Set getExtensionKeys(){ return null; } + public Set getUnicodeLocaleAttributes(){ return null; } + public Set getUnicodeLocaleKeys(){ return null; } + public String getBaseName(){ return null; } + public String getCharacterOrientation(){ return null; } + public String getCountry(){ return null; } + public String getDisplayCountry(){ return null; } + public String getDisplayCountry(ULocale p0){ return null; } + public String getDisplayKeywordValue(String p0){ return null; } + public String getDisplayKeywordValue(String p0, ULocale p1){ return null; } + public String getDisplayLanguage(){ return null; } + public String getDisplayLanguage(ULocale p0){ return null; } + public String getDisplayLanguageWithDialect(){ return null; } + public String getDisplayLanguageWithDialect(ULocale p0){ return null; } + public String getDisplayName(){ return null; } + public String getDisplayName(ULocale p0){ return null; } + public String getDisplayNameWithDialect(){ return null; } + public String getDisplayNameWithDialect(ULocale p0){ return null; } + public String getDisplayScript(){ return null; } + public String getDisplayScript(ULocale p0){ return null; } + public String getDisplayVariant(){ return null; } + public String getDisplayVariant(ULocale p0){ return null; } + public String getExtension(char p0){ return null; } + public String getISO3Country(){ return null; } + public String getISO3Language(){ return null; } + public String getKeywordValue(String p0){ return null; } + public String getLanguage(){ return null; } + public String getLineOrientation(){ return null; } + public String getName(){ return null; } + public String getScript(){ return null; } + public String getUnicodeLocaleType(String p0){ return null; } + public String getVariant(){ return null; } + public String toLanguageTag(){ return null; } + public String toString(){ return null; } + public ULocale getFallback(){ return null; } + public ULocale setKeywordValue(String p0, String p1){ return null; } + public ULocale(String p0){} + public ULocale(String p0, String p1){} + public ULocale(String p0, String p1, String p2){} + public boolean equals(Object p0){ return false; } + public boolean isRightToLeft(){ return false; } + public int compareTo(ULocale p0){ return 0; } + public int hashCode(){ return 0; } + public static Iterator getKeywords(String p0){ return null; } + public static String canonicalize(String p0){ return null; } + public static String getBaseName(String p0){ return null; } + public static String getCountry(String p0){ return null; } + public static String getDisplayCountry(String p0, String p1){ return null; } + public static String getDisplayCountry(String p0, ULocale p1){ return null; } + public static String getDisplayKeyword(String p0){ return null; } + public static String getDisplayKeyword(String p0, String p1){ return null; } + public static String getDisplayKeyword(String p0, ULocale p1){ return null; } + public static String getDisplayKeywordValue(String p0, String p1, String p2){ return null; } + public static String getDisplayKeywordValue(String p0, String p1, ULocale p2){ return null; } + public static String getDisplayLanguage(String p0, String p1){ return null; } + public static String getDisplayLanguage(String p0, ULocale p1){ return null; } + public static String getDisplayLanguageWithDialect(String p0, String p1){ return null; } + public static String getDisplayLanguageWithDialect(String p0, ULocale p1){ return null; } + public static String getDisplayName(String p0, String p1){ return null; } + public static String getDisplayName(String p0, ULocale p1){ return null; } + public static String getDisplayNameWithDialect(String p0, String p1){ return null; } + public static String getDisplayNameWithDialect(String p0, ULocale p1){ return null; } + public static String getDisplayScript(String p0, String p1){ return null; } + public static String getDisplayScript(String p0, ULocale p1){ return null; } + public static String getDisplayVariant(String p0, String p1){ return null; } + public static String getDisplayVariant(String p0, ULocale p1){ return null; } + public static String getFallback(String p0){ return null; } + public static String getISO3Country(String p0){ return null; } + public static String getISO3Language(String p0){ return null; } + public static String getKeywordValue(String p0, String p1){ return null; } + public static String getLanguage(String p0){ return null; } + public static String getName(String p0){ return null; } + public static String getScript(String p0){ return null; } + public static String getVariant(String p0){ return null; } + public static String setKeywordValue(String p0, String p1, String p2){ return null; } + public static String toLegacyKey(String p0){ return null; } + public static String toLegacyType(String p0, String p1){ return null; } + public static String toUnicodeLocaleKey(String p0){ return null; } + public static String toUnicodeLocaleType(String p0, String p1){ return null; } + public static String[] getISOCountries(){ return null; } + public static String[] getISOLanguages(){ return null; } + public static ULocale CANADA = null; + public static ULocale CANADA_FRENCH = null; + public static ULocale CHINA = null; + public static ULocale CHINESE = null; + public static ULocale ENGLISH = null; + public static ULocale FRANCE = null; + public static ULocale FRENCH = null; + public static ULocale GERMAN = null; + public static ULocale GERMANY = null; + public static ULocale ITALIAN = null; + public static ULocale ITALY = null; + public static ULocale JAPAN = null; + public static ULocale JAPANESE = null; + public static ULocale KOREA = null; + public static ULocale KOREAN = null; + public static ULocale PRC = null; + public static ULocale ROOT = null; + public static ULocale SIMPLIFIED_CHINESE = null; + public static ULocale TAIWAN = null; + public static ULocale TRADITIONAL_CHINESE = null; + public static ULocale UK = null; + public static ULocale US = null; + public static ULocale acceptLanguage(String p0, ULocale[] p1, boolean[] p2){ return null; } + public static ULocale acceptLanguage(String p0, boolean[] p1){ return null; } + public static ULocale acceptLanguage(ULocale[] p0, ULocale[] p1, boolean[] p2){ return null; } + public static ULocale acceptLanguage(ULocale[] p0, boolean[] p1){ return null; } + public static ULocale addLikelySubtags(ULocale p0){ return null; } + public static ULocale createCanonical(String p0){ return null; } + public static ULocale forLanguageTag(String p0){ return null; } + public static ULocale forLocale(Locale p0){ return null; } + public static ULocale getDefault(){ return null; } + public static ULocale getDefault(ULocale.Category p0){ return null; } + public static ULocale minimizeSubtags(ULocale p0){ return null; } + public static ULocale[] getAvailableLocales(){ return null; } + public static char PRIVATE_USE_EXTENSION = '0'; + public static char UNICODE_LOCALE_EXTENSION = '0'; + static public enum Category + { + DISPLAY, FORMAT; + private Category() {} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/net/Uri.java b/java/ql/test/stubs/google-android-9.0.0/android/net/Uri.java index 2b63354b8f2..963829742b2 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/net/Uri.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/net/Uri.java @@ -1,692 +1,75 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.net; +// Generated automatically from android.net.Uri for testing purposes -import android.content.Intent; +package android.net; import android.os.Parcel; import android.os.Parcelable; - import java.io.File; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.AbstractList; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashSet; import java.util.List; -import java.util.Locale; -import java.util.Objects; -import java.util.RandomAccess; import java.util.Set; -/** - * Immutable URI reference. A URI reference includes a URI and a fragment, the - * component of the URI following a '#'. Builds and parses URI references which - * conform to RFC 2396. - * - *

    - * In the interest of performance, this class performs little to no validation. - * Behavior is undefined for invalid input. This class is very forgiving--in the - * face of invalid input, it will return garbage rather than throw an exception - * unless otherwise specified. - */ -public abstract class Uri implements Parcelable, Comparable { - /* - * This class aims to do as little up front work as possible. To accomplish - * that, we vary the implementation depending on what the user passes in. For - * example, we have one implementation if the user passes in a URI string - * (StringUri) and another if the user passes in the individual components - * (OpaqueUri). Concurrency notes*: Like any truly immutable object, this class - * is safe for concurrent use. This class uses a caching pattern in some places - * where it doesn't use volatile or synchronized. This is safe to do with ints - * because getting or setting an int is atomic. It's safe to do with a String - * because the internal fields are final and the memory model guarantees other - * threads won't see a partially initialized instance. We are not guaranteed - * that some threads will immediately see changes from other threads on certain - * platforms, but we don't mind if those threads reconstruct the cached result. - * As a result, we get thread safe caching with no concurrency overhead, which - * means the most common case, access from a single thread, is as fast as - * possible. From the Java Language spec.: "17.5 Final Field Semantics ... when - * the object is seen by another thread, that thread will always see the - * correctly constructed version of that object's final fields. It will also see - * versions of any object or array referenced by those final fields that are at - * least as up-to-date as the final fields are." In that same vein, all - * non-transient fields within Uri implementations should be final and immutable - * so as to ensure true immutability for clients even when they don't use proper - * concurrency control. For reference, from RFC 2396: "4.3. Parsing a URI - * Reference A URI reference is typically parsed according to the four main - * components and fragment identifier in order to determine what components are - * present and whether the reference is relative or absolute. The individual - * components are then parsed for their subparts and, if not opaque, to verify - * their validity. Although the BNF defines what is allowed in each component, - * it is ambiguous in terms of differentiating between an authority component - * and a path component that begins with two slash characters. The greedy - * algorithm is used for disambiguation: the left-most matching rule soaks up as - * much of the URI reference string as it is capable of matching. In other - * words, the authority component wins." The "four main components" of a - * hierarchical URI consist of ://? - */ - - /** - * NOTE: EMPTY accesses this field during its own initialization, so this field - * *must* be initialized first, or else EMPTY will see a null value! - * - * Placeholder for strings which haven't been cached. This enables us to cache - * null. We intentionally create a new String instance so we can compare its - * identity and there is no chance we will confuse it with user data. - */ - - /** - * Returns true if this URI is hierarchical like "http://google.com". Absolute - * URIs are hierarchical if the scheme-specific part starts with a '/'. Relative - * URIs are always hierarchical. - */ - public abstract boolean isHierarchical(); - - /** - * Returns true if this URI is opaque like "mailto:nobody@google.com". The - * scheme-specific part of an opaque URI cannot start with a '/'. - */ - public boolean isOpaque() { - return false; - } - - /** - * Returns true if this URI is relative, i.e. if it doesn't contain an - * explicit scheme. - * - * @return true if this URI is relative, false if it's absolute - */ - public abstract boolean isRelative(); - - /** - * Returns true if this URI is absolute, i.e. if it contains an explicit - * scheme. - * - * @return true if this URI is absolute, false if it's relative - */ - public boolean isAbsolute() { - return !isRelative(); - } - - /** - * Gets the scheme of this URI. Example: "http" - * - * @return the scheme or null if this is a relative URI - */ - public abstract String getScheme(); - - /** - * Gets the scheme-specific part of this URI, i.e. everything between the - * scheme separator ':' and the fragment separator '#'. If this is a relative - * URI, this method returns the entire URI. Decodes escaped octets. - * - *

    - * Example: "//www.google.com/search?q=android" - * - * @return the decoded scheme-specific-part - */ - public abstract String getSchemeSpecificPart(); - - /** - * Gets the scheme-specific part of this URI, i.e. everything between the - * scheme separator ':' and the fragment separator '#'. If this is a relative - * URI, this method returns the entire URI. Leaves escaped octets intact. - * - *

    - * Example: "//www.google.com/search?q=android" - * - * @return the decoded scheme-specific-part - */ - public abstract String getEncodedSchemeSpecificPart(); - - /** - * Gets the decoded authority part of this URI. For server addresses, the - * authority is structured as follows: - * {@code [ userinfo '@' ] host [ ':' port ]} - * - *

    - * Examples: "google.com", "bob@google.com:80" - * - * @return the authority for this URI or null if not present - */ - public abstract String getAuthority(); - - /** - * Gets the encoded authority part of this URI. For server addresses, the - * authority is structured as follows: - * {@code [ userinfo '@' ] host [ ':' port ]} - * - *

    - * Examples: "google.com", "bob@google.com:80" - * - * @return the authority for this URI or null if not present - */ - public abstract String getEncodedAuthority(); - - /** - * Gets the decoded user information from the authority. For example, if the - * authority is "nobody@google.com", this method will return "nobody". - * - * @return the user info for this URI or null if not present - */ - public abstract String getUserInfo(); - - /** - * Gets the encoded user information from the authority. For example, if the - * authority is "nobody@google.com", this method will return "nobody". - * - * @return the user info for this URI or null if not present - */ - public abstract String getEncodedUserInfo(); - - /** - * Gets the encoded host from the authority for this URI. For example, if the - * authority is "bob@google.com", this method will return "google.com". - * - * @return the host for this URI or null if not present - */ - public abstract String getHost(); - - /** - * Gets the port from the authority for this URI. For example, if the authority - * is "google.com:80", this method will return 80. - * - * @return the port for this URI or -1 if invalid or not present - */ - public abstract int getPort(); - - /** - * Gets the decoded path. - * - * @return the decoded path, or null if this is not a hierarchical URI (like - * "mailto:nobody@google.com") or the URI is invalid - */ - public abstract String getPath(); - - /** - * Gets the encoded path. - * - * @return the encoded path, or null if this is not a hierarchical URI (like - * "mailto:nobody@google.com") or the URI is invalid - */ - public abstract String getEncodedPath(); - - /** - * Gets the decoded query component from this URI. The query comes after the - * query separator ('?') and before the fragment separator ('#'). This method - * would return "q=android" for "http://www.google.com/search?q=android". - * - * @return the decoded query or null if there isn't one - */ - public abstract String getQuery(); - - /** - * Gets the encoded query component from this URI. The query comes after the - * query separator ('?') and before the fragment separator ('#'). This method - * would return "q=android" for "http://www.google.com/search?q=android". - * - * @return the encoded query or null if there isn't one - */ - public abstract String getEncodedQuery(); - - /** - * Gets the decoded fragment part of this URI, everything after the '#'. - * - * @return the decoded fragment or null if there isn't one - */ - public abstract String getFragment(); - - /** - * Gets the encoded fragment part of this URI, everything after the '#'. - * - * @return the encoded fragment or null if there isn't one - */ - public abstract String getEncodedFragment(); - - /** - * Gets the decoded path segments. - * - * @return decoded path segments, each without a leading or trailing '/' - */ +abstract public class Uri implements Comparable, Parcelable +{ + public List getQueryParameters(String p0){ return null; } + public Set getQueryParameterNames(){ return null; } + public String getQueryParameter(String p0){ return null; } + public Uri normalizeScheme(){ return null; } public abstract List getPathSegments(); - - /** - * Gets the decoded last segment in the path. - * - * @return the decoded last segment or null if the path is empty - */ + public abstract String getAuthority(); + public abstract String getEncodedAuthority(); + public abstract String getEncodedFragment(); + public abstract String getEncodedPath(); + public abstract String getEncodedQuery(); + public abstract String getEncodedSchemeSpecificPart(); + public abstract String getEncodedUserInfo(); + public abstract String getFragment(); + public abstract String getHost(); public abstract String getLastPathSegment(); - - /** - * Compares this Uri to another object for equality. Returns true if the encoded - * string representations of this Uri and the given Uri are equal. Case counts. - * Paths are not normalized. If one Uri specifies a default port explicitly and - * the other leaves it implicit, they will not be considered equal. - */ - public boolean equals(Object o) { - return false; - } - - /** - * Hashes the encoded string represention of this Uri consistently with - * {@link #equals(Object)}. - */ - public int hashCode() { - return -1; - } - - /** - * Compares the string representation of this Uri with that of another. - */ - public int compareTo(Uri other) { - return -1; - } - - /** - * Returns the encoded string representation of this URI. Example: - * "http://google.com/" - */ + public abstract String getPath(); + public abstract String getQuery(); + public abstract String getScheme(); + public abstract String getSchemeSpecificPart(); + public abstract String getUserInfo(); public abstract String toString(); - - /** - * Return a string representation of the URI that is safe to print to logs and - * other places where PII should be avoided. - * - * @hide - */ - public String toSafeString() { - return null; + public abstract Uri.Builder buildUpon(); + public abstract boolean isHierarchical(); + public abstract boolean isRelative(); + public abstract int getPort(); + public boolean equals(Object p0){ return false; } + public boolean getBooleanQueryParameter(String p0, boolean p1){ return false; } + public boolean isAbsolute(){ return false; } + public boolean isOpaque(){ return false; } + public int compareTo(Uri p0){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static String decode(String p0){ return null; } + public static String encode(String p0){ return null; } + public static String encode(String p0, String p1){ return null; } + public static Uri EMPTY = null; + public static Uri fromFile(File p0){ return null; } + public static Uri fromParts(String p0, String p1, String p2){ return null; } + public static Uri parse(String p0){ return null; } + public static Uri withAppendedPath(Uri p0, String p1){ return null; } + public static void writeToParcel(Parcel p0, Uri p1){} + static public class Builder + { + public Builder(){} + public String toString(){ return null; } + public Uri build(){ return null; } + public Uri.Builder appendEncodedPath(String p0){ return null; } + public Uri.Builder appendPath(String p0){ return null; } + public Uri.Builder appendQueryParameter(String p0, String p1){ return null; } + public Uri.Builder authority(String p0){ return null; } + public Uri.Builder clearQuery(){ return null; } + public Uri.Builder encodedAuthority(String p0){ return null; } + public Uri.Builder encodedFragment(String p0){ return null; } + public Uri.Builder encodedOpaquePart(String p0){ return null; } + public Uri.Builder encodedPath(String p0){ return null; } + public Uri.Builder encodedQuery(String p0){ return null; } + public Uri.Builder fragment(String p0){ return null; } + public Uri.Builder opaquePart(String p0){ return null; } + public Uri.Builder path(String p0){ return null; } + public Uri.Builder query(String p0){ return null; } + public Uri.Builder scheme(String p0){ return null; } } - - /** - * Creates a Uri which parses the given encoded URI string. - * - * @param uriString an RFC 2396-compliant, encoded URI - * @throws NullPointerException if uriString is null - * @return Uri for this given uri string - */ - public static Uri parse(String uriString) { - return null; - } - - /** - * Creates a Uri from a file. The URI has the form "file://". - * Encodes path characters with the exception of '/'. - * - *

    - * Example: "file:///tmp/android.txt" - * - * @throws NullPointerException if file is null - * @return a Uri for the given file - */ - public static Uri fromFile(File file) { - return null; - } - - /** - * Creates an opaque Uri from the given components. Encodes the ssp which means - * this method cannot be used to create hierarchical URIs. - * - * @param scheme of the URI - * @param ssp scheme-specific-part, everything between the scheme separator - * (':') and the fragment separator ('#'), which will get - * encoded - * @param fragment fragment, everything after the '#', null if undefined, will - * get encoded - * - * @throws NullPointerException if scheme or ssp is null - * @return Uri composed of the given scheme, ssp, and fragment - * - * @see Builder if you don't want the ssp and fragment to be encoded - */ - public static Uri fromParts(String scheme, String ssp, String fragment) { - return null; - } - - /** - * Returns a set of the unique names of all query parameters. Iterating over the - * set will return the names in order of their first occurrence. - * - * @throws UnsupportedOperationException if this isn't a hierarchical URI - * - * @return a set of decoded names - */ - public Set getQueryParameterNames() { - return null; - } - - /** - * Searches the query string for parameter values with the given key. - * - * @param key which will be encoded - * - * @throws UnsupportedOperationException if this isn't a hierarchical URI - * @throws NullPointerException if key is null - * @return a list of decoded values - */ - public List getQueryParameters(String key) { - return null; - } - - /** - * Searches the query string for the first value with the given key. - * - *

    - * Warning: Prior to Jelly Bean, this decoded the '+' character - * as '+' rather than ' '. - * - * @param key which will be encoded - * @throws UnsupportedOperationException if this isn't a hierarchical URI - * @throws NullPointerException if key is null - * @return the decoded value or null if no parameter is found - */ - public String getQueryParameter(String key) { - return null; - } - - /** - * Searches the query string for the first value with the given key and - * interprets it as a boolean value. "false" and "0" are interpreted as - * false, everything else is interpreted as true. - * - * @param key which will be decoded - * @param defaultValue the default value to return if there is no query - * parameter for key - * @return the boolean interpretation of the query parameter key - */ - public boolean getBooleanQueryParameter(String key, boolean defaultValue) { - return false; - } - - /** - * Return an equivalent URI with a lowercase scheme component. This aligns the - * Uri with Android best practices for intent filtering. - * - *

    - * For example, "HTTP://www.android.com" becomes "http://www.android.com" - * - *

    - * All URIs received from outside Android (such as user input, or external - * sources like Bluetooth, NFC, or the Internet) should be normalized before - * they are used to create an Intent. - * - *

    - * This method does not validate bad URI's, or 'fix' poorly formatted - * URI's - so do not use it for input validation. A Uri will always be returned, - * even if the Uri is badly formatted to begin with and a scheme component - * cannot be found. - * - * @return normalized Uri (never null) - * @see android.content.Intent#setData - * @see android.content.Intent#setDataAndNormalize - */ - public Uri normalizeScheme() { - return null; - } - - /** - * Writes a Uri to a Parcel. - * - * @param out parcel to write to - * @param uri to write, can be null - */ - public static void writeToParcel(Parcel out, Uri uri) { - } - - /** - * Encodes characters in the given string as '%'-escaped octets using the UTF-8 - * scheme. Leaves letters ("A-Z", "a-z"), numbers ("0-9"), and unreserved - * characters ("_-!.~'()*") intact. Encodes all other characters. - * - * @param s string to encode - * @return an encoded version of s suitable for use as a URI component, or null - * if s is null - */ - public static String encode(String s) { - return null; - } - - /** - * Encodes characters in the given string as '%'-escaped octets using the UTF-8 - * scheme. Leaves letters ("A-Z", "a-z"), numbers ("0-9"), and unreserved - * characters ("_-!.~'()*") intact. Encodes all other characters with the - * exception of those specified in the allow argument. - * - * @param s string to encode - * @param allow set of additional characters to allow in the encoded form, null - * if no characters should be skipped - * @return an encoded version of s suitable for use as a URI component, or null - * if s is null - */ - public static String encode(String s, String allow) { - return null; - } - - /** - * Decodes '%'-escaped octets in the given string using the UTF-8 scheme. - * Replaces invalid octets with the unicode replacement character ("\\uFFFD"). - * - * @param s encoded string to decode - * @return the given string with escaped octets decoded, or null if s is null - */ - public static String decode(String s) { - return null; - } - - /** - * Creates a new Uri by appending an already-encoded path segment to a base Uri. - * - * @param baseUri Uri to append path segment to - * @param pathSegment encoded path segment to append - * @return a new Uri based on baseUri with the given segment appended to the - * path - * @throws NullPointerException if baseUri is null - */ - public static Uri withAppendedPath(Uri baseUri, String pathSegment) { - return null; - } - - /** - * If this {@link Uri} is {@code file://}, then resolve and return its canonical - * path. Also fixes legacy emulated storage paths so they are usable across user - * boundaries. Should always be called from the app process before sending - * elsewhere. - * - * @hide - */ - public Uri getCanonicalUri() { - return null; - } - - /** - * If this is a {@code file://} Uri, it will be reported to {@link StrictMode}. - * - * @hide - */ - public void checkFileUriExposed(String location) { - } - - /** - * If this is a {@code content://} Uri without access flags, it will be reported - * to {@link StrictMode}. - * - * @hide - */ - public void checkContentUriWithoutPermission(String location, int flags) { - } - - /** - * Test if this is a path prefix match against the given Uri. Verifies that - * scheme, authority, and atomic path segments match. - * - * @hide - */ - public boolean isPathPrefixMatch(Uri prefix) { - return false; - } - - public Builder buildUpon() { return null; } - - /** - * Helper class for building or manipulating URI references. Not safe for - * concurrent use. - * - *

    An absolute hierarchical URI reference follows the pattern: - * {@code ://?#} - * - *

    Relative URI references (which are always hierarchical) follow one - * of two patterns: {@code ?#} - * or {@code //?#} - * - *

    An opaque URI follows this pattern: - * {@code :#} - * - *

    Use {@link Uri#buildUpon()} to obtain a builder representing an existing URI. - */ - public static final class Builder { - /** - * Constructs a new Builder. - */ - public Builder() {} - /** - * Sets the scheme. - * - * @param scheme name or {@code null} if this is a relative Uri - */ - public Builder scheme(String scheme) { - return null; - } - /** - * Encodes and sets the given opaque scheme-specific-part. - * - * @param opaquePart decoded opaque part - */ - public Builder opaquePart(String opaquePart) { - return null; - } - /** - * Sets the previously encoded opaque scheme-specific-part. - * - * @param opaquePart encoded opaque part - */ - public Builder encodedOpaquePart(String opaquePart) { - return null; - } - /** - * Encodes and sets the authority. - */ - public Builder authority(String authority) { - return null; - } - /** - * Sets the previously encoded authority. - */ - public Builder encodedAuthority(String authority) { - return null; - } - /** - * Sets the path. Leaves '/' characters intact but encodes others as - * necessary. - * - *

    If the path is not null and doesn't start with a '/', and if - * you specify a scheme and/or authority, the builder will prepend the - * given path with a '/'. - */ - public Builder path(String path) { - return null; - } - /** - * Sets the previously encoded path. - * - *

    If the path is not null and doesn't start with a '/', and if - * you specify a scheme and/or authority, the builder will prepend the - * given path with a '/'. - */ - public Builder encodedPath(String path) { - return null; - } - /** - * Encodes the given segment and appends it to the path. - */ - public Builder appendPath(String newSegment) { - return null; - } - /** - * Appends the given segment to the path. - */ - public Builder appendEncodedPath(String newSegment) { - return null; - } - /** - * Encodes and sets the query. - */ - public Builder query(String query) { - return null; - } - /** - * Sets the previously encoded query. - */ - public Builder encodedQuery(String query) { - return null; - } - /** - * Encodes and sets the fragment. - */ - public Builder fragment(String fragment) { - return null; - } - /** - * Sets the previously encoded fragment. - */ - public Builder encodedFragment(String fragment) { - return null; - } - /** - * Encodes the key and value and then appends the parameter to the - * query string. - * - * @param key which will be encoded - * @param value which will be encoded - */ - public Builder appendQueryParameter(String key, String value) { - return null; - } - /** - * Clears the the previously set query. - */ - public Builder clearQuery() { - return null; - } - /** - * Constructs a Uri with the current attributes. - * - * @throws UnsupportedOperationException if the URI is opaque and the - * scheme is null - */ - public Uri build() { - return null; - } - @Override - public String toString() { - return null; - } - } - } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/AsyncTask.java b/java/ql/test/stubs/google-android-9.0.0/android/os/AsyncTask.java new file mode 100644 index 00000000000..23ca3179bbc --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/AsyncTask.java @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.os; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; + +/** + *

    AsyncTask enables proper and easy use of the UI thread. This class allows you + * to perform background operations and publish results on the UI thread without + * having to manipulate threads and/or handlers.

    + * + *

    AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler} + * and does not constitute a generic threading framework. AsyncTasks should ideally be + * used for short operations (a few seconds at the most.) If you need to keep threads + * running for long periods of time, it is highly recommended you use the various APIs + * provided by the java.util.concurrent package such as {@link Executor}, + * {@link ThreadPoolExecutor} and {@link FutureTask}.

    + * + *

    An asynchronous task is defined by a computation that runs on a background thread and + * whose result is published on the UI thread. An asynchronous task is defined by 3 generic + * types, called Params, Progress and Result, + * and 4 steps, called onPreExecute, doInBackground, + * onProgressUpdate and onPostExecute.

    + * + *
    + *

    Developer Guides

    + *

    For more information about using tasks and threads, read the + * Processes and + * Threads developer guide.

    + *
    + * + *

    Usage

    + *

    AsyncTask must be subclassed to be used. The subclass will override at least + * one method ({@link #doInBackground}), and most often will override a + * second one ({@link #onPostExecute}.)

    + * + *

    Here is an example of subclassing:

    + *
    + * private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
    + *     protected Long doInBackground(URL... urls) {
    + *         int count = urls.length;
    + *         long totalSize = 0;
    + *         for (int i = 0; i < count; i++) {
    + *             totalSize += Downloader.downloadFile(urls[i]);
    + *             publishProgress((int) ((i / (float) count) * 100));
    + *             // Escape early if cancel() is called
    + *             if (isCancelled()) break;
    + *         }
    + *         return totalSize;
    + *     }
    + *
    + *     protected void onProgressUpdate(Integer... progress) {
    + *         setProgressPercent(progress[0]);
    + *     }
    + *
    + *     protected void onPostExecute(Long result) {
    + *         showDialog("Downloaded " + result + " bytes");
    + *     }
    + * }
    + * 
    + * + *

    Once created, a task is executed very simply:

    + *
    + * new DownloadFilesTask().execute(url1, url2, url3);
    + * 
    + * + *

    AsyncTask's generic types

    + *

    The three types used by an asynchronous task are the following:

    + *
      + *
    1. Params, the type of the parameters sent to the task upon + * execution.
    2. + *
    3. Progress, the type of the progress units published during + * the background computation.
    4. + *
    5. Result, the type of the result of the background + * computation.
    6. + *
    + *

    Not all types are always used by an asynchronous task. To mark a type as unused, + * simply use the type {@link Void}:

    + *
    + * private class MyTask extends AsyncTask<Void, Void, Void> { ... }
    + * 
    + * + *

    The 4 steps

    + *

    When an asynchronous task is executed, the task goes through 4 steps:

    + *
      + *
    1. {@link #onPreExecute()}, invoked on the UI thread before the task + * is executed. This step is normally used to setup the task, for instance by + * showing a progress bar in the user interface.
    2. + *
    3. {@link #doInBackground}, invoked on the background thread + * immediately after {@link #onPreExecute()} finishes executing. This step is used + * to perform background computation that can take a long time. The parameters + * of the asynchronous task are passed to this step. The result of the computation must + * be returned by this step and will be passed back to the last step. This step + * can also use {@link #publishProgress} to publish one or more units + * of progress. These values are published on the UI thread, in the + * {@link #onProgressUpdate} step.
    4. + *
    5. {@link #onProgressUpdate}, invoked on the UI thread after a + * call to {@link #publishProgress}. The timing of the execution is + * undefined. This method is used to display any form of progress in the user + * interface while the background computation is still executing. For instance, + * it can be used to animate a progress bar or show logs in a text field.
    6. + *
    7. {@link #onPostExecute}, invoked on the UI thread after the background + * computation finishes. The result of the background computation is passed to + * this step as a parameter.
    8. + *
    + * + *

    Cancelling a task

    + *

    A task can be cancelled at any time by invoking {@link #cancel(boolean)}. Invoking + * this method will cause subsequent calls to {@link #isCancelled()} to return true. + * After invoking this method, {@link #onCancelled(Object)}, instead of + * {@link #onPostExecute(Object)} will be invoked after {@link #doInBackground(Object[])} + * returns. To ensure that a task is cancelled as quickly as possible, you should always + * check the return value of {@link #isCancelled()} periodically from + * {@link #doInBackground(Object[])}, if possible (inside a loop for instance.)

    + * + *

    Threading rules

    + *

    There are a few threading rules that must be followed for this class to + * work properly:

    + *
      + *
    • The AsyncTask class must be loaded on the UI thread. This is done + * automatically as of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}.
    • + *
    • The task instance must be created on the UI thread.
    • + *
    • {@link #execute} must be invoked on the UI thread.
    • + *
    • Do not call {@link #onPreExecute()}, {@link #onPostExecute}, + * {@link #doInBackground}, {@link #onProgressUpdate} manually.
    • + *
    • The task can be executed only once (an exception will be thrown if + * a second execution is attempted.)
    • + *
    + * + *

    Memory observability

    + *

    AsyncTask guarantees that all callback calls are synchronized in such a way that the following + * operations are safe without explicit synchronizations.

    + *
      + *
    • Set member fields in the constructor or {@link #onPreExecute}, and refer to them + * in {@link #doInBackground}. + *
    • Set member fields in {@link #doInBackground}, and refer to them in + * {@link #onProgressUpdate} and {@link #onPostExecute}. + *
    + * + *

    Order of execution

    + *

    When first introduced, AsyncTasks were executed serially on a single background + * thread. Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed + * to a pool of threads allowing multiple tasks to operate in parallel. Starting with + * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are executed on a single + * thread to avoid common application errors caused by parallel execution.

    + *

    If you truly want parallel execution, you can invoke + * {@link #executeOnExecutor(java.util.concurrent.Executor, Object[])} with + * {@link #THREAD_POOL_EXECUTOR}.

    + */ +public abstract class AsyncTask { + /** + * Creates a new asynchronous task. This constructor must be invoked on the UI thread. + */ + public AsyncTask() { + } + + /** + * Override this method to perform a computation on a background thread. The + * specified parameters are the parameters passed to {@link #execute} + * by the caller of this task. + * + * This method can call {@link #publishProgress} to publish updates + * on the UI thread. + * + * @param params The parameters of the task. + * + * @return A result, defined by the subclass of this task. + * + * @see #onPreExecute() + * @see #onPostExecute + * @see #publishProgress + */ + protected abstract Result doInBackground(Params... params); + + /** + * Runs on the UI thread before {@link #doInBackground}. + * + * @see #onPostExecute + * @see #doInBackground + */ + protected void onPreExecute() { + } + + /** + *

    Runs on the UI thread after {@link #doInBackground}. The + * specified result is the value returned by {@link #doInBackground}.

    + * + *

    This method won't be invoked if the task was cancelled.

    + * + * @param result The result of the operation computed by {@link #doInBackground}. + * + * @see #onPreExecute + * @see #doInBackground + * @see #onCancelled(Object) + */ + protected void onPostExecute(Result result) { + } + + /** + * Runs on the UI thread after {@link #publishProgress} is invoked. + * The specified values are the values passed to {@link #publishProgress}. + * + * @param values The values indicating progress. + * + * @see #publishProgress + * @see #doInBackground + */ + protected void onProgressUpdate(Progress... values) { + } + + /** + *

    Runs on the UI thread after {@link #cancel(boolean)} is invoked and + * {@link #doInBackground(Object[])} has finished.

    + * + *

    The default implementation simply invokes {@link #onCancelled()} and + * ignores the result. If you write your own implementation, do not call + * super.onCancelled(result).

    + * + * @param result The result, if any, computed in + * {@link #doInBackground(Object[])}, can be null + * + * @see #cancel(boolean) + * @see #isCancelled() + */ + protected void onCancelled(Result result) { + onCancelled(); + } + + /** + *

    Applications should preferably override {@link #onCancelled(Object)}. + * This method is invoked by the default implementation of + * {@link #onCancelled(Object)}.

    + * + *

    Runs on the UI thread after {@link #cancel(boolean)} is invoked and + * {@link #doInBackground(Object[])} has finished.

    + * + * @see #onCancelled(Object) + * @see #cancel(boolean) + * @see #isCancelled() + */ + protected void onCancelled() { + } + + /** + * Returns true if this task was cancelled before it completed + * normally. If you are calling {@link #cancel(boolean)} on the task, + * the value returned by this method should be checked periodically from + * {@link #doInBackground(Object[])} to end the task as soon as possible. + * + * @return true if task was cancelled before it completed + * + * @see #cancel(boolean) + */ + public final boolean isCancelled() { + return false; + } + + /** + *

    Attempts to cancel execution of this task. This attempt will + * fail if the task has already completed, already been cancelled, + * or could not be cancelled for some other reason. If successful, + * and this task has not started when cancel is called, + * this task should never run. If the task has already started, + * then the mayInterruptIfRunning parameter determines + * whether the thread executing this task should be interrupted in + * an attempt to stop the task.

    + * + *

    Calling this method will result in {@link #onCancelled(Object)} being + * invoked on the UI thread after {@link #doInBackground(Object[])} + * returns. Calling this method guarantees that {@link #onPostExecute(Object)} + * is never invoked. After invoking this method, you should check the + * value returned by {@link #isCancelled()} periodically from + * {@link #doInBackground(Object[])} to finish the task as early as + * possible.

    + * + * @param mayInterruptIfRunning true if the thread executing this + * task should be interrupted; otherwise, in-progress tasks are allowed + * to complete. + * + * @return false if the task could not be cancelled, + * typically because it has already completed normally; + * true otherwise + * + * @see #isCancelled() + * @see #onCancelled(Object) + */ + public final boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + /** + * Waits if necessary for the computation to complete, and then + * retrieves its result. + * + * @return The computed result. + * + * @throws CancellationException If the computation was cancelled. + * @throws ExecutionException If the computation threw an exception. + * @throws InterruptedException If the current thread was interrupted + * while waiting. + */ + public final Result get() throws InterruptedException, ExecutionException { + return null; + } + + /** + * Executes the task with the specified parameters. The task returns + * itself (this) so that the caller can keep a reference to it. + * + *

    Note: this function schedules the task on a queue for a single background + * thread or pool of threads depending on the platform version. When first + * introduced, AsyncTasks were executed serially on a single background thread. + * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed + * to a pool of threads allowing multiple tasks to operate in parallel. Starting + * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being + * executed on a single thread to avoid common application errors caused + * by parallel execution. If you truly want parallel execution, you can use + * the {@link #executeOnExecutor} version of this method + * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings + * on its use. + * + *

    This method must be invoked on the UI thread. + * + * @param params The parameters of the task. + * + * @return This instance of AsyncTask. + * + * @throws IllegalStateException If {@link #getStatus()} returns either + * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}. + * + * @see #executeOnExecutor(java.util.concurrent.Executor, Object[]) + * @see #execute(Runnable) + */ + public final AsyncTask execute(Params... params) { + return null; + } + + /** + * Executes the task with the specified parameters. The task returns + * itself (this) so that the caller can keep a reference to it. + * + *

    This method is typically used with {@link #THREAD_POOL_EXECUTOR} to + * allow multiple tasks to run in parallel on a pool of threads managed by + * AsyncTask, however you can also use your own {@link Executor} for custom + * behavior. + * + *

    Warning: Allowing multiple tasks to run in parallel from + * a thread pool is generally not what one wants, because the order + * of their operation is not defined. For example, if these tasks are used + * to modify any state in common (such as writing a file due to a button click), + * there are no guarantees on the order of the modifications. + * Without careful work it is possible in rare cases for the newer version + * of the data to be over-written by an older one, leading to obscure data + * loss and stability issues. Such changes are best + * executed in serial; to guarantee such work is serialized regardless of + * platform version you can use this function with {@link #SERIAL_EXECUTOR}. + * + *

    This method must be invoked on the UI thread. + * + * @param exec The executor to use. {@link #THREAD_POOL_EXECUTOR} is available as a + * convenient process-wide thread pool for tasks that are loosely coupled. + * @param params The parameters of the task. + * + * @return This instance of AsyncTask. + * + * @throws IllegalStateException If {@link #getStatus()} returns either + * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}. + * + * @see #execute(Object[]) + */ + public final AsyncTask executeOnExecutor(Executor exec, + Params... params) { + return null; + } + + /** + * This method can be invoked from {@link #doInBackground} to + * publish updates on the UI thread while the background computation is + * still running. Each call to this method will trigger the execution of + * {@link #onProgressUpdate} on the UI thread. + * + * {@link #onProgressUpdate} will not be called if the task has been + * canceled. + * + * @param values The progress values to update the UI with. + * + * @see #onProgressUpdate + * @see #doInBackground + */ + protected final void publishProgress(Progress... values) { + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/BaseBundle.java b/java/ql/test/stubs/google-android-9.0.0/android/os/BaseBundle.java index 3ac33642ab4..3cc3c310c38 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/os/BaseBundle.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/BaseBundle.java @@ -1,832 +1,43 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Generated automatically from android.os.BaseBundle for testing purposes + package android.os; -import java.io.Serializable; -import java.util.ArrayList; +import android.os.PersistableBundle; import java.util.Set; -/** - * A mapping from String keys to values of various types. In most cases, you - * should work directly with either the {@link Bundle} or - * {@link PersistableBundle} subclass. - */ -public class BaseBundle { - /** - * Constructs a new, empty Bundle that uses a specific ClassLoader for - * instantiating Parcelable and Serializable objects. - * - * @param loader An explicit ClassLoader to use when instantiating objects - * inside of the Bundle. - * @param capacity Initial size of the ArrayMap. - */ - BaseBundle(ClassLoader loader, int capacity) { - } - - /** - * Constructs a new, empty Bundle. - */ - BaseBundle() { - } - - /** - * Constructs a Bundle whose data is stored as a Parcel. The data will be - * unparcelled on first contact, using the assigned ClassLoader. - * - * @param parcelledData a Parcel containing a Bundle - */ - BaseBundle(Parcel parcelledData) { - } - - BaseBundle(Parcel parcelledData, int length) { - } - - /** - * Constructs a new, empty Bundle that uses a specific ClassLoader for - * instantiating Parcelable and Serializable objects. - * - * @param loader An explicit ClassLoader to use when instantiating objects - * inside of the Bundle. - */ - BaseBundle(ClassLoader loader) { - } - - /** - * Constructs a new, empty Bundle sized to hold the given number of elements. - * The Bundle will grow as needed. - * - * @param capacity the initial capacity of the Bundle - */ - BaseBundle(int capacity) { - } - - /** - * Constructs a Bundle containing a copy of the mappings from the given Bundle. - * - * @param b a Bundle to be copied. - */ - BaseBundle(BaseBundle b) { - } - - /** - * Special constructor that does not initialize the bundle. - */ - BaseBundle(boolean doInit) { - } - - /** - * TODO: optimize this later (getting just the value part of a Bundle with a - * single pair) once Bundle.forPair() above is implemented with a special - * single-value Map implementation/serialization. - * - * Note: value in single-pair Bundle may be null. - * - * @hide - */ - public String getPairValue() { - return null; - } - - /** - * Changes the ClassLoader this Bundle uses when instantiating objects. - * - * @param loader An explicit ClassLoader to use when instantiating objects - * inside of the Bundle. - */ - void setClassLoader(ClassLoader loader) { - } - - /** - * Return the ClassLoader currently associated with this Bundle. - */ - ClassLoader getClassLoader() { - return null; - } - - /** - * @hide - */ - public boolean isParcelled() { - return false; - } - - /** - * @hide - */ - public boolean isEmptyParcel() { - return false; - } - - /** - * Returns true if the given key is contained in the mapping - * of this Bundle. - * - * @param key a String key - * @return true if the key is part of the mapping, false otherwise - */ - public boolean containsKey(String key) { - return false; - } - - /** - * Returns the entry with the given key as an object. - * - * @param key a String key - * @return an Object, or null - */ - public Object get(String key) { - return null; - } - - /** - * Removes any entry with the given key from the mapping of this Bundle. - * - * @param key a String key - */ - public void remove(String key) { - } - - /** {@hide} */ - public void putObject(String key, Object value) { - } - - /** - * Inserts a Boolean value into the mapping of this Bundle, replacing - * any existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value a boolean - */ - public void putBoolean(String key, boolean value) { - } - - /** - * Inserts a byte value into the mapping of this Bundle, replacing - * any existing value for the given key. - * - * @param key a String, or null - * @param value a byte - */ - void putByte(String key, byte value) { - } - - /** - * Inserts a char value into the mapping of this Bundle, replacing - * any existing value for the given key. - * - * @param key a String, or null - * @param value a char - */ - void putChar(String key, char value) { - } - - /** - * Inserts a short value into the mapping of this Bundle, replacing - * any existing value for the given key. - * - * @param key a String, or null - * @param value a short - */ - void putShort(String key, short value) { - } - - /** - * Inserts an int value into the mapping of this Bundle, replacing - * any existing value for the given key. - * - * @param key a String, or null - * @param value an int - */ - public void putInt(String key, int value) { - } - - /** - * Inserts a long value into the mapping of this Bundle, replacing - * any existing value for the given key. - * - * @param key a String, or null - * @param value a long - */ - public void putLong(String key, long value) { - } - - /** - * Inserts a float value into the mapping of this Bundle, replacing - * any existing value for the given key. - * - * @param key a String, or null - * @param value a float - */ - void putFloat(String key, float value) { - } - - /** - * Inserts a double value into the mapping of this Bundle, replacing - * any existing value for the given key. - * - * @param key a String, or null - * @param value a double - */ - public void putDouble(String key, double value) { - } - - /** - * Inserts a String value into the mapping of this Bundle, replacing - * any existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value a String, or null - */ - public void putString(String key, String value) { - } - - /** - * Inserts a CharSequence value into the mapping of this Bundle, replacing - * any existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value a CharSequence, or null - */ - void putCharSequence(String key, CharSequence value) { - } - - /** - * Inserts an ArrayList value into the mapping of this Bundle, replacing - * any existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value an ArrayList object, or null - */ - void putIntegerArrayList(String key, ArrayList value) { - } - - /** - * Inserts an ArrayList value into the mapping of this Bundle, replacing - * any existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value an ArrayList object, or null - */ - void putStringArrayList(String key, ArrayList value) { - } - - - /** - * Inserts an ArrayList value into the mapping of this Bundle, - * replacing any existing value for the given key. Either key or value may be - * null. - * - * @param key a String, or null - * @param value an ArrayList object, or null - */ - void putCharSequenceArrayList(String key, ArrayList value) { - } - - /** - * Inserts a Serializable value into the mapping of this Bundle, replacing any - * existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value a Serializable object, or null - */ - void putSerializable(String key, Serializable value) { - } - - /** - * Inserts a boolean array value into the mapping of this Bundle, replacing any - * existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value a boolean array object, or null - */ - public void putBooleanArray(String key, boolean[] value) { - } - - /** - * Inserts a byte array value into the mapping of this Bundle, replacing any - * existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value a byte array object, or null - */ - void putByteArray(String key, byte[] value) { - } - - /** - * Inserts a short array value into the mapping of this Bundle, replacing any - * existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value a short array object, or null - */ - void putShortArray(String key, short[] value) { - } - - /** - * Inserts a char array value into the mapping of this Bundle, replacing any - * existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value a char array object, or null - */ - void putCharArray(String key, char[] value) { - } - - /** - * Inserts an int array value into the mapping of this Bundle, replacing any - * existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value an int array object, or null - */ - public void putIntArray(String key, int[] value) { - } - - /** - * Inserts a long array value into the mapping of this Bundle, replacing any - * existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value a long array object, or null - */ - public void putLongArray(String key, long[] value) { - } - - /** - * Inserts a float array value into the mapping of this Bundle, replacing any - * existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value a float array object, or null - */ - void putFloatArray(String key, float[] value) { - } - - /** - * Inserts a double array value into the mapping of this Bundle, replacing any - * existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value a double array object, or null - */ - public void putDoubleArray(String key, double[] value) { - } - - /** - * Inserts a String array value into the mapping of this Bundle, replacing any - * existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value a String array object, or null - */ - public void putStringArray(String key, String[] value) { - } - - /** - * Inserts a CharSequence array value into the mapping of this Bundle, replacing - * any existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value a CharSequence array object, or null - */ - void putCharSequenceArray(String key, CharSequence[] value) { - } - - /** - * Returns the value associated with the given key, or false if no mapping of - * the desired type exists for the given key. - * - * @param key a String - * @return a boolean value - */ - public boolean getBoolean(String key) { - return false; - } - - /** - * Returns the value associated with the given key, or defaultValue if no - * mapping of the desired type exists for the given key. - * - * @param key a String - * @param defaultValue Value to return if key does not exist - * @return a boolean value - */ - public boolean getBoolean(String key, boolean defaultValue) { - return false; - } - - /** - * Returns the value associated with the given key, or (byte) 0 if no mapping of - * the desired type exists for the given key. - * - * @param key a String - * @return a byte value - */ - byte getByte(String key) { - return -1; - } - - /** - * Returns the value associated with the given key, or defaultValue if no - * mapping of the desired type exists for the given key. - * - * @param key a String - * @param defaultValue Value to return if key does not exist - * @return a byte value - */ - Byte getByte(String key, byte defaultValue) { - return -1; - } - - /** - * Returns the value associated with the given key, or (char) 0 if no mapping of - * the desired type exists for the given key. - * - * @param key a String - * @return a char value - */ - char getChar(String key) { - return 'a'; - } - - /** - * Returns the value associated with the given key, or defaultValue if no - * mapping of the desired type exists for the given key. - * - * @param key a String - * @param defaultValue Value to return if key does not exist - * @return a char value - */ - char getChar(String key, char defaultValue) { - return 'a'; - } - - /** - * Returns the value associated with the given key, or (short) 0 if no mapping - * of the desired type exists for the given key. - * - * @param key a String - * @return a short value - */ - short getShort(String key) { - return -1; - } - - /** - * Returns the value associated with the given key, or defaultValue if no - * mapping of the desired type exists for the given key. - * - * @param key a String - * @param defaultValue Value to return if key does not exist - * @return a short value - */ - short getShort(String key, short defaultValue) { - return -1; - } - - /** - * Returns the value associated with the given key, or 0 if no mapping of the - * desired type exists for the given key. - * - * @param key a String - * @return an int value - */ - public int getInt(String key) { - return -1; - } - - /** - * Returns the value associated with the given key, or defaultValue if no - * mapping of the desired type exists for the given key. - * - * @param key a String - * @param defaultValue Value to return if key does not exist - * @return an int value - */ - public int getInt(String key, int defaultValue) { - return -1; - } - - /** - * Returns the value associated with the given key, or 0L if no mapping of the - * desired type exists for the given key. - * - * @param key a String - * @return a long value - */ - public long getLong(String key) { - return -1; - } - - /** - * Returns the value associated with the given key, or defaultValue if no - * mapping of the desired type exists for the given key. - * - * @param key a String - * @param defaultValue Value to return if key does not exist - * @return a long value - */ - public long getLong(String key, long defaultValue) { - return -1; - } - - /** - * Returns the value associated with the given key, or 0.0f if no mapping of the - * desired type exists for the given key. - * - * @param key a String - * @return a float value - */ - float getFloat(String key) { - return -1; - } - - /** - * Returns the value associated with the given key, or defaultValue if no - * mapping of the desired type exists for the given key. - * - * @param key a String - * @param defaultValue Value to return if key does not exist - * @return a float value - */ - float getFloat(String key, float defaultValue) { - return -1; - } - - /** - * Returns the value associated with the given key, or 0.0 if no mapping of the - * desired type exists for the given key. - * - * @param key a String - * @return a double value - */ - public double getDouble(String key) { - return -1; - } - - /** - * Returns the value associated with the given key, or defaultValue if no - * mapping of the desired type exists for the given key. - * - * @param key a String - * @param defaultValue Value to return if key does not exist - * @return a double value - */ - public double getDouble(String key, double defaultValue) { - return -1; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return a String value, or null - */ - public String getString(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or defaultValue if no - * mapping of the desired type exists for the given key or if a null value is - * explicitly associated with the given key. - * - * @param key a String, or null - * @param defaultValue Value to return if key does not exist or if a null value - * is associated with the given key. - * @return the String value associated with the given key, or defaultValue if no - * valid String object is currently mapped to that key. - */ - public String getString(String key, String defaultValue) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return a CharSequence value, or null - */ - CharSequence getCharSequence(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or defaultValue if no - * mapping of the desired type exists for the given key or if a null value is - * explicitly associated with the given key. - * - * @param key a String, or null - * @param defaultValue Value to return if key does not exist or if a null value - * is associated with the given key. - * @return the CharSequence value associated with the given key, or defaultValue - * if no valid CharSequence object is currently mapped to that key. - */ - CharSequence getCharSequence(String key, CharSequence defaultValue) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return a Serializable value, or null - */ - Serializable getSerializable(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return an ArrayList value, or null - */ - ArrayList getIntegerArrayList(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return an ArrayList value, or null - */ - ArrayList getStringArrayList(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return an ArrayList value, or null - */ - ArrayList getCharSequenceArrayList(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return a boolean[] value, or null - */ - public boolean[] getBooleanArray(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return a byte[] value, or null - */ - byte[] getByteArray(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return a short[] value, or null - */ - short[] getShortArray(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return a char[] value, or null - */ - char[] getCharArray(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return an int[] value, or null - */ - public int[] getIntArray(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return a long[] value, or null - */ - public long[] getLongArray(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return a float[] value, or null - */ - float[] getFloatArray(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return a double[] value, or null - */ - public double[] getDoubleArray(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return a String[] value, or null - */ - public String[] getStringArray(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return a CharSequence[] value, or null - */ - CharSequence[] getCharSequenceArray(String key) { - return null; - } - - /** - * Writes the Bundle contents to a Parcel, typically in order for it to be - * passed through an IBinder connection. - * - * @param parcel The parcel to copy this bundle to. - */ - void writeToParcelInner(Parcel parcel, int flags) { - } - - /** - * Reads the Parcel contents into this Bundle, typically in order for it to be - * passed through an IBinder connection. - * - * @param parcel The parcel to overwrite this bundle from. - */ - void readFromParcelInner(Parcel parcel) { - } - +public class BaseBundle +{ + public Object get(String p0){ return null; } + public Set keySet(){ return null; } + public String getString(String p0){ return null; } + public String getString(String p0, String p1){ return null; } + public String[] getStringArray(String p0){ return null; } + public boolean containsKey(String p0){ return false; } + public boolean getBoolean(String p0){ return false; } + public boolean getBoolean(String p0, boolean p1){ return false; } + public boolean isEmpty(){ return false; } + public boolean[] getBooleanArray(String p0){ return null; } + public double getDouble(String p0){ return 0; } + public double getDouble(String p0, double p1){ return 0; } + public double[] getDoubleArray(String p0){ return null; } + public int getInt(String p0){ return 0; } + public int getInt(String p0, int p1){ return 0; } + public int size(){ return 0; } + public int[] getIntArray(String p0){ return null; } + public long getLong(String p0){ return 0; } + public long getLong(String p0, long p1){ return 0; } + public long[] getLongArray(String p0){ return null; } + public void clear(){} + public void putAll(PersistableBundle p0){} + public void putBoolean(String p0, boolean p1){} + public void putBooleanArray(String p0, boolean[] p1){} + public void putDouble(String p0, double p1){} + public void putDoubleArray(String p0, double[] p1){} + public void putInt(String p0, int p1){} + public void putIntArray(String p0, int[] p1){} + public void putLong(String p0, long p1){} + public void putLongArray(String p0, long[] p1){} + public void putString(String p0, String p1){} + public void putStringArray(String p0, String[] p1){} + public void remove(String p0){} } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/Bundle.java b/java/ql/test/stubs/google-android-9.0.0/android/os/Bundle.java index 6c074780ee4..4beb1cf5dee 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/os/Bundle.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/Bundle.java @@ -1,499 +1,86 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Generated automatically from android.os.Bundle for testing purposes + package android.os; +import android.os.BaseBundle; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PersistableBundle; +import android.util.Size; +import android.util.SizeF; +import android.util.SparseArray; import java.io.Serializable; import java.util.ArrayList; -import java.util.List; -/** - * A mapping from String keys to various {@link Parcelable} values. - * - * @see PersistableBundle - */ -public final class Bundle extends BaseBundle implements Cloneable, Parcelable { - - /** - * Constructs a new, empty Bundle. - */ - public Bundle() { - super(); - } - - /** - * Removes all elements from the mapping of this Bundle. - */ - public void clear() { - } - - /** - * Removes any entry with the given key from the mapping of this Bundle. - * - * @param key a String key - */ - public void remove(String key) { - } - - /** - * Inserts all mappings from the given Bundle into this Bundle. - * - * @param bundle a Bundle - */ - public void putAll(Bundle bundle) { - } - - /** - * Return the size of {@link #mParcelledData} in bytes if available, otherwise - * {@code 0}. - * - * @hide - */ - public int getSize() { - return -1; - } - - /** - * Inserts a byte value into the mapping of this Bundle, replacing any existing - * value for the given key. - * - * @param key a String, or null - * @param value a byte - */ - public void putByte(String key, byte value) { - } - - /** - * Inserts a char value into the mapping of this Bundle, replacing any existing - * value for the given key. - * - * @param key a String, or null - * @param value a char - */ - public void putChar(String key, char value) { - } - - /** - * Inserts a short value into the mapping of this Bundle, replacing any existing - * value for the given key. - * - * @param key a String, or null - * @param value a short - */ - public void putShort(String key, short value) { - } - - /** - * Inserts a float value into the mapping of this Bundle, replacing any existing - * value for the given key. - * - * @param key a String, or null - * @param value a float - */ - public void putFloat(String key, float value) { - } - - /** - * Inserts a CharSequence value into the mapping of this Bundle, replacing any - * existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value a CharSequence, or null - */ - public void putCharSequence(String key, CharSequence value) { - } - - /** - * Inserts an ArrayList value into the mapping of this Bundle, - * replacing any existing value for the given key. Either key or value may be - * null. - * - * @param key a String, or null - * @param value an ArrayList object, or null - */ - public void putIntegerArrayList(String key, ArrayList value) { - } - - /** - * Inserts an ArrayList value into the mapping of this Bundle, replacing - * any existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value an ArrayList object, or null - */ - public void putStringArrayList(String key, ArrayList value) { - } - - /** - * Inserts an ArrayList value into the mapping of this Bundle, - * replacing any existing value for the given key. Either key or value may be - * null. - * - * @param key a String, or null - * @param value an ArrayList object, or null - */ - public void putCharSequenceArrayList(String key, ArrayList value) { - } - - /** - * Inserts a Serializable value into the mapping of this Bundle, replacing any - * existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value a Serializable object, or null - */ - public void putSerializable(String key, Serializable value) { - } - - /** - * Inserts a byte array value into the mapping of this Bundle, replacing any - * existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value a byte array object, or null - */ - public void putByteArray(String key, byte[] value) { - } - - /** - * Inserts a short array value into the mapping of this Bundle, replacing any - * existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value a short array object, or null - */ - public void putShortArray(String key, short[] value) { - } - - /** - * Inserts a char array value into the mapping of this Bundle, replacing any - * existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value a char array object, or null - */ - public void putCharArray(String key, char[] value) { - } - - /** - * Inserts a float array value into the mapping of this Bundle, replacing any - * existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value a float array object, or null - */ - public void putFloatArray(String key, float[] value) { - } - - /** - * Inserts a CharSequence array value into the mapping of this Bundle, replacing - * any existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value a CharSequence array object, or null - */ - public void putCharSequenceArray(String key, CharSequence[] value) { - } - - /** - * Inserts a Bundle value into the mapping of this Bundle, replacing any - * existing value for the given key. Either key or value may be null. - * - * @param key a String, or null - * @param value a Bundle object, or null - */ - public void putBundle(String key, Bundle value) { - } - - /** - * Returns the value associated with the given key, or (byte) 0 if no mapping of - * the desired type exists for the given key. - * - * @param key a String - * @return a byte value - */ - public byte getByte(String key) { - return -1; - } - - /** - * Returns the value associated with the given key, or defaultValue if no - * mapping of the desired type exists for the given key. - * - * @param key a String - * @param defaultValue Value to return if key does not exist - * @return a byte value - */ - public Byte getByte(String key, byte defaultValue) { - return -1; - } - - /** - * Returns the value associated with the given key, or (char) 0 if no mapping of - * the desired type exists for the given key. - * - * @param key a String - * @return a char value - */ - public char getChar(String key) { - return 'a'; - } - - /** - * Returns the value associated with the given key, or defaultValue if no - * mapping of the desired type exists for the given key. - * - * @param key a String - * @param defaultValue Value to return if key does not exist - * @return a char value - */ - public char getChar(String key, char defaultValue) { - return 'a'; - } - - /** - * Returns the value associated with the given key, or (short) 0 if no mapping - * of the desired type exists for the given key. - * - * @param key a String - * @return a short value - */ - public short getShort(String key) { - return -1; - } - - /** - * Returns the value associated with the given key, or defaultValue if no - * mapping of the desired type exists for the given key. - * - * @param key a String - * @param defaultValue Value to return if key does not exist - * @return a short value - */ - public short getShort(String key, short defaultValue) { - return -1; - } - - /** - * Returns the value associated with the given key, or 0.0f if no mapping of the - * desired type exists for the given key. - * - * @param key a String - * @return a float value - */ - public float getFloat(String key) { - return -1; - } - - /** - * Returns the value associated with the given key, or defaultValue if no - * mapping of the desired type exists for the given key. - * - * @param key a String - * @param defaultValue Value to return if key does not exist - * @return a float value - */ - public float getFloat(String key, float defaultValue) { - return -1; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return a CharSequence value, or null - */ - public CharSequence getCharSequence(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or defaultValue if no - * mapping of the desired type exists for the given key or if a null value is - * explicitly associatd with the given key. - * - * @param key a String, or null - * @param defaultValue Value to return if key does not exist or if a null value - * is associated with the given key. - * @return the CharSequence value associated with the given key, or defaultValue - * if no valid CharSequence object is currently mapped to that key. - */ - public CharSequence getCharSequence(String key, CharSequence defaultValue) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return a Bundle value, or null - */ - public Bundle getBundle(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return an ArrayList value, or null - */ - public ArrayList getParcelableArrayList(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return a Serializable value, or null - */ - public Serializable getSerializable(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return an ArrayList value, or null - */ - public ArrayList getIntegerArrayList(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return an ArrayList value, or null - */ - public ArrayList getStringArrayList(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return an ArrayList value, or null - */ - public ArrayList getCharSequenceArrayList(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return a byte[] value, or null - */ - public byte[] getByteArray(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return a short[] value, or null - */ - public short[] getShortArray(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return a char[] value, or null - */ - public char[] getCharArray(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return a float[] value, or null - */ - public float[] getFloatArray(String key) { - return null; - } - - /** - * Returns the value associated with the given key, or null if no mapping of the - * desired type exists for the given key or a null value is explicitly - * associated with the key. - * - * @param key a String, or null - * @return a CharSequence[] value, or null - */ - public CharSequence[] getCharSequenceArray(String key) { - return null; - } - - /** - * Writes the Bundle contents to a Parcel, typically in order for it to be - * passed through an IBinder connection. - * - * @param parcel The parcel to copy this bundle to. - */ - public void writeToParcel(Parcel parcel, int flags) { - } - - /** - * Reads the Parcel contents into this Bundle, typically in order for it to be - * passed through an IBinder connection. - * - * @param parcel The parcel to overwrite this bundle from. - */ - public void readFromParcel(Parcel parcel) { - } - - public synchronized String toString() { - return null; - } - - /** - * @hide - */ - public synchronized String toShortString() { - return null; - } -} \ No newline at end of file +public class Bundle extends BaseBundle implements Cloneable, Parcelable +{ + public ArrayList getParcelableArrayList(String p0){ return null; } + public SparseArray getSparseParcelableArray(String p0){ return null; } + public T getParcelable(String p0){ return null; } + public ArrayList getCharSequenceArrayList(String p0){ return null; } + public ArrayList getIntegerArrayList(String p0){ return null; } + public ArrayList getStringArrayList(String p0){ return null; } + public Bundle deepCopy(){ return null; } + public Bundle getBundle(String p0){ return null; } + public Bundle(){} + public Bundle(Bundle p0){} + public Bundle(ClassLoader p0){} + public Bundle(PersistableBundle p0){} + public Bundle(int p0){} + public Byte getByte(String p0, byte p1){ return null; } + public CharSequence getCharSequence(String p0){ return null; } + public CharSequence getCharSequence(String p0, CharSequence p1){ return null; } + public CharSequence[] getCharSequenceArray(String p0){ return null; } + public ClassLoader getClassLoader(){ return null; } + public IBinder getBinder(String p0){ return null; } + public Object clone(){ return null; } + public Parcelable[] getParcelableArray(String p0){ return null; } + public Serializable getSerializable(String p0){ return null; } + public Size getSize(String p0){ return null; } + public SizeF getSizeF(String p0){ return null; } + public String toString(){ return null; } + public boolean hasFileDescriptors(){ return false; } + public byte getByte(String p0){ return 0; } + public byte[] getByteArray(String p0){ return null; } + public char getChar(String p0){ return '0'; } + public char getChar(String p0, char p1){ return '0'; } + public char[] getCharArray(String p0){ return null; } + public float getFloat(String p0){ return 0; } + public float getFloat(String p0, float p1){ return 0; } + public float[] getFloatArray(String p0){ return null; } + public int describeContents(){ return 0; } + public short getShort(String p0){ return 0; } + public short getShort(String p0, short p1){ return 0; } + public short[] getShortArray(String p0){ return null; } + public static Bundle EMPTY = null; + public static Parcelable.Creator CREATOR = null; + public void clear(){} + public void putAll(Bundle p0){} + public void putBinder(String p0, IBinder p1){} + public void putBundle(String p0, Bundle p1){} + public void putByte(String p0, byte p1){} + public void putByteArray(String p0, byte[] p1){} + public void putChar(String p0, char p1){} + public void putCharArray(String p0, char[] p1){} + public void putCharSequence(String p0, CharSequence p1){} + public void putCharSequenceArray(String p0, CharSequence[] p1){} + public void putCharSequenceArrayList(String p0, ArrayList p1){} + public void putFloat(String p0, float p1){} + public void putFloatArray(String p0, float[] p1){} + public void putIntegerArrayList(String p0, ArrayList p1){} + public void putParcelable(String p0, Parcelable p1){} + public void putParcelableArray(String p0, Parcelable[] p1){} + public void putParcelableArrayList(String p0, ArrayList p1){} + public void putSerializable(String p0, Serializable p1){} + public void putShort(String p0, short p1){} + public void putShortArray(String p0, short[] p1){} + public void putSize(String p0, Size p1){} + public void putSizeF(String p0, SizeF p1){} + public void putSparseParcelableArray(String p0, SparseArray p1){} + public void putStringArrayList(String p0, ArrayList p1){} + public void readFromParcel(Parcel p0){} + public void remove(String p0){} + public void setClassLoader(ClassLoader p0){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/Handler.java b/java/ql/test/stubs/google-android-9.0.0/android/os/Handler.java new file mode 100644 index 00000000000..5d1ae91db88 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/Handler.java @@ -0,0 +1,53 @@ +// Generated automatically from android.os.Handler for testing purposes + +package android.os; + +import android.os.Looper; +import android.os.Message; +import android.util.Printer; + +public class Handler +{ + public Handler(){} + public Handler(Handler.Callback p0){} + public Handler(Looper p0){} + public Handler(Looper p0, Handler.Callback p1){} + public String getMessageName(Message p0){ return null; } + public String toString(){ return null; } + public boolean sendMessageAtTime(Message p0, long p1){ return false; } + public final Looper getLooper(){ return null; } + public final Message obtainMessage(){ return null; } + public final Message obtainMessage(int p0){ return null; } + public final Message obtainMessage(int p0, Object p1){ return null; } + public final Message obtainMessage(int p0, int p1, int p2){ return null; } + public final Message obtainMessage(int p0, int p1, int p2, Object p3){ return null; } + public final boolean hasCallbacks(Runnable p0){ return false; } + public final boolean hasMessages(int p0){ return false; } + public final boolean hasMessages(int p0, Object p1){ return false; } + public final boolean post(Runnable p0){ return false; } + public final boolean postAtFrontOfQueue(Runnable p0){ return false; } + public final boolean postAtTime(Runnable p0, Object p1, long p2){ return false; } + public final boolean postAtTime(Runnable p0, long p1){ return false; } + public final boolean postDelayed(Runnable p0, Object p1, long p2){ return false; } + public final boolean postDelayed(Runnable p0, long p1){ return false; } + public final boolean sendEmptyMessage(int p0){ return false; } + public final boolean sendEmptyMessageAtTime(int p0, long p1){ return false; } + public final boolean sendEmptyMessageDelayed(int p0, long p1){ return false; } + public final boolean sendMessage(Message p0){ return false; } + public final boolean sendMessageAtFrontOfQueue(Message p0){ return false; } + public final boolean sendMessageDelayed(Message p0, long p1){ return false; } + public final void dump(Printer p0, String p1){} + public final void removeCallbacks(Runnable p0){} + public final void removeCallbacks(Runnable p0, Object p1){} + public final void removeCallbacksAndMessages(Object p0){} + public final void removeMessages(int p0){} + public final void removeMessages(int p0, Object p1){} + public static Handler createAsync(Looper p0){ return null; } + public static Handler createAsync(Looper p0, Handler.Callback p1){ return null; } + public void dispatchMessage(Message p0){} + public void handleMessage(Message p0){} + static public interface Callback + { + boolean handleMessage(Message p0); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/IBinder.java b/java/ql/test/stubs/google-android-9.0.0/android/os/IBinder.java new file mode 100644 index 00000000000..c12d5cf179b --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/IBinder.java @@ -0,0 +1,32 @@ +// Generated automatically from android.os.IBinder for testing purposes + +package android.os; + +import android.os.IInterface; +import android.os.Parcel; +import java.io.FileDescriptor; + +public interface IBinder +{ + IInterface queryLocalInterface(String p0); + String getInterfaceDescriptor(); + boolean isBinderAlive(); + boolean pingBinder(); + boolean transact(int p0, Parcel p1, Parcel p2, int p3); + boolean unlinkToDeath(IBinder.DeathRecipient p0, int p1); + static int DUMP_TRANSACTION = 0; + static int FIRST_CALL_TRANSACTION = 0; + static int FLAG_ONEWAY = 0; + static int INTERFACE_TRANSACTION = 0; + static int LAST_CALL_TRANSACTION = 0; + static int LIKE_TRANSACTION = 0; + static int PING_TRANSACTION = 0; + static int TWEET_TRANSACTION = 0; + static public interface DeathRecipient + { + void binderDied(); + } + void dump(FileDescriptor p0, String[] p1); + void dumpAsync(FileDescriptor p0, String[] p1); + void linkToDeath(IBinder.DeathRecipient p0, int p1); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/IInterface.java b/java/ql/test/stubs/google-android-9.0.0/android/os/IInterface.java new file mode 100644 index 00000000000..ccc3ae0a62d --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/IInterface.java @@ -0,0 +1,10 @@ +// Generated automatically from android.os.IInterface for testing purposes + +package android.os; + +import android.os.IBinder; + +public interface IInterface +{ + IBinder asBinder(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/LocaleList.java b/java/ql/test/stubs/google-android-9.0.0/android/os/LocaleList.java new file mode 100644 index 00000000000..113f5910a49 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/LocaleList.java @@ -0,0 +1,32 @@ +// Generated automatically from android.os.LocaleList for testing purposes + +package android.os; + +import android.icu.util.ULocale; +import android.os.Parcel; +import android.os.Parcelable; +import java.util.Locale; + +public class LocaleList implements Parcelable +{ + protected LocaleList() {} + public Locale get(int p0){ return null; } + public Locale getFirstMatch(String[] p0){ return null; } + public LocaleList(Locale... p0){} + public String toLanguageTags(){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public boolean isEmpty(){ return false; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public int indexOf(Locale p0){ return 0; } + public int size(){ return 0; } + public static LocaleList forLanguageTags(String p0){ return null; } + public static LocaleList getAdjustedDefault(){ return null; } + public static LocaleList getDefault(){ return null; } + public static LocaleList getEmptyLocaleList(){ return null; } + public static Parcelable.Creator CREATOR = null; + public static boolean isPseudoLocale(ULocale p0){ return false; } + public static void setDefault(LocaleList p0){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/Looper.java b/java/ql/test/stubs/google-android-9.0.0/android/os/Looper.java new file mode 100644 index 00000000000..bc7076c2125 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/Looper.java @@ -0,0 +1,25 @@ +// Generated automatically from android.os.Looper for testing purposes + +package android.os; + +import android.os.MessageQueue; +import android.util.Printer; + +public class Looper +{ + protected Looper() {} + public MessageQueue getQueue(){ return null; } + public String toString(){ return null; } + public Thread getThread(){ return null; } + public boolean isCurrentThread(){ return false; } + public static Looper getMainLooper(){ return null; } + public static Looper myLooper(){ return null; } + public static MessageQueue myQueue(){ return null; } + public static void loop(){} + public static void prepare(){} + public static void prepareMainLooper(){} + public void dump(Printer p0, String p1){} + public void quit(){} + public void quitSafely(){} + public void setMessageLogging(Printer p0){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/Message.java b/java/ql/test/stubs/google-android-9.0.0/android/os/Message.java new file mode 100644 index 00000000000..b6f98d43430 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/Message.java @@ -0,0 +1,44 @@ +// Generated automatically from android.os.Message for testing purposes + +package android.os; + +import android.os.Bundle; +import android.os.Handler; +import android.os.Messenger; +import android.os.Parcel; +import android.os.Parcelable; + +public class Message implements Parcelable +{ + public Bundle getData(){ return null; } + public Bundle peekData(){ return null; } + public Handler getTarget(){ return null; } + public Message(){} + public Messenger replyTo = null; + public Object obj = null; + public Runnable getCallback(){ return null; } + public String toString(){ return null; } + public boolean isAsynchronous(){ return false; } + public int arg1 = 0; + public int arg2 = 0; + public int describeContents(){ return 0; } + public int sendingUid = 0; + public int what = 0; + public long getWhen(){ return 0; } + public static Message obtain(){ return null; } + public static Message obtain(Handler p0){ return null; } + public static Message obtain(Handler p0, Runnable p1){ return null; } + public static Message obtain(Handler p0, int p1){ return null; } + public static Message obtain(Handler p0, int p1, Object p2){ return null; } + public static Message obtain(Handler p0, int p1, int p2, int p3){ return null; } + public static Message obtain(Handler p0, int p1, int p2, int p3, Object p4){ return null; } + public static Message obtain(Message p0){ return null; } + public static Parcelable.Creator CREATOR = null; + public void copyFrom(Message p0){} + public void recycle(){} + public void sendToTarget(){} + public void setAsynchronous(boolean p0){} + public void setData(Bundle p0){} + public void setTarget(Handler p0){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/MessageQueue.java b/java/ql/test/stubs/google-android-9.0.0/android/os/MessageQueue.java new file mode 100644 index 00000000000..d49871ccdf9 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/MessageQueue.java @@ -0,0 +1,27 @@ +// Generated automatically from android.os.MessageQueue for testing purposes + +package android.os; + +import java.io.FileDescriptor; + +public class MessageQueue +{ + protected MessageQueue() {} + protected void finalize(){} + public boolean isIdle(){ return false; } + public void addIdleHandler(MessageQueue.IdleHandler p0){} + public void addOnFileDescriptorEventListener(FileDescriptor p0, int p1, MessageQueue.OnFileDescriptorEventListener p2){} + public void removeIdleHandler(MessageQueue.IdleHandler p0){} + public void removeOnFileDescriptorEventListener(FileDescriptor p0){} + static public interface IdleHandler + { + boolean queueIdle(); + } + static public interface OnFileDescriptorEventListener + { + int onFileDescriptorEvents(FileDescriptor p0, int p1); + static int EVENT_ERROR = 0; + static int EVENT_INPUT = 0; + static int EVENT_OUTPUT = 0; + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/Messenger.java b/java/ql/test/stubs/google-android-9.0.0/android/os/Messenger.java new file mode 100644 index 00000000000..08d2a975153 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/Messenger.java @@ -0,0 +1,25 @@ +// Generated automatically from android.os.Messenger for testing purposes + +package android.os; + +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.Parcel; +import android.os.Parcelable; + +public class Messenger implements Parcelable +{ + protected Messenger() {} + public IBinder getBinder(){ return null; } + public Messenger(Handler p0){} + public Messenger(IBinder p0){} + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Messenger readMessengerOrNullFromParcel(Parcel p0){ return null; } + public static Parcelable.Creator CREATOR = null; + public static void writeMessengerOrNullToParcel(Messenger p0, Parcel p1){} + public void send(Message p0){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/Parcel.java b/java/ql/test/stubs/google-android-9.0.0/android/os/Parcel.java index 077da662edb..7e8608e24c6 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/os/Parcel.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/Parcel.java @@ -1,670 +1,144 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Generated automatically from android.os.Parcel for testing purposes + package android.os; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; +import android.os.Bundle; +import android.os.IBinder; +import android.os.IInterface; +import android.os.ParcelFileDescriptor; +import android.os.Parcelable; +import android.os.PersistableBundle; +import android.util.ArrayMap; +import android.util.Size; +import android.util.SizeF; +import android.util.SparseArray; +import android.util.SparseBooleanArray; import java.io.FileDescriptor; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.ObjectStreamClass; import java.io.Serializable; -import java.lang.reflect.Array; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; -public final class Parcel { - - /** - * Retrieve a new Parcel object from the pool. - */ - public static Parcel obtain() { - return null; - } - - /** - * Write a byte array into the parcel at the current {@link #dataPosition}, - * growing {@link #dataCapacity} if needed. - * - * @param b Bytes to place into the parcel. - */ - public final void writeByteArray(byte[] b) { - } - - /** - * Write a byte array into the parcel at the current {@link #dataPosition}, - * growing {@link #dataCapacity} if needed. - * - * @param b Bytes to place into the parcel. - * @param offset Index of first byte to be written. - * @param len Number of bytes to write. - */ - public final void writeByteArray(byte[] b, int offset, int len) { - } - - /** - * Write a blob of data into the parcel at the current {@link #dataPosition}, - * growing {@link #dataCapacity} if needed. - * - * @param b Bytes to place into the parcel. {@hide} {@SystemApi} - */ - public final void writeBlob(byte[] b) { - } - - /** - * Write a blob of data into the parcel at the current {@link #dataPosition}, - * growing {@link #dataCapacity} if needed. - * - * @param b Bytes to place into the parcel. - * @param offset Index of first byte to be written. - * @param len Number of bytes to write. {@hide} {@SystemApi} - */ - public final void writeBlob(byte[] b, int offset, int len) { - } - - /** - * Write an integer value into the parcel at the current dataPosition(), growing - * dataCapacity() if needed. - */ - public final void writeInt(int val) { - } - - /** - * Write a long integer value into the parcel at the current dataPosition(), - * growing dataCapacity() if needed. - */ - public final void writeLong(long val) { - } - - /** - * Write a floating point value into the parcel at the current dataPosition(), - * growing dataCapacity() if needed. - */ - public final void writeFloat(float val) { - } - - /** - * Write a double precision floating point value into the parcel at the current - * dataPosition(), growing dataCapacity() if needed. - */ - public final void writeDouble(double val) { - } - - /** - * Write a string value into the parcel at the current dataPosition(), growing - * dataCapacity() if needed. - */ - public final void writeString(String val) { - } - - /** - * Write a string without going though a {@link ReadWriteHelper}. Subclasses of - * {@link ReadWriteHelper} must use this method instead of {@link #writeString} - * to avoid infinity recursive calls. - * - * @hide - */ - public void writeStringNoHelper(String val) { - } - - /** @hide */ - public final void writeBoolean(boolean val) { - } - - /** - * Write a CharSequence value into the parcel at the current dataPosition(), - * growing dataCapacity() if needed. - * - * @hide - */ - public final void writeCharSequence(CharSequence val) { - } - - /** - * Write a byte value into the parcel at the current dataPosition(), growing - * dataCapacity() if needed. - */ - public final void writeByte(byte val) { - } - - /** - * Please use {@link #writeBundle} instead. Flattens a Map into the parcel at - * the current dataPosition(), growing dataCapacity() if needed. The Map keys - * must be String objects. The Map values are written using {@link #writeValue} - * and must follow the specification there. - * - *

    - * It is strongly recommended to use {@link #writeBundle} instead of this - * method, since the Bundle class provides a type-safe API that allows you to - * avoid mysterious type errors at the point of marshalling. - */ - public final void writeMap(Map val) { - } - - /** - * Flatten a Bundle into the parcel at the current dataPosition(), growing - * dataCapacity() if needed. - */ - public final void writeBundle(Bundle val) { - } - - /** - * Flatten a List into the parcel at the current dataPosition(), growing - * dataCapacity() if needed. The List values are written using - * {@link #writeValue} and must follow the specification there. - */ - public final void writeList(List val) { - } - - /** - * Flatten an Object array into the parcel at the current dataPosition(), - * growing dataCapacity() if needed. The array values are written using - * {@link #writeValue} and must follow the specification there. - */ - public final void writeArray(Object[] val) { - } - - public final void writeBooleanArray(boolean[] val) { - } - - public final boolean[] createBooleanArray() { - return null; - } - - public final void readBooleanArray(boolean[] val) { - } - - public final void writeCharArray(char[] val) { - } - - public final char[] createCharArray() { - return null; - } - - public final void readCharArray(char[] val) { - } - - public final void writeIntArray(int[] val) { - } - - public final int[] createIntArray() { - return null; - } - - public final void readIntArray(int[] val) { - } - - public final void writeLongArray(long[] val) { - } - - public final long[] createLongArray() { - return null; - } - - public final void readLongArray(long[] val) { - } - - public final void writeFloatArray(float[] val) { - } - - public final float[] createFloatArray() { - return null; - } - - public final void readFloatArray(float[] val) { - } - - public final void writeDoubleArray(double[] val) { - } - - public final double[] createDoubleArray() { - return null; - } - - public final void readDoubleArray(double[] val) { - } - - public final void writeStringArray(String[] val) { - } - - public final String[] createStringArray() { - return null; - } - - public final void readStringArray(String[] val) { - } - - /** - * @hide - */ - public final void writeCharSequenceArray(CharSequence[] val) { - } - - /** - * @hide - */ - public final void writeCharSequenceList(ArrayList val) { - } - - /** - * Flatten a List containing String objects into the parcel, at the current - * dataPosition() and growing dataCapacity() if needed. They can later be - * retrieved with {@link #createStringArrayList} or {@link #readStringList}. - * - * @param val The list of strings to be written. - * - * @see #createStringArrayList - * @see #readStringList - */ - public final void writeStringList(List val) { - } - - /** - * Flatten a generic object in to a parcel. The given Object value may currently - * be one of the following types: - * - *

      - *
    • null - *
    • String - *
    • Byte - *
    • Short - *
    • Integer - *
    • Long - *
    • Float - *
    • Double - *
    • Boolean - *
    • String[] - *
    • boolean[] - *
    • byte[] - *
    • int[] - *
    • long[] - *
    • Object[] (supporting objects of the same type defined here). - *
    • {@link Bundle} - *
    • Map (as supported by {@link #writeMap}). - *
    • Any object that implements the {@link Parcelable} protocol. - *
    • Parcelable[] - *
    • CharSequence (as supported by {@link TextUtils#writeToParcel}). - *
    • List (as supported by {@link #writeList}). - *
    • {@link SparseArray} (as supported by - * {@link #writeSparseArray(SparseArray)}). - *
    • {@link IBinder} - *
    • Any object that implements Serializable (but see - * {@link #writeSerializable} for caveats). Note that all of the previous types - * have relatively efficient implementations for writing to a Parcel; having to - * rely on the generic serialization approach is much less efficient and should - * be avoided whenever possible. - *
    - * - *

    - * {@link Parcelable} objects are written with {@link Parcelable#writeToParcel} - * using contextual flags of 0. When serializing objects containing - * {@link ParcelFileDescriptor}s, this may result in file descriptor leaks when - * they are returned from Binder calls (where - * {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} should be used). - *

    - */ - public final void writeValue(Object v) { - } - - /** - * Flatten the name of the class of the Parcelable and its contents into the - * parcel. - * - * @param p The Parcelable object to be written. - * @param parcelableFlags Contextual flags as per - * {@link Parcelable#writeToParcel(Parcel, int) - * Parcelable.writeToParcel()}. - */ - public final void writeParcelable(Parcelable p, int parcelableFlags) { - } - - /** @hide */ - public final void writeParcelableCreator(Parcelable p) { - } - - /** - * Write a generic serializable object in to a Parcel. It is strongly - * recommended that this method be avoided, since the serialization overhead is - * extremely large, and this approach will be much slower than using the other - * approaches to writing data in to a Parcel. - */ - public final void writeSerializable(Serializable s) { - } - - /** - * Special function for writing an exception result at the header of a parcel, - * to be used when returning an exception from a transaction. Note that this - * currently only supports a few exception types; any other exception will be - * re-thrown by this function as a RuntimeException (to be caught by the - * system's last-resort exception handling when dispatching a transaction). - * - *

    - * The supported exception types are: - *

      - *
    • {@link BadParcelableException} - *
    • {@link IllegalArgumentException} - *
    • {@link IllegalStateException} - *
    • {@link NullPointerException} - *
    • {@link SecurityException} - *
    • {@link UnsupportedOperationException} - *
    • {@link NetworkOnMainThreadException} - *
    - * - * @param e The Exception to be written. - * - * @see #writeNoException - * @see #readException - */ - public final void writeException(Exception e) { - } - - /** - * Special function for writing information at the front of the Parcel - * indicating that no exception occurred. - * - * @see #writeException - * @see #readException - */ - public final void writeNoException() { - } - - /** - * Special function for reading an exception result from the header of a parcel, - * to be used after receiving the result of a transaction. This will throw the - * exception for you if it had been written to the Parcel, otherwise return and - * let you read the normal result data from the Parcel. - * - * @see #writeException - * @see #writeNoException - */ - public final void readException() { - } - - /** - * Parses the header of a Binder call's response Parcel and returns the - * exception code. Deals with lite or fat headers. In the common successful - * case, this header is generally zero. In less common cases, it's a small - * negative number and will be followed by an error string. - * - * This exists purely for android.database.DatabaseUtils and insulating it from - * having to handle fat headers as returned by e.g. StrictMode-induced RPC - * responses. - * - * @hide - */ - public final int readExceptionCode() { - return -1; - } - - /** - * Throw an exception with the given message. Not intended for use outside the - * Parcel class. - * - * @param code Used to determine which exception class to throw. - * @param msg The exception message. - */ - public final void readException(int code, String msg) { - } - - /** - * Read an integer value from the parcel at the current dataPosition(). - */ - public final int readInt() { - return -1; - } - - /** - * Read a long integer value from the parcel at the current dataPosition(). - */ - public final long readLong() { - return -1; - } - - /** - * Read a floating point value from the parcel at the current dataPosition(). - */ - public final float readFloat() { - return -1; - } - - /** - * Read a double precision floating point value from the parcel at the current - * dataPosition(). - */ - public final double readDouble() { - return -1; - } - - /** - * Read a string value from the parcel at the current dataPosition(). - */ - public final String readString() { - return null; - } - - /** @hide */ - public final boolean readBoolean() { - return false; - } - - /** - * Read a CharSequence value from the parcel at the current dataPosition(). - * - * @hide - */ - public final CharSequence readCharSequence() { - return null; - } - - /** - * Read a byte value from the parcel at the current dataPosition(). - */ - public final byte readByte() { - return -1; - } - - /** - * Please use {@link #readBundle(ClassLoader)} instead (whose data must have - * been written with {@link #writeBundle}. Read into an existing Map object from - * the parcel at the current dataPosition(). - */ - public final void readMap(Map outVal, ClassLoader loader) { - } - - /** - * Read into an existing List object from the parcel at the current - * dataPosition(), using the given class loader to load any enclosed - * Parcelables. If it is null, the default class loader is used. - */ - public final void readList(List outVal, ClassLoader loader) { - } - - /** - * Please use {@link #readBundle(ClassLoader)} instead (whose data must have - * been written with {@link #writeBundle}. Read and return a new HashMap object - * from the parcel at the current dataPosition(), using the given class loader - * to load any enclosed Parcelables. Returns null if the previously written map - * object was null. - */ - public final HashMap readHashMap(ClassLoader loader) { - return null; - } - - /** - * Read and return a new Bundle object from the parcel at the current - * dataPosition(). Returns null if the previously written Bundle object was - * null. - */ - public final Bundle readBundle() { - return null; - } - - /** - * Read and return a new Bundle object from the parcel at the current - * dataPosition(), using the given class loader to initialize the class loader - * of the Bundle for later retrieval of Parcelable objects. Returns null if the - * previously written Bundle object was null. - */ - public final Bundle readBundle(ClassLoader loader) { - return null; - } - - /** - * Read and return a byte[] object from the parcel. - */ - public final byte[] createByteArray() { - return null; - } - - /** - * Read a byte[] object from the parcel and copy it into the given byte array. - */ - public final void readByteArray(byte[] val) { - } - - /** - * Read a blob of data from the parcel and return it as a byte array. - * {@hide} {@SystemApi} - */ - public final byte[] readBlob() { - return null; - } - - /** - * Read and return a String[] object from the parcel. {@hide} - */ - public final String[] readStringArray() { - return null; - } - - /** - * Read and return a CharSequence[] object from the parcel. {@hide} - */ - public final CharSequence[] readCharSequenceArray() { - return null; - } - - /** - * Read and return an ArrayList<CharSequence> object from the parcel. - * {@hide} - */ - public final ArrayList readCharSequenceList() { - return null; - } - - /** - * Read and return a new ArrayList object from the parcel at the current - * dataPosition(). Returns null if the previously written list object was null. - * The given class loader will be used to load any enclosed Parcelables. - */ - public final ArrayList readArrayList(ClassLoader loader) { - return null; - } - - /** - * Read and return a new Object array from the parcel at the current - * dataPosition(). Returns null if the previously written array was null. The - * given class loader will be used to load any enclosed Parcelables. - */ - public final Object[] readArray(ClassLoader loader) { - return null; - } - - /** - * Read and return a new ArrayList containing String objects from the parcel - * that was written with {@link #writeStringList} at the current dataPosition(). - * Returns null if the previously written list object was null. - * - * @return A newly created ArrayList containing strings with the same data as - * those that were previously written. - * - * @see #writeStringList - */ - public final ArrayList createStringArrayList() { - return null; - } - - /** - * Read into the given List items String objects that were written with - * {@link #writeStringList} at the current dataPosition(). - * - * @see #writeStringList - */ - public final void readStringList(List list) { - } - - /** - * Read a typed object from a parcel. The given class loader will be used to - * load any enclosed Parcelables. If it is null, the default class loader will - * be used. - */ - public final Object readValue(ClassLoader loader) { - return null; - } - - /** - * Read and return a new Parcelable from the parcel. The given class loader will - * be used to load any enclosed Parcelables. If it is null, the default class - * loader will be used. - * - * @param loader A ClassLoader from which to instantiate the Parcelable object, - * or null for the default class loader. - * @return Returns the newly created Parcelable, or null if a null object has - * been written. - * @throws BadParcelableException Throws BadParcelableException if there was an - * error trying to instantiate the Parcelable. - */ - public final T readParcelable(ClassLoader loader) { - return null; - } - - /** - * Read and return a new Parcelable array from the parcel. The given class - * loader will be used to load any enclosed Parcelables. - * - * @return the Parcelable array, or null if the array is null - */ - public final Parcelable[] readParcelableArray(ClassLoader loader) { - return null; - } - - /** @hide */ - public final T[] readParcelableArray(ClassLoader loader, Class clazz) { - return null; - } - - /** - * Read and return a new Serializable object from the parcel. - * - * @return the Serializable object, or null if the Serializable name wasn't - * found in the parcel. - */ - public final Serializable readSerializable() { - return null; - } - - private final Serializable readSerializable(final ClassLoader loader) { - return null; - } +public class Parcel +{ + protected Parcel() {} + protected void finalize(){} + public ArrayMap createTypedArrayMap(Parcelable.Creator p0){ return null; } + public List readParcelableList(List p0, ClassLoader p1){ return null; } + public SparseArray createTypedSparseArray(Parcelable.Creator p0){ return null; } + public T readParcelable(ClassLoader p0){ return null; } + public void writeParcelableArray(T[] p0, int p1){} + public void writeParcelableList(List p0, int p1){} + public void writeTypedArray(T[] p0, int p1){} + public void writeTypedArrayMap(ArrayMap p0, int p1){} + public void writeTypedList(List p0){} + public void writeTypedObject(T p0, int p1){} + public void writeTypedSparseArray(SparseArray p0, int p1){} + public ArrayList createTypedArrayList(Parcelable.Creator p0){ return null; } + public SparseArray readSparseArray(ClassLoader p0){ return null; } + public T readTypedObject(Parcelable.Creator p0){ return null; } + public T[] createTypedArray(Parcelable.Creator p0){ return null; } + public void readTypedArray(T[] p0, Parcelable.Creator p1){} + public void readTypedList(List p0, Parcelable.Creator p1){} + public void writeSparseArray(SparseArray p0){} + public ArrayList readArrayList(ClassLoader p0){ return null; } + public ArrayList createBinderArrayList(){ return null; } + public ArrayList createStringArrayList(){ return null; } + public Bundle readBundle(){ return null; } + public Bundle readBundle(ClassLoader p0){ return null; } + public HashMap readHashMap(ClassLoader p0){ return null; } + public IBinder readStrongBinder(){ return null; } + public IBinder[] createBinderArray(){ return null; } + public Object readValue(ClassLoader p0){ return null; } + public Object[] readArray(ClassLoader p0){ return null; } + public ParcelFileDescriptor readFileDescriptor(){ return null; } + public Parcelable[] readParcelableArray(ClassLoader p0){ return null; } + public PersistableBundle readPersistableBundle(){ return null; } + public PersistableBundle readPersistableBundle(ClassLoader p0){ return null; } + public Serializable readSerializable(){ return null; } + public Size readSize(){ return null; } + public SizeF readSizeF(){ return null; } + public SparseBooleanArray readSparseBooleanArray(){ return null; } + public String readString(){ return null; } + public String[] createStringArray(){ return null; } + public boolean hasFileDescriptors(){ return false; } + public boolean readBoolean(){ return false; } + public boolean[] createBooleanArray(){ return null; } + public byte readByte(){ return 0; } + public byte[] createByteArray(){ return null; } + public byte[] marshall(){ return null; } + public char[] createCharArray(){ return null; } + public double readDouble(){ return 0; } + public double[] createDoubleArray(){ return null; } + public float readFloat(){ return 0; } + public float[] createFloatArray(){ return null; } + public int dataAvail(){ return 0; } + public int dataCapacity(){ return 0; } + public int dataPosition(){ return 0; } + public int dataSize(){ return 0; } + public int readInt(){ return 0; } + public int[] createIntArray(){ return null; } + public long readLong(){ return 0; } + public long[] createLongArray(){ return null; } + public static Parcel obtain(){ return null; } + public static Parcelable.Creator STRING_CREATOR = null; + public void appendFrom(Parcel p0, int p1, int p2){} + public void enforceInterface(String p0){} + public void readBinderArray(IBinder[] p0){} + public void readBinderList(List p0){} + public void readBooleanArray(boolean[] p0){} + public void readByteArray(byte[] p0){} + public void readCharArray(char[] p0){} + public void readDoubleArray(double[] p0){} + public void readException(){} + public void readException(int p0, String p1){} + public void readFloatArray(float[] p0){} + public void readIntArray(int[] p0){} + public void readList(List p0, ClassLoader p1){} + public void readLongArray(long[] p0){} + public void readMap(Map p0, ClassLoader p1){} + public void readStringArray(String[] p0){} + public void readStringList(List p0){} + public void recycle(){} + public void setDataCapacity(int p0){} + public void setDataPosition(int p0){} + public void setDataSize(int p0){} + public void unmarshall(byte[] p0, int p1, int p2){} + public void writeArray(Object[] p0){} + public void writeBinderArray(IBinder[] p0){} + public void writeBinderList(List p0){} + public void writeBoolean(boolean p0){} + public void writeBooleanArray(boolean[] p0){} + public void writeBundle(Bundle p0){} + public void writeByte(byte p0){} + public void writeByteArray(byte[] p0){} + public void writeByteArray(byte[] p0, int p1, int p2){} + public void writeCharArray(char[] p0){} + public void writeDouble(double p0){} + public void writeDoubleArray(double[] p0){} + public void writeException(Exception p0){} + public void writeFileDescriptor(FileDescriptor p0){} + public void writeFloat(float p0){} + public void writeFloatArray(float[] p0){} + public void writeInt(int p0){} + public void writeIntArray(int[] p0){} + public void writeInterfaceToken(String p0){} + public void writeList(List p0){} + public void writeLong(long p0){} + public void writeLongArray(long[] p0){} + public void writeMap(Map p0){} + public void writeNoException(){} + public void writeParcelable(Parcelable p0, int p1){} + public void writePersistableBundle(PersistableBundle p0){} + public void writeSerializable(Serializable p0){} + public void writeSize(Size p0){} + public void writeSizeF(SizeF p0){} + public void writeSparseBooleanArray(SparseBooleanArray p0){} + public void writeString(String p0){} + public void writeStringArray(String[] p0){} + public void writeStringList(List p0){} + public void writeStrongBinder(IBinder p0){} + public void writeStrongInterface(IInterface p0){} + public void writeValue(Object p0){} } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/Parcelable.java b/java/ql/test/stubs/google-android-9.0.0/android/os/Parcelable.java index ba9942dbb79..626061a6799 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/os/Parcelable.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/Parcelable.java @@ -1,86 +1,18 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Generated automatically from android.os.Parcelable for testing purposes + package android.os; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; +import android.os.Parcel; -/** - * Interface for classes whose instances can be written to and restored from a - * {@link Parcel}. Classes implementing the Parcelable interface must also have - * a non-null static field called CREATOR of a type that implements - * the {@link Parcelable.Creator} interface. - * - *

    - * A typical implementation of Parcelable is: - *

    - * - *
    - * public class MyParcelable implements Parcelable {
    - *     private int mData;
    - *
    - *     public int describeContents() {
    - *         return 0;
    - *     }
    - *
    - *     public void writeToParcel(Parcel out, int flags) {
    - *         out.writeInt(mData);
    - *     }
    - *
    - *     public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() {
    - *         public MyParcelable createFromParcel(Parcel in) {
    - *             return new MyParcelable(in);
    - *         }
    - *
    - *         public MyParcelable[] newArray(int size) {
    - *             return new MyParcelable[size];
    - *         }
    - *     };
    - * 
    - *     private MyParcelable(Parcel in) {
    - *         mData = in.readInt();
    - *     }
    - * }
    - * 
    - */ -public interface Parcelable { - /** - * Flatten this object in to a Parcel. - * - * @param dest The Parcel in which the object should be written. - * @param flags Additional flags about how the object should be written. May be - * 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}. - */ - public void writeToParcel(Parcel dest, int flags); - - /** - * Specialization of {@link Creator} that allows you to receive the ClassLoader - * the object is being created in. - */ - public interface ClassLoaderCreator { - /** - * Create a new instance of the Parcelable class, instantiating it from the - * given Parcel whose data had previously been written by - * {@link Parcelable#writeToParcel Parcelable.writeToParcel()} and using the - * given ClassLoader. - * - * @param source The Parcel to read the object's data from. - * @param loader The ClassLoader that this object is being created in. - * @return Returns a new instance of the Parcelable class. - */ - public T createFromParcel(Parcel source, ClassLoader loader); +public interface Parcelable +{ + int describeContents(); + static int CONTENTS_FILE_DESCRIPTOR = 0; + static int PARCELABLE_WRITE_RETURN_VALUE = 0; + static public interface Creator + { + T createFromParcel(Parcel p0); + T[] newArray(int p0); } -} \ No newline at end of file + void writeToParcel(Parcel p0, int p1); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/PersistableBundle.java b/java/ql/test/stubs/google-android-9.0.0/android/os/PersistableBundle.java new file mode 100644 index 00000000000..1aa4768bc89 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/PersistableBundle.java @@ -0,0 +1,23 @@ +// Generated automatically from android.os.PersistableBundle for testing purposes + +package android.os; + +import android.os.BaseBundle; +import android.os.Parcel; +import android.os.Parcelable; + +public class PersistableBundle extends BaseBundle implements Cloneable, Parcelable +{ + public Object clone(){ return null; } + public PersistableBundle deepCopy(){ return null; } + public PersistableBundle getPersistableBundle(String p0){ return null; } + public PersistableBundle(){} + public PersistableBundle(PersistableBundle p0){} + public PersistableBundle(int p0){} + public String toString(){ return null; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static PersistableBundle EMPTY = null; + public void putPersistableBundle(String p0, PersistableBundle p1){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/UserHandle.java b/java/ql/test/stubs/google-android-9.0.0/android/os/UserHandle.java new file mode 100644 index 00000000000..a2e4d596054 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/UserHandle.java @@ -0,0 +1,21 @@ +// Generated automatically from android.os.UserHandle for testing purposes + +package android.os; + +import android.os.Parcel; +import android.os.Parcelable; + +public class UserHandle implements Parcelable +{ + protected UserHandle() {} + public String toString(){ return null; } + public UserHandle(Parcel p0){} + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static UserHandle getUserHandleForUid(int p0){ return null; } + public static UserHandle readFromParcel(Parcel p0){ return null; } + public static void writeToParcel(UserHandle p0, Parcel p1){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/util/AttributeSet.java b/java/ql/test/stubs/google-android-9.0.0/android/util/AttributeSet.java new file mode 100644 index 00000000000..04bdbe6b8d2 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/util/AttributeSet.java @@ -0,0 +1,31 @@ +// Generated automatically from android.util.AttributeSet for testing purposes + +package android.util; + + +public interface AttributeSet +{ + String getAttributeName(int p0); + String getAttributeValue(String p0, String p1); + String getAttributeValue(int p0); + String getClassAttribute(); + String getIdAttribute(); + String getPositionDescription(); + boolean getAttributeBooleanValue(String p0, String p1, boolean p2); + boolean getAttributeBooleanValue(int p0, boolean p1); + default String getAttributeNamespace(int p0){ return null; } + float getAttributeFloatValue(String p0, String p1, float p2); + float getAttributeFloatValue(int p0, float p1); + int getAttributeCount(); + int getAttributeIntValue(String p0, String p1, int p2); + int getAttributeIntValue(int p0, int p1); + int getAttributeListValue(String p0, String p1, String[] p2, int p3); + int getAttributeListValue(int p0, String[] p1, int p2); + int getAttributeNameResource(int p0); + int getAttributeResourceValue(String p0, String p1, int p2); + int getAttributeResourceValue(int p0, int p1); + int getAttributeUnsignedIntValue(String p0, String p1, int p2); + int getAttributeUnsignedIntValue(int p0, int p1); + int getIdAttributeResourceValue(int p0); + int getStyleAttribute(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/util/DisplayMetrics.java b/java/ql/test/stubs/google-android-9.0.0/android/util/DisplayMetrics.java new file mode 100644 index 00000000000..fe64b670921 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/util/DisplayMetrics.java @@ -0,0 +1,46 @@ +// Generated automatically from android.util.DisplayMetrics for testing purposes + +package android.util; + + +public class DisplayMetrics +{ + public DisplayMetrics(){} + public String toString(){ return null; } + public boolean equals(DisplayMetrics p0){ return false; } + public boolean equals(Object p0){ return false; } + public float density = 0; + public float scaledDensity = 0; + public float xdpi = 0; + public float ydpi = 0; + public int densityDpi = 0; + public int hashCode(){ return 0; } + public int heightPixels = 0; + public int widthPixels = 0; + public static int DENSITY_140 = 0; + public static int DENSITY_180 = 0; + public static int DENSITY_200 = 0; + public static int DENSITY_220 = 0; + public static int DENSITY_260 = 0; + public static int DENSITY_280 = 0; + public static int DENSITY_300 = 0; + public static int DENSITY_340 = 0; + public static int DENSITY_360 = 0; + public static int DENSITY_400 = 0; + public static int DENSITY_420 = 0; + public static int DENSITY_440 = 0; + public static int DENSITY_450 = 0; + public static int DENSITY_560 = 0; + public static int DENSITY_600 = 0; + public static int DENSITY_DEFAULT = 0; + public static int DENSITY_DEVICE_STABLE = 0; + public static int DENSITY_HIGH = 0; + public static int DENSITY_LOW = 0; + public static int DENSITY_MEDIUM = 0; + public static int DENSITY_TV = 0; + public static int DENSITY_XHIGH = 0; + public static int DENSITY_XXHIGH = 0; + public static int DENSITY_XXXHIGH = 0; + public void setTo(DisplayMetrics p0){} + public void setToDefaults(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/util/Printer.java b/java/ql/test/stubs/google-android-9.0.0/android/util/Printer.java new file mode 100644 index 00000000000..e6b67a13572 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/util/Printer.java @@ -0,0 +1,9 @@ +// Generated automatically from android.util.Printer for testing purposes + +package android.util; + + +public interface Printer +{ + void println(String p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/util/Size.java b/java/ql/test/stubs/google-android-9.0.0/android/util/Size.java new file mode 100644 index 00000000000..9cd6edc8555 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/util/Size.java @@ -0,0 +1,16 @@ +// Generated automatically from android.util.Size for testing purposes + +package android.util; + + +public class Size +{ + protected Size() {} + public Size(int p0, int p1){} + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public int getHeight(){ return 0; } + public int getWidth(){ return 0; } + public int hashCode(){ return 0; } + public static Size parseSize(String p0){ return null; } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/util/SizeF.java b/java/ql/test/stubs/google-android-9.0.0/android/util/SizeF.java new file mode 100644 index 00000000000..16558fd17e6 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/util/SizeF.java @@ -0,0 +1,16 @@ +// Generated automatically from android.util.SizeF for testing purposes + +package android.util; + + +public class SizeF +{ + protected SizeF() {} + public SizeF(float p0, float p1){} + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public float getHeight(){ return 0; } + public float getWidth(){ return 0; } + public int hashCode(){ return 0; } + public static SizeF parseSizeF(String p0){ return null; } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/util/SparseArray.java b/java/ql/test/stubs/google-android-9.0.0/android/util/SparseArray.java new file mode 100644 index 00000000000..13c447c9741 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/util/SparseArray.java @@ -0,0 +1,27 @@ +// Generated automatically from android.util.SparseArray for testing purposes + +package android.util; + + +public class SparseArray implements Cloneable +{ + public E get(int p0){ return null; } + public E get(int p0, E p1){ return null; } + public E valueAt(int p0){ return null; } + public SparseArray(){} + public SparseArray(int p0){} + public SparseArray clone(){ return null; } + public String toString(){ return null; } + public int indexOfKey(int p0){ return 0; } + public int indexOfValue(E p0){ return 0; } + public int keyAt(int p0){ return 0; } + public int size(){ return 0; } + public void append(int p0, E p1){} + public void clear(){} + public void delete(int p0){} + public void put(int p0, E p1){} + public void remove(int p0){} + public void removeAt(int p0){} + public void removeAtRange(int p0, int p1){} + public void setValueAt(int p0, E p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/util/SparseBooleanArray.java b/java/ql/test/stubs/google-android-9.0.0/android/util/SparseBooleanArray.java new file mode 100644 index 00000000000..3c84c18e71b --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/util/SparseBooleanArray.java @@ -0,0 +1,27 @@ +// Generated automatically from android.util.SparseBooleanArray for testing purposes + +package android.util; + + +public class SparseBooleanArray implements Cloneable +{ + public SparseBooleanArray clone(){ return null; } + public SparseBooleanArray(){} + public SparseBooleanArray(int p0){} + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public boolean get(int p0){ return false; } + public boolean get(int p0, boolean p1){ return false; } + public boolean valueAt(int p0){ return false; } + public int hashCode(){ return 0; } + public int indexOfKey(int p0){ return 0; } + public int indexOfValue(boolean p0){ return 0; } + public int keyAt(int p0){ return 0; } + public int size(){ return 0; } + public void append(int p0, boolean p1){} + public void clear(){} + public void delete(int p0){} + public void put(int p0, boolean p1){} + public void removeAt(int p0){} + public void setValueAt(int p0, boolean p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/util/TypedValue.java b/java/ql/test/stubs/google-android-9.0.0/android/util/TypedValue.java new file mode 100644 index 00000000000..269ab8bcf67 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/util/TypedValue.java @@ -0,0 +1,73 @@ +// Generated automatically from android.util.TypedValue for testing purposes + +package android.util; + +import android.util.DisplayMetrics; + +public class TypedValue +{ + public CharSequence string = null; + public String toString(){ return null; } + public TypedValue(){} + public boolean isColorType(){ return false; } + public final CharSequence coerceToString(){ return null; } + public final float getFloat(){ return 0; } + public float getDimension(DisplayMetrics p0){ return 0; } + public float getFraction(float p0, float p1){ return 0; } + public int assetCookie = 0; + public int changingConfigurations = 0; + public int data = 0; + public int density = 0; + public int getComplexUnit(){ return 0; } + public int resourceId = 0; + public int sourceResourceId = 0; + public int type = 0; + public static String coerceToString(int p0, int p1){ return null; } + public static float applyDimension(int p0, float p1, DisplayMetrics p2){ return 0; } + public static float complexToDimension(int p0, DisplayMetrics p1){ return 0; } + public static float complexToFloat(int p0){ return 0; } + public static float complexToFraction(int p0, float p1, float p2){ return 0; } + public static int COMPLEX_MANTISSA_MASK = 0; + public static int COMPLEX_MANTISSA_SHIFT = 0; + public static int COMPLEX_RADIX_0p23 = 0; + public static int COMPLEX_RADIX_16p7 = 0; + public static int COMPLEX_RADIX_23p0 = 0; + public static int COMPLEX_RADIX_8p15 = 0; + public static int COMPLEX_RADIX_MASK = 0; + public static int COMPLEX_RADIX_SHIFT = 0; + public static int COMPLEX_UNIT_DIP = 0; + public static int COMPLEX_UNIT_FRACTION = 0; + public static int COMPLEX_UNIT_FRACTION_PARENT = 0; + public static int COMPLEX_UNIT_IN = 0; + public static int COMPLEX_UNIT_MASK = 0; + public static int COMPLEX_UNIT_MM = 0; + public static int COMPLEX_UNIT_PT = 0; + public static int COMPLEX_UNIT_PX = 0; + public static int COMPLEX_UNIT_SHIFT = 0; + public static int COMPLEX_UNIT_SP = 0; + public static int DATA_NULL_EMPTY = 0; + public static int DATA_NULL_UNDEFINED = 0; + public static int DENSITY_DEFAULT = 0; + public static int DENSITY_NONE = 0; + public static int TYPE_ATTRIBUTE = 0; + public static int TYPE_DIMENSION = 0; + public static int TYPE_FIRST_COLOR_INT = 0; + public static int TYPE_FIRST_INT = 0; + public static int TYPE_FLOAT = 0; + public static int TYPE_FRACTION = 0; + public static int TYPE_INT_BOOLEAN = 0; + public static int TYPE_INT_COLOR_ARGB4 = 0; + public static int TYPE_INT_COLOR_ARGB8 = 0; + public static int TYPE_INT_COLOR_RGB4 = 0; + public static int TYPE_INT_COLOR_RGB8 = 0; + public static int TYPE_INT_DEC = 0; + public static int TYPE_INT_HEX = 0; + public static int TYPE_LAST_COLOR_INT = 0; + public static int TYPE_LAST_INT = 0; + public static int TYPE_NULL = 0; + public static int TYPE_REFERENCE = 0; + public static int TYPE_STRING = 0; + public static int complexToDimensionPixelOffset(int p0, DisplayMetrics p1){ return 0; } + public static int complexToDimensionPixelSize(int p0, DisplayMetrics p1){ return 0; } + public void setTo(TypedValue p0){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/ContextThemeWrapper.java b/java/ql/test/stubs/google-android-9.0.0/android/view/ContextThemeWrapper.java new file mode 100644 index 00000000000..e1a65acf681 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/ContextThemeWrapper.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.annotation.StyleRes; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.res.AssetManager; +import android.content.res.Configuration; +import android.content.res.Resources; + +/** + * A context wrapper that allows you to modify or replace the theme of the + * wrapped context. + */ +public class ContextThemeWrapper extends ContextWrapper { + /** + * Creates a new context wrapper with no theme and no base context. + *

    + * Note: A base context must be attached + * using {@link #attachBaseContext(Context)} before calling any other + * method on the newly constructed context wrapper. + */ + public ContextThemeWrapper() { + super(null); + } + + /** + * Creates a new context wrapper with the specified theme. + *

    + * The specified theme will be applied on top of the base context's theme. + * Any attributes not explicitly defined in the theme identified by + * themeResId will retain their original values. + * + * @param base the base context + * @param themeResId the resource ID of the theme to be applied on top of + * the base context's theme + */ + public ContextThemeWrapper(Context base, @StyleRes int themeResId) { + super(base); + } + + /** + * Creates a new context wrapper with the specified theme. + *

    + * Unlike {@link #ContextThemeWrapper(Context, int)}, the theme passed to + * this constructor will completely replace the base context's theme. + * + * @param base the base context + * @param theme the theme against which resources should be inflated + */ + public ContextThemeWrapper(Context base, Resources.Theme theme) { + super(base); + } + + /** + * Call to set an "override configuration" on this context -- this is + * a configuration that replies one or more values of the standard + * configuration that is applied to the context. See + * {@link Context#createConfigurationContext(Configuration)} for more + * information. + * + *

    This method can only be called once, and must be called before any + * calls to {@link #getResources()} or {@link #getAssets()} are made. + */ + public void applyOverrideConfiguration(Configuration overrideConfiguration) { + } + + /** + * Used by ActivityThread to apply the overridden configuration to onConfigurationChange + * callbacks. + * @hide + */ + public Configuration getOverrideConfiguration() { + return null; + } + + @Override + public AssetManager getAssets() { + return null; + } + + @Override + public Resources getResources() { + return null; + } + + private Resources getResourcesInternal() { + return null; + } + + @Override + public void setTheme(int resid) { + } + + @Override + public Resources.Theme getTheme() { + return null; + } + + @Override + public Object getSystemService(String name) { + return null; + } + + /** + * Called by {@link #setTheme} and {@link #getTheme} to apply a theme + * resource to the current Theme object. May be overridden to change the + * default (simple) behavior. This method will not be called in multiple + * threads simultaneously. + * + * @param theme the theme being modified + * @param resId the style resource being applied to theme + * @param first {@code true} if this is the first time a style is being + * applied to theme + */ + protected void onApplyThemeResource(Resources.Theme theme, int resId, boolean first) {} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/Display.java b/java/ql/test/stubs/google-android-9.0.0/android/view/Display.java new file mode 100644 index 00000000000..d4832a80460 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/Display.java @@ -0,0 +1,88 @@ +// Generated automatically from android.view.Display for testing purposes + +package android.view; + +import android.graphics.ColorSpace; +import android.graphics.Point; +import android.graphics.Rect; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.DisplayMetrics; +import android.view.DisplayCutout; + +public class Display +{ + public ColorSpace getPreferredWideGamutColorSpace(){ return null; } + public Display.HdrCapabilities getHdrCapabilities(){ return null; } + public Display.Mode getMode(){ return null; } + public Display.Mode[] getSupportedModes(){ return null; } + public DisplayCutout getCutout(){ return null; } + public String getName(){ return null; } + public String toString(){ return null; } + public boolean isHdr(){ return false; } + public boolean isValid(){ return false; } + public boolean isWideColorGamut(){ return false; } + public float getRefreshRate(){ return 0; } + public float[] getSupportedRefreshRates(){ return null; } + public int getDisplayId(){ return 0; } + public int getFlags(){ return 0; } + public int getHeight(){ return 0; } + public int getOrientation(){ return 0; } + public int getPixelFormat(){ return 0; } + public int getRotation(){ return 0; } + public int getState(){ return 0; } + public int getWidth(){ return 0; } + public long getAppVsyncOffsetNanos(){ return 0; } + public long getPresentationDeadlineNanos(){ return 0; } + public static int DEFAULT_DISPLAY = 0; + public static int FLAG_PRESENTATION = 0; + public static int FLAG_PRIVATE = 0; + public static int FLAG_ROUND = 0; + public static int FLAG_SECURE = 0; + public static int FLAG_SUPPORTS_PROTECTED_BUFFERS = 0; + public static int INVALID_DISPLAY = 0; + public static int STATE_DOZE = 0; + public static int STATE_DOZE_SUSPEND = 0; + public static int STATE_OFF = 0; + public static int STATE_ON = 0; + public static int STATE_ON_SUSPEND = 0; + public static int STATE_UNKNOWN = 0; + public static int STATE_VR = 0; + public void getCurrentSizeRange(Point p0, Point p1){} + public void getMetrics(DisplayMetrics p0){} + public void getRealMetrics(DisplayMetrics p0){} + public void getRealSize(Point p0){} + public void getRectSize(Rect p0){} + public void getSize(Point p0){} + static public class HdrCapabilities implements Parcelable + { + public boolean equals(Object p0){ return false; } + public float getDesiredMaxAverageLuminance(){ return 0; } + public float getDesiredMaxLuminance(){ return 0; } + public float getDesiredMinLuminance(){ return 0; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public int[] getSupportedHdrTypes(){ return null; } + public static Parcelable.Creator CREATOR = null; + public static float INVALID_LUMINANCE = 0; + public static int HDR_TYPE_DOLBY_VISION = 0; + public static int HDR_TYPE_HDR10 = 0; + public static int HDR_TYPE_HDR10_PLUS = 0; + public static int HDR_TYPE_HLG = 0; + public void writeToParcel(Parcel p0, int p1){} + } + static public class Mode implements Parcelable + { + protected Mode() {} + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public float getRefreshRate(){ return 0; } + public int describeContents(){ return 0; } + public int getModeId(){ return 0; } + public int getPhysicalHeight(){ return 0; } + public int getPhysicalWidth(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/DisplayCutout.java b/java/ql/test/stubs/google-android-9.0.0/android/view/DisplayCutout.java new file mode 100644 index 00000000000..ad045b47acb --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/DisplayCutout.java @@ -0,0 +1,26 @@ +// Generated automatically from android.view.DisplayCutout for testing purposes + +package android.view; + +import android.graphics.Insets; +import android.graphics.Rect; +import java.util.List; + +public class DisplayCutout +{ + protected DisplayCutout() {} + public DisplayCutout(Insets p0, Rect p1, Rect p2, Rect p3, Rect p4){} + public DisplayCutout(Rect p0, List p1){} + public List getBoundingRects(){ return null; } + public Rect getBoundingRectBottom(){ return null; } + public Rect getBoundingRectLeft(){ return null; } + public Rect getBoundingRectRight(){ return null; } + public Rect getBoundingRectTop(){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public int getSafeInsetBottom(){ return 0; } + public int getSafeInsetLeft(){ return 0; } + public int getSafeInsetRight(){ return 0; } + public int getSafeInsetTop(){ return 0; } + public int hashCode(){ return 0; } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/org/xmlpull/v1/XmlPullParser.java b/java/ql/test/stubs/google-android-9.0.0/org/xmlpull/v1/XmlPullParser.java new file mode 100644 index 00000000000..c693a5f6289 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/org/xmlpull/v1/XmlPullParser.java @@ -0,0 +1,64 @@ +// Generated automatically from org.xmlpull.v1.XmlPullParser for testing purposes + +package org.xmlpull.v1; + +import java.io.InputStream; +import java.io.Reader; + +public interface XmlPullParser +{ + Object getProperty(String p0); + String getAttributeName(int p0); + String getAttributeNamespace(int p0); + String getAttributePrefix(int p0); + String getAttributeType(int p0); + String getAttributeValue(String p0, String p1); + String getAttributeValue(int p0); + String getInputEncoding(); + String getName(); + String getNamespace(); + String getNamespace(String p0); + String getNamespacePrefix(int p0); + String getNamespaceUri(int p0); + String getPositionDescription(); + String getPrefix(); + String getText(); + String nextText(); + boolean getFeature(String p0); + boolean isAttributeDefault(int p0); + boolean isEmptyElementTag(); + boolean isWhitespace(); + char[] getTextCharacters(int[] p0); + int getAttributeCount(); + int getColumnNumber(); + int getDepth(); + int getEventType(); + int getLineNumber(); + int getNamespaceCount(int p0); + int next(); + int nextTag(); + int nextToken(); + static String FEATURE_PROCESS_DOCDECL = null; + static String FEATURE_PROCESS_NAMESPACES = null; + static String FEATURE_REPORT_NAMESPACE_ATTRIBUTES = null; + static String FEATURE_VALIDATION = null; + static String NO_NAMESPACE = null; + static String[] TYPES = null; + static int CDSECT = 0; + static int COMMENT = 0; + static int DOCDECL = 0; + static int END_DOCUMENT = 0; + static int END_TAG = 0; + static int ENTITY_REF = 0; + static int IGNORABLE_WHITESPACE = 0; + static int PROCESSING_INSTRUCTION = 0; + static int START_DOCUMENT = 0; + static int START_TAG = 0; + static int TEXT = 0; + void defineEntityReplacementText(String p0, String p1); + void require(int p0, String p1, String p2); + void setFeature(String p0, boolean p1); + void setInput(InputStream p0, String p1); + void setInput(Reader p0); + void setProperty(String p0, Object p1); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/org/xmlpull/v1/XmlSerializer.java b/java/ql/test/stubs/google-android-9.0.0/org/xmlpull/v1/XmlSerializer.java new file mode 100644 index 00000000000..f0a985adebe --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/org/xmlpull/v1/XmlSerializer.java @@ -0,0 +1,35 @@ +// Generated automatically from org.xmlpull.v1.XmlSerializer for testing purposes + +package org.xmlpull.v1; + +import java.io.OutputStream; +import java.io.Writer; + +public interface XmlSerializer +{ + Object getProperty(String p0); + String getName(); + String getNamespace(); + String getPrefix(String p0, boolean p1); + XmlSerializer attribute(String p0, String p1, String p2); + XmlSerializer endTag(String p0, String p1); + XmlSerializer startTag(String p0, String p1); + XmlSerializer text(String p0); + XmlSerializer text(char[] p0, int p1, int p2); + boolean getFeature(String p0); + int getDepth(); + void cdsect(String p0); + void comment(String p0); + void docdecl(String p0); + void endDocument(); + void entityRef(String p0); + void flush(); + void ignorableWhitespace(String p0); + void processingInstruction(String p0); + void setFeature(String p0, boolean p1); + void setOutput(OutputStream p0, String p1); + void setOutput(Writer p0); + void setPrefix(String p0, String p1); + void setProperty(String p0, Object p1); + void startDocument(String p0, Boolean p1); +} diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/Gson.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/Gson.java index bbe53dc2a5f..a269763665b 100644 --- a/java/ql/test/stubs/gson-2.8.6/com/google/gson/Gson.java +++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/Gson.java @@ -1,7 +1,38 @@ package com.google.gson; +import java.lang.reflect.Type; +import java.io.Reader; +import com.google.gson.stream.JsonReader; + public final class Gson { + public Gson() { + } + public String toJson(Object src) { return null; } + + public String toJson(Object src, Type typeOfSrc) { + return null; + } + + public T fromJson(String json, Class classOfT) throws JsonSyntaxException { + return null; + } + + public T fromJson(String json, Type typeOfT) throws JsonSyntaxException { + return null; + } + + public T fromJson(Reader json, Class classOfT) throws JsonSyntaxException, JsonIOException { + return null; + } + + public T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException { + return null; + } + + public T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException { + return null; + } } diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/GsonBuilder.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/GsonBuilder.java new file mode 100644 index 00000000000..3853cb40356 --- /dev/null +++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/GsonBuilder.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gson; + +import java.lang.reflect.Type; + +public final class GsonBuilder { + /** + * Creates a GsonBuilder instance that can be used to build Gson with various configuration + * settings. GsonBuilder follows the builder pattern, and it is typically used by first + * invoking various configuration methods to set desired options, and finally calling + * {@link #create()}. + */ + public GsonBuilder() { + } + + /** + * Constructs a GsonBuilder instance from a Gson instance. The newly constructed GsonBuilder + * has the same configuration as the previously built Gson instance. + * + * @param gson the gson instance whose configuration should by applied to a new GsonBuilder. + */ + GsonBuilder(Gson gson) { + } + + /** + * Configures Gson for custom serialization or deserialization. This method combines the + * registration of an {@link TypeAdapter}, {@link InstanceCreator}, {@link JsonSerializer}, and a + * {@link JsonDeserializer}. It is best used when a single object {@code typeAdapter} implements + * all the required interfaces for custom serialization with Gson. If a type adapter was + * previously registered for the specified {@code type}, it is overwritten. + * + *

    This registers the type specified and no other types: you must manually register related + * types! For example, applications registering {@code boolean.class} should also register {@code + * Boolean.class}. + * + * @param type the type definition for the type adapter being registered + * @param typeAdapter This object must implement at least one of the {@link TypeAdapter}, + * {@link InstanceCreator}, {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces. + * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern + */ + public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) { + return null; + } + + /** + * Register a factory for type adapters. Registering a factory is useful when the type + * adapter needs to be configured based on the type of the field being processed. Gson + * is designed to handle a large number of factories, so you should consider registering + * them to be at par with registering an individual type adapter. + * + * @since 2.1 + */ + public GsonBuilder registerTypeAdapterFactory(TypeAdapterFactory factory) { + return null; + } + + /** + * Configures Gson for custom serialization or deserialization for an inheritance type hierarchy. + * This method combines the registration of a {@link TypeAdapter}, {@link JsonSerializer} and + * a {@link JsonDeserializer}. If a type adapter was previously registered for the specified + * type hierarchy, it is overridden. If a type adapter is registered for a specific type in + * the type hierarchy, it will be invoked instead of the one registered for the type hierarchy. + * + * @param baseType the class definition for the type adapter being registered for the base class + * or interface + * @param typeAdapter This object must implement at least one of {@link TypeAdapter}, + * {@link JsonSerializer} or {@link JsonDeserializer} interfaces. + * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern + * @since 1.7 + */ + public GsonBuilder registerTypeHierarchyAdapter(Class baseType, Object typeAdapter) { + return null; + } + + /** + * Creates a {@link Gson} instance based on the current configuration. This method is free of + * side-effects to this {@code GsonBuilder} instance and hence can be called multiple times. + * + * @return an instance of Gson configured with the options currently set in this builder + */ + public Gson create() { + return null; + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonIOException.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonIOException.java new file mode 100644 index 00000000000..a59ec65e6a3 --- /dev/null +++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonIOException.java @@ -0,0 +1,4 @@ +package com.google.gson; + +public final class JsonIOException extends RuntimeException { +} diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonSyntaxException.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonSyntaxException.java new file mode 100644 index 00000000000..c4dcc69b2ba --- /dev/null +++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonSyntaxException.java @@ -0,0 +1,4 @@ +package com.google.gson; + +public final class JsonSyntaxException extends RuntimeException { +} diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/TypeAdapter.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/TypeAdapter.java new file mode 100644 index 00000000000..73e6ef993b7 --- /dev/null +++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/TypeAdapter.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gson; + +import com.google.gson.stream.JsonReader; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; + +public abstract class TypeAdapter { + /** + * Converts {@code value} to a JSON document and writes it to {@code out}. + * Unlike Gson's similar {@link Gson#toJson(JsonElement, Appendable) toJson} + * method, this write is strict. Create a {@link + * JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call + * {@link #write(com.google.gson.stream.JsonWriter, Object)} for lenient + * writing. + * + * @param value the Java object to convert. May be null. + * @since 2.2 + */ + public final void toJson(Writer out, T value) throws IOException { + } + + /** + * This wrapper method is used to make a type adapter null tolerant. In general, a + * type adapter is required to handle nulls in write and read methods. Here is how this + * is typically done:
    + *

       {@code
    +   *
    +   * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
    +   *   new TypeAdapter() {
    +   *     public Foo read(JsonReader in) throws IOException {
    +   *       if (in.peek() == JsonToken.NULL) {
    +   *         in.nextNull();
    +   *         return null;
    +   *       }
    +   *       // read a Foo from in and return it
    +   *     }
    +   *     public void write(JsonWriter out, Foo src) throws IOException {
    +   *       if (src == null) {
    +   *         out.nullValue();
    +   *         return;
    +   *       }
    +   *       // write src as JSON to out
    +   *     }
    +   *   }).create();
    +   * }
    + * You can avoid this boilerplate handling of nulls by wrapping your type adapter with + * this method. Here is how we will rewrite the above example: + *
       {@code
    +   *
    +   * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
    +   *   new TypeAdapter() {
    +   *     public Foo read(JsonReader in) throws IOException {
    +   *       // read a Foo from in and return it
    +   *     }
    +   *     public void write(JsonWriter out, Foo src) throws IOException {
    +   *       // write src as JSON to out
    +   *     }
    +   *   }.nullSafe()).create();
    +   * }
    + * Note that we didn't need to check for nulls in our type adapter after we used nullSafe. + */ + public final TypeAdapter nullSafe() { + return null; + } + + /** + * Converts {@code value} to a JSON document. Unlike Gson's similar {@link + * Gson#toJson(Object) toJson} method, this write is strict. Create a {@link + * JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call + * {@link #write(com.google.gson.stream.JsonWriter, Object)} for lenient + * writing. + * + * @param value the Java object to convert. May be null. + * @since 2.2 + */ + public final String toJson(T value) { + return null; + } + + /** + * Reads one JSON value (an array, object, string, number, boolean or null) + * and converts it to a Java object. Returns the converted object. + * + * @return the converted Java object. May be null. + */ + public abstract T read(JsonReader in) throws IOException; + + /** + * Converts the JSON document in {@code in} to a Java object. Unlike Gson's + * similar {@link Gson#fromJson(java.io.Reader, Class) fromJson} method, this + * read is strict. Create a {@link JsonReader#setLenient(boolean) lenient} + * {@code JsonReader} and call {@link #read(JsonReader)} for lenient reading. + * + * @return the converted Java object. May be null. + * @since 2.2 + */ + public final T fromJson(Reader in) throws IOException { + return null; + } + + /** + * Converts the JSON document in {@code json} to a Java object. Unlike Gson's + * similar {@link Gson#fromJson(String, Class) fromJson} method, this read is + * strict. Create a {@link JsonReader#setLenient(boolean) lenient} {@code + * JsonReader} and call {@link #read(JsonReader)} for lenient reading. + * + * @return the converted Java object. May be null. + * @since 2.2 + */ + public final T fromJson(String json) throws IOException { + return null; + } +} diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/TypeAdapterFactory.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/TypeAdapterFactory.java new file mode 100644 index 00000000000..d6cc8133712 --- /dev/null +++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/TypeAdapterFactory.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gson; + +import com.google.gson.reflect.TypeToken; + +public interface TypeAdapterFactory { + + /** + * Returns a type adapter for {@code type}, or null if this factory doesn't + * support {@code type}. + */ + TypeAdapter create(Gson gson, TypeToken type); +} \ No newline at end of file diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/reflect/TypeToken.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/reflect/TypeToken.java new file mode 100644 index 00000000000..a35b2a45b85 --- /dev/null +++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/reflect/TypeToken.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gson.reflect; + +/** + * Represents a generic type {@code T}. Java doesn't yet provide a way to + * represent generic types, so this class does. Forces clients to create a + * subclass of this class which enables retrieval the type information even at + * runtime. + * + *

    For example, to create a type literal for {@code List}, you can + * create an empty anonymous inner class: + * + *

    + * {@code TypeToken> list = new TypeToken>() {};} + * + *

    This syntax cannot be used to create type literals that have wildcard + * parameters, such as {@code Class} or {@code List}. + * + * @author Bob Lee + * @author Sven Mawson + * @author Jesse Wilson + */ +public class TypeToken { + + /** + * Constructs a new type literal. Derives represented class from type + * parameter. + * + *

    Clients create an empty anonymous subclass. Doing so embeds the type + * parameter in the anonymous class's type hierarchy so we can reconstitute it + * at runtime despite erasure. + */ + protected TypeToken() { + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/stream/JsonReader.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/stream/JsonReader.java new file mode 100644 index 00000000000..5d0d2ad112f --- /dev/null +++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/stream/JsonReader.java @@ -0,0 +1,66 @@ +package com.google.gson.stream; + +import java.io.Closeable; +import java.io.IOException; +import java.io.Reader; + +public class JsonReader implements Closeable { + public JsonReader(Reader in) { + } + + public final void setLenient(boolean lenient) { + } + + public final boolean isLenient() { + return false; + } + + public void beginArray() throws IOException { + } + + public void endArray() throws IOException { + } + + public void beginObject() throws IOException { + } + + public void endObject() throws IOException { + } + + public boolean hasNext() throws IOException { + return false; + } + + public String nextName() throws IOException { + return null; + } + + public String nextString() throws IOException { + return null; + } + + public boolean nextBoolean() throws IOException { + return false; + } + + public void nextNull() throws IOException { + } + + public double nextDouble() throws IOException { + return -1; + } + + public long nextLong() throws IOException { + return -1; + } + + public int nextInt() throws IOException { + return -1; + } + + public void close() throws IOException { + } + + public void skipValue() throws IOException { + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java new file mode 100644 index 00000000000..b808ccf4e1f --- /dev/null +++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gson.typeadapters; + +import java.io.IOException; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; + +/** + * Adapts values whose runtime type may differ from their declaration type. This + * is necessary when a field's type is not the same type that GSON should create + * when deserializing that field. For example, consider these types: + *

       {@code
    + *   abstract class Shape {
    + *     int x;
    + *     int y;
    + *   }
    + *   class Circle extends Shape {
    + *     int radius;
    + *   }
    + *   class Rectangle extends Shape {
    + *     int width;
    + *     int height;
    + *   }
    + *   class Diamond extends Shape {
    + *     int width;
    + *     int height;
    + *   }
    + *   class Drawing {
    + *     Shape bottomShape;
    + *     Shape topShape;
    + *   }
    + * }
    + *

    Without additional type information, the serialized JSON is ambiguous. Is + * the bottom shape in this drawing a rectangle or a diamond?

       {@code
    + *   {
    + *     "bottomShape": {
    + *       "width": 10,
    + *       "height": 5,
    + *       "x": 0,
    + *       "y": 0
    + *     },
    + *     "topShape": {
    + *       "radius": 2,
    + *       "x": 4,
    + *       "y": 1
    + *     }
    + *   }}
    + * This class addresses this problem by adding type information to the + * serialized JSON and honoring that type information when the JSON is + * deserialized:
       {@code
    + *   {
    + *     "bottomShape": {
    + *       "type": "Diamond",
    + *       "width": 10,
    + *       "height": 5,
    + *       "x": 0,
    + *       "y": 0
    + *     },
    + *     "topShape": {
    + *       "type": "Circle",
    + *       "radius": 2,
    + *       "x": 4,
    + *       "y": 1
    + *     }
    + *   }}
    + * Both the type field name ({@code "type"}) and the type labels ({@code + * "Rectangle"}) are configurable. + * + *

    Registering Types

    + * Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field + * name to the {@link #of} factory method. If you don't supply an explicit type + * field name, {@code "type"} will be used.
       {@code
    + *   RuntimeTypeAdapterFactory shapeAdapterFactory
    + *       = RuntimeTypeAdapterFactory.of(Shape.class, "type");
    + * }
    + * Next register all of your subtypes. Every subtype must be explicitly + * registered. This protects your application from injection attacks. If you + * don't supply an explicit type label, the type's simple name will be used. + *
       {@code
    + *   shapeAdapterFactory.registerSubtype(Rectangle.class, "Rectangle");
    + *   shapeAdapterFactory.registerSubtype(Circle.class, "Circle");
    + *   shapeAdapterFactory.registerSubtype(Diamond.class, "Diamond");
    + * }
    + * Finally, register the type adapter factory in your application's GSON builder: + *
       {@code
    + *   Gson gson = new GsonBuilder()
    + *       .registerTypeAdapterFactory(shapeAdapterFactory)
    + *       .create();
    + * }
    + * Like {@code GsonBuilder}, this API supports chaining:
       {@code
    + *   RuntimeTypeAdapterFactory shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
    + *       .registerSubtype(Rectangle.class)
    + *       .registerSubtype(Circle.class)
    + *       .registerSubtype(Diamond.class);
    + * }
    + * + *

    Serialization and deserialization

    + * In order to serialize and deserialize a polymorphic object, + * you must specify the base type explicitly. + *
       {@code
    + *   Diamond diamond = new Diamond();
    + *   String json = gson.toJson(diamond, Shape.class);
    + * }
    + * And then: + *
       {@code
    + *   Shape shape = gson.fromJson(json, Shape.class);
    + * }
    + */ +public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory { + /** + * Creates a new runtime type adapter using for {@code baseType} using {@code + * typeFieldName} as the type field name. Type field names are case sensitive. + * {@code maintainType} flag decide if the type will be stored in pojo or not. + */ + public static RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName, boolean maintainType) { + return null; + } + + /** + * Creates a new runtime type adapter using for {@code baseType} using {@code + * typeFieldName} as the type field name. Type field names are case sensitive. + */ + public static RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName) { + return null; + } + + /** + * Creates a new runtime type adapter for {@code baseType} using {@code "type"} as + * the type field name. + */ + public static RuntimeTypeAdapterFactory of(Class baseType) { + return null; + } + + /** + * Registers {@code type} identified by {@code label}. Labels are case + * sensitive. + * + * @throws IllegalArgumentException if either {@code type} or {@code label} + * have already been registered on this type adapter. + */ + public RuntimeTypeAdapterFactory registerSubtype(Class type, String label) { + return null; + } + + /** + * Registers {@code type} identified by its {@link Class#getSimpleName simple + * name}. Labels are case sensitive. + * + * @throws IllegalArgumentException if either {@code type} or its simple name + * have already been registered on this type adapter. + */ + public RuntimeTypeAdapterFactory registerSubtype(Class type) { + return null; + } + + public TypeAdapter create(Gson gson, TypeToken type) { + return null; + } +} diff --git a/java/ql/test/stubs/guava-30.0/com/google/common/reflect/TypeToken.java b/java/ql/test/stubs/guava-30.0/com/google/common/reflect/TypeToken.java new file mode 100644 index 00000000000..b6ac36271b9 --- /dev/null +++ b/java/ql/test/stubs/guava-30.0/com/google/common/reflect/TypeToken.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2006 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.reflect; + +import java.io.Serializable; + +public abstract class TypeToken implements Serializable { +} \ No newline at end of file diff --git a/java/ql/test/stubs/jackson-core-2.12/com/fasterxml/jackson/core/TreeNode.java b/java/ql/test/stubs/jackson-core-2.12/com/fasterxml/jackson/core/TreeNode.java index 338f58961da..7a6cf357a4d 100644 --- a/java/ql/test/stubs/jackson-core-2.12/com/fasterxml/jackson/core/TreeNode.java +++ b/java/ql/test/stubs/jackson-core-2.12/com/fasterxml/jackson/core/TreeNode.java @@ -9,7 +9,9 @@ package com.fasterxml.jackson.core; import java.util.Iterator; public interface TreeNode { - JsonParser.NumberType numberType(); + default JsonParser.NumberType numberType() { + return null; + } int size(); @@ -35,6 +37,8 @@ public interface TreeNode { TreeNode at(String jsonPointerExpression) throws IllegalArgumentException; - JsonParser traverse(); + default JsonParser traverse() { + return null; + } } diff --git a/java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/JsonSerializable.java b/java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/JsonSerializable.java new file mode 100644 index 00000000000..78c5f874642 --- /dev/null +++ b/java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/JsonSerializable.java @@ -0,0 +1,10 @@ +package com.fasterxml.jackson.databind; + +import java.io.IOException; + +// This interface does not actually have these types.. This is a significantly oversimplified stub. +public interface JsonSerializable { + public void serialize(Object gen, Object serializers) throws IOException; + + public void serializeWithType(Object gen, Object serializers, Object typeSer) throws IOException; +} diff --git a/java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/ArrayNode.java b/java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/ArrayNode.java new file mode 100644 index 00000000000..3f0d995623e --- /dev/null +++ b/java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/ArrayNode.java @@ -0,0 +1,10 @@ +package com.fasterxml.jackson.databind.node; + +import com.fasterxml.jackson.databind.JsonNode; + +public abstract class ArrayNode extends ContainerNode { + + public ArrayNode add(JsonNode value) { + return null; + } +} diff --git a/java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/BaseJsonNode.java b/java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/BaseJsonNode.java new file mode 100644 index 00000000000..85564f3eb34 --- /dev/null +++ b/java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/BaseJsonNode.java @@ -0,0 +1,7 @@ +package com.fasterxml.jackson.databind.node; + +import com.fasterxml.jackson.databind.JsonNode; + +public abstract class BaseJsonNode extends JsonNode { + +} diff --git a/java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/ContainerNode.java b/java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/ContainerNode.java new file mode 100644 index 00000000000..7a52ca08e75 --- /dev/null +++ b/java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/ContainerNode.java @@ -0,0 +1,5 @@ +package com.fasterxml.jackson.databind.node; + +public abstract class ContainerNode> extends BaseJsonNode { + +} diff --git a/java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/ObjectNode.java b/java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/ObjectNode.java new file mode 100644 index 00000000000..b19f9f9e556 --- /dev/null +++ b/java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/ObjectNode.java @@ -0,0 +1,9 @@ +package com.fasterxml.jackson.databind.node; + +public abstract class ObjectNode extends ContainerNode { + public ArrayNode putArray(String propertyName) + { + return null; + } + +} diff --git a/java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/POJONode.java b/java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/POJONode.java new file mode 100644 index 00000000000..ab268242d87 --- /dev/null +++ b/java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/POJONode.java @@ -0,0 +1,76 @@ +package com.fasterxml.jackson.databind.node; + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.List; + +public class POJONode extends ValueNode { + protected final Object _value; + + public POJONode(Object v) { + _value = v; + } + + @Override + public boolean equals(Object o) { + return false; + } + + public String toString() { + return null; + } + + @Override + public T deepCopy() { + return null; + } + + @Override + public JsonNode get(int index) { + return null; + } + + @Override + public JsonNode path(String fieldName) { + return null; + } + + @Override + public JsonNode path(int index) { + return null; + } + + @Override + public String asText() { + return null; + } + + @Override + public JsonNode findValue(String fieldName) { + return null; + } + + @Override + public JsonNode findPath(String fieldName) { + return null; + } + + @Override + public JsonNode findParent(String fieldName) { + return null; + } + + @Override + public List findValues(String fieldName, List foundSoFar) { + return null; + } + + @Override + public List findValuesAsText(String fieldName, List foundSoFar) { + return null; + } + + @Override + public List findParents(String fieldName, List foundSoFar) { + return null; + } +} diff --git a/java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/ValueNode.java b/java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/ValueNode.java new file mode 100644 index 00000000000..318818bc9af --- /dev/null +++ b/java/ql/test/stubs/jackson-databind-2.12/com/fasterxml/jackson/databind/node/ValueNode.java @@ -0,0 +1,5 @@ +package com.fasterxml.jackson.databind.node; + +public abstract class ValueNode extends BaseJsonNode { + +} diff --git a/java/ql/test/stubs/jakarta-mail-2.0.1/jakarta/mail/Authenticator.java b/java/ql/test/stubs/jakarta-mail-2.0.1/jakarta/mail/Authenticator.java new file mode 100644 index 00000000000..594f3876f19 --- /dev/null +++ b/java/ql/test/stubs/jakarta-mail-2.0.1/jakarta/mail/Authenticator.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.mail; + +public abstract class Authenticator { +} diff --git a/java/ql/test/stubs/jakarta-mail-2.0.1/jakarta/mail/PasswordAuthentication.java b/java/ql/test/stubs/jakarta-mail-2.0.1/jakarta/mail/PasswordAuthentication.java new file mode 100644 index 00000000000..20751b7f7d9 --- /dev/null +++ b/java/ql/test/stubs/jakarta-mail-2.0.1/jakarta/mail/PasswordAuthentication.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the terms of the Eclipse + * Public License v. 2.0, which is available at http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary Licenses when the + * conditions for such availability set forth in the Eclipse Public License v. 2.0 are satisfied: + * GNU General Public License, version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.mail; + +public final class PasswordAuthentication { + public PasswordAuthentication(String userName, String password) {} + + public String getUserName() { + return null; + } + + public String getPassword() { + return null; + } + +} diff --git a/java/ql/test/stubs/jakarta-mail-2.0.1/jakarta/mail/Session.java b/java/ql/test/stubs/jakarta-mail-2.0.1/jakarta/mail/Session.java new file mode 100644 index 00000000000..6f525babdae --- /dev/null +++ b/java/ql/test/stubs/jakarta-mail-2.0.1/jakarta/mail/Session.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the terms of the Eclipse + * Public License v. 2.0, which is available at http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary Licenses when the + * conditions for such availability set forth in the Eclipse Public License v. 2.0 are satisfied: + * GNU General Public License, version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package jakarta.mail; + +import java.lang.reflect.*; +import java.io.*; +import java.net.*; +import java.security.*; +import java.util.Properties; + +public final class Session { + public static Session getInstance(Properties props, Authenticator authenticator) { + return null; + } + + public static Session getInstance(Properties props) { + return null; + } + + public static synchronized Session getDefaultInstance(Properties props, + Authenticator authenticator) { + return null; + } + + public static Session getDefaultInstance(Properties props) { + return null; + } + + public synchronized void setDebug(boolean debug) {} + + public synchronized boolean getDebug() { + return false; + } + + public synchronized void setDebugOut(PrintStream out) {} + + public synchronized PrintStream getDebugOut() { + return null; + } + + public synchronized Provider[] getProviders() { + return null; + } + + public synchronized Provider getProvider(String protocol) throws NoSuchProviderException { + return null; + } + + public synchronized void setProvider(Provider provider) throws NoSuchProviderException {} + + public PasswordAuthentication requestPasswordAuthentication(InetAddress addr, int port, + String protocol, String prompt, String defaultUserName) { + return null; + } + + public Properties getProperties() { + return null; + } + + public String getProperty(String name) { + return null; + } + + public synchronized void addProvider(Provider provider) {} + + public synchronized void setProtocolForAddress(String addresstype, String protocol) {} + +} diff --git a/java/ql/test/stubs/netty-4.1.x/LICENSE.txt b/java/ql/test/stubs/netty-4.1.x/LICENSE.txt new file mode 100644 index 00000000000..5f3c6540d8f --- /dev/null +++ b/java/ql/test/stubs/netty-4.1.x/LICENSE.txt @@ -0,0 +1,203 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + \ No newline at end of file diff --git a/java/ql/test/stubs/netty-4.1.x/io/netty/buffer/ByteBuf.java b/java/ql/test/stubs/netty-4.1.x/io/netty/buffer/ByteBuf.java new file mode 100644 index 00000000000..cb327fdf002 --- /dev/null +++ b/java/ql/test/stubs/netty-4.1.x/io/netty/buffer/ByteBuf.java @@ -0,0 +1,19 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.buffer; + +public abstract class ByteBuf implements Comparable { +} \ No newline at end of file diff --git a/java/ql/test/stubs/netty-4.1.x/io/netty/handler/codec/http/cookie/Cookie.java b/java/ql/test/stubs/netty-4.1.x/io/netty/handler/codec/http/cookie/Cookie.java new file mode 100644 index 00000000000..ecb0c689438 --- /dev/null +++ b/java/ql/test/stubs/netty-4.1.x/io/netty/handler/codec/http/cookie/Cookie.java @@ -0,0 +1,146 @@ +/* + * Copyright 2015 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.http.cookie; + +/** + * An interface defining an + * HTTP cookie. + */ +public interface Cookie extends Comparable { + + /** + * Constant for undefined MaxAge attribute value. + */ + long UNDEFINED_MAX_AGE = Long.MIN_VALUE; + + /** + * Returns the name of this {@link Cookie}. + * + * @return The name of this {@link Cookie} + */ + String name(); + + /** + * Returns the value of this {@link Cookie}. + * + * @return The value of this {@link Cookie} + */ + String value(); + + /** + * Sets the value of this {@link Cookie}. + * + * @param value The value to set + */ + void setValue(String value); + + /** + * Returns true if the raw value of this {@link Cookie}, + * was wrapped with double quotes in original Set-Cookie header. + * + * @return If the value of this {@link Cookie} is to be wrapped + */ + boolean wrap(); + + /** + * Sets true if the value of this {@link Cookie} + * is to be wrapped with double quotes. + * + * @param wrap true if wrap + */ + void setWrap(boolean wrap); + + /** + * Returns the domain of this {@link Cookie}. + * + * @return The domain of this {@link Cookie} + */ + String domain(); + + /** + * Sets the domain of this {@link Cookie}. + * + * @param domain The domain to use + */ + void setDomain(String domain); + + /** + * Returns the path of this {@link Cookie}. + * + * @return The {@link Cookie}'s path + */ + String path(); + + /** + * Sets the path of this {@link Cookie}. + * + * @param path The path to use for this {@link Cookie} + */ + void setPath(String path); + + /** + * Returns the maximum age of this {@link Cookie} in seconds or {@link Cookie#UNDEFINED_MAX_AGE} if unspecified + * + * @return The maximum age of this {@link Cookie} + */ + long maxAge(); + + /** + * Sets the maximum age of this {@link Cookie} in seconds. + * If an age of {@code 0} is specified, this {@link Cookie} will be + * automatically removed by browser because it will expire immediately. + * If {@link Cookie#UNDEFINED_MAX_AGE} is specified, this {@link Cookie} will be removed when the + * browser is closed. + * + * @param maxAge The maximum age of this {@link Cookie} in seconds + */ + void setMaxAge(long maxAge); + + /** + * Checks to see if this {@link Cookie} is secure + * + * @return True if this {@link Cookie} is secure, otherwise false + */ + boolean isSecure(); + + /** + * Sets the security getStatus of this {@link Cookie} + * + * @param secure True if this {@link Cookie} is to be secure, otherwise false + */ + void setSecure(boolean secure); + + /** + * Checks to see if this {@link Cookie} can only be accessed via HTTP. + * If this returns true, the {@link Cookie} cannot be accessed through + * client side script - But only if the browser supports it. + * For more information, please look here + * + * @return True if this {@link Cookie} is HTTP-only or false if it isn't + */ + boolean isHttpOnly(); + + /** + * Determines if this {@link Cookie} is HTTP only. + * If set to true, this {@link Cookie} cannot be accessed by a client + * side script. However, this works only if the browser supports it. + * For for information, please look + * here. + * + * @param httpOnly True if the {@link Cookie} is HTTP only, otherwise false. + */ + void setHttpOnly(boolean httpOnly); +} \ No newline at end of file diff --git a/java/ql/test/stubs/ratpack-1.9.x/LICENSE.txt b/java/ql/test/stubs/ratpack-1.9.x/LICENSE.txt new file mode 100644 index 00000000000..e0908d496f6 --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/LICENSE.txt @@ -0,0 +1,15 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + \ No newline at end of file diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/form/Form.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/form/Form.java new file mode 100644 index 00000000000..dd4c14991b3 --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/form/Form.java @@ -0,0 +1,108 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.core.form; + +import ratpack.core.handling.Context; +import ratpack.core.parse.Parse; +import ratpack.func.Nullable; +import ratpack.func.MultiValueMap; + +import java.util.List; + +/** + * An uploaded form. + *

    + * The form is modelled as a {@link MultiValueMap}, with extra methods for dealing with file uploads. + * That is, uploaded files are not visible via the methods provided by {@link MultiValueMap}. + *

    + * All instances of this type are immutable. + * Calling any mutative method of {@link MultiValueMap} will result in an {@link UnsupportedOperationException}. + *

    Example usage:

    + *
    {@code
    + * import ratpack.core.handling.Handler;
    + * import ratpack.core.handling.Context;
    + * import ratpack.core.form.Form;
    + * import ratpack.core.form.UploadedFile;
    + *
    + * import java.util.List;
    + *
    + * public class Example {
    + *   public static class FormHandler implements Handler {
    + *     public void handle(Context context) throws Exception {
    + *       context.parse(Form.class).then(form -> {
    + *         UploadedFile file = form.file("someFile.txt");
    + *         String param = form.get("param");
    + *         List multi = form.getAll("multi");
    + *         context.render("form uploaded!");
    + *       });
    + *     }
    + *   }
    + * }
    + * }
    + * + *

    + * To include the query parameters from the request in the parsed form, use {@link Form#form(boolean)}. + * This can be useful if you want to support both {@code GET} and {@code PUT} submission with a single handler. + */ +public interface Form extends MultiValueMap { + + /** + * Return the first uploaded file with the given name. + * + * @param name The name of the uploaded file in the form + * @return The uploaded file, or {@code null} if no file was uploaded by that name + */ + @Nullable + UploadedFile file(String name); + + /** + * Return all of the uploaded files with the given name. + * + * @param name The name of the uploaded files in the form + * @return The uploaded files, or an empty list if no files were uploaded by that name + */ + List files(String name); + + /** + * Returns all of the uploaded files. + * + * @return all of the uploaded files. + */ + MultiValueMap files(); + + /** + * Creates a {@link Context#parse parseable object} to parse a request body into a {@link Form}. + *

    + * Default options will be used (no query parameters included). + * + * @return a parse object + */ + static Parse form() { + return null; + } + + /** + * Creates a {@link Context#parse parseable object} to parse a request body into a {@link Form}. + * + * @param includeQueryParams whether to include the query parameters from the request in the parsed form + * @return a parse object + */ + static Parse form(boolean includeQueryParams) { + return null; + } + +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/form/FormParseOpts.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/form/FormParseOpts.java new file mode 100644 index 00000000000..7a338d428c1 --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/form/FormParseOpts.java @@ -0,0 +1,29 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.core.form; + +/** + * Options for parsing a {@link Form}. + */ +public interface FormParseOpts { + /** + * Whether to include the query parameters from the request in the parsed form. + * + * @return whether to include the query parameters from the request in the parsed form + */ + boolean isIncludeQueryParams(); +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/form/UploadedFile.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/form/UploadedFile.java new file mode 100644 index 00000000000..ab0c7b8facd --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/form/UploadedFile.java @@ -0,0 +1,37 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.core.form; + +import ratpack.core.http.TypedData; +import ratpack.func.Nullable; + +/** + * A file that was uploaded via a form. + * + * @see Form + */ +public interface UploadedFile extends TypedData { + + /** + * The name given for the file. + * + * @return The name given for the file, or {@code null} if no name was provided. + */ + @Nullable + String getFileName(); + +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/handling/Context.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/handling/Context.java new file mode 100644 index 00000000000..39fa9929b46 --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/handling/Context.java @@ -0,0 +1,472 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.core.handling; + +import com.google.common.reflect.TypeToken; +import ratpack.core.http.*; +import ratpack.core.parse.Parse; +import ratpack.core.render.*; +import ratpack.exec.api.NonBlocking; +import ratpack.exec.registry.NotInRegistryException; +import ratpack.exec.registry.Registry; +import ratpack.exec.Promise; + +import java.nio.file.Path; +import java.time.Instant; +import java.util.Arrays; +import java.util.Date; +import java.util.Optional; + +/** + * The context of an individual {@link Handler} invocation. + *

    + * It provides: + *

      + *
    • Access the HTTP {@link #getRequest() request} and {@link #getResponse() response}
    • + *
    • Delegation (via the {@link #next} and {@link #insert} family of methods)
    • + *
    • Access to contextual objects (see below)
    • + *
    • Convenience for common handler operations
    • + *
    + * + *

    Contextual objects

    + *

    + * A context is also a {@link Registry} of objects. + * Arbitrary objects can be "pushed" into the context for use by downstream handlers. + *

    + * There are some significant contextual objects that drive key infrastructure. + * For example, error handling is based on informing the contextual {@link ServerErrorHandler} of exceptions. + * The error handling strategy for an application can be changed by pushing a new implementation of this interface into the context that is used downstream. + *

    + * See {@link #insert(Handler...)} for more on how to do this. + *

    Default contextual objects

    + *

    There is also a set of default objects that are made available via the Ratpack infrastructure: + *

      + *
    • A {@link FileSystemBinding} that is the application {@link ServerConfig#getBaseDir()}
    • + *
    • A {@link MimeTypes} implementation
    • + *
    • A {@link ServerErrorHandler}
    • + *
    • A {@link ClientErrorHandler}
    • + *
    • A {@link PublicAddress}
    • + *
    • A {@link Redirector}
    • + *
    + */ +public interface Context extends Registry { + + /** + * A type token for this type. + * + * @since 1.1 + */ + TypeToken TYPE = null; + + /** + * Returns this. + * + * @return this. + */ + Context getContext(); + + /** + * The HTTP request. + * + * @return The HTTP request. + */ + Request getRequest(); + + /** + * The HTTP response. + * + * @return The HTTP response. + */ + Response getResponse(); + + /** + * Delegate handling to the next handler in line. + *

    + * The request and response of this object should not be accessed after this method is called. + */ + @NonBlocking + void next(); + + /** + * Invokes the next handler, after adding the given registry. + *

    + * The given registry is appended to the existing. + * This means that it can shadow objects previously available. + *

    {@code
    +   * import ratpack.exec.registry.Registry;
    +   * import ratpack.test.embed.EmbeddedApp;
    +   *
    +   * import static org.junit.Assert.assertEquals;
    +   *
    +   * public class Example {
    +   *
    +   *   public static void main(String... args) throws Exception {
    +   *     EmbeddedApp.fromHandlers(chain -> chain
    +   *         .all(ctx -> ctx.next(Registry.single("foo")))
    +   *         .all(ctx -> ctx.render(ctx.get(String.class)))
    +   *     ).test(httpClient -> {
    +   *       assertEquals("foo", httpClient.getText());
    +   *     });
    +   *   }
    +   * }
    +   * }
    + * + * @param registry The registry to make available for subsequent handlers. + */ + @NonBlocking + void next(Registry registry); + + /** + * Inserts some handlers into the pipeline, then delegates to the first. + *

    + * The request and response of this object should not be accessed after this method is called. + * + * @param handlers The handlers to insert. + */ + @NonBlocking + void insert(Handler... handlers); + + /** + * Inserts some handlers into the pipeline to execute with the given registry, then delegates to the first. + *

    + * The given registry is only applicable to the inserted handlers. + *

    + * Almost always, the registry should be a super set of the current registry. + * + * @param handlers The handlers to insert + * @param registry The registry for the inserted handlers + */ + @NonBlocking + void insert(Registry registry, Handler... handlers); + + + /** + * Handles any error thrown during request handling. + *

    + * Uncaught exceptions that are thrown any time during request handling will end up here. + *

    + * Forwards the exception to the {@link ServerErrorHandler} within the current registry. + * Add an implementation of this interface to the registry to handle errors. + * The default implementation is not suitable for production usage. + *

    + * If the exception is-a {@link ClientErrorException}, + * the {@link #clientError(int)} method will be called with the exception's status code + * instead of being forward to the server error handler. + * + * @param throwable The exception that occurred + */ + @NonBlocking + void error(Throwable throwable); + + /** + * Forwards the error to the {@link ClientErrorHandler} in this service. + * + * The default configuration of Ratpack includes a {@link ClientErrorHandler} in all contexts. + * A {@link NotInRegistryException} will only be thrown if a very custom service setup is being used. + * + * @param statusCode The 4xx range status code that indicates the error type + * @throws NotInRegistryException if no {@link ClientErrorHandler} can be found in the service + */ + @NonBlocking + void clientError(int statusCode) throws NotInRegistryException; + + /** + * Render the given object, using the rendering framework. + *

    + * The first {@link Renderer}, that is able to render the given object will be delegated to. + * If the given argument is {@code null}, this method will have the same effect as {@link #clientError(int) clientError(404)}. + *

    + * If no renderer can be found for the given type, a {@link NoSuchRendererException} will be given to {@link #error(Throwable)}. + *

    + * If a renderer throws an exception during its execution it will be wrapped in a {@link RendererException} and given to {@link #error(Throwable)}. + *

    + * Ratpack has built in support for rendering the following types: + *

      + *
    • {@link Path}
    • + *
    • {@link CharSequence}
    • + *
    • {@link JsonRender} (Typically created via {@link Jackson#json(Object)})
    • + *
    • {@link Promise} (renders the promised value, using this {@code render()} method)
    • + *
    • {@link Publisher} (converts the publisher to a promise using {@link Streams#toPromise(Publisher)} and renders it)
    • + *
    • {@link Renderable} (Delegates to the {@link Renderable#render(Context)} method of the object)
    • + *
    + *

    + * See {@link Renderer} for more on how to contribute to the rendering framework. + *

    + * The object-to-render will be decorated by all registered {@link RenderableDecorator} whose type is exactly equal to the type of the object-to-render, before being passed to the selected renderer. + * + * @param object The object-to-render + * @throws NoSuchRendererException if no suitable renderer can be found + */ + @NonBlocking + void render(Object object) throws NoSuchRendererException; + + /** + * Returns the request header with the specified name. + *

    + * If there is more than value for the specified header, the first value is returned. + *

    + * Shorthand for {@code getRequest().getHeaders().get(String)}. + * + * @param name the case insensitive name of the header to get retrieve the first value of + * @return the header value or {@code null} if there is no such header + * @since 1.4 + */ + default Optional header(CharSequence name) { + return Optional.ofNullable(getRequest().getHeaders().get(name)); + } + + /** + * Sets a response header. + *

    + * Any previously set values for the header will be removed. + *

    + * Shorthand for {@code getResponse().getHeaders().set(CharSequence, Iterable)}. + * + * @param name the name of the header to set + * @param values the header values + * @return {@code this} + * @since 1.4 + * @see MutableHeaders#set(CharSequence, Iterable) + */ + default Context header(CharSequence name, Object... values) { + getResponse().getHeaders().set(name, Arrays.asList(values)); + return this; + } + + /** + * Sends a temporary redirect response (i.e. 302) to the client using the specified redirect location. + * + * @param to the location to redirect to + * @see #redirect(int, Object) + * @since 1.3 + */ + void redirect(Object to); + + /** + * Sends a redirect response to the given location, and with the given status code. + *

    + * This method retrieves the {@link Redirector} from the registry, and forwards the given arguments along with {@code this} context. + * + * @param code The status code of the redirect + * @param to the redirect location URL + * @see Redirector + * @since 1.3 + */ + void redirect(int code, Object to); + + /** + * Convenience method for handling last-modified based HTTP caching. + *

    + * The given date is the "last modified" value of the response. + * If the client sent an "If-Modified-Since" header that is of equal or greater value than date, + * a 304 will be returned to the client. + * Otherwise, the given runnable will be executed (it should send a response) + * and the "Last-Modified" header will be set by this method. + * + * @param lastModified the effective last modified date of the response + * @param serve the response sending action if the response needs to be sent + */ + @NonBlocking + default void lastModified(Date lastModified, Runnable serve) { + lastModified(lastModified.toInstant(), serve); + } + + /** + * Convenience method for handling last-modified based HTTP caching. + *

    + * The given date is the "last modified" value of the response. + * If the client sent an "If-Modified-Since" header that is of equal or greater value than date, + * a 304 will be returned to the client. + * Otherwise, the given runnable will be executed (it should send a response) + * and the "Last-Modified" header will be set by this method. + * + * @param lastModified the effective last modified date of the response + * @param serve the response sending action if the response needs to be sent + * @since 1.4 + */ + @NonBlocking + void lastModified(Instant lastModified, Runnable serve); + + /** + * Parse the request into the given type, using no options (or more specifically an instance of {@link NullParseOpts} as the options). + *

    + * The code sample is functionally identical to the sample given for the {@link #parse(Parse)} variant + *

    {@code
    +   * import ratpack.core.handling.Handler;
    +   * import ratpack.core.handling.Context;
    +   * import ratpack.core.form.Form;
    +   *
    +   * public class FormHandler implements Handler {
    +   *   public void handle(Context context) {
    +   *     context.parse(Form.class).then(form -> context.render(form.get("someFormParam")));
    +   *   }
    +   * }
    +   * }
    + *

    + * That is, it is a convenient form of {@code parse(Parse.of(T))}. + * + * @param type the type to parse to + * @param the type to parse to + * @return a promise for the parsed object + */ + Promise parse(Class type); + + /** + * Parse the request into the given type, using no options (or more specifically an instance of {@link NullParseOpts} as the options). + *

    + * The code sample is functionally identical to the sample given for the {@link #parse(Parse)} variant… + *

    {@code
    +   * import ratpack.core.handling.Handler;
    +   * import ratpack.core.handling.Context;
    +   * import ratpack.core.form.Form;
    +   * import com.google.common.reflect.TypeToken;
    +   *
    +   * public class FormHandler implements Handler {
    +   *   public void handle(Context context) {
    +   *     context.parse(new TypeToken() {}).then(form -> context.render(form.get("someFormParam")));
    +   *   }
    +   * }
    +   * }
    + *

    + * That is, it is a convenient form of {@code parse(Parse.of(T))}. + * + * @param type the type to parse to + * @param the type to parse to + * @return a promise for the parsed object + */ + Promise parse(TypeToken type); + + /** + * Constructs a {@link Parse} from the given args and delegates to {@link #parse(Parse)}. + * + * @param type The type to parse to + * @param options The parse options + * @param The type to parse to + * @param The type of the parse opts + * @return a promise for the parsed object + */ + Promise parse(Class type, O options); + + /** + * Constructs a {@link Parse} from the given args and delegates to {@link #parse(Parse)}. + * + * @param type The type to parse to + * @param options The parse options + * @param The type to parse to + * @param The type of the parse opts + * @return a promise for the parsed object + */ + Promise parse(TypeToken type, O options); + + /** + * Parses the request body into an object. + *

    + * How to parse the request is determined by the given {@link Parse} object. + * + *

    Parser Resolution

    + *

    + * Parser resolution happens as follows: + *

      + *
    1. All {@link Parser parsers} are retrieved from the context registry (i.e. {@link #getAll(Class) getAll(Parser.class)});
    2. + *
    3. Found parsers are checked (in order returned by {@code getAll()}) for compatibility with the options type;
    4. + *
    5. If a parser is found that is compatible, its {@link Parser#parse(Context, TypedData, Parse)} method is called;
    6. + *
    7. If the parser returns {@code null} the next parser will be tried, if it returns a value it will be returned by this method;
    8. + *
    9. If no compatible parser could be found, a {@link NoSuchParserException} will be thrown.
    10. + *
    + * + *

    Parser Compatibility

    + *

    + * A parser is compatible if all of the following hold true: + *

      + *
    • The opts of the given {@code parse} object is an {@code instanceof} its {@link Parser#getOptsType()}, or the opts are {@code null}.
    • + *
    • The {@link Parser#parse(Context, TypedData, Parse)} method returns a non null value.
    • + *
    + * + *

    Core Parsers

    + *

    + * Ratpack core provides parsers for {@link Form}, and JSON (see {@link Jackson}). + * + *

    Example Usage

    + *
    {@code
    +   * import ratpack.core.handling.Handler;
    +   * import ratpack.core.handling.Context;
    +   * import ratpack.core.form.Form;
    +   * import ratpack.core.parse.NullParseOpts;
    +   *
    +   * public class FormHandler implements Handler {
    +   *   public void handle(Context context) {
    +   *     context.parse(Form.class).then(form -> context.render(form.get("someFormParam")));
    +   *   }
    +   * }
    +   * }
    + * + * @param parse The specification of how to parse the request + * @param The type of object the request is parsed into + * @param the type of the parse options object + * @return a promise for the parsed object + * @see #parse(Class) + * @see #parse(Class, Object) + * @see Parser + */ + Promise parse(Parse parse); + + /** + * Parses the provided request body into an object. + *

    + * This variant can be used when a reference to the request body has already been obtained. + * For example, this can be used during the implementation of a {@link Parser} that needs to delegate to another parser. + *

    + * From within a handler, it is more common to use {@link #parse(Parse)} or similar. + * + * @param body The request body + * @param parse The specification of how to parse the request + * @param The type of object the request is parsed into + * @param The type of the parse options object + * @return a promise for the parsed object + * @see #parse(Parse) + * @throws Exception any thrown by the parser + */ + T parse(TypedData body, Parse parse) throws Exception; + + /** + * Gets the file relative to the contextual {@link FileSystemBinding}. + *

    + * Shorthand for {@code get(FileSystemBinding.class).file(path)}. + *

    + * The default configuration of Ratpack includes a {@link FileSystemBinding} in all contexts. + * A {@link NotInRegistryException} will only be thrown if a very custom service setup is being used. + * + * @param path The path to pass to the {@link FileSystemBinding#file(String)} method. + * @return The file relative to the contextual {@link FileSystemBinding} + * @throws NotInRegistryException if there is no {@link FileSystemBinding} in the current service + */ + Path file(String path) throws NotInRegistryException; + + /** + * Issues a 404 client error. + *

    + * This method is literally a shorthand for {@link #clientError(int) clientError(404)}. + *

    + * This is a terminal handler operation. + * + * @since 1.1 + */ + default void notFound() { + clientError(404); + } + +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/handling/Handler.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/handling/Handler.java new file mode 100644 index 00000000000..13d4ec1781a --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/handling/Handler.java @@ -0,0 +1,128 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.core.handling; + +import ratpack.exec.registry.Registry; + +/** + * A handler participates in the processing of a request/response pair, operating on a {@link Context}. + *

    + * Handlers are the key component of Ratpack applications. + * A handler either generate a response, or delegate to another handler in some way. + *

    + *

    Non blocking/Asynchronous

    + *

    + * Handlers are expected to be asynchronous. + * That is, there is no expectation that the handler is "finished" when its {@link #handle(Context)} method returns. + * This facilitates the use of non blocking IO without needing to enter some kind of special mode. + * An implication is that handlers must ensure that they either send a response or delegate to another handler. + *

    + *

    Handler pipeline

    + *

    + * Handlers are always part of a pipeline structure. + * The {@link Context} that the handler operates on provides the {@link Context#next()} method that passes control to the next handler in the pipeline. + * The last handler in the pipeline is always that generates a {@code 404} client error. + *

    + * Handlers can themselves insert other handlers into the pipeline, using the {@link Context#insert(Handler...)} family of methods. + *

    Examples

    + * While there is no strict taxonomy of handlers, the following are indicative examples of common functions. + * + *
    + * import ratpack.core.handling.*;
    + *
    + * // A responder may just return a response to the client
    + *
    + * class SimpleHandler implements Handler {
    + *   void handle(Context exchange) {
    + *     exchange.getResponse().send("Hello World!");
    + *   }
    + * }
    + *
    + * // A responder may add a response header, before delegating to the next in the pipeline
    + *
    + * class DecoratingHandler implements Handler {
    + *   void handle(Context exchange) {
    + *     exchange.getResponse().getHeaders().set("Cache-Control", "no-cache");
    + *     exchange.next();
    + *   }
    + * }
    + *
    + * // Or a handler may conditionally respond
    + *
    + * class ConditionalHandler implements Handler {
    + *   void handle(Context exchange) {
    + *     if (exchange.getRequest().getPath().equals("foo")) {
    + *       exchange.getResponse().send("Hello World!");
    + *     } else {
    + *       exchange.next();
    + *     }
    + *   }
    + * }
    + *
    + * // A handler does not need to participate in the response, but can instead "route" the exchange to different handlers
    + *
    + * class RoutingHandler implements Handler {
    + *   private final Handler[] fooHandlers;
    + *
    + *   public RoutingHandler(Handler... fooHandlers) {
    + *     this.fooHandlers = fooHandlers;
    + *   }
    + *
    + *   void handle(Context exchange) {
    + *     if (exchange.getRequest().getPath().startsWith("foo/")) {
    + *       exchange.insert(fooHandlers);
    + *     } else {
    + *       exchange.next();
    + *     }
    + *   }
    + * }
    + *
    + * // It can sometimes be appropriate to directly delegate to a handler, instead of using exchange.insert()
    + *
    + * class FilteringHandler implements Handler {
    + *   private final Handler nestedHandler;
    + *
    + *   public FilteringHandler(Handler nestedHandler) {
    + *     this.nestedHandler = nestedHandler;
    + *   }
    + *
    + *   void handle(Context exchange) {
    + *     if (exchange.getRequest().getPath().startsWith("foo/")) {
    + *       nestedHandler.handle(exchange);
    + *     } else {
    + *       exchange.next();
    + *     }
    + *   }
    + * }
    + * 
    + * + * @see Handlers + * @see Chain + * @see Registry + */ +@FunctionalInterface +public interface Handler { + + /** + * Handles the context. + * + * @param ctx The context to handle + * @throws Exception if anything goes wrong (exception will be implicitly passed to the context's {@link Context#error(Throwable)} method) + */ + void handle(Context ctx) throws Exception; + +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/http/Headers.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/http/Headers.java new file mode 100644 index 00000000000..7a5eb2f4072 --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/http/Headers.java @@ -0,0 +1,132 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.core.http; + +import ratpack.func.Nullable; +import ratpack.func.MultiValueMap; + +import java.time.Instant; +import java.util.Date; +import java.util.List; +import java.util.Set; + +/** + * An immutable set of HTTP headers. + */ +public interface Headers { + + /** + * Returns the header value with the specified header name. + *

    + * If there is more than one header value for the specified header name, the first value is returned. + * + * @param name The case insensitive name of the header to get retrieve the first value of + * @return the header value or {@code null} if there is no such header + */ + @Nullable + String get(CharSequence name); + + /** + * Returns the header value with the specified header name. + *

    + * If there is more than one header value for the specified header name, the first value is returned. + * + * @param name The case insensitive name of the header to get retrieve the first value of + * @return the header value or {@code null} if there is no such header + */ + @Nullable + String get(String name); + + /** + * Returns the header value as a date with the specified header name. + *

    + * If there is more than one header value for the specified header name, the first value is returned. + * + * @param name The case insensitive name of the header to get retrieve the first value of + * @return the header value as a date or {@code null} if there is no such header or the header value is not a valid date format + */ + @Nullable + Date getDate(CharSequence name); + + /** + * Returns the header value as an instant with the specified header name. + *

    + * If there is more than one header value for the specified header name, the first value is returned. + * + * @param name the case insensitive name of the header to get retrieve the first value of + * @return the header value as an instant or {@code null} if there is no such header or the header value is not a valid date format + * @since 1.4 + */ + @Nullable + default Instant getInstant(CharSequence name) { + Date date = getDate(name); + return date == null ? null : Instant.ofEpochMilli(date.getTime()); + } + + /** + * Returns the header value as a date with the specified header name. + *

    + * If there is more than one header value for the specified header name, the first value is returned. + * + * @param name The case insensitive name of the header to get retrieve the first value of + * @return the header value as a date or {@code null} if there is no such header or the header value is not a valid date format + */ + @Nullable + Date getDate(String name); + + /** + * Returns all of the header values with the specified header name. + * + * @param name The case insensitive name of the header to retrieve all of the values of + * @return the {@link java.util.List} of header values, or an empty list if there is no such header + */ + List getAll(CharSequence name); + + /** + * Returns all of the header values with the specified header name. + * + * @param name The case insensitive name of the header to retrieve all of the values of + * @return the {@link java.util.List} of header values, or an empty list if there is no such header + */ + List getAll(String name); + + /** + * Checks whether a header has been specified for the given value. + * + * @param name The name of the header to check the existence of + * @return True if there is a header with the specified header name + */ + boolean contains(CharSequence name); + + /** + * Checks whether a header has been specified for the given value. + * + * @param name The name of the header to check the existence of + * @return True if there is a header with the specified header name + */ + boolean contains(String name); + + /** + * All header names. + * + * @return The names of all headers that were sent + */ + Set getNames(); + + MultiValueMap asMultiValueMap(); + +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/http/MediaType.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/http/MediaType.java new file mode 100644 index 00000000000..0c8427bd56c --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/http/MediaType.java @@ -0,0 +1,129 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.core.http; + +import com.google.common.collect.ImmutableListMultimap; +import ratpack.func.Nullable; + +/** + * A structured value for a Content-Type header value. + *

    + * Can also represent a non existent (i.e. empty) value. + */ +@SuppressWarnings("UnusedDeclaration") +public interface MediaType { + + /** + * {@value}. + */ + String PLAIN_TEXT_UTF8 = "text/plain;charset=utf-8"; + + /** + * {@value}. + */ + String APPLICATION_JSON = "application/json"; + + /** + * {@value}. + */ + String JSON_SUFFIX = "+json"; + + /** + * {@value}. + */ + String APPLICATION_FORM = "application/x-www-form-urlencoded"; + + /** + * {@value}. + */ + String TEXT_HTML = "text/html"; + + /** + * The type without parameters. + *

    + * Given a mime type of "text/plain;charset=utf-8", returns "text/plain". + *

    + * May be null to represent no content type. + * + * @return The mime type without parameters, or null if this represents the absence of a value. + */ + @Nullable + String getType(); + + /** + * The multimap containing parameters of the mime type. + *

    + * Given a mime type of "application/json;charset=utf-8", the {@code get("charset")} returns {@code ["utf-8"]}". + * May be empty, never null. + *

    + * All param names have been lower cased. The {@code charset} parameter values has been lower cased too. + * + * @return the immutable multimap of the media type params. + */ + ImmutableListMultimap getParams(); + + /** + * The value of the "charset" parameter. + * + * @return the value of the charset parameter, or {@code null} if the no charset parameter was specified + */ + @Nullable + String getCharset(); + + /** + * The value of the "charset" parameter, or the given default value of no charset was specified. + * + * @param defaultValue the value if this type has no charset + * @return the value of the charset parameter, or the given default + */ + String getCharset(String defaultValue); + + /** + * True if this type starts with "{@code text/}". + * + * @return True if this type starts with "{@code text/}". + */ + boolean isText(); + + /** + * True if this type equals {@value #APPLICATION_JSON}, or ends with {@value #JSON_SUFFIX}. + * + * @return if this represents a JSON like type + */ + boolean isJson(); + + /** + * True if this type equals {@value #APPLICATION_FORM}. + * + * @return True if this type equals {@value #APPLICATION_FORM}. + */ + boolean isForm(); + + /** + * True if this type equals {@value #TEXT_HTML}. + * + * @return True if this type equals {@value #TEXT_HTML}. + */ + boolean isHtml(); + + /** + * True if this represents the absence of a value (i.e. no Content-Type header) + * + * @return True if this represents the absence of a value (i.e. no Content-Type header) + */ + boolean isEmpty(); +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/http/MutableHeaders.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/http/MutableHeaders.java new file mode 100644 index 00000000000..c6dd20c5439 --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/http/MutableHeaders.java @@ -0,0 +1,108 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.core.http; + +import java.time.Instant; +import java.util.Calendar; +import java.util.Date; + +/** + * A set of HTTP headers that can be changed. + */ +public interface MutableHeaders extends Headers { + + /** + * Adds a new header with the specified name and value. + *

    + * Will not replace any existing values for the header. + *

    + * Objects of type {@link Instant}, {@link Calendar} or {@link Date} will be converted to a + * RFC 7231 date/time string. + * + * @param name The name of the header + * @param value The value of the header + * @return this + */ + MutableHeaders add(CharSequence name, Object value); + + /** + * Sets the (only) value for the header with the specified name. + *

    + * All existing values for the same header will be removed. + *

    + * Objects of type {@link Instant}, {@link Calendar} or {@link Date} will be converted to a + * RFC 7231 date/time string. + * + * @param name The name of the header + * @param value The value of the header + * @return this + */ + MutableHeaders set(CharSequence name, Object value); + + /** + * Set a header with the given date as the value. + * + * @param name The name of the header + * @param value The date value + * @return this + */ + MutableHeaders setDate(CharSequence name, Date value); + + /** + * Set a header with the given date as the value. + * + * @param name the name of the header + * @param value the date value + * @return this + * @since 1.4 + */ + default MutableHeaders setDate(CharSequence name, Instant value) { + return setDate(name, new Date(value.toEpochMilli())); + } + + /** + * Sets a new header with the specified name and values. + *

    + * All existing values for the same header will be removed. + *

    + * Objects of type {@link Instant}, {@link Calendar} or {@link Date} will be converted to a + * RFC 7231 date/time string. + * + * @param name The name of the header + * @param values The values of the header + * @return this + */ + MutableHeaders set(CharSequence name, Iterable values); + + /** + * Removes the header with the specified name. + * + * @param name The name of the header to remove. + * @return this + */ + MutableHeaders remove(CharSequence name); + + /** + * Removes all headers from this message. + * + * @return this + */ + MutableHeaders clear(); + + MutableHeaders copy(Headers headers); + +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/http/Request.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/http/Request.java new file mode 100644 index 00000000000..6df96b963e4 --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/http/Request.java @@ -0,0 +1,361 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.core.http; + +import com.google.common.reflect.TypeToken; +import io.netty.buffer.ByteBuf; +import io.netty.handler.codec.http.cookie.Cookie; +import ratpack.exec.Promise; +import ratpack.exec.stream.TransformablePublisher; +import ratpack.func.MultiValueMap; + +import java.time.Duration; +import java.time.Instant; +import java.util.Optional; +import java.util.Set; +import java.util.function.Supplier; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * A request to be handled. + */ +public interface Request { + + /** + * A type token for this type. + * + * @since 1.1 + */ + TypeToken TYPE = null; + + /** + * The raw URI of the request. + *

    + * This value may be an absolute URI or an absolute path. + * + * @return The raw URI of the request. + */ + String getRawUri(); + + /** + * The complete URI of the request (path + query string). + *

    + * This value is always absolute (i.e. begins with "{@code /}"). + * + * @return The complete URI of the request (path + query string). + */ + String getUri(); + + /** + * The query string component of the request URI, without the "?". + *

    + * If the request does not contain a query component, an empty string will be returned. + * + * @return The query string component of the request URI, without the "?". + */ + String getQuery(); + + /** + * The URI without the query string and leading forward slash. + * + * @return The URI without the query string and leading forward slash + */ + String getPath(); + + /** + * TBD. + * + * @return TBD. + */ + MultiValueMap getQueryParams(); + + /** + * The cookies that were sent with the request. + *

    + * An empty set will be returned if no cookies were sent. + * + * @return The cookies that were sent with the request. + */ + Set getCookies(); + + /** + * Returns the value of the cookie with the specified name if it was sent. + *

    + * If there is more than one cookie with this name, this method will throw an exception. + * + * @param name The name of the cookie to get the value of + * @return The cookie value, or null if not present + */ + @Nullable + String oneCookie(String name); + + /** + * The body of the request. + *

    + * If this request does not have a body, a non null object is still returned but it effectively has no data. + *

    + * If the transmitted content is larger than provided {@link ServerConfig#getMaxContentLength()}, the given block will be invoked. + * If the block completes successfully, the promise will be terminated. + * If the block errors, the promise will carry the failure. + *

    + * If the body is larger then {@link #getMaxContentLength()}, a {@link RequestBodyTooLargeException} will be propagated. + * + * @return the body of the request + */ + Promise getBody(); + + /** + * Overrides the idle timeout for this connection. + *

    + * The default is set as part of server config via {@link ServerConfigBuilder#idleTimeout(Duration)}. + *

    + * The override strictly applies to only the request/response exchange that this request is involved in. + * + * @param idleTimeout the idle timeout ({@link Duration#ZERO} = no timeout, must not be negative, must not be null) + * @since 1.5 + * @see ServerConfig#getIdleTimeout() + */ + void setIdleTimeout(Duration idleTimeout); + + /** + * The body of the request allowing up to the provided size for the content. + *

    + * If this request does not have a body, a non null object is still returned but it effectively has no data. + *

    + * If the transmitted content is larger than the provided {@code maxContentLength}, an {@code 413} client error will be issued. + * + * @param maxContentLength the maximum number of bytes allowed for the request. + * @return the body of the request. + * @since 1.1 + */ + Promise getBody(long maxContentLength); + + /** + * Allows reading the body as a stream, with back pressure. + *

    + * Similar to {@link #getBodyStream(long)}, except uses {@link ServerConfig#getMaxContentLength()} as the max content length. + * + * @return a publisher of the request body + * @see #getBodyStream(long) + * @since 1.2 + */ + TransformablePublisher getBodyStream(); + + /** + * Allows reading the body as a stream, with back pressure. + *

    + * The returned publisher emits the body as {@link ByteBuf}s. + * The subscriber MUST {@code release()} each emitted byte buf. + * Failing to do so will leak memory. + *

    + * If the request body is larger than the given {@code maxContentLength}, a {@link RequestBodyTooLargeException} will be emitted. + * If the request body has already been read, a {@link RequestBodyAlreadyReadException} will be emitted. + *

    + * The returned publisher is bound to the calling execution via {@link Streams#bindExec(Publisher)}. + * If your subscriber's onNext(), onComplete() or onError() methods are asynchronous they MUST use {@link Promise}, + * {@link Blocking} or similar execution control constructs. + *

    + * The following demonstrates how to use this method to stream the request body to a file, using asynchronous IO. + * + *

    {@code
    +   * import com.google.common.io.Files;
    +   * import io.netty.buffer.ByteBuf;
    +   * import org.apache.commons.lang3.RandomStringUtils;
    +   * import org.reactivestreams.Subscriber;
    +   * import org.reactivestreams.Subscription;
    +   * import org.junit.Assert;
    +   * import ratpack.exec.Promise;
    +   * import ratpack.core.http.client.ReceivedResponse;
    +   * import ratpack.test.embed.EmbeddedApp;
    +   * import ratpack.test.embed.EphemeralBaseDir;
    +   *
    +   * import java.io.IOException;
    +   * import java.nio.channels.AsynchronousFileChannel;
    +   * import java.nio.charset.StandardCharsets;
    +   * import java.nio.file.Path;
    +   * import java.nio.file.StandardOpenOption;
    +   *
    +   * public class Example {
    +   *   public static void main(String[] args) throws Exception {
    +   *     EphemeralBaseDir.tmpDir().use(dir -> {
    +   *       String string = RandomStringUtils.random(1024 * 1024);
    +   *       int length = string.getBytes(StandardCharsets.UTF_8).length;
    +   *
    +   *       Path file = dir.path("out.txt");
    +   *
    +   *       EmbeddedApp.fromHandler(ctx ->
    +   *         ctx.getRequest().getBodyStream(length).subscribe(new Subscriber() {
    +   *
    +   *           private Subscription subscription;
    +   *           private AsynchronousFileChannel out;
    +   *           long written;
    +   *
    +   *           {@literal @}Override
    +   *           public void onSubscribe(Subscription s) {
    +   *             subscription = s;
    +   *             try {
    +   *               this.out = AsynchronousFileChannel.open(
    +   *                 file, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING
    +   *               );
    +   *               subscription.request(1);
    +   *             } catch (IOException e) {
    +   *               subscription.cancel();
    +   *               ctx.error(e);
    +   *             }
    +   *           }
    +   *
    +   *           {@literal @}Override
    +   *           public void onNext(ByteBuf byteBuf) {
    +   *             Promise.async(down ->
    +   *               out.write(byteBuf.nioBuffer(), written, null, down.completionHandler())
    +   *             ).onError(error -> {
    +   *               byteBuf.release();
    +   *               subscription.cancel();
    +   *               out.close();
    +   *               ctx.error(error);
    +   *             }).then(bytesWritten -> {
    +   *               byteBuf.release();
    +   *               written += bytesWritten;
    +   *               subscription.request(1);
    +   *             });
    +   *           }
    +   *
    +   *           {@literal @}Override
    +   *           public void onError(Throwable t) {
    +   *             ctx.error(t);
    +   *             try {
    +   *               out.close();
    +   *             } catch (IOException ignore) {
    +   *               // ignore
    +   *             }
    +   *           }
    +   *
    +   *           {@literal @}Override
    +   *           public void onComplete() {
    +   *             try {
    +   *               out.close();
    +   *             } catch (IOException ignore) {
    +   *               // ignore
    +   *             }
    +   *             ctx.render("ok");
    +   *           }
    +   *         })
    +   *       ).test(http -> {
    +   *         ReceivedResponse response = http.request(requestSpec -> requestSpec.method("POST").getBody().text(string));
    +   *         Assert.assertEquals(response.getBody().getText(), "ok");
    +   *
    +   *         String fileContents = Files.asCharSource(file.toFile(), StandardCharsets.UTF_8).read();
    +   *         Assert.assertEquals(fileContents, string);
    +   *       });
    +   *     });
    +   *   }
    +   * }
    +   * }
    + * + * @return a publisher of the request body + * @see #getBodyStream(long) + * @since 1.2 + */ + TransformablePublisher getBodyStream(long maxContentLength); + + /** + * The request headers. + * + * @return The request headers. + */ + Headers getHeaders(); + + /** + * The type of the data as specified in the {@code "content-type"} header. + *

    + * If no {@code "content-type"} header is specified, an empty {@link MediaType} is returned. + * + * @return The type of the data. + * @see MediaType#isEmpty() + */ + MediaType getContentType(); + + /** + * A flag representing whether or not the request originated via AJAX. + * @return A flag representing whether or not the request originated via AJAX. + */ + boolean isAjaxRequest(); + + /** + * Whether this request contains a {@code Expect: 100-Continue} header. + *

    + * You do not need to send the {@code 100 Continue} status response. + * It will be automatically sent when the body is read via {@link #getBody()} or {@link #getBodyStream(long)} (or variants). + * Ratpack will not emit a {@code 417 Expectation Failed} response if the body is not read. + * The response specified by the handling code will not be altered, as per normal. + *

    + * If the header is present for the request, this method will return {@code true} before and after the {@code 100 Continue} response has been sent. + * + * @return whether this request includes the {@code Expect: 100-Continue} header + * @since 1.2 + */ + boolean isExpectsContinue(); + + /** + * Whether this request contains a {@code Transfer-Encoding: chunked} header. + *

    + * See https://en.wikipedia.org/wiki/Chunked_transfer_encoding. + * + * @return whether this request contains a {@code Transfer-Encoding: chunked} header + * @since 1.2 + */ + boolean isChunkedTransfer(); + + /** + * The advertised content length of the request body. + *

    + * Will return {@code -1} if no {@code Content-Length} header is present, or is not valid long value. + * + * @return the advertised content length of the request body. + * @since 1.2 + */ + long getContentLength(); + + /** + * The timestamp for when this request was received. + * Specifically, this is the timestamp of creation of the request object. + * + * @return the instant timestamp for the request. + */ + Instant getTimestamp(); + + /** + * Sets the allowed max content length for the request body. + *

    + * This setting will be used when {@link #getBody()} or {@link #getBodyStream()} are called, + * and when implicitly reading the request body in order to respond (e.g. when issuing a response without trying to read the body). + * + * @param maxContentLength the maximum request body length in bytes + * @since 1.5 + */ + void setMaxContentLength(long maxContentLength); + + /** + * The max allowed content length for the request body. + * + * @see #setMaxContentLength(long) + * @return the maximum request body length in bytes + * @since 1.5 + */ + long getMaxContentLength(); + +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/http/Response.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/http/Response.java new file mode 100644 index 00000000000..c5a13ab5a49 --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/http/Response.java @@ -0,0 +1,210 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.core.http; + +import com.google.common.reflect.TypeToken; +import io.netty.buffer.ByteBuf; +import io.netty.handler.codec.http.cookie.Cookie; +import ratpack.core.handling.Context; +import ratpack.exec.api.NonBlocking; + +import java.nio.file.Path; +import java.util.Set; +import java.util.function.Supplier; + +/** + * A response to a request. + *

    + * The headers and status are configured, before committing the response with one of the {@link #send} methods. + */ +public interface Response { + + /** + * A type token for this type. + * + * @since 1.1 + */ + TypeToken TYPE = null; + + /** + * Creates a new cookie with the given name and value. + *

    + * The cookie will have no expiry. Use the returned cookie object to fine tune the cookie. + * + * @param name The name of the cookie + * @param value The value of the cookie + * @return The cookie that will be sent + */ + Cookie cookie(String name, String value); + + /** + * Adds a cookie to the response with a 0 max-age, forcing the client to expire it. + *

    + * If the cookie that you want to expire has an explicit path, you must use {@link Cookie#setPath(String)} on the return + * value of this method to have the cookie expire. + * + * @param name The name of the cookie to expire. + * @return The created cookie + */ + Cookie expireCookie(String name); + + /** + * The cookies that are to be part of the response. + *

    + * The cookies are mutable. + * + * @return The cookies that are to be part of the response. + */ + Set getCookies(); + + /** + * The response headers. + * + * @return The response headers. + */ + MutableHeaders getHeaders(); + + /** + * Sets the status line of the response. + *

    + * The message used will be the standard for the code. + * + * @param code The status code of the response to use when it is sent. + * @return This + */ + default Response status(int code) { + return null; + } + + /** + * Sends the response back to the client, with no body. + */ + @NonBlocking + void send(); + + Response contentTypeIfNotSet(Supplier contentType); + + /** + * Sends the response, using "{@code text/plain}" as the content type and the given string as the response body. + *

    + * Equivalent to calling "{@code send\("text/plain", text)}. + * + * @param text The text to render as a plain text response. + */ + @NonBlocking + void send(String text); + + /** + * Sends the response, using the given content type and string as the response body. + *

    + * The string will be sent in "utf8" encoding, and the given content type will have this appended. + * That is, given a {@code contentType} of "{@code application/json}" the actual value for the {@code Content-Type} + * header will be "{@code application/json;charset=utf8}". + *

    + * The value given for content type will override any previously set value for this header. + * @param contentType The value of the content type header + * @param body The string to render as the body of the response + */ + @NonBlocking + void send(CharSequence contentType, String body); + + /** + * Sends the response, using "{@code application/octet-stream}" as the content type (if a content type hasn't + * already been set) and the given byte array as the response body. + * + * @param bytes The response body + */ + @NonBlocking + void send(byte[] bytes); + + /** + * Sends the response, using the given content type and byte array as the response body. + * @param contentType The value of the {@code Content-Type} header + * @param bytes The response body + */ + @NonBlocking + void send(CharSequence contentType, byte[] bytes); + + /** + * Sends the response, using "{@code application/octet-stream}" as the content type (if a content type hasn't + * already been set) and the given bytes as the response body. + * + * @param buffer The response body + */ + @NonBlocking + void send(ByteBuf buffer); + + /** + * Sends the response, using the given content type and bytes as the response body. + * @param contentType The value of the {@code Content-Type} header + * @param buffer The response body + */ + @NonBlocking + void send(CharSequence contentType, ByteBuf buffer); + + /** + * Sets the response {@code Content-Type} header. + * + * @param contentType The value of the {@code Content-Type} header + * @return This + */ + Response contentType(CharSequence contentType); + + /** + * Sets the response {@code Content-Type} header, if it has not already been set. + * + * @param contentType The value of the {@code Content-Type} header + * @return This + */ + default Response contentTypeIfNotSet(CharSequence contentType) { + return null; + } + + /** + * Sends the response, using the file as the response body. + *

    + * This method does not set the content length, content type or anything else. + * It is generally preferable to use the {@link Context#render(Object)} method with a file/path object, + * or an {@link Chain#files(Action)}. + * + * @param file the response body + */ + @NonBlocking + void sendFile(Path file); + + /** + * Prevents the response from being compressed. + * + * @return {@code this} + */ + Response noCompress(); + + /** + * Forces the closing of the current connection, even if the client requested it to be kept alive. + *

    + * This method can be used when it is desirable to force the client's connection to close, defeating HTTP keep alive. + * This can be desirable in some networking environments where rate limiting or throttling is performed via edge routers or similar. + *

    + * This method simply calls {@code getHeaders().set("Connection", "close")}, which has the same effect. + * + * @return {@code this} + * @since 1.1 + */ + default Response forceCloseConnection() { + return null; + } +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/http/TypedData.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/http/TypedData.java new file mode 100644 index 00000000000..c6fb4f1ef19 --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/http/TypedData.java @@ -0,0 +1,94 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.core.http; + +import io.netty.buffer.ByteBuf; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.Charset; + +/** + * Data that potentially has a content type. + */ +public interface TypedData { + + /** + * The type of the data. + * + * @return The type of the data. + */ + MediaType getContentType(); + + /** + * The data as text. + *

    + * If a content type was provided, and it provided a charset parameter, that charset will be used to decode the text. + * If no charset was provided, {@code UTF-8} will be assumed. + *

    + * This can lead to incorrect results for non {@code text/*} type content types. + * For example, {@code application/json} is implicitly {@code UTF-8} but this method will not know that. + * + * @return The data decoded as text + */ + String getText(); + + String getText(Charset charset); + + /** + * The raw data as bytes. + * + * The returned array should not be modified. + * + * @return the raw data as bytes. + */ + byte[] getBytes(); + + /** + * The raw data as a (unmodifiable) buffer. + * + * @return the raw data as bytes. + */ + ByteBuf getBuffer(); + + /** + * Writes the data to the given output stream. + *

    + * Data is effectively piped from {@link #getInputStream()} to the given output stream. + * As such, if the given output stream might block (e.g. is backed by a file or socket) then + * this should be called from a blocking thread using {@link Blocking#get(Factory)} + * This method does not flush or close the stream. + * + * @param outputStream The stream to write to + * @throws IOException any thrown when writing to the output stream + */ + void writeTo(OutputStream outputStream) throws IOException; + + /** + * An input stream of the data. + *

    + * This input stream is backed by an in memory buffer. + * Reading from this input stream will never block. + *

    + * It is not necessary to close the input stream. + * + * @return an input stream of the data + */ + InputStream getInputStream(); + +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/parse/Parse.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/parse/Parse.java new file mode 100644 index 00000000000..e88a0c7c1e7 --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/parse/Parse.java @@ -0,0 +1,106 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.core.parse; + +import com.google.common.reflect.TypeToken; +import ratpack.core.handling.Context; + +import java.util.Optional; + +/** + * The specification of a particular parse. + *

    + * Construct instances via the {@link #of} methods. + * + * @param the type of object to construct from the request body + * @param the type of object that provides options/configuration for the parsing + * @see Context#parse(Parse) + * @see Parser + * @see ParserSupport + */ +public class Parse { + + /** + * The type of object to construct from the request body. + * + * @return the type of object to construct from the request body + */ + public TypeToken getType() { + return null; + } + + /** + * The type of object that provides options/configuration for the parsing. + *

    + * For any parse request, no options may be specified. + * Parser implementations should throw an exception if they require an options object when none is supplied. + * + * @return the type of object that provides options/configuration for the parsing + */ + public Optional getOpts() { + return null; + } + + /** + * Creates a parse object. + * + * @param type the type of object to construct from the request body + * @param opts the options object + * @param the type of object to construct from the request body + * @param the type of object that provides options/configuration for the parsing + * @return a parse instance from the given arguments + */ + public static Parse of(TypeToken type, O opts) { + return null; + } + + /** + * Creates a parse object, with no options. + * + * @param type the type of object to construct from the request body + * @param the type of object to construct from the request body + * @return a parse instance to the given type + */ + public static Parse of(TypeToken type) { + return null; + } + + /** + * Creates a parse object. + * + * @param type the type of object to construct from the request body + * @param opts the options object + * @param the type of object to construct from the request body + * @param the type of object that provides options/configuration for the parsing + * @return a parse instance from the given arguments + */ + public static Parse of(Class type, O opts) { + return null; + } + + /** + * Creates a parse object, with no options. + * + * @param type the type of object to construct from the request body + * @param the type of object to construct from the request body + * @return a parse instance to the given type + */ + public static Parse of(Class type) { + return null; + } + +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/render/NoSuchRendererException.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/render/NoSuchRendererException.java new file mode 100644 index 00000000000..3970860711c --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/core/render/NoSuchRendererException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.core.render; + +/** + * Thrown when a request is made to render an object, but no suitable renderer can be found. + */ +public class NoSuchRendererException extends RuntimeException { + + private static final long serialVersionUID = 0; + + /** + * Constructor. + * + * @param object The object to be rendered. + */ + public NoSuchRendererException(Object object) { + super("No renderer for object '" + object + "' of type '" + object.getClass() + "'"); + } + +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/exec/Promise.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/exec/Promise.java new file mode 100644 index 00000000000..8c07b48c97f --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/exec/Promise.java @@ -0,0 +1,128 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.exec; + +import ratpack.func.Action; +import ratpack.func.Factory; +import ratpack.func.Function; +import ratpack.func.Predicate; + +/** + * A promise for a single value. + *

    + * A promise is a representation of a value which will become available later. + * Methods such as {@link #map(Function)}, {@link #flatMap(Function)}, {@link #cache()} etc.) allow a pipeline of "operations" to be specified, + * that the value will travel through as it becomes available. + * Such operations are implemented via the {@link #transform(Function)} method. + * Each operation returns a new promise object, not the original promise object. + *

    + * To create a promise, use the {@link Promise#async(Upstream)} method (or one of the variants such as {@link Promise#sync(Factory)}. + * To test code that uses promises, use the {@link ratpack.test.exec.ExecHarness}. + *

    + * The promise is not "activated" until the {@link #then(Action)} method is called. + * This method terminates the pipeline, and receives the final value. + *

    + * Promise objects are multi use. + * Every promise pipeline has a value producing function at its start. + * Activating a promise (i.e. calling {@link #then(Action)}) invokes the function. + * The {@link #cache()} operation can be used to change this behaviour. + * + * @param the type of promised value + */ +@SuppressWarnings("JavadocReference") +public interface Promise { + + static Promise sync(Factory factory) { + return null; + } + + static Promise flatten(Factory> factory) { + return null; + } + + static Promise value(T t) { + return null; + } + + Promise map(Function transformer); + + Promise flatMap(Function> transformer); + + void then(Action then); + + Promise next(Action action); + + default Promise onError(Class errorType, Action errorHandler) { + return null; + } + + default Promise onError(Action errorHandler) { + return null; + } + + default Promise mapIf(Predicate predicate, Function transformer) { + return null; + } + + default Promise mapIf(Predicate predicate, Function onTrue, Function onFalse) { + return null; + } + + default Promise mapError(Function transformer) { + return null; + } + + default Promise mapError(Class type, Function function) { + return null; + } + + default Promise mapError(Predicate predicate, Function function) { + return null; + } + + default Promise flatMapError(Function> function) { + return null; + } + + default Promise flatMapError(Class type, Function> function) { + return null; + } + + default Promise flatMapError(Predicate predicate, Function> function) { + return null; + } + + default Promise route(Predicate predicate, Action action) { + return null; + } + + default Promise cache() { + return null; + } + + default Promise cacheIf(Predicate shouldCache) { + return null; + } + + default Promise fork() { + return null; + } + + default Promise apply(Function, ? extends Promise> function) { + return null; + } +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/exec/api/NonBlocking.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/exec/api/NonBlocking.java new file mode 100644 index 00000000000..8c77398f144 --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/exec/api/NonBlocking.java @@ -0,0 +1,40 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.exec.api; + +import java.lang.annotation.*; + +/** + * Declares that a method or function-like method parameter is non-blocking and can freely use {@link ratpack.exec.Promise} and other async constructs. + *

    + * If this annotation is present on a method, it indicates that the method may be asynchronous. + * That is, it is not necessarily expected to have completed its logical work when the method returns. + * The method must however use {@link ratpack.exec.Promise}, {@link ratpack.exec.Operation}, or other execution mechanisms to perform asynchronous work. + *

    + * Most such methods are invoked as part of Ratpack. + * If you need to invoke such a method, do so as part of a discrete {@link ratpack.exec.Operation}. + *

    + * If this annotation is present on a function type method parameter, it indicates that the annotated function may be asynchronous. + * Similarly, if you need to invoke such a parameter, do so as part of a discrete {@link ratpack.exec.Operation}. + *

    + * Note: the ability to annotate method parameters with this annotation was added in version {@code 1.1.0}. + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.PARAMETER}) +public @interface NonBlocking { +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/exec/registry/NotInRegistryException.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/exec/registry/NotInRegistryException.java new file mode 100644 index 00000000000..9e6ec9583f5 --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/exec/registry/NotInRegistryException.java @@ -0,0 +1,47 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.exec.registry; + +import com.google.common.reflect.TypeToken; + +/** + * Thrown when a request is made for an object that a registry cannot provide. + * + * @see Registry#get(Class) + */ +public class NotInRegistryException extends RuntimeException { + + private static final long serialVersionUID = 0; + + /** + * Constructs the exception. + * + * @param type The requested type of the object + */ + public NotInRegistryException(TypeToken type) { + this(String.format("No object for type '%s' in registry", type)); + } + + /** + * Constructor. + * + * @param message The exception message + */ + public NotInRegistryException(String message) { + super(message); + } +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/exec/registry/Registry.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/exec/registry/Registry.java new file mode 100644 index 00000000000..b372f792083 --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/exec/registry/Registry.java @@ -0,0 +1,244 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.exec.registry; + +import com.google.common.reflect.TypeToken; + +import java.util.Optional; +import java.util.function.Supplier; + +/** + * An object registry. + *

    + * Registries are primarily used for inter {@link ratpack.core.handling.Handler} communication in request processing. + * The {@link ratpack.core.handling.Context} object that handlers operate on implements the {@link Registry} interface. + *

    + * import ratpack.core.handling.Handler;
    + * import ratpack.core.handling.Context;
    + * import ratpack.exec.registry.Registry;
    + *
    + * import static org.junit.Assert.assertTrue;
    + *
    + * public class Thing {
    + *   private final String name
    + *   public Thing(String name) { this.name = name; }
    + *   public String getName() { return name; }
    + * }
    + *
    + * public class UpstreamHandler implements Handler {
    + *   public void handle(Context context) {
    + *     context.next(Registry.single(new Thing("foo")));
    + *   }
    + * }
    + *
    + * public class DownstreamHandler implements Handler {
    + *   public void handle(Context context) {
    + *     assertTrue(context instanceof Registry);
    + *     Thing thing = context.get(Thing.class);
    + *     context.render(thing.getName());
    + *   }
    + * }
    + *
    + * import ratpack.test.handling.HandlingResult;
    + * import ratpack.test.handling.RequestFixture;
    + *
    + * import static ratpack.core.handling.Handlers.chain;
    + * import static ratpack.func.Action.noop;
    + *
    + * import static org.junit.Assert.assertEquals;
    + *
    + * Handler chain = chain(new UpstreamHandler(), new DownstreamHandler());
    + * HandlingResult result = RequestFixture.handle(chain, noop());
    + *
    + * assertEquals("foo", result.rendered(String.class));
    + * 
    + *

    Thread safety

    + *

    + * Registry objects are assumed to be thread safe. + * No external synchronization is performed around registry access. + * As registry objects may be used across multiple requests, they should be thread safe. + *

    + * Registries that are created per request however do not need to be thread safe. + * + *

    Ordering

    + *

    + * Registry objects are returned in the reverse order that they were added (i.e. Last-In-First-Out). + * + *

    {@code
    + * import com.google.common.base.Joiner;
    + * import ratpack.exec.registry.Registry;
    + *
    + * import static org.junit.Assert.assertEquals;
    + *
    + * public class Example {
    + *   public static void main(String... args) throws Exception {
    + *     Registry registry = Registry.of(r -> r
    + *         .add("Ratpack")
    + *         .add("foo")
    + *         .add("bar")
    + *     );
    + *
    + *     assertEquals("bar", registry.get(String.class));
    + *
    + *     String joined = Joiner.on(", ").join(registry.getAll(String.class));
    + *     assertEquals("bar, foo, Ratpack", joined);
    + *   }
    + * }
    + * }
    + *

    + * While this is strictly the case for the core registry implementations in Ratpack, adapted implementations (e.g. Guice, Spring etc.) may have more nuanced ordering semantics. + * To the greatest extent possible, registry implementations should strive to honor LIFO ordering. + */ +public interface Registry { + + /** + * Provides an object of the specified type, or throws an exception if no object of that type is available. + * + * @param type The type of the object to provide + * @param The type of the object to provide + * @return An object of the specified type + * @throws NotInRegistryException If no object of this type can be returned + */ + default O get(Class type) throws NotInRegistryException { + return null; + } + + /** + * Provides an object of the specified type, or throws an exception if no object of that type is available. + * + * @param type The type of the object to provide + * @param The type of the object to provide + * @return An object of the specified type + * @throws NotInRegistryException If no object of this type can be returned + */ + default O get(TypeToken type) throws NotInRegistryException { + return null; + } + + /** + * Does the same thing as {@link #get(Class)}, except returns null instead of throwing an exception. + * + * @param type The type of the object to provide + * @param The type of the object to provide + * @return An object of the specified type, or null if no object of this type is available. + */ + default Optional maybeGet(Class type) { + return null; + } + + /** + * Does the same thing as {@link #get(Class)}, except returns null instead of throwing an exception. + * + * @param type The type of the object to provide + * @param The type of the object to provide + * @return An optional of an object of the specified type + */ + Optional maybeGet(TypeToken type); + + /** + * Returns all of the objects whose declared type is assignment compatible with the given type. + * + * @param type the type of objects to search for + * @param the type of objects to search for + * @return All objects of the given type + */ + default Iterable getAll(Class type) { + return null; + } + + /** + * Returns all of the objects whose declared type is assignment compatible with the given type. + * + * @param type the type of objects to search for + * @param the type of objects to search for + * @return All objects of the given type + */ + Iterable getAll(TypeToken type); + + /** + * Creates a new registry by joining {@code this} registry with the given registry + *

    + * The returned registry is effectively the union of the two registries, with the {@code child} registry taking precedence. + * This means that child entries are effectively "returned first". + *

    {@code
    +   * import ratpack.exec.registry.Registry;
    +   *
    +   * import java.util.List;
    +   * import com.google.common.collect.Lists;
    +   *
    +   * import static org.junit.Assert.assertEquals;
    +   *
    +   * public class Example {
    +   *
    +   *   public static interface Thing {
    +   *     String getName();
    +   *   }
    +   *
    +   *   public static class ThingImpl implements Thing {
    +   *     private final String name;
    +   *
    +   *     public ThingImpl(String name) {
    +   *       this.name = name;
    +   *     }
    +   *
    +   *     public String getName() {
    +   *       return name;
    +   *     }
    +   *   }
    +   *
    +   *   public static void main(String[] args) {
    +   *     Registry child = Registry.builder().add(Thing.class, new ThingImpl("child-1")).add(Thing.class, new ThingImpl("child-2")).build();
    +   *     Registry parent = Registry.builder().add(Thing.class, new ThingImpl("parent-1")).add(Thing.class, new ThingImpl("parent-2")).build();
    +   *     Registry joined = parent.join(child);
    +   *
    +   *     assertEquals("child-2", joined.get(Thing.class).getName());
    +   *     List all = Lists.newArrayList(joined.getAll(Thing.class));
    +   *     assertEquals("child-2", all.get(0).getName());
    +   *     assertEquals("child-1", all.get(1).getName());
    +   *     assertEquals("parent-2", all.get(2).getName());
    +   *     assertEquals("parent-1", all.get(3).getName());
    +   *   }
    +   * }
    +   * }
    + * + * @param child the child registry + * @return a registry which is the combination of the {@code this} and the given child + */ + default Registry join(Registry child) { + return null; + } + + /** + * Returns an empty registry. + * + * @return an empty registry + */ + static Registry empty() { + return null; + } + + /** + * Creates a new {@link RegistryBuilder registry builder}. + * + * @return a new registry builder + * @see RegistryBuilder + */ + static RegistryBuilder builder() { + return null; + } + +} \ No newline at end of file diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/exec/registry/RegistryBuilder.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/exec/registry/RegistryBuilder.java new file mode 100644 index 00000000000..b89a711d835 --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/exec/registry/RegistryBuilder.java @@ -0,0 +1,19 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.exec.registry; + +public interface RegistryBuilder {} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/exec/stream/TransformablePublisher.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/exec/stream/TransformablePublisher.java new file mode 100644 index 00000000000..9442fcabd7d --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/exec/stream/TransformablePublisher.java @@ -0,0 +1,29 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.exec.stream; + +/** + * A wrapper over a {@link Publisher} that makes it more convenient to chain transformations of different kinds. + *

    + * Note that this type implements the publisher interface, + * so behaves just like the publisher that it is wrapping with respect to the + * {@link Publisher#subscribe(Subscriber)} method. + * + * @param the type of item emitted by this publisher + */ +public interface TransformablePublisher { +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/func/Action.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/func/Action.java new file mode 100644 index 00000000000..cc1c67fa07a --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/func/Action.java @@ -0,0 +1,266 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.func; + +import java.util.function.Consumer; + +/** + * A generic type for an object that does some work with a thing. + *

    + * This type serves the same purpose as the JDK's {@link java.util.function.Consumer}, but allows throwing checked exceptions. + * It contains methods for bridging to and from the JDK type. + * + * @param The type of thing. + */ +@FunctionalInterface +public interface Action { + + /** + * Executes the action against the given thing. + * + * @param t the thing to execute the action against + * @throws Exception if anything goes wrong + */ + void execute(T t) throws Exception; + + /** + * Returns an action that does precisely nothing. + * + * @return an action that does precisely nothing + */ + static Action noop() { + return null; + } + + /** + * If the given action is {@code null}, returns {@link #noop()}, otherwise returns the given action. + * + * @param action an action, maybe {@code null}. + * @param the type of parameter received by the action + * @return the given {@code action} param if it is not {@code null}, else a {@link #noop()}. + */ + static Action noopIfNull(@Nullable Action action) { + return null; + } + + /** + * Returns a new action that executes the given actions in order. + * + * @param actions the actions to join into one action + * @param the type of object the action accepts + * @return the newly created aggregate action + */ + @SafeVarargs + static Action join(final Action... actions) { + return null; + } + + /** + * Returns a new action that executes this action and then the given action. + * + * @param action the action to execute after this action + * @param the type of object the action accepts + * @return the newly created aggregate action + */ + default Action append(Action action) { + return null; + } + + /** + * Returns a new action that executes the given action and then this action. + * + * @param action the action to execute before this action + * @param the type of object the action accepts + * @return the newly created aggregate action + */ + default Action prepend(Action action) { + return null; + } + + /** + * Returns an action that receives a throwable and immediately throws it. + * + * @return an action that receives a throwable and immediately throws it + */ + static Action throwException() { + return null; + } + + /** + * An action that receives a throwable to thrown, suppressing the given value. + * + * @return an action that receives a throwable to thrown, suppressing the given value + * @since 1.5 + */ + static Action suppressAndThrow(Throwable toSuppress) { + return null; + } + + /** + * Returns an action that immediately throws the given exception. + *

    + * The exception is thrown via {@link Exceptions#toException(Throwable)} + * + * @param the argument type (anything, as the argument is ignored) + * @param throwable the throwable to immediately throw when the returned action is executed + * @return an action that immediately throws the given exception. + */ + static Action throwException(final Throwable throwable) { + return null; + } + + /** + * Executes the action with the given argument, then returns the argument. + *

    {@code
    +   * import ratpack.func.Action;
    +   * import java.util.ArrayList;
    +   *
    +   * import static org.junit.Assert.assertEquals;
    +   *
    +   * public class Example {
    +   *   public static void main(String... args) throws Exception {
    +   *     assertEquals("foo", Action.with(new ArrayList<>(), list -> list.add("foo")).get(0));
    +   *   }
    +   * }
    +   * }
    + * @param t the argument to execute the given action with + * @param action the action to execute with the given argument + * @param the type of the argument + * @return the given argument (i.e. {@code t}) + * @throws Exception any thrown by {@code action} + */ + static T with(T t, Action action) throws Exception { + return null; + } + + /** + * Executes with the given argument, then returns the argument. + *
    {@code
    +   * import ratpack.func.Action;
    +   * import java.util.List;
    +   * import java.util.ArrayList;
    +   *
    +   * import static org.junit.Assert.assertEquals;
    +   *
    +   * public class Example {
    +   *   public static void main(String... args) throws Exception {
    +   *     assertEquals("foo", run(list -> list.add("foo")).get(0));
    +   *   }
    +   *
    +   *   private static List run(Action> action) throws Exception {
    +   *     return action.with(new ArrayList<>());
    +   *   }
    +   * }
    +   * }
    + * @param o the argument to execute the given action with + * @param the type of the argument + * @return the given argument (i.e. {@code o}) + * @throws Exception any thrown by {@link #execute(Object)} + */ + default O with(O o) throws Exception { + return null; + } + + /** + * Like {@link #with(Object, Action)}, but unchecks any exceptions thrown by the action via {@link Exceptions#uncheck(Throwable)}. + * + * @param t the argument to execute the given action with + * @param action the action to execute with the given argument + * @param the type of the argument + * @return the given argument (i.e. {@code t}) + */ + static T uncheckedWith(T t, Action action) { + return null; + } + + /** + * Like {@link #with(Object)}, but unchecks any exceptions thrown by the action via {@link Exceptions#uncheck(Throwable)}. + * + * @param o the argument to execute with + * @param the type of the argument + * @return the given argument (i.e. {@code o}) + */ + default O uncheckedWith(O o) { + return null; + } + + /** + * Creates a JDK {@link Consumer} from this action. + *

    + * Any exceptions thrown by {@code this} action will be unchecked via {@link Exceptions#uncheck(Throwable)} and rethrown. + * + * @return this function as a JDK style consumer. + */ + default Consumer toConsumer() { + return null; + } + + /** + * Creates an exception-taking action that executes the given action before throwing the exception. + * + * @param action the action to perform before throwing the exception + * @return an action that performs the given action before throwing its argument + * @since 1.5 + */ + static Action beforeThrow(Action action) { + return null; + } + + /** + * Creates an action that delegates to the given action if the given predicate applies, else delegates to {@link #noop()}. + *

    + * This is equivalent to {@link #when(Predicate, Action, Action) when(predicate, action, noop())}. + * + * @param predicate the condition for the argument + * @param action the action to execute if the predicate applies + * @param the type of argument + * @return an action that delegates to the given action if the predicate applies, else noops + * @see #when(Predicate, Action, Action) + * @see #conditional(Action, Action) + * @since 1.5 + */ + static Action when(Predicate predicate, Action action) { + return null; + } + + /** + * Creates an action that delegates to the first action if the given predicate applies, else the second action. + * + * @param predicate the condition for the argument + * @param onTrue the action to execute if the predicate applies + * @param onFalse the action to execute if the predicate DOES NOT apply + * @param the type of argument + * @return an action that delegates to the first action if the predicate applies, else the second argument + * @see #when(Predicate, Action) + * @see #conditional(Action, Action) + * @since 1.5 + */ + static Action when(Predicate predicate, Action onTrue, Action onFalse) { + return null; + } + + /** + * A spec for adding conditions to a conditional action. + * + * @param the input type + * @see #conditional(Action, Action) + * @since 1.5 + */ + interface ConditionalSpec { + ConditionalSpec when(Predicate predicate, Action action); + } +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/func/Factory.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/func/Factory.java new file mode 100644 index 00000000000..fb86f767ddf --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/func/Factory.java @@ -0,0 +1,50 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.func; + +/** + * An object that creates another. + * + * Factories are expected to create a new object each time. + * Implementors should explain there behaviour if they do not do this. + * + * @param the type of object that this factory creates + */ +@FunctionalInterface +public interface Factory { + + /** + * Creates a new object. + * + * @return a newly created object + * @throws Exception any + */ + T create() throws Exception; + + /** + * Creates a factory that always returns the given item. + * + * @param item the item to always provide + * @param the type of the item + * @return a factory that always returns {@code item} + * @since 1.1 + */ + static Factory constant(T item) { + return () -> item; + } + +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/func/Function.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/func/Function.java new file mode 100644 index 00000000000..eab01ada8fd --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/func/Function.java @@ -0,0 +1,208 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.func; + +import java.util.Objects; + +/** + * A single argument function. + *

    + * This type serves the same purpose as the JDK's {@link java.util.function.Function}, but allows throwing checked exceptions. + * It contains methods for bridging to and from the JDK type. + * + * @param the type of the input + * @param the type of the output + */ +@FunctionalInterface +public interface Function { + + /** + * The function implementation. + * + * @param i the input to the function + * @return the output of the function + * @throws Exception any + */ + O apply(I i) throws Exception; + + /** + * Joins {@code this} function with the given function. + * + *

    {@code
    +   * import ratpack.func.Function;
    +   *
    +   * import static org.junit.Assert.assertEquals;
    +   *
    +   * public class Example {
    +   *   public static void main(String[] args) throws Exception {
    +   *     Function function = in -> in + "-bar";
    +   *     assertEquals("FOO-BAR", function.andThen(String::toUpperCase).apply("foo"));
    +   *   }
    +   * }
    +   * }
    + *

    + * Analogous to {@link java.util.function.Function#andThen(java.util.function.Function)}. + * + * @param after the function to apply to the result of {@code this} function + * @param the type of the final output + * @return the result of applying the given function to {@code this} function + * @throws Exception any thrown by {@code this} or {@code after} + */ + default Function andThen(Function after) throws Exception { + return null; + } + + /** + * Joins the given function with {@code this} function. + * + *

    {@code
    +   * import ratpack.func.Function;
    +   *
    +   * import static org.junit.Assert.assertEquals;
    +   *
    +   * public class Example {
    +   *   public static void main(String... args) throws Exception {
    +   *     Function function = String::toUpperCase;
    +   *     assertEquals("FOO-BAR", function.compose(in -> in + "-BAR").apply("foo"));
    +   *   }
    +   * }
    +   * }
    + *

    + * Analogous to {@link java.util.function.Function#compose(java.util.function.Function)}. + * + * @param before the function to apply {@code this} function to the result of + * @param the type of the new input + * @return the result of applying {@code this} function to the result of the given function + * @throws Exception any thrown by {@code this} or {@code before} + */ + default Function compose(Function before) throws Exception { + return null; + } + + + + /** + * Returns an identity function (return value always same as input). + * + * @param the type of the input and output objects to the function + * @return a function that always returns its input argument + */ + static Function identity() { + return null; + } + + /** + * Returns a function that always returns the given argument. + * + * @param t the value to always return + * @param the type of returned value + * @return a function that returns the given value + */ + static Function constant(T t) { + return null; + } + + /** + * Creates a function that delegates to the given function if the given predicate applies, else delegates to {@link #identity()}. + *

    + * This is equivalent to {@link #when(Predicate, Function, Function) when(predicate, function, identity())}. + * + * @param predicate the condition for the argument + * @param function the function to apply if the predicate applies + * @param the type of argument and return value + * @return a function that delegates to the given function if the predicate applies, else returns the argument + * @see #when(Predicate, Function, Function) + * @see #conditional(Function, Action) + * @since 1.5 + */ + static Function when(Predicate predicate, Function function) { + return null; + } + + /** + * Creates a function that delegates to the first function if the given predicate applies, else the second function. + * + * @param predicate the condition for the argument + * @param onTrue the function to apply if the predicate applies + * @param onFalse the function to apply if the predicate DOES NOT apply + * @param the type of argument + * @param the type of return value + * @return a function that delegates to the first function if the predicate applies, else the second argument + * @see #when(Predicate, Function) + * @see #conditional(Function, Action) + * @since 1.5 + */ + static Function when(Predicate predicate, Function onTrue, Function onFalse) { + return null; + } + + /** + * A spec for adding conditions to a conditional function. + * + * @param the input type + * @param the output type + * @see #conditional(Function, Action) + * @since 1.5 + */ + interface ConditionalSpec { + + /** + * Adds a conditional function. + * + * @param predicate the condition predicate + * @param function the function to apply if the predicate applies + * @return {@code this} + */ + ConditionalSpec when(Predicate predicate, Function function); + } + + /** + * Creates a function that delegates based on the specified conditions. + *

    + * If no conditions match, an {@link IllegalArgumentException} will be thrown. + * Use {@link #conditional(Function, Action)} alternatively to specify a different "else" strategy. + * + * @param conditions the conditions + * @param the input type + * @param the output type + * @return a conditional function + * @see #conditional(Function, Action) + * @throws Exception any thrown by {@code conditions} + * @since 1.5 + */ + static Function conditional(Action> conditions) throws Exception { + return null; + } + + /** + * Creates a function that delegates based on the specified conditions. + *

    + * If no condition applies, the {@code onElse} function will be delegated to. + * + * @param onElse the function to delegate to if no condition matches + * @param conditions the conditions + * @param the input type + * @param the output type + * @return a conditional function + * @see #conditional(Action) + * @throws Exception any thrown by {@code conditions} + * @since 1.5 + */ + static Function conditional(Function onElse, Action> conditions) throws Exception { + return null; + } +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/func/MultiValueMap.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/func/MultiValueMap.java new file mode 100644 index 00000000000..7846313291c --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/func/MultiValueMap.java @@ -0,0 +1,101 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.func; + +import com.google.common.collect.ListMultimap; + +import java.util.List; +import java.util.Map; + +/** + * A map that may contain multiple values for a given key, but typically only one value. + *

    + * Unlike other multi map types, this type is optimized for the case where there is only one value for a key. + * The map acts just like a normal {@link Map}, but has extra methods for getting all values for a key. + *

    + * All implementations of this type are immutable. Mutating operations throw {@link UnsupportedOperationException}. + *

    + * Where there is multiple values for a given key, retrieving a single value will return the first value, + * where the first value is intrinsic to the service in which the map is being used. + * + * @param The type of key objects + * @param The type of value objects + */ +public interface MultiValueMap extends Map { + + static MultiValueMap empty() { + return null; + } + + /** + * All of the values for the given key. An empty list if there are no values for the key. + *

    + * The returned list is immutable. + * + * @param key The key to return all values of + * @return all of the values for the given key, or an empty list if there are no values for the key. + */ + List getAll(K key); + + /** + * Returns a new view of the map where each map value is a list of all the values for the given key (i.e. a traditional multi map). + *

    + * The returned map is immutable. + * @return A new view of the map where each map value is a list of all the values for the given key + */ + Map> getAll(); + + /** + * Get the first value for the key, or {@code null} if there are no values for the key. + * + * @param key The key to obtain the first value for + * @return The first value for the given key, or {@code null} if there are no values for the given key + */ + V get(Object key); + + /** + * Throws {@link UnsupportedOperationException}. + * + * {@inheritDoc} + */ + V put(K key, V value); + + /** + * Throws {@link UnsupportedOperationException}. + * + * {@inheritDoc} + */ + V remove(Object key); + + /** + * Throws {@link UnsupportedOperationException}. + * + * {@inheritDoc} + */ + @SuppressWarnings("NullableProblems") + void putAll(Map m); + + /** + * Throws {@link UnsupportedOperationException}. + * + * {@inheritDoc} + */ + void clear(); + + ListMultimap asMultimap(); + +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/func/Nullable.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/func/Nullable.java new file mode 100644 index 00000000000..9456fc77d05 --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/func/Nullable.java @@ -0,0 +1,33 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.func; + +import java.lang.annotation.*; + +/** + * Denotes that something may be null. + *

      + *
    • On a parameter, denotes that it is valid to supply null as the value for the parameter. + *
    • On a method, denotes that the method may return null. + *
    • On a field, denotes that the field value may be null. + *
    + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD}) +public @interface Nullable { +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/func/Predicate.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/func/Predicate.java new file mode 100644 index 00000000000..96ac048aaec --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/func/Predicate.java @@ -0,0 +1,96 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.func; + +/** + * A function that returns {@code true} or {@code false} for a value. + *

    + * This type serves the same purpose as the JDK's {@link java.util.function.Predicate}, but allows throwing checked exceptions. + * It contains methods for bridging to and from the JDK type. + * + * @param the type of object "tested" by the predicate + */ +@FunctionalInterface +public interface Predicate { + + /** + * Tests the given value. + * + * @param t the value to "test" + * @return {@code true} if the predicate applied, otherwise {@code false} + * @throws Exception any + */ + boolean apply(T t) throws Exception; + + /** + * Creates a predicate from a JDK predicate. + * + * @param predicate the JDK predicate + * @param the type of object this predicate tests + * @return the given JDK predicate as a predicate + */ + static Predicate from(java.util.function.Predicate predicate) { + return null; + } + + /** + * Creates a predicate from a Guava predicate. + * + * @param predicate the Guava predicate + * @param the type of object this predicate tests + * @return the given Guava predicate as a predicate + */ + static Predicate fromGuava(com.google.common.base.Predicate predicate) { + return null; + } + + /** + * A predicate that always returns {@code true}, regardless of the input object. + * + * @param the type of input object + * @return a predicate that always returns {@code true} + * @since 1.1 + */ + static Predicate alwaysTrue() { + return null; + } + + /** + * A predicate that always returns {@code false}, regardless of the input object. + * + * @param the type of input object + * @return a predicate that always returns {@code false} + * @since 1.1 + */ + static Predicate alwaysFalse() { + return null; + } + + /** + * Creates a function the returns one of the given values. + * + * @param onTrue the value to return if the predicate applies + * @param onFalse the value to return if the predicate does not apply + * @param the output value + * @return a function + * @since 1.5 + */ + default Function function(O onTrue, O onFalse) { + return null; + } + +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/jackson/Jackson.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/jackson/Jackson.java new file mode 100644 index 00000000000..9121d81fda1 --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/jackson/Jackson.java @@ -0,0 +1,32 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ratpack.jackson; + +import ratpack.func.Nullable; +import ratpack.core.parse.Parse; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + + +public abstract class Jackson { + + private Jackson() { + } + + public static Parse jsonNode(@Nullable ObjectMapper objectMapper) { + return null; + } +} diff --git a/java/ql/test/stubs/ratpack-1.9.x/ratpack/jackson/JsonParseOpts.java b/java/ql/test/stubs/ratpack-1.9.x/ratpack/jackson/JsonParseOpts.java new file mode 100644 index 00000000000..eaac5622981 --- /dev/null +++ b/java/ql/test/stubs/ratpack-1.9.x/ratpack/jackson/JsonParseOpts.java @@ -0,0 +1,27 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ratpack.jackson; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.Optional; + +public interface JsonParseOpts { + + Optional getObjectMapper(); + +} diff --git a/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/codec/Base64.java b/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/codec/Base64.java new file mode 100644 index 00000000000..d1599df6abe --- /dev/null +++ b/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/codec/Base64.java @@ -0,0 +1,36 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + +package org.apache.shiro.codec; + +public class Base64 { + static final int CHUNK_SIZE = 76; + static final byte[] CHUNK_SEPARATOR = "\r\n".getBytes(); + private static final int BASELENGTH = 255; + private static final int LOOKUPLENGTH = 64; + private static final int EIGHTBIT = 8; + private static final int SIXTEENBIT = 16; + private static final int TWENTYFOURBITGROUP = 24; + private static final int FOURBYTE = 4; + private static final int SIGN = -128; + private static final byte PAD = 61; + private static final byte[] base64Alphabet = new byte[255]; + private static final byte[] lookUpBase64Alphabet = new byte[64]; + + public Base64() { + } + + + public static byte[] decode(String base64Encoded) { + byte[] bytes = new byte[1024]; + return decode(bytes); + } + + public static byte[] decode(byte[] base64Data) { + return base64Data; + } + } + + diff --git a/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/crypto/AbstractSymmetricCipherService.java b/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/crypto/AbstractSymmetricCipherService.java new file mode 100644 index 00000000000..70cce8e76c5 --- /dev/null +++ b/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/crypto/AbstractSymmetricCipherService.java @@ -0,0 +1,28 @@ +package org.apache.shiro.crypto; + +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import javax.crypto.KeyGenerator; + +public abstract class AbstractSymmetricCipherService extends JcaCipherService { + protected AbstractSymmetricCipherService(String algorithmName) { + super(algorithmName); + } + + public Key generateNewKey() { + return this.generateNewKey(this.getKeySize()); + } + + public Key generateNewKey(int keyBitSize) { + KeyGenerator kg; + try { + kg = KeyGenerator.getInstance(this.getAlgorithmName()); + } catch (NoSuchAlgorithmException var5) { + String msg = "Unable to acquire " + this.getAlgorithmName() + " algorithm. This is required to function."; + throw new IllegalStateException(msg, var5); + } + + kg.init(keyBitSize); + return kg.generateKey(); + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/crypto/AesCipherService.java b/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/crypto/AesCipherService.java new file mode 100644 index 00000000000..c8a727d1400 --- /dev/null +++ b/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/crypto/AesCipherService.java @@ -0,0 +1,9 @@ +package org.apache.shiro.crypto; + +public class AesCipherService extends DefaultBlockCipherService { + private static final String ALGORITHM_NAME = "AES"; + + public AesCipherService() { + super("AES"); + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/crypto/CipherService.java b/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/crypto/CipherService.java new file mode 100644 index 00000000000..b5996d331c6 --- /dev/null +++ b/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/crypto/CipherService.java @@ -0,0 +1,12 @@ + +package org.apache.shiro.crypto; + +import java.io.InputStream; +import java.io.OutputStream; + +public interface CipherService { + + void decrypt(InputStream var1, OutputStream var2, byte[] var3) throws Exception; + + void encrypt(InputStream var1, OutputStream var2, byte[] var3) throws Exception; +} diff --git a/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/crypto/DefaultBlockCipherService.java b/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/crypto/DefaultBlockCipherService.java new file mode 100644 index 00000000000..e4aa1e8b482 --- /dev/null +++ b/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/crypto/DefaultBlockCipherService.java @@ -0,0 +1,120 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + +package org.apache.shiro.crypto; + +public class DefaultBlockCipherService extends AbstractSymmetricCipherService { + private static final int DEFAULT_BLOCK_SIZE = 0; + private static final String TRANSFORMATION_STRING_DELIMITER = "/"; + private static final int DEFAULT_STREAMING_BLOCK_SIZE = 8; + private String modeName; + private int blockSize; + private String paddingSchemeName; + private String streamingModeName; + private int streamingBlockSize; + private String streamingPaddingSchemeName; + private String transformationString; + private String streamingTransformationString; + + public DefaultBlockCipherService(String algorithmName) { + super(algorithmName); + this.modeName = OperationMode.CBC.name(); + this.paddingSchemeName = PaddingScheme.PKCS5.getTransformationName(); + this.blockSize = 0; + this.streamingModeName = OperationMode.CBC.name(); + this.streamingPaddingSchemeName = PaddingScheme.PKCS5.getTransformationName(); + this.streamingBlockSize = 8; + } + + public String getModeName() { + return null; + } + + public void setModeName(String modeName) { + } + + public void setMode(OperationMode mode) { + } + + public String getPaddingSchemeName() { + return null; + } + + public void setPaddingSchemeName(String paddingSchemeName) { + + } + + public void setPaddingScheme(PaddingScheme paddingScheme) { + + } + + public int getBlockSize() { + return 1; + } + + public void setBlockSize(int blockSize) { + } + + public String getStreamingModeName() { + return null; + } + + private boolean isModeStreamingCompatible(String modeName) { + return false; + } + + public void setStreamingModeName(String streamingModeName) { + + } + + public void setStreamingMode(OperationMode mode) { + + } + + public String getStreamingPaddingSchemeName() { + return null; + } + + public void setStreamingPaddingSchemeName(String streamingPaddingSchemeName) { + } + + public void setStreamingPaddingScheme(PaddingScheme scheme) { + } + + public int getStreamingBlockSize() { + return 1; + } + + public void setStreamingBlockSize(int streamingBlockSize) { + } + + protected String getTransformationString(boolean streaming) { + return null; + } + + private String buildTransformationString() { + return null; + } + + private String buildStreamingTransformationString() { + return null; + } + + private String buildTransformationString(String modeName, String paddingSchemeName, int blockSize) { + return null; + } + + private boolean isModeInitializationVectorCompatible(String modeName) { + return false; + } + + protected boolean isGenerateInitializationVectors(boolean streaming) { + return false; + } + + protected byte[] generateInitializationVector(boolean streaming) { + return null; + } +} diff --git a/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/crypto/JcaCipherService.java b/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/crypto/JcaCipherService.java new file mode 100644 index 00000000000..8d1c628e4ff --- /dev/null +++ b/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/crypto/JcaCipherService.java @@ -0,0 +1,113 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + +package org.apache.shiro.crypto; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public abstract class JcaCipherService implements CipherService { + private static final int DEFAULT_KEY_SIZE = 128; + private static final int DEFAULT_STREAMING_BUFFER_SIZE = 512; + private static final int BITS_PER_BYTE = 8; + private static final String RANDOM_NUM_GENERATOR_ALGORITHM_NAME = "SHA1PRNG"; + private String algorithmName; + private int keySize; + private int streamingBufferSize; + private boolean generateInitializationVectors; + private int initializationVectorSize; + + + protected JcaCipherService(String algorithmName) { + this.algorithmName = algorithmName; + this.keySize = 128; + this.initializationVectorSize = 128; + this.streamingBufferSize = 512; + this.generateInitializationVectors = true; + } + + public String getAlgorithmName() { + return null; + } + + public int getKeySize() { + return 1; + } + + public void setKeySize(int keySize) { + } + + public boolean isGenerateInitializationVectors() { + return false; + } + + public void setGenerateInitializationVectors(boolean generateInitializationVectors) { + } + + public int getInitializationVectorSize() { + return 1; + } + + public void setInitializationVectorSize(int initializationVectorSize) throws IllegalArgumentException { + + } + + protected boolean isGenerateInitializationVectors(boolean streaming) { + return false; + } + + public int getStreamingBufferSize() { + return 1; + } + + public void setStreamingBufferSize(int streamingBufferSize) { + } + + protected String getTransformationString(boolean streaming) { + return null; + } + + protected byte[] generateInitializationVector(boolean streaming) { + return null; + } + + + + + private byte[] crypt(byte[] bytes, byte[] key, byte[] iv, int mode) throws IllegalArgumentException, Exception { + return null; + } + + public void encrypt(InputStream in, OutputStream out, byte[] key) throws Exception { + } + + private void encrypt(InputStream in, OutputStream out, byte[] key, byte[] iv, boolean prependIv) throws Exception { + } + + public void decrypt(InputStream in, OutputStream out, byte[] key) throws Exception { + } + + private void decrypt(InputStream in, OutputStream out, byte[] key, boolean ivPrepended) throws Exception { + } + + private void decrypt(InputStream in, OutputStream out, byte[] decryptionKey, byte[] iv) throws Exception { + + } + + private void crypt(InputStream in, OutputStream out, byte[] keyBytes, byte[] iv, int cryptMode) throws Exception { + + } + + +} diff --git a/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/crypto/OperationMode.java b/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/crypto/OperationMode.java new file mode 100644 index 00000000000..d5eadd91170 --- /dev/null +++ b/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/crypto/OperationMode.java @@ -0,0 +1,19 @@ + +package org.apache.shiro.crypto; + +public enum OperationMode { + CBC, + CCM, + CFB, + CTR, + EAX, + ECB, + GCM, + NONE, + OCB, + OFB, + PCBC; + + private OperationMode() { + } +} diff --git a/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/crypto/PaddingScheme.java b/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/crypto/PaddingScheme.java new file mode 100644 index 00000000000..d09017041bc --- /dev/null +++ b/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/crypto/PaddingScheme.java @@ -0,0 +1,25 @@ +package org.apache.shiro.crypto; + +public enum PaddingScheme { + NONE("NoPadding"), + ISO10126("ISO10126Padding"), + OAEP("OAEPPadding"), + OAEPWithMd5AndMgf1("OAEPWithMD5AndMGF1Padding"), + OAEPWithSha1AndMgf1("OAEPWithSHA-1AndMGF1Padding"), + OAEPWithSha256AndMgf1("OAEPWithSHA-256AndMGF1Padding"), + OAEPWithSha384AndMgf1("OAEPWithSHA-384AndMGF1Padding"), + OAEPWithSha512AndMgf1("OAEPWithSHA-512AndMGF1Padding"), + PKCS1("PKCS1Padding"), + PKCS5("PKCS5Padding"), + SSL3("SSL3Padding"); + + private final String transformationName; + + private PaddingScheme(String transformationName) { + this.transformationName = transformationName; + } + + public String getTransformationName() { + return this.transformationName; + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/mgt/AbstractRememberMeManager.java b/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/mgt/AbstractRememberMeManager.java new file mode 100644 index 00000000000..e251ed8bc0c --- /dev/null +++ b/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/mgt/AbstractRememberMeManager.java @@ -0,0 +1,34 @@ + +package org.apache.shiro.mgt; +import org.apache.shiro.crypto.AesCipherService; + +public abstract class AbstractRememberMeManager { + + private byte[] encryptionCipherKey; + private byte[] decryptionCipherKey; + private AesCipherService cipherService; + + public AbstractRememberMeManager() { + AesCipherService cipherService = new AesCipherService(); + this.cipherService = cipherService; + this.setCipherKey(cipherService.generateNewKey().getEncoded()); + } + + public void setEncryptionCipherKey(byte[] encryptionCipherKey) { + + this.encryptionCipherKey = encryptionCipherKey; + } + + + public void setDecryptionCipherKey(byte[] decryptionCipherKey) { + this.decryptionCipherKey = decryptionCipherKey; + } + + + public void setCipherKey(byte[] cipherKey) { + this.setEncryptionCipherKey(cipherKey); + this.setDecryptionCipherKey(cipherKey); + } + + +} diff --git a/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/web/mgt/CookieRememberMeManager.java b/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/web/mgt/CookieRememberMeManager.java new file mode 100644 index 00000000000..39811aeca7a --- /dev/null +++ b/java/ql/test/stubs/shiro-core-1.4.0/org/apache/shiro/web/mgt/CookieRememberMeManager.java @@ -0,0 +1,30 @@ +package org.apache.shiro.web.mgt; + +import org.apache.shiro.mgt.AbstractRememberMeManager; +public class CookieRememberMeManager extends AbstractRememberMeManager { + + public CookieRememberMeManager() { + } + + + public void setCookie() { + } + + protected void rememberSerializedIdentity() { + } + + private boolean isIdentityRemoved() { + return false; + } + + protected byte[] getRememberedSerializedIdentity() { + return null; + } + + private String ensurePadding(String base64) { + return null; + } + + + +} diff --git a/java/upgrades/017ac1ed2df1eaa5d8c4ae1849261c82392209d4/old.dbscheme b/java/upgrades/017ac1ed2df1eaa5d8c4ae1849261c82392209d4/old.dbscheme new file mode 100644 index 00000000000..017ac1ed2df --- /dev/null +++ b/java/upgrades/017ac1ed2df1eaa5d8c4ae1849261c82392209d4/old.dbscheme @@ -0,0 +1,983 @@ +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * javac A.java B.java C.java + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * javac A.java B.java C.java + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--javac-args` + * 2 | A.java + * 3 | B.java + * 4 | C.java + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | A.java + * 1 | B.java + * 2 | C.java + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + * External artifacts + */ + +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +snapshotDate( + unique date snapshotDate : date ref +); + +sourceLocationPrefix( + string prefix : string ref +); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/* + * SMAP + */ + +smap_header( + int outputFileId: @file ref, + string outputFilename: string ref, + string defaultStratum: string ref +); + +smap_files( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + string inputFileName: string ref, + int inputFileId: @file ref +); + +smap_lines( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + int inputStartLine: int ref, + int inputLineCount: int ref, + int outputStartLine: int ref, + int outputLineIncrement: int ref +); + +/* + * Locations and files + */ + +@location = @location_default ; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +hasLocation( + int locatableid: @locatable ref, + int id: @location ref +); + +@sourceline = @locatable ; + +#keyset[element_id] +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +/* + * Java + */ + +cupackage( + unique int id: @file ref, + int packageid: @package ref +); + +#keyset[fileid,keyName] +jarManifestMain( + int fileid: @file ref, + string keyName: string ref, + string value: string ref +); + +#keyset[fileid,entryName,keyName] +jarManifestEntries( + int fileid: @file ref, + string entryName: string ref, + string keyName: string ref, + string value: string ref +); + +packages( + unique int id: @package, + string nodeName: string ref +); + +primitives( + unique int id: @primitive, + string nodeName: string ref +); + +modifiers( + unique int id: @modifier, + string nodeName: string ref +); + +classes( + unique int id: @class, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @class ref +); + +isRecord( + unique int id: @class ref +); + +interfaces( + unique int id: @interface, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @interface ref +); + +fielddecls( + unique int id: @fielddecl, + int parentid: @reftype ref +); + +#keyset[fieldId] #keyset[fieldDeclId,pos] +fieldDeclaredIn( + int fieldId: @field ref, + int fieldDeclId: @fielddecl ref, + int pos: int ref +); + +fields( + unique int id: @field, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @field ref +); + +constrs( + unique int id: @constructor, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @constructor ref +); + +methods( + unique int id: @method, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @method ref +); + +#keyset[parentid,pos] +params( + unique int id: @param, + int typeid: @type ref, + int pos: int ref, + int parentid: @callable ref, + int sourceid: @param ref +); + +paramName( + unique int id: @param ref, + string nodeName: string ref +); + +isVarargsParam( + int param: @param ref +); + +exceptions( + unique int id: @exception, + int typeid: @type ref, + int parentid: @callable ref +); + +isAnnotType( + int interfaceid: @interface ref +); + +isAnnotElem( + int methodid: @method ref +); + +annotValue( + int parentid: @annotation ref, + int id2: @method ref, + unique int value: @expr ref +); + +isEnumType( + int classid: @class ref +); + +isEnumConst( + int fieldid: @field ref +); + +#keyset[parentid,pos] +typeVars( + unique int id: @typevariable, + string nodeName: string ref, + int pos: int ref, + int kind: int ref, // deprecated + int parentid: @typeorcallable ref +); + +wildcards( + unique int id: @wildcard, + string nodeName: string ref, + int kind: int ref +); + +#keyset[parentid,pos] +typeBounds( + unique int id: @typebound, + int typeid: @reftype ref, + int pos: int ref, + int parentid: @boundedtype ref +); + +#keyset[parentid,pos] +typeArgs( + int argumentid: @reftype ref, + int pos: int ref, + int parentid: @typeorcallable ref +); + +isParameterized( + int memberid: @member ref +); + +isRaw( + int memberid: @member ref +); + +erasure( + unique int memberid: @member ref, + int erasureid: @member ref +); + +#keyset[classid] #keyset[parent] +isAnonymClass( + int classid: @class ref, + int parent: @classinstancexpr ref +); + +#keyset[typeid] #keyset[parent] +isLocalClassOrInterface( + int typeid: @classorinterface ref, + int parent: @localtypedeclstmt ref +); + +isDefConstr( + int constructorid: @constructor ref +); + +#keyset[exprId] +lambdaKind( + int exprId: @lambdaexpr ref, + int bodyKind: int ref +); + +arrays( + unique int id: @array, + string nodeName: string ref, + int elementtypeid: @type ref, + int dimension: int ref, + int componenttypeid: @type ref +); + +enclInReftype( + unique int child: @reftype ref, + int parent: @reftype ref +); + +extendsReftype( + int id1: @reftype ref, + int id2: @classorinterface ref +); + +implInterface( + int id1: @classorarray ref, + int id2: @interface ref +); + +permits( + int id1: @classorinterface ref, + int id2: @classorinterface ref +); + +hasModifier( + int id1: @modifiable ref, + int id2: @modifier ref +); + +imports( + unique int id: @import, + int holder: @typeorpackage ref, + string name: string ref, + int kind: int ref +); + +#keyset[parent,idx] +stmts( + unique int id: @stmt, + int kind: int ref, + int parent: @stmtparent ref, + int idx: int ref, + int bodydecl: @callable ref +); + +@stmtparent = @callable | @stmt | @switchexpr; + +case @stmt.kind of + 0 = @block +| 1 = @ifstmt +| 2 = @forstmt +| 3 = @enhancedforstmt +| 4 = @whilestmt +| 5 = @dostmt +| 6 = @trystmt +| 7 = @switchstmt +| 8 = @synchronizedstmt +| 9 = @returnstmt +| 10 = @throwstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @emptystmt +| 14 = @exprstmt +| 15 = @labeledstmt +| 16 = @assertstmt +| 17 = @localvariabledeclstmt +| 18 = @localtypedeclstmt +| 19 = @constructorinvocationstmt +| 20 = @superconstructorinvocationstmt +| 21 = @case +| 22 = @catchclause +| 23 = @yieldstmt +; + +#keyset[parent,idx] +exprs( + unique int id: @expr, + int kind: int ref, + int typeid: @type ref, + int parent: @exprparent ref, + int idx: int ref +); + +callableEnclosingExpr( + unique int id: @expr ref, + int callable_id: @callable ref +); + +statementEnclosingExpr( + unique int id: @expr ref, + int statement_id: @stmt ref +); + +isParenthesized( + unique int id: @expr ref, + int parentheses: int ref +); + +case @expr.kind of + 1 = @arrayaccess +| 2 = @arraycreationexpr +| 3 = @arrayinit +| 4 = @assignexpr +| 5 = @assignaddexpr +| 6 = @assignsubexpr +| 7 = @assignmulexpr +| 8 = @assigndivexpr +| 9 = @assignremexpr +| 10 = @assignandexpr +| 11 = @assignorexpr +| 12 = @assignxorexpr +| 13 = @assignlshiftexpr +| 14 = @assignrshiftexpr +| 15 = @assignurshiftexpr +| 16 = @booleanliteral +| 17 = @integerliteral +| 18 = @longliteral +| 19 = @floatingpointliteral +| 20 = @doubleliteral +| 21 = @characterliteral +| 22 = @stringliteral +| 23 = @nullliteral +| 24 = @mulexpr +| 25 = @divexpr +| 26 = @remexpr +| 27 = @addexpr +| 28 = @subexpr +| 29 = @lshiftexpr +| 30 = @rshiftexpr +| 31 = @urshiftexpr +| 32 = @andbitexpr +| 33 = @orbitexpr +| 34 = @xorbitexpr +| 35 = @andlogicalexpr +| 36 = @orlogicalexpr +| 37 = @ltexpr +| 38 = @gtexpr +| 39 = @leexpr +| 40 = @geexpr +| 41 = @eqexpr +| 42 = @neexpr +| 43 = @postincexpr +| 44 = @postdecexpr +| 45 = @preincexpr +| 46 = @predecexpr +| 47 = @minusexpr +| 48 = @plusexpr +| 49 = @bitnotexpr +| 50 = @lognotexpr +| 51 = @castexpr +| 52 = @newexpr +| 53 = @conditionalexpr +| 54 = @parexpr // deprecated +| 55 = @instanceofexpr +| 56 = @localvariabledeclexpr +| 57 = @typeliteral +| 58 = @thisaccess +| 59 = @superaccess +| 60 = @varaccess +| 61 = @methodaccess +| 62 = @unannotatedtypeaccess +| 63 = @arraytypeaccess +| 64 = @packageaccess +| 65 = @wildcardtypeaccess +| 66 = @declannotation +| 67 = @uniontypeaccess +| 68 = @lambdaexpr +| 69 = @memberref +| 70 = @annotatedtypeaccess +| 71 = @typeannotation +| 72 = @intersectiontypeaccess +| 73 = @switchexpr +; + +@classinstancexpr = @newexpr | @lambdaexpr | @memberref + +@annotation = @declannotation | @typeannotation +@typeaccess = @unannotatedtypeaccess | @annotatedtypeaccess + +@assignment = @assignexpr + | @assignop; + +@unaryassignment = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr; + +@assignop = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + | @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignurshiftexpr; + +@literal = @booleanliteral + | @integerliteral + | @longliteral + | @floatingpointliteral + | @doubleliteral + | @characterliteral + | @stringliteral + | @nullliteral; + +@binaryexpr = @mulexpr + | @divexpr + | @remexpr + | @addexpr + | @subexpr + | @lshiftexpr + | @rshiftexpr + | @urshiftexpr + | @andbitexpr + | @orbitexpr + | @xorbitexpr + | @andlogicalexpr + | @orlogicalexpr + | @ltexpr + | @gtexpr + | @leexpr + | @geexpr + | @eqexpr + | @neexpr; + +@unaryexpr = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr + | @minusexpr + | @plusexpr + | @bitnotexpr + | @lognotexpr; + +@caller = @classinstancexpr + | @methodaccess + | @constructorinvocationstmt + | @superconstructorinvocationstmt; + +callableBinding( + unique int callerid: @caller ref, + int callee: @callable ref +); + +memberRefBinding( + unique int id: @expr ref, + int callable: @callable ref +); + +@exprparent = @stmt | @expr | @callable | @field | @fielddecl | @class | @interface | @param | @localvar | @typevariable; + +variableBinding( + unique int expr: @varaccess ref, + int variable: @variable ref +); + +@variable = @localscopevariable | @field; + +@localscopevariable = @localvar | @param; + +localvars( + unique int id: @localvar, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @localvariabledeclexpr ref +); + +@namedexprorstmt = @breakstmt + | @continuestmt + | @labeledstmt + | @literal; + +namestrings( + string name: string ref, + string value: string ref, + unique int parent: @namedexprorstmt ref +); + +/* + * Modules + */ + +#keyset[name] +modules( + unique int id: @module, + string name: string ref +); + +isOpen( + int id: @module ref +); + +#keyset[fileId] +cumodule( + int fileId: @file ref, + int moduleId: @module ref +); + +@directive = @requires + | @exports + | @opens + | @uses + | @provides + +#keyset[directive] +directives( + int id: @module ref, + int directive: @directive ref +); + +requires( + unique int id: @requires, + int target: @module ref +); + +isTransitive( + int id: @requires ref +); + +isStatic( + int id: @requires ref +); + +exports( + unique int id: @exports, + int target: @package ref +); + +exportsTo( + int id: @exports ref, + int target: @module ref +); + +opens( + unique int id: @opens, + int target: @package ref +); + +opensTo( + int id: @opens ref, + int target: @module ref +); + +uses( + unique int id: @uses, + string serviceInterface: string ref +); + +provides( + unique int id: @provides, + string serviceInterface: string ref +); + +providesWith( + int id: @provides ref, + string serviceImpl: string ref +); + +/* + * Javadoc + */ + +javadoc( + unique int id: @javadoc +); + +isNormalComment( + int commentid : @javadoc ref +); + +isEolComment( + int commentid : @javadoc ref +); + +hasJavadoc( + int documentableid: @member ref, + int javadocid: @javadoc ref +); + +#keyset[parentid,idx] +javadocTag( + unique int id: @javadocTag, + string name: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +#keyset[parentid,idx] +javadocText( + unique int id: @javadocText, + string text: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +@javadocParent = @javadoc | @javadocTag; +@javadocElement = @javadocTag | @javadocText; + +@typeorpackage = @type | @package; + +@typeorcallable = @type | @callable; +@classorinterface = @interface | @class; +@boundedtype = @typevariable | @wildcard; +@reftype = @classorinterface | @array | @boundedtype; +@classorarray = @class | @array; +@type = @primitive | @reftype; +@callable = @method | @constructor; +@element = @file | @package | @primitive | @class | @interface | @method | @constructor | @modifier | @param | @exception | @field | + @annotation | @boundedtype | @array | @localvar | @expr | @stmt | @import | @fielddecl; + +@modifiable = @member_modifiable| @param | @localvar ; + +@member_modifiable = @class | @interface | @method | @constructor | @field ; + +@member = @method | @constructor | @field | @reftype ; + +@locatable = @file | @class | @interface | @fielddecl | @field | @constructor | @method | @param | @exception + | @boundedtype | @typebound | @array | @primitive + | @import | @stmt | @expr | @localvar | @javadoc | @javadocTag | @javadocText + | @xmllocatable; + +@top = @element | @locatable | @folder; + +/* + * XML Files + */ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* + * configuration files with key value pairs + */ + +configs( + unique int id: @config +); + +configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref +); + +configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref +); + +configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref +); + +@configLocatable = @config | @configName | @configValue; diff --git a/java/upgrades/017ac1ed2df1eaa5d8c4ae1849261c82392209d4/semmlecode.dbscheme b/java/upgrades/017ac1ed2df1eaa5d8c4ae1849261c82392209d4/semmlecode.dbscheme new file mode 100755 index 00000000000..89a76edebff --- /dev/null +++ b/java/upgrades/017ac1ed2df1eaa5d8c4ae1849261c82392209d4/semmlecode.dbscheme @@ -0,0 +1,982 @@ +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * javac A.java B.java C.java + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * javac A.java B.java C.java + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--javac-args` + * 2 | A.java + * 3 | B.java + * 4 | C.java + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | A.java + * 1 | B.java + * 2 | C.java + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + * External artifacts + */ + +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +snapshotDate( + unique date snapshotDate : date ref +); + +sourceLocationPrefix( + string prefix : string ref +); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/* + * SMAP + */ + +smap_header( + int outputFileId: @file ref, + string outputFilename: string ref, + string defaultStratum: string ref +); + +smap_files( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + string inputFileName: string ref, + int inputFileId: @file ref +); + +smap_lines( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + int inputStartLine: int ref, + int inputLineCount: int ref, + int outputStartLine: int ref, + int outputLineIncrement: int ref +); + +/* + * Locations and files + */ + +@location = @location_default ; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +hasLocation( + int locatableid: @locatable ref, + int id: @location ref +); + +@sourceline = @locatable ; + +#keyset[element_id] +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +/* + * Java + */ + +cupackage( + unique int id: @file ref, + int packageid: @package ref +); + +#keyset[fileid,keyName] +jarManifestMain( + int fileid: @file ref, + string keyName: string ref, + string value: string ref +); + +#keyset[fileid,entryName,keyName] +jarManifestEntries( + int fileid: @file ref, + string entryName: string ref, + string keyName: string ref, + string value: string ref +); + +packages( + unique int id: @package, + string nodeName: string ref +); + +primitives( + unique int id: @primitive, + string nodeName: string ref +); + +modifiers( + unique int id: @modifier, + string nodeName: string ref +); + +classes( + unique int id: @class, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @class ref +); + +isRecord( + unique int id: @class ref +); + +interfaces( + unique int id: @interface, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @interface ref +); + +fielddecls( + unique int id: @fielddecl, + int parentid: @reftype ref +); + +#keyset[fieldId] #keyset[fieldDeclId,pos] +fieldDeclaredIn( + int fieldId: @field ref, + int fieldDeclId: @fielddecl ref, + int pos: int ref +); + +fields( + unique int id: @field, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @field ref +); + +constrs( + unique int id: @constructor, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @constructor ref +); + +methods( + unique int id: @method, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @method ref +); + +#keyset[parentid,pos] +params( + unique int id: @param, + int typeid: @type ref, + int pos: int ref, + int parentid: @callable ref, + int sourceid: @param ref +); + +paramName( + unique int id: @param ref, + string nodeName: string ref +); + +isVarargsParam( + int param: @param ref +); + +exceptions( + unique int id: @exception, + int typeid: @type ref, + int parentid: @callable ref +); + +isAnnotType( + int interfaceid: @interface ref +); + +isAnnotElem( + int methodid: @method ref +); + +annotValue( + int parentid: @annotation ref, + int id2: @method ref, + unique int value: @expr ref +); + +isEnumType( + int classid: @class ref +); + +isEnumConst( + int fieldid: @field ref +); + +#keyset[parentid,pos] +typeVars( + unique int id: @typevariable, + string nodeName: string ref, + int pos: int ref, + int kind: int ref, // deprecated + int parentid: @classorinterfaceorcallable ref +); + +wildcards( + unique int id: @wildcard, + string nodeName: string ref, + int kind: int ref +); + +#keyset[parentid,pos] +typeBounds( + unique int id: @typebound, + int typeid: @reftype ref, + int pos: int ref, + int parentid: @boundedtype ref +); + +#keyset[parentid,pos] +typeArgs( + int argumentid: @reftype ref, + int pos: int ref, + int parentid: @classorinterfaceorcallable ref +); + +isParameterized( + int memberid: @member ref +); + +isRaw( + int memberid: @member ref +); + +erasure( + unique int memberid: @member ref, + int erasureid: @member ref +); + +#keyset[classid] #keyset[parent] +isAnonymClass( + int classid: @class ref, + int parent: @classinstancexpr ref +); + +#keyset[typeid] #keyset[parent] +isLocalClassOrInterface( + int typeid: @classorinterface ref, + int parent: @localtypedeclstmt ref +); + +isDefConstr( + int constructorid: @constructor ref +); + +#keyset[exprId] +lambdaKind( + int exprId: @lambdaexpr ref, + int bodyKind: int ref +); + +arrays( + unique int id: @array, + string nodeName: string ref, + int elementtypeid: @type ref, + int dimension: int ref, + int componenttypeid: @type ref +); + +enclInReftype( + unique int child: @reftype ref, + int parent: @reftype ref +); + +extendsReftype( + int id1: @reftype ref, + int id2: @classorinterface ref +); + +implInterface( + int id1: @classorarray ref, + int id2: @interface ref +); + +permits( + int id1: @classorinterface ref, + int id2: @classorinterface ref +); + +hasModifier( + int id1: @modifiable ref, + int id2: @modifier ref +); + +imports( + unique int id: @import, + int holder: @classorinterfaceorpackage ref, + string name: string ref, + int kind: int ref +); + +#keyset[parent,idx] +stmts( + unique int id: @stmt, + int kind: int ref, + int parent: @stmtparent ref, + int idx: int ref, + int bodydecl: @callable ref +); + +@stmtparent = @callable | @stmt | @switchexpr; + +case @stmt.kind of + 0 = @block +| 1 = @ifstmt +| 2 = @forstmt +| 3 = @enhancedforstmt +| 4 = @whilestmt +| 5 = @dostmt +| 6 = @trystmt +| 7 = @switchstmt +| 8 = @synchronizedstmt +| 9 = @returnstmt +| 10 = @throwstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @emptystmt +| 14 = @exprstmt +| 15 = @labeledstmt +| 16 = @assertstmt +| 17 = @localvariabledeclstmt +| 18 = @localtypedeclstmt +| 19 = @constructorinvocationstmt +| 20 = @superconstructorinvocationstmt +| 21 = @case +| 22 = @catchclause +| 23 = @yieldstmt +; + +#keyset[parent,idx] +exprs( + unique int id: @expr, + int kind: int ref, + int typeid: @type ref, + int parent: @exprparent ref, + int idx: int ref +); + +callableEnclosingExpr( + unique int id: @expr ref, + int callable_id: @callable ref +); + +statementEnclosingExpr( + unique int id: @expr ref, + int statement_id: @stmt ref +); + +isParenthesized( + unique int id: @expr ref, + int parentheses: int ref +); + +case @expr.kind of + 1 = @arrayaccess +| 2 = @arraycreationexpr +| 3 = @arrayinit +| 4 = @assignexpr +| 5 = @assignaddexpr +| 6 = @assignsubexpr +| 7 = @assignmulexpr +| 8 = @assigndivexpr +| 9 = @assignremexpr +| 10 = @assignandexpr +| 11 = @assignorexpr +| 12 = @assignxorexpr +| 13 = @assignlshiftexpr +| 14 = @assignrshiftexpr +| 15 = @assignurshiftexpr +| 16 = @booleanliteral +| 17 = @integerliteral +| 18 = @longliteral +| 19 = @floatingpointliteral +| 20 = @doubleliteral +| 21 = @characterliteral +| 22 = @stringliteral +| 23 = @nullliteral +| 24 = @mulexpr +| 25 = @divexpr +| 26 = @remexpr +| 27 = @addexpr +| 28 = @subexpr +| 29 = @lshiftexpr +| 30 = @rshiftexpr +| 31 = @urshiftexpr +| 32 = @andbitexpr +| 33 = @orbitexpr +| 34 = @xorbitexpr +| 35 = @andlogicalexpr +| 36 = @orlogicalexpr +| 37 = @ltexpr +| 38 = @gtexpr +| 39 = @leexpr +| 40 = @geexpr +| 41 = @eqexpr +| 42 = @neexpr +| 43 = @postincexpr +| 44 = @postdecexpr +| 45 = @preincexpr +| 46 = @predecexpr +| 47 = @minusexpr +| 48 = @plusexpr +| 49 = @bitnotexpr +| 50 = @lognotexpr +| 51 = @castexpr +| 52 = @newexpr +| 53 = @conditionalexpr +| 54 = @parexpr // deprecated +| 55 = @instanceofexpr +| 56 = @localvariabledeclexpr +| 57 = @typeliteral +| 58 = @thisaccess +| 59 = @superaccess +| 60 = @varaccess +| 61 = @methodaccess +| 62 = @unannotatedtypeaccess +| 63 = @arraytypeaccess +| 64 = @packageaccess +| 65 = @wildcardtypeaccess +| 66 = @declannotation +| 67 = @uniontypeaccess +| 68 = @lambdaexpr +| 69 = @memberref +| 70 = @annotatedtypeaccess +| 71 = @typeannotation +| 72 = @intersectiontypeaccess +| 73 = @switchexpr +; + +@classinstancexpr = @newexpr | @lambdaexpr | @memberref + +@annotation = @declannotation | @typeannotation +@typeaccess = @unannotatedtypeaccess | @annotatedtypeaccess + +@assignment = @assignexpr + | @assignop; + +@unaryassignment = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr; + +@assignop = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + | @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignurshiftexpr; + +@literal = @booleanliteral + | @integerliteral + | @longliteral + | @floatingpointliteral + | @doubleliteral + | @characterliteral + | @stringliteral + | @nullliteral; + +@binaryexpr = @mulexpr + | @divexpr + | @remexpr + | @addexpr + | @subexpr + | @lshiftexpr + | @rshiftexpr + | @urshiftexpr + | @andbitexpr + | @orbitexpr + | @xorbitexpr + | @andlogicalexpr + | @orlogicalexpr + | @ltexpr + | @gtexpr + | @leexpr + | @geexpr + | @eqexpr + | @neexpr; + +@unaryexpr = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr + | @minusexpr + | @plusexpr + | @bitnotexpr + | @lognotexpr; + +@caller = @classinstancexpr + | @methodaccess + | @constructorinvocationstmt + | @superconstructorinvocationstmt; + +callableBinding( + unique int callerid: @caller ref, + int callee: @callable ref +); + +memberRefBinding( + unique int id: @expr ref, + int callable: @callable ref +); + +@exprparent = @stmt | @expr | @callable | @field | @fielddecl | @class | @interface | @param | @localvar | @typevariable; + +variableBinding( + unique int expr: @varaccess ref, + int variable: @variable ref +); + +@variable = @localscopevariable | @field; + +@localscopevariable = @localvar | @param; + +localvars( + unique int id: @localvar, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @localvariabledeclexpr ref +); + +@namedexprorstmt = @breakstmt + | @continuestmt + | @labeledstmt + | @literal; + +namestrings( + string name: string ref, + string value: string ref, + unique int parent: @namedexprorstmt ref +); + +/* + * Modules + */ + +#keyset[name] +modules( + unique int id: @module, + string name: string ref +); + +isOpen( + int id: @module ref +); + +#keyset[fileId] +cumodule( + int fileId: @file ref, + int moduleId: @module ref +); + +@directive = @requires + | @exports + | @opens + | @uses + | @provides + +#keyset[directive] +directives( + int id: @module ref, + int directive: @directive ref +); + +requires( + unique int id: @requires, + int target: @module ref +); + +isTransitive( + int id: @requires ref +); + +isStatic( + int id: @requires ref +); + +exports( + unique int id: @exports, + int target: @package ref +); + +exportsTo( + int id: @exports ref, + int target: @module ref +); + +opens( + unique int id: @opens, + int target: @package ref +); + +opensTo( + int id: @opens ref, + int target: @module ref +); + +uses( + unique int id: @uses, + string serviceInterface: string ref +); + +provides( + unique int id: @provides, + string serviceInterface: string ref +); + +providesWith( + int id: @provides ref, + string serviceImpl: string ref +); + +/* + * Javadoc + */ + +javadoc( + unique int id: @javadoc +); + +isNormalComment( + int commentid : @javadoc ref +); + +isEolComment( + int commentid : @javadoc ref +); + +hasJavadoc( + int documentableid: @member ref, + int javadocid: @javadoc ref +); + +#keyset[parentid,idx] +javadocTag( + unique int id: @javadocTag, + string name: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +#keyset[parentid,idx] +javadocText( + unique int id: @javadocText, + string text: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +@javadocParent = @javadoc | @javadocTag; +@javadocElement = @javadocTag | @javadocText; + +@classorinterface = @interface | @class; +@classorinterfaceorpackage = @classorinterface | @package; +@classorinterfaceorcallable = @classorinterface | @callable; +@boundedtype = @typevariable | @wildcard; +@reftype = @classorinterface | @array | @boundedtype; +@classorarray = @class | @array; +@type = @primitive | @reftype; +@callable = @method | @constructor; +@element = @file | @package | @primitive | @class | @interface | @method | @constructor | @modifier | @param | @exception | @field | + @annotation | @boundedtype | @array | @localvar | @expr | @stmt | @import | @fielddecl; + +@modifiable = @member_modifiable| @param | @localvar ; + +@member_modifiable = @class | @interface | @method | @constructor | @field ; + +@member = @method | @constructor | @field | @reftype ; + +@locatable = @file | @class | @interface | @fielddecl | @field | @constructor | @method | @param | @exception + | @boundedtype | @typebound | @array | @primitive + | @import | @stmt | @expr | @localvar | @javadoc | @javadocTag | @javadocText + | @xmllocatable; + +@top = @element | @locatable | @folder; + +/* + * XML Files + */ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* + * configuration files with key value pairs + */ + +configs( + unique int id: @config +); + +configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref +); + +configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref +); + +configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref +); + +@configLocatable = @config | @configName | @configValue; diff --git a/java/upgrades/017ac1ed2df1eaa5d8c4ae1849261c82392209d4/upgrade.properties b/java/upgrades/017ac1ed2df1eaa5d8c4ae1849261c82392209d4/upgrade.properties new file mode 100644 index 00000000000..526347e02aa --- /dev/null +++ b/java/upgrades/017ac1ed2df1eaa5d8c4ae1849261c82392209d4/upgrade.properties @@ -0,0 +1,3 @@ +description: Use the more specific classorinterface rather than type +compatibility: backwards + diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index 94319e4998c..8eadda7e63a 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -991,7 +991,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - if (attrs.isSymbolicLink()) return FileVisitResult.SKIP_SUBTREE; + if (!attrs.isRegularFile() && !attrs.isDirectory()) return FileVisitResult.SKIP_SUBTREE; if (!file.equals(currentRoot[0]) && excludes.contains(file)) return FileVisitResult.SKIP_SUBTREE; diff --git a/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java index 1e9b7d6ad84..62ee175f4a1 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/FileExtractor.java @@ -5,7 +5,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; -import java.nio.charset.Charset; +import java.nio.charset.CharacterCodingException; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.LinkedHashSet; @@ -17,6 +17,7 @@ import com.semmle.js.extractor.trapcache.CachingTrapWriter; import com.semmle.js.extractor.trapcache.ITrapCache; import com.semmle.util.data.StringUtil; import com.semmle.util.exception.Exceptions; +import com.semmle.util.exception.ResourceError; import com.semmle.util.extraction.ExtractorOutputConfig; import com.semmle.util.files.FileUtil; import com.semmle.util.io.WholeIO; @@ -438,7 +439,16 @@ public class FileExtractor { } // populate source archive - String source = new WholeIO(config.getDefaultEncoding()).strictread(f); + WholeIO wholeIO = new WholeIO(config.getDefaultEncoding(), true); + String source = wholeIO.read(f); + if (source == null) { + if (wholeIO.getLastException() instanceof CharacterCodingException) { + System.err.println("Skipped due to unsupported character encoding: " + f); + return 0; + } else { + throw new ResourceError("Failed to read file " + f, wholeIO.getLastException()); + } + } outputConfig.getSourceArchive().add(f, source); // extract language-independent bits diff --git a/javascript/extractor/src/com/semmle/js/extractor/HTMLExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/HTMLExtractor.java index e850e9e3b34..664d898d5e1 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/HTMLExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/HTMLExtractor.java @@ -89,18 +89,23 @@ public class HTMLExtractor implements IExtractor { } } else { Attributes attributes = elt.getAttributes(); + boolean attributesAreExtracted = shouldExtractAttributes(elt); // attributes can be null for directives if (attributes != null) for (Attribute attr : attributes) { // ignore empty attributes if (attr.getValue() == null || attr.getValue().isEmpty()) continue; + // If attributes are not extracted we can't use the attribute as the parent node. + // In this case, use the enclosing element as the node. + Segment parentSegment = attributesAreExtracted ? attr : elt; + extractTemplateTags( textualExtractor, attr.getSource(), attr.getBegin(), attr.getEnd(), - () -> context.getNodeLabel(attr)); + () -> context.getNodeLabel(parentSegment)); String source = attr.getValue(); int valueStart = attr.getValueSegment().getBegin(); @@ -113,7 +118,7 @@ public class HTMLExtractor implements IExtractor { source, valueStart, false /* isTypeScript */, - context.getNodeLabel(attr)); + context.getNodeLabel(parentSegment)); } else if (isAngularTemplateAttributeName(attr.getName())) { // For an attribute *ngFor="let var of EXPR", start parsing at EXPR int offset = 0; @@ -133,7 +138,7 @@ public class HTMLExtractor implements IExtractor { source, valueStart + offset, false /* isTypeScript */, - context.getNodeLabel(attr)); + context.getNodeLabel(parentSegment)); } else if (source.startsWith("javascript:")) { source = source.substring(11); extractSnippet( @@ -144,7 +149,7 @@ public class HTMLExtractor implements IExtractor { source, valueStart + 11, false /* isTypeScript */, - context.getNodeLabel(attr)); + context.getNodeLabel(parentSegment)); } } } diff --git a/javascript/extractor/src/com/semmle/js/extractor/Main.java b/javascript/extractor/src/com/semmle/js/extractor/Main.java index 201f918f3e4..7132ba9cc2b 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/Main.java +++ b/javascript/extractor/src/com/semmle/js/extractor/Main.java @@ -43,7 +43,7 @@ public class Main { * A version identifier that should be updated every time the extractor changes in such a way that * it may produce different tuples for the same file under the same {@link ExtractorConfig}. */ - public static final String EXTRACTOR_VERSION = "2021-09-01"; + public static final String EXTRACTOR_VERSION = "2021-10-25"; public static final Pattern NEWLINE = Pattern.compile("\n"); diff --git a/javascript/extractor/tests/generatedcode/input/attributes.html b/javascript/extractor/tests/generatedcode/input/attributes.html new file mode 100644 index 00000000000..8735b988cb4 --- /dev/null +++ b/javascript/extractor/tests/generatedcode/input/attributes.html @@ -0,0 +1,10 @@ + + + +

    +
    +
    +
    +
    + + diff --git a/javascript/extractor/tests/generatedcode/output/trap/attributes.html.trap b/javascript/extractor/tests/generatedcode/output/trap/attributes.html.trap new file mode 100644 index 00000000000..cb5eb79b1ca --- /dev/null +++ b/javascript/extractor/tests/generatedcode/output/trap/attributes.html.trap @@ -0,0 +1,392 @@ +#10000=@"/attributes.html;sourcefile" +files(#10000,"/attributes.html") +#10001=@"/;folder" +folders(#10001,"/") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=* +#20002=* +template_placeholder_tag_info(#20001,#20002,"{{foo}}") +#20003=@"loc,{#10000},4,14,4,20" +locations_default(#20003,#10000,4,14,4,20) +hasLocation(#20001,#20003) +scopes(#20000,0) +#20004=@"script;{#10000},4,16" +#20005=* +lines(#20005,#20004,"foo","") +#20006=@"loc,{#10000},4,16,4,18" +locations_default(#20006,#10000,4,16,4,18) +hasLocation(#20005,#20006) +numlines(#20004,1,1,0) +#20007=* +tokeninfo(#20007,6,#20004,0,"foo") +hasLocation(#20007,#20006) +#20008=* +tokeninfo(#20008,0,#20004,1,"") +#20009=@"loc,{#10000},4,19,4,18" +locations_default(#20009,#10000,4,19,4,18) +hasLocation(#20008,#20009) +toplevels(#20004,4) +hasLocation(#20004,#20006) +#20010=@"module;{#10000},4,16" +scopes(#20010,3) +scopenodes(#20004,#20010) +scopenesting(#20010,#20000) +is_module(#20004) +#20011=* +stmts(#20011,2,#20004,0,"foo") +hasLocation(#20011,#20006) +stmt_containers(#20011,#20004) +#20012=* +exprs(#20012,79,#20011,0,"foo") +hasLocation(#20012,#20006) +enclosing_stmt(#20012,#20011) +expr_containers(#20012,#20004) +literals("foo","foo",#20012) +#20013=@"var;{foo};{#20010}" +variables(#20013,"foo",#20010) +bind(#20012,#20013) +#20014=* +entry_cfg_node(#20014,#20004) +#20015=@"loc,{#10000},4,16,4,15" +locations_default(#20015,#10000,4,16,4,15) +hasLocation(#20014,#20015) +#20016=* +exit_cfg_node(#20016,#20004) +hasLocation(#20016,#20009) +successor(#20011,#20012) +successor(#20012,#20016) +successor(#20014,#20011) +toplevel_parent_xml_node(#20004,#20001) +#20017=* +#20018=* +template_placeholder_tag_info(#20017,#20018,"{{{foo}}}") +#20019=@"loc,{#10000},5,14,5,22" +locations_default(#20019,#10000,5,14,5,22) +hasLocation(#20017,#20019) +scopes(#20000,0) +#20020=@"script;{#10000},5,17" +#20021=* +lines(#20021,#20020,"foo","") +#20022=@"loc,{#10000},5,17,5,19" +locations_default(#20022,#10000,5,17,5,19) +hasLocation(#20021,#20022) +numlines(#20020,1,1,0) +#20023=* +tokeninfo(#20023,6,#20020,0,"foo") +hasLocation(#20023,#20022) +#20024=* +tokeninfo(#20024,0,#20020,1,"") +#20025=@"loc,{#10000},5,20,5,19" +locations_default(#20025,#10000,5,20,5,19) +hasLocation(#20024,#20025) +toplevels(#20020,4) +hasLocation(#20020,#20022) +#20026=@"module;{#10000},5,17" +scopes(#20026,3) +scopenodes(#20020,#20026) +scopenesting(#20026,#20000) +is_module(#20020) +#20027=* +stmts(#20027,2,#20020,0,"foo") +hasLocation(#20027,#20022) +stmt_containers(#20027,#20020) +#20028=* +exprs(#20028,79,#20027,0,"foo") +hasLocation(#20028,#20022) +enclosing_stmt(#20028,#20027) +expr_containers(#20028,#20020) +literals("foo","foo",#20028) +#20029=@"var;{foo};{#20026}" +variables(#20029,"foo",#20026) +bind(#20028,#20029) +#20030=* +entry_cfg_node(#20030,#20020) +#20031=@"loc,{#10000},5,17,5,16" +locations_default(#20031,#10000,5,17,5,16) +hasLocation(#20030,#20031) +#20032=* +exit_cfg_node(#20032,#20020) +hasLocation(#20032,#20025) +successor(#20027,#20028) +successor(#20028,#20032) +successor(#20030,#20027) +toplevel_parent_xml_node(#20020,#20017) +#20033=* +template_placeholder_tag_info(#20033,#20018,"{{/foo}}") +#20034=@"loc,{#10000},5,23,5,30" +locations_default(#20034,#10000,5,23,5,30) +hasLocation(#20033,#20034) +scopes(#20000,0) +#20035=@"script;{#10000},5,25" +toplevels(#20035,4) +#20036=@"loc,{#10000},5,25,5,25" +locations_default(#20036,#10000,5,25,5,25) +hasLocation(#20035,#20036) +#20037=* +js_parse_errors(#20037,#20035,"Error: Unterminated regular expression","/foo") +#20038=@"loc,{#10000},5,26,5,26" +locations_default(#20038,#10000,5,26,5,26) +hasLocation(#20037,#20038) +#20039=* +lines(#20039,#20035,"/foo","") +#20040=@"loc,{#10000},5,25,5,28" +locations_default(#20040,#10000,5,25,5,28) +hasLocation(#20039,#20040) +numlines(#20035,1,0,0) +toplevel_parent_xml_node(#20035,#20033) +#20041=* +#20042=* +template_placeholder_tag_info(#20041,#20042,"{{#foo}}") +#20043=@"loc,{#10000},6,14,6,21" +locations_default(#20043,#10000,6,14,6,21) +hasLocation(#20041,#20043) +scopes(#20000,0) +#20044=@"script;{#10000},6,16" +toplevels(#20044,4) +#20045=@"loc,{#10000},6,16,6,16" +locations_default(#20045,#10000,6,16,6,16) +hasLocation(#20044,#20045) +#20046=* +js_parse_errors(#20046,#20044,"Error: Unexpected token","#foo") +hasLocation(#20046,#20045) +#20047=* +lines(#20047,#20044,"#foo","") +#20048=@"loc,{#10000},6,16,6,19" +locations_default(#20048,#10000,6,16,6,19) +hasLocation(#20047,#20048) +numlines(#20044,1,0,0) +toplevel_parent_xml_node(#20044,#20041) +#20049=* +template_placeholder_tag_info(#20049,#20042,"{{/foo}}") +#20050=@"loc,{#10000},6,22,6,29" +locations_default(#20050,#10000,6,22,6,29) +hasLocation(#20049,#20050) +scopes(#20000,0) +#20051=@"script;{#10000},6,24" +toplevels(#20051,4) +#20052=@"loc,{#10000},6,24,6,24" +locations_default(#20052,#10000,6,24,6,24) +hasLocation(#20051,#20052) +#20053=* +js_parse_errors(#20053,#20051,"Error: Unterminated regular expression","/foo") +#20054=@"loc,{#10000},6,25,6,25" +locations_default(#20054,#10000,6,25,6,25) +hasLocation(#20053,#20054) +#20055=* +lines(#20055,#20051,"/foo","") +#20056=@"loc,{#10000},6,24,6,27" +locations_default(#20056,#10000,6,24,6,27) +hasLocation(#20055,#20056) +numlines(#20051,1,0,0) +toplevel_parent_xml_node(#20051,#20049) +#20057=* +#20058=* +template_placeholder_tag_info(#20057,#20058,"{{#foo}}") +#20059=@"loc,{#10000},8,18,8,25" +locations_default(#20059,#10000,8,18,8,25) +hasLocation(#20057,#20059) +scopes(#20000,0) +#20060=@"script;{#10000},8,20" +toplevels(#20060,4) +#20061=@"loc,{#10000},8,20,8,20" +locations_default(#20061,#10000,8,20,8,20) +hasLocation(#20060,#20061) +#20062=* +js_parse_errors(#20062,#20060,"Error: Unexpected token","#foo") +hasLocation(#20062,#20061) +#20063=* +lines(#20063,#20060,"#foo","") +#20064=@"loc,{#10000},8,20,8,23" +locations_default(#20064,#10000,8,20,8,23) +hasLocation(#20063,#20064) +numlines(#20060,1,0,0) +toplevel_parent_xml_node(#20060,#20057) +#20065=* +template_placeholder_tag_info(#20065,#20058,"{{baz}}") +#20066=@"loc,{#10000},8,30,8,36" +locations_default(#20066,#10000,8,30,8,36) +hasLocation(#20065,#20066) +scopes(#20000,0) +#20067=@"script;{#10000},8,32" +#20068=* +lines(#20068,#20067,"baz","") +#20069=@"loc,{#10000},8,32,8,34" +locations_default(#20069,#10000,8,32,8,34) +hasLocation(#20068,#20069) +numlines(#20067,1,1,0) +#20070=* +tokeninfo(#20070,6,#20067,0,"baz") +hasLocation(#20070,#20069) +#20071=* +tokeninfo(#20071,0,#20067,1,"") +#20072=@"loc,{#10000},8,35,8,34" +locations_default(#20072,#10000,8,35,8,34) +hasLocation(#20071,#20072) +toplevels(#20067,4) +hasLocation(#20067,#20069) +#20073=@"module;{#10000},8,32" +scopes(#20073,3) +scopenodes(#20067,#20073) +scopenesting(#20073,#20000) +is_module(#20067) +#20074=* +stmts(#20074,2,#20067,0,"baz") +hasLocation(#20074,#20069) +stmt_containers(#20074,#20067) +#20075=* +exprs(#20075,79,#20074,0,"baz") +hasLocation(#20075,#20069) +enclosing_stmt(#20075,#20074) +expr_containers(#20075,#20067) +literals("baz","baz",#20075) +#20076=@"var;{baz};{#20073}" +variables(#20076,"baz",#20073) +bind(#20075,#20076) +#20077=* +entry_cfg_node(#20077,#20067) +#20078=@"loc,{#10000},8,32,8,31" +locations_default(#20078,#10000,8,32,8,31) +hasLocation(#20077,#20078) +#20079=* +exit_cfg_node(#20079,#20067) +hasLocation(#20079,#20072) +successor(#20074,#20075) +successor(#20075,#20079) +successor(#20077,#20074) +toplevel_parent_xml_node(#20067,#20065) +#20080=* +template_placeholder_tag_info(#20080,#20058,"{{/foo}}") +#20081=@"loc,{#10000},8,37,8,44" +locations_default(#20081,#10000,8,37,8,44) +hasLocation(#20080,#20081) +scopes(#20000,0) +#20082=@"script;{#10000},8,39" +toplevels(#20082,4) +#20083=@"loc,{#10000},8,39,8,39" +locations_default(#20083,#10000,8,39,8,39) +hasLocation(#20082,#20083) +#20084=* +js_parse_errors(#20084,#20082,"Error: Unterminated regular expression","/foo") +#20085=@"loc,{#10000},8,40,8,40" +locations_default(#20085,#10000,8,40,8,40) +hasLocation(#20084,#20085) +#20086=* +lines(#20086,#20082,"/foo","") +#20087=@"loc,{#10000},8,39,8,42" +locations_default(#20087,#10000,8,39,8,42) +hasLocation(#20086,#20087) +numlines(#20082,1,0,0) +toplevel_parent_xml_node(#20082,#20080) +#20088=* +xmlChars(#20088," +",#10000,0,0,#10000) +#20089=@"loc,{#10000},1,16,1,16" +locations_default(#20089,#10000,1,16,1,16) +xmllocations(#20088,#20089) +#20090=* +xmlChars(#20090," +",#10000,2,0,#10000) +#20091=@"loc,{#10000},10,8,10,8" +locations_default(#20091,#10000,10,8,10,8) +xmllocations(#20090,#20091) +#20092=* +xmlElements(#20092,"html",#10000,1,#10000) +#20093=@"loc,{#10000},2,1,10,7" +locations_default(#20093,#10000,2,1,10,7) +xmllocations(#20092,#20093) +#20094=* +xmlChars(#20094," + ",#20092,0,0,#10000) +#20095=@"loc,{#10000},2,7,3,2" +locations_default(#20095,#10000,2,7,3,2) +xmllocations(#20094,#20095) +#20096=* +xmlChars(#20096," +",#20092,2,0,#10000) +#20097=@"loc,{#10000},9,10,9,10" +locations_default(#20097,#10000,9,10,9,10) +xmllocations(#20096,#20097) +#20098=* +xmlElements(#20098,"body",#20092,1,#10000) +#20099=@"loc,{#10000},3,3,9,9" +locations_default(#20099,#10000,3,3,9,9) +xmllocations(#20098,#20099) +#20100=* +xmlChars(#20100," + ",#20098,0,0,#10000) +#20101=@"loc,{#10000},3,9,4,4" +locations_default(#20101,#10000,3,9,4,4) +xmllocations(#20100,#20101) +#20102=* +xmlChars(#20102," + ",#20098,2,0,#10000) +#20103=@"loc,{#10000},4,28,5,4" +locations_default(#20103,#10000,4,28,5,4) +xmllocations(#20102,#20103) +#20104=* +xmlChars(#20104," + ",#20098,4,0,#10000) +#20105=@"loc,{#10000},5,33,6,4" +locations_default(#20105,#10000,5,33,6,4) +xmllocations(#20104,#20105) +#20106=* +xmlChars(#20106," + ",#20098,6,0,#10000) +#20107=@"loc,{#10000},6,32,7,4" +locations_default(#20107,#10000,6,32,7,4) +xmllocations(#20106,#20107) +#20108=* +xmlChars(#20108," + ",#20098,8,0,#10000) +#20109=@"loc,{#10000},7,36,8,4" +locations_default(#20109,#10000,7,36,8,4) +xmllocations(#20108,#20109) +#20110=* +xmlChars(#20110," + ",#20098,10,0,#10000) +#20111=@"loc,{#10000},8,47,9,2" +locations_default(#20111,#10000,8,47,9,2) +xmllocations(#20110,#20111) +xmlElements(#20058,"div",#20098,9,#10000) +#20112=@"loc,{#10000},8,5,8,46" +locations_default(#20112,#10000,8,5,8,46) +xmllocations(#20058,#20112) +#20113=* +xmlElements(#20113,"div",#20098,7,#10000) +#20114=@"loc,{#10000},7,5,7,35" +locations_default(#20114,#10000,7,5,7,35) +xmllocations(#20113,#20114) +#20115=* +xmlElements(#20115,"div",#20098,5,#10000) +#20116=@"loc,{#10000},6,5,6,31" +locations_default(#20116,#10000,6,5,6,31) +xmllocations(#20115,#20116) +xmlAttrs(#20042,#20115,"foo","{{#foo}}{{/foo}}/",0,#10000) +#20117=@"loc,{#10000},6,10,6,30" +locations_default(#20117,#10000,6,10,6,30) +xmllocations(#20042,#20117) +#20118=* +xmlElements(#20118,"div",#20098,3,#10000) +#20119=@"loc,{#10000},5,5,5,32" +locations_default(#20119,#10000,5,5,5,32) +xmllocations(#20118,#20119) +xmlAttrs(#20018,#20118,"foo","{{{foo}}}{{/foo}}/",0,#10000) +#20120=@"loc,{#10000},5,10,5,31" +locations_default(#20120,#10000,5,10,5,31) +xmllocations(#20018,#20120) +#20121=* +xmlElements(#20121,"div",#20098,1,#10000) +#20122=@"loc,{#10000},4,5,4,27" +locations_default(#20122,#10000,4,5,4,27) +xmllocations(#20121,#20122) +xmlAttrs(#20002,#20121,"foo","{{foo}}",0,#10000) +#20123=@"loc,{#10000},4,10,4,20" +locations_default(#20123,#10000,4,10,4,20) +xmllocations(#20002,#20123) +numlines(#10000,10,3,0) +filetype(#10000,"html") diff --git a/javascript/ql/examples/qlpack.yml b/javascript/ql/examples/qlpack.yml index 039f203982c..0a688278528 100644 --- a/javascript/ql/examples/qlpack.yml +++ b/javascript/ql/examples/qlpack.yml @@ -1,4 +1,4 @@ -name: codeql-javascript-examples +name: codeql/javascript-examples version: 0.0.3 dependencies: codeql/javascript-all: "*" diff --git a/javascript/ql/experimental/adaptivethreatmodeling/README.md b/javascript/ql/experimental/adaptivethreatmodeling/README.md new file mode 100644 index 00000000000..341534e3fdf --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/README.md @@ -0,0 +1,6 @@ +# [Internal only] Adaptive Threat Modeling for JavaScript + +This directory contains CodeQL libraries and queries that power adaptive threat modeling for JavaScript. +All APIs are experimental and may change in the future. + +These queries can only be run by internal users; for external users they will return no results. diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/ATMConfig.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/ATMConfig.qll new file mode 100644 index 00000000000..bec90be46cc --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/ATMConfig.qll @@ -0,0 +1,112 @@ +/* + * For internal use only. + * + * Configures boosting for adaptive threat modeling (ATM). + */ + +private import javascript as raw +import EndpointTypes + +/** + * EXPERIMENTAL. This API may change in the future. + * + * A configuration class for defining known endpoints and endpoint filters for adaptive threat + * modeling (ATM). Each boosted query must define its own extension of this abstract class. + * + * A configuration defines a set of known sources (`isKnownSource`) and sinks (`isKnownSink`). + * It must also define a sink endpoint filter (`isEffectiveSink`) that filters candidate sinks + * predicted by the machine learning model to a set of effective sinks. + * + * To get started with ATM, you can copy-paste an implementation of the relevant predicates from a + * `DataFlow::Configuration` or `TaintTracking::Configuration` class for a standard security query. + * For example, for SQL injection you can start by defining the `isKnownSource` and `isKnownSink` + * predicates in the ATM configuration by copying and pasting the implementations of `isSource` and + * `isSink` from `SqlInjection::Configuration`. + * + * Note that if the security query configuration defines additional edges beyond the standard data + * flow edges, such as `NosqlInjection::Configuration`, you may need to replace the definition of + * `isAdditionalFlowStep` with a more generalised definition of additional edges. See + * `NosqlInjectionATM.qll` for an example of doing this. + */ +abstract class ATMConfig extends string { + bindingset[this] + ATMConfig() { any() } + + /** + * EXPERIMENTAL. This API may change in the future. + * + * Holds if `source` is a known source of flow. + */ + predicate isKnownSource(raw::DataFlow::Node source) { none() } + + /** + * EXPERIMENTAL. This API may change in the future. + * + * Holds if `sink` is a known sink of flow. + */ + predicate isKnownSink(raw::DataFlow::Node sink) { none() } + + /** + * EXPERIMENTAL. This API may change in the future. + * + * Holds if the candidate source `candidateSource` predicted by the machine learning model should be + * an effective source, i.e. one considered as a possible source of flow in the boosted query. + */ + predicate isEffectiveSource(raw::DataFlow::Node candidateSource) { none() } + + /** + * EXPERIMENTAL. This API may change in the future. + * + * Holds if the candidate sink `candidateSink` predicted by the machine learning model should be + * an effective sink, i.e. one considered as a possible sink of flow in the boosted query. + */ + predicate isEffectiveSink(raw::DataFlow::Node candidateSink) { none() } + + /** + * EXPERIMENTAL. This API may change in the future. + * + * Holds if the candidate sink `candidateSink` predicted by the machine learning model should be + * an effective sink that overrides the score provided by the machine learning model with the + * score `score` for reason `why`. The effective sinks identified by this predicate MUST be a + * subset of those identified by the `isEffectiveSink` predicate. + * + * For example, in the ATM external API query, we use this method to ensure the ATM external API + * query produces the same results as the standard external API query, but assigns flows + * involving sinks that are filtered out by the endpoint filters a score of 0. + * + * This predicate can be phased out once we no longer need to rely on predicates like + * `paddedScore` in the ATM CodeQL libraries to add scores to alert messages in a way that works + * with lexical sort orders. + */ + predicate isEffectiveSinkWithOverridingScore( + raw::DataFlow::Node candidateSink, float score, string why + ) { + none() + } + + /** + * EXPERIMENTAL. This API may change in the future. + * + * Get an endpoint type for the sources of this query. A query may have multiple applicable + * endpoint types for its sources. + */ + EndpointType getASourceEndpointType() { none() } + + /** + * EXPERIMENTAL. This API may change in the future. + * + * Get an endpoint type for the sinks of this query. A query may have multiple applicable + * endpoint types for its sinks. + */ + EndpointType getASinkEndpointType() { none() } + + /** + * EXPERIMENTAL. This API may change in the future. + * + * Specifies the default cut-off value that controls how many alerts are produced. + * The cut-off value must be in the range [0,1]. + * A cut-off value of 0 only produces alerts that are likely true-positives. + * A cut-off value of 1 produces all alerts including those that are likely false-positives. + */ + float getScoreCutoff() { result = 0.0 } +} diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/AdaptiveThreatModeling.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/AdaptiveThreatModeling.qll new file mode 100644 index 00000000000..678182f3987 --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/AdaptiveThreatModeling.qll @@ -0,0 +1,125 @@ +/* + * For internal use only. + * + * Provides information about the results of boosted queries for use in adaptive threat modeling (ATM). + */ + +private import javascript as raw +private import raw::DataFlow as DataFlow +import ATMConfig +private import BaseScoring +private import EndpointScoring as EndpointScoring + +module ATM { + /** + * EXPERIMENTAL. This API may change in the future. + * + * This module contains informational predicates about the results returned by adaptive threat + * modeling (ATM). + */ + module ResultsInfo { + /** + * Indicates whether the flow from source to sink represents a result with + * sufficiently high likelihood of being a true-positive. + */ + pragma[inline] + private predicate shouldResultBeIncluded(DataFlow::Node source, DataFlow::Node sink) { + any(ScoringResults results).shouldResultBeIncluded(source, sink) + } + + /** + * EXPERIMENTAL. This API may change in the future. + * + * Returns the score for the flow between the source `source` and the `sink` sink in the + * boosted query. + */ + pragma[inline] + float getScoreForFlow(DataFlow::Node source, DataFlow::Node sink) { + any(DataFlow::Configuration cfg).hasFlow(source, sink) and + shouldResultBeIncluded(source, sink) and + result = unique(float s | s = any(ScoringResults results).getScoreForFlow(source, sink)) + } + + /** + * Pad a score returned from `getKnownScoreForFlow` to a particular length by adding a decimal + * point if one does not already exist, and "0"s after that decimal point. + * + * Note that this predicate must itself define an upper bound on `length`, so that it has a + * finite number of results. Currently this is defined as 12. + */ + private string paddedScore(float score, int length) { + // In this definition, we must restrict the values that `length` and `score` can take on so + // that the predicate has a finite number of results. + (score = getScoreForFlow(_, _) or score = 0) and + length = result.length() and + ( + // We need to make sure the padded score contains a "." so lexically sorting the padded + // scores is equivalent to numerically sorting the scores. + score.toString().charAt(_) = "." and + result = score.toString() + or + not score.toString().charAt(_) = "." and + result = score.toString() + "." + ) + or + result = paddedScore(score, length - 1) + "0" and + length <= 12 + } + + /** + * EXPERIMENTAL. This API may change in the future. + * + * Return a string representing the score of the flow between `source` and `sink` in the + * boosted query. + * + * The returned string is a fixed length, such that lexically sorting the strings returned by + * this predicate gives the same sort order as numerically sorting the scores of the flows. + */ + pragma[inline] + string getScoreStringForFlow(DataFlow::Node source, DataFlow::Node sink) { + exists(float score | + score = getScoreForFlow(source, sink) and + ( + // A length of 12 is equivalent to 10 decimal places. + score.toString().length() >= 12 and + result = score.toString().substring(0, 12) + or + score.toString().length() < 12 and + result = paddedScore(score, 12) + ) + ) + } + + /** + * EXPERIMENTAL. This API may change in the future. + * + * Indicates whether the flow from source to sink is likely to be reported by the base security + * query. + * + * Currently this is a heuristic: it ignores potential differences in the definitions of + * additional flow steps. + */ + pragma[inline] + predicate isFlowLikelyInBaseQuery(DataFlow::Node source, DataFlow::Node sink) { + getCfg().isKnownSource(source) and getCfg().isKnownSink(sink) + } + + /** + * EXPERIMENTAL. This API may change in the future. + * + * Get additional information about why ATM included the flow from source to sink as an alert. + */ + pragma[inline] + string getAdditionalAlertInfo(DataFlow::Node source, DataFlow::Node sink) { + exists(string sourceOrigins, string sinkOrigins | + sourceOrigins = concat(any(ScoringResults results).getASourceOrigin(source), ", ") and + sinkOrigins = concat(any(ScoringResults results).getASinkOrigin(sink), ", ") and + result = + "[Source origins: " + + any(string s | if sourceOrigins != "" then s = sourceOrigins else s = "unknown") + + "; sink origins: " + + any(string s | if sinkOrigins != "" then s = sinkOrigins else s = "unknown") + "]" + ) + } + } +} diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/BaseScoring.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/BaseScoring.qll new file mode 100644 index 00000000000..8d02cc8948c --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/BaseScoring.qll @@ -0,0 +1,121 @@ +/* + * For internal use only. + * + * Provides shared scoring functionality for use in adaptive threat modeling (ATM). + */ + +private import javascript +private import ATMConfig + +external predicate adaptiveThreatModelingModels( + string modelChecksum, string modelLanguage, string modelName, string modelType +); + +/** Get the ATM configuration. */ +ATMConfig getCfg() { any() } + +/** + * This module provides functionality that takes an endpoint and provides an entity that encloses that + * endpoint and is suitable for similarity analysis. + */ +module EndpointToEntity { + private import CodeToFeatures + + /** + * Get an entity enclosing the endpoint that is suitable for similarity analysis. In general, + * this may associate multiple entities to a single endpoint. + */ + DatabaseFeatures::Entity getAnEntityForEndpoint(DataFlow::Node endpoint) { + DatabaseFeatures::entities(result, _, _, _, _, _, _, _, _) and + result.getDefinedFunction() = endpoint.getContainer().getEnclosingContainer*() + } +} + +/** + * This module provides functionality that takes an entity and provides effective endpoints within + * that entity. + * + * We use the following terminology to describe endpoints: + * + * - The *candidate* endpoints are the set of data flow nodes that should be passed to the + * appropriate endpoint filter to produce the set of effective endpoints. + * When we have a model that beats the performance of the baseline, we will likely define the + * candidate endpoints based on the most confident predictions of the model. + * - An *effective* endpoint is a candidate endpoint which passes through the endpoint filter. + * In other words, it is a candidate endpoint for which the `isEffectiveSink` (or + * `isEffectiveSource`) predicate defined in the `ATMConfig` instance in scope holds. + */ +module EntityToEffectiveEndpoint { + private import CodeToFeatures + + /** + * Returns endpoint candidates within the specified entities. + * + * The baseline implementation of this is that a candidate endpoint is any data flow node that is + * enclosed within the specified entity. + */ + private DataFlow::Node getABaselineEndpointCandidate(DatabaseFeatures::Entity entity) { + result.getContainer().getEnclosingContainer*() = entity.getDefinedFunction() + } + + /** + * Get an effective source enclosed by the specified entity. + * + * N.B. This is _not_ an inverse of `EndpointToEntity::getAnEntityForEndpoint`: the effective + * source may occur in a function defined within the specified entity. + */ + DataFlow::Node getAnEffectiveSource(DatabaseFeatures::Entity entity) { + result = getABaselineEndpointCandidate(entity) and + getCfg().isEffectiveSource(result) + } + + /** + * Get an effective sink enclosed by the specified entity. + * + * N.B. This is _not_ an inverse of `EndpointToEntity::getAnEntityForEndpoint`: the effective + * sink may occur in a function defined within the specified entity. + */ + DataFlow::Node getAnEffectiveSink(DatabaseFeatures::Entity entity) { + result = getABaselineEndpointCandidate(entity) and + getCfg().isEffectiveSink(result) + } +} + +/** + * Scoring information produced by a scoring model. + * + * Scoring models include embedding models and endpoint scoring models. + */ +abstract class ScoringResults extends string { + bindingset[this] + ScoringResults() { any() } + + /** + * Get ATM's confidence that a path between `source` and `sink` represents a security + * vulnerability. This will be a number between 0.0 and 1.0. + */ + abstract float getScoreForFlow(DataFlow::Node source, DataFlow::Node sink); + + /** + * Get a string representing why ATM included the given source in the dataflow analysis. + * + * In general, there may be multiple reasons why ATM included the given source, in which case + * this predicate should have multiple results. + */ + abstract string getASourceOrigin(DataFlow::Node source); + + /** + * Get a string representing why ATM included the given sink in the dataflow analysis. + * + * In general, there may be multiple reasons why ATM included the given sink, in which case this + * predicate should have multiple results. + */ + abstract string getASinkOrigin(DataFlow::Node sink); + + /** + * Indicates whether the flow from source to sink represents a result with + * sufficiently high likelihood of being a true-positive. + */ + pragma[inline] + abstract predicate shouldResultBeIncluded(DataFlow::Node source, DataFlow::Node sink); +} diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/CodeToFeatures.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/CodeToFeatures.qll new file mode 100644 index 00000000000..3619415606b --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/CodeToFeatures.qll @@ -0,0 +1,444 @@ +/* + * For internal use only. + * + * Extracts data about the functions in the database for use in adaptive threat modeling (ATM). + */ + +module Raw { + private import javascript as raw + + class RawAstNode = raw::ASTNode; + + class Entity = raw::Function; + + class Location = raw::Location; + + /** + * Exposed as a tool for defining anchors for semantic search. + */ + class UnderlyingFunction = raw::Function; + + /** + * Determines whether an entity should be omitted from ATM. + */ + predicate isEntityIgnored(Entity entity) { + // Ignore entities which don't have definitions, for example those in TypeScript + // declaration files. + not exists(entity.getBody()) + or + // Ignore entities with an empty body, for example the JavaScript function () => {}. + entity.getNumBodyStmt() = 0 and not exists(entity.getAReturnedExpr()) + } + + newtype WrappedAstNode = TAstNode(RawAstNode rawNode) + + /** + * This class represents nodes in the AST. + */ + class AstNode extends TAstNode { + RawAstNode rawNode; + + AstNode() { this = TAstNode(rawNode) } + + AstNode getAChildNode() { result = TAstNode(rawNode.getAChild()) } + + AstNode getParentNode() { result = TAstNode(rawNode.getParent()) } + + /** + * Holds if the AST node has `result` as its `index`th attribute. + * + * The index is not intended to mean anything, and is only here for disambiguation. + * There are no guarantees about any particular index being used (or not being used). + */ + string astNodeAttribute(int index) { + ( + // NB: Unary and binary operator expressions e.g. -a, a + b and compound + // assignments e.g. a += b can be identified by the expression type. + result = rawNode.(raw::Identifier).getName() + or + // Computed property accesses for which we can predetermine the property being accessed. + // NB: May alias with operators e.g. could have '+' as a property name. + result = rawNode.(raw::IndexExpr).getPropertyName() + or + // We use `getRawValue` to give us distinct representations for `0xa`, `0xA`, and `10`. + result = rawNode.(raw::NumberLiteral).getRawValue() + or + // We use `getValue` rather than `getRawValue` so we assign `"a"` and `'a'` the same representation. + not rawNode instanceof raw::NumberLiteral and + result = rawNode.(raw::Literal).getValue() + or + result = rawNode.(raw::TemplateElement).getRawValue() + ) and + index = 0 + } + + /** + * Returns a string indicating the "type" of the AST node. + */ + string astNodeType() { + // The definition of this method should correspond with that of the `@ast_node` entry in the + // dbscheme. + result = "js_exprs." + any(int kind | exprs(rawNode, kind, _, _, _)) + or + result = "js_properties." + any(int kind | properties(rawNode, _, _, kind, _)) + or + result = "js_stmts." + any(int kind | stmts(rawNode, kind, _, _, _)) + or + result = "js_toplevel" and rawNode instanceof raw::TopLevel + or + result = "js_typeexprs." + any(int kind | typeexprs(rawNode, kind, _, _, _)) + } + + /** + * Holds if `result` is the `index`'th child of the AST node, for some arbitrary indexing. + * A root of the AST should be its own child, with an arbitrary (though conventionally + * 0) index. + * + * Notably, the order in which child nodes are visited is not required to be meaningful, + * and no particular index is required to be meaningful. However, `(parent, index)` + * should be a keyset. + */ + pragma[nomagic] + AstNode astNodeChild(int index) { + result = + rank[index - 1](AstNode child, raw::Location l | + child = this.getAChildNode() and l = child.getLocation() + | + child + order by + l.getStartLine(), l.getStartColumn(), l.getEndLine(), l.getEndColumn(), + child.astNodeType() + ) + or + not exists(result.getParentNode()) and this = result and index = 0 + } + + raw::Location getLocation() { result = rawNode.getLocation() } + + string toString() { result = rawNode.toString() } + + predicate isEntityNameNode(Entity entity) { + exists(int index | + TAstNode(entity) = getParentNode() and + this = getParentNode().astNodeChild(index) and + // An entity name node must be the first child of the entity. + index = min(int otherIndex | exists(getParentNode().astNodeChild(otherIndex))) and + entity.getName() = rawNode.(raw::VarDecl).getName() + ) + } + } + + /** + * Holds if `result` is the `index`'th child of the `parent` entity. Such + * a node is a root of an AST associated with this entity. + */ + AstNode entityChild(AstNode parent, int index) { + // In JavaScript, entities appear in the AST parent/child relationship. + result = parent.astNodeChild(index) + } + + /** + * Holds if `node` is contained in `entity`. Note that a single node may be contained + * in multiple entities, if they are nested. An entity, in particular, should be + * reported as contained within itself. + */ + predicate entityContains(Entity entity, AstNode node) { + node.getParentNode*() = TAstNode(entity) and not node.isEntityNameNode(entity) + } + + /** + * Get the name of the entity. + * + * We attempt to assign unnamed entities approximate names if they are passed to a likely + * external library function. If we can't assign them an approximate name, we give them the name + * `""`, so that these entities are included in `AdaptiveThreatModeling.qll`. + * + * For entities which have multiple names, we choose the lexically smallest name. + */ + string getEntityName(Entity entity) { + if exists(entity.getName()) + then + // https://github.com/github/ml-ql-adaptive-threat-modeling/issues/244 discusses making use + // of all the names during training. + result = min(entity.getName()) + else + if exists(getApproximateNameForEntity(entity)) + then result = getApproximateNameForEntity(entity) + else result = "" + } + + /** + * Holds if the call `call` has `entity` is its `argumentIndex`th argument. + */ + private predicate entityUsedAsArgumentToCall( + Entity entity, raw::DataFlow::CallNode call, int argumentIndex + ) { + raw::DataFlow::localFlowStep*(call.getArgument(argumentIndex), entity.flow()) + } + + /** + * Returns a generated name for the entity. This name is generated such that + * entities with the same names have similar behaviour. + */ + private string getApproximateNameForEntity(Entity entity) { + count(raw::DataFlow::CallNode call, int index | entityUsedAsArgumentToCall(entity, call, index)) = + 1 and + exists(raw::DataFlow::CallNode call, int index, string basePart | + entityUsedAsArgumentToCall(entity, call, index) and + ( + if count(getReceiverName(call)) = 1 + then basePart = getReceiverName(call) + "." + else basePart = "" + ) and + result = basePart + call.getCalleeName() + "#functionalargument" + ) + } + + private string getReceiverName(raw::DataFlow::CallNode call) { + result = call.getReceiver().asExpr().(raw::VarAccess).getName() + } + + /** Consistency checks: these predicates should each have no results */ + module Consistency { + /** `getEntityName` should assign each entity a single name. */ + query predicate entityWithManyNames(Entity entity, string name) { + name = getEntityName(entity) and + count(getEntityName(entity)) > 1 + } + + query predicate nodeWithNoType(AstNode node) { not exists(node.astNodeType()) } + + query predicate nodeWithManyTypes(AstNode node, string type) { + type = node.astNodeType() and + count(node.astNodeType()) > 1 + } + + query predicate nodeWithNoParent(AstNode node, string type) { + not node = any(AstNode parent).astNodeChild(_) and + type = node.astNodeType() and + not exists(RawAstNode rawNode | node = TAstNode(rawNode) and rawNode instanceof raw::Module) + } + + query predicate duplicateChildIndex(AstNode parent, int index, AstNode child) { + child = parent.astNodeChild(index) and + count(parent.astNodeChild(index)) > 1 + } + + query predicate duplicateAttributeIndex(AstNode node, int index) { + exists(node.astNodeAttribute(index)) and + count(node.astNodeAttribute(index)) > 1 + } + } +} + +module Wrapped { + /* + * We require any node with attributes to be a leaf. Where a non-leaf node + * has an attribute, we instead create a synthetic leaf node that has that + * attribute. + */ + + /** + * Holds if the AST node `e` is a leaf node. + */ + private predicate isLeaf(Raw::AstNode e) { not exists(e.astNodeChild(_)) } + + newtype WrappedEntity = + TEntity(Raw::Entity entity) { + exists(entity.getLocation().getFile().getRelativePath()) and + Raw::entityContains(entity, _) + } + + /** + * A type ranging over the kinds of entities for which we want to consider embeddings. + */ + class Entity extends WrappedEntity { + Raw::Entity rawEntity; + + Entity() { this = TEntity(rawEntity) and not Raw::isEntityIgnored(rawEntity) } + + string getName() { result = Raw::getEntityName(rawEntity) } + + AstNode getAstRoot(int index) { + result = TAstNode(rawEntity, Raw::entityChild(Raw::TAstNode(rawEntity), index)) + } + + string toString() { result = rawEntity.toString() } + + Raw::Location getLocation() { result = rawEntity.getLocation() } + + Raw::UnderlyingFunction getDefinedFunction() { result = rawEntity } + } + + newtype WrappedAstNode = + TAstNode(Raw::Entity enclosingEntity, Raw::AstNode node) { + Raw::entityContains(enclosingEntity, node) + } or + TSyntheticNode( + Raw::Entity enclosingEntity, Raw::AstNode node, int syntheticChildIndex, int attrIndex + ) { + Raw::entityContains(enclosingEntity, node) and + exists(node.astNodeAttribute(attrIndex)) and + not isLeaf(node) and + if exists(node.astNodeChild(_)) + then + syntheticChildIndex = + attrIndex - min(int other | exists(node.astNodeAttribute(other))) + + max(int other | exists(node.astNodeChild(other))) + 1 + else syntheticChildIndex = attrIndex + } + + pragma[nomagic] + private AstNode injectedChild(Raw::Entity enclosingEntity, Raw::AstNode parent, int index) { + result = TAstNode(enclosingEntity, parent.astNodeChild(index)) or + result = TSyntheticNode(enclosingEntity, parent, index, _) + } + + /** + * A type ranging over AST nodes. Ultimately, only nodes contained in entities will + * be considered. + */ + class AstNode extends WrappedAstNode { + Raw::Entity enclosingEntity; + Raw::AstNode rawNode; + + AstNode() { + ( + this = TAstNode(enclosingEntity, rawNode) or + this = TSyntheticNode(enclosingEntity, rawNode, _, _) + ) and + not Raw::isEntityIgnored(enclosingEntity) + } + + string getAttribute(int index) { + result = rawNode.astNodeAttribute(index) and + not exists(TSyntheticNode(enclosingEntity, rawNode, _, index)) + } + + string getType() { result = rawNode.astNodeType() } + + AstNode getChild(int index) { result = injectedChild(enclosingEntity, rawNode, index) } + + string toString() { result = getType() } + + Raw::Location getLocation() { result = rawNode.getLocation() } + } + + /** + * A synthetic AST node, created to be a leaf for an otherwise non-leaf attribute. + */ + class SyntheticAstNode extends AstNode, TSyntheticNode { + int childIndex; + int attributeIndex; + + SyntheticAstNode() { + this = TSyntheticNode(enclosingEntity, rawNode, childIndex, attributeIndex) + } + + override string getAttribute(int index) { + result = rawNode.astNodeAttribute(attributeIndex) and index = attributeIndex + } + + override string getType() { + result = rawNode.astNodeType() + "::" + } + + override AstNode getChild(int index) { none() } + } +} + +module DatabaseFeatures { + /** + * Exposed as a tool for defining anchors for semantic search. + */ + class UnderlyingFunction = Raw::UnderlyingFunction; + + private class Location = Raw::Location; + + private newtype TEntityOrAstNode = + TEntity(Wrapped::Entity entity) or + TAstNode(Wrapped::AstNode astNode) + + class EntityOrAstNode extends TEntityOrAstNode { + abstract string getType(); + + abstract string toString(); + + abstract Location getLocation(); + } + + class Entity extends EntityOrAstNode, TEntity { + Wrapped::Entity entity; + + Entity() { this = TEntity(entity) } + + string getName() { result = entity.getName() } + + AstNode getAstRoot(int index) { result = TAstNode(entity.getAstRoot(index)) } + + override string getType() { result = "javascript function" } + + override string toString() { result = "Entity: " + getName() } + + override Location getLocation() { result = entity.getLocation() } + + UnderlyingFunction getDefinedFunction() { result = entity.getDefinedFunction() } + } + + class AstNode extends EntityOrAstNode, TAstNode { + Wrapped::AstNode rawNode; + + AstNode() { this = TAstNode(rawNode) } + + AstNode getChild(int index) { result = TAstNode(rawNode.getChild(index)) } + + string getAttribute(int index) { result = rawNode.getAttribute(index) } + + override string getType() { result = rawNode.getType() } + + override string toString() { result = this.getType() } + + override Location getLocation() { result = rawNode.getLocation() } + } + + /** Consistency checks: these predicates should each have no results */ + module Consistency { + query predicate nonLeafAttribute(AstNode node, int index, string attribute) { + attribute = node.getAttribute(index) and + exists(node.getChild(_)) + } + } + + query predicate entities( + Entity entity, string entity_name, string entity_type, string path, int startLine, + int startColumn, int endLine, int endColumn, string absolutePath + ) { + entity_name = entity.getName() and + entity_type = entity.getType() and + exists(Location l | l = entity.getLocation() | + path = l.getFile().getRelativePath() and + absolutePath = l.getFile().getAbsolutePath() and + l.hasLocationInfo(_, startLine, startColumn, endLine, endColumn) + ) + } + + query predicate astNodes( + Entity enclosingEntity, EntityOrAstNode parent, int index, AstNode node, string node_type + ) { + node = enclosingEntity.getAstRoot(index) and + parent = enclosingEntity and + node_type = node.getType() + or + astNodes(enclosingEntity, _, _, parent, _) and + node = parent.(AstNode).getChild(index) and + node_type = node.getType() + } + + query predicate nodeAttributes(AstNode node, string attr) { + // Only get attributes of AST nodes we extract. + // This excludes nodes in standard libraries since the standard library files + // are located outside the source root. + astNodes(_, _, _, node, _) and + attr = node.getAttribute(_) + } +} diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/CoreKnowledge.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/CoreKnowledge.qll new file mode 100644 index 00000000000..29494c16855 --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/CoreKnowledge.qll @@ -0,0 +1,208 @@ +/* + * For internal use only. + * + * Provides predicates that expose the knowledge of models + * in the core CodeQL JavaScript libraries. + */ + +private import javascript +private import semmle.javascript.security.dataflow.XxeCustomizations +private import semmle.javascript.security.dataflow.RemotePropertyInjectionCustomizations +private import semmle.javascript.security.dataflow.TypeConfusionThroughParameterTamperingCustomizations +private import semmle.javascript.security.dataflow.ZipSlipCustomizations +private import semmle.javascript.security.dataflow.TaintedPathCustomizations +private import semmle.javascript.security.dataflow.CleartextLoggingCustomizations +private import semmle.javascript.security.dataflow.XpathInjectionCustomizations +private import semmle.javascript.security.dataflow.Xss::Shared as Xss +private import semmle.javascript.security.dataflow.StackTraceExposureCustomizations +private import semmle.javascript.security.dataflow.ClientSideUrlRedirectCustomizations +private import semmle.javascript.security.dataflow.CodeInjectionCustomizations +private import semmle.javascript.security.dataflow.RequestForgeryCustomizations +private import semmle.javascript.security.dataflow.CorsMisconfigurationForCredentialsCustomizations +private import semmle.javascript.security.dataflow.ShellCommandInjectionFromEnvironmentCustomizations +private import semmle.javascript.security.dataflow.DifferentKindsComparisonBypassCustomizations +private import semmle.javascript.security.dataflow.CommandInjectionCustomizations +private import semmle.javascript.security.dataflow.PrototypePollutionCustomizations +private import semmle.javascript.security.dataflow.UnvalidatedDynamicMethodCallCustomizations +private import semmle.javascript.security.dataflow.TaintedFormatStringCustomizations +private import semmle.javascript.security.dataflow.NosqlInjectionCustomizations +private import semmle.javascript.security.dataflow.PostMessageStarCustomizations +private import semmle.javascript.security.dataflow.RegExpInjectionCustomizations +private import semmle.javascript.security.dataflow.SqlInjectionCustomizations +private import semmle.javascript.security.dataflow.InsecureRandomnessCustomizations +private import semmle.javascript.security.dataflow.XmlBombCustomizations +private import semmle.javascript.security.dataflow.InsufficientPasswordHashCustomizations +private import semmle.javascript.security.dataflow.HardcodedCredentialsCustomizations +private import semmle.javascript.security.dataflow.FileAccessToHttpCustomizations +private import semmle.javascript.security.dataflow.UnsafeDynamicMethodAccessCustomizations +private import semmle.javascript.security.dataflow.UnsafeDeserializationCustomizations +private import semmle.javascript.security.dataflow.HardcodedDataInterpretedAsCodeCustomizations +private import semmle.javascript.security.dataflow.ServerSideUrlRedirectCustomizations +private import semmle.javascript.security.dataflow.IndirectCommandInjectionCustomizations +private import semmle.javascript.security.dataflow.ConditionalBypassCustomizations +private import semmle.javascript.security.dataflow.HttpToFileAccessCustomizations +private import semmle.javascript.security.dataflow.BrokenCryptoAlgorithmCustomizations +private import semmle.javascript.security.dataflow.LoopBoundInjectionCustomizations +private import semmle.javascript.security.dataflow.CleartextStorageCustomizations +import FilteringReasons + +/** + * Holds if the node `n` is a known sink in a modeled library, or a sibling-argument of such a sink. + */ +predicate isArgumentToKnownLibrarySinkFunction(DataFlow::Node n) { + exists(DataFlow::InvokeNode invk, DataFlow::Node known | + invk.getAnArgument() = n and invk.getAnArgument() = known and isKnownLibrarySink(known) + ) +} + +/** + * Holds if the node `n` is a known sink for the external API security query. + * + * This corresponds to known sinks from security queries whose sources include remote flow and + * DOM-based sources. + */ +predicate isKnownExternalAPIQuerySink(DataFlow::Node n) { + n instanceof Xxe::Sink or + n instanceof TaintedPath::Sink or + n instanceof XpathInjection::Sink or + n instanceof Xss::Sink or + n instanceof ClientSideUrlRedirect::Sink or + n instanceof CodeInjection::Sink or + n instanceof RequestForgery::Sink or + n instanceof CorsMisconfigurationForCredentials::Sink or + n instanceof CommandInjection::Sink or + n instanceof PrototypePollution::Sink or + n instanceof UnvalidatedDynamicMethodCall::Sink or + n instanceof TaintedFormatString::Sink or + n instanceof NosqlInjection::Sink or + n instanceof PostMessageStar::Sink or + n instanceof RegExpInjection::Sink or + n instanceof SqlInjection::Sink or + n instanceof XmlBomb::Sink or + n instanceof ZipSlip::Sink or + n instanceof UnsafeDeserialization::Sink or + n instanceof ServerSideUrlRedirect::Sink or + n instanceof CleartextStorage::Sink or + n instanceof HttpToFileAccess::Sink +} + +/** + * Holds if the node `n` is a known sink in a modeled library. + */ +predicate isKnownLibrarySink(DataFlow::Node n) { + isKnownExternalAPIQuerySink(n) or + n instanceof CleartextLogging::Sink or + n instanceof StackTraceExposure::Sink or + n instanceof ShellCommandInjectionFromEnvironment::Sink or + n instanceof InsecureRandomness::Sink or + n instanceof FileAccessToHttp::Sink or + n instanceof IndirectCommandInjection::Sink +} + +/** + * Holds if the node `n` is known as the predecessor in a modeled flow step. + */ +predicate isKnownStepSrc(DataFlow::Node n) { + any(TaintTracking::AdditionalTaintStep s).step(n, _) or + any(DataFlow::AdditionalFlowStep s).step(n, _) or + any(DataFlow::AdditionalFlowStep s).step(n, _, _, _) +} + +/** + * Holds if `n` is an argument to a function of a builtin object. + */ +private predicate isArgumentToBuiltinFunction(DataFlow::Node n, FilteringReason reason) { + exists(DataFlow::SourceNode builtin, DataFlow::SourceNode receiver, DataFlow::InvokeNode invk | + ( + builtin instanceof DataFlow::ArrayCreationNode and + reason instanceof ArgumentToArrayReason + or + builtin = + DataFlow::globalVarRef([ + "Map", "Set", "WeakMap", "WeakSet", "Number", "Object", "String", "Array", "Error", + "Math", "Boolean" + ]) and + reason instanceof ArgumentToBuiltinGlobalVarRefReason + ) + | + receiver = [builtin.getAnInvocation(), builtin] and + invk = [receiver, receiver.getAPropertyRead()].getAnInvocation() and + invk.getAnArgument() = n + ) + or + exists(Expr primitive, MethodCallExpr c | + primitive instanceof ConstantString or + primitive instanceof NumberLiteral or + primitive instanceof BooleanLiteral + | + c.calls(primitive, _) and + c.getAnArgument() = n.asExpr() and + reason instanceof ConstantReceiverReason + ) + or + exists(DataFlow::CallNode call | + call.getAnArgument() = n and + call.getCalleeName() = + [ + "indexOf", "hasOwnProperty", "substring", "isDecimal", "decode", "encode", "keys", "shift", + "values", "forEach", "toString", "slice", "splice", "push", "isArray", "sort" + ] and + reason instanceof BuiltinCallNameReason + ) +} + +predicate isOtherModeledArgument(DataFlow::Node n, FilteringReason reason) { + isArgumentToBuiltinFunction(n, reason) + or + any(LodashUnderscore::Member m).getACall().getAnArgument() = n and + reason instanceof LodashUnderscoreArgumentReason + or + exists(ClientRequest r | + r.getAnArgument() = n or n = r.getUrl() or n = r.getHost() or n = r.getADataNode() + ) and + reason instanceof ClientRequestReason + or + exists(PromiseDefinition p | + n = [p.getResolveParameter(), p.getRejectParameter()].getACall().getAnArgument() + ) and + reason instanceof PromiseDefinitionReason + or + n instanceof CryptographicKey and reason instanceof CryptographicKeyReason + or + any(CryptographicOperation op).getInput().flow() = n and + reason instanceof CryptographicOperationFlowReason + or + exists(DataFlow::CallNode call | n = call.getAnArgument() | + call.getCalleeName() = getAStandardLoggerMethodName() and + reason instanceof LoggerMethodReason + or + call.getCalleeName() = ["setTimeout", "clearTimeout"] and + reason instanceof TimeoutReason + or + call.getReceiver() = DataFlow::globalVarRef(["localStorage", "sessionStorage"]) and + reason instanceof ReceiverStorageReason + or + call instanceof StringOps::StartsWith and reason instanceof StringStartsWithReason + or + call instanceof StringOps::EndsWith and reason instanceof StringEndsWithReason + or + call instanceof StringOps::RegExpTest and reason instanceof StringRegExpTestReason + or + call instanceof EventRegistration and reason instanceof EventRegistrationReason + or + call instanceof EventDispatch and reason instanceof EventDispatchReason + or + call = any(MembershipCandidate c).getTest() and + reason instanceof MembershipCandidateTestReason + or + call instanceof FileSystemAccess and reason instanceof FileSystemAccessReason + or + call instanceof DatabaseAccess and reason instanceof DatabaseAccessReason + or + call = DOM::domValueRef() and reason instanceof DOMReason + or + call.getCalleeName() = "next" and + exists(DataFlow::FunctionNode f | call = f.getLastParameter().getACall()) and + reason instanceof NextFunctionCallReason + ) +} diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointFeatures.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointFeatures.qll new file mode 100644 index 00000000000..85ca174ca4e --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointFeatures.qll @@ -0,0 +1,290 @@ +/* + * For internal use only. + * + * Extracts data about the database for use in adaptive threat modeling (ATM). + */ + +import javascript +import CodeToFeatures +import EndpointScoring + +/** + * Gets the value of the token-based feature named `featureName` for the endpoint `endpoint`. + * + * This is a single string containing a space-separated list of tokens. + */ +private string getTokenFeature(DataFlow::Node endpoint, string featureName) { + // Features for endpoints that are contained within a function. + exists(DatabaseFeatures::Entity entity | entity = getRepresentativeEntityForEndpoint(endpoint) | + // The name of the function that encloses the endpoint. + featureName = "enclosingFunctionName" and result = entity.getName() + or + // A feature containing natural language tokens from the function that encloses the endpoint in + // the order that they appear in the source code. + featureName = "enclosingFunctionBody" and + result = unique(string x | x = FunctionBodies::getBodyTokenFeatureForEntity(entity)) + ) + or + exists(getACallBasedTokenFeatureComponent(endpoint, _, featureName)) and + result = + concat(DataFlow::CallNode call, string component | + component = getACallBasedTokenFeatureComponent(endpoint, call, featureName) + | + component, " " + ) + or + // The access path of the function being called, both with and without structural info, if the + // function being called originates from an external API. For example, the endpoint here: + // + // ```js + // const mongoose = require('mongoose'), + // User = mongoose.model('User', null); + // User.findOne(ENDPOINT); + // ``` + // + // would have a callee access path with structural info of + // `mongoose member model instanceorreturn member findOne instanceorreturn`, and a callee access + // path without structural info of `mongoose model findOne`. + // + // These features indicate that the callee comes from (reading the access path backwards) an + // instance of the `findOne` member of an instance of the `model` member of the `mongoose` + // external library. + exists(AccessPaths::Boolean includeStructuralInfo | + featureName = + "calleeAccessPath" + + any(string x | if includeStructuralInfo = true then x = "WithStructuralInfo" else x = "") and + result = + concat(API::Node node, string accessPath | + node.getInducingNode().(DataFlow::CallNode).getAnArgument() = endpoint and + accessPath = AccessPaths::getAccessPath(node, includeStructuralInfo) + | + accessPath, " " + ) + ) +} + +/** + * Gets a value of the function-call-related token-based feature named `featureName` associated + * with the function call `call` and the endpoint `endpoint`. + * + * This may in general report multiple strings, each containing a space-separated list of tokens. + * + * **Technical details:** This predicate can have multiple values per endpoint and feature name. As a + * result, the results from this predicate must be concatenated together. However concatenating + * other features like the function body tokens is expensive, so we separate out this predicate + * from others like `FunctionBodies::getBodyTokenFeatureForEntity` to avoid having to perform this + * concatenation operation on other features like the function body tokens. + */ +private string getACallBasedTokenFeatureComponent( + DataFlow::Node endpoint, DataFlow::CallNode call, string featureName +) { + // Features for endpoints that are an argument to a function call. + endpoint = call.getAnArgument() and + ( + // The name of the function being called, e.g. in a call `Artist.findOne(...)`, this is `findOne`. + featureName = "calleeName" and result = call.getCalleeName() + or + // The name of the receiver of the call, e.g. in a call `Artist.findOne(...)`, this is `Artist`. + featureName = "receiverName" and result = call.getReceiver().asExpr().(VarRef).getName() + or + // The argument index of the endpoint, e.g. in `f(a, endpoint, b)`, this is 1. + featureName = "argumentIndex" and + result = any(int argIndex | call.getArgument(argIndex) = endpoint).toString() + or + // The name of the API that the function being called originates from, if the function being + // called originates from an external API. For example, the endpoint here: + // + // ```js + // const mongoose = require('mongoose'), + // User = mongoose.model('User', null); + // User.findOne(ENDPOINT); + // ``` + // + // would have a callee API name of `mongoose`. + featureName = "calleeApiName" and + result = getAnApiName(call) + ) +} + +/** This module provides functionality for getting the function body feature associated with a particular entity. */ +module FunctionBodies { + /** Holds if `node` is an AST node within the entity `entity` and `token` is a node attribute associated with `node`. */ + private predicate bodyTokens( + DatabaseFeatures::Entity entity, DatabaseFeatures::AstNode node, string token + ) { + DatabaseFeatures::astNodes(entity, _, _, node, _) and + token = unique(string t | DatabaseFeatures::nodeAttributes(node, t)) + } + + /** + * Gets the body token feature for the specified entity. + * + * This is a string containing natural language tokens in the order that they appear in the source code for the entity. + */ + string getBodyTokenFeatureForEntity(DatabaseFeatures::Entity entity) { + // If a function has more than 256 body subtokens, then featurize it as absent. This + // approximates the behavior of the classifer on non-generic body features where large body + // features are replaced by the absent token. + if count(DatabaseFeatures::AstNode node, string token | bodyTokens(entity, node, token)) > 256 + then result = "" + else + result = + concat(int i, string rankedToken | + rankedToken = + rank[i](DatabaseFeatures::AstNode node, string token, Location l | + bodyTokens(entity, node, token) and l = node.getLocation() + | + token + order by + l.getFile().getAbsolutePath(), l.getStartLine(), l.getStartColumn(), l.getEndLine(), + l.getEndColumn(), token + ) + | + rankedToken, " " order by i + ) + } +} + +/** + * Returns a name of the API that a node originates from, if the node originates from an API. + * + * This predicate may have multiple results if the node corresponds to multiple nodes in the API graph forest. + */ +pragma[inline] +private string getAnApiName(DataFlow::Node node) { + API::moduleImport(result).getASuccessor*().getInducingNode() = node +} + +/** + * This module provides functionality for getting a representation of the access path of nodes + * within the program. + * + * For example, it gives the `User.find` callee here: + * + * ```js + * const mongoose = require('mongoose'), + * User = mongoose.model('User', null); + * User.find({ 'isAdmin': true }) + * ``` + * the access path `mongoose member model instanceorreturn member find instanceorreturn`. + * + * This access path is based on the simplified access path that the untrusted data flowing to + * external API query associates to each of its sinks, with modifications to optionally include + * explicit structural information and to improve how well the path tokenizes. + */ +private module AccessPaths { + bindingset[str] + private predicate isNumericString(string str) { exists(str.toInt()) } + + /** + * Gets a parameter of `base` with name `name`, or a property named `name` of a destructuring parameter. + */ + private API::Node getNamedParameter(API::Node base, string name) { + exists(API::Node param | + param = base.getAParameter() and + not param = base.getReceiver() + | + result = param and + name = param.getAnImmediateUse().asExpr().(Parameter).getName() + or + param.getAnImmediateUse().asExpr() instanceof DestructuringPattern and + result = param.getMember(name) + ) + } + + /** + * A utility class that is equivalent to `boolean` but does not require type joining. + */ + class Boolean extends boolean { + Boolean() { this = true or this = false } + } + + /** Get the access path for the node. This includes structural information like `member`, `param`, and `functionalarg` if `includeStructuralInfo` is true. */ + string getAccessPath(API::Node node, Boolean includeStructuralInfo) { + node = API::moduleImport(result) + or + exists(API::Node base, string baseName | + base.getDepth() < node.getDepth() and baseName = getAccessPath(base, includeStructuralInfo) + | + // e.g. `new X`, `X()` + node = [base.getInstance(), base.getReturn()] and + if includeStructuralInfo = true + then result = baseName + " instanceorreturn" + else result = baseName + or + // e.g. `x.y`, `x[y]`, `const { y } = x`, where `y` is non-numeric and is known at analysis + // time. + exists(string member | + node = base.getMember(member) and + not node = base.getUnknownMember() and + not isNumericString(member) and + not (member = "default" and base = API::moduleImport(_)) and + not member = "then" // use the 'promised' edges for .then callbacks + | + if includeStructuralInfo = true + then result = baseName + " member " + member + else result = baseName + " " + member + ) + or + // e.g. `x.y`, `x[y]`, `const { y } = x`, where `y` is numeric or not known at analysis time. + ( + node = base.getUnknownMember() or + node = base.getMember(any(string s | isNumericString(s))) + ) and + if includeStructuralInfo = true then result = baseName + " member" else result = baseName + or + // e.g. `x.then(y => ...)` + node = base.getPromised() and + result = baseName + or + // e.g. `x.y((a, b) => ...)` + // Name callback parameters after their name in the source code. + // For example, the `res` parameter in `express.get('/foo', (req, res) => {...})` will be + // named `express member get functionalarg param res`. + exists(string paramName | + node = getNamedParameter(base.getAParameter(), paramName) and + ( + if includeStructuralInfo = true + then result = baseName + " functionalarg param " + paramName + else result = baseName + " " + paramName + ) + or + exists(string callbackName, string index | + node = + getNamedParameter(base.getASuccessor("param " + index).getMember(callbackName), + paramName) and + index != "-1" and // ignore receiver + if includeStructuralInfo = true + then + result = + baseName + " functionalarg " + index + " " + callbackName + " param " + paramName + else result = baseName + " " + index + " " + callbackName + " " + paramName + ) + ) + ) + } +} + +/** Get a name of a supported generic token-based feature. */ +private string getASupportedFeatureName() { + result = + [ + "enclosingFunctionName", "calleeName", "receiverName", "argumentIndex", "calleeApiName", + "calleeAccessPath", "calleeAccessPathWithStructuralInfo", "enclosingFunctionBody" + ] +} + +/** + * Generic token-based features for ATM. + * + * This predicate holds if the generic token-based feature named `featureName` has the value + * `featureValue` for the endpoint `endpoint`. + */ +predicate tokenFeatures(DataFlow::Node endpoint, string featureName, string featureValue) { + featureName = getASupportedFeatureName() and + ( + featureValue = unique(string x | x = getTokenFeature(endpoint, featureName)) + or + not exists(unique(string x | x = getTokenFeature(endpoint, featureName))) and featureValue = "" + ) +} diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointScoring.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointScoring.qll new file mode 100644 index 00000000000..8efe184e10e --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointScoring.qll @@ -0,0 +1,223 @@ +/* + * For internal use only. + * + * Provides an implementation of scoring alerts for use in adaptive threat modeling (ATM). + */ + +private import javascript +import BaseScoring +import CodeToFeatures +import EndpointFeatures as EndpointFeatures +import EndpointTypes + +private string getACompatibleModelChecksum() { + adaptiveThreatModelingModels(result, "javascript", _, "atm-endpoint-scoring") +} + +/** + * The maximum number of AST nodes an entity containing an endpoint should have before we should + * choose a smaller entity to represent the endpoint. + * + * This is intended to represent a balance in terms of the amount of context we provide to the + * model: we don't want the function to be too small, because then it doesn't contain very much + * context and miss useful information, but also we don't want it to be too large, because then + * there's likely to be a lot of irrelevant or very loosely related context. + */ +private int getMaxNumAstNodes() { result = 1024 } + +/** + * Returns the number of AST nodes contained within the specified entity. + */ +private int getNumAstNodesInEntity(DatabaseFeatures::Entity entity) { + // Restrict the values `entity` can take on + entity = EndpointToEntity::getAnEntityForEndpoint(_) and + result = + count(DatabaseFeatures::AstNode astNode | DatabaseFeatures::astNodes(entity, _, _, astNode, _)) +} + +/** + * Get a single entity to use as the representative entity for the endpoint. + * + * We try to use the largest entity containing the endpoint that's below the AST node limit defined + * in `getMaxNumAstNodes`. In the event of a tie, we use the entity that appears first within the + * source archive. + * + * If no entities are smaller than the AST node limit, then we use the smallest entity containing + * the endpoint. + */ +DatabaseFeatures::Entity getRepresentativeEntityForEndpoint(DataFlow::Node endpoint) { + // Check whether there's an entity containing the endpoint that's smaller than the AST node limit. + if + getNumAstNodesInEntity(EndpointToEntity::getAnEntityForEndpoint(endpoint)) <= + getMaxNumAstNodes() + then + // Use the largest entity smaller than the AST node limit, resolving ties using the entity that + // appears first in the source archive. + result = + rank[1](DatabaseFeatures::Entity entity, int numAstNodes, Location l | + entity = EndpointToEntity::getAnEntityForEndpoint(endpoint) and + numAstNodes = getNumAstNodesInEntity(entity) and + numAstNodes <= getMaxNumAstNodes() and + l = entity.getLocation() + | + entity + order by + numAstNodes desc, l.getStartLine(), l.getStartColumn(), l.getEndLine(), l.getEndColumn() + ) + else + // Use the smallest entity, resolving ties using the entity that + // appears first in the source archive. + result = + rank[1](DatabaseFeatures::Entity entity, int numAstNodes, Location l | + entity = EndpointToEntity::getAnEntityForEndpoint(endpoint) and + numAstNodes = getNumAstNodesInEntity(entity) and + l = entity.getLocation() + | + entity + order by + numAstNodes, l.getStartLine(), l.getStartColumn(), l.getEndLine(), l.getEndColumn() + ) +} + +module ModelScoring { + predicate endpoints(DataFlow::Node endpoint) { + getCfg().isEffectiveSource(endpoint) or + getCfg().isEffectiveSink(endpoint) + } + + private int requestedEndpointTypes() { result = any(EndpointType type).getEncoding() } + + private predicate relevantTokenFeatures( + DataFlow::Node endpoint, string featureName, string featureValue + ) { + endpoints(endpoint) and + EndpointFeatures::tokenFeatures(endpoint, featureName, featureValue) + } + + predicate endpointScores(DataFlow::Node endpoint, int encodedEndpointType, float score) = + scoreEndpoints(endpoints/1, requestedEndpointTypes/0, relevantTokenFeatures/3, + getACompatibleModelChecksum/0)(endpoint, encodedEndpointType, score) +} + +/** + * Return ATM's confidence that `source` is a source for the given security query. This will be a + * number between 0.0 and 1.0. + */ +private float getScoreForSource(DataFlow::Node source) { + if getCfg().isKnownSource(source) + then result = 1.0 + else ( + // This restriction on `source` has no semantic effect but improves performance. + getCfg().isEffectiveSource(source) and + ModelScoring::endpointScores(source, getCfg().getASourceEndpointType().getEncoding(), result) + ) +} + +/** + * Return ATM's confidence that `sink` is a sink for the given security query. This will be a + * number between 0.0 and 1.0. + */ +private float getScoreForSink(DataFlow::Node sink) { + if getCfg().isKnownSink(sink) + then result = 1.0 + else + if getCfg().isEffectiveSinkWithOverridingScore(sink, result, _) + then any() + else ( + // This restriction on `sink` has no semantic effect but improves performance. + getCfg().isEffectiveSink(sink) and + ModelScoring::endpointScores(sink, getCfg().getASinkEndpointType().getEncoding(), result) + ) +} + +class EndpointScoringResults extends ScoringResults { + EndpointScoringResults() { + this = "EndpointScoringResults" and exists(getACompatibleModelChecksum()) + } + + /** + * Get ATM's confidence that a path between `source` and `sink` represents a security + * vulnerability. This will be a number between 0.0 and 1.0. + */ + override float getScoreForFlow(DataFlow::Node source, DataFlow::Node sink) { + result = getScoreForSource(source) * getScoreForSink(sink) + } + + /** + * Get a string representing why ATM included the given source in the dataflow analysis. + * + * In general, there may be multiple reasons why ATM included the given source, in which case + * this predicate should have multiple results. + */ + pragma[inline] + override string getASourceOrigin(DataFlow::Node source) { + result = "known" and getCfg().isKnownSource(source) + or + result = "predicted" and getCfg().isEffectiveSource(source) + } + + /** + * Get a string representing why ATM included the given sink in the dataflow analysis. + * + * In general, there may be multiple reasons why ATM included the given sink, in which case + * this predicate should have multiple results. + */ + pragma[inline] + override string getASinkOrigin(DataFlow::Node sink) { + result = "known" and getCfg().isKnownSink(sink) + or + not getCfg().isKnownSink(sink) and + getCfg().isEffectiveSinkWithOverridingScore(sink, _, result) + or + not getCfg().isKnownSink(sink) and + not getCfg().isEffectiveSinkWithOverridingScore(sink, _, _) and + result = + "predicted (scores: " + + concat(EndpointType type, float score | + ModelScoring::endpointScores(sink, type.getEncoding(), score) + | + type.getDescription() + "=" + score.toString(), ", " order by type.getEncoding() + ) + ")" and + getCfg().isEffectiveSink(sink) + } + + pragma[inline] + override predicate shouldResultBeIncluded(DataFlow::Node source, DataFlow::Node sink) { + if getCfg().isKnownSink(sink) + then any() + else + if getCfg().isEffectiveSinkWithOverridingScore(sink, _, _) + then + exists(float score | + getCfg().isEffectiveSinkWithOverridingScore(sink, score, _) and + score >= getCfg().getScoreCutoff() + ) + else ( + // This restriction on `sink` has no semantic effect but improves performance. + getCfg().isEffectiveSink(sink) and + exists(float sinkScore | + ModelScoring::endpointScores(sink, getCfg().getASinkEndpointType().getEncoding(), + sinkScore) and + // Include the endpoint if (a) the query endpoint type scores higher than all other + // endpoint types, or (b) the query endpoint type scores at least + // 0.5 - (getCfg().getScoreCutoff() / 2). + sinkScore >= + [ + max(float s | ModelScoring::endpointScores(sink, _, s)), + 0.5 - getCfg().getScoreCutoff() / 2 + ] + ) + ) + } +} + +module Debugging { + query predicate hopInputEndpoints = ModelScoring::endpoints/1; + + query predicate endpointScores = ModelScoring::endpointScores/3; + + query predicate shouldResultBeIncluded(DataFlow::Node source, DataFlow::Node sink) { + any(ScoringResults scoringResults).shouldResultBeIncluded(source, sink) and + any(DataFlow::Configuration cfg).hasFlow(source, sink) + } +} diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointTypes.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointTypes.qll new file mode 100644 index 00000000000..4f4bc2782ab --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointTypes.qll @@ -0,0 +1,57 @@ +/** + * For internal use only. + * + * Defines the set of classes that endpoint scoring models can predict. Endpoint scoring models must + * only predict classes defined within this file. This file is the source of truth for the integer + * representation of each of these classes. + */ +newtype TEndpointType = + TNotASinkType() or + TXssSinkType() or + TNosqlInjectionSinkType() or + TSqlInjectionSinkType() or + TTaintedPathSinkType() + +/** A class that can be predicted by endpoint scoring models. */ +abstract class EndpointType extends TEndpointType { + abstract string getDescription(); + + abstract int getEncoding(); + + string toString() { result = getDescription() } +} + +/** The `NotASink` class that can be predicted by endpoint scoring models. */ +class NotASinkType extends EndpointType, TNotASinkType { + override string getDescription() { result = "NotASink" } + + override int getEncoding() { result = 0 } +} + +/** The `XssSink` class that can be predicted by endpoint scoring models. */ +class XssSinkType extends EndpointType, TXssSinkType { + override string getDescription() { result = "XssSink" } + + override int getEncoding() { result = 1 } +} + +/** The `NosqlInjectionSink` class that can be predicted by endpoint scoring models. */ +class NosqlInjectionSinkType extends EndpointType, TNosqlInjectionSinkType { + override string getDescription() { result = "NosqlInjectionSink" } + + override int getEncoding() { result = 2 } +} + +/** The `SqlInjectionSink` class that can be predicted by endpoint scoring models. */ +class SqlInjectionSinkType extends EndpointType, TSqlInjectionSinkType { + override string getDescription() { result = "SqlInjectionSink" } + + override int getEncoding() { result = 3 } +} + +/** The `TaintedPathSink` class that can be predicted by endpoint scoring models. */ +class TaintedPathSinkType extends EndpointType, TTaintedPathSinkType { + override string getDescription() { result = "TaintedPathSink" } + + override int getEncoding() { result = 4 } +} diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/FilteringReasons.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/FilteringReasons.qll new file mode 100644 index 00000000000..c046fbd12ef --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/FilteringReasons.qll @@ -0,0 +1,196 @@ +/** + * For internal use only. + * + * Defines a set of reasons why a particular endpoint was filtered out. This set of reasons + * contains both reasons why an endpoint could be `NotASink` and reasons why an endpoint could be + * `LikelyNotASink`. The `NotASinkReason`s defined here are exhaustive, but the + * `LikelyNotASinkReason`s are not exhaustive. + */ +newtype TFilteringReason = + TIsArgumentToBuiltinFunctionReason() or + TLodashUnderscoreArgumentReason() or + TClientRequestReason() or + TPromiseDefinitionReason() or + TCryptographicKeyReason() or + TCryptographicOperationFlowReason() or + TLoggerMethodReason() or + TTimeoutReason() or + TReceiverStorageReason() or + TStringStartsWithReason() or + TStringEndsWithReason() or + TStringRegExpTestReason() or + TEventRegistrationReason() or + TEventDispatchReason() or + TMembershipCandidateTestReason() or + TFileSystemAccessReason() or + TDatabaseAccessReason() or + TDOMReason() or + TNextFunctionCallReason() or + TArgumentToArrayReason() or + TArgumentToBuiltinGlobalVarRefReason() or + TConstantReceiverReason() or + TBuiltinCallNameReason() + +/** A reason why a particular endpoint was filtered out by the endpoint filters. */ +abstract class FilteringReason extends TFilteringReason { + abstract string getDescription(); + + abstract int getEncoding(); + + string toString() { result = getDescription() } +} + +/** + * A reason why a particular endpoint might be considered to be `NotASink`. + * + * An endpoint is `NotASink` if it has at least one `NotASinkReason`, it does not have any + * `LikelyNotASinkReason`s, and it is not a known sink. + */ +abstract class NotASinkReason extends FilteringReason { } + +/** + * A reason why a particular endpoint might be considered to be `LikelyNotASink`. + * + * An endpoint is `LikelyNotASink` if it has at least one `LikelyNotASinkReason` and it is not a + * known sink. + */ +abstract class LikelyNotASinkReason extends FilteringReason { } + +class IsArgumentToBuiltinFunctionReason extends NotASinkReason, TIsArgumentToBuiltinFunctionReason { + override string getDescription() { result = "IsArgumentToBuiltinFunction" } + + override int getEncoding() { result = 5 } +} + +class LodashUnderscoreArgumentReason extends NotASinkReason, TLodashUnderscoreArgumentReason { + override string getDescription() { result = "LodashUnderscoreArgument" } + + override int getEncoding() { result = 6 } +} + +class ClientRequestReason extends NotASinkReason, TClientRequestReason { + override string getDescription() { result = "ClientRequest" } + + override int getEncoding() { result = 7 } +} + +class PromiseDefinitionReason extends NotASinkReason, TPromiseDefinitionReason { + override string getDescription() { result = "PromiseDefinition" } + + override int getEncoding() { result = 8 } +} + +class CryptographicKeyReason extends NotASinkReason, TCryptographicKeyReason { + override string getDescription() { result = "CryptographicKey" } + + override int getEncoding() { result = 9 } +} + +class CryptographicOperationFlowReason extends NotASinkReason, TCryptographicOperationFlowReason { + override string getDescription() { result = "CryptographicOperationFlow" } + + override int getEncoding() { result = 10 } +} + +class LoggerMethodReason extends NotASinkReason, TLoggerMethodReason { + override string getDescription() { result = "LoggerMethod" } + + override int getEncoding() { result = 11 } +} + +class TimeoutReason extends NotASinkReason, TTimeoutReason { + override string getDescription() { result = "Timeout" } + + override int getEncoding() { result = 12 } +} + +class ReceiverStorageReason extends NotASinkReason, TReceiverStorageReason { + override string getDescription() { result = "ReceiverStorage" } + + override int getEncoding() { result = 13 } +} + +class StringStartsWithReason extends NotASinkReason, TStringStartsWithReason { + override string getDescription() { result = "StringStartsWith" } + + override int getEncoding() { result = 14 } +} + +class StringEndsWithReason extends NotASinkReason, TStringEndsWithReason { + override string getDescription() { result = "StringEndsWith" } + + override int getEncoding() { result = 15 } +} + +class StringRegExpTestReason extends NotASinkReason, TStringRegExpTestReason { + override string getDescription() { result = "StringRegExpTest" } + + override int getEncoding() { result = 16 } +} + +class EventRegistrationReason extends NotASinkReason, TEventRegistrationReason { + override string getDescription() { result = "EventRegistration" } + + override int getEncoding() { result = 17 } +} + +class EventDispatchReason extends NotASinkReason, TEventDispatchReason { + override string getDescription() { result = "EventDispatch" } + + override int getEncoding() { result = 18 } +} + +class MembershipCandidateTestReason extends NotASinkReason, TMembershipCandidateTestReason { + override string getDescription() { result = "MembershipCandidateTest" } + + override int getEncoding() { result = 19 } +} + +class FileSystemAccessReason extends NotASinkReason, TFileSystemAccessReason { + override string getDescription() { result = "FileSystemAccess" } + + override int getEncoding() { result = 20 } +} + +class DatabaseAccessReason extends NotASinkReason, TDatabaseAccessReason { + override string getDescription() { result = "DatabaseAccess" } + + override int getEncoding() { result = 21 } +} + +class DOMReason extends NotASinkReason, TDOMReason { + override string getDescription() { result = "DOM" } + + override int getEncoding() { result = 22 } +} + +class NextFunctionCallReason extends NotASinkReason, TNextFunctionCallReason { + override string getDescription() { result = "NextFunctionCall" } + + override int getEncoding() { result = 23 } +} + +class ArgumentToArrayReason extends LikelyNotASinkReason, TArgumentToArrayReason { + override string getDescription() { result = "ArgumentToArray" } + + override int getEncoding() { result = 24 } +} + +class ArgumentToBuiltinGlobalVarRefReason extends LikelyNotASinkReason, + TArgumentToBuiltinGlobalVarRefReason { + override string getDescription() { result = "ArgumentToBuiltinGlobalVarRef" } + + override int getEncoding() { result = 25 } +} + +class ConstantReceiverReason extends NotASinkReason, TConstantReceiverReason { + override string getDescription() { result = "ConstantReceiver" } + + override int getEncoding() { result = 26 } +} + +class BuiltinCallNameReason extends NotASinkReason, TBuiltinCallNameReason { + override string getDescription() { result = "BuiltinCallName" } + + override int getEncoding() { result = 27 } +} diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/NosqlInjectionATM.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/NosqlInjectionATM.qll new file mode 100644 index 00000000000..7fc5c44f2f6 --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/NosqlInjectionATM.qll @@ -0,0 +1,178 @@ +/** + * For internal use only. + * + * Defines shared code used by the NoSQL injection boosted query. + */ + +import javascript +private import semmle.javascript.heuristics.SyntacticHeuristics +private import semmle.javascript.security.dataflow.NosqlInjectionCustomizations +private import semmle.javascript.security.TaintedObject +import AdaptiveThreatModeling +private import CoreKnowledge as CoreKnowledge +private import StandardEndpointFilters as StandardEndpointFilters + +module SinkEndpointFilter { + /** + * Provides a set of reasons why a given data flow node should be excluded as a sink candidate. + * + * If this predicate has no results for a sink candidate `n`, then we should treat `n` as an + * effective sink. + */ + string getAReasonSinkExcluded(DataFlow::Node sinkCandidate) { + ( + result = StandardEndpointFilters::getAReasonSinkExcluded(sinkCandidate) + or + // Require NoSQL injection sink candidates to be direct arguments to external library calls. + // + // The standard endpoint filters allow sink candidates which are within object literals or + // array literals, for example `req.sendFile(_, { path: ENDPOINT })`. + // + // However, the NoSQL injection query deals differently with these types of sinks compared to + // other security queries. Other security queries such as SQL injection tend to treat + // `ENDPOINT` as the ground truth sink, but the NoSQL injection query instead treats + // `{ path: ENDPOINT }` as the ground truth sink and defines an additional flow step to ensure + // data flows from `ENDPOINT` to the ground truth sink `{ path: ENDPOINT }`. + // + // Therefore for the NoSQL injection boosted query, we must explicitly ignore sink candidates + // within object literals or array literals, to avoid having multiple alerts for the same + // security vulnerability (one FP where the sink is `ENDPOINT` and one TP where the sink is + // `{ path: ENDPOINT }`). + // + // We use the same reason as in the standard endpoint filters to avoid duplicate reasons for + // endpoints that are neither direct nor indirect arguments to a likely external library call. + not sinkCandidate = StandardEndpointFilters::getALikelyExternalLibraryCall().getAnArgument() and + result = "not an argument to a likely external library call" + or + exists(DataFlow::CallNode call | sinkCandidate = call.getAnArgument() | + // additional databases accesses that aren't modeled yet + call.(DataFlow::MethodCallNode).getMethodName() = + ["create", "createCollection", "createIndexes"] and + result = "matches database access call heuristic" + or + // Remove modeled sinks + CoreKnowledge::isArgumentToKnownLibrarySinkFunction(sinkCandidate) and + result = "modeled sink" + or + // Remove common kinds of unlikely sinks + CoreKnowledge::isKnownStepSrc(sinkCandidate) and + result = "predecessor in a modeled flow step" + or + // Remove modeled database calls. Arguments to modeled calls are very likely to be modeled + // as sinks if they are true positives. Therefore arguments that are not modeled as sinks + // are unlikely to be true positives. + call instanceof DatabaseAccess and + result = "modeled database access" + or + // Remove calls to APIs that aren't relevant to NoSQL injection + call.getReceiver().asExpr() instanceof HTTP::RequestExpr and + result = "receiver is a HTTP request expression" + or + call.getReceiver().asExpr() instanceof HTTP::ResponseExpr and + result = "receiver is a HTTP response expression" + ) + ) and + not ( + // Explicitly allow the following heuristic sinks. + // + // These are copied from the `HeuristicNosqlInjectionSink` class defined within + // `codeql/javascript/ql/src/semmle/javascript/heuristics/AdditionalSinks.qll`. + // We can't reuse the class because importing that file would cause us to treat these + // heuristic sinks as known sinks. + isAssignedToOrConcatenatedWith(sinkCandidate, "(?i)(nosql|query)") or + isArgTo(sinkCandidate, "(?i)(query)") + ) + } +} + +class NosqlInjectionATMConfig extends ATMConfig { + NosqlInjectionATMConfig() { this = "NosqlInjectionATMConfig" } + + override predicate isKnownSource(DataFlow::Node source) { + source instanceof NosqlInjection::Source or TaintedObject::isSource(source, _) + } + + override predicate isKnownSink(DataFlow::Node sink) { sink instanceof NosqlInjection::Sink } + + override predicate isEffectiveSink(DataFlow::Node sinkCandidate) { + not exists(SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)) + } + + override EndpointType getASinkEndpointType() { result instanceof NosqlInjectionSinkType } +} + +/** Holds if src -> trg is an additional flow step in the non-boosted NoSQL injection security query. */ +predicate isBaseAdditionalFlowStep( + DataFlow::Node src, DataFlow::Node trg, DataFlow::FlowLabel inlbl, DataFlow::FlowLabel outlbl +) { + TaintedObject::step(src, trg, inlbl, outlbl) + or + // additional flow step to track taint through NoSQL query objects + inlbl = TaintedObject::label() and + outlbl = TaintedObject::label() and + exists(NoSQL::Query query, DataFlow::SourceNode queryObj | + queryObj.flowsToExpr(query) and + queryObj.flowsTo(trg) and + src = queryObj.getAPropertyWrite().getRhs() + ) +} + +/** + * This predicate allows us to propagate data flow through property writes and array constructors + * within a query object, enabling the security query to pick up NoSQL injection vulnerabilities + * involving more complex queries. + */ +DataFlow::Node getASubexpressionWithinQuery(DataFlow::Node query) { + exists(DataFlow::SourceNode receiver | + receiver.flowsTo(getASubexpressionWithinQuery*(query.getALocalSource())) and + result = + [ + receiver.(DataFlow::SourceNode).getAPropertyWrite().getRhs(), + receiver.(DataFlow::ArrayCreationNode).getAnElement() + ] + ) +} + +/** + * A taint-tracking configuration for reasoning about NoSQL injection vulnerabilities. + * + * This is largely a copy of the taint tracking configuration for the standard NoSQL injection + * query, except additional ATM sinks have been added and the additional flow step has been + * generalised to cover the sinks predicted by ATM. + */ +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "NosqlInjectionATM" } + + override predicate isSource(DataFlow::Node source) { source instanceof NosqlInjection::Source } + + override predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) { + TaintedObject::isSource(source, label) + } + + override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) { + sink.(NosqlInjection::Sink).getAFlowLabel() = label + or + // Allow effective sinks to have any taint label + any(NosqlInjectionATMConfig cfg).isEffectiveSink(sink) + } + + override predicate isSanitizer(DataFlow::Node node) { + super.isSanitizer(node) or + node instanceof NosqlInjection::Sanitizer + } + + override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) { + guard instanceof TaintedObject::SanitizerGuard + } + + override predicate isAdditionalFlowStep( + DataFlow::Node src, DataFlow::Node trg, DataFlow::FlowLabel inlbl, DataFlow::FlowLabel outlbl + ) { + // additional flow steps from the base (non-boosted) security query + isBaseAdditionalFlowStep(src, trg, inlbl, outlbl) + or + // relaxed version of previous step to track taint through unmodeled NoSQL query objects + any(NosqlInjectionATMConfig cfg).isEffectiveSink(trg) and + src = getASubexpressionWithinQuery(trg) + } +} diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/SqlInjectionATM.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/SqlInjectionATM.qll new file mode 100644 index 00000000000..0893c689b95 --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/SqlInjectionATM.qll @@ -0,0 +1,94 @@ +/** + * For internal use only. + * + * Defines shared code used by the SQL injection boosted query. + */ + +import semmle.javascript.heuristics.SyntacticHeuristics +import semmle.javascript.security.dataflow.SqlInjectionCustomizations +import AdaptiveThreatModeling +import CoreKnowledge as CoreKnowledge +import StandardEndpointFilters as StandardEndpointFilters + +/** + * This module provides logic to filter candidate sinks to those which are likely SQL injection + * sinks. + */ +module SinkEndpointFilter { + private import javascript + private import SQL + + /** + * Provides a set of reasons why a given data flow node should be excluded as a sink candidate. + * + * If this predicate has no results for a sink candidate `n`, then we should treat `n` as an + * effective sink. + */ + string getAReasonSinkExcluded(DataFlow::Node sinkCandidate) { + ( + result = StandardEndpointFilters::getAReasonSinkExcluded(sinkCandidate) + or + exists(DataFlow::CallNode call | sinkCandidate = call.getAnArgument() | + // prepared statements for SQL + any(DataFlow::CallNode cn | cn.getCalleeName() = "prepare") + .getAMethodCall("run") + .getAnArgument() = sinkCandidate and + result = "prepared SQL statement" + or + sinkCandidate instanceof DataFlow::ArrayCreationNode and + result = "array creation" + or + // UI is unrelated to SQL + call.getCalleeName().regexpMatch("(?i).*(render|html).*") and + result = "HTML / rendering" + ) + ) and + not ( + // Explicitly allow the following heuristic sinks. + // + // These are copied from the `HeuristicSqlInjectionSink` class defined within + // `codeql/javascript/ql/src/semmle/javascript/heuristics/AdditionalSinks.qll`. + // We can't reuse the class because importing that file would cause us to treat these + // heuristic sinks as known sinks. + isAssignedToOrConcatenatedWith(sinkCandidate, "(?i)(sql|query)") or + isArgTo(sinkCandidate, "(?i)(query)") or + isConcatenatedWithString(sinkCandidate, + "(?s).*(ALTER|COUNT|CREATE|DATABASE|DELETE|DISTINCT|DROP|FROM|GROUP|INSERT|INTO|LIMIT|ORDER|SELECT|TABLE|UPDATE|WHERE).*") + ) + } +} + +class SqlInjectionATMConfig extends ATMConfig { + SqlInjectionATMConfig() { this = "SqlInjectionATMConfig" } + + override predicate isKnownSource(DataFlow::Node source) { source instanceof SqlInjection::Source } + + override predicate isKnownSink(DataFlow::Node sink) { sink instanceof SqlInjection::Sink } + + override predicate isEffectiveSink(DataFlow::Node sinkCandidate) { + not exists(SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)) + } + + override EndpointType getASinkEndpointType() { result instanceof SqlInjectionSinkType } +} + +/** + * A taint-tracking configuration for reasoning about SQL injection vulnerabilities. + * + * This is largely a copy of the taint tracking configuration for the standard SQL injection + * query, except additional sinks have been added using the sink endpoint filter. + */ +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "SqlInjectionATM" } + + override predicate isSource(DataFlow::Node source) { source instanceof SqlInjection::Source } + + override predicate isSink(DataFlow::Node sink) { + sink instanceof SqlInjection::Sink or any(SqlInjectionATMConfig cfg).isEffectiveSink(sink) + } + + override predicate isSanitizer(DataFlow::Node node) { + super.isSanitizer(node) or + node instanceof SqlInjection::Sanitizer + } +} diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/StandardEndpointFilters.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/StandardEndpointFilters.qll new file mode 100644 index 00000000000..333dc61ff96 --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/StandardEndpointFilters.qll @@ -0,0 +1,137 @@ +/** + * For internal use only. + * + * Provides classes and predicates that are useful for endpoint filters. + * + * The standard use of this library is to make use of `isPotentialEffectiveSink/1` + */ + +private import javascript +private import semmle.javascript.filters.ClassifyFiles as ClassifyFiles +private import semmle.javascript.heuristics.SyntacticHeuristics +private import CoreKnowledge as CoreKnowledge + +/** Provides a set of reasons why a given data flow node should be excluded as a sink candidate. */ +string getAReasonSinkExcluded(DataFlow::Node n) { + not flowsToArgumentOfLikelyExternalLibraryCall(n) and + result = "not an argument to a likely external library call" + or + isArgumentToModeledFunction(n) and result = "argument to modeled function" + or + isArgumentToSinklessLibrary(n) and result = "argument to sinkless library" + or + isSanitizer(n) and result = "sanitizer" + or + isPredicate(n) and result = "predicate" + or + isHash(n) and result = "hash" + or + isNumeric(n) and result = "numeric" + or + // Ignore candidate sinks within externs, generated, library, and test code + exists(string category | category = ["externs", "generated", "library", "test"] | + ClassifyFiles::classify(n.getFile(), category) and + result = "in " + category + " file" + ) +} + +/** + * Holds if the node `n` is an argument to a function that has a manual model. + */ +predicate isArgumentToModeledFunction(DataFlow::Node n) { + exists(DataFlow::InvokeNode invk, DataFlow::Node known | + invk.getAnArgument() = n and invk.getAnArgument() = known and isSomeModeledArgument(known) + ) +} + +/** + * Holds if the node `n` is an argument that has a manual model. + */ +predicate isSomeModeledArgument(DataFlow::Node n) { + CoreKnowledge::isKnownLibrarySink(n) or + CoreKnowledge::isKnownStepSrc(n) or + CoreKnowledge::isOtherModeledArgument(n, _) +} + +/** + * Holds if `n` appears to be a numeric value. + */ +predicate isNumeric(DataFlow::Node n) { isReadFrom(n, ".*index.*") } + +/** + * Holds if `n` is an argument to a library without sinks. + */ +predicate isArgumentToSinklessLibrary(DataFlow::Node n) { + exists(DataFlow::InvokeNode invk, DataFlow::SourceNode commonSafeLibrary, string libraryName | + libraryName = ["slugify", "striptags", "marked"] + | + commonSafeLibrary = DataFlow::moduleImport(libraryName) and + invk = [commonSafeLibrary, commonSafeLibrary.getAPropertyRead()].getAnInvocation() and + n = invk.getAnArgument() + ) +} + +predicate isSanitizer(DataFlow::Node n) { + exists(DataFlow::CallNode call | n = call.getAnArgument() | + call.getCalleeName().regexpMatch("(?i).*(escape|valid(ate)?|sanitize|purify).*") + ) +} + +predicate isPredicate(DataFlow::Node n) { + exists(DataFlow::CallNode call | n = call.getAnArgument() | + call.getCalleeName().regexpMatch("(equals|(|is|has|can)(_|[A-Z])).*") + ) +} + +predicate isHash(DataFlow::Node n) { + exists(DataFlow::CallNode call | n = call.getAnArgument() | + call.getCalleeName().regexpMatch("(?i)^(sha\\d*|md5|hash)$") + ) +} + +/** + * Holds if the data flow node is a (possibly indirect) argument of a likely external library call. + * + * This includes direct arguments of likely external library calls as well as nested object + * literals within those calls. + */ +predicate flowsToArgumentOfLikelyExternalLibraryCall(DataFlow::Node n) { + n = getACallWithoutCallee().getAnArgument() + or + exists(DataFlow::SourceNode src | flowsToArgumentOfLikelyExternalLibraryCall(src) | + n = src.getAPropertyWrite().getRhs() + ) + or + exists(DataFlow::ArrayCreationNode arr | flowsToArgumentOfLikelyExternalLibraryCall(arr) | + n = arr.getAnElement() + ) +} + +/** + * Get calls which are likely to be to external non-built-in libraries. + */ +DataFlow::CallNode getALikelyExternalLibraryCall() { result = getACallWithoutCallee() } + +/** + * Gets a node that flows to callback-parameter `p`. + */ +private DataFlow::SourceNode getACallback(DataFlow::ParameterNode p, DataFlow::TypeBackTracker t) { + t.start() and + result = p and + any(DataFlow::FunctionNode f).getLastParameter() = p and + exists(p.getACall()) + or + exists(DataFlow::TypeBackTracker t2 | result = getACallback(p, t2).backtrack(t2, t)) +} + +/** + * Get calls for which we do not have the callee (i.e. the definition of the called function). This + * acts as a heuristic for identifying calls to external library functions. + */ +private DataFlow::CallNode getACallWithoutCallee() { + forall(Function callee | callee = result.getACallee() | callee.getTopLevel().isExterns()) and + not exists(DataFlow::ParameterNode param, DataFlow::FunctionNode callback | + param.flowsTo(result.getCalleeNode()) and + callback = getACallback(param, DataFlow::TypeBackTracker::end()) + ) +} diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/TaintedPathATM.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/TaintedPathATM.qll new file mode 100644 index 00000000000..5bbacae50b7 --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/TaintedPathATM.qll @@ -0,0 +1,123 @@ +/** + * For internal use only. + * + * Defines shared code used by the path injection boosted query. + */ + +import semmle.javascript.heuristics.SyntacticHeuristics +import semmle.javascript.security.dataflow.TaintedPathCustomizations +import AdaptiveThreatModeling +import CoreKnowledge as CoreKnowledge +import StandardEndpointFilters as StandardEndpointFilters + +/** + * This module provides logic to filter candidate sinks to those which are likely path injection + * sinks. + */ +module SinkEndpointFilter { + private import javascript + private import TaintedPath + + /** + * Provides a set of reasons why a given data flow node should be excluded as a sink candidate. + * + * If this predicate has no results for a sink candidate `n`, then we should treat `n` as an + * effective sink. + */ + string getAReasonSinkExcluded(DataFlow::Node sinkCandidate) { + result = StandardEndpointFilters::getAReasonSinkExcluded(sinkCandidate) and + not ( + // Explicitly allow the following heuristic sinks. + // + // These are mostly copied from the `HeuristicTaintedPathSink` class defined within + // `codeql/javascript/ql/src/semmle/javascript/heuristics/AdditionalSinks.qll`. + // We can't reuse the class because importing that file would cause us to treat these + // heuristic sinks as known sinks. + isAssignedToOrConcatenatedWith(sinkCandidate, "(?i)(file|folder|dir|absolute)") + or + isArgTo(sinkCandidate, "(?i)(get|read)file") + or + exists(string pathPattern | + // paths with at least two parts, and either a trailing or leading slash + pathPattern = "(?i)([a-z0-9_.-]+/){2,}" or + pathPattern = "(?i)(/[a-z0-9_.-]+){2,}" + | + isConcatenatedWithString(sinkCandidate, pathPattern) + ) + or + isConcatenatedWithStrings(".*/", sinkCandidate, "/.*") + or + // In addition to the names from `HeuristicTaintedPathSink` in the + // `isAssignedToOrConcatenatedWith` predicate call above, we also allow the noisier "path" + // name. + isAssignedToOrConcatenatedWith(sinkCandidate, "(?i)path") + ) + } +} + +class TaintedPathATMConfig extends ATMConfig { + TaintedPathATMConfig() { this = "TaintedPathATMConfig" } + + override predicate isKnownSource(DataFlow::Node source) { source instanceof TaintedPath::Source } + + override predicate isKnownSink(DataFlow::Node sink) { sink instanceof TaintedPath::Sink } + + override predicate isEffectiveSink(DataFlow::Node sinkCandidate) { + not exists(SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)) + } + + override EndpointType getASinkEndpointType() { result instanceof TaintedPathSinkType } +} + +/** + * A taint-tracking configuration for reasoning about path injection vulnerabilities. + * + * This is largely a copy of the taint tracking configuration for the standard path injection + * query, except additional ATM sinks have been added to the `isSink` predicate. + */ +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "TaintedPathATM" } + + override predicate isSource(DataFlow::Node source) { source instanceof TaintedPath::Source } + + override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) { + label = sink.(TaintedPath::Sink).getAFlowLabel() + or + // Allow effective sinks to have any taint label + any(TaintedPathATMConfig cfg).isEffectiveSink(sink) + } + + override predicate isSanitizer(DataFlow::Node node) { node instanceof TaintedPath::Sanitizer } + + override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode node) { + node instanceof BarrierGuardNodeAsSanitizerGuardNode + } + + override predicate isAdditionalFlowStep( + DataFlow::Node src, DataFlow::Node dst, DataFlow::FlowLabel srclabel, + DataFlow::FlowLabel dstlabel + ) { + TaintedPath::isAdditionalTaintedPathFlowStep(src, dst, srclabel, dstlabel) + } +} + +/** + * This class provides sanitizer guards for path injection. + * + * The standard library path injection query uses a data flow configuration, and therefore defines + * barrier nodes. However we're using a taint tracking configuration for path injection to find new + * kinds of less certain results. Since taint tracking configurations use sanitizer guards instead + * of barrier guards, we port the barrier guards for the boosted query from the standard library to + * sanitizer guards here. + */ +class BarrierGuardNodeAsSanitizerGuardNode extends TaintTracking::LabeledSanitizerGuardNode { + BarrierGuardNodeAsSanitizerGuardNode() { this instanceof TaintedPath::BarrierGuardNode } + + override predicate sanitizes(boolean outcome, Expr e) { + blocks(outcome, e) or blocks(outcome, e, _) + } + + override predicate sanitizes(boolean outcome, Expr e, DataFlow::FlowLabel label) { + sanitizes(outcome, e) + } +} diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/XssATM.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/XssATM.qll new file mode 100644 index 00000000000..ba16b60aa31 --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/XssATM.qll @@ -0,0 +1,103 @@ +/** + * For internal use only. + * + * Defines shared code used by the XSS boosted query. + */ + +private import semmle.javascript.heuristics.SyntacticHeuristics +private import semmle.javascript.security.dataflow.DomBasedXssCustomizations +import AdaptiveThreatModeling +import CoreKnowledge as CoreKnowledge +import StandardEndpointFilters as StandardEndpointFilters + +/** + * This module provides logic to filter candidate sinks to those which are likely XSS sinks. + */ +module SinkEndpointFilter { + private import javascript + private import DomBasedXss + + /** + * Provides a set of reasons why a given data flow node should be excluded as a sink candidate. + * + * If this predicate has no results for a sink candidate `n`, then we should treat `n` as an + * effective sink. + */ + string getAReasonSinkExcluded(DataFlow::Node sinkCandidate) { + ( + result = StandardEndpointFilters::getAReasonSinkExcluded(sinkCandidate) + or + exists(DataFlow::CallNode call | sinkCandidate = call.getAnArgument() | + call.getCalleeName() = "setState" + ) and + result = "setState calls ought to be safe in react applications" + ) and + not ( + // Explicitly allow the following heuristic sinks. + // + // These are copied from the `HeuristicDomBasedXssSink` class defined within + // `codeql/javascript/ql/src/semmle/javascript/heuristics/AdditionalSinks.qll`. + // We can't reuse the class because importing that file would cause us to treat these + // heuristic sinks as known sinks. + isAssignedToOrConcatenatedWith(sinkCandidate, "(?i)(html|innerhtml)") + or + isArgTo(sinkCandidate, "(?i)(html|render)") + or + sinkCandidate instanceof StringOps::HtmlConcatenationLeaf + or + isConcatenatedWithStrings("(?is).*<[a-z ]+.*", sinkCandidate, "(?s).*>.*") + or + // In addition to the heuristic sinks from `HeuristicDomBasedXssSink`, explicitly allow + // property writes like `elem.innerHTML = ` that may not be picked up as HTML + // concatenation leaves. + exists(DataFlow::PropWrite pw | + pw.getPropertyName().regexpMatch("(?i).*html*") and + pw.getRhs() = sinkCandidate + ) + ) + } +} + +class DomBasedXssATMConfig extends ATMConfig { + DomBasedXssATMConfig() { this = "DomBasedXssATMConfig" } + + override predicate isKnownSource(DataFlow::Node source) { source instanceof DomBasedXss::Source } + + override predicate isKnownSink(DataFlow::Node sink) { sink instanceof DomBasedXss::Sink } + + override predicate isEffectiveSink(DataFlow::Node sinkCandidate) { + not exists(SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)) + } + + override EndpointType getASinkEndpointType() { result instanceof XssSinkType } +} + +/** + * A taint-tracking configuration for reasoning about XSS vulnerabilities. + * + * This is largely a copy of the taint tracking configuration for the standard XSSThroughDom query, + * except additional ATM sinks have been added to the `isSink` predicate. + */ +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "DomBasedXssATMConfiguration" } + + override predicate isSource(DataFlow::Node source) { source instanceof DomBasedXss::Source } + + override predicate isSink(DataFlow::Node sink) { + sink instanceof DomBasedXss::Sink or + any(DomBasedXssATMConfig cfg).isEffectiveSink(sink) + } + + override predicate isSanitizer(DataFlow::Node node) { + super.isSanitizer(node) or + node instanceof DomBasedXss::Sanitizer + } + + override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) { + guard instanceof DomBasedXss::SanitizerGuard + } + + override predicate isSanitizerEdge(DataFlow::Node pred, DataFlow::Node succ) { + DomBasedXss::isOptionallySanitizedEdge(pred, succ) + } +} diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml b/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml new file mode 100644 index 00000000000..98f6feeff90 --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml @@ -0,0 +1,6 @@ +name: codeql/javascript-experimental-atm-lib +version: 0.0.0 +extractor: javascript +library: true +dependencies: + codeql/javascript-all: "*" diff --git a/javascript/ql/experimental/adaptivethreatmodeling/src/NosqlInjectionATM.ql b/javascript/ql/experimental/adaptivethreatmodeling/src/NosqlInjectionATM.ql new file mode 100644 index 00000000000..977de7353a9 --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/src/NosqlInjectionATM.ql @@ -0,0 +1,30 @@ +/** + * For internal use only. + * + * @name NoSQL database query built from user-controlled sources (boosted) + * @description Building a database query from user-controlled sources is vulnerable to insertion of + * malicious code by the user. + * @kind path-problem + * @scored + * @problem.severity error + * @security-severity 8.8 + * @id adaptive-threat-modeling/js/nosql-injection + * @tags experimental experimental/atm security + */ + +import ATM::ResultsInfo +import DataFlow::PathGraph +import experimental.adaptivethreatmodeling.NosqlInjectionATM + +from + DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, float score, + string scoreString +where + cfg.hasFlowPath(source, sink) and + not isFlowLikelyInBaseQuery(source.getNode(), sink.getNode()) and + score = getScoreForFlow(source.getNode(), sink.getNode()) and + scoreString = getScoreStringForFlow(source.getNode(), sink.getNode()) +select sink.getNode(), source, sink, + "[Score = " + scoreString + "] This may be a NoSQL query depending on $@ " + + getAdditionalAlertInfo(source.getNode(), sink.getNode()), source.getNode(), + "a user-provided value", score diff --git a/javascript/ql/experimental/adaptivethreatmodeling/src/SqlInjectionATM.ql b/javascript/ql/experimental/adaptivethreatmodeling/src/SqlInjectionATM.ql new file mode 100644 index 00000000000..7878ad97a40 --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/src/SqlInjectionATM.ql @@ -0,0 +1,30 @@ +/** + * For internal use only. + * + * @name SQL database query built from user-controlled sources (boosted) + * @description Building a database query from user-controlled sources is vulnerable to insertion of + * malicious code by the user. + * @kind path-problem + * @scored + * @problem.severity error + * @security-severity 8.8 + * @id adaptive-threat-modeling/js/sql-injection + * @tags experimental experimental/atm security + */ + +import experimental.adaptivethreatmodeling.SqlInjectionATM +import ATM::ResultsInfo +import DataFlow::PathGraph + +from + DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, float score, + string scoreString +where + cfg.hasFlowPath(source, sink) and + not isFlowLikelyInBaseQuery(source.getNode(), sink.getNode()) and + score = getScoreForFlow(source.getNode(), sink.getNode()) and + scoreString = getScoreStringForFlow(source.getNode(), sink.getNode()) +select sink.getNode(), source, sink, + "[Score = " + scoreString + "] This may be a js/sql result depending on $@ " + + getAdditionalAlertInfo(source.getNode(), sink.getNode()), source.getNode(), + "a user-provided value", score diff --git a/javascript/ql/experimental/adaptivethreatmodeling/src/TaintedPathATM.ql b/javascript/ql/experimental/adaptivethreatmodeling/src/TaintedPathATM.ql new file mode 100644 index 00000000000..352c290a82d --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/src/TaintedPathATM.ql @@ -0,0 +1,30 @@ +/** + * For internal use only. + * + * @name Uncontrolled data used in path expression (boosted) + * @description Accessing paths influenced by users can allow an attacker to access + * unexpected resources. + * @kind path-problem + * @scored + * @problem.severity error + * @security-severity 7.5 + * @id adaptive-threat-modeling/js/path-injection + * @tags experimental experimental/atm security + */ + +import ATM::ResultsInfo +import DataFlow::PathGraph +import experimental.adaptivethreatmodeling.TaintedPathATM + +from + DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, float score, + string scoreString +where + cfg.hasFlowPath(source, sink) and + not isFlowLikelyInBaseQuery(source.getNode(), sink.getNode()) and + score = getScoreForFlow(source.getNode(), sink.getNode()) and + scoreString = getScoreStringForFlow(source.getNode(), sink.getNode()) +select sink.getNode(), source, sink, + "[Score = " + scoreString + "] This may be a js/path-injection result depending on $@ " + + getAdditionalAlertInfo(source.getNode(), sink.getNode()), source.getNode(), + "a user-provided value", score diff --git a/javascript/ql/experimental/adaptivethreatmodeling/src/XssATM.ql b/javascript/ql/experimental/adaptivethreatmodeling/src/XssATM.ql new file mode 100644 index 00000000000..1180846f71c --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/src/XssATM.ql @@ -0,0 +1,31 @@ +/** + * For internal use only. + * + * @name Client-side cross-site scripting (boosted) + * @description Writing user input directly to the DOM allows for + * a cross-site scripting vulnerability. + * @kind path-problem + * @scored + * @problem.severity error + * @security-severity 6.1 + * @id adaptive-threat-modeling/js/xss + * @tags experimental experimental/atm security + */ + +import javascript +import ATM::ResultsInfo +import DataFlow::PathGraph +import experimental.adaptivethreatmodeling.XssATM + +from + DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, float score, + string scoreString +where + cfg.hasFlowPath(source, sink) and + not isFlowLikelyInBaseQuery(source.getNode(), sink.getNode()) and + score = getScoreForFlow(source.getNode(), sink.getNode()) and + scoreString = getScoreStringForFlow(source.getNode(), sink.getNode()) +select sink.getNode(), source, sink, + "[Score = " + scoreString + "] This may be a js/xss result depending on $@ " + + getAdditionalAlertInfo(source.getNode(), sink.getNode()), source.getNode(), + "a user-provided value", score diff --git a/javascript/ql/experimental/adaptivethreatmodeling/src/codeql-suites/javascript-atm-code-scanning.qls b/javascript/ql/experimental/adaptivethreatmodeling/src/codeql-suites/javascript-atm-code-scanning.qls new file mode 100644 index 00000000000..2a5997e9d7a --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/src/codeql-suites/javascript-atm-code-scanning.qls @@ -0,0 +1,8 @@ +- description: ATM boosted Code Scanning queries for JavaScript +- queries: . +- include: + id: + - adaptive-threat-modeling/js/nosql-injection + - adaptive-threat-modeling/js/sql-injection + - adaptive-threat-modeling/js/path-injection + - adaptive-threat-modeling/js/xss diff --git a/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.lock.yml b/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.lock.yml new file mode 100644 index 00000000000..06dd07fc7dc --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.lock.yml @@ -0,0 +1,4 @@ +--- +dependencies: {} +compiled: false +lockVersion: 1.0.0 diff --git a/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml b/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml new file mode 100644 index 00000000000..e957f2c859c --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml @@ -0,0 +1,7 @@ +name: codeql/javascript-experimental-atm-queries +language: javascript +version: 0.0.0 +suites: codeql-suites +defaultSuiteFile: codeql-suites/javascript-atm-code-scanning.qls +dependencies: + codeql/javascript-experimental-atm-lib: "*" diff --git a/javascript/ql/experimental/adaptivethreatmodeling/src/queries.xml b/javascript/ql/experimental/adaptivethreatmodeling/src/queries.xml new file mode 100644 index 00000000000..d4346295164 --- /dev/null +++ b/javascript/ql/experimental/adaptivethreatmodeling/src/queries.xml @@ -0,0 +1 @@ + diff --git a/javascript/ql/src/Declarations/UnusedVariable.qll b/javascript/ql/lib/Declarations/UnusedVariable.qll similarity index 100% rename from javascript/ql/src/Declarations/UnusedVariable.qll rename to javascript/ql/lib/Declarations/UnusedVariable.qll diff --git a/javascript/ql/src/Expressions/DOMProperties.qll b/javascript/ql/lib/Expressions/DOMProperties.qll similarity index 100% rename from javascript/ql/src/Expressions/DOMProperties.qll rename to javascript/ql/lib/Expressions/DOMProperties.qll diff --git a/javascript/ql/src/Expressions/ExprHasNoEffect.qll b/javascript/ql/lib/Expressions/ExprHasNoEffect.qll similarity index 100% rename from javascript/ql/src/Expressions/ExprHasNoEffect.qll rename to javascript/ql/lib/Expressions/ExprHasNoEffect.qll diff --git a/javascript/ql/src/LanguageFeatures/UnusedIndexVariable.qll b/javascript/ql/lib/LanguageFeatures/UnusedIndexVariable.qll similarity index 100% rename from javascript/ql/src/LanguageFeatures/UnusedIndexVariable.qll rename to javascript/ql/lib/LanguageFeatures/UnusedIndexVariable.qll diff --git a/javascript/ql/lib/semmle/javascript/DOM.qll b/javascript/ql/lib/semmle/javascript/DOM.qll index 5d721858de8..8ded83854c8 100644 --- a/javascript/ql/lib/semmle/javascript/DOM.qll +++ b/javascript/ql/lib/semmle/javascript/DOM.qll @@ -179,15 +179,7 @@ module DOM { eltName = attr.getElement().getName() and attrName = attr.getName() | - ( - eltName = "script" or - eltName = "iframe" or - eltName = "embed" or - eltName = "video" or - eltName = "audio" or - eltName = "source" or - eltName = "track" - ) and + eltName = ["script", "iframe", "embed", "video", "audio", "source", "track"] and attrName = "src" or ( @@ -258,11 +250,11 @@ module DOM { /** Gets a call that queries the DOM for a collection of DOM nodes. */ private DataFlow::SourceNode domElementCollection() { exists(string collectionName | - collectionName = "getElementsByClassName" or - collectionName = "getElementsByName" or - collectionName = "getElementsByTagName" or - collectionName = "getElementsByTagNameNS" or - collectionName = "querySelectorAll" + collectionName = + [ + "getElementsByClassName", "getElementsByName", "getElementsByTagName", + "getElementsByTagNameNS", "querySelectorAll" + ] | ( result = documentRef().getAMethodCall(collectionName) or @@ -274,11 +266,8 @@ module DOM { /** Gets a call that creates a DOM node or queries the DOM for a DOM node. */ private DataFlow::SourceNode domElementCreationOrQuery() { exists(string methodName | - methodName = "createElement" or - methodName = "createElementNS" or - methodName = "createRange" or - methodName = "getElementById" or - methodName = "querySelector" + methodName = + ["createElement", "createElementNS", "createRange", "getElementById", "querySelector"] | result = documentRef().getAMethodCall(methodName) or result = DataFlow::globalVarRef(methodName).getACall() @@ -465,11 +454,7 @@ module DOM { private class DefaultRange extends Range { DefaultRange() { exists(string propName | this = documentRef().getAPropertyRead(propName) | - propName = "documentURI" or - propName = "documentURIObject" or - propName = "location" or - propName = "referrer" or - propName = "URL" + propName = ["documentURI", "documentURIObject", "location", "referrer", "URL"] ) or this = DOM::domValueRef().getAPropertyRead("baseUri") diff --git a/javascript/ql/lib/semmle/javascript/DynamicPropertyAccess.qll b/javascript/ql/lib/semmle/javascript/DynamicPropertyAccess.qll index 456fd773f64..b7308ca5ad4 100644 --- a/javascript/ql/lib/semmle/javascript/DynamicPropertyAccess.qll +++ b/javascript/ql/lib/semmle/javascript/DynamicPropertyAccess.qll @@ -14,7 +14,7 @@ private import semmle.javascript.dataflow.internal.FlowSteps SourceNode getAnEnumeratedArrayElement(SourceNode array) { exists(MethodCallNode call, string name | call = array.getAMethodCall(name) and - (name = "forEach" or name = "map") and + name = ["forEach", "map"] and result = call.getCallback(0).getParameter(0) ) or diff --git a/javascript/ql/lib/semmle/javascript/Files.qll b/javascript/ql/lib/semmle/javascript/Files.qll index 7bb0bb5b314..1bb20539d95 100644 --- a/javascript/ql/lib/semmle/javascript/Files.qll +++ b/javascript/ql/lib/semmle/javascript/Files.qll @@ -34,7 +34,7 @@ abstract class Container extends @container { /** * Gets a URL representing the location of this container. * - * For more information see [Providing URLs](https://help.semmle.com/QL/learn-ql/ql/locations.html#providing-urls). + * For more information see [Providing URLs](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/#providing-urls). */ abstract string getURL(); @@ -246,13 +246,7 @@ class File extends Container, @file { * A file type. */ class FileType extends string { - FileType() { - this = "javascript" or - this = "html" or - this = "typescript" or - this = "json" or - this = "yaml" - } + FileType() { this = ["javascript", "html", "typescript", "json", "yaml"] } /** * Holds if this is the JavaScript file type. diff --git a/javascript/ql/lib/semmle/javascript/JSDoc.qll b/javascript/ql/lib/semmle/javascript/JSDoc.qll index e7fb681da2a..2621c92e3c6 100644 --- a/javascript/ql/lib/semmle/javascript/JSDoc.qll +++ b/javascript/ql/lib/semmle/javascript/JSDoc.qll @@ -291,13 +291,7 @@ class JSDocNamedTypeExpr extends @jsdoc_named_type_expr, JSDocTypeExpr { override predicate isNumbery() { exists(string name | name = getName() | - name = "number" or - name = "Number" or - name = "double" or - name = "Double" or - name = "int" or - name = "integer" or - name = "Integer" + name = ["number", "Number", "double", "Double", "int", "integer", "Integer"] ) } diff --git a/javascript/ql/lib/semmle/javascript/Locations.qll b/javascript/ql/lib/semmle/javascript/Locations.qll index bdf83fb395a..9bf5c341915 100644 --- a/javascript/ql/lib/semmle/javascript/Locations.qll +++ b/javascript/ql/lib/semmle/javascript/Locations.qll @@ -6,7 +6,7 @@ import javascript * A location as given by a file, a start line, a start column, * an end line, and an end column. * - * For more information about locations see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * For more information about locations see [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ class Location extends @location { /** Gets the file for this location. */ @@ -70,7 +70,7 @@ class Location extends @location { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/javascript/ql/lib/semmle/javascript/PackageExports.qll b/javascript/ql/lib/semmle/javascript/PackageExports.qll index 97009adc065..5d67b958d28 100644 --- a/javascript/ql/lib/semmle/javascript/PackageExports.qll +++ b/javascript/ql/lib/semmle/javascript/PackageExports.qll @@ -30,7 +30,11 @@ private DataFlow::Node getAValueExportedByPackage() { getAnExportFromModule(any(PackageJSON pack | exists(pack.getPackageName())).getMainModule()) or // module.exports.bar.baz = result; - result = getAValueExportedByPackage().(DataFlow::PropWrite).getRhs() + exists(DataFlow::PropWrite write | + write = getAValueExportedByPackage() and + write.getPropertyName() = publicPropertyName() and + result = write.getRhs() + ) or // class Foo { // bar() {} // <- result @@ -39,15 +43,17 @@ private DataFlow::Node getAValueExportedByPackage() { exists(DataFlow::SourceNode callee | callee = getAValueExportedByPackage().(DataFlow::NewNode).getCalleeNode().getALocalSource() | - result = callee.getAPropertyRead("prototype").getAPropertyWrite().getRhs() + result = callee.getAPropertyRead("prototype").getAPropertyWrite(publicPropertyName()).getRhs() or - result = callee.(DataFlow::ClassNode).getAnInstanceMethod() + result = callee.(DataFlow::ClassNode).getInstanceMethod(publicPropertyName()) and + not isPrivateMethodDeclaration(result) ) or result = getAValueExportedByPackage().getALocalSource() or // Nested property reads. - result = getAValueExportedByPackage().(DataFlow::SourceNode).getAPropertyReference() + result = + getAValueExportedByPackage().(DataFlow::SourceNode).getAPropertyReference(publicPropertyName()) or // module.exports.foo = require("./other-module.js"); exists(Module mod | @@ -61,9 +67,12 @@ private DataFlow::Node getAValueExportedByPackage() { // static baz() {} // <- result // constructor() {} // <- result // }; - exists(DataFlow::ClassNode cla | cla = getAValueExportedByPackage() | - result = cla.getAnInstanceMethod() or - result = cla.getAStaticMethod() or + exists(DataFlow::ClassNode cla | + cla = getAValueExportedByPackage() and + not isPrivateMethodDeclaration(result) + | + result = cla.getInstanceMethod(publicPropertyName()) or + result = cla.getStaticMethod(publicPropertyName()) or result = cla.getConstructor() ) or @@ -120,7 +129,8 @@ private DataFlow::Node getAValueExportedByPackage() { or // Object.defineProperty exists(CallToObjectDefineProperty call | - [call, call.getBaseObject()] = getAValueExportedByPackage() + [call, call.getBaseObject()] = getAValueExportedByPackage() and + call.getPropertyName() = publicPropertyName() | result = call.getPropertyDescriptor().getALocalSource().getAPropertyReference("value") or @@ -164,9 +174,31 @@ private DataFlow::Node getAValueExportedByPackage() { * Gets an exported node from the module `mod`. */ private DataFlow::Node getAnExportFromModule(Module mod) { - result = mod.getAnExportedValue(_) + result = mod.getAnExportedValue(publicPropertyName()) or result = mod.getABulkExportedNode() or result.analyze().getAValue() = TAbstractModuleObject(mod) } + +/** + * Gets a property name that we consider to be public. + * + * This only allows properties whose first character is a letter or number. + */ +bindingset[result] +private string publicPropertyName() { result.regexpMatch("[a-zA-Z0-9].*") } + +/** + * Holds if the given function is part of a private (or protected) method declaration. + */ +private predicate isPrivateMethodDeclaration(DataFlow::FunctionNode func) { + exists(MethodDeclaration decl | + decl.getBody() = func.getFunction() and + ( + decl.isPrivate() + or + decl.isProtected() + ) + ) +} diff --git a/javascript/ql/lib/semmle/javascript/RangeAnalysis.qll b/javascript/ql/lib/semmle/javascript/RangeAnalysis.qll index 28f8b59c3fe..d1e9b48a6d0 100644 --- a/javascript/ql/lib/semmle/javascript/RangeAnalysis.qll +++ b/javascript/ql/lib/semmle/javascript/RangeAnalysis.qll @@ -236,10 +236,9 @@ module RangeAnalysis { ) { if exists(r.getImmediatePredecessor()) then linearDefinitionSum(r.getImmediatePredecessor(), xroot, xsign, yroot, ysign, bias) - else - if exists(r.asExpr().getIntValue()) - then none() // do not model constants as sums - else ( + else ( + not exists(r.asExpr().getIntValue()) and // do not model constants as sums + ( exists(AddExpr add, int bias1, int bias2 | r.asExpr() = add | // r = r1 + r2 linearDefinition(add.getLeftOperand().flow(), xroot, xsign, bias1) and @@ -257,6 +256,7 @@ module RangeAnalysis { linearDefinitionSum(r.asExpr().(NegExpr).getOperand().flow(), xroot, -xsign, yroot, -ysign, -bias) ) + ) } /** diff --git a/javascript/ql/lib/semmle/javascript/RestrictedLocations.qll b/javascript/ql/lib/semmle/javascript/RestrictedLocations.qll index 8c45ad533ba..9fd70ca1230 100644 --- a/javascript/ql/lib/semmle/javascript/RestrictedLocations.qll +++ b/javascript/ql/lib/semmle/javascript/RestrictedLocations.qll @@ -14,7 +14,7 @@ class FirstLineOf extends Locatable { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -43,7 +43,7 @@ class LastLineOf extends Locatable { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/javascript/ql/lib/semmle/javascript/SSA.qll b/javascript/ql/lib/semmle/javascript/SSA.qll index d55f29ee1a8..11acc7d284c 100644 --- a/javascript/ql/lib/semmle/javascript/SSA.qll +++ b/javascript/ql/lib/semmle/javascript/SSA.qll @@ -417,7 +417,7 @@ class SsaVariable extends TSsaDefinition { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -482,7 +482,7 @@ class SsaDefinition extends TSsaDefinition { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ abstract predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/javascript/ql/lib/semmle/javascript/StandardLibrary.qll b/javascript/ql/lib/semmle/javascript/StandardLibrary.qll index 9ca2bf1725f..ad77d40a833 100644 --- a/javascript/ql/lib/semmle/javascript/StandardLibrary.qll +++ b/javascript/ql/lib/semmle/javascript/StandardLibrary.qll @@ -55,13 +55,7 @@ private class ArrayIterationCallbackAsPartialInvoke extends DataFlow::PartialInv getNumArgument() = 2 and // Filter out library methods named 'forEach' etc not DataFlow::moduleImport(_).flowsTo(getReceiver()) and - exists(string name | name = getMethodName() | - name = "filter" or - name = "forEach" or - name = "map" or - name = "some" or - name = "every" - ) + getMethodName() = ["filter", "forEach", "map", "some", "every"] } override DataFlow::Node getBoundReceiver(DataFlow::Node callback) { diff --git a/javascript/ql/lib/semmle/javascript/XML.qll b/javascript/ql/lib/semmle/javascript/XML.qll index 5871fed0ddd..76f3b3cb022 100755 --- a/javascript/ql/lib/semmle/javascript/XML.qll +++ b/javascript/ql/lib/semmle/javascript/XML.qll @@ -24,7 +24,7 @@ class XMLLocatable extends @xmllocatable, TXMLLocatable { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -108,7 +108,7 @@ class XMLParent extends @xmlparent { } /** Gets the text value contained in this XML parent. */ - string getTextValue() { result = allCharactersString() } + string getTextValue() { result = this.allCharactersString() } /** Gets a printable representation of this XML parent. */ string toString() { result = this.getName() } @@ -119,7 +119,7 @@ class XMLFile extends XMLParent, File { XMLFile() { xmlEncoding(this, _) } /** Gets a printable representation of this XML file. */ - override string toString() { result = getName() } + override string toString() { result = this.getName() } /** Gets the name of this XML file. */ override string getName() { result = File.super.getAbsolutePath() } @@ -129,14 +129,14 @@ class XMLFile extends XMLParent, File { * * Gets the path of this XML file. */ - deprecated string getPath() { result = getAbsolutePath() } + deprecated string getPath() { result = this.getAbsolutePath() } /** * DEPRECATED: Use `getParentContainer().getAbsolutePath()` instead. * * Gets the path of the folder that contains this XML file. */ - deprecated string getFolder() { result = getParentContainer().getAbsolutePath() } + deprecated string getFolder() { result = this.getParentContainer().getAbsolutePath() } /** Gets the encoding of this XML file. */ string getEncoding() { xmlEncoding(this, result) } @@ -200,7 +200,7 @@ class XMLDTD extends XMLLocatable, @xmldtd { */ class XMLElement extends @xmlelement, XMLParent, XMLLocatable { /** Holds if this XML element has the given `name`. */ - predicate hasName(string name) { name = getName() } + predicate hasName(string name) { name = this.getName() } /** Gets the name of this XML element. */ override string getName() { xmlElements(this, result, _, _, _) } @@ -239,7 +239,7 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable { string getAttributeValue(string name) { result = this.getAttribute(name).getValue() } /** Gets a printable representation of this XML element. */ - override string toString() { result = getName() } + override string toString() { result = this.getName() } } /** diff --git a/javascript/ql/lib/semmle/javascript/dataflow/AbstractValues.qll b/javascript/ql/lib/semmle/javascript/dataflow/AbstractValues.qll index 5e4776cfdbf..b887972503e 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/AbstractValues.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/AbstractValues.qll @@ -109,7 +109,7 @@ class AbstractValue extends TAbstractValue { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `f`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo(string f, int startline, int startcolumn, int endline, int endcolumn) { f = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0 diff --git a/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll b/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll index 2d49693d6aa..f9a8f45b3a3 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll @@ -1776,7 +1776,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll b/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll index b5d1290a516..a396ffc68e9 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll @@ -148,7 +148,7 @@ module DataFlow { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ cached predicate hasLocationInfo( @@ -1405,14 +1405,7 @@ module DataFlow { */ class Incompleteness extends string { Incompleteness() { - this = "await" or - this = "call" or - this = "eval" or - this = "global" or - this = "heap" or - this = "import" or - this = "namespace" or - this = "yield" + this = ["await", "call", "eval", "global", "heap", "import", "namespace", "yield"] } } diff --git a/javascript/ql/lib/semmle/javascript/dataflow/InferredTypes.qll b/javascript/ql/lib/semmle/javascript/dataflow/InferredTypes.qll index 90fed456d76..b7b2b9ba1e5 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/InferredTypes.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/InferredTypes.qll @@ -29,13 +29,7 @@ newtype TypeTag = */ class TypeofTag extends string { TypeofTag() { - this = "undefined" or - this = "boolean" or - this = "number" or - this = "string" or - this = "function" or - this = "object" or - this = "symbol" + this = ["undefined", "boolean", "number", "string", "function", "object", "symbol"] } } diff --git a/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll b/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll index 1a642d980bd..3b58b7e046f 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll @@ -924,13 +924,11 @@ module TaintTracking { pred = invoke.getArgument(0) and succ = invoke | - name = "Error" or - name = "EvalError" or - name = "RangeError" or - name = "ReferenceError" or - name = "SyntaxError" or - name = "TypeError" or - name = "URIError" + name = + [ + "Error", "EvalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError", + "URIError" + ] ) } } diff --git a/javascript/ql/src/filters/ClassifyFiles.qll b/javascript/ql/lib/semmle/javascript/filters/ClassifyFiles.qll similarity index 100% rename from javascript/ql/src/filters/ClassifyFiles.qll rename to javascript/ql/lib/semmle/javascript/filters/ClassifyFiles.qll diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Angular2.qll b/javascript/ql/lib/semmle/javascript/frameworks/Angular2.qll index 4041a7e7413..6a2e0132678 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Angular2.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Angular2.qll @@ -152,7 +152,7 @@ module Angular2 { /** A value that is about to be promoted to a trusted script value. */ private class AngularCodeInjectionSink extends CodeInjection::Sink { AngularCodeInjectionSink() { - this = domSanitizer().getAMethodCall(["bypassSecurityTrustScript"]).getArgument(0) + this = domSanitizer().getAMethodCall("bypassSecurityTrustScript").getArgument(0) } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSCore.qll b/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSCore.qll index e8abb51c99d..88e55b390cb 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSCore.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSCore.qll @@ -177,15 +177,10 @@ class ModuleApiCallDependencyInjection extends DependencyInjection { * This method excludes the method names that are also present on the AngularJS '$provide' object. */ private int injectableArgPos() { - ( - methodName = "directive" or - methodName = "filter" or - methodName = "controller" or - methodName = "animation" - ) and + methodName = ["directive", "filter", "controller", "animation"] and result = 1 or - (methodName = "config" or methodName = "run") and + methodName = ["config", "run"] and result = 0 } @@ -199,64 +194,17 @@ class ModuleApiCallDependencyInjection extends DependencyInjection { * (cf. https://docs.angularjs.org/api/ng/directive/). */ private predicate builtinDirective(string name) { - name = "ngApp" or - name = "ngBind" or - name = "ngBindHtml" or - name = "ngBindTemplate" or - name = "ngBlur" or - name = "ngChange" or - name = "ngChecked" or - name = "ngClass" or - name = "ngClassEven" or - name = "ngClassOdd" or - name = "ngClick" or - name = "ngCloak" or - name = "ngController" or - name = "ngCopy" or - name = "ngCsp" or - name = "ngCut" or - name = "ngDblclick" or - name = "ngDisabled" or - name = "ngFocus" or - name = "ngForm" or - name = "ngHide" or - name = "ngHref" or - name = "ngIf" or - name = "ngInclude" or - name = "ngInit" or - name = "ngJq" or - name = "ngKeydown" or - name = "ngKeypress" or - name = "ngKeyup" or - name = "ngList" or - name = "ngMaxlength" or - name = "ngMinlength" or - name = "ngModel" or - name = "ngModelOptions" or - name = "ngMousedown" or - name = "ngMouseenter" or - name = "ngMouseleave" or - name = "ngMousemove" or - name = "ngMouseover" or - name = "ngMouseup" or - name = "ngNonBindable" or - name = "ngOpen" or - name = "ngOptions" or - name = "ngPaste" or - name = "ngPattern" or - name = "ngPluralize" or - name = "ngReadonly" or - name = "ngRepeat" or - name = "ngRequired" or - name = "ngSelected" or - name = "ngShow" or - name = "ngSrc" or - name = "ngSrcset" or - name = "ngStyle" or - name = "ngSubmit" or - name = "ngSwitch" or - name = "ngTransclude" or - name = "ngValue" + name = + [ + "ngApp", "ngBind", "ngBindHtml", "ngBindTemplate", "ngBlur", "ngChange", "ngChecked", + "ngClass", "ngClassEven", "ngClassOdd", "ngClick", "ngCloak", "ngController", "ngCopy", + "ngCsp", "ngCut", "ngDblclick", "ngDisabled", "ngFocus", "ngForm", "ngHide", "ngHref", "ngIf", + "ngInclude", "ngInit", "ngJq", "ngKeydown", "ngKeypress", "ngKeyup", "ngList", "ngMaxlength", + "ngMinlength", "ngModel", "ngModelOptions", "ngMousedown", "ngMouseenter", "ngMouseleave", + "ngMousemove", "ngMouseover", "ngMouseup", "ngNonBindable", "ngOpen", "ngOptions", "ngPaste", + "ngPattern", "ngPluralize", "ngReadonly", "ngRepeat", "ngRequired", "ngSelected", "ngShow", + "ngSrc", "ngSrcset", "ngStyle", "ngSubmit", "ngSwitch", "ngTransclude", "ngValue" + ] } private newtype TDirectiveInstance = @@ -676,10 +624,7 @@ private class JQLiteObject extends JQuery::ObjectSource::Range { ) ) or - exists(ServiceReference element | - element.getName() = "$rootElement" or - element.getName() = "$document" - | + exists(ServiceReference element | element.getName() = ["$rootElement", "$document"] | this = element.getAReference() ) } @@ -780,23 +725,17 @@ private class BuiltinServiceCall extends AngularJSCall { override predicate interpretsArgumentAsCode(Expr e) { exists(ScopeServiceReference scope, string methodName | - methodName = "$apply" or - methodName = "$applyAsync" or - methodName = "$eval" or - methodName = "$evalAsync" or - methodName = "$watch" or - methodName = "$watchCollection" or - methodName = "$watchGroup" + methodName = + [ + "$apply", "$applyAsync", "$eval", "$evalAsync", "$watch", "$watchCollection", + "$watchGroup" + ] | call = scope.getAMethodCall(methodName) and e = call.getArgument(0) ) or - exists(ServiceReference service | - service.getName() = "$compile" or - service.getName() = "$parse" or - service.getName() = "$interpolate" - | + exists(ServiceReference service | service.getName() = ["$compile", "$parse", "$interpolate"] | call = service.getACall() and e = call.getArgument(0) ) @@ -952,7 +891,7 @@ class ElementScope extends AngularScope, MkElementScope { DataFlow::SourceNode routeProviderRef() { result = builtinServiceRef("$routeProvider") or - exists(string m | m = "when" or m = "otherwise" | result = routeProviderRef().getAMethodCall(m)) + exists(string m | m = ["when", "otherwise"] | result = routeProviderRef().getAMethodCall(m)) } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSExpressions.qll b/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSExpressions.qll index a984ec0c9e9..7eb1f760a46 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSExpressions.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSExpressions.qll @@ -277,24 +277,11 @@ private module Lexer { override string getPattern() { result = concat(string op | - op = "===" or - op = "!==" or - op = "==" or - op = "!=" or - op = "<=" or - op = ">=" or - op = "&&" or - op = "||" or - op = "*" or - op = "!" or - op = "=" or - op = "<" or - op = ">" or - op = "+" or - op = "-" or - op = "/" or - op = "%" or - op = "|" + op = + [ + "===", "!==", "==", "!=", "<=", ">=", "&&", "||", "*", "!", "=", "<", ">", "+", "-", + "/", "%", "|" + ] | "\\Q" + op + "\\E", "|" order by op.length() desc ) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/AsyncPackage.qll b/javascript/ql/lib/semmle/javascript/frameworks/AsyncPackage.qll index 49cacd08aac..a0608d45253 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/AsyncPackage.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/AsyncPackage.qll @@ -103,25 +103,12 @@ module AsyncPackage { IterationCall() { this = memberVariant(name).getACall() and - ( - name = "concat" or - name = "detect" or - name = "each" or - name = "eachOf" or - name = "forEach" or - name = "forEachOf" or - name = "every" or - name = "filter" or - name = "groupBy" or - name = "map" or - name = "mapValues" or - name = "reduce" or - name = "reduceRight" or - name = "reject" or - name = "some" or - name = "sortBy" or - name = "transform" - ) + name = + [ + "concat", "detect", "each", "eachOf", "forEach", "forEachOf", "every", "filter", + "groupBy", "map", "mapValues", "reduce", "reduceRight", "reject", "some", "sortBy", + "transform" + ] } /** @@ -176,10 +163,7 @@ module AsyncPackage { pred = getLastParameter(iteratee).getACall().getArgument(i) and succ = final.getParameter(i) and exists(string name | name = call.getName() | - name = "concat" or - name = "map" or - name = "reduce" or - name = "reduceRight" + name = ["concat", "map", "reduce", "reduceRight"] ) ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/ComposedFunctions.qll b/javascript/ql/lib/semmle/javascript/frameworks/ComposedFunctions.qll index 6887758b064..1fd4e49db5a 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/ComposedFunctions.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/ComposedFunctions.qll @@ -82,7 +82,7 @@ module FunctionCompositionCall { /** A call whose arguments are functions `f,g,h` which are composed into `f(g(h(...))` */ private class RightToLeft extends WithArrayOverloading { RightToLeft() { - this = DataFlow::moduleImport(["compose-function"]).getACall() + this = DataFlow::moduleImport("compose-function").getACall() or this = DataFlow::moduleMember(["redux", "ramda", "@reduxjs/toolkit", "recompose"], "compose") diff --git a/javascript/ql/lib/semmle/javascript/frameworks/CryptoLibraries.qll b/javascript/ql/lib/semmle/javascript/frameworks/CryptoLibraries.qll index f8c942ff6b0..6a255b70b12 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/CryptoLibraries.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/CryptoLibraries.qll @@ -408,14 +408,7 @@ private module Forge { this = mod.getAPropertyRead("cipher").getAMemberCall(createName) and getArgument(0).asExpr().mayHaveStringValue(cipherName) and cipherName = cipherPrefix + "-" + cipherSuffix and - ( - cipherSuffix = "CBC" or - cipherSuffix = "CFB" or - cipherSuffix = "CTR" or - cipherSuffix = "ECB" or - cipherSuffix = "GCM" or - cipherSuffix = "OFB" - ) and + cipherSuffix = ["CBC", "CFB", "CTR", "ECB", "GCM", "OFB"] and algorithmName = cipherPrefix and key = getArgument(1) ) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/EventEmitter.qll b/javascript/ql/lib/semmle/javascript/frameworks/EventEmitter.qll index 67b746e4473..f466d96dd9d 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/EventEmitter.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/EventEmitter.qll @@ -11,13 +11,7 @@ module EventEmitter { } /** Gets the name of a method on `EventEmitter` that registers an event handler. */ - string on() { - result = "addListener" or - result = "on" or - result = "once" or - result = "prependListener" or - result = "prependOnceListener" - } + string on() { result = ["addListener", "on", "once", "prependListener", "prependOnceListener"] } /** * Gets a node that refers to an EventEmitter object. diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Express.qll b/javascript/ql/lib/semmle/javascript/frameworks/Express.qll index a68156f6c6d..3dd4c0aa257 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Express.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Express.qll @@ -378,23 +378,11 @@ module Express { */ private predicate isChainableResponseMethodCall(RouteHandler handler, MethodCallExpr call) { exists(string name | call.calls(handler.getAResponseExpr(), name) | - name = "append" or - name = "attachment" or - name = "clearCookie" or - name = "contentType" or - name = "cookie" or - name = "format" or - name = "header" or - name = "json" or - name = "jsonp" or - name = "links" or - name = "location" or - name = "send" or - name = "sendStatus" or - name = "set" or - name = "status" or - name = "type" or - name = "vary" + name = + [ + "append", "attachment", "location", "send", "sendStatus", "set", "status", "type", "vary", + "clearCookie", "contentType", "cookie", "format", "header", "json", "jsonp", "links" + ] ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Files.qll b/javascript/ql/lib/semmle/javascript/frameworks/Files.qll index 60ee6e2d10f..b68fe2b8983 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Files.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Files.qll @@ -429,12 +429,7 @@ private class LibraryAccess extends FileSystemAccess, DataFlow::InvokeNode { this = DataFlow::moduleMember("node-dir", any(string s | - s = "readFiles" or - s = "readFilesStream" or - s = "files" or - s = "promiseFiles" or - s = "subdirs" or - s = "paths" + s = ["readFiles", "readFilesStream", "files", "promiseFiles", "subdirs", "paths"] )).getACall() ) or diff --git a/javascript/ql/lib/semmle/javascript/frameworks/HTTP.qll b/javascript/ql/lib/semmle/javascript/frameworks/HTTP.qll index 94f9c4245b1..742b4855bcc 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/HTTP.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/HTTP.qll @@ -81,29 +81,12 @@ module HTTP { */ class RequestMethodName extends string { RequestMethodName() { - this = "CHECKOUT" or - this = "COPY" or - this = "DELETE" or - this = "GET" or - this = "HEAD" or - this = "LOCK" or - this = "MERGE" or - this = "MKACTIVITY" or - this = "MKCOL" or - this = "MOVE" or - this = "M-SEARCH" or - this = "NOTIFY" or - this = "OPTIONS" or - this = "PATCH" or - this = "POST" or - this = "PURGE" or - this = "PUT" or - this = "REPORT" or - this = "SEARCH" or - this = "SUBSCRIBE" or - this = "TRACE" or - this = "UNLOCK" or - this = "UNSUBSCRIBE" + this = + [ + "CHECKOUT", "COPY", "DELETE", "GET", "HEAD", "LOCK", "MERGE", "MKACTIVITY", "MKCOL", + "MOVE", "M-SEARCH", "NOTIFY", "OPTIONS", "PATCH", "POST", "PURGE", "PUT", "REPORT", + "SEARCH", "SUBSCRIBE", "TRACE", "UNLOCK", "UNSUBSCRIBE" + ] } /** @@ -111,14 +94,7 @@ module HTTP { * such as for `GET` and `HEAD` requests. */ predicate isSafe() { - this = "GET" or - this = "HEAD" or - this = "OPTIONS" or - this = "PRI" or - this = "PROPFIND" or - this = "REPORT" or - this = "SEARCH" or - this = "TRACE" + this = ["GET", "HEAD", "OPTIONS", "PRI", "PROPFIND", "REPORT", "SEARCH", "TRACE"] } } @@ -477,13 +453,7 @@ module HTTP { * Headers are never considered third-party controllable by this predicate, although the * third party does have some control over the the Referer and Origin headers. */ - predicate isThirdPartyControllable() { - exists(string kind | kind = getKind() | - kind = "parameter" or - kind = "url" or - kind = "body" - ) - } + predicate isThirdPartyControllable() { getKind() = ["parameter", "url", "body"] } } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/LodashUnderscore.qll b/javascript/ql/lib/semmle/javascript/frameworks/LodashUnderscore.qll index c05dbc3a8d6..714eeab3135 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/LodashUnderscore.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/LodashUnderscore.qll @@ -47,312 +47,50 @@ module LodashUnderscore { */ private predicate isLodashMember(string name) { // Can be generated using Object.keys(require('lodash')) - name = "templateSettings" or - name = "after" or - name = "ary" or - name = "assign" or - name = "assignIn" or - name = "assignInWith" or - name = "assignWith" or - name = "at" or - name = "before" or - name = "bind" or - name = "bindAll" or - name = "bindKey" or - name = "castArray" or - name = "chain" or - name = "chunk" or - name = "compact" or - name = "concat" or - name = "cond" or - name = "conforms" or - name = "constant" or - name = "countBy" or - name = "create" or - name = "curry" or - name = "curryRight" or - name = "debounce" or - name = "defaults" or - name = "defaultsDeep" or - name = "defer" or - name = "delay" or - name = "difference" or - name = "differenceBy" or - name = "differenceWith" or - name = "drop" or - name = "dropRight" or - name = "dropRightWhile" or - name = "dropWhile" or - name = "fill" or - name = "filter" or - name = "flatMap" or - name = "flatMapDeep" or - name = "flatMapDepth" or - name = "flatten" or - name = "flattenDeep" or - name = "flattenDepth" or - name = "flip" or - name = "flow" or - name = "flowRight" or - name = "fromPairs" or - name = "functions" or - name = "functionsIn" or - name = "groupBy" or - name = "initial" or - name = "intersection" or - name = "intersectionBy" or - name = "intersectionWith" or - name = "invert" or - name = "invertBy" or - name = "invokeMap" or - name = "iteratee" or - name = "keyBy" or - name = "keys" or - name = "keysIn" or - name = "map" or - name = "mapKeys" or - name = "mapValues" or - name = "matches" or - name = "matchesProperty" or - name = "memoize" or - name = "merge" or - name = "mergeWith" or - name = "method" or - name = "methodOf" or - name = "mixin" or - name = "negate" or - name = "nthArg" or - name = "omit" or - name = "omitBy" or - name = "once" or - name = "orderBy" or - name = "over" or - name = "overArgs" or - name = "overEvery" or - name = "overSome" or - name = "partial" or - name = "partialRight" or - name = "partition" or - name = "pick" or - name = "pickBy" or - name = "property" or - name = "propertyOf" or - name = "pull" or - name = "pullAll" or - name = "pullAllBy" or - name = "pullAllWith" or - name = "pullAt" or - name = "range" or - name = "rangeRight" or - name = "rearg" or - name = "reject" or - name = "remove" or - name = "rest" or - name = "reverse" or - name = "sampleSize" or - name = "set" or - name = "setWith" or - name = "shuffle" or - name = "slice" or - name = "sortBy" or - name = "sortedUniq" or - name = "sortedUniqBy" or - name = "split" or - name = "spread" or - name = "tail" or - name = "take" or - name = "takeRight" or - name = "takeRightWhile" or - name = "takeWhile" or - name = "tap" or - name = "throttle" or - name = "thru" or - name = "toArray" or - name = "toPairs" or - name = "toPairsIn" or - name = "toPath" or - name = "toPlainObject" or - name = "transform" or - name = "unary" or - name = "union" or - name = "unionBy" or - name = "unionWith" or - name = "uniq" or - name = "uniqBy" or - name = "uniqWith" or - name = "unset" or - name = "unzip" or - name = "unzipWith" or - name = "update" or - name = "updateWith" or - name = "values" or - name = "valuesIn" or - name = "without" or - name = "words" or - name = "wrap" or - name = "xor" or - name = "xorBy" or - name = "xorWith" or - name = "zip" or - name = "zipObject" or - name = "zipObjectDeep" or - name = "zipWith" or - name = "entries" or - name = "entriesIn" or - name = "extend" or - name = "extendWith" or - name = "add" or - name = "attempt" or - name = "camelCase" or - name = "capitalize" or - name = "ceil" or - name = "clamp" or - name = "clone" or - name = "cloneDeep" or - name = "cloneDeepWith" or - name = "cloneWith" or - name = "conformsTo" or - name = "deburr" or - name = "defaultTo" or - name = "divide" or - name = "endsWith" or - name = "eq" or - name = "escape" or - name = "escapeRegExp" or - name = "every" or - name = "find" or - name = "findIndex" or - name = "findKey" or - name = "findLast" or - name = "findLastIndex" or - name = "findLastKey" or - name = "floor" or - name = "forEach" or - name = "forEachRight" or - name = "forIn" or - name = "forInRight" or - name = "forOwn" or - name = "forOwnRight" or - name = "get" or - name = "gt" or - name = "gte" or - name = "has" or - name = "hasIn" or - name = "head" or - name = "identity" or - name = "includes" or - name = "indexOf" or - name = "inRange" or - name = "invoke" or - name = "isArguments" or - name = "isArray" or - name = "isArrayBuffer" or - name = "isArrayLike" or - name = "isArrayLikeObject" or - name = "isBoolean" or - name = "isBuffer" or - name = "isDate" or - name = "isElement" or - name = "isEmpty" or - name = "isEqual" or - name = "isEqualWith" or - name = "isError" or - name = "isFinite" or - name = "isFunction" or - name = "isInteger" or - name = "isLength" or - name = "isMap" or - name = "isMatch" or - name = "isMatchWith" or - name = "isNaN" or - name = "isNative" or - name = "isNil" or - name = "isNull" or - name = "isNumber" or - name = "isObject" or - name = "isObjectLike" or - name = "isPlainObject" or - name = "isRegExp" or - name = "isSafeInteger" or - name = "isSet" or - name = "isString" or - name = "isSymbol" or - name = "isTypedArray" or - name = "isUndefined" or - name = "isWeakMap" or - name = "isWeakSet" or - name = "join" or - name = "kebabCase" or - name = "last" or - name = "lastIndexOf" or - name = "lowerCase" or - name = "lowerFirst" or - name = "lt" or - name = "lte" or - name = "max" or - name = "maxBy" or - name = "mean" or - name = "meanBy" or - name = "min" or - name = "minBy" or - name = "stubArray" or - name = "stubFalse" or - name = "stubObject" or - name = "stubString" or - name = "stubTrue" or - name = "multiply" or - name = "nth" or - name = "noConflict" or - name = "noop" or - name = "now" or - name = "pad" or - name = "padEnd" or - name = "padStart" or - name = "parseInt" or - name = "random" or - name = "reduce" or - name = "reduceRight" or - name = "repeat" or - name = "replace" or - name = "result" or - name = "round" or - name = "runInContext" or - name = "sample" or - name = "size" or - name = "snakeCase" or - name = "some" or - name = "sortedIndex" or - name = "sortedIndexBy" or - name = "sortedIndexOf" or - name = "sortedLastIndex" or - name = "sortedLastIndexBy" or - name = "sortedLastIndexOf" or - name = "startCase" or - name = "startsWith" or - name = "subtract" or - name = "sum" or - name = "sumBy" or - name = "template" or - name = "times" or - name = "toFinite" or - name = "toInteger" or - name = "toLength" or - name = "toLower" or - name = "toNumber" or - name = "toSafeInteger" or - name = "toString" or - name = "toUpper" or - name = "trim" or - name = "trimEnd" or - name = "trimStart" or - name = "truncate" or - name = "unescape" or - name = "uniqueId" or - name = "upperCase" or - name = "upperFirst" or - name = "each" or - name = "eachRight" or - name = "first" + name = + [ + "templateSettings", "after", "ary", "assign", "assignIn", "assignInWith", "assignWith", + "at", "before", "bind", "bindAll", "bindKey", "castArray", "chain", "chunk", "compact", + "concat", "cond", "conforms", "constant", "countBy", "create", "curry", "curryRight", + "debounce", "defaults", "defaultsDeep", "defer", "delay", "difference", "differenceBy", + "differenceWith", "drop", "dropRight", "dropRightWhile", "dropWhile", "fill", "filter", + "flatMap", "flatMapDeep", "flatMapDepth", "flatten", "flattenDeep", "flattenDepth", "flip", + "flow", "flowRight", "fromPairs", "functions", "functionsIn", "groupBy", "initial", + "intersection", "intersectionBy", "intersectionWith", "invert", "invertBy", "invokeMap", + "iteratee", "keyBy", "keys", "keysIn", "map", "mapKeys", "mapValues", "matches", + "matchesProperty", "memoize", "merge", "mergeWith", "method", "methodOf", "mixin", "negate", + "nthArg", "omit", "omitBy", "once", "orderBy", "over", "overArgs", "overEvery", "overSome", + "partial", "partialRight", "partition", "pick", "pickBy", "property", "propertyOf", "pull", + "pullAll", "pullAllBy", "pullAllWith", "pullAt", "range", "rangeRight", "rearg", "reject", + "remove", "rest", "reverse", "sampleSize", "set", "setWith", "shuffle", "slice", "sortBy", + "sortedUniq", "sortedUniqBy", "split", "spread", "tail", "take", "takeRight", + "takeRightWhile", "takeWhile", "tap", "throttle", "thru", "toArray", "toPairs", "toPairsIn", + "toPath", "toPlainObject", "transform", "unary", "union", "unionBy", "unionWith", "uniq", + "uniqBy", "uniqWith", "unset", "unzip", "unzipWith", "update", "updateWith", "values", + "valuesIn", "without", "words", "wrap", "xor", "xorBy", "xorWith", "zip", "zipObject", + "zipObjectDeep", "zipWith", "entries", "entriesIn", "extend", "extendWith", "add", + "attempt", "camelCase", "capitalize", "ceil", "clamp", "clone", "cloneDeep", + "cloneDeepWith", "cloneWith", "conformsTo", "deburr", "defaultTo", "divide", "endsWith", + "eq", "escape", "escapeRegExp", "every", "find", "findIndex", "findKey", "findLast", + "findLastIndex", "findLastKey", "floor", "forEach", "forEachRight", "forIn", "forInRight", + "forOwn", "forOwnRight", "get", "gt", "gte", "has", "hasIn", "head", "identity", "includes", + "indexOf", "inRange", "invoke", "isArguments", "isArray", "isArrayBuffer", "isArrayLike", + "isArrayLikeObject", "isBoolean", "isBuffer", "isDate", "isElement", "isEmpty", "isEqual", + "isEqualWith", "isError", "isFinite", "isFunction", "isInteger", "isLength", "isMap", + "isMatch", "isMatchWith", "isNaN", "isNative", "isNil", "isNull", "isNumber", "isObject", + "isObjectLike", "isPlainObject", "isRegExp", "isSafeInteger", "isSet", "isString", + "isSymbol", "isTypedArray", "isUndefined", "isWeakMap", "isWeakSet", "join", "kebabCase", + "last", "lastIndexOf", "lowerCase", "lowerFirst", "lt", "lte", "max", "maxBy", "mean", + "meanBy", "min", "minBy", "stubArray", "stubFalse", "stubObject", "stubString", "stubTrue", + "multiply", "nth", "noConflict", "noop", "now", "pad", "padEnd", "padStart", "parseInt", + "random", "reduce", "reduceRight", "repeat", "replace", "result", "round", "runInContext", + "sample", "size", "snakeCase", "some", "sortedIndex", "sortedIndexBy", "sortedIndexOf", + "sortedLastIndex", "sortedLastIndexBy", "sortedLastIndexOf", "startCase", "startsWith", + "subtract", "sum", "sumBy", "template", "times", "toFinite", "toInteger", "toLength", + "toLower", "toNumber", "toSafeInteger", "toString", "toUpper", "trim", "trimEnd", + "trimStart", "truncate", "unescape", "uniqueId", "upperCase", "upperFirst", "each", + "eachRight", "first" + ] } /** @@ -363,27 +101,15 @@ module LodashUnderscore { exists(DataFlow::CallNode call, string name | // Members ending with By, With, or While indicate that they are a variant of // another function that takes a callback. - name.matches("%By") or - name.matches("%With") or - name.matches("%While") or + name.matches(["%By", "%With", "%While"]) + or // Other members that don't fit the above pattern. - name = "each" or - name = "eachRight" or - name = "every" or - name = "filter" or - name = "find" or - name = "findLast" or - name = "flatMap" or - name = "flatMapDeep" or - name = "flatMapDepth" or - name = "forEach" or - name = "forEachRight" or - name = "partition" or - name = "reduce" or - name = "reduceRight" or - name = "replace" or - name = "some" or - name = "transform" + name = + [ + "each", "eachRight", "every", "filter", "find", "findLast", "flatMap", "flatMapDeep", + "flatMapDepth", "forEach", "forEachRight", "partition", "reduce", "reduceRight", + "replace", "some", "transform" + ] | call = member(name).getACall() and pred = call.getAnArgument().(DataFlow::FunctionNode).getExceptionalReturn() and @@ -461,91 +187,30 @@ private class LodashCallbackAsPartialInvoke extends DataFlow::PartialInvokeNode: this = LodashUnderscore::member(name).getACall() and getNumArgument() = argumentCount | - ( - name = "bind" or - name = "callback" or - name = "iteratee" - ) and + name = ["bind", "callback", "iteratee"] and callbackIndex = 0 and contextIndex = 1 and argumentCount = 2 or - ( - name = "all" or - name = "any" or - name = "collect" or - name = "countBy" or - name = "detect" or - name = "dropRightWhile" or - name = "dropWhile" or - name = "each" or - name = "eachRight" or - name = "every" or - name = "filter" or - name = "find" or - name = "findIndex" or - name = "findKey" or - name = "findLast" or - name = "findLastIndex" or - name = "findLastKey" or - name = "forEach" or - name = "forEachRight" or - name = "forIn" or - name = "forInRight" or - name = "groupBy" or - name = "indexBy" or - name = "map" or - name = "mapKeys" or - name = "mapValues" or - name = "max" or - name = "min" or - name = "omit" or - name = "partition" or - name = "pick" or - name = "reject" or - name = "remove" or - name = "select" or - name = "some" or - name = "sortBy" or - name = "sum" or - name = "takeRightWhile" or - name = "takeWhile" or - name = "tap" or - name = "thru" or - name = "times" or - name = "unzipWith" or - name = "zipWith" - ) and + name = + [ + "all", "any", "collect", "countBy", "detect", "dropRightWhile", "dropWhile", "each", + "eachRight", "every", "filter", "find", "findIndex", "findKey", "findLast", + "findLastIndex", "findLastKey", "forEach", "forEachRight", "forIn", "forInRight", + "groupBy", "indexBy", "map", "mapKeys", "mapValues", "max", "min", "omit", "partition", + "pick", "reject", "remove", "select", "some", "sortBy", "sum", "takeRightWhile", + "takeWhile", "tap", "thru", "times", "unzipWith", "zipWith" + ] and callbackIndex = 1 and contextIndex = 2 and argumentCount = 3 or - ( - name = "foldl" or - name = "foldr" or - name = "inject" or - name = "reduce" or - name = "reduceRight" or - name = "transform" - ) and + name = ["foldl", "foldr", "inject", "reduce", "reduceRight", "transform"] and callbackIndex = 1 and contextIndex = 3 and argumentCount = 4 or - ( - name = "sortedlastIndex" - or - name = "assign" - or - name = "eq" - or - name = "extend" - or - name = "merge" - or - name = "sortedIndex" and - name = "uniq" - ) and + name = ["sortedlastIndex", "assign", "eq", "extend", "merge", "sortedIndex", "uniq"] and callbackIndex = 2 and contextIndex = 3 and argumentCount = 4 diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll b/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll index ef678db2835..4a64ed2a7e1 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll @@ -18,19 +18,11 @@ abstract class LoggerCall extends DataFlow::CallNode { * Gets a log level name that is used in RFC5424, `npm`, `console`. */ string getAStandardLoggerMethodName() { - result = "crit" or - result = "dir" or - result = "debug" or - result = "error" or - result = "emerg" or - result = "fatal" or - result = "info" or - result = "log" or - result = "notice" or - result = "silly" or - result = "trace" or - result = "verbose" or - result = "warn" + result = + [ + "crit", "dir", "trace", "verbose", "warn", "debug", "error", "emerg", "fatal", "info", "log", + "notice", "silly" + ] } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/MooTools.qll b/javascript/ql/lib/semmle/javascript/frameworks/MooTools.qll index 46a813d5c08..c1b4811e889 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/MooTools.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/MooTools.qll @@ -35,7 +35,7 @@ module MooTools { predicate interpretsNodeAsHtml(DataFlow::Node node) { exists(Element e | node = e.getAnElementPropertyValue("html") or - node = e.getAMethodCall(["appendHtml"]).getArgument(0) + node = e.getAMethodCall("appendHtml").getArgument(0) ) } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll b/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll index b27641e313b..9e975a2872d 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll @@ -252,28 +252,13 @@ private module Mongoose { * Holds if Model method `name` returns a Query. */ predicate returnsQuery(string name) { - name = "$where" or - name = "count" or - name = "countDocuments" or - name = "deleteMany" or - name = "deleteOne" or - name = "find" or - name = "findById" or - name = "findByIdAndDelete" or - name = "findByIdAndRemove" or - name = "findByIdAndUpdate" or - name = "findOne" or - name = "findOneAndDelete" or - name = "findOneAndRemove" or - name = "findOneAndReplace" or - name = "findOneAndUpdate" or - name = "geosearch" or - name = "remove" or - name = "replaceOne" or - name = "update" or - name = "updateMany" or - name = "updateOne" or - name = "where" + name = + [ + "$where", "count", "findOne", "findOneAndDelete", "findOneAndRemove", + "findOneAndReplace", "findOneAndUpdate", "geosearch", "remove", "replaceOne", "update", + "updateMany", "countDocuments", "updateOne", "where", "deleteMany", "deleteOne", "find", + "findById", "findByIdAndDelete", "findByIdAndRemove", "findByIdAndUpdate" + ] } /** @@ -347,117 +332,34 @@ private module Mongoose { */ predicate interpretsArgumentAsQuery(string name, int n) { n = 0 and - ( - name = "and" or - name = "count" or - name = "countDocuments" or - name = "deleteMany" or - name = "deleteOne" or - name = "elemMatch" or - name = "find" or - name = "findOne" or - name = "findOneAndDelete" or - name = "findOneAndRemove" or - name = "findOneAndReplace" or - name = "findOneAndUpdate" or - name = "merge" or - name = "nor" or - name = "or" or - name = "remove" or - name = "replaceOne" or - name = "setQuery" or - name = "setUpdate" or - name = "update" or - name = "updateMany" or - name = "updateOne" or - name = "where" - ) + name = + [ + "and", "count", "findOneAndReplace", "findOneAndUpdate", "merge", "nor", "or", "remove", + "replaceOne", "setQuery", "setUpdate", "update", "countDocuments", "updateMany", + "updateOne", "where", "deleteMany", "deleteOne", "elemMatch", "find", "findOne", + "findOneAndDelete", "findOneAndRemove" + ] or n = 1 and - ( - name = "distinct" or - name = "findOneAndUpdate" or - name = "update" or - name = "updateMany" or - name = "updateOne" - ) + name = ["distinct", "findOneAndUpdate", "update", "updateMany", "updateOne"] } /** * Holds if Query method `name` returns a Query. */ predicate returnsQuery(string name) { - name = "$where" or - name = "J" or - name = "all" or - name = "and" or - name = "batchsize" or - name = "box" or - name = "center" or - name = "centerSphere" or - name = "circle" or - name = "collation" or - name = "comment" or - name = "count" or - name = "countDocuments" or - name = "distinct" or - name = "elemMatch" or - name = "equals" or - name = "error" or - name = "estimatedDocumentCount" or - name = "exists" or - name = "explain" or - name = "find" or - name = "findById" or - name = "findOne" or - name = "findOneAndRemove" or - name = "findOneAndUpdate" or - name = "geometry" or - name = "get" or - name = "gt" or - name = "gte" or - name = "hint" or - name = "in" or - name = "intersects" or - name = "lean" or - name = "limit" or - name = "lt" or - name = "lte" or - name = "map" or - name = "map" or - name = "maxDistance" or - name = "maxTimeMS" or - name = "maxscan" or - name = "mod" or - name = "ne" or - name = "near" or - name = "nearSphere" or - name = "nin" or - name = "or" or - name = "orFail" or - name = "polygon" or - name = "populate" or - name = "read" or - name = "readConcern" or - name = "regexp" or - name = "remove" or - name = "select" or - name = "session" or - name = "set" or - name = "setOptions" or - name = "setQuery" or - name = "setUpdate" or - name = "size" or - name = "skip" or - name = "slaveOk" or - name = "slice" or - name = "snapshot" or - name = "sort" or - name = "update" or - name = "w" or - name = "where" or - name = "within" or - name = "wtimeout" + name = + [ + "$where", "J", "comment", "count", "countDocuments", "distinct", "elemMatch", "equals", + "error", "estimatedDocumentCount", "exists", "explain", "all", "find", "findById", + "findOne", "findOneAndRemove", "findOneAndUpdate", "geometry", "get", "gt", "gte", + "hint", "and", "in", "intersects", "lean", "limit", "lt", "lte", "map", "map", + "maxDistance", "maxTimeMS", "batchsize", "maxscan", "mod", "ne", "near", "nearSphere", + "nin", "or", "orFail", "polygon", "populate", "box", "read", "readConcern", "regexp", + "remove", "select", "session", "set", "setOptions", "setQuery", "setUpdate", "center", + "size", "skip", "slaveOk", "slice", "snapshot", "sort", "update", "w", "where", + "within", "centerSphere", "wtimeout", "circle", "collation" + ] } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll b/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll index 43fde9639c2..764e49b1daf 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll @@ -523,14 +523,11 @@ module NodeJSLib { /** A write to the file system. */ private class NodeJSFileSystemAccessWrite extends FileSystemWriteAccess, NodeJSFileSystemAccess { NodeJSFileSystemAccessWrite() { - methodName = "appendFile" or - methodName = "appendFileSync" or - methodName = "write" or - methodName = "writeFile" or - methodName = "writeFileSync" or - methodName = "writeSync" or - methodName = "link" or - methodName = "linkSync" + methodName = + [ + "appendFile", "appendFileSync", "write", "writeFile", "writeFileSync", "writeSync", + "link", "linkSync" + ] } override DataFlow::Node getADataNode() { @@ -699,13 +696,7 @@ module NodeJSLib { ) or shell = false and - ( - methodName = "execFile" or - methodName = "execFileSync" or - methodName = "spawn" or - methodName = "spawnSync" or - methodName = "fork" - ) + methodName = ["execFile", "execFileSync", "spawn", "spawnSync", "fork"] ) and // all of the above methods take the command as their first argument result = getParameter(0).getARhs() @@ -716,18 +707,12 @@ module NodeJSLib { override predicate isShellInterpreted(DataFlow::Node arg) { arg = getACommandArgument(true) } override DataFlow::Node getArgumentList() { - ( - methodName = "execFile" or - methodName = "execFileSync" or - methodName = "fork" or - methodName = "spawn" or - methodName = "spawnSync" - ) and + methodName = ["execFile", "execFileSync", "fork", "spawn", "spawnSync"] and // all of the above methods take the argument list as their second argument result = getParameter(1).getARhs() } - override predicate isSync() { "Sync" = methodName.suffix(methodName.length() - 4) } + override predicate isSync() { methodName.matches("%Sync") } override DataFlow::Node getOptionsArg() { not result.getALocalSource() instanceof DataFlow::FunctionNode and // looks like callback diff --git a/javascript/ql/lib/semmle/javascript/frameworks/PkgCloud.qll b/javascript/ql/lib/semmle/javascript/frameworks/PkgCloud.qll index 48e0deb0cf8..a619dedcad9 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/PkgCloud.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/PkgCloud.qll @@ -11,17 +11,11 @@ module PkgCloud { private predicate takesConfigurationObject(DataFlow::InvokeNode invk, int i) { exists(DataFlow::ModuleImportNode mod, DataFlow::SourceNode receiver, string type | mod.getPath() = "pkgcloud" and - ( - type = "compute" or - type = "storage" or - type = "database" or - type = "dns" or - type = "blockstorage" or - type = "loadbalancer" or - type = "network" or - type = "orchestration" or - type = "cdn" - ) and + type = + [ + "compute", "storage", "database", "dns", "blockstorage", "loadbalancer", "network", + "orchestration", "cdn" + ] and ( // require('pkgcloud').compute receiver = mod.getAPropertyRead(type) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/ShellJS.qll b/javascript/ql/lib/semmle/javascript/frameworks/ShellJS.qll index 4ba780b0480..426c8d81765 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/ShellJS.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/ShellJS.qll @@ -80,17 +80,7 @@ module ShellJS { */ private class ShellJSGenericFileAccess extends FileSystemAccess, ShellJSCall { ShellJSGenericFileAccess() { - name = "cd" or - name = "cp" or - name = "chmod" or - name = "pushd" or - name = "find" or - name = "ls" or - name = "ln" or - name = "mkdir" or - name = "mv" or - name = "rm" or - name = "touch" + name = ["cd", "cp", "touch", "chmod", "pushd", "find", "ls", "ln", "mkdir", "mv", "rm"] } override DataFlow::Node getAPathArgument() { result = getAnArgument() } @@ -110,13 +100,7 @@ module ShellJS { * A file system access that returns the contents of a file. */ private class ShellJSRead extends FileSystemReadAccess, ShellJSCall { - ShellJSRead() { - name = "cat" or - name = "head" or - name = "sort" or - name = "tail" or - name = "uniq" - } + ShellJSRead() { name = ["cat", "head", "sort", "tail", "uniq"] } override DataFlow::Node getAPathArgument() { result = getAnArgument() } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/StringFormatters.qll b/javascript/ql/lib/semmle/javascript/frameworks/StringFormatters.qll index 6b8dc704af9..6d9cf8957e7 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/StringFormatters.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/StringFormatters.qll @@ -34,14 +34,7 @@ private class LibraryFormatter extends PrintfStyleCall { returns = false and mod = "console" and ( - ( - meth = "debug" or - meth = "error" or - meth = "info" or - meth = "log" or - meth = "trace" or - meth = "warn" - ) and + meth = ["debug", "error", "info", "log", "trace", "warn"] and formatIndex = 0 or meth = "assert" and formatIndex = 1 diff --git a/javascript/ql/lib/semmle/javascript/frameworks/SystemCommandExecutors.qll b/javascript/ql/lib/semmle/javascript/frameworks/SystemCommandExecutors.qll index ce445cfd174..89eb8c9e9ea 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/SystemCommandExecutors.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/SystemCommandExecutors.qll @@ -107,9 +107,7 @@ private class SystemCommandExecutors extends SystemCommandExecution, DataFlow::I */ bindingset[name] private boolean getSync(string name) { - if name.suffix(name.length() - 4) = "Sync" or name.suffix(name.length() - 4) = "sync" - then result = true - else result = false + if name.matches("%Sync") or name.matches("%sync") then result = true else result = false } private class RemoteCommandExecutor extends SystemCommandExecution, DataFlow::InvokeNode { diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll b/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll index 02baad2b008..42b560b28a0 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll @@ -9,26 +9,11 @@ module Templating { * Gets a string that is a known template delimiter. */ string getADelimiter() { - result = "<%" or - result = "%>" or - result = "{{" or - result = "}}" or - result = "{%" or - result = "%}" or - result = "<@" or - result = "@>" or - result = "<#" or - result = "#>" or - result = "{#" or - result = "#}" or - result = "{$" or - result = "$}" or - result = "[%" or - result = "%]" or - result = "[[" or - result = "]]" or - result = "" + result = + [ + "<%", "%>", "{#", "#}", "{$", "$}", "[%", "%]", "[[", "]]", "", "{{", "}}", "{%", + "%}", "<@", "@>", "<#", "#>" + ] } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll index 0ee45aefa11..2101f29fdab 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Vue.qll @@ -151,7 +151,7 @@ module Vue { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -640,7 +640,7 @@ module Vue { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/javascript/ql/lib/semmle/javascript/frameworks/jQuery.qll b/javascript/ql/lib/semmle/javascript/frameworks/jQuery.qll index 38401464b4a..b91770b8bf6 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/jQuery.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/jQuery.qll @@ -441,19 +441,11 @@ module JQuery { * arguments as HTML. */ predicate isMethodArgumentInterpretedAsHtml(string name) { - name = "after" or - name = "append" or - name = "appendTo" or - name = "before" or - name = "html" or - name = "insertAfter" or - name = "insertBefore" or - name = "prepend" or - name = "prependTo" or - name = "replaceWith" or - name = "wrap" or - name = "wrapAll" or - name = "wrapInner" + name = + [ + "after", "append", "wrap", "wrapAll", "wrapInner", "appendTo", "before", "html", + "insertAfter", "insertBefore", "prepend", "prependTo", "replaceWith" + ] } /** @@ -461,13 +453,7 @@ module JQuery { * arguments as a selector. */ predicate isMethodArgumentInterpretedAsSelector(string name) { - name = "appendTo" or - name = "insertAfter" or - name = "insertBefore" or - name = "prependTo" or - name = "wrap" or - name = "wrapAll" or - name = "wrapInner" + name = ["appendTo", "insertAfter", "insertBefore", "prependTo", "wrap", "wrapAll", "wrapInner"] } module DollarSource { diff --git a/javascript/ql/lib/semmle/javascript/frameworks/xUnit.qll b/javascript/ql/lib/semmle/javascript/frameworks/xUnit.qll index a897cb486aa..e07bd29b3c3 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/xUnit.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/xUnit.qll @@ -131,7 +131,7 @@ class XUnitAnnotation extends Expr { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/javascript/ql/lib/semmle/javascript/linters/JSLint.qll b/javascript/ql/lib/semmle/javascript/linters/JSLint.qll index bfe8de1768d..04fe0bb5a6c 100644 --- a/javascript/ql/lib/semmle/javascript/linters/JSLint.qll +++ b/javascript/ql/lib/semmle/javascript/linters/JSLint.qll @@ -153,90 +153,36 @@ class JSLintOptions extends JSLintDirective { private string jsLintImplicitGlobal(string category) { // cf. http://www.jslint.com/help.html#global category = "browser" and - ( - result = "clearInterval" or - result = "clearTimeout" or - result = "document" or - result = "event" or - result = "frames" or - result = "history" or - result = "Image" or - result = "location" or - result = "name" or - result = "navigator" or - result = "Option" or - result = "parent" or - result = "screen" or - result = "setInterval" or - result = "setTimeout" or - result = "window" or - result = "XMLHttpRequest" - ) + result = + [ + "clearInterval", "clearTimeout", "Option", "parent", "screen", "setInterval", "setTimeout", + "window", "XMLHttpRequest", "document", "event", "frames", "history", "Image", "location", + "name", "navigator" + ] or category = "devel" and - ( - result = "alert" or - result = "confirm" or - result = "console" or - result = "Debug" or - result = "opera" or - result = "prompt" or - result = "WSH" - ) + result = ["alert", "confirm", "console", "Debug", "opera", "prompt", "WSH"] or category = "node" and - ( - result = "Buffer" or - result = "clearInterval" or - result = "clearTimeout" or - result = "console" or - result = "exports" or - result = "result" or - result = "module" or - result = "process" or - result = "querystring" or - result = "require" or - result = "setInterval" or - result = "setTimeout" or - result = "__filename" or - result = "__dirname" - ) + result = + [ + "Buffer", "clearInterval", "setInterval", "setTimeout", "__filename", "__dirname", + "clearTimeout", "console", "exports", "result", "module", "process", "querystring", "require" + ] or category = "couch" and - ( - result = "emit" or - result = "getRow" or - result = "isArray" or - result = "log" or - result = "provides" or - result = "registerType" or - result = "require" or - result = "send" or - result = "start" or - result = "sum" or - result = "toJSON" - ) + result = + [ + "emit", "getRow", "toJSON", "isArray", "log", "provides", "registerType", "require", "send", + "start", "sum" + ] or category = "rhino" and - ( - result = "defineClass" or - result = "deserialize" or - result = "gc" or - result = "help" or - result = "load" or - result = "loadClass" or - result = "print" or - result = "quit" or - result = "readFile" or - result = "readUrl" or - result = "runCommand" or - result = "seal" or - result = "serialize" or - result = "spawn" or - result = "sync" or - result = "toint32" or - result = "version" - ) + result = + [ + "defineClass", "deserialize", "runCommand", "seal", "serialize", "spawn", "sync", "toint32", + "version", "gc", "help", "load", "loadClass", "print", "quit", "readFile", "readUrl" + ] } /** diff --git a/javascript/ql/lib/semmle/javascript/security/CryptoAlgorithms.qll b/javascript/ql/lib/semmle/javascript/security/CryptoAlgorithms.qll index d9f25b42c9a..4b3c5f2a49f 100644 --- a/javascript/ql/lib/semmle/javascript/security/CryptoAlgorithms.qll +++ b/javascript/ql/lib/semmle/javascript/security/CryptoAlgorithms.qll @@ -15,68 +15,35 @@ */ private module AlgorithmNames { predicate isStrongHashingAlgorithm(string name) { - name = "DSA" or - name = "ED25519" or - name = "ES256" or - name = "ECDSA256" or - name = "ES384" or - name = "ECDSA384" or - name = "ES512" or - name = "ECDSA512" or - name = "SHA2" or - name = "SHA224" or - name = "SHA256" or - name = "SHA384" or - name = "SHA512" or - name = "SHA3" + name = + [ + "DSA", "ED25519", "ES256", "ECDSA256", "ES384", "ECDSA384", "ES512", "ECDSA512", "SHA2", + "SHA224", "SHA256", "SHA384", "SHA512", "SHA3", "SHA3224", "SHA3256", "SHA3384", "SHA3512" + ] } predicate isWeakHashingAlgorithm(string name) { - name = "HAVEL128" or - name = "MD2" or - name = "MD4" or - name = "MD5" or - name = "PANAMA" or - name = "RIPEMD" or - name = "RIPEMD128" or - name = "RIPEMD256" or - name = "RIPEMD160" or - name = "RIPEMD320" or - name = "SHA0" or - name = "SHA1" + name = + [ + "HAVEL128", "MD2", "MD4", "MD5", "PANAMA", "RIPEMD", "RIPEMD128", "RIPEMD256", "RIPEMD160", + "RIPEMD320", "SHA0", "SHA1" + ] } predicate isStrongEncryptionAlgorithm(string name) { - name = "AES" or - name = "AES128" or - name = "AES192" or - name = "AES256" or - name = "AES512" or - name = "RSA" or - name = "RABBIT" or - name = "BLOWFISH" + name = ["AES", "AES128", "AES192", "AES256", "AES512", "RSA", "RABBIT", "BLOWFISH"] } predicate isWeakEncryptionAlgorithm(string name) { - name = "DES" or - name = "3DES" or - name = "TRIPLEDES" or - name = "TDEA" or - name = "TRIPLEDEA" or - name = "ARC2" or - name = "RC2" or - name = "ARC4" or - name = "RC4" or - name = "ARCFOUR" or - name = "ARC5" or - name = "RC5" + name = + [ + "DES", "3DES", "TRIPLEDES", "TDEA", "TRIPLEDEA", "ARC2", "RC2", "ARC4", "RC4", "ARCFOUR", + "ARC5", "RC5" + ] } predicate isStrongPasswordHashingAlgorithm(string name) { - name = "ARGON2" or - name = "PBKDF2" or - name = "BCRYPT" or - name = "SCRYPT" + name = ["ARGON2", "PBKDF2", "BCRYPT", "SCRYPT"] } predicate isWeakPasswordHashingAlgorithm(string name) { none() } diff --git a/javascript/ql/lib/semmle/javascript/security/TaintedUrlSuffix.qll b/javascript/ql/lib/semmle/javascript/security/TaintedUrlSuffix.qll index a8151180f9b..c83a3bf85af 100644 --- a/javascript/ql/lib/semmle/javascript/security/TaintedUrlSuffix.qll +++ b/javascript/ql/lib/semmle/javascript/security/TaintedUrlSuffix.qll @@ -29,20 +29,11 @@ module TaintedUrlSuffix { /** Holds for `pred -> succ` is a step of form `x -> x.p` */ private predicate isSafeLocationProp(DataFlow::PropRead read) { // Ignore properties that refer to the scheme, domain, port, auth, or path. - exists(string name | name = read.getPropertyName() | - name = "protocol" or - name = "scheme" or - name = "host" or - name = "hostname" or - name = "domain" or - name = "origin" or - name = "port" or - name = "path" or - name = "pathname" or - name = "username" or - name = "password" or - name = "auth" - ) + read.getPropertyName() = + [ + "protocol", "scheme", "host", "hostname", "domain", "origin", "port", "path", "pathname", + "username", "password", "auth" + ] } /** diff --git a/javascript/ql/lib/semmle/javascript/security/UselessUseOfCat.qll b/javascript/ql/lib/semmle/javascript/security/UselessUseOfCat.qll index 604a8182e96..b2b65038508 100644 --- a/javascript/ql/lib/semmle/javascript/security/UselessUseOfCat.qll +++ b/javascript/ql/lib/semmle/javascript/security/UselessUseOfCat.qll @@ -303,14 +303,11 @@ module PrettyPrintCatCall { bindingset[str] private string createSimplifiedStringConcat(string str) { // Remove an initial ""+ (e.g. in `""+file`) - if str.prefix(5) = "\"\" + " + if str.matches("\"\" + %") then result = str.suffix(5) else // prettify `${newpath}` to just newpath - if - str.prefix(3) = "`${" and - str.suffix(str.length() - 2) = "}`" and - not str.suffix(3).matches("%{%") + if str.matches("`${%") and str.matches("%}`") and not str.suffix(3).matches("%{%") then result = str.prefix(str.length() - 2).suffix(3) else result = str } diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll index 65d3b44dafa..f426dd050c8 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll @@ -88,12 +88,7 @@ module ClientSideUrlRedirect { class LocationSink extends Sink, DataFlow::ValueNode { LocationSink() { // A call to a `window.navigate` or `window.open` - exists(string name | - name = "navigate" or - name = "open" or - name = "openDialog" or - name = "showModalDialog" - | + exists(string name | name = ["navigate", "open", "openDialog", "showModalDialog"] | this = DataFlow::globalVarRef(name).getACall().getArgument(0) ) or @@ -102,7 +97,7 @@ module ClientSideUrlRedirect { locationCall = DOM::locationRef().getAMethodCall(name) and this = locationCall.getArgument(0) | - name = "replace" or name = "assign" + name = ["replace", "assign"] ) or // An assignment to `location` @@ -113,7 +108,7 @@ module ClientSideUrlRedirect { pw = DOM::locationRef().getAPropertyWrite(propName) and this = pw.getRhs() | - propName = "href" or propName = "protocol" or propName = "hostname" + propName = ["href", "protocol", "hostname"] ) or // A redirection using the AngularJS `$location` service @@ -153,9 +148,8 @@ module ClientSideUrlRedirect { */ class SrcAttributeUrlSink extends ScriptUrlSink, DataFlow::ValueNode { SrcAttributeUrlSink() { - exists(DOM::AttributeDefinition attr, string eltName | - attr.getElement().getName() = eltName and - (eltName = "script" or eltName = "iframe") and + exists(DOM::AttributeDefinition attr | + attr.getElement().getName() = ["script", "iframe"] and attr.getName() = "src" and this = attr.getValueNode() ) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/DOM.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/DOM.qll index 204f04e9c52..5528aa257d1 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/DOM.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/DOM.qll @@ -88,14 +88,7 @@ class DomMethodCallExpr extends MethodCallExpr { name = "setAttributeNS" and argPos = 2 ) and // restrict to potentially dangerous attributes - exists(string attr | - attr = "action" or - attr = "formaction" or - attr = "href" or - attr = "src" or - attr = "xlink:href" or - attr = "data" - | + exists(string attr | attr = ["action", "formaction", "href", "src", "xlink:href", "data"] | getArgument(argPos - 1).getStringValue().toLowerCase() = attr ) ) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/LoopBoundInjectionCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/LoopBoundInjectionCustomizations.qll index 8e3857d049b..9a44bf2d41f 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/LoopBoundInjectionCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/LoopBoundInjectionCustomizations.qll @@ -115,66 +115,18 @@ module LoopBoundInjection { * Holds if `name` is a method from lodash vulnerable to a DoS attack if called with a tainted object. */ predicate loopableLodashMethod(string name) { - name = "chunk" or - name = "compact" or - name = "difference" or - name = "differenceBy" or - name = "differenceWith" or - name = "drop" or - name = "dropRight" or - name = "dropRightWhile" or - name = "dropWhile" or - name = "fill" or - name = "findIndex" or - name = "findLastIndex" or - name = "flatten" or - name = "flattenDeep" or - name = "flattenDepth" or - name = "initial" or - name = "intersection" or - name = "intersectionBy" or - name = "intersectionWith" or - name = "join" or - name = "remove" or - name = "reverse" or - name = "slice" or - name = "sortedUniq" or - name = "sortedUniqBy" or - name = "tail" or - name = "union" or - name = "unionBy" or - name = "unionWith" or - name = "uniqBy" or - name = "unzip" or - name = "unzipWith" or - name = "without" or - name = "zip" or - name = "zipObject" or - name = "zipObjectDeep" or - name = "zipWith" or - name = "countBy" or - name = "each" or - name = "forEach" or - name = "eachRight" or - name = "forEachRight" or - name = "filter" or - name = "find" or - name = "findLast" or - name = "flatMap" or - name = "flatMapDeep" or - name = "flatMapDepth" or - name = "forEach" or - name = "forEachRight" or - name = "groupBy" or - name = "invokeMap" or - name = "keyBy" or - name = "map" or - name = "orderBy" or - name = "partition" or - name = "reduce" or - name = "reduceRight" or - name = "reject" or - name = "sortBy" + name = + [ + "chunk", "compact", "difference", "differenceBy", "differenceWith", "drop", "dropRight", + "dropRightWhile", "dropWhile", "fill", "findIndex", "findLastIndex", "flatten", + "flattenDeep", "flattenDepth", "initial", "intersection", "intersectionBy", + "intersectionWith", "join", "remove", "reverse", "slice", "sortedUniq", "sortedUniqBy", + "tail", "union", "unionBy", "unionWith", "uniqBy", "unzip", "unzipWith", "without", "zip", + "zipObject", "zipObjectDeep", "zipWith", "countBy", "each", "forEach", "eachRight", + "forEachRight", "filter", "find", "findLast", "flatMap", "flatMapDeep", "flatMapDepth", + "forEach", "forEachRight", "groupBy", "invokeMap", "keyBy", "map", "orderBy", "partition", + "reduce", "reduceRight", "reject", "sortBy" + ] } /** diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll index 13ac5d358c9..c4440a31c7b 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll @@ -751,13 +751,11 @@ module TaintedPath { exists(mcn.getAnArgument().asExpr().getIntValue()) or exists(string argumentlessMethodName | - argumentlessMethodName = "toLocaleLowerCase" or - argumentlessMethodName = "toLocaleUpperCase" or - argumentlessMethodName = "toLowerCase" or - argumentlessMethodName = "toUpperCase" or - argumentlessMethodName = "trim" or - argumentlessMethodName = "trimLeft" or - argumentlessMethodName = "trimRight" + argumentlessMethodName = + [ + "toLocaleLowerCase", "toLocaleUpperCase", "toLowerCase", "toUpperCase", "trim", + "trimLeft", "trimRight" + ] | name = argumentlessMethodName ) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/TypeConfusionThroughParameterTamperingCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/TypeConfusionThroughParameterTamperingCustomizations.qll index ab237733acc..3364b704dc1 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/TypeConfusionThroughParameterTamperingCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/TypeConfusionThroughParameterTamperingCustomizations.qll @@ -39,11 +39,7 @@ module TypeConfusionThroughParameterTampering { private class StringArrayAmbiguousMethodCall extends Sink { StringArrayAmbiguousMethodCall() { exists(string name, DataFlow::MethodCallNode mc | - name = "concat" or - name = "includes" or - name = "indexOf" or - name = "lastIndexOf" or - name = "slice" + name = ["concat", "includes", "indexOf", "lastIndexOf", "slice"] | mc.calls(this, name) and // ignore patterns that are innocent in practice diff --git a/javascript/ql/lib/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll index 9574e6e6376..98783c5b9e4 100644 --- a/javascript/ql/lib/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll @@ -67,14 +67,7 @@ module PolynomialReDoS { | this = mcn.getArgument(0) and regexp = mcn.getReceiver() and - ( - name = "match" or - name = "split" or - name = "matchAll" or - name = "replace" or - name = "replaceAll" or - name = "search" - ) + name = ["match", "split", "matchAll", "replace", "replaceAll", "search"] or this = mcn.getReceiver() and regexp = mcn.getArgument(0) and diff --git a/javascript/ql/lib/semmle/javascript/security/performance/ReDoSUtil.qll b/javascript/ql/lib/semmle/javascript/security/performance/ReDoSUtil.qll index 65431d7179e..2cd324ed8f7 100644 --- a/javascript/ql/lib/semmle/javascript/security/performance/ReDoSUtil.qll +++ b/javascript/ql/lib/semmle/javascript/security/performance/ReDoSUtil.qll @@ -139,8 +139,6 @@ class RegExpRoot extends RegExpTerm { predicate isRelevant() { // there is at least one repetition getRoot(any(InfiniteRepetitionQuantifier q)) = this and - // there are no lookbehinds - not exists(RegExpLookbehind lbh | getRoot(lbh) = this) and // is actually used as a RegExp isUsedAsRegExp() and // not excluded for library specific reasons @@ -479,7 +477,7 @@ private module CharacterClasses { result = ["0", "9"] or cc.getValue() = "s" and - result = [" "] + result = " " or cc.getValue() = "w" and result = ["a", "Z", "_", "0", "9"] @@ -492,7 +490,7 @@ private module CharacterClasses { result = "9" or cc.getValue() = "s" and - result = [" "] + result = " " or cc.getValue() = "w" and result = "a" diff --git a/javascript/ql/lib/tutorial.qll b/javascript/ql/lib/tutorial.qll new file mode 100644 index 00000000000..8cb1797a532 --- /dev/null +++ b/javascript/ql/lib/tutorial.qll @@ -0,0 +1,1207 @@ +/** + * This library is used in the QL detective tutorials. + * + * Note: Data is usually stored in a separate database and the QL libraries only contain predicates, + * but for this tutorial both the data and the predicates are stored in the library. + */ +class Person extends string { + Person() { + this = "Ronil" or + this = "Dina" or + this = "Ravi" or + this = "Bruce" or + this = "Jo" or + this = "Aida" or + this = "Esme" or + this = "Charlie" or + this = "Fred" or + this = "Meera" or + this = "Maya" or + this = "Chad" or + this = "Tiana" or + this = "Laura" or + this = "George" or + this = "Will" or + this = "Mary" or + this = "Almira" or + this = "Susannah" or + this = "Rhoda" or + this = "Cynthia" or + this = "Eunice" or + this = "Olive" or + this = "Virginia" or + this = "Angeline" or + this = "Helen" or + this = "Cornelia" or + this = "Harriet" or + this = "Mahala" or + this = "Abby" or + this = "Margaret" or + this = "Deb" or + this = "Minerva" or + this = "Severus" or + this = "Lavina" or + this = "Adeline" or + this = "Cath" or + this = "Elisa" or + this = "Lucretia" or + this = "Anne" or + this = "Eleanor" or + this = "Joanna" or + this = "Adam" or + this = "Agnes" or + this = "Rosanna" or + this = "Clara" or + this = "Melissa" or + this = "Amy" or + this = "Isabel" or + this = "Jemima" or + this = "Cordelia" or + this = "Melinda" or + this = "Delila" or + this = "Jeremiah" or + this = "Elijah" or + this = "Hester" or + this = "Walter" or + this = "Oliver" or + this = "Hugh" or + this = "Aaron" or + this = "Reuben" or + this = "Eli" or + this = "Amos" or + this = "Augustus" or + this = "Theodore" or + this = "Ira" or + this = "Timothy" or + this = "Cyrus" or + this = "Horace" or + this = "Simon" or + this = "Asa" or + this = "Frank" or + this = "Nelson" or + this = "Leonard" or + this = "Harrison" or + this = "Anthony" or + this = "Louis" or + this = "Milton" or + this = "Noah" or + this = "Cornelius" or + this = "Abdul" or + this = "Warren" or + this = "Harvey" or + this = "Dennis" or + this = "Wesley" or + this = "Sylvester" or + this = "Gilbert" or + this = "Sullivan" or + this = "Edmund" or + this = "Wilson" or + this = "Perry" or + this = "Matthew" or + this = "Simba" or + this = "Nala" or + this = "Rafiki" or + this = "Shenzi" or + this = "Ernest" or + this = "Gertrude" or + this = "Oscar" or + this = "Lilian" or + this = "Raymond" or + this = "Elgar" or + this = "Elmer" or + this = "Herbert" or + this = "Maude" or + this = "Mae" or + this = "Otto" or + this = "Edwin" or + this = "Ophelia" or + this = "Parsley" or + this = "Sage" or + this = "Rosemary" or + this = "Thyme" or + this = "Garfunkel" or + this = "King Basil" or + this = "Stephen" + } + + /** Gets the hair color of the person. If the person is bald, there is no result. */ + string getHairColor() { + this = "Ronil" and result = "black" + or + this = "Dina" and result = "black" + or + this = "Ravi" and result = "black" + or + this = "Bruce" and result = "brown" + or + this = "Jo" and result = "red" + or + this = "Aida" and result = "blond" + or + this = "Esme" and result = "blond" + or + this = "Fred" and result = "gray" + or + this = "Meera" and result = "brown" + or + this = "Maya" and result = "brown" + or + this = "Chad" and result = "brown" + or + this = "Tiana" and result = "black" + or + this = "Laura" and result = "blond" + or + this = "George" and result = "blond" + or + this = "Will" and result = "blond" + or + this = "Mary" and result = "blond" + or + this = "Almira" and result = "black" + or + this = "Susannah" and result = "blond" + or + this = "Rhoda" and result = "blond" + or + this = "Cynthia" and result = "gray" + or + this = "Eunice" and result = "white" + or + this = "Olive" and result = "brown" + or + this = "Virginia" and result = "brown" + or + this = "Angeline" and result = "red" + or + this = "Helen" and result = "white" + or + this = "Cornelia" and result = "gray" + or + this = "Harriet" and result = "white" + or + this = "Mahala" and result = "black" + or + this = "Abby" and result = "red" + or + this = "Margaret" and result = "brown" + or + this = "Deb" and result = "brown" + or + this = "Minerva" and result = "brown" + or + this = "Severus" and result = "black" + or + this = "Lavina" and result = "brown" + or + this = "Adeline" and result = "brown" + or + this = "Cath" and result = "brown" + or + this = "Elisa" and result = "brown" + or + this = "Lucretia" and result = "gray" + or + this = "Anne" and result = "black" + or + this = "Eleanor" and result = "brown" + or + this = "Joanna" and result = "brown" + or + this = "Adam" and result = "black" + or + this = "Agnes" and result = "black" + or + this = "Rosanna" and result = "gray" + or + this = "Clara" and result = "blond" + or + this = "Melissa" and result = "brown" + or + this = "Amy" and result = "brown" + or + this = "Isabel" and result = "black" + or + this = "Jemima" and result = "red" + or + this = "Cordelia" and result = "red" + or + this = "Melinda" and result = "gray" + or + this = "Delila" and result = "white" + or + this = "Jeremiah" and result = "gray" + or + this = "Hester" and result = "black" + or + this = "Walter" and result = "black" + or + this = "Aaron" and result = "gray" + or + this = "Reuben" and result = "gray" + or + this = "Eli" and result = "gray" + or + this = "Amos" and result = "white" + or + this = "Augustus" and result = "white" + or + this = "Theodore" and result = "white" + or + this = "Timothy" and result = "brown" + or + this = "Cyrus" and result = "brown" + or + this = "Horace" and result = "brown" + or + this = "Simon" and result = "brown" + or + this = "Asa" and result = "brown" + or + this = "Frank" and result = "brown" + or + this = "Nelson" and result = "black" + or + this = "Leonard" and result = "black" + or + this = "Harrison" and result = "black" + or + this = "Anthony" and result = "black" + or + this = "Louis" and result = "black" + or + this = "Milton" and result = "blond" + or + this = "Noah" and result = "blond" + or + this = "Cornelius" and result = "red" + or + this = "Abdul" and result = "brown" + or + this = "Warren" and result = "red" + or + this = "Harvey" and result = "blond" + or + this = "Dennis" and result = "blond" + or + this = "Wesley" and result = "brown" + or + this = "Sylvester" and result = "brown" + or + this = "Gilbert" and result = "brown" + or + this = "Sullivan" and result = "brown" + or + this = "Edmund" and result = "brown" + or + this = "Wilson" and result = "blond" + or + this = "Perry" and result = "black" + or + this = "Simba" and result = "brown" + or + this = "Nala" and result = "brown" + or + this = "Rafiki" and result = "red" + or + this = "Shenzi" and result = "gray" + or + this = "Ernest" and result = "blond" + or + this = "Gertrude" and result = "brown" + or + this = "Oscar" and result = "blond" + or + this = "Lilian" and result = "brown" + or + this = "Raymond" and result = "brown" + or + this = "Elgar" and result = "brown" + or + this = "Elmer" and result = "brown" + or + this = "Herbert" and result = "brown" + or + this = "Maude" and result = "brown" + or + this = "Mae" and result = "brown" + or + this = "Otto" and result = "black" + or + this = "Edwin" and result = "black" + or + this = "Ophelia" and result = "brown" + or + this = "Parsley" and result = "brown" + or + this = "Sage" and result = "brown" + or + this = "Rosemary" and result = "brown" + or + this = "Thyme" and result = "brown" + or + this = "Garfunkel" and result = "brown" + or + this = "King Basil" and result = "brown" + or + this = "Stephen" and result = "black" + or + this = "Stephen" and result = "gray" + } + + /** Gets the age of the person (in years). If the person is deceased, there is no result. */ + int getAge() { + this = "Ronil" and result = 21 + or + this = "Dina" and result = 53 + or + this = "Ravi" and result = 16 + or + this = "Bruce" and result = 35 + or + this = "Jo" and result = 47 + or + this = "Aida" and result = 26 + or + this = "Esme" and result = 25 + or + this = "Charlie" and result = 31 + or + this = "Fred" and result = 68 + or + this = "Meera" and result = 62 + or + this = "Maya" and result = 29 + or + this = "Chad" and result = 49 + or + this = "Tiana" and result = 18 + or + this = "Laura" and result = 2 + or + this = "George" and result = 3 + or + this = "Will" and result = 41 + or + this = "Mary" and result = 51 + or + this = "Almira" and result = 1 + or + this = "Susannah" and result = 97 + or + this = "Rhoda" and result = 39 + or + this = "Cynthia" and result = 89 + or + this = "Eunice" and result = 83 + or + this = "Olive" and result = 25 + or + this = "Virginia" and result = 52 + or + this = "Angeline" and result = 22 + or + this = "Helen" and result = 79 + or + this = "Cornelia" and result = 59 + or + this = "Harriet" and result = 57 + or + this = "Mahala" and result = 61 + or + this = "Abby" and result = 24 + or + this = "Margaret" and result = 59 + or + this = "Deb" and result = 31 + or + this = "Minerva" and result = 72 + or + this = "Severus" and result = 61 + or + this = "Lavina" and result = 33 + or + this = "Adeline" and result = 17 + or + this = "Cath" and result = 22 + or + this = "Elisa" and result = 9 + or + this = "Lucretia" and result = 56 + or + this = "Anne" and result = 11 + or + this = "Eleanor" and result = 80 + or + this = "Joanna" and result = 43 + or + this = "Adam" and result = 37 + or + this = "Agnes" and result = 47 + or + this = "Rosanna" and result = 61 + or + this = "Clara" and result = 31 + or + this = "Melissa" and result = 37 + or + this = "Amy" and result = 12 + or + this = "Isabel" and result = 6 + or + this = "Jemima" and result = 16 + or + this = "Cordelia" and result = 21 + or + this = "Melinda" and result = 55 + or + this = "Delila" and result = 66 + or + this = "Jeremiah" and result = 54 + or + this = "Elijah" and result = 42 + or + this = "Hester" and result = 68 + or + this = "Walter" and result = 66 + or + this = "Oliver" and result = 33 + or + this = "Hugh" and result = 51 + or + this = "Aaron" and result = 49 + or + this = "Reuben" and result = 58 + or + this = "Eli" and result = 70 + or + this = "Amos" and result = 65 + or + this = "Augustus" and result = 56 + or + this = "Theodore" and result = 69 + or + this = "Ira" and result = 1 + or + this = "Timothy" and result = 54 + or + this = "Cyrus" and result = 78 + or + this = "Horace" and result = 34 + or + this = "Simon" and result = 23 + or + this = "Asa" and result = 28 + or + this = "Frank" and result = 59 + or + this = "Nelson" and result = 38 + or + this = "Leonard" and result = 58 + or + this = "Harrison" and result = 7 + or + this = "Anthony" and result = 2 + or + this = "Louis" and result = 34 + or + this = "Milton" and result = 36 + or + this = "Noah" and result = 48 + or + this = "Cornelius" and result = 41 + or + this = "Abdul" and result = 67 + or + this = "Warren" and result = 47 + or + this = "Harvey" and result = 31 + or + this = "Dennis" and result = 39 + or + this = "Wesley" and result = 13 + or + this = "Sylvester" and result = 19 + or + this = "Gilbert" and result = 16 + or + this = "Sullivan" and result = 17 + or + this = "Edmund" and result = 29 + or + this = "Wilson" and result = 27 + or + this = "Perry" and result = 31 + or + this = "Matthew" and result = 55 + or + this = "Simba" and result = 8 + or + this = "Nala" and result = 7 + or + this = "Rafiki" and result = 76 + or + this = "Shenzi" and result = 67 + } + + /** Gets the height of the person (in cm). If the person is deceased, there is no result. */ + float getHeight() { + this = "Ronil" and result = 183.0 + or + this = "Dina" and result = 155.1 + or + this = "Ravi" and result = 175.2 + or + this = "Bruce" and result = 191.3 + or + this = "Jo" and result = 163.4 + or + this = "Aida" and result = 182.6 + or + this = "Esme" and result = 176.9 + or + this = "Charlie" and result = 189.7 + or + this = "Fred" and result = 179.4 + or + this = "Meera" and result = 160.1 + or + this = "Maya" and result = 153.0 + or + this = "Chad" and result = 168.5 + or + this = "Tiana" and result = 149.7 + or + this = "Laura" and result = 87.5 + or + this = "George" and result = 96.4 + or + this = "Will" and result = 167.1 + or + this = "Mary" and result = 159.8 + or + this = "Almira" and result = 62.1 + or + this = "Susannah" and result = 145.8 + or + this = "Rhoda" and result = 180.1 + or + this = "Cynthia" and result = 161.8 + or + this = "Eunice" and result = 153.2 + or + this = "Olive" and result = 179.9 + or + this = "Virginia" and result = 165.1 + or + this = "Angeline" and result = 172.3 + or + this = "Helen" and result = 163.1 + or + this = "Cornelia" and result = 160.8 + or + this = "Harriet" and result = 163.2 + or + this = "Mahala" and result = 157.7 + or + this = "Abby" and result = 174.5 + or + this = "Margaret" and result = 165.6 + or + this = "Deb" and result = 171.6 + or + this = "Minerva" and result = 168.7 + or + this = "Severus" and result = 188.8 + or + this = "Lavina" and result = 155.1 + or + this = "Adeline" and result = 165.5 + or + this = "Cath" and result = 147.8 + or + this = "Elisa" and result = 129.4 + or + this = "Lucretia" and result = 153.6 + or + this = "Anne" and result = 140.4 + or + this = "Eleanor" and result = 151.1 + or + this = "Joanna" and result = 167.2 + or + this = "Adam" and result = 155.5 + or + this = "Agnes" and result = 156.8 + or + this = "Rosanna" and result = 162.4 + or + this = "Clara" and result = 158.6 + or + this = "Melissa" and result = 182.3 + or + this = "Amy" and result = 147.1 + or + this = "Isabel" and result = 121.4 + or + this = "Jemima" and result = 149.8 + or + this = "Cordelia" and result = 151.7 + or + this = "Melinda" and result = 154.4 + or + this = "Delila" and result = 163.4 + or + this = "Jeremiah" and result = 167.5 + or + this = "Elijah" and result = 184.5 + or + this = "Hester" and result = 152.7 + or + this = "Walter" and result = 159.6 + or + this = "Oliver" and result = 192.4 + or + this = "Hugh" and result = 173.1 + or + this = "Aaron" and result = 176.6 + or + this = "Reuben" and result = 169.9 + or + this = "Eli" and result = 180.4 + or + this = "Amos" and result = 167.4 + or + this = "Augustus" and result = 156.5 + or + this = "Theodore" and result = 176.6 + or + this = "Ira" and result = 54.1 + or + this = "Timothy" and result = 172.2 + or + this = "Cyrus" and result = 157.9 + or + this = "Horace" and result = 169.3 + or + this = "Simon" and result = 157.1 + or + this = "Asa" and result = 149.4 + or + this = "Frank" and result = 167.2 + or + this = "Nelson" and result = 173.0 + or + this = "Leonard" and result = 172.0 + or + this = "Harrison" and result = 126.0 + or + this = "Anthony" and result = 98.4 + or + this = "Louis" and result = 186.8 + or + this = "Milton" and result = 157.8 + or + this = "Noah" and result = 190.5 + or + this = "Cornelius" and result = 183.1 + or + this = "Abdul" and result = 182.0 + or + this = "Warren" and result = 175.0 + or + this = "Harvey" and result = 169.3 + or + this = "Dennis" and result = 160.4 + or + this = "Wesley" and result = 139.8 + or + this = "Sylvester" and result = 188.2 + or + this = "Gilbert" and result = 177.6 + or + this = "Sullivan" and result = 168.3 + or + this = "Edmund" and result = 159.2 + or + this = "Wilson" and result = 167.6 + or + this = "Perry" and result = 189.1 + or + this = "Matthew" and result = 167.2 + or + this = "Simba" and result = 140.1 + or + this = "Nala" and result = 138.0 + or + this = "Rafiki" and result = 139.3 + or + this = "Shenzi" and result = 171.1 + } + + /** Gets the location of the person's home ("north", "south", "east", or "west"). If the person is deceased, there is no result. */ + string getLocation() { + this = "Ronil" and result = "north" + or + this = "Dina" and result = "north" + or + this = "Ravi" and result = "north" + or + this = "Bruce" and result = "south" + or + this = "Jo" and result = "west" + or + this = "Aida" and result = "east" + or + this = "Esme" and result = "east" + or + this = "Charlie" and result = "south" + or + this = "Fred" and result = "west" + or + this = "Meera" and result = "south" + or + this = "Maya" and result = "south" + or + this = "Chad" and result = "south" + or + this = "Tiana" and result = "west" + or + this = "Laura" and result = "south" + or + this = "George" and result = "south" + or + this = "Will" and result = "south" + or + this = "Mary" and result = "south" + or + this = "Almira" and result = "south" + or + this = "Susannah" and result = "north" + or + this = "Rhoda" and result = "north" + or + this = "Cynthia" and result = "north" + or + this = "Eunice" and result = "north" + or + this = "Olive" and result = "west" + or + this = "Virginia" and result = "west" + or + this = "Angeline" and result = "west" + or + this = "Helen" and result = "west" + or + this = "Cornelia" and result = "east" + or + this = "Harriet" and result = "east" + or + this = "Mahala" and result = "east" + or + this = "Abby" and result = "east" + or + this = "Margaret" and result = "east" + or + this = "Deb" and result = "east" + or + this = "Minerva" and result = "south" + or + this = "Severus" and result = "north" + or + this = "Lavina" and result = "east" + or + this = "Adeline" and result = "west" + or + this = "Cath" and result = "east" + or + this = "Elisa" and result = "east" + or + this = "Lucretia" and result = "north" + or + this = "Anne" and result = "north" + or + this = "Eleanor" and result = "south" + or + this = "Joanna" and result = "south" + or + this = "Adam" and result = "east" + or + this = "Agnes" and result = "east" + or + this = "Rosanna" and result = "east" + or + this = "Clara" and result = "east" + or + this = "Melissa" and result = "west" + or + this = "Amy" and result = "west" + or + this = "Isabel" and result = "west" + or + this = "Jemima" and result = "west" + or + this = "Cordelia" and result = "west" + or + this = "Melinda" and result = "west" + or + this = "Delila" and result = "south" + or + this = "Jeremiah" and result = "north" + or + this = "Elijah" and result = "north" + or + this = "Hester" and result = "east" + or + this = "Walter" and result = "east" + or + this = "Oliver" and result = "east" + or + this = "Hugh" and result = "south" + or + this = "Aaron" and result = "south" + or + this = "Reuben" and result = "west" + or + this = "Eli" and result = "west" + or + this = "Amos" and result = "east" + or + this = "Augustus" and result = "south" + or + this = "Theodore" and result = "west" + or + this = "Ira" and result = "south" + or + this = "Timothy" and result = "north" + or + this = "Cyrus" and result = "north" + or + this = "Horace" and result = "east" + or + this = "Simon" and result = "east" + or + this = "Asa" and result = "east" + or + this = "Frank" and result = "west" + or + this = "Nelson" and result = "west" + or + this = "Leonard" and result = "west" + or + this = "Harrison" and result = "north" + or + this = "Anthony" and result = "north" + or + this = "Louis" and result = "north" + or + this = "Milton" and result = "south" + or + this = "Noah" and result = "south" + or + this = "Cornelius" and result = "east" + or + this = "Abdul" and result = "east" + or + this = "Warren" and result = "west" + or + this = "Harvey" and result = "west" + or + this = "Dennis" and result = "west" + or + this = "Wesley" and result = "west" + or + this = "Sylvester" and result = "south" + or + this = "Gilbert" and result = "east" + or + this = "Sullivan" and result = "east" + or + this = "Edmund" and result = "north" + or + this = "Wilson" and result = "north" + or + this = "Perry" and result = "west" + or + this = "Matthew" and result = "east" + or + this = "Simba" and result = "south" + or + this = "Nala" and result = "south" + or + this = "Rafiki" and result = "north" + or + this = "Shenzi" and result = "west" + } + + /** Holds if the person is deceased. */ + predicate isDeceased() { + this = "Ernest" or + this = "Gertrude" or + this = "Oscar" or + this = "Lilian" or + this = "Edwin" or + this = "Raymond" or + this = "Elgar" or + this = "Elmer" or + this = "Herbert" or + this = "Maude" or + this = "Mae" or + this = "Otto" or + this = "Ophelia" or + this = "Parsley" or + this = "Sage" or + this = "Rosemary" or + this = "Thyme" or + this = "Garfunkel" or + this = "King Basil" + } + + /** Gets a parent of the person (alive or deceased). */ + Person getAParent() { + this = "Stephen" and result = "Edmund" + or + this = "Edmund" and result = "Augustus" + or + this = "Augustus" and result = "Stephen" + or + this = "Abby" and result = "Cornelia" + or + this = "Abby" and result = "Amos" + or + this = "Abdul" and result = "Susannah" + or + this = "Adam" and result = "Amos" + or + this = "Adeline" and result = "Melinda" + or + this = "Adeline" and result = "Frank" + or + this = "Agnes" and result = "Abdul" + or + this = "Aida" and result = "Agnes" + or + this = "Almira" and result = "Sylvester" + or + this = "Amos" and result = "Eunice" + or + this = "Amy" and result = "Noah" + or + this = "Amy" and result = "Chad" + or + this = "Angeline" and result = "Reuben" + or + this = "Angeline" and result = "Lucretia" + or + this = "Anne" and result = "Rhoda" + or + this = "Anne" and result = "Louis" + or + this = "Anthony" and result = "Lavina" + or + this = "Anthony" and result = "Asa" + or + this = "Asa" and result = "Cornelia" + or + this = "Cath" and result = "Harriet" + or + this = "Charlie" and result = "Matthew" + or + this = "Clara" and result = "Ernest" + or + this = "Cornelia" and result = "Cynthia" + or + this = "Cornelius" and result = "Eli" + or + this = "Deb" and result = "Margaret" + or + this = "Dennis" and result = "Fred" + or + this = "Eli" and result = "Susannah" + or + this = "Elijah" and result = "Delila" + or + this = "Elisa" and result = "Deb" + or + this = "Elisa" and result = "Horace" + or + this = "Esme" and result = "Margaret" + or + this = "Frank" and result = "Eleanor" + or + this = "Frank" and result = "Cyrus" + or + this = "George" and result = "Maya" + or + this = "George" and result = "Wilson" + or + this = "Gilbert" and result = "Cornelius" + or + this = "Harriet" and result = "Cynthia" + or + this = "Harrison" and result = "Louis" + or + this = "Harvey" and result = "Fred" + or + this = "Helen" and result = "Susannah" + or + this = "Hester" and result = "Edwin" + or + this = "Hugh" and result = "Cyrus" + or + this = "Hugh" and result = "Helen" + or + this = "Ira" and result = "Maya" + or + this = "Ira" and result = "Wilson" + or + this = "Isabel" and result = "Perry" + or + this = "Isabel" and result = "Harvey" + or + this = "Jemima" and result = "Melinda" + or + this = "Jemima" and result = "Frank" + or + this = "Ernest" and result = "Lilian" + or + this = "Ernest" and result = "Oscar" + or + this = "Gertrude" and result = "Ophelia" + or + this = "Gertrude" and result = "Raymond" + or + this = "Lilian" and result = "Elgar" + or + this = "Lilian" and result = "Mae" + or + this = "Raymond" and result = "Elgar" + or + this = "Raymond" and result = "Mae" + or + this = "Elmer" and result = "Ophelia" + or + this = "Elmer" and result = "Raymond" + or + this = "Herbert" and result = "Ophelia" + or + this = "Herbert" and result = "Raymond" + or + this = "Maude" and result = "Ophelia" + or + this = "Maude" and result = "Raymond" + or + this = "Otto" and result = "Elgar" + or + this = "Otto" and result = "Mae" + or + this = "Edwin" and result = "Otto" + or + this = "Parsley" and result = "Simon" + or + this = "Parsley" and result = "Garfunkel" + or + this = "Sage" and result = "Simon" + or + this = "Sage" and result = "Garfunkel" + or + this = "Rosemary" and result = "Simon" + or + this = "Rosemary" and result = "Garfunkel" + or + this = "Thyme" and result = "Simon" + or + this = "Thyme" and result = "Garfunkel" + or + this = "King Basil" and result = "Ophelia" + or + this = "King Basil" and result = "Raymond" + or + this = "Jo" and result = "Theodore" + or + this = "Joanna" and result = "Shenzi" + or + this = "Laura" and result = "Maya" + or + this = "Laura" and result = "Wilson" + or + this = "Lavina" and result = "Mahala" + or + this = "Lavina" and result = "Walter" + or + this = "Leonard" and result = "Cyrus" + or + this = "Leonard" and result = "Helen" + or + this = "Lucretia" and result = "Eleanor" + or + this = "Lucretia" and result = "Cyrus" + or + this = "Mahala" and result = "Eunice" + or + this = "Margaret" and result = "Cynthia" + or + this = "Matthew" and result = "Cyrus" + or + this = "Matthew" and result = "Helen" + or + this = "Maya" and result = "Meera" + or + this = "Melinda" and result = "Rafiki" + or + this = "Melissa" and result = "Mahala" + or + this = "Melissa" and result = "Walter" + or + this = "Nala" and result = "Bruce" + or + this = "Nelson" and result = "Mahala" + or + this = "Nelson" and result = "Walter" + or + this = "Noah" and result = "Eli" + or + this = "Olive" and result = "Reuben" + or + this = "Olive" and result = "Lucretia" + or + this = "Oliver" and result = "Matthew" + or + this = "Perry" and result = "Leonard" + or + this = "Ravi" and result = "Dina" + or + this = "Simba" and result = "Will" + or + this = "Simon" and result = "Margaret" + or + this = "Sullivan" and result = "Cornelius" + or + this = "Sylvester" and result = "Timothy" + or + this = "Theodore" and result = "Susannah" + or + this = "Tiana" and result = "Jo" + or + this = "Virginia" and result = "Helen" + or + this = "Warren" and result = "Shenzi" + or + this = "Wesley" and result = "Warren" + or + this = "Wesley" and result = "Jo" + or + this = "Will" and result = "Eli" + } + + /** Holds if the person is allowed in the region. Initially, all villagers are allowed in every region. */ + predicate isAllowedIn(string region) { + region = "north" or + region = "south" or + region = "east" or + region = "west" + } +} + +/** Returns a parent of the person. */ +Person parentOf(Person p) { result = p.getAParent() } diff --git a/javascript/ql/src/AlertSuppression.ql b/javascript/ql/src/AlertSuppression.ql index 43bfe3a020c..4366eb3ba7e 100644 --- a/javascript/ql/src/AlertSuppression.ql +++ b/javascript/ql/src/AlertSuppression.ql @@ -63,7 +63,7 @@ class SuppressionScope extends @locatable { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/javascript/ql/src/AngularJS/IncompatibleService.ql b/javascript/ql/src/AngularJS/IncompatibleService.ql index 7e6daafd72b..4fb290cd56d 100644 --- a/javascript/ql/src/AngularJS/IncompatibleService.ql +++ b/javascript/ql/src/AngularJS/IncompatibleService.ql @@ -71,13 +71,7 @@ predicate isCompatibleRequestedService(InjectableFunctionServiceRequest request, isRunMethod(request) or isControllerFunction(request) ) and - ( - kind = "value" or - kind = "service" or - kind = "factory" or - kind = "constant" or - kind = "provider-value" - ) + kind = ["value", "service", "factory", "constant", "provider-value"] or isControllerFunction(request) and kind = "controller-only" diff --git a/javascript/ql/src/Comments/CommentedOut.qll b/javascript/ql/src/Comments/CommentedOut.qll index 21cc27388ab..a0d00d70642 100644 --- a/javascript/ql/src/Comments/CommentedOut.qll +++ b/javascript/ql/src/Comments/CommentedOut.qll @@ -127,7 +127,7 @@ class CommentedOutCode extends Comment { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/javascript/ql/src/Declarations/UnusedProperty.ql b/javascript/ql/src/Declarations/UnusedProperty.ql index e9e38409bcb..19d43a09db2 100644 --- a/javascript/ql/src/Declarations/UnusedProperty.ql +++ b/javascript/ql/src/Declarations/UnusedProperty.ql @@ -10,7 +10,7 @@ import javascript import semmle.javascript.dataflow.LocalObjects -import UnusedVariable +import Declarations.UnusedVariable import UnusedParameter import Expressions.ExprHasNoEffect diff --git a/javascript/ql/src/Declarations/UnusedVariable.ql b/javascript/ql/src/Declarations/UnusedVariable.ql index 773672197a1..8fb405a7cf9 100644 --- a/javascript/ql/src/Declarations/UnusedVariable.ql +++ b/javascript/ql/src/Declarations/UnusedVariable.ql @@ -10,7 +10,7 @@ */ import javascript -import UnusedVariable +import Declarations.UnusedVariable /** * Holds if `v` is mentioned in a JSDoc comment in the same file, and that file diff --git a/javascript/ql/src/Expressions/ExprHasNoEffect.ql b/javascript/ql/src/Expressions/ExprHasNoEffect.ql index 5ef6be7ba14..f0cd3addcb6 100644 --- a/javascript/ql/src/Expressions/ExprHasNoEffect.ql +++ b/javascript/ql/src/Expressions/ExprHasNoEffect.ql @@ -13,7 +13,7 @@ */ import javascript -import ExprHasNoEffect +import Expressions.ExprHasNoEffect import semmle.javascript.RestrictedLocations from Expr e diff --git a/javascript/ql/src/Expressions/MisspelledIdentifier.ql b/javascript/ql/src/Expressions/MisspelledIdentifier.ql index 6eba0ede3a7..342f4072277 100644 --- a/javascript/ql/src/Expressions/MisspelledIdentifier.ql +++ b/javascript/ql/src/Expressions/MisspelledIdentifier.ql @@ -22,7 +22,7 @@ class IdentifierPart extends string { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/javascript/ql/src/Expressions/SelfAssignment.ql b/javascript/ql/src/Expressions/SelfAssignment.ql index ff324831cf6..6aab4c46bb2 100644 --- a/javascript/ql/src/Expressions/SelfAssignment.ql +++ b/javascript/ql/src/Expressions/SelfAssignment.ql @@ -12,7 +12,7 @@ */ import Clones -import DOMProperties +import Expressions.DOMProperties /** * Gets a description of expression `e`, which is assumed to be the left-hand diff --git a/javascript/ql/src/LanguageFeatures/EmptyArrayInit.ql b/javascript/ql/src/LanguageFeatures/EmptyArrayInit.ql index eaa9ffdc1fc..9c95ec9365d 100644 --- a/javascript/ql/src/LanguageFeatures/EmptyArrayInit.ql +++ b/javascript/ql/src/LanguageFeatures/EmptyArrayInit.ql @@ -29,7 +29,7 @@ class OmittedArrayElement extends ArrayExpr { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/javascript/ql/src/LanguageFeatures/SpuriousArguments.ql b/javascript/ql/src/LanguageFeatures/SpuriousArguments.ql index b2e86524e20..29f62648956 100644 --- a/javascript/ql/src/LanguageFeatures/SpuriousArguments.ql +++ b/javascript/ql/src/LanguageFeatures/SpuriousArguments.ql @@ -68,7 +68,7 @@ class SpuriousArguments extends Expr { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/javascript/ql/src/LanguageFeatures/UnusedIndexVariable.ql b/javascript/ql/src/LanguageFeatures/UnusedIndexVariable.ql index da3c1d4b4cf..ba39738a777 100644 --- a/javascript/ql/src/LanguageFeatures/UnusedIndexVariable.ql +++ b/javascript/ql/src/LanguageFeatures/UnusedIndexVariable.ql @@ -10,7 +10,7 @@ */ import javascript -import UnusedIndexVariable +import LanguageFeatures.UnusedIndexVariable from RelationalComparison rel, Variable idx, Variable v where unusedIndexVariable(rel, idx, v) diff --git a/javascript/ql/src/React/UnsupportedStateUpdateInLifecycleMethod.ql b/javascript/ql/src/React/UnsupportedStateUpdateInLifecycleMethod.ql index d1798ee51e8..bf47712e3e6 100644 --- a/javascript/ql/src/React/UnsupportedStateUpdateInLifecycleMethod.ql +++ b/javascript/ql/src/React/UnsupportedStateUpdateInLifecycleMethod.ql @@ -76,13 +76,11 @@ class StateUpdateVolatileMethod extends Function { // - componentsWillMount // - componentsDidMount exists(ReactComponent c | - methodName = "componentDidUnmount" or - methodName = "componentDidUpdate" or - methodName = "componentWillUpdate" or - methodName = "getDefaultProps" or - methodName = "getInitialState" or - methodName = "render" or - methodName = "shouldComponentUpdate" + methodName = + [ + "componentDidUnmount", "componentDidUpdate", "componentWillUpdate", "getDefaultProps", + "getInitialState", "render", "shouldComponentUpdate" + ] | this = c.getInstanceMethod(methodName) ) diff --git a/javascript/ql/src/Security/CWE-020/UselessRegExpCharacterEscape.ql b/javascript/ql/src/Security/CWE-020/UselessRegExpCharacterEscape.ql index 29933de7848..7bd17135e33 100644 --- a/javascript/ql/src/Security/CWE-020/UselessRegExpCharacterEscape.ql +++ b/javascript/ql/src/Security/CWE-020/UselessRegExpCharacterEscape.ql @@ -67,7 +67,7 @@ class RegExpPatternMistake extends TRegExpPatternMistake { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/javascript/ql/src/Security/CWE-078/IndirectCommandInjection.ql b/javascript/ql/src/Security/CWE-078/IndirectCommandInjection.ql index eb29b56cac3..7520a95ed9c 100644 --- a/javascript/ql/src/Security/CWE-078/IndirectCommandInjection.ql +++ b/javascript/ql/src/Security/CWE-078/IndirectCommandInjection.ql @@ -5,7 +5,7 @@ * command-line injection vulnerabilities. * @kind path-problem * @problem.severity warning - * @security-severity 9.8 + * @security-severity 6.3 * @precision medium * @id js/indirect-command-line-injection * @tags correctness diff --git a/javascript/ql/src/Security/CWE-078/ShellCommandInjectionFromEnvironment.ql b/javascript/ql/src/Security/CWE-078/ShellCommandInjectionFromEnvironment.ql index b4bd735d493..cad1039814c 100644 --- a/javascript/ql/src/Security/CWE-078/ShellCommandInjectionFromEnvironment.ql +++ b/javascript/ql/src/Security/CWE-078/ShellCommandInjectionFromEnvironment.ql @@ -4,7 +4,7 @@ * environment may cause subtle bugs or vulnerabilities. * @kind path-problem * @problem.severity warning - * @security-severity 9.8 + * @security-severity 6.3 * @precision high * @id js/shell-command-injection-from-environment * @tags correctness diff --git a/javascript/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.ql b/javascript/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.ql index b0b22a96704..e29a75c1163 100644 --- a/javascript/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.ql +++ b/javascript/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.ql @@ -4,7 +4,7 @@ * user to change the meaning of the command. * @kind path-problem * @problem.severity error - * @security-severity 9.8 + * @security-severity 6.3 * @precision high * @id js/shell-command-constructed-from-input * @tags correctness diff --git a/javascript/ql/src/Security/CWE-078/UselessUseOfCat.ql b/javascript/ql/src/Security/CWE-078/UselessUseOfCat.ql index fd29399546a..e0678b4142b 100644 --- a/javascript/ql/src/Security/CWE-078/UselessUseOfCat.ql +++ b/javascript/ql/src/Security/CWE-078/UselessUseOfCat.ql @@ -3,7 +3,7 @@ * @description Using the `cat` process to read a file is unnecessarily complex, inefficient, unportable, and can lead to subtle bugs, or even security vulnerabilities. * @kind problem * @problem.severity error - * @security-severity 9.8 + * @security-severity 6.3 * @precision high * @id js/unnecessary-use-of-cat * @tags correctness diff --git a/javascript/ql/src/Security/CWE-094/CodeInjection.ql b/javascript/ql/src/Security/CWE-094/CodeInjection.ql index 2934107f1bb..fbf39beaca4 100644 --- a/javascript/ql/src/Security/CWE-094/CodeInjection.ql +++ b/javascript/ql/src/Security/CWE-094/CodeInjection.ql @@ -4,11 +4,12 @@ * code execution. * @kind path-problem * @problem.severity error - * @security-severity 6.1 + * @security-severity 9.3 * @precision high * @id js/code-injection * @tags security * external/cwe/cwe-094 + * external/cwe/cwe-095 * external/cwe/cwe-079 * external/cwe/cwe-116 */ diff --git a/javascript/ql/src/Security/CWE-116/IncompleteSanitization.ql b/javascript/ql/src/Security/CWE-116/IncompleteSanitization.ql index 08bb355799a..4e1a66ab22c 100644 --- a/javascript/ql/src/Security/CWE-116/IncompleteSanitization.ql +++ b/javascript/ql/src/Security/CWE-116/IncompleteSanitization.ql @@ -79,14 +79,11 @@ predicate allBackslashesEscaped(DataFlow::Node nd) { or // flow through string methods exists(DataFlow::MethodCallNode mc, string m | - m = "replace" or - m = "replaceAll" or - m = "slice" or - m = "substr" or - m = "substring" or - m = "toLowerCase" or - m = "toUpperCase" or - m = "trim" + m = + [ + "replace", "replaceAll", "slice", "substr", "substring", "toLowerCase", "toUpperCase", + "trim" + ] | mc = nd and m = mc.getMethodName() and allBackslashesEscaped(mc.getReceiver()) ) diff --git a/javascript/ql/src/Security/CWE-134/TaintedFormatString.ql b/javascript/ql/src/Security/CWE-134/TaintedFormatString.ql index 25cb62bd9b1..06f44703a5d 100644 --- a/javascript/ql/src/Security/CWE-134/TaintedFormatString.ql +++ b/javascript/ql/src/Security/CWE-134/TaintedFormatString.ql @@ -3,7 +3,7 @@ * @description Using external input in format strings can lead to garbled output. * @kind path-problem * @problem.severity warning - * @security-severity 9.3 + * @security-severity 7.3 * @precision high * @id js/tainted-format-string * @tags security diff --git a/javascript/ql/src/Security/CWE-730/ServerCrash.ql b/javascript/ql/src/Security/CWE-730/ServerCrash.ql index 7c16287d48c..336cc2abf70 100644 --- a/javascript/ql/src/Security/CWE-730/ServerCrash.ql +++ b/javascript/ql/src/Security/CWE-730/ServerCrash.ql @@ -104,7 +104,7 @@ class AsyncSentinelCall extends DataFlow::CallNode { exists(DataFlow::FunctionNode node | node.getAstNode() = asyncCallee | // manual models exists(string memberName | - not "Sync" = memberName.suffix(memberName.length() - 4) and + not memberName.matches("%Sync") and this = NodeJSLib::FS::moduleMember(memberName).getACall() and node = this.getCallback([1 .. 2]) ) diff --git a/javascript/ql/src/Security/CWE-834/LoopBoundInjection.ql b/javascript/ql/src/Security/CWE-834/LoopBoundInjection.ql index 028835343c1..638829de15e 100644 --- a/javascript/ql/src/Security/CWE-834/LoopBoundInjection.ql +++ b/javascript/ql/src/Security/CWE-834/LoopBoundInjection.ql @@ -4,10 +4,11 @@ * property can cause indefinite looping. * @kind path-problem * @problem.severity warning - * @security-severity 6.5 + * @security-severity 7.5 * @id js/loop-bound-injection * @tags security * external/cwe/cwe-834 + * external/cwe/cwe-730 * @precision high */ diff --git a/javascript/ql/src/Security/CWE-912/HttpToFileAccess.ql b/javascript/ql/src/Security/CWE-912/HttpToFileAccess.ql index 9a8ac7b4b73..772297dda63 100644 --- a/javascript/ql/src/Security/CWE-912/HttpToFileAccess.ql +++ b/javascript/ql/src/Security/CWE-912/HttpToFileAccess.ql @@ -3,7 +3,7 @@ * @description Writing network data directly to the file system allows arbitrary file upload and might indicate a backdoor. * @kind path-problem * @problem.severity warning - * @security-severity 9.8 + * @security-severity 6.3 * @precision medium * @id js/http-to-file-access * @tags security diff --git a/javascript/ql/src/Statements/UseOfReturnlessFunction.ql b/javascript/ql/src/Statements/UseOfReturnlessFunction.ql index 117e22eb867..08928946eb5 100644 --- a/javascript/ql/src/Statements/UseOfReturnlessFunction.ql +++ b/javascript/ql/src/Statements/UseOfReturnlessFunction.ql @@ -111,16 +111,11 @@ predicate callToVoidFunction(DataFlow::CallNode call, Function func) { * and the callback is expected to return a value. */ predicate hasNonVoidCallbackMethod(string name) { - name = "every" or - name = "filter" or - name = "find" or - name = "findIndex" or - name = "flatMap" or - name = "map" or - name = "reduce" or - name = "reduceRight" or - name = "some" or - name = "sort" + name = + [ + "every", "filter", "find", "findIndex", "flatMap", "map", "reduce", "reduceRight", "some", + "sort" + ] } DataFlow::SourceNode array(DataFlow::TypeTracker t) { diff --git a/javascript/ql/src/experimental/poi/PoI.qll b/javascript/ql/src/experimental/poi/PoI.qll index 20eeeee9f79..c51909c5c9e 100644 --- a/javascript/ql/src/experimental/poi/PoI.qll +++ b/javascript/ql/src/experimental/poi/PoI.qll @@ -55,7 +55,7 @@ import javascript private import DataFlow -private import filters.ClassifyFiles +private import semmle.javascript.filters.ClassifyFiles private import semmle.javascript.RestrictedLocations /** diff --git a/javascript/ql/src/external/DefectFilter.qll b/javascript/ql/src/external/DefectFilter.qll index d35e41d9afd..40c9527e96d 100644 --- a/javascript/ql/src/external/DefectFilter.qll +++ b/javascript/ql/src/external/DefectFilter.qll @@ -8,7 +8,7 @@ import semmle.javascript.Files * column `startcolumn` of line `startline` to column `endcolumn` of line `endline` * in file `filepath`. * - * For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * For more information, see [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ external predicate defectResults( int id, string queryPath, string file, int startline, int startcol, int endline, int endcol, diff --git a/javascript/ql/src/external/MetricFilter.qll b/javascript/ql/src/external/MetricFilter.qll index 5edecac75d8..e27b733e4b9 100644 --- a/javascript/ql/src/external/MetricFilter.qll +++ b/javascript/ql/src/external/MetricFilter.qll @@ -8,7 +8,7 @@ import javascript * column `startcolumn` of line `startline` to column `endcolumn` of line `endline` * in file `filepath`. * - * For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * For more information, see [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ external predicate metricResults( int id, string queryPath, string file, int startline, int startcol, int endline, int endcol, diff --git a/javascript/ql/src/filters/ClassifyFiles.ql b/javascript/ql/src/filters/ClassifyFiles.ql index fa7aad41ab4..9100485a6d6 100644 --- a/javascript/ql/src/filters/ClassifyFiles.ql +++ b/javascript/ql/src/filters/ClassifyFiles.ql @@ -8,7 +8,7 @@ */ import javascript -import ClassifyFiles +import semmle.javascript.filters.ClassifyFiles from File f, string category where classify(f, category) diff --git a/javascript/ql/src/meta/Consistency.ql b/javascript/ql/src/meta/Consistency.ql index b87c256f609..3e2ec0c412a 100644 --- a/javascript/ql/src/meta/Consistency.ql +++ b/javascript/ql/src/meta/Consistency.ql @@ -37,22 +37,12 @@ predicate exprWithoutEnclosingStmt(Expr e) { * `"3 results for toString()"`. */ predicate uniqueness_error(int number, string what, string problem) { - ( - what = "toString" or - what = "getLocation" or - what = "getTopLevel" or - what = "getEnclosingStmt" or - what = "getContainer" or - what = "getEnclosingContainer" or - what = "getEntry" or - what = "getExit" or - what = "getFirstControlFlowNode" or - what = "getOuterScope" or - what = "getScopeElement" or - what = "getBaseName" or - what = "getOperator" or - what = "getTest" - ) and + what = + [ + "toString", "getLocation", "getTopLevel", "getEnclosingStmt", "getContainer", + "getEnclosingContainer", "getEntry", "getExit", "getFirstControlFlowNode", "getOuterScope", + "getScopeElement", "getBaseName", "getOperator", "getTest" + ] and ( number = 0 and problem = "no results for " + what + "()" or diff --git a/javascript/ql/src/qlpack.yml b/javascript/ql/src/qlpack.yml index 51ae6649bb3..e69cb686fe0 100644 --- a/javascript/ql/src/qlpack.yml +++ b/javascript/ql/src/qlpack.yml @@ -2,6 +2,7 @@ name: codeql/javascript-queries version: 0.0.3 suites: codeql-suites extractor: javascript +defaultSuiteFile: codeql-suites/javascript-code-scanning.qls dependencies: codeql/javascript-all: "*" codeql/suite-helpers: "*" diff --git a/javascript/ql/test/library-tests/PackageExports/notPublic.ts b/javascript/ql/test/library-tests/PackageExports/notPublic.ts new file mode 100644 index 00000000000..b5ca23574b1 --- /dev/null +++ b/javascript/ql/test/library-tests/PackageExports/notPublic.ts @@ -0,0 +1,11 @@ +export class PublicClass { + protected constructor(p) {} + private privateMethod(p) {} + protected protectedMethod(p) {} + _kindaPrivateMethod(p) {} + $kindaPrivateMethod(p) {} + #esPrivateMethod(p) {} + + _kindaPrivateFieldMethod = (p) => {}; + private privateFieldMethod = (p) => {}; +} diff --git a/javascript/ql/test/library-tests/PackageExports/tests.expected b/javascript/ql/test/library-tests/PackageExports/tests.expected index 1d46e149521..e4ed532c708 100644 --- a/javascript/ql/test/library-tests/PackageExports/tests.expected +++ b/javascript/ql/test/library-tests/PackageExports/tests.expected @@ -15,3 +15,4 @@ getAnExportedValue | lib1/reexport/a.js:1:1:3:1 | | reexported | lib1/reexport/a.js:2:17:2:40 | functio ... ed() {} | | lib1/reexport/b.js:1:1:6:1 | | base | lib1/reexport/b.js:4:11:4:28 | function base() {} | | lib1/reexport/b.js:1:1:6:1 | | reexported | lib1/reexport/a.js:2:17:2:40 | functio ... ed() {} | +| notPublic.ts:1:1:12:0 | | PublicClass | notPublic.ts:1:8:11:1 | class P ... > {};\\n} | diff --git a/javascript/ql/test/library-tests/SensitiveActions/tests.ql b/javascript/ql/test/library-tests/SensitiveActions/tests.ql index 6413443d5f0..7b6e1909267 100644 --- a/javascript/ql/test/library-tests/SensitiveActions/tests.ql +++ b/javascript/ql/test/library-tests/SensitiveActions/tests.ql @@ -4,15 +4,11 @@ import semmle.javascript.security.SensitiveActions query predicate cleartextPasswordExpr(CleartextPasswordExpr e) { any() } string getASamplePassword() { - result = "abcdefgh" or - result = "sOKY6ccizpmvF*32so%Q" or - result = "XXXXXXXX" or - result = "example_password" or - result = "change_me" or - result = "" or - result = "insert-auth-from-gui" or - result = "admin" or - result = "root" + result = + [ + "abcdefgh", "sOKY6ccizpmvF*32so%Q", "XXXXXXXX", "example_password", "change_me", "", + "insert-auth-from-gui", "admin", "root" + ] } query predicate dummyPasswords(string password, boolean isDummy) { diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected index a38b8cb37fe..dbf74f78d8f 100644 --- a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected +++ b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected @@ -362,6 +362,7 @@ | regexplib/uri.js:29:2:29:45 | ((http\\:\\/\\/\|https\\:\\/\\/\|ftp\\:\\/\\/)\|(www.))+ | Strings with many repetitions of 'wwwa' can start matching anywhere after the start of the preceeding ((http\\:\\/\\/\|https\\:\\/\\/\|ftp\\:\\/\\/)\|(www.))+(([a-zA-Z0-9\\.-]+\\.[a-zA-Z]{2,4})\|([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}))(\\/[a-zA-Z0-9%:/-_\\?\\.'~]*)? | | regexplib/uri.js:29:48:29:62 | [a-zA-Z0-9\\.-]+ | Strings starting with 'wwwa' and with many repetitions of 'www--' can start matching anywhere after the start of the preceeding ((http\\:\\/\\/\|https\\:\\/\\/\|ftp\\:\\/\\/)\|(www.))+ | | regexplib/uri.js:31:65:31:69 | [^<]+ | Strings starting with 'href=! >' and with many repetitions of 'href=! >;' can start matching anywhere after the start of the preceeding href\\s*=\\s*(?:(?:\\"(?[^\\"]*)\\")\|(?[^\\s*] ))>(?[^<]+)<\\/\\w> | +| regexplib/uri.js:34:3:34:9 | [^\\=&]+ | Strings with many repetitions of '%' can start matching anywhere after the start of the preceeding ([^\\=&]+)(?<!param1\|param2\|param3)\\=([^\\=&]+)(&)? | | regexplib/uri.js:36:40:36:42 | \\d* | Strings starting with '$1' and with many repetitions of '1' can start matching anywhere after the start of the preceeding [1-9]+ | | regexplib/uri.js:38:20:38:28 | [a-z0-9]+ | Strings starting with 'a' and with many repetitions of '0' can start matching anywhere after the start of the preceeding [a-z]+ | | regexplib/uri.js:38:35:38:40 | [a-z]+ | Strings starting with 'a.' and with many repetitions of 'aa' can start matching anywhere after the start of the preceeding [a-z0-9]+ | @@ -369,8 +370,10 @@ | regexplib/uri.js:39:7:39:9 | .*? | Strings starting with '<a' and with many repetitions of ' ' can start matching anywhere after the start of the preceeding \\s* | | regexplib/uri.js:39:43:39:45 | .*? | Strings with many repetitions of '>a' can start matching anywhere after the start of the preceeding .*? | | regexplib/uri.js:41:16:41:31 | [a-zA-Z0-9\\-\\.]+ | Strings starting with '0' and with many repetitions of '00' can start matching anywhere after the start of the preceeding [a-zA-Z0-9]+ | +| regexplib/uri.js:44:2:44:4 | .*? | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding .*?$(?<!\\.aspx) | | regexplib/uri.js:47:31:47:36 | [\\w-]+ | Strings with many repetitions of '-' can start matching anywhere after the start of the preceeding [\\w-\\s]* | | regexplib/uri.js:51:51:51:53 | \\S+ | Strings with many repetitions of '!@' can start matching anywhere after the start of the preceeding \\S+ | +| regexplib/uri.js:53:3:53:9 | [^\\=&]+ | Strings with many repetitions of '%' can start matching anywhere after the start of the preceeding ([^\\=&]+)(?<!param1\|param2\|param3)\\=([^\\=&]+)(&)? | | regexplib/uri.js:54:40:54:42 | \\d* | Strings starting with '$1' and with many repetitions of '1' can start matching anywhere after the start of the preceeding [1-9]+ | | regexplib/uri.js:55:20:55:28 | [a-z0-9]+ | Strings starting with 'a' and with many repetitions of '0' can start matching anywhere after the start of the preceeding [a-z]+ | | regexplib/uri.js:55:35:55:40 | [a-z]+ | Strings starting with 'a.' and with many repetitions of 'aa' can start matching anywhere after the start of the preceeding [a-z0-9]+ | @@ -380,6 +383,7 @@ | regexplib/uri.js:64:31:64:36 | [\\w-]+ | Strings with many repetitions of '-' can start matching anywhere after the start of the preceeding [\\w-\\s]* | | regexplib/uri.js:70:16:70:31 | [a-zA-Z0-9\\-\\.]+ | Strings starting with '0' and with many repetitions of '00' can start matching anywhere after the start of the preceeding [a-zA-Z0-9]+ | | regexplib/uri.js:71:75:71:89 | [^\\/\\\\:*?"<>\|]+ | Strings starting with 'A:\\\\!.' and with many repetitions of '!.' can start matching anywhere after the start of the preceeding [^\\/\\\\:*?"<>\|]+ | +| regexplib/uri.js:73:2:73:4 | .*? | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding .*?$(?<!\\.aspx) | | tst.js:14:15:14:16 | .* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding (.*,)+ | | tst.js:23:21:23:28 | [\\s\\S]*? | Strings starting with '(*' and with many repetitions of '(*a' can start matching anywhere after the start of the preceeding [\\s\\S]*? | | tst.js:23:33:23:40 | [\\s\\S]*? | Strings starting with '(*(*' and with many repetitions of '(*' can start matching anywhere after the start of the preceeding [\\s\\S]*? | @@ -507,3 +511,4 @@ | tst.js:382:14:382:23 | (foo\|FOO)* | Strings with many repetitions of 'foo' can start matching anywhere after the start of the preceeding (foo\|FOO)*bar | | tst.js:384:15:384:26 | ([AB]\|[ab])* | Strings with many repetitions of 'A' can start matching anywhere after the start of the preceeding ([AB]\|[ab])*C | | tst.js:385:14:385:25 | ([DE]\|[de])* | Strings with many repetitions of 'd' can start matching anywhere after the start of the preceeding ([DE]\|[de])*F | +| tst.js:388:14:388:20 | (a\|aa)* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding (a\|aa)*$ | diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/ReDoS.expected b/javascript/ql/test/query-tests/Performance/ReDoS/ReDoS.expected index fe23342eb02..d7155b3711f 100644 --- a/javascript/ql/test/query-tests/Performance/ReDoS/ReDoS.expected +++ b/javascript/ql/test/query-tests/Performance/ReDoS/ReDoS.expected @@ -24,6 +24,7 @@ | regexplib/address.js:75:220:75:222 | \\w+ | This part of the regular expression may cause exponential backtracking on strings starting with 'C/O ' and containing many repetitions of 'a'. | | regexplib/address.js:75:616:75:618 | \\w+ | This part of the regular expression may cause exponential backtracking on strings starting with '9 a C/O ' and containing many repetitions of 'a'. | | regexplib/address.js:75:803:75:811 | [A-Za-z]+ | This part of the regular expression may cause exponential backtracking on strings starting with '9 a ' and containing many repetitions of 'A'. | +| regexplib/dates.js:27:341:27:348 | [^\\(\\)]* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '''. | | regexplib/email.js:1:16:1:22 | [-.\\w]* | This part of the regular expression may cause exponential backtracking on strings starting with '0' and containing many repetitions of '0'. | | regexplib/email.js:5:24:5:35 | [a-zA-Z0-9]+ | This part of the regular expression may cause exponential backtracking on strings starting with '0' and containing many repetitions of '0'. | | regexplib/email.js:5:63:5:74 | [a-zA-Z0-9]+ | This part of the regular expression may cause exponential backtracking on strings starting with '0@0' and containing many repetitions of '0'. | @@ -180,3 +181,5 @@ | tst.js:379:16:379:22 | [\\s\\S]* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | | tst.js:382:14:382:23 | (foo\|FOO)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'foo'. | | tst.js:385:14:385:25 | ([DE]\|[de])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'd'. | +| tst.js:387:27:387:33 | (a\|aa)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'aa'. | +| tst.js:388:14:388:20 | (a\|aa)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'aa'. | diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/tst.js b/javascript/ql/test/query-tests/Performance/ReDoS/tst.js index 1ab557520cb..6f3f8b1cd22 100644 --- a/javascript/ql/test/query-tests/Performance/ReDoS/tst.js +++ b/javascript/ql/test/query-tests/Performance/ReDoS/tst.js @@ -383,3 +383,6 @@ var bad91 = /(foo|FOO)*bar/i; var good47 = /([AB]|[ab])*C/; var bad92 = /([DE]|[de])*F/i; + +var bad93 = /(?<=^v?|\sv?)(a|aa)*$/; +var bad94 = /(a|aa)*$/; \ No newline at end of file diff --git a/javascript/ql/test/testUtilities/ConsistencyChecking.qll b/javascript/ql/test/testUtilities/ConsistencyChecking.qll index cd02a998649..f63eb933ff6 100644 --- a/javascript/ql/test/testUtilities/ConsistencyChecking.qll +++ b/javascript/ql/test/testUtilities/ConsistencyChecking.qll @@ -62,7 +62,7 @@ private class AssertionComment extends LineComment { /** * Holds if a consistency issue is expected at this location. */ - predicate expectConsistencyError() { getText().matches(["%[INCONSISTENCY]%"]) } + predicate expectConsistencyError() { getText().matches("%[INCONSISTENCY]%") } } private DataFlow::Node getASink() { exists(DataFlow::Configuration cfg | cfg.hasFlow(_, result)) } diff --git a/misc/scripts/generate-code-scanning-query-list.py b/misc/scripts/generate-code-scanning-query-list.py index dfb27e6e741..052655a5deb 100644 --- a/misc/scripts/generate-code-scanning-query-list.py +++ b/misc/scripts/generate-code-scanning-query-list.py @@ -1,6 +1,7 @@ import subprocess import json import csv +import shutil import sys import os import argparse @@ -27,9 +28,42 @@ arguments = parser.parse_args() assert hasattr(arguments, "ignore_missing_query_packs") # Define which languages and query packs to consider -languages = [ "cpp", "csharp", "go", "java", "javascript", "python"] +languages = [ "cpp", "csharp", "go", "java", "javascript", "python", "ruby"] packs = [ "code-scanning", "security-and-quality", "security-extended" ] +class CodeQL: + def __init__(self): + pass + + def __enter__(self): + self.proc = subprocess.Popen(['codeql', 'execute','cli-server'], + executable=shutil.which('codeql'), + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=sys.stderr, + env=os.environ.copy(), + ) + return self + def __exit__(self, type, value, tb): + self.proc.stdin.write(b'["shutdown"]\0') + self.proc.stdin.close() + try: + self.proc.wait(5) + except: + self.proc.kill() + + def command(self, args): + data = json.dumps(args) + data_bytes = data.encode('utf-8') + self.proc.stdin.write(data_bytes) + self.proc.stdin.write(b'\0') + self.proc.stdin.flush() + res = b'' + while True: + b = self.proc.stdout.read(1) + if b == b'\0': + return res.decode('utf-8') + res += b def prefix_repo_nwo(filename): """ @@ -98,73 +132,74 @@ except Exception as e: print("Error: couldn't invoke 'git'. Is it on the path? Aborting.", file=sys.stderr) raise e -try: # Check for `codeql` on path - subprocess_run(["codeql","--version"]) -except Exception as e: - print("Error: couldn't invoke CodeQL CLI 'codeql'. Is it on the path? Aborting.", file=sys.stderr) - raise e - -# Define CodeQL search path so it'll find the CodeQL repositories: -# - anywhere in the current Git clone (including current working directory) -# - the 'codeql' subdirectory of the cwd -# -# (and assumes the codeql-go repo is in a similar location) -codeql_search_path = "./codeql:./codeql-go:." # will be extended further down - -# Extend CodeQL search path by detecting root of the current Git repo (if any). This means that you -# can run this script from any location within the CodeQL git repository. -try: - git_toplevel_dir = subprocess_run(["git","rev-parse","--show-toplevel"]) - - # Current working directory is in a Git repo. Add it to the search path, just in case it's the CodeQL repo - git_toplevel_dir = git_toplevel_dir.stdout.strip() - codeql_search_path += ":" + git_toplevel_dir + ":" + git_toplevel_dir + "/../codeql-go" -except: - # git rev-parse --show-toplevel exited with non-zero exit code. We're not in a Git repo - pass - -# Create CSV writer and write CSV header to stdout -csvwriter = csv.writer(sys.stdout) -csvwriter.writerow([ - "Query filename", "Suite", "Query name", "Query ID", - "Kind", "Severity", "Precision", "Tags" -]) - -# Iterate over all languages and packs, and resolve which queries are part of those packs -for lang in languages: - for pack in packs: - # Get absolute paths to queries in this pack by using 'codeql resolve queries' - try: - queries_subp = subprocess_run(["codeql","resolve","queries","--search-path", codeql_search_path, "%s-%s.qls" % (lang, pack)]) +with CodeQL() as codeql: + try: # Check for `codeql` on path + codeql.command(["--version"]) except Exception as e: - # Resolving queries might go wrong if the github/codeql and github/codeql-go repositories are not - # on the search path. - level = "Warning" if arguments.ignore_missing_query_packs else "Error" - print( - "%s: couldn't find query pack '%s' for language '%s'. Do you have the right repositories in the right places (search path: '%s')?" % (level, pack, lang, codeql_search_path), - file=sys.stderr - ) - if arguments.ignore_missing_query_packs: - continue - else: - sys.exit("You can use '--ignore-missing-query-packs' to ignore this error") + print("Error: couldn't invoke CodeQL CLI 'codeql'. Is it on the path? Aborting.", file=sys.stderr) + raise e - # Investigate metadata for every query by using 'codeql resolve metadata' - for queryfile in queries_subp.stdout.strip().split("\n"): - query_metadata_json = subprocess_run(["codeql","resolve","metadata",queryfile]).stdout.strip() + # Define CodeQL search path so it'll find the CodeQL repositories: + # - anywhere in the current Git clone (including current working directory) + # - the 'codeql' subdirectory of the cwd + # + # (and assumes the codeql-go repo is in a similar location) + codeql_search_path = "./codeql:./codeql-go:." # will be extended further down - # Turn an absolute path to a query file into an nwo-prefixed path (e.g. github/codeql/java/ql/src/....) - queryfile_nwo = prefix_repo_nwo(queryfile) + # Extend CodeQL search path by detecting root of the current Git repo (if any). This means that you + # can run this script from any location within the CodeQL git repository. + try: + git_toplevel_dir = subprocess_run(["git","rev-parse","--show-toplevel"]) - meta = json.loads(query_metadata_json) + # Current working directory is in a Git repo. Add it to the search path, just in case it's the CodeQL repo + git_toplevel_dir = git_toplevel_dir.stdout.strip() + codeql_search_path += ":" + git_toplevel_dir + ":" + git_toplevel_dir + "/../codeql-go" + except: + # git rev-parse --show-toplevel exited with non-zero exit code. We're not in a Git repo + pass - # Python's CSV writer will automatically quote fields if necessary - csvwriter.writerow([ - queryfile_nwo, pack, - get_query_metadata('name', meta, queryfile_nwo), - get_query_metadata('id', meta, queryfile_nwo), - get_query_metadata('kind', meta, queryfile_nwo), - get_query_metadata('problem.severity', meta, queryfile_nwo), - get_query_metadata('precision', meta, queryfile_nwo), - get_query_metadata('tags', meta, queryfile_nwo) - ]) + # Create CSV writer and write CSV header to stdout + csvwriter = csv.writer(sys.stdout) + csvwriter.writerow([ + "Query filename", "Suite", "Query name", "Query ID", + "Kind", "Severity", "Precision", "Tags" + ]) + + # Iterate over all languages and packs, and resolve which queries are part of those packs + for lang in languages: + for pack in packs: + # Get absolute paths to queries in this pack by using 'codeql resolve queries' + try: + queries_subp = codeql.command(["resolve","queries","--search-path", codeql_search_path, "%s-%s.qls" % (lang, pack)]) + except Exception as e: + # Resolving queries might go wrong if the github/codeql and github/codeql-go repositories are not + # on the search path. + level = "Warning" if arguments.ignore_missing_query_packs else "Error" + print( + "%s: couldn't find query pack '%s' for language '%s'. Do you have the right repositories in the right places (search path: '%s')?" % (level, pack, lang, codeql_search_path), + file=sys.stderr + ) + if arguments.ignore_missing_query_packs: + continue + else: + sys.exit("You can use '--ignore-missing-query-packs' to ignore this error") + + # Investigate metadata for every query by using 'codeql resolve metadata' + for queryfile in queries_subp.strip().split("\n"): + query_metadata_json = codeql.command(["resolve","metadata",queryfile]).strip() + + # Turn an absolute path to a query file into an nwo-prefixed path (e.g. github/codeql/java/ql/src/....) + queryfile_nwo = prefix_repo_nwo(queryfile) + + meta = json.loads(query_metadata_json) + + # Python's CSV writer will automatically quote fields if necessary + csvwriter.writerow([ + queryfile_nwo, pack, + get_query_metadata('name', meta, queryfile_nwo), + get_query_metadata('id', meta, queryfile_nwo), + get_query_metadata('kind', meta, queryfile_nwo), + get_query_metadata('problem.severity', meta, queryfile_nwo), + get_query_metadata('precision', meta, queryfile_nwo), + get_query_metadata('tags', meta, queryfile_nwo) + ]) diff --git a/misc/scripts/library-coverage/comment-pr.py b/misc/scripts/library-coverage/comment-pr.py index 546e47f6519..28e3855abb2 100644 --- a/misc/scripts/library-coverage/comment-pr.py +++ b/misc/scripts/library-coverage/comment-pr.py @@ -25,16 +25,13 @@ def get_comment_text(output_file, repo, run_id): comment = comment_first_line + \ f"The generated reports are available in the [artifacts of this workflow run](https://github.com/{repo}/actions/runs/{run_id}). " + \ - "The differences will be picked up by the nightly job after the PR gets merged. " + "The differences will be picked up by the nightly job after the PR gets merged.\n\n" - if size < 2000: - print("There's a small change in the CSV framework coverage reports") - comment += "The following differences were found: \n\n" - with open(output_file, 'r') as file: - comment += file.read() - else: - print("There's a large change in the CSV framework coverage reports") - comment += f"The differences can be found in the {comparison_artifact_name} [artifact of this workflow run](https://github.com/{repo}/actions/runs/{run_id})." + comment += "<details><summary>Click to show differences in coverage</summary>\n\n" + with open(output_file, 'r') as file: + comment += file.read() + + comment += "</details>\n" return comment diff --git a/python/change-notes/2021-09-14-promote-regex-injection.md b/python/change-notes/2021-09-14-promote-regex-injection.md new file mode 100644 index 00000000000..0141251dea8 --- /dev/null +++ b/python/change-notes/2021-09-14-promote-regex-injection.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The query "Regular expression injection" (`py/regex-injection`) has been promoted from experimental to the main query pack. Its results will now appear by default. This query was originally [submitted as an experimental query by @jorgectf](https://github.com/github/codeql/pull/5442). diff --git a/python/change-notes/2021-10-08-add-dataflow-for-boolean-expressions.md b/python/change-notes/2021-10-08-add-dataflow-for-boolean-expressions.md new file mode 100644 index 00000000000..2c6a39f481f --- /dev/null +++ b/python/change-notes/2021-10-08-add-dataflow-for-boolean-expressions.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* Added data-flow from both `x` and `y` to `x or y` and `x and y`, as a slight over-approximation of what is described in the + [Python Language Reference](https://docs.python.org/3/reference/expressions.html#boolean-operations). diff --git a/python/change-notes/2021-10-08-improve-pickle-dill-shelve-modeling.md b/python/change-notes/2021-10-08-improve-pickle-dill-shelve-modeling.md new file mode 100644 index 00000000000..48c868ee416 --- /dev/null +++ b/python/change-notes/2021-10-08-improve-pickle-dill-shelve-modeling.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Improved modeling of decoding through pickle related functions (which can lead to code execution), resulting in additional sinks for the _Deserializing untrusted input_ query (`py/unsafe-deserialization`). Now we fully support `pickle.load`, `pickle.loads`, `pickle.Unpickler`, `marshal.load`, `marshal.loads`, `dill.load`, `dill.loads`, `shelve.open`. diff --git a/python/change-notes/2021-10-20-extraction-errors-as-warnings.md b/python/change-notes/2021-10-20-extraction-errors-as-warnings.md new file mode 100644 index 00000000000..698435a2e3c --- /dev/null +++ b/python/change-notes/2021-10-20-extraction-errors-as-warnings.md @@ -0,0 +1,2 @@ +codescanning +* Problems with extraction that in most cases won't completely break the analysis are now reported as warnings rather than errors. diff --git a/python/change-notes/2021-10-26-ruamel.yaml-modeling.md b/python/change-notes/2021-10-26-ruamel.yaml-modeling.md new file mode 100644 index 00000000000..16c5bfce9eb --- /dev/null +++ b/python/change-notes/2021-10-26-ruamel.yaml-modeling.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Added modeling of the `ruamel.yaml` PyPI package, resulting in additional sinks for the _Deserializing untrusted input_ (`py/unsafe-deserialization`) query (since `ruamel.yaml.load` can lead to code execution). diff --git a/python/ql/lib/semmle/python/ApiGraphs.qll b/python/ql/lib/semmle/python/ApiGraphs.qll index 19a287df63a..9ab510d0ee5 100644 --- a/python/ql/lib/semmle/python/ApiGraphs.qll +++ b/python/ql/lib/semmle/python/ApiGraphs.qll @@ -55,7 +55,7 @@ module API { /** * Gets a call to the function represented by this API component. */ - DataFlow::CallCfgNode getACall() { result = getReturn().getAnImmediateUse() } + DataFlow::CallCfgNode getACall() { result = this.getReturn().getAnImmediateUse() } /** * Gets a node representing member `m` of this API component. @@ -67,21 +67,21 @@ module API { */ bindingset[m] bindingset[result] - Node getMember(string m) { result = getASuccessor(Label::member(m)) } + Node getMember(string m) { result = this.getASuccessor(Label::member(m)) } /** * Gets a node representing a member of this API component where the name of the member is * not known statically. */ - Node getUnknownMember() { result = getASuccessor(Label::unknownMember()) } + Node getUnknownMember() { result = this.getASuccessor(Label::unknownMember()) } /** * Gets a node representing a member of this API component where the name of the member may * or may not be known statically. */ Node getAMember() { - result = getASuccessor(Label::member(_)) or - result = getUnknownMember() + result = this.getASuccessor(Label::member(_)) or + result = this.getUnknownMember() } /** @@ -90,23 +90,25 @@ module API { * This predicate may have multiple results when there are multiple invocations of this API component. * Consider using `getACall()` if there is a need to distinguish between individual calls. */ - Node getReturn() { result = getASuccessor(Label::return()) } + Node getReturn() { result = this.getASuccessor(Label::return()) } /** * Gets a node representing a subclass of the class represented by this node. */ - Node getASubclass() { result = getASuccessor(Label::subclass()) } + Node getASubclass() { result = this.getASuccessor(Label::subclass()) } /** * Gets a node representing the result from awaiting this node. */ - Node getAwaited() { result = getASuccessor(Label::await()) } + Node getAwaited() { result = this.getASuccessor(Label::await()) } /** * Gets a string representation of the lexicographically least among all shortest access paths * from the root to this node. */ - string getPath() { result = min(string p | p = getAPath(Impl::distanceFromRoot(this)) | p) } + string getPath() { + result = min(string p | p = this.getAPath(Impl::distanceFromRoot(this)) | p) + } /** * Gets a node such that there is an edge in the API graph between this node and the other @@ -124,13 +126,13 @@ module API { * Gets a node such that there is an edge in the API graph between this node and the other * one. */ - Node getAPredecessor() { result = getAPredecessor(_) } + Node getAPredecessor() { result = this.getAPredecessor(_) } /** * Gets a node such that there is an edge in the API graph between that other node and * this one. */ - Node getASuccessor() { result = getASuccessor(_) } + Node getASuccessor() { result = this.getASuccessor(_) } /** * Gets the data-flow node that gives rise to this node, if any. @@ -142,16 +144,16 @@ module API { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { - getInducingNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + this.getInducingNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or // For nodes that do not have a meaningful location, `path` is the empty string and all other // parameters are zero. - not exists(getInducingNode()) and + not exists(this.getInducingNode()) and filepath = "" and startline = 0 and startcolumn = 0 and @@ -202,7 +204,7 @@ module API { or this = Impl::MkModuleImport(_) and type = "ModuleImport " | - result = type + getPath() + result = type + this.getPath() or not exists(this.getPath()) and result = type + "with no path" ) diff --git a/python/ql/lib/semmle/python/Comment.qll b/python/ql/lib/semmle/python/Comment.qll index 94dd429e404..24810b418ac 100644 --- a/python/ql/lib/semmle/python/Comment.qll +++ b/python/ql/lib/semmle/python/Comment.qll @@ -67,7 +67,7 @@ class CommentBlock extends @py_comment { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/python/ql/lib/semmle/python/Concepts.qll b/python/ql/lib/semmle/python/Concepts.qll index 5517347e692..a61734f1b3e 100644 --- a/python/ql/lib/semmle/python/Concepts.qll +++ b/python/ql/lib/semmle/python/Concepts.qll @@ -355,6 +355,53 @@ module SqlExecution { } } +/** + * A data-flow node that executes a regular expression. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `RegexExecution::Range` instead. + */ +class RegexExecution extends DataFlow::Node { + RegexExecution::Range range; + + RegexExecution() { this = range } + + /** Gets the data flow node for the regex being executed by this node. */ + DataFlow::Node getRegex() { result = range.getRegex() } + + /** Gets a dataflow node for the string to be searched or matched against. */ + DataFlow::Node getString() { result = range.getString() } + + /** + * Gets the name of this regex execution, typically the name of an executing method. + * This is used for nice alert messages and should include the module if possible. + */ + string getName() { result = range.getName() } +} + +/** Provides classes for modeling new regular-expression execution APIs. */ +module RegexExecution { + /** + * A data-flow node that executes a regular expression. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `RegexExecution` instead. + */ + abstract class Range extends DataFlow::Node { + /** Gets the data flow node for the regex being executed by this node. */ + abstract DataFlow::Node getRegex(); + + /** Gets a dataflow node for the string to be searched or matched against. */ + abstract DataFlow::Node getString(); + + /** + * Gets the name of this regex execution, typically the name of an executing method. + * This is used for nice alert messages and should include the module if possible. + */ + abstract string getName(); + } +} + /** * A data-flow node that escapes meta-characters, which could be used to prevent * injection attacks. @@ -411,6 +458,9 @@ module Escaping { /** Gets the escape-kind for escaping a string so it can safely be included in HTML. */ string getHtmlKind() { result = "html" } + + /** Gets the escape-kind for escaping a string so it can safely be included in HTML. */ + string getRegexKind() { result = "regex" } // TODO: If adding an XML kind, update the modeling of the `MarkupSafe` PyPI package. // // Technically it claims to escape for both HTML and XML, but for now we don't have @@ -427,6 +477,14 @@ class HtmlEscaping extends Escaping { HtmlEscaping() { range.getKind() = Escaping::getHtmlKind() } } +/** + * An escape of a string so it can be safely included in + * the body of a regex. + */ +class RegexEscaping extends Escaping { + RegexEscaping() { range.getKind() = Escaping::getRegexKind() } +} + /** Provides classes for modeling HTTP-related APIs. */ module HTTP { import semmle.python.web.HttpConstants diff --git a/python/ql/lib/semmle/python/Exprs.qll b/python/ql/lib/semmle/python/Exprs.qll index 98c24b126a4..5afa651de22 100644 --- a/python/ql/lib/semmle/python/Exprs.qll +++ b/python/ql/lib/semmle/python/Exprs.qll @@ -17,7 +17,7 @@ class Expr extends Expr_, AstNode { * Whether this expression defines variable `v` * If doing dataflow, then consider using SsaVariable.getDefinition() for more precision. */ - predicate defines(Variable v) { this.getASubExpression+().defines(v) } + predicate defines(Variable v) { this.getASubExpression().defines(v) } /** Whether this expression may have a side effect (as determined purely from its syntax) */ predicate hasSideEffects() { @@ -240,7 +240,7 @@ class Call extends Call_ { /** Gets the tuple (*) argument of this call, provided there is exactly one. */ Expr getStarArg() { count(this.getStarargs()) < 2 and - result = getStarargs() + result = this.getStarargs() } } diff --git a/python/ql/lib/semmle/python/Files.qll b/python/ql/lib/semmle/python/Files.qll index 2fa1a733fc6..99570fc4d7a 100644 --- a/python/ql/lib/semmle/python/Files.qll +++ b/python/ql/lib/semmle/python/Files.qll @@ -13,7 +13,7 @@ class File extends Container, @file { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -123,7 +123,7 @@ class Folder extends Container, @folder { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -256,7 +256,7 @@ abstract class Container extends @container { * </table> */ string getBaseName() { - result = getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1) + result = this.getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1) } /** @@ -282,7 +282,9 @@ abstract class Container extends @container { * <tr><td>"/tmp/x.tar.gz"</td><td>"gz"</td></tr> * </table> */ - string getExtension() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) } + string getExtension() { + result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) + } /** * Gets the stem of this container, that is, the prefix of its base name up to @@ -301,7 +303,9 @@ abstract class Container extends @container { * <tr><td>"/tmp/x.tar.gz"</td><td>"x.tar"</td></tr> * </table> */ - string getStem() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) } + string getStem() { + result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) + } File getFile(string baseName) { result = this.getAFile() and @@ -323,7 +327,7 @@ abstract class Container extends @container { /** * Gets a URL representing the location of this container. * - * For more information see [Providing URLs](https://help.semmle.com/QL/learn-ql/ql/locations.html#providing-urls). + * For more information see [Providing URLs](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/#providing-urls). */ abstract string getURL(); @@ -429,7 +433,7 @@ class Location extends @location { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -457,7 +461,7 @@ class Line extends @py_line { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/python/ql/lib/semmle/python/Flow.qll b/python/ql/lib/semmle/python/Flow.qll index beda3cef1c4..ab3d0a5f393 100755 --- a/python/ql/lib/semmle/python/Flow.qll +++ b/python/ql/lib/semmle/python/Flow.qll @@ -851,9 +851,9 @@ class ForNode extends ControlFlowNode { /** Holds if this `for` statement causes iteration over `sequence` storing each step of the iteration in `target` */ predicate iterates(ControlFlowNode target, ControlFlowNode sequence) { - sequence = getSequence() and - target = possibleTarget() and - not target = unrolledSuffix().possibleTarget() + sequence = this.getSequence() and + target = this.possibleTarget() and + not target = this.unrolledSuffix().possibleTarget() } /** Gets the sequence node for this `for` statement. */ @@ -1111,7 +1111,7 @@ class BasicBlock extends @py_flow_node { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/python/ql/lib/semmle/python/Frameworks.qll b/python/ql/lib/semmle/python/Frameworks.qll index b3ff235c3ee..7363b0d26c7 100644 --- a/python/ql/lib/semmle/python/Frameworks.qll +++ b/python/ql/lib/semmle/python/Frameworks.qll @@ -25,6 +25,7 @@ private import semmle.python.frameworks.Peewee private import semmle.python.frameworks.Psycopg2 private import semmle.python.frameworks.PyMySQL private import semmle.python.frameworks.Rsa +private import semmle.python.frameworks.RuamelYaml private import semmle.python.frameworks.Simplejson private import semmle.python.frameworks.SqlAlchemy private import semmle.python.frameworks.Stdlib diff --git a/python/ql/lib/semmle/python/Function.qll b/python/ql/lib/semmle/python/Function.qll index 2c09188ccb1..acaca8655ad 100644 --- a/python/ql/lib/semmle/python/Function.qll +++ b/python/ql/lib/semmle/python/Function.qll @@ -58,6 +58,7 @@ class Function extends Function_, Scope, AstNode { /** Gets the name of the nth argument (for simple arguments) */ string getArgName(int index) { result = this.getArg(index).(Name).getId() } + /** Gets the parameter of this function with the name `name`. */ Parameter getArgByName(string name) { ( result = this.getAnArg() diff --git a/python/ql/lib/semmle/python/GuardedControlFlow.qll b/python/ql/lib/semmle/python/GuardedControlFlow.qll index 37ecfee37d5..73ea183850a 100644 --- a/python/ql/lib/semmle/python/GuardedControlFlow.qll +++ b/python/ql/lib/semmle/python/GuardedControlFlow.qll @@ -9,6 +9,7 @@ class ConditionBlock extends BasicBlock { } /** Basic blocks controlled by this condition, i.e. those BBs for which the condition is testIsTrue */ + pragma[nomagic] predicate controls(BasicBlock controlled, boolean testIsTrue) { /* * For this block to control the block 'controlled' with 'testIsTrue' the following must be true: diff --git a/python/ql/lib/semmle/python/Import.qll b/python/ql/lib/semmle/python/Import.qll index 40c1c27a851..9620b01e4c6 100644 --- a/python/ql/lib/semmle/python/Import.qll +++ b/python/ql/lib/semmle/python/Import.qll @@ -31,7 +31,7 @@ class ImportExpr extends ImportExpr_ { // relative imports are no longer allowed in Python 3 major_version() < 3 and // and can be explicitly turned off in later versions of Python 2 - not getEnclosingModule().hasFromFuture("absolute_import") + not this.getEnclosingModule().hasFromFuture("absolute_import") } /** @@ -53,8 +53,8 @@ class ImportExpr extends ImportExpr_ { * the name of the topmost module that will be imported. */ private string relativeTopName() { - getLevel() = -1 and - result = basePackageName(1) + "." + this.getTopName() and + this.getLevel() = -1 and + result = this.basePackageName(1) + "." + this.getTopName() and valid_module_name(result) } @@ -62,7 +62,7 @@ class ImportExpr extends ImportExpr_ { if this.getLevel() <= 0 then result = this.getTopName() else ( - result = basePackageName(this.getLevel()) and + result = this.basePackageName(this.getLevel()) and valid_module_name(result) ) } @@ -73,17 +73,17 @@ class ImportExpr extends ImportExpr_ { * which may not be the name of the module. */ string bottomModuleName() { - result = relativeTopName() + this.remainderOfName() + result = this.relativeTopName() + this.remainderOfName() or - not exists(relativeTopName()) and + not exists(this.relativeTopName()) and result = this.qualifiedTopName() + this.remainderOfName() } /** Gets the name of topmost module or package being imported */ string topModuleName() { - result = relativeTopName() + result = this.relativeTopName() or - not exists(relativeTopName()) and + not exists(this.relativeTopName()) and result = this.qualifiedTopName() } @@ -94,7 +94,7 @@ class ImportExpr extends ImportExpr_ { */ string getImportedModuleName() { exists(string bottomName | bottomName = this.bottomModuleName() | - if this.isTop() then result = topModuleName() else result = bottomName + if this.isTop() then result = this.topModuleName() else result = bottomName ) } diff --git a/python/ql/lib/semmle/python/Module.qll b/python/ql/lib/semmle/python/Module.qll index 8f9344f60c0..6baf41b4a03 100644 --- a/python/ql/lib/semmle/python/Module.qll +++ b/python/ql/lib/semmle/python/Module.qll @@ -86,13 +86,13 @@ class Module extends Module_, Scope, AstNode { /** Gets the package containing this module (or parent package if this is a package) */ Module getPackage() { this.getName().matches("%.%") and - result.getName() = getName().regexpReplaceAll("\\.[^.]*$", "") + result.getName() = this.getName().regexpReplaceAll("\\.[^.]*$", "") } /** Gets the name of the package containing this module */ string getPackageName() { this.getName().matches("%.%") and - result = getName().regexpReplaceAll("\\.[^.]*$", "") + result = this.getName().regexpReplaceAll("\\.[^.]*$", "") } /** Gets the metrics for this module */ diff --git a/python/ql/lib/semmle/python/PrintAst.qll b/python/ql/lib/semmle/python/PrintAst.qll index e06e8bc9ffb..d76285ee3fa 100644 --- a/python/ql/lib/semmle/python/PrintAst.qll +++ b/python/ql/lib/semmle/python/PrintAst.qll @@ -52,8 +52,7 @@ private newtype TPrintAstNode = TStmtListNode(StmtList list) { shouldPrint(list.getAnItem(), _) and not list = any(Module mod).getBody() and - not forall(AstNode child | child = list.getAnItem() | isNotNeeded(child)) and - exists(list.getAnItem()) + not forall(AstNode child | child = list.getAnItem() | isNotNeeded(child)) } or TRegExpTermNode(RegExpTerm term) { exists(StrConst str | term.getRootTerm() = getParsedRegExp(str) and shouldPrint(str, _)) diff --git a/python/ql/lib/semmle/python/RegexTreeView.qll b/python/ql/lib/semmle/python/RegexTreeView.qll index ad1949e4bc4..75084dfa5ec 100644 --- a/python/ql/lib/semmle/python/RegexTreeView.qll +++ b/python/ql/lib/semmle/python/RegexTreeView.qll @@ -49,16 +49,17 @@ newtype TRegExpParent = * or another regular expression term. */ class RegExpParent extends TRegExpParent { + /** Gets a textual representation of this element. */ string toString() { result = "RegExpParent" } /** Gets the `i`th child term. */ abstract RegExpTerm getChild(int i); /** Gets a child term . */ - RegExpTerm getAChild() { result = getChild(_) } + RegExpTerm getAChild() { result = this.getChild(_) } /** Gets the number of child terms. */ - int getNumChild() { result = count(getAChild()) } + int getNumChild() { result = count(this.getAChild()) } /** Gets the associated regex. */ abstract Regex getRegex(); @@ -72,14 +73,18 @@ class RegExpLiteral extends TRegExpLiteral, RegExpParent { override RegExpTerm getChild(int i) { i = 0 and result.getRegex() = re and result.isRootTerm() } + /** Holds if dot, `.`, matches all characters, including newlines. */ predicate isDotAll() { re.getAMode() = "DOTALL" } + /** Holds if this regex matching is case-insensitive for this regex. */ predicate isIgnoreCase() { re.getAMode() = "IGNORECASE" } + /** Get a string representing all modes for this regex. */ string getFlags() { result = concat(string mode | mode = re.getAMode() | mode, " | ") } override Regex getRegex() { result = re } + /** Gets the primary QL class for this regex. */ string getPrimaryQLClass() { result = "RegExpLiteral" } } @@ -117,7 +122,7 @@ class RegExpTerm extends RegExpParent { RegExpTerm getRootTerm() { this.isRootTerm() and result = this or - result = getParent().(RegExpTerm).getRootTerm() + result = this.getParent().(RegExpTerm).getRootTerm() } /** @@ -196,7 +201,7 @@ class RegExpTerm extends RegExpParent { /** Gets the regular expression term that is matched (textually) before this one, if any. */ RegExpTerm getPredecessor() { - exists(RegExpTerm parent | parent = getParent() | + exists(RegExpTerm parent | parent = this.getParent() | result = parent.(RegExpSequence).previousElement(this) or not exists(parent.(RegExpSequence).previousElement(this)) and @@ -207,7 +212,7 @@ class RegExpTerm extends RegExpParent { /** Gets the regular expression term that is matched (textually) after this one, if any. */ RegExpTerm getSuccessor() { - exists(RegExpTerm parent | parent = getParent() | + exists(RegExpTerm parent | parent = this.getParent() | result = parent.(RegExpSequence).nextElement(this) or not exists(parent.(RegExpSequence).nextElement(this)) and @@ -246,8 +251,10 @@ class RegExpQuantifier extends RegExpTerm, TRegExpQuantifier { result.getEnd() = part_end } + /** Hols if this term may match an unlimited number of times. */ predicate mayRepeatForever() { may_repeat_forever = true } + /** Gets the qualifier for this term. That is e.g "?" for "a?". */ string getQualifier() { result = re.getText().substring(part_end, end) } override string getPrimaryQLClass() { result = "RegExpQuantifier" } @@ -322,8 +329,10 @@ class RegExpRange extends RegExpQuantifier { RegExpRange() { re.multiples(part_end, end, lower, upper) } + /** Gets the string defining the upper bound of this range, if any. */ string getUpper() { result = upper } + /** Gets the string defining the lower bound of this range, if any. */ string getLower() { result = lower } /** @@ -358,7 +367,7 @@ class RegExpSequence extends RegExpTerm, TRegExpSequence { override RegExpTerm getChild(int i) { result = seqChild(re, start, end, i) } /** Gets the element preceding `element` in this sequence. */ - RegExpTerm previousElement(RegExpTerm element) { element = nextElement(result) } + RegExpTerm previousElement(RegExpTerm element) { element = this.nextElement(result) } /** Gets the element following `element` in this sequence. */ RegExpTerm nextElement(RegExpTerm element) { @@ -461,15 +470,17 @@ class RegExpEscape extends RegExpNormalChar { // TODO: Find a way to include a formfeed character // this.getUnescaped() = "f" and result = " " // or - isUnicode() and - result = getUnicode() + this.isUnicode() and + result = this.getUnicode() } + /** Holds if this terms name is given by the part following the escape character. */ predicate isIdentityEscape() { not this.getUnescaped() in ["n", "r", "t", "f"] } override string getPrimaryQLClass() { result = "RegExpEscape" } - string getUnescaped() { result = this.getText().suffix(1) } + /** Gets the part of the term following the escape character. That is e.g. "w" if the term is "\w". */ + private string getUnescaped() { result = this.getText().suffix(1) } /** * Gets the text for this escape. That is e.g. "\w". @@ -479,7 +490,7 @@ class RegExpEscape extends RegExpNormalChar { /** * Holds if this is a unicode escape. */ - private predicate isUnicode() { getText().prefix(2) = ["\\u", "\\U"] } + private predicate isUnicode() { this.getText().prefix(2) = ["\\u", "\\U"] } /** * Gets the unicode char for this escape. @@ -536,15 +547,8 @@ private int toHex(string hex) { * ``` */ class RegExpCharacterClassEscape extends RegExpEscape { - // string value; - RegExpCharacterClassEscape() { - // value = re.getText().substring(start + 1, end) and - // value in ["d", "D", "s", "S", "w", "W"] - this.getValue() in ["d", "D", "s", "S", "w", "W"] - } + RegExpCharacterClassEscape() { this.getValue() in ["d", "D", "s", "S", "w", "W"] } - /** Gets the name of the character class; for example, `w` for `\w`. */ - // override string getValue() { result = value } override RegExpTerm getChild(int i) { none() } override string getPrimaryQLClass() { result = "RegExpCharacterClassEscape" } @@ -563,19 +567,22 @@ class RegExpCharacterClassEscape extends RegExpEscape { class RegExpCharacterClass extends RegExpTerm, TRegExpCharacterClass { RegExpCharacterClass() { this = TRegExpCharacterClass(re, start, end) } + /** Holds if this character class is inverted, matching the opposite of its content. */ predicate isInverted() { re.getChar(start + 1) = "^" } + /** Gets the `i`th char inside this charater class. */ string getCharThing(int i) { result = re.getChar(i + start) } + /** Holds if this character class can match anything. */ predicate isUniversalClass() { // [^] - isInverted() and not exists(getAChild()) + this.isInverted() and not exists(this.getAChild()) or // [\w\W] and similar - not isInverted() and + not this.isInverted() and exists(string cce1, string cce2 | - cce1 = getAChild().(RegExpCharacterClassEscape).getValue() and - cce2 = getAChild().(RegExpCharacterClassEscape).getValue() + cce1 = this.getAChild().(RegExpCharacterClassEscape).getValue() and + cce2 = this.getAChild().(RegExpCharacterClassEscape).getValue() | cce1 != cce2 and cce1.toLowerCase() = cce2.toLowerCase() ) @@ -620,6 +627,7 @@ class RegExpCharacterRange extends RegExpTerm, TRegExpCharacterRange { re.charRange(_, start, lower_end, upper_start, end) } + /** Holds if this range goes from `lo` to `hi`, in effect is `lo-hi`. */ predicate isRange(string lo, string hi) { lo = re.getText().substring(start, lower_end) and hi = re.getText().substring(upper_start, end) @@ -653,8 +661,13 @@ class RegExpCharacterRange extends RegExpTerm, TRegExpCharacterRange { class RegExpNormalChar extends RegExpTerm, TRegExpNormalChar { RegExpNormalChar() { this = TRegExpNormalChar(re, start, end) } + /** + * Holds if this constant represents a valid Unicode character (as opposed + * to a surrogate code point that does not correspond to a character by itself.) + */ predicate isCharacter() { any() } + /** Gets the string representation of the char matched by this term. */ string getValue() { result = re.getText().substring(start, end) } override RegExpTerm getChild(int i) { none() } @@ -684,15 +697,15 @@ class RegExpConstant extends RegExpTerm { qstart <= start and end <= qend ) and value = this.(RegExpNormalChar).getValue() - // This will never hold - // or - // this = TRegExpSpecialChar(re, start, end) and - // re.inCharSet(start) and - // value = this.(RegExpSpecialChar).getChar() } + /** + * Holds if this constant represents a valid Unicode character (as opposed + * to a surrogate code point that does not correspond to a character by itself.) + */ predicate isCharacter() { any() } + /** Gets the string matched by this constant term. */ string getValue() { result = value } override RegExpTerm getChild(int i) { none() } @@ -731,10 +744,6 @@ class RegExpGroup extends RegExpTerm, TRegExpGroup { /** Gets the name of this capture group, if any. */ string getName() { result = re.getGroupName(start, end) } - predicate isCharacter() { any() } - - string getValue() { result = re.getText().substring(start, end) } - override RegExpTerm getChild(int i) { result.getRegex() = re and i = 0 and @@ -762,8 +771,13 @@ class RegExpSpecialChar extends RegExpTerm, TRegExpSpecialChar { re.specialCharacter(start, end, char) } + /** + * Holds if this constant represents a valid Unicode character (as opposed + * to a surrogate code point that does not correspond to a character by itself.) + */ predicate isCharacter() { any() } + /** Gets the char for this term. */ string getChar() { result = char } override RegExpTerm getChild(int i) { none() } @@ -828,8 +842,6 @@ class RegExpCaret extends RegExpSpecialChar { class RegExpZeroWidthMatch extends RegExpGroup { RegExpZeroWidthMatch() { re.zeroWidthMatch(start, end) } - override predicate isCharacter() { any() } - override RegExpTerm getChild(int i) { none() } override string getPrimaryQLClass() { result = "RegExpZeroWidthMatch" } diff --git a/python/ql/lib/semmle/python/concepts/CryptoAlgorithms.qll b/python/ql/lib/semmle/python/concepts/CryptoAlgorithms.qll index d9f25b42c9a..4b3c5f2a49f 100644 --- a/python/ql/lib/semmle/python/concepts/CryptoAlgorithms.qll +++ b/python/ql/lib/semmle/python/concepts/CryptoAlgorithms.qll @@ -15,68 +15,35 @@ */ private module AlgorithmNames { predicate isStrongHashingAlgorithm(string name) { - name = "DSA" or - name = "ED25519" or - name = "ES256" or - name = "ECDSA256" or - name = "ES384" or - name = "ECDSA384" or - name = "ES512" or - name = "ECDSA512" or - name = "SHA2" or - name = "SHA224" or - name = "SHA256" or - name = "SHA384" or - name = "SHA512" or - name = "SHA3" + name = + [ + "DSA", "ED25519", "ES256", "ECDSA256", "ES384", "ECDSA384", "ES512", "ECDSA512", "SHA2", + "SHA224", "SHA256", "SHA384", "SHA512", "SHA3", "SHA3224", "SHA3256", "SHA3384", "SHA3512" + ] } predicate isWeakHashingAlgorithm(string name) { - name = "HAVEL128" or - name = "MD2" or - name = "MD4" or - name = "MD5" or - name = "PANAMA" or - name = "RIPEMD" or - name = "RIPEMD128" or - name = "RIPEMD256" or - name = "RIPEMD160" or - name = "RIPEMD320" or - name = "SHA0" or - name = "SHA1" + name = + [ + "HAVEL128", "MD2", "MD4", "MD5", "PANAMA", "RIPEMD", "RIPEMD128", "RIPEMD256", "RIPEMD160", + "RIPEMD320", "SHA0", "SHA1" + ] } predicate isStrongEncryptionAlgorithm(string name) { - name = "AES" or - name = "AES128" or - name = "AES192" or - name = "AES256" or - name = "AES512" or - name = "RSA" or - name = "RABBIT" or - name = "BLOWFISH" + name = ["AES", "AES128", "AES192", "AES256", "AES512", "RSA", "RABBIT", "BLOWFISH"] } predicate isWeakEncryptionAlgorithm(string name) { - name = "DES" or - name = "3DES" or - name = "TRIPLEDES" or - name = "TDEA" or - name = "TRIPLEDEA" or - name = "ARC2" or - name = "RC2" or - name = "ARC4" or - name = "RC4" or - name = "ARCFOUR" or - name = "ARC5" or - name = "RC5" + name = + [ + "DES", "3DES", "TRIPLEDES", "TDEA", "TRIPLEDEA", "ARC2", "RC2", "ARC4", "RC4", "ARCFOUR", + "ARC5", "RC5" + ] } predicate isStrongPasswordHashingAlgorithm(string name) { - name = "ARGON2" or - name = "PBKDF2" or - name = "BCRYPT" or - name = "SCRYPT" + name = ["ARGON2", "PBKDF2", "BCRYPT", "SCRYPT"] } predicate isWeakPasswordHashingAlgorithm(string name) { none() } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll index 0c99a25ccc4..b3d03ea4e26 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll @@ -110,12 +110,12 @@ abstract class Configuration extends string { /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowTo(Node sink) { hasFlow(_, sink) } + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } /** * Holds if data may flow from some source to `sink` for this configuration. */ - predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) } + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } /** * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` @@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx { } int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } } private class RetNodeEx extends NodeEx { @@ -744,8 +746,12 @@ private module Stage1 { returnFlowCallableNodeCand(c, kind, config) and p.getEnclosingCallable() = c and exists(ap) and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) ) } @@ -1394,8 +1400,12 @@ private module Stage2 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2083,8 +2093,12 @@ private module Stage3 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2139,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and - tupleLimit < (tails - 1) * nodes + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() ) } @@ -2842,8 +2857,12 @@ private module Stage4 { fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and kind = ret.getKind() and p.getPosition() = pos and - // we don't expect a parameter to return stored in itself - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) ) } @@ -2916,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { int getParameterPos() { p.isParameterOf(_, result) } + ParamNodeEx getParamNode() { result = p } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( @@ -2973,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { - exists(int aps, int nodes, int apLimit, int tupleLimit | - aps = countPotentialAps(apa, config) and - nodes = countNodesUsingAccessPath(apa, config) and - accessPathCostLimits(apLimit, tupleLimit) and - if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true - ) + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) } /** @@ -3166,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { } override string toString() { - result = "[" + this.toStringImpl(true) + length().toString() + ")]" + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" or result = "[" + this.toStringImpl(false) } @@ -3248,7 +3272,7 @@ class PathNode extends TPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3305,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } - override string toString() { result = this.getNodeEx().toString() + ppAp() } + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } - override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() } + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -3375,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { override PathNodeImpl getASuccessorImpl() { // an intermediate step to another intermediate node - result = getSuccMid() + result = this.getSuccMid() or // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges exists(PathNodeMid mid, PathNodeSink sink | - mid = getSuccMid() and + mid = this.getSuccMid() and mid.getNodeEx() = sink.getNodeEx() and mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbindConf(mid.getConfiguration()) and @@ -3456,7 +3482,7 @@ private predicate pathStep( exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() or pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or @@ -3533,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(ArgNode arg | arg = mid.getNodeEx().asNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = ap.getApprox() + apa = ap.getApprox() and + config = mid.getConfiguration() ) } @@ -3557,12 +3585,14 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, Configuration config ) { exists(AccessPathApprox apa | - pathIntoArg(mid, i, outercc, call, ap, apa) and + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) ) } @@ -3571,12 +3601,13 @@ private predicate pathIntoCallable0( * before and after entering the callable are `outercc` and `innercc`, * respectively. */ +pragma[nomagic] private predicate pathIntoCallable( PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, - DataFlowCall call + DataFlowCall call, Configuration config ) { exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and p.isParameterOf(callable, i) and ( sc = TSummaryCtxSome(p, ap) @@ -3606,18 +3637,23 @@ private predicate paramFlowsThrough( ap = mid.getAp() and apa = ap.getApprox() and pos = sc.getParameterPos() and - not kind.(ParamUpdateReturnKind).getPosition() = pos + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) ) } pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, - AccessPathApprox apa + AccessPathApprox apa, Configuration config ) { exists(CallContext innercc, SummaryCtx sc | - pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration())) + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) ) } @@ -3627,9 +3663,9 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { - exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | - pathThroughCallable0(call, mid, kind, cc, ap, apa) and - out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration())) + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -3643,10 +3679,11 @@ private module Subpaths { PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, NodeEx out, AccessPath apout ) { - pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and - pathIntoCallable(arg, par, _, innercc, sc, _) and - paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, - unbindConf(arg.getConfiguration())) + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) } /** @@ -4033,7 +4070,7 @@ private module FlowExploration { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll index f588a25a176..e11244c42b0 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll @@ -801,6 +801,9 @@ private module Cached { exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) } + cached + predicate allowParameterReturnInSelfCached(ParamNode p) { allowParameterReturnInSelf(p) } + cached newtype TCallContext = TAnyCallContext() or @@ -937,7 +940,7 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall { } override predicate relevantFor(DataFlowCallable callable) { - recordDataFlowCallSite(getCall(), callable) + recordDataFlowCallSite(this.getCall(), callable) } override predicate matchesCall(DataFlowCall call) { call = this.getCall() } @@ -1236,6 +1239,13 @@ class TypedContent extends MkTypedContent { /** Gets a textual representation of this content. */ string toString() { result = c.toString() } + + /** + * Holds if access paths with this `TypedContent` at their head always should + * be tracked at high precision. This disables adaptive access path precision + * for such access paths. + */ + predicate forceHighPrecision() { forceHighPrecision(c) } } /** @@ -1250,7 +1260,7 @@ abstract class AccessPathFront extends TAccessPathFront { TypedContent getHead() { this = TFrontHead(result) } - predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) } + predicate isClearedAt(Node n) { clearsContentCached(n, this.getHead().getContent()) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll index a55e65a81f6..dd64fc70039 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll @@ -175,6 +175,7 @@ module Consistency { query predicate postWithInFlow(Node n, string msg) { isPostUpdateNode(n) and + not clearsContent(n, _) and simpleLocalFlowStep(_, n) and msg = "PostUpdateNode should not be the target of local flow." } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll index 44c64234b75..b1a9ea7aa3a 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll @@ -152,6 +152,7 @@ class DataFlowExpr = Expr; * Flow comes from definitions, uses and refinements. */ // TODO: Consider constraining `nodeFrom` and `nodeTo` to be in the same scope. +// If they have different enclosing callables, we get consistency errors. module EssaFlow { predicate essaFlowStep(Node nodeFrom, Node nodeTo) { // Definition @@ -200,6 +201,9 @@ module EssaFlow { // If expressions nodeFrom.asCfgNode() = nodeTo.asCfgNode().(IfExprNode).getAnOperand() or + // boolean inline expressions such as `x or y` or `x and y` + nodeFrom.asCfgNode() = nodeTo.asCfgNode().(BoolExprNode).getAnOperand() + or // Flow inside an unpacking assignment iterableUnpackingFlowStep(nodeFrom, nodeTo) or @@ -225,35 +229,60 @@ module EssaFlow { //-------- /** * This is the local flow predicate that is used as a building block in global - * data flow. It is a strict subset of the `localFlowStep` predicate, as it - * excludes SSA flow through instance fields. + * data flow. + * + * Local flow can happen either at import time, when the module is initialised + * or at runtime when callables in the module are called. */ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { - // If there is ESSA-flow out of a node `node`, we want flow + // If there is local flow out of a node `node`, we want flow // both out of `node` and any post-update node of `node`. exists(Node node | - EssaFlow::essaFlowStep(node, nodeTo) and nodeFrom = update(node) and ( - not node instanceof EssaNode or - not nodeTo instanceof EssaNode or - localEssaStep(node, nodeTo) + importTimeLocalFlowStep(node, nodeTo) or + runtimeLocalFlowStep(node, nodeTo) ) ) } /** - * Holds if there is an Essa flow step from `nodeFrom` to `nodeTo` that does not switch between - * local and global SSA variables. + * Holds if `node` is found at the top level of a module. */ -private predicate localEssaStep(EssaNode nodeFrom, EssaNode nodeTo) { - EssaFlow::essaFlowStep(nodeFrom, nodeTo) and - ( - nodeFrom.getVar() instanceof GlobalSsaVariable and - nodeTo.getVar() instanceof GlobalSsaVariable - or - not nodeFrom.getVar() instanceof GlobalSsaVariable and - not nodeTo.getVar() instanceof GlobalSsaVariable +pragma[inline] +predicate isTopLevel(Node node) { node.getScope() instanceof Module } + +/** Holds if there is local flow from `nodeFrom` to `nodeTo` at import time. */ +predicate importTimeLocalFlowStep(Node nodeFrom, Node nodeTo) { + // As a proxy for whether statements can be executed at import time, + // we check if they appear at the top level. + // This will miss statements inside functions called from the top level. + isTopLevel(nodeFrom) and + isTopLevel(nodeTo) and + EssaFlow::essaFlowStep(nodeFrom, nodeTo) +} + +/** Holds if there is local flow from `nodeFrom` to `nodeTo` at runtime. */ +predicate runtimeLocalFlowStep(Node nodeFrom, Node nodeTo) { + // Anything not at the top level can be executed at runtime. + not isTopLevel(nodeFrom) and + not isTopLevel(nodeTo) and + EssaFlow::essaFlowStep(nodeFrom, nodeTo) +} + +/** `ModuleVariable`s are accessed via jump steps at runtime. */ +predicate runtimeJumpStep(Node nodeFrom, Node nodeTo) { + // Module variable read + nodeFrom.(ModuleVariableNode).getARead() = nodeTo + or + // Module variable write + nodeFrom = nodeTo.(ModuleVariableNode).getAWrite() + or + // Setting the possible values of the variable at the end of import time + exists(SsaVariable def | + def = any(SsaVariable var).getAnUltimateDefinition() and + def.getDefinition() = nodeFrom.asCfgNode() and + def.getVariable() = nodeTo.(ModuleVariableNode).getVariable() ) } @@ -581,11 +610,11 @@ class DataFlowLambda extends DataFlowCallable, TLambda { override string toString() { result = lambda.toString() } - override CallNode getACall() { result = getCallableValue().getACall() } + override CallNode getACall() { result = this.getCallableValue().getACall() } override Scope getScope() { result = lambda.getEvaluatingScope() } - override NameNode getParameter(int n) { result = getParameter(getCallableValue(), n) } + override NameNode getParameter(int n) { result = getParameter(this.getCallableValue(), n) } override string getName() { result = "Lambda callable" } @@ -857,11 +886,7 @@ string ppReprType(DataFlowType t) { none() } * taken into account. */ predicate jumpStep(Node nodeFrom, Node nodeTo) { - // Module variable read - nodeFrom.(ModuleVariableNode).getARead() = nodeTo - or - // Module variable write - nodeFrom = nodeTo.(ModuleVariableNode).getAWrite() + runtimeJumpStep(nodeFrom, nodeTo) or // Read of module attribute: exists(AttrRead r, ModuleValue mv | @@ -1620,6 +1645,12 @@ predicate isImmutableOrUnobservable(Node n) { none() } int accessPathLimit() { result = 5 } +/** + * Holds if access paths with `c` at their head always should be tracked at high + * precision. This disables adaptive access path precision for such access paths. + */ +predicate forceHighPrecision(Content c) { none() } + /** Holds if `n` should be hidden from path explanations. */ predicate nodeIsHidden(Node n) { none() } @@ -1633,3 +1664,12 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { no /** Extra data-flow steps needed for lambda flow analysis. */ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() } + +/** + * Holds if flow is allowed to pass from parameter `p` and back to itself as a + * side-effect, resulting in a summary from `p` to itself. + * + * One example would be to allow flow like `p.foo = p.bar;`, which is disallowed + * by default as a heuristic. + */ +predicate allowParameterReturnInSelf(ParameterNode p) { none() } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll index 928082d7e8a..ee626ed26bf 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll @@ -102,7 +102,7 @@ class Node extends TNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -332,7 +332,7 @@ class ModuleVariableNode extends Node, TModuleVariableNode { override Scope getScope() { result = mod } override string toString() { - result = "ModuleVariableNode for " + var.toString() + " in " + mod.toString() + result = "ModuleVariableNode for " + mod.getName() + "." + var.getId() } /** Gets the module in which this variable appears. */ diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/LocalSources.qll b/python/ql/lib/semmle/python/dataflow/new/internal/LocalSources.qll index df1ee7bba16..76cc1573b24 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/LocalSources.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/LocalSources.qll @@ -62,12 +62,12 @@ class LocalSourceNode extends Node { /** * Gets a read of attribute `attrName` on this node. */ - AttrRead getAnAttributeRead(string attrName) { result = getAnAttributeReference(attrName) } + AttrRead getAnAttributeRead(string attrName) { result = this.getAnAttributeReference(attrName) } /** * Gets a write of attribute `attrName` on this node. */ - AttrWrite getAnAttributeWrite(string attrName) { result = getAnAttributeReference(attrName) } + AttrWrite getAnAttributeWrite(string attrName) { result = this.getAnAttributeReference(attrName) } /** * Gets a reference (read or write) of any attribute on this node. @@ -81,12 +81,12 @@ class LocalSourceNode extends Node { /** * Gets a read of any attribute on this node. */ - AttrRead getAnAttributeRead() { result = getAnAttributeReference() } + AttrRead getAnAttributeRead() { result = this.getAnAttributeReference() } /** * Gets a write of any attribute on this node. */ - AttrWrite getAnAttributeWrite() { result = getAnAttributeReference() } + AttrWrite getAnAttributeWrite() { result = this.getAnAttributeReference() } /** * Gets a call to this node. diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/PrintNode.qll b/python/ql/lib/semmle/python/dataflow/new/internal/PrintNode.qll index 234019a498c..c7fd7dcecf3 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/PrintNode.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/PrintNode.qll @@ -58,7 +58,6 @@ string prettyNode(DataFlow::Node node) { */ bindingset[node] string prettyNodeForInlineTest(DataFlow::Node node) { - exists(node.asExpr()) and result = prettyExpr(node.asExpr()) or exists(Expr e | e = node.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() | diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/tainttracking1/TaintTrackingImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/tainttracking1/TaintTrackingImpl.qll index f4f73b8247c..acb029c23d9 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/tainttracking1/TaintTrackingImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/tainttracking1/TaintTrackingImpl.qll @@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration { predicate isSanitizer(DataFlow::Node node) { none() } final override predicate isBarrier(DataFlow::Node node) { - isSanitizer(node) or + this.isSanitizer(node) or defaultTaintSanitizer(node) } /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } - final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } + final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) } /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } - final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } + final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } - final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } + final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { + this.isSanitizerGuard(guard) + } /** * Holds if the additional taint propagation step from `node1` to `node2` @@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration { predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() } final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - isAdditionalTaintStep(node1, node2) or + this.isAdditionalTaintStep(node1, node2) or defaultAdditionalTaintStep(node1, node2) } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/tainttracking2/TaintTrackingImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/tainttracking2/TaintTrackingImpl.qll index f4f73b8247c..acb029c23d9 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/tainttracking2/TaintTrackingImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/tainttracking2/TaintTrackingImpl.qll @@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration { predicate isSanitizer(DataFlow::Node node) { none() } final override predicate isBarrier(DataFlow::Node node) { - isSanitizer(node) or + this.isSanitizer(node) or defaultTaintSanitizer(node) } /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } - final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } + final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) } /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } - final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } + final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } - final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } + final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { + this.isSanitizerGuard(guard) + } /** * Holds if the additional taint propagation step from `node1` to `node2` @@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration { predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() } final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - isAdditionalTaintStep(node1, node2) or + this.isAdditionalTaintStep(node1, node2) or defaultAdditionalTaintStep(node1, node2) } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/tainttracking3/TaintTrackingImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/tainttracking3/TaintTrackingImpl.qll index f4f73b8247c..acb029c23d9 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/tainttracking3/TaintTrackingImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/tainttracking3/TaintTrackingImpl.qll @@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration { predicate isSanitizer(DataFlow::Node node) { none() } final override predicate isBarrier(DataFlow::Node node) { - isSanitizer(node) or + this.isSanitizer(node) or defaultTaintSanitizer(node) } /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } - final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } + final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) } /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } - final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } + final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } - final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } + final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { + this.isSanitizerGuard(guard) + } /** * Holds if the additional taint propagation step from `node1` to `node2` @@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration { predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() } final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - isAdditionalTaintStep(node1, node2) or + this.isAdditionalTaintStep(node1, node2) or defaultAdditionalTaintStep(node1, node2) } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/tainttracking4/TaintTrackingImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/tainttracking4/TaintTrackingImpl.qll index f4f73b8247c..acb029c23d9 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/tainttracking4/TaintTrackingImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/tainttracking4/TaintTrackingImpl.qll @@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration { predicate isSanitizer(DataFlow::Node node) { none() } final override predicate isBarrier(DataFlow::Node node) { - isSanitizer(node) or + this.isSanitizer(node) or defaultTaintSanitizer(node) } /** Holds if taint propagation into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } - final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) } + final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) } /** Holds if taint propagation out of `node` is prohibited. */ predicate isSanitizerOut(DataFlow::Node node) { none() } - final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) } + final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } - final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) } + final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { + this.isSanitizerGuard(guard) + } /** * Holds if the additional taint propagation step from `node1` to `node2` @@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration { predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() } final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - isAdditionalTaintStep(node1, node2) or + this.isAdditionalTaintStep(node1, node2) or defaultAdditionalTaintStep(node1, node2) } diff --git a/python/ql/lib/semmle/python/dataflow/old/TaintTracking.qll b/python/ql/lib/semmle/python/dataflow/old/TaintTracking.qll index d6d1f9388c4..e7ee6a3ee08 100755 --- a/python/ql/lib/semmle/python/dataflow/old/TaintTracking.qll +++ b/python/ql/lib/semmle/python/dataflow/old/TaintTracking.qll @@ -384,7 +384,7 @@ abstract class TaintSource extends @py_flow_node { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -498,7 +498,7 @@ abstract class TaintSink extends @py_flow_node { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/python/ql/lib/semmle/python/essa/Definitions.qll b/python/ql/lib/semmle/python/essa/Definitions.qll index 752ff9da329..e4e4bba747f 100644 --- a/python/ql/lib/semmle/python/essa/Definitions.qll +++ b/python/ql/lib/semmle/python/essa/Definitions.qll @@ -225,9 +225,9 @@ class ModuleVariable extends SsaSourceVariable { } override ControlFlowNode getAnImplicitUse() { - result = global_variable_callnode() + result = this.global_variable_callnode() or - result = global_variable_import() + result = this.global_variable_import() or exists(ImportTimeScope scope | scope.entryEdge(result, _) | this = scope.getOuterVariable(_) or diff --git a/python/ql/lib/semmle/python/essa/Essa.qll b/python/ql/lib/semmle/python/essa/Essa.qll index d703b72242a..2d403070c4c 100644 --- a/python/ql/lib/semmle/python/essa/Essa.qll +++ b/python/ql/lib/semmle/python/essa/Essa.qll @@ -41,7 +41,7 @@ class EssaVariable extends TEssaDefinition { */ ControlFlowNode getASourceUse() { exists(SsaSourceVariable var | - result = use_for_var(var) and + result = this.use_for_var(var) and result = var.getASourceUse() ) } @@ -258,7 +258,7 @@ class PhiFunction extends EssaDefinition, TPhiFunction { /** Gets another definition of the same source variable that reaches this definition. */ private EssaDefinition reachingDefinition(BasicBlock pred) { result.getScope() = this.getScope() and - result.getSourceVariable() = pred_var(pred) and + result.getSourceVariable() = this.pred_var(pred) and result.reachesEndOfBlock(pred) } diff --git a/python/ql/lib/semmle/python/frameworks/Aiohttp.qll b/python/ql/lib/semmle/python/frameworks/Aiohttp.qll index 46bcf3e554c..748f6c92d39 100644 --- a/python/ql/lib/semmle/python/frameworks/Aiohttp.qll +++ b/python/ql/lib/semmle/python/frameworks/Aiohttp.qll @@ -424,7 +424,7 @@ module AiohttpWebModel { override string getAttributeName() { none() } - override string getMethodName() { result in ["read_nowait"] } + override string getMethodName() { result = "read_nowait" } override string getAsyncMethodName() { result in [ diff --git a/python/ql/lib/semmle/python/frameworks/Cryptodome.qll b/python/ql/lib/semmle/python/frameworks/Cryptodome.qll index 4d108196148..54b5b9437a3 100644 --- a/python/ql/lib/semmle/python/frameworks/Cryptodome.qll +++ b/python/ql/lib/semmle/python/frameworks/Cryptodome.qll @@ -116,7 +116,7 @@ private module CryptodomeModel { ] and this = API::moduleImport(["Crypto", "Cryptodome"]) - .getMember(["Cipher"]) + .getMember("Cipher") .getMember(cipherName) .getMember("new") .getReturn() @@ -135,21 +135,21 @@ private module CryptodomeModel { or // for the following methods, method signatures can be found in // https://pycryptodome.readthedocs.io/en/latest/src/cipher/modern.html - methodName in ["update"] and + methodName = "update" and result in [this.getArg(0), this.getArgByName("data")] or // although `mac_tag` is used as the parameter name in the spec above, some implementations use `received_mac_tag`, for an example, see // https://github.com/Legrandin/pycryptodome/blob/5dace638b70ac35bb5d9b565f3e75f7869c9d851/lib/Crypto/Cipher/ChaCha20_Poly1305.py#L207 - methodName in ["verify"] and + methodName = "verify" and result in [this.getArg(0), this.getArgByName(["mac_tag", "received_mac_tag"])] or - methodName in ["hexverify"] and + methodName = "hexverify" and result in [this.getArg(0), this.getArgByName("mac_tag_hex")] or - methodName in ["encrypt_and_digest"] and + methodName = "encrypt_and_digest" and result in [this.getArg(0), this.getArgByName("plaintext")] or - methodName in ["decrypt_and_verify"] and + methodName = "decrypt_and_verify" and result in [ this.getArg(0), this.getArgByName("ciphertext"), this.getArg(1), this.getArgByName("mac_tag") @@ -169,7 +169,7 @@ private module CryptodomeModel { methodName in ["sign", "verify"] and this = API::moduleImport(["Crypto", "Cryptodome"]) - .getMember(["Signature"]) + .getMember("Signature") .getMember(signatureName) .getMember("new") .getReturn() @@ -185,11 +185,11 @@ private module CryptodomeModel { methodName = "sign" and result in [this.getArg(0), this.getArgByName("msg_hash")] // Cryptodome.Hash instance or - methodName in ["verify"] and + methodName = "verify" and ( - result in [this.getArg(0), this.getArgByName(["msg_hash"])] // Cryptodome.Hash instance + result in [this.getArg(0), this.getArgByName("msg_hash")] // Cryptodome.Hash instance or - result in [this.getArg(1), this.getArgByName(["signature"])] + result in [this.getArg(1), this.getArgByName("signature")] ) } } @@ -204,7 +204,7 @@ private module CryptodomeModel { CryptodomeGenericHashOperation() { exists(API::Node hashModule | hashModule = - API::moduleImport(["Crypto", "Cryptodome"]).getMember(["Hash"]).getMember(hashName) + API::moduleImport(["Crypto", "Cryptodome"]).getMember("Hash").getMember(hashName) | this = hashModule.getMember("new").getACall() or diff --git a/python/ql/lib/semmle/python/frameworks/Dill.qll b/python/ql/lib/semmle/python/frameworks/Dill.qll index 94af905756b..168542272f9 100644 --- a/python/ql/lib/semmle/python/frameworks/Dill.qll +++ b/python/ql/lib/semmle/python/frameworks/Dill.qll @@ -1,5 +1,5 @@ /** - * Provides classes modeling security-relevant aspects of the 'dill' package. + * Provides classes modeling security-relevant aspects of the `dill` PyPI package. * See https://pypi.org/project/dill/. */ @@ -10,18 +10,41 @@ private import semmle.python.Concepts private import semmle.python.ApiGraphs /** - * A call to `dill.loads` - * See https://pypi.org/project/dill/ (which currently refers you - * to https://docs.python.org/3/library/pickle.html#pickle.loads) + * Provides models for the `dill` PyPI package. + * See https://pypi.org/project/dill/. */ -private class DillLoadsCall extends Decoding::Range, DataFlow::CallCfgNode { - DillLoadsCall() { this = API::moduleImport("dill").getMember("loads").getACall() } +private module Dill { + /** + * A call to `dill.load` + * See https://pypi.org/project/dill/ (which currently refers you + * to https://docs.python.org/3/library/pickle.html#pickle.load) + */ + private class DillLoadCall extends Decoding::Range, DataFlow::CallCfgNode { + DillLoadCall() { this = API::moduleImport("dill").getMember("load").getACall() } - override predicate mayExecuteInput() { any() } + override predicate mayExecuteInput() { any() } - override DataFlow::Node getAnInput() { result = this.getArg(0) } + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("file")] } - override DataFlow::Node getOutput() { result = this } + override DataFlow::Node getOutput() { result = this } - override string getFormat() { result = "dill" } + override string getFormat() { result = "dill" } + } + + /** + * A call to `dill.loads` + * See https://pypi.org/project/dill/ (which currently refers you + * to https://docs.python.org/3/library/pickle.html#pickle.loads) + */ + private class DillLoadsCall extends Decoding::Range, DataFlow::CallCfgNode { + DillLoadsCall() { this = API::moduleImport("dill").getMember("loads").getACall() } + + override predicate mayExecuteInput() { any() } + + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("str")] } + + override DataFlow::Node getOutput() { result = this } + + override string getFormat() { result = "dill" } + } } diff --git a/python/ql/lib/semmle/python/frameworks/Django.qll b/python/ql/lib/semmle/python/frameworks/Django.qll index 08afa55635a..89ff0537c97 100644 --- a/python/ql/lib/semmle/python/frameworks/Django.qll +++ b/python/ql/lib/semmle/python/frameworks/Django.qll @@ -1844,11 +1844,13 @@ private module PrivateDjango { t.start() and result.asCfgNode().(CallNode).getFunction() = this.asViewRef().asCfgNode() or - exists(DataFlow::TypeTracker t2 | result = asViewResult(t2).track(t2, t)) + exists(DataFlow::TypeTracker t2 | result = this.asViewResult(t2).track(t2, t)) } /** Gets a reference to the result of calling the `as_view` classmethod of this class. */ - DataFlow::Node asViewResult() { asViewResult(DataFlow::TypeTracker::end()).flowsTo(result) } + DataFlow::Node asViewResult() { + this.asViewResult(DataFlow::TypeTracker::end()).flowsTo(result) + } } /** A class that we consider a django View class. */ @@ -1944,10 +1946,10 @@ private module PrivateDjango { abstract DataFlow::Node getViewArg(); final override DjangoRouteHandler getARequestHandler() { - poorMansFunctionTracker(result) = getViewArg() + poorMansFunctionTracker(result) = this.getViewArg() or exists(DjangoViewClass vc | - getViewArg() = vc.asViewResult() and + this.getViewArg() = vc.asViewResult() and result = vc.getARequestHandler() ) } diff --git a/python/ql/lib/semmle/python/frameworks/Flask.qll b/python/ql/lib/semmle/python/frameworks/Flask.qll index e854e07658b..3c2cc5af5ec 100644 --- a/python/ql/lib/semmle/python/frameworks/Flask.qll +++ b/python/ql/lib/semmle/python/frameworks/Flask.qll @@ -292,12 +292,12 @@ module Flask { override Function getARequestHandler() { exists(DataFlow::LocalSourceNode func_src | - func_src.flowsTo(getViewArg()) and + func_src.flowsTo(this.getViewArg()) and func_src.asExpr().(CallableExpr) = result.getDefinition() ) or exists(FlaskViewClass vc | - getViewArg() = vc.asViewResult().getAUse() and + this.getViewArg() = vc.asViewResult().getAUse() and result = vc.getARequestHandler() ) } diff --git a/python/ql/lib/semmle/python/frameworks/RuamelYaml.qll b/python/ql/lib/semmle/python/frameworks/RuamelYaml.qll new file mode 100644 index 00000000000..2d553c409b0 --- /dev/null +++ b/python/ql/lib/semmle/python/frameworks/RuamelYaml.qll @@ -0,0 +1,57 @@ +/** + * Provides classes modeling security-relevant aspects of the `ruamel.yaml` PyPI package + * + * See + * - https://pypi.org/project/ruamel.yaml/ + */ + +private import python +private import semmle.python.dataflow.new.DataFlow +private import semmle.python.Concepts +private import semmle.python.ApiGraphs + +/** + * Provides models for the `ruamel.yaml` PyPI package. + * + * See + * - https://pypi.org/project/ruamel.yaml/ + */ +private module RuamelYaml { + // Note: `ruamel.yaml` is a fork of the `PyYAML` PyPI package, so that's why the + // interface is so similar. + /** + * A call to any of the loading functions in `yaml` (`load`, `load_all`, `safe_load`, `safe_load_all`) + * + * See https://pyyaml.org/wiki/PyYAMLDocumentation (you will have to scroll down). + */ + private class RuamelYamlLoadCall extends Decoding::Range, DataFlow::CallCfgNode { + string func_name; + + RuamelYamlLoadCall() { + func_name in ["load", "load_all", "safe_load", "safe_load_all"] and + this = API::moduleImport("ruamel").getMember("yaml").getMember(func_name).getACall() + } + + override predicate mayExecuteInput() { + func_name in ["load", "load_all"] and + // If the `Loader` argument is not set, the default loader will be used, which is + // not safe. The only safe loaders are `SafeLoader` or `BaseLoader` (and their + // variants with C implementation). + not exists(DataFlow::Node loader_arg | + loader_arg in [this.getArg(1), this.getArgByName("Loader")] + | + loader_arg = + API::moduleImport("ruamel") + .getMember("yaml") + .getMember(["SafeLoader", "BaseLoader", "CSafeLoader", "CBaseLoader"]) + .getAUse() + ) + } + + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("stream")] } + + override DataFlow::Node getOutput() { result = this } + + override string getFormat() { result = "YAML" } + } +} diff --git a/python/ql/lib/semmle/python/frameworks/Stdlib.qll b/python/ql/lib/semmle/python/frameworks/Stdlib.qll index 539f0dcabb0..973af899896 100644 --- a/python/ql/lib/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/lib/semmle/python/frameworks/Stdlib.qll @@ -195,6 +195,101 @@ private module StdlibPrivate { } } + /** + * The `os.path` module offers a number of methods for checking if a file exists and/or has certain + * properties, leading to a file system access. + * A call to `os.path.exists` or `os.path.lexists` will check if a file exists on the file system. + * (Although, on some platforms, the check may return `false` due to missing permissions.) + * A call to `os.path.getatime` will raise `OSError` if the file does not exist or is inaccessible. + * See: + * - https://docs.python.org/3/library/os.path.html#os.path.exists + * - https://docs.python.org/3/library/os.path.html#os.path.lexists + * - https://docs.python.org/3/library/os.path.html#os.path.isfile + * - https://docs.python.org/3/library/os.path.html#os.path.isdir + * - https://docs.python.org/3/library/os.path.html#os.path.islink + * - https://docs.python.org/3/library/os.path.html#os.path.ismount + * - https://docs.python.org/3/library/os.path.html#os.path.getatime + * - https://docs.python.org/3/library/os.path.html#os.path.getmtime + * - https://docs.python.org/3/library/os.path.html#os.path.getctime + * - https://docs.python.org/3/library/os.path.html#os.path.getsize + * - https://docs.python.org/3/library/os.path.html#os.path.realpath + */ + private class OsPathProbingCall extends FileSystemAccess::Range, DataFlow::CallCfgNode { + OsPathProbingCall() { + this = + os::path() + .getMember([ + // these check if the file exists + "exists", "lexists", "isfile", "isdir", "islink", "ismount", + // these raise errors if the file does not exist + "getatime", "getmtime", "getctime", "getsize" + ]) + .getACall() + } + + override DataFlow::Node getAPathArgument() { + result in [this.getArg(0), this.getArgByName("path")] + } + } + + /** A call to `os.path.samefile` will raise an exception if an `os.stat()` call on either pathname fails. */ + private class OsPathSamefileCall extends FileSystemAccess::Range, DataFlow::CallCfgNode { + OsPathSamefileCall() { this = os::path().getMember("samefile").getACall() } + + override DataFlow::Node getAPathArgument() { + result in [ + this.getArg(0), this.getArgByName("path1"), this.getArg(1), this.getArgByName("path2") + ] + } + } + + // Functions with non-standard arguments: + // - os.path.join(path, *paths) + // - os.path.relpath(path, start=os.curdir) + // these functions need special treatment when computing `getPathArg`. + // + // Functions that excluded because they can act as sanitizers: + // - os.path.commonpath(paths): takes a sequence + // - os.path.commonprefix(list): takes a list argument + // unless the user control all arguments, we are comparing with a known value. + private string pathComputation() { + result in [ + "abspath", "basename", "commonpath", "dirname", "expanduser", "expandvars", "join", + "normcase", "normpath", "realpath", "relpath", "split", "splitdrive", "splitext" + ] + } + + /** + * The `os.path` module offers a number of methods for computing new paths from existing paths. + * These should all propagate taint. + */ + private class OsPathComputation extends DataFlow::CallCfgNode { + string methodName; + + OsPathComputation() { + methodName = pathComputation() and + this = os::path().getMember(methodName).getACall() + } + + DataFlow::Node getPathArg() { + result in [this.getArg(0), this.getArgByName("path")] + or + methodName = "join" and result = this.getArg(_) + or + methodName = "relpath" and result in [this.getArg(1), this.getArgByName("start")] + } + } + + /** An additional taint step for path computations. */ + private class OsPathComputationAdditionalTaintStep extends TaintTracking::AdditionalTaintStep { + override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + exists(OsPathComputation call | + nodeTo = call and + nodeFrom = call.getPathArg() + ) + } + } + /** * A call to `os.path.normpath`. * See https://docs.python.org/3/library/os.path.html#os.path.normpath @@ -205,16 +300,6 @@ private module StdlibPrivate { DataFlow::Node getPathArg() { result in [this.getArg(0), this.getArgByName("path")] } } - /** An additional taint step for calls to `os.path.normpath` */ - private class OsPathNormpathCallAdditionalTaintStep extends TaintTracking::AdditionalTaintStep { - override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - exists(OsPathNormpathCall call | - nodeTo = call and - nodeFrom = call.getPathArg() - ) - } - } - /** * A call to `os.path.abspath`. * See https://docs.python.org/3/library/os.path.html#os.path.abspath @@ -225,16 +310,6 @@ private module StdlibPrivate { DataFlow::Node getPathArg() { result in [this.getArg(0), this.getArgByName("path")] } } - /** An additional taint step for calls to `os.path.abspath` */ - private class OsPathAbspathCallAdditionalTaintStep extends TaintTracking::AdditionalTaintStep { - override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - exists(OsPathAbspathCall call | - nodeTo = call and - nodeFrom = call.getPathArg() - ) - } - } - /** * A call to `os.path.realpath`. * See https://docs.python.org/3/library/os.path.html#os.path.realpath @@ -245,16 +320,6 @@ private module StdlibPrivate { DataFlow::Node getPathArg() { result in [this.getArg(0), this.getArgByName("path")] } } - /** An additional taint step for calls to `os.path.realpath` */ - private class OsPathRealpathCallAdditionalTaintStep extends TaintTracking::AdditionalTaintStep { - override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - exists(OsPathRealpathCall call | - nodeTo = call and - nodeFrom = call.getPathArg() - ) - } - } - /** * A call to `os.system`. * See https://docs.python.org/3/library/os.html#os.system @@ -397,8 +462,8 @@ private module StdlibPrivate { result = this.get_executable_arg() or exists(DataFlow::Node arg_args, boolean shell | - arg_args = get_args_arg() and - shell = get_shell_arg_value() + arg_args = this.get_args_arg() and + shell = this.get_shell_arg_value() | // When "executable" argument is set, and "shell" argument is `False`, the // "args" argument will only be used to set the program name and arguments to @@ -428,6 +493,22 @@ private module StdlibPrivate { // --------------------------------------------------------------------------- // marshal // --------------------------------------------------------------------------- + /** + * A call to `marshal.load` + * See https://docs.python.org/3/library/marshal.html#marshal.load + */ + private class MarshalLoadCall extends Decoding::Range, DataFlow::CallCfgNode { + MarshalLoadCall() { this = API::moduleImport("marshal").getMember("load").getACall() } + + override predicate mayExecuteInput() { any() } + + override DataFlow::Node getAnInput() { result = this.getArg(0) } + + override DataFlow::Node getOutput() { result = this } + + override string getFormat() { result = "marshal" } + } + /** * A call to `marshal.loads` * See https://docs.python.org/3/library/marshal.html#marshal.loads @@ -447,15 +528,23 @@ private module StdlibPrivate { // --------------------------------------------------------------------------- // pickle // --------------------------------------------------------------------------- - /** Gets a reference to the `pickle` module. */ - DataFlow::Node pickle() { result = API::moduleImport(["pickle", "cPickle", "_pickle"]).getAUse() } + /** Gets a reference to any of the `pickle` modules. */ + API::Node pickle() { result = API::moduleImport(["pickle", "cPickle", "_pickle"]) } - /** Provides models for the `pickle` module. */ - module pickle { - /** Gets a reference to the `pickle.loads` function. */ - DataFlow::Node loads() { - result = API::moduleImport(["pickle", "cPickle", "_pickle"]).getMember("loads").getAUse() - } + /** + * A call to `pickle.load` + * See https://docs.python.org/3/library/pickle.html#pickle.load + */ + private class PickleLoadCall extends Decoding::Range, DataFlow::CallCfgNode { + PickleLoadCall() { this = pickle().getMember("load").getACall() } + + override predicate mayExecuteInput() { any() } + + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("file")] } + + override DataFlow::Node getOutput() { result = this } + + override string getFormat() { result = "pickle" } } /** @@ -463,11 +552,63 @@ private module StdlibPrivate { * See https://docs.python.org/3/library/pickle.html#pickle.loads */ private class PickleLoadsCall extends Decoding::Range, DataFlow::CallCfgNode { - PickleLoadsCall() { this.getFunction() = pickle::loads() } + PickleLoadsCall() { this = pickle().getMember("loads").getACall() } override predicate mayExecuteInput() { any() } - override DataFlow::Node getAnInput() { result = this.getArg(0) } + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("data")] } + + override DataFlow::Node getOutput() { result = this } + + override string getFormat() { result = "pickle" } + } + + /** + * A construction of a `pickle.Unpickler` + * See https://docs.python.org/3/library/pickle.html#pickle.Unpickler + */ + private class PickleUnpicklerCall extends Decoding::Range, DataFlow::CallCfgNode { + PickleUnpicklerCall() { this = pickle().getMember("Unpickler").getACall() } + + override predicate mayExecuteInput() { any() } + + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("file")] } + + override DataFlow::Node getOutput() { result = this.getAMethodCall("load") } + + override string getFormat() { result = "pickle" } + } + + // --------------------------------------------------------------------------- + // shelve + // --------------------------------------------------------------------------- + /** + * A call to `shelve.open` + * See https://docs.python.org/3/library/shelve.html#shelve.open + * + * Claiming there is decoding of the input to `shelve.open` is a bit questionable, since + * it's not the filename, but the contents of the file that is decoded. + * + * However, we definitely want to be able to alert if a user is able to control what + * file is used, since that can lead to code execution (even if that file is free of + * path injection). + * + * So right now the best way we have of modeling this seems to be to treat the filename + * argument as being deserialized... + */ + private class ShelveOpenCall extends Decoding::Range, FileSystemAccess::Range, + DataFlow::CallCfgNode { + ShelveOpenCall() { this = API::moduleImport("shelve").getMember("open").getACall() } + + override predicate mayExecuteInput() { any() } + + override DataFlow::Node getAnInput() { + result in [this.getArg(0), this.getArgByName("filename")] + } + + override DataFlow::Node getAPathArgument() { + result in [this.getArg(0), this.getArgByName("filename")] + } override DataFlow::Node getOutput() { result = this } @@ -1136,7 +1277,7 @@ private module StdlibPrivate { /** * Gets a name of an attribute of a `pathlib.Path` object that is also a `pathlib.Path` object. */ - private string pathlibPathAttribute() { result in ["parent"] } + private string pathlibPathAttribute() { result = "parent" } /** * Gets a name of a method of a `pathlib.Path` object that returns a `pathlib.Path` object. @@ -1495,6 +1636,119 @@ private module StdlibPrivate { result = this.getArg(any(int i | i >= msgIndex)) } } + + // --------------------------------------------------------------------------- + // re + // --------------------------------------------------------------------------- + /** + * List of methods in the `re` module immediately executing a regular expression. + * + * See https://docs.python.org/3/library/re.html#module-contents + */ + private class RegexExecutionMethod extends string { + RegexExecutionMethod() { + this in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn"] + } + + /** Gets the index of the argument representing the string to be searched by a regex. */ + int getStringArgIndex() { + this in ["match", "fullmatch", "search", "split", "findall", "finditer"] and + result = 1 + or + this in ["sub", "subn"] and + result = 2 + } + } + + /** + * A a call to a method from the `re` module immediately executing a regular expression. + * + * See `RegexExecutionMethods` + */ + private class DirectRegexExecution extends DataFlow::CallCfgNode, RegexExecution::Range { + RegexExecutionMethod method; + + DirectRegexExecution() { this = API::moduleImport("re").getMember(method).getACall() } + + override DataFlow::Node getRegex() { result in [this.getArg(0), this.getArgByName("pattern")] } + + override DataFlow::Node getString() { + result in [this.getArg(method.getStringArgIndex()), this.getArgByName("string")] + } + + override string getName() { result = "re." + method } + } + + /** Helper module for tracking compiled regexes. */ + private module CompiledRegexes { + private DataFlow::TypeTrackingNode compiledRegex(DataFlow::TypeTracker t, DataFlow::Node regex) { + t.start() and + result = API::moduleImport("re").getMember("compile").getACall() and + regex in [ + result.(DataFlow::CallCfgNode).getArg(0), + result.(DataFlow::CallCfgNode).getArgByName("pattern") + ] + or + exists(DataFlow::TypeTracker t2 | result = compiledRegex(t2, regex).track(t2, t)) + } + + DataFlow::Node compiledRegex(DataFlow::Node regex) { + compiledRegex(DataFlow::TypeTracker::end(), regex).flowsTo(result) + } + } + + private import CompiledRegexes + + /** + * A call on compiled regular expression (obtained via `re.compile`) executing a + * regular expression. + * + * Given the following example: + * + * ```py + * pattern = re.compile(input) + * pattern.match(s) + * ``` + * + * This class will identify that `re.compile` compiles `input` and afterwards + * executes `re`'s `match`. As a result, `this` will refer to `pattern.match(s)` + * and `this.getRegexNode()` will return the node for `input` (`re.compile`'s first argument). + * + * + * See `RegexExecutionMethods` + * + * See https://docs.python.org/3/library/re.html#regular-expression-objects + */ + private class CompiledRegexExecution extends DataFlow::MethodCallNode, RegexExecution::Range { + DataFlow::Node regexNode; + RegexExecutionMethod method; + + CompiledRegexExecution() { this.calls(compiledRegex(regexNode), method) } + + override DataFlow::Node getRegex() { result = regexNode } + + override DataFlow::Node getString() { + result in [this.getArg(method.getStringArgIndex() - 1), this.getArgByName("string")] + } + + override string getName() { result = "re." + method } + } + + /** + * A call to 're.escape'. + * See https://docs.python.org/3/library/re.html#re.escape + */ + private class ReEscapeCall extends Escaping::Range, DataFlow::CallCfgNode { + ReEscapeCall() { this = API::moduleImport("re").getMember("escape").getACall() } + + override DataFlow::Node getAnInput() { + result in [this.getArg(0), this.getArgByName("pattern")] + } + + override DataFlow::Node getOutput() { result = this } + + override string getKind() { result = Escaping::getRegexKind() } + } } // --------------------------------------------------------------------------- diff --git a/python/ql/lib/semmle/python/frameworks/Tornado.qll b/python/ql/lib/semmle/python/frameworks/Tornado.qll index ba4898facc8..91ae3ac2575 100644 --- a/python/ql/lib/semmle/python/frameworks/Tornado.qll +++ b/python/ql/lib/semmle/python/frameworks/Tornado.qll @@ -318,7 +318,7 @@ private module Tornado { ] } - override string getMethodName() { result in ["full_url"] } + override string getMethodName() { result = "full_url" } override string getAsyncMethodName() { none() } } diff --git a/python/ql/lib/semmle/python/frameworks/Werkzeug.qll b/python/ql/lib/semmle/python/frameworks/Werkzeug.qll index 039481f8522..e9e3f257871 100644 --- a/python/ql/lib/semmle/python/frameworks/Werkzeug.qll +++ b/python/ql/lib/semmle/python/frameworks/Werkzeug.qll @@ -58,7 +58,7 @@ module Werkzeug { override string getAttributeName() { none() } - override string getMethodName() { result in ["getlist"] } + override string getMethodName() { result = "getlist" } override string getAsyncMethodName() { none() } } diff --git a/python/ql/lib/semmle/python/frameworks/Yaml.qll b/python/ql/lib/semmle/python/frameworks/Yaml.qll index e818b2e95ec..07a98ec2ba3 100644 --- a/python/ql/lib/semmle/python/frameworks/Yaml.qll +++ b/python/ql/lib/semmle/python/frameworks/Yaml.qll @@ -9,7 +9,6 @@ private import python private import semmle.python.dataflow.new.DataFlow -private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.Concepts private import semmle.python.ApiGraphs @@ -41,11 +40,17 @@ private module Yaml { } /** - * This function was thought safe from the 5.1 release in 2017, when the default loader was changed to `FullLoader`. - * In 2020 new exploits were found, meaning it's not safe. The Current plan is to change the default to `SafeLoader` in release 6.0 - * (as explained in https://github.com/yaml/pyyaml/issues/420#issuecomment-696752389). - * Until 6.0 is released, we will mark `yaml.load` as possibly leading to arbitrary code execution. - * See https://github.com/yaml/pyyaml/wiki/PyYAML-yaml.load(input)-Deprecation for more details. + * This function was thought safe from the 5.1 release in 2017, when the default + * loader was changed to `FullLoader` (see + * https://github.com/yaml/pyyaml/wiki/PyYAML-yaml.load(input)-Deprecation). + * + * In 2020 new exploits were found, meaning it's not safe. With the 6.0 release (see + * https://github.com/yaml/pyyaml/commit/8cdff2c80573b8be8e8ad28929264a913a63aa33), + * when using `load` and `load_all` you are now required to specify a Loader. But + * from what I (@RasmusWL) can gather, `FullLoader` is not to be considered safe, + * although known exploits have been mitigated (is at least my impression). Also see + * https://github.com/yaml/pyyaml/issues/420#issuecomment-696752389 for more + * details. */ override predicate mayExecuteInput() { func_name in ["full_load", "full_load_all", "unsafe_load", "unsafe_load_all"] @@ -63,7 +68,7 @@ private module Yaml { ) } - override DataFlow::Node getAnInput() { result = this.getArg(0) } + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("stream")] } override DataFlow::Node getOutput() { result = this } diff --git a/python/ql/lib/semmle/python/frameworks/Yarl.qll b/python/ql/lib/semmle/python/frameworks/Yarl.qll index 00b0911471b..5ea78c1ac8e 100644 --- a/python/ql/lib/semmle/python/frameworks/Yarl.qll +++ b/python/ql/lib/semmle/python/frameworks/Yarl.qll @@ -68,7 +68,7 @@ module Yarl { ] } - override string getMethodName() { result in ["human_repr"] } + override string getMethodName() { result = "human_repr" } override string getAsyncMethodName() { none() } } diff --git a/python/ql/lib/semmle/python/objects/ObjectAPI.qll b/python/ql/lib/semmle/python/objects/ObjectAPI.qll index 683bcc5ce36..946eb1de04d 100644 --- a/python/ql/lib/semmle/python/objects/ObjectAPI.qll +++ b/python/ql/lib/semmle/python/objects/ObjectAPI.qll @@ -79,7 +79,7 @@ class Value extends TObject { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/python/ql/lib/semmle/python/objects/TObject.qll b/python/ql/lib/semmle/python/objects/TObject.qll index d83d47af1ee..99eb05aa795 100644 --- a/python/ql/lib/semmle/python/objects/TObject.qll +++ b/python/ql/lib/semmle/python/objects/TObject.qll @@ -387,7 +387,7 @@ private predicate concrete_class(PythonClassObjectInternal cls) { not exists(Raise r, Name ex | r.getScope() = f and (r.getException() = ex or r.getException().(Call).getFunc() = ex) and - (ex.getId() = "NotImplementedError" or ex.getId() = "NotImplemented") + ex.getId() = ["NotImplementedError", "NotImplemented"] ) ) ) @@ -437,11 +437,7 @@ predicate missing_imported_module(ControlFlowNode imp, Context ctx, string name) * Helper for missing modules to determine if name `x.y` is a module `x.y` or * an attribute `y` of module `x`. This list should be added to as required. */ -predicate common_module_name(string name) { - name = "zope.interface" - or - name = "six.moves" -} +predicate common_module_name(string name) { name = ["zope.interface", "six.moves"] } /** * A declaration of a class, either a built-in class or a source definition @@ -482,16 +478,11 @@ library class ClassDecl extends @py_object { */ predicate isSpecial() { exists(string name | this = Builtin::special(name) | - name = "type" or - name = "super" or - name = "bool" or - name = "NoneType" or - name = "tuple" or - name = "property" or - name = "ClassMethod" or - name = "StaticMethod" or - name = "MethodType" or - name = "ModuleType" + name = + [ + "type", "super", "bool", "NoneType", "tuple", "property", "ClassMethod", "StaticMethod", + "MethodType", "ModuleType" + ] ) } @@ -514,11 +505,7 @@ library class ClassDecl extends @py_object { /** Holds if this class is the abstract base class */ predicate isAbstractBaseClass(string name) { - exists(Module m | - m.getName() = "_abcoll" - or - m.getName() = "_collections_abc" - | + exists(Module m | m.getName() = ["_abcoll", "_collections_abc"] | this.getClass().getScope() = m and this.getName() = name ) diff --git a/python/ql/lib/semmle/python/pointsto/PointsTo.qll b/python/ql/lib/semmle/python/pointsto/PointsTo.qll index 48bbb283d07..548b25115ae 100644 --- a/python/ql/lib/semmle/python/pointsto/PointsTo.qll +++ b/python/ql/lib/semmle/python/pointsto/PointsTo.qll @@ -300,7 +300,7 @@ module PointsToInternal { ssa_definition_points_to(var.getDefinition(), context, value, origin) or exists(EssaVariable prev | - ssaShortCut+(prev, var) and + ssaShortCut(prev, var) and variablePointsTo(prev, context, value, origin) ) } diff --git a/python/ql/lib/semmle/python/regex.qll b/python/ql/lib/semmle/python/regex.qll index 883d2c3fbc4..09719aa2fe6 100644 --- a/python/ql/lib/semmle/python/regex.qll +++ b/python/ql/lib/semmle/python/regex.qll @@ -102,15 +102,7 @@ string mode_from_node(DataFlow::Node node) { node = re_flag_tracker(result) } * Gets a regular expression mode flag associated with the given value. */ deprecated string mode_from_mode_object(Value obj) { - ( - result = "DEBUG" or - result = "IGNORECASE" or - result = "LOCALE" or - result = "MULTILINE" or - result = "DOTALL" or - result = "UNICODE" or - result = "VERBOSE" - ) and + result in ["DEBUG", "IGNORECASE", "LOCALE", "MULTILINE", "DOTALL", "UNICODE", "VERBOSE"] and exists(int flag | flag = Value::named("sre_constants.SRE_FLAG_" + result).(OI::ObjectInternal).intValue() and obj.(OI::ObjectInternal).intValue().bitAnd(flag) = flag @@ -611,14 +603,7 @@ abstract class RegexString extends Expr { this.getChar(start + 1) = "?" and end = start + 3 and c = this.getChar(start + 2) and - ( - c = "i" or - c = "L" or - c = "m" or - c = "s" or - c = "u" or - c = "x" - ) + c in ["i", "L", "m", "s", "u", "x"] } /** diff --git a/python/ql/lib/semmle/python/security/ClearText.qll b/python/ql/lib/semmle/python/security/ClearText.qll index 8e964d19386..9905040da18 100644 --- a/python/ql/lib/semmle/python/security/ClearText.qll +++ b/python/ql/lib/semmle/python/security/ClearText.qll @@ -47,11 +47,7 @@ module ClearTextLogging { meth.getObject(name).(NameNode).getId().matches("logg%") and call.getAnArg() = this | - name = "error" or - name = "warn" or - name = "warning" or - name = "debug" or - name = "info" + name = ["error", "warn", "warning", "debug", "info"] ) } } diff --git a/python/ql/lib/semmle/python/security/Exceptions.qll b/python/ql/lib/semmle/python/security/Exceptions.qll index 25b0592c931..7bc4374bd64 100644 --- a/python/ql/lib/semmle/python/security/Exceptions.qll +++ b/python/ql/lib/semmle/python/security/Exceptions.qll @@ -74,13 +74,10 @@ class ExceptionInfoSequence extends SequenceKind { class CallToTracebackFunction extends ErrorInfoSource { CallToTracebackFunction() { exists(string name | - name = "extract_tb" or - name = "extract_stack" or - name = "format_list" or - name = "format_exception_only" or - name = "format_exception" or - name = "format_tb" or - name = "format_stack" + name in [ + "extract_tb", "extract_stack", "format_list", "format_exception_only", "format_exception", + "format_tb", "format_stack" + ] | this = traceback_function(name).getACall() ) diff --git a/python/ql/lib/semmle/python/security/dataflow/ChainedConfigs12.qll b/python/ql/lib/semmle/python/security/dataflow/ChainedConfigs12.qll index ea2af4f8e8d..241ec52c7d0 100644 --- a/python/ql/lib/semmle/python/security/dataflow/ChainedConfigs12.qll +++ b/python/ql/lib/semmle/python/security/dataflow/ChainedConfigs12.qll @@ -46,7 +46,7 @@ class CustomPathNode extends TCustomPathNode { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/python/ql/lib/semmle/python/security/dataflow/PolynomialReDoSCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/PolynomialReDoSCustomizations.qll index cbaf3b982e9..b92ff341ec8 100644 --- a/python/ql/lib/semmle/python/security/dataflow/PolynomialReDoSCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/PolynomialReDoSCustomizations.qll @@ -60,8 +60,8 @@ module PolynomialReDoS { RegExpTerm t; RegexExecutionAsSink() { - exists(CompiledRegexes::RegexExecution re | - re.getRegexNode().asExpr() = t.getRegex() and + exists(RegexExecution re | + re.getRegex().asExpr() = t.getRegex() and this = re.getString() ) and t.isRootTerm() @@ -76,137 +76,3 @@ module PolynomialReDoS { */ class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { } } - -/** Helper module for tracking compiled regexes. */ -private module CompiledRegexes { - // TODO: This module should be refactored and merged with the experimental work done on detecting - // regex injections, such that this can be expressed from just using a concept. - /** A configuration for finding uses of compiled regexes. */ - class RegexDefinitionConfiguration extends DataFlow2::Configuration { - RegexDefinitionConfiguration() { this = "RegexDefinitionConfiguration" } - - override predicate isSource(DataFlow::Node source) { source instanceof RegexDefinitonSource } - - override predicate isSink(DataFlow::Node sink) { sink instanceof RegexDefinitionSink } - } - - /** A regex compilation. */ - class RegexDefinitonSource extends DataFlow::CallCfgNode { - DataFlow::Node regexNode; - - RegexDefinitonSource() { - this = API::moduleImport("re").getMember("compile").getACall() and - regexNode in [this.getArg(0), this.getArgByName("pattern")] - } - - /** Gets the regex that is being compiled by this node. */ - RegExpTerm getRegExp() { result.getRegex() = regexNode.asExpr() and result.isRootTerm() } - - /** Gets the data flow node for the regex being compiled by this node. */ - DataFlow::Node getRegexNode() { result = regexNode } - } - - /** A use of a compiled regex. */ - class RegexDefinitionSink extends DataFlow::Node { - RegexExecutionMethod method; - DataFlow::CallCfgNode executingCall; - - RegexDefinitionSink() { - exists(DataFlow::AttrRead reMethod | - executingCall.getFunction() = reMethod and - reMethod.getAttributeName() = method and - this = reMethod.getObject() - ) - } - - /** Gets the method used to execute the regex. */ - RegexExecutionMethod getMethod() { result = method } - - /** Gets the data flow node for the executing call. */ - DataFlow::CallCfgNode getExecutingCall() { result = executingCall } - } - - /** A data flow node executing a regex. */ - abstract class RegexExecution extends DataFlow::Node { - /** Gets the data flow node for the regex being compiled by this node. */ - abstract DataFlow::Node getRegexNode(); - - /** Gets a dataflow node for the string to be searched or matched against. */ - abstract DataFlow::Node getString(); - } - - private class RegexExecutionMethod extends string { - RegexExecutionMethod() { - this in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn"] - } - } - - /** Gets the index of the argument representing the string to be searched by a regex. */ - int stringArg(RegexExecutionMethod method) { - method in ["match", "fullmatch", "search", "split", "findall", "finditer"] and - result = 1 - or - method in ["sub", "subn"] and - result = 2 - } - - /** - * A class to find `re` methods immediately executing an expression. - * - * See `RegexExecutionMethods` - */ - class DirectRegex extends DataFlow::CallCfgNode, RegexExecution { - RegexExecutionMethod method; - - DirectRegex() { this = API::moduleImport("re").getMember(method).getACall() } - - override DataFlow::Node getRegexNode() { - result in [this.getArg(0), this.getArgByName("pattern")] - } - - override DataFlow::Node getString() { - result in [this.getArg(stringArg(method)), this.getArgByName("string")] - } - } - - /** - * A class to find `re` methods immediately executing a compiled expression by `re.compile`. - * - * Given the following example: - * - * ```py - * pattern = re.compile(input) - * pattern.match(s) - * ``` - * - * This class will identify that `re.compile` compiles `input` and afterwards - * executes `re`'s `match`. As a result, `this` will refer to `pattern.match(s)` - * and `this.getRegexNode()` will return the node for `input` (`re.compile`'s first argument) - * - * - * See `RegexExecutionMethods` - * - * See https://docs.python.org/3/library/re.html#regular-expression-objects - */ - private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution { - DataFlow::Node regexNode; - RegexExecutionMethod method; - - CompiledRegex() { - exists( - RegexDefinitionConfiguration conf, RegexDefinitonSource source, RegexDefinitionSink sink - | - conf.hasFlow(source, sink) and - regexNode = source.getRegexNode() and - method = sink.getMethod() and - this = sink.getExecutingCall() - ) - } - - override DataFlow::Node getRegexNode() { result = regexNode } - - override DataFlow::Node getString() { - result in [this.getArg(stringArg(method) - 1), this.getArgByName("string")] - } - } -} diff --git a/python/ql/lib/semmle/python/security/dataflow/ReflectedXSSCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/ReflectedXSSCustomizations.qll index 0e5410a8be2..93363d3409a 100644 --- a/python/ql/lib/semmle/python/security/dataflow/ReflectedXSSCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/ReflectedXSSCustomizations.qll @@ -59,7 +59,7 @@ module ReflectedXSS { class HtmlEscapingAsSanitizer extends Sanitizer { HtmlEscapingAsSanitizer() { // TODO: For now, since there is not an `isSanitizingStep` member-predicate part of a - // `TaintTracking::Configuration`, we use treat the output is a taint-sanitizer. This + // `TaintTracking::Configuration`, we treat the output as a taint-sanitizer. This // is slightly imprecise, which you can see in the `m_unsafe + SAFE` test-case in // python/ql/test/library-tests/frameworks/markupsafe/taint_test.py // diff --git a/python/ql/lib/semmle/python/security/injection/Command.qll b/python/ql/lib/semmle/python/security/injection/Command.qll index 3ed453268ee..2bb4d275938 100644 --- a/python/ql/lib/semmle/python/security/injection/Command.qll +++ b/python/ql/lib/semmle/python/security/injection/Command.qll @@ -13,18 +13,11 @@ import semmle.python.security.strings.Untrusted /** Abstract taint sink that is potentially vulnerable to malicious shell commands. */ abstract class CommandSink extends TaintSink { } -private ModuleObject osOrPopenModule() { - result.getName() = "os" or - result.getName() = "popen2" -} +private ModuleObject osOrPopenModule() { result.getName() = ["os", "popen2"] } private Object makeOsCall() { exists(string name | result = ModuleObject::named("subprocess").attr(name) | - name = "Popen" or - name = "call" or - name = "check_call" or - name = "check_output" or - name = "run" + name = ["Popen", "call", "check_call", "check_output", "run"] ) } @@ -65,8 +58,7 @@ class ShellCommand extends CommandSink { call.getAnArg() = this and call.getFunction().refersTo(osOrPopenModule().attr(name)) | - name = "system" or - name = "popen" or + name = ["system", "popen"] or name.matches("popen_") ) or diff --git a/python/ql/lib/semmle/python/security/injection/RegexInjection.qll b/python/ql/lib/semmle/python/security/injection/RegexInjection.qll new file mode 100644 index 00000000000..80601bd638f --- /dev/null +++ b/python/ql/lib/semmle/python/security/injection/RegexInjection.qll @@ -0,0 +1,37 @@ +/** + * Provides a taint-tracking configuration for detecting regular expression injection + * vulnerabilities. + * + * Note, for performance reasons: only import this file if + * `RegexInjection::Configuration` is needed, otherwise + * `RegexInjectionCustomizations` should be imported instead. + */ + +private import python +import semmle.python.dataflow.new.DataFlow +import semmle.python.dataflow.new.TaintTracking + +/** + * Provides a taint-tracking configuration for detecting regular expression injection + * vulnerabilities. + */ +module RegexInjection { + import RegexInjectionCustomizations::RegexInjection + + /** + * A taint-tracking configuration for detecting "reflected server-side cross-site scripting" vulnerabilities. + */ + class Configuration extends TaintTracking::Configuration { + Configuration() { this = "RegexInjection" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + + override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer } + + override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { + guard instanceof SanitizerGuard + } + } +} diff --git a/python/ql/lib/semmle/python/security/injection/RegexInjectionCustomizations.qll b/python/ql/lib/semmle/python/security/injection/RegexInjectionCustomizations.qll new file mode 100644 index 00000000000..c26bae1c1b4 --- /dev/null +++ b/python/ql/lib/semmle/python/security/injection/RegexInjectionCustomizations.qll @@ -0,0 +1,62 @@ +/** + * Provides default sources, sinks and sanitizers for detecting + * "regular expression injection" + * vulnerabilities, as well as extension points for adding your own. + */ + +private import python +private import semmle.python.Concepts +private import semmle.python.dataflow.new.DataFlow +private import semmle.python.dataflow.new.TaintTracking +private import semmle.python.dataflow.new.RemoteFlowSources + +/** + * Provides default sources, sinks and sanitizers for detecting + * "regular expression injection" + * vulnerabilities, as well as extension points for adding your own. + */ +module RegexInjection { + /** + * A data flow source for "regular expression injection" vulnerabilities. + */ + abstract class Source extends DataFlow::Node { } + + /** + * A sink for "regular expression injection" vulnerabilities is the execution of a regular expression. + * If you have a custom way to execute regular expressions, you can extend `RegexExecution::Range`. + */ + class Sink extends DataFlow::Node { + RegexExecution regexExecution; + + Sink() { this = regexExecution.getRegex() } + + /** Gets the call that executes the regular expression marked by this sink. */ + RegexExecution getRegexExecution() { result = regexExecution } + } + + /** + * A sanitizer for "regular expression injection" vulnerabilities. + */ + abstract class Sanitizer extends DataFlow::Node { } + + /** + * A sanitizer guard for "regular expression injection" vulnerabilities. + */ + abstract class SanitizerGuard extends DataFlow::BarrierGuard { } + + /** + * A source of remote user input, considered as a flow source. + */ + class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + + /** + * A regex escaping, considered as a sanitizer. + */ + class RegexEscapingAsSanitizer extends Sanitizer { + RegexEscapingAsSanitizer() { + // Due to use-use flow, we want the output rather than an input + // (so the input can still flow to other sinks). + this = any(RegexEscaping esc).getOutput() + } + } +} diff --git a/python/ql/lib/semmle/python/security/performance/ReDoSUtil.qll b/python/ql/lib/semmle/python/security/performance/ReDoSUtil.qll index 65431d7179e..2cd324ed8f7 100644 --- a/python/ql/lib/semmle/python/security/performance/ReDoSUtil.qll +++ b/python/ql/lib/semmle/python/security/performance/ReDoSUtil.qll @@ -139,8 +139,6 @@ class RegExpRoot extends RegExpTerm { predicate isRelevant() { // there is at least one repetition getRoot(any(InfiniteRepetitionQuantifier q)) = this and - // there are no lookbehinds - not exists(RegExpLookbehind lbh | getRoot(lbh) = this) and // is actually used as a RegExp isUsedAsRegExp() and // not excluded for library specific reasons @@ -479,7 +477,7 @@ private module CharacterClasses { result = ["0", "9"] or cc.getValue() = "s" and - result = [" "] + result = " " or cc.getValue() = "w" and result = ["a", "Z", "_", "0", "9"] @@ -492,7 +490,7 @@ private module CharacterClasses { result = "9" or cc.getValue() = "s" and - result = [" "] + result = " " or cc.getValue() = "w" and result = "a" diff --git a/python/ql/lib/semmle/python/templates/PyxlTags.qll b/python/ql/lib/semmle/python/templates/PyxlTags.qll index f0e663cdad0..abfef070d78 100644 --- a/python/ql/lib/semmle/python/templates/PyxlTags.qll +++ b/python/ql/lib/semmle/python/templates/PyxlTags.qll @@ -29,7 +29,7 @@ private predicate pyxl_tag(Call c, string name) { } class PyxlHtmlTag extends PyxlTag { - PyxlHtmlTag() { this.getPyxlTagName().prefix(2) = "x_" } + PyxlHtmlTag() { this.getPyxlTagName().matches("x\\_%") } string getTagName() { result = this.getPyxlTagName().suffix(2) } diff --git a/python/ql/lib/semmle/python/types/Extensions.qll b/python/ql/lib/semmle/python/types/Extensions.qll index 9c067ed7e4a..a692782b92a 100644 --- a/python/ql/lib/semmle/python/types/Extensions.qll +++ b/python/ql/lib/semmle/python/types/Extensions.qll @@ -112,14 +112,7 @@ class BottleRoutePointToExtension extends PointsToExtension { /* Python 3.6+ regex module constants */ string short_flag(string flag) { - ( - flag = "ASCII" or - flag = "IGNORECASE" or - flag = "LOCALE" or - flag = "UNICODE" or - flag = "MULTILINE" or - flag = "TEMPLATE" - ) and + flag in ["ASCII", "IGNORECASE", "LOCALE", "UNICODE", "MULTILINE", "TEMPLATE"] and result = flag.prefix(1) or flag = "DOTALL" and result = "S" diff --git a/python/ql/lib/semmle/python/types/FunctionObject.qll b/python/ql/lib/semmle/python/types/FunctionObject.qll index c293a43d675..eb2f1e07c8b 100644 --- a/python/ql/lib/semmle/python/types/FunctionObject.qll +++ b/python/ql/lib/semmle/python/types/FunctionObject.qll @@ -183,6 +183,7 @@ class PyFunctionObject extends FunctionObject { } /** Factored out to help join ordering */ + pragma[noinline] private predicate implicitlyReturns(Object none_, ClassObject noneType) { noneType = theNoneType() and not this.getFunction().isGenerator() and diff --git a/python/ql/lib/semmle/python/types/Object.qll b/python/ql/lib/semmle/python/types/Object.qll index a9e360080ce..9b5472af69a 100644 --- a/python/ql/lib/semmle/python/types/Object.qll +++ b/python/ql/lib/semmle/python/types/Object.qll @@ -69,7 +69,7 @@ class Object extends @py_object { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/python/ql/lib/semmle/python/web/Http.qll b/python/ql/lib/semmle/python/web/Http.qll index 527a050d814..fc1b1bc5756 100644 --- a/python/ql/lib/semmle/python/web/Http.qll +++ b/python/ql/lib/semmle/python/web/Http.qll @@ -33,7 +33,7 @@ class WsgiEnvironment extends TaintKind { ( text = "QUERY_STRING" or text = "PATH_INFO" or - text.prefix(5) = "HTTP_" + text.matches("HTTP\\_%") ) ) } diff --git a/python/ql/lib/semmle/python/web/HttpConstants.qll b/python/ql/lib/semmle/python/web/HttpConstants.qll index d3e8b0412a4..f2ed3c92f97 100644 --- a/python/ql/lib/semmle/python/web/HttpConstants.qll +++ b/python/ql/lib/semmle/python/web/HttpConstants.qll @@ -1,13 +1,5 @@ /** Gets an HTTP verb, in upper case */ -string httpVerb() { - result = "GET" or - result = "POST" or - result = "PUT" or - result = "PATCH" or - result = "DELETE" or - result = "OPTIONS" or - result = "HEAD" -} +string httpVerb() { result in ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"] } /** Gets an HTTP verb, in lower case */ string httpVerbLower() { result = httpVerb().toLowerCase() } diff --git a/python/ql/lib/semmle/python/web/django/Model.qll b/python/ql/lib/semmle/python/web/django/Model.qll index d48fe6e04f9..0b04340091a 100644 --- a/python/ql/lib/semmle/python/web/django/Model.qll +++ b/python/ql/lib/semmle/python/web/django/Model.qll @@ -15,31 +15,11 @@ class DjangoDbTableObjects extends TaintKind { override TaintKind getTaintOfMethodResult(string name) { result = this and - ( - name = "filter" or - name = "exclude" or - name = "annotate" or - name = "order_by" or - name = "reverse" or - name = "distinct" or - name = "values" or - name = "values_list" or - name = "dates" or - name = "datetimes" or - name = "none" or - name = "all" or - name = "union" or - name = "intersection" or - name = "difference" or - name = "select_related" or - name = "prefetch_related" or - name = "extra" or - name = "defer" or - name = "only" or - name = "using" or - name = "select_for_update" or - name = "raw" - ) + name in [ + "filter", "exclude", "none", "all", "union", "intersection", "difference", "select_related", + "prefetch_related", "extra", "defer", "only", "annotate", "using", "select_for_update", + "raw", "order_by", "reverse", "distinct", "values", "values_list", "dates", "datetimes" + ] } } diff --git a/python/ql/lib/semmle/python/web/falcon/Request.qll b/python/ql/lib/semmle/python/web/falcon/Request.qll index 4b6ceb93fb6..3951d80d29d 100644 --- a/python/ql/lib/semmle/python/web/falcon/Request.qll +++ b/python/ql/lib/semmle/python/web/falcon/Request.qll @@ -12,13 +12,7 @@ class FalconRequest extends TaintKind { name = "env" and result instanceof WsgiEnvironment or result instanceof ExternalStringKind and - ( - name = "uri" or - name = "url" or - name = "forwarded_uri" or - name = "relative_uri" or - name = "query_string" - ) + name in ["uri", "url", "forwarded_uri", "relative_uri", "query_string"] or result instanceof ExternalStringDictKind and (name = "cookies" or name = "params") diff --git a/python/ql/lib/semmle/python/web/flask/Request.qll b/python/ql/lib/semmle/python/web/flask/Request.qll index ceae5e7a2c6..67bf77a5ac5 100644 --- a/python/ql/lib/semmle/python/web/flask/Request.qll +++ b/python/ql/lib/semmle/python/web/flask/Request.qll @@ -32,12 +32,7 @@ class FlaskRequestData extends HttpRequestTaintSource { class FlaskRequestArgs extends HttpRequestTaintSource { FlaskRequestArgs() { exists(string attr | flask_request_attr(this, attr) | - attr = "args" or - attr = "form" or - attr = "values" or - attr = "files" or - attr = "headers" or - attr = "json" + attr in ["args", "form", "values", "files", "headers", "json"] ) } diff --git a/python/ql/lib/semmle/python/xml/XML.qll b/python/ql/lib/semmle/python/xml/XML.qll index 5871fed0ddd..76f3b3cb022 100755 --- a/python/ql/lib/semmle/python/xml/XML.qll +++ b/python/ql/lib/semmle/python/xml/XML.qll @@ -24,7 +24,7 @@ class XMLLocatable extends @xmllocatable, TXMLLocatable { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -108,7 +108,7 @@ class XMLParent extends @xmlparent { } /** Gets the text value contained in this XML parent. */ - string getTextValue() { result = allCharactersString() } + string getTextValue() { result = this.allCharactersString() } /** Gets a printable representation of this XML parent. */ string toString() { result = this.getName() } @@ -119,7 +119,7 @@ class XMLFile extends XMLParent, File { XMLFile() { xmlEncoding(this, _) } /** Gets a printable representation of this XML file. */ - override string toString() { result = getName() } + override string toString() { result = this.getName() } /** Gets the name of this XML file. */ override string getName() { result = File.super.getAbsolutePath() } @@ -129,14 +129,14 @@ class XMLFile extends XMLParent, File { * * Gets the path of this XML file. */ - deprecated string getPath() { result = getAbsolutePath() } + deprecated string getPath() { result = this.getAbsolutePath() } /** * DEPRECATED: Use `getParentContainer().getAbsolutePath()` instead. * * Gets the path of the folder that contains this XML file. */ - deprecated string getFolder() { result = getParentContainer().getAbsolutePath() } + deprecated string getFolder() { result = this.getParentContainer().getAbsolutePath() } /** Gets the encoding of this XML file. */ string getEncoding() { xmlEncoding(this, result) } @@ -200,7 +200,7 @@ class XMLDTD extends XMLLocatable, @xmldtd { */ class XMLElement extends @xmlelement, XMLParent, XMLLocatable { /** Holds if this XML element has the given `name`. */ - predicate hasName(string name) { name = getName() } + predicate hasName(string name) { name = this.getName() } /** Gets the name of this XML element. */ override string getName() { xmlElements(this, result, _, _, _) } @@ -239,7 +239,7 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable { string getAttributeValue(string name) { result = this.getAttribute(name).getValue() } /** Gets a printable representation of this XML element. */ - override string toString() { result = getName() } + override string toString() { result = this.getName() } } /** diff --git a/python/ql/lib/tutorial.qll b/python/ql/lib/tutorial.qll new file mode 100644 index 00000000000..8cb1797a532 --- /dev/null +++ b/python/ql/lib/tutorial.qll @@ -0,0 +1,1207 @@ +/** + * This library is used in the QL detective tutorials. + * + * Note: Data is usually stored in a separate database and the QL libraries only contain predicates, + * but for this tutorial both the data and the predicates are stored in the library. + */ +class Person extends string { + Person() { + this = "Ronil" or + this = "Dina" or + this = "Ravi" or + this = "Bruce" or + this = "Jo" or + this = "Aida" or + this = "Esme" or + this = "Charlie" or + this = "Fred" or + this = "Meera" or + this = "Maya" or + this = "Chad" or + this = "Tiana" or + this = "Laura" or + this = "George" or + this = "Will" or + this = "Mary" or + this = "Almira" or + this = "Susannah" or + this = "Rhoda" or + this = "Cynthia" or + this = "Eunice" or + this = "Olive" or + this = "Virginia" or + this = "Angeline" or + this = "Helen" or + this = "Cornelia" or + this = "Harriet" or + this = "Mahala" or + this = "Abby" or + this = "Margaret" or + this = "Deb" or + this = "Minerva" or + this = "Severus" or + this = "Lavina" or + this = "Adeline" or + this = "Cath" or + this = "Elisa" or + this = "Lucretia" or + this = "Anne" or + this = "Eleanor" or + this = "Joanna" or + this = "Adam" or + this = "Agnes" or + this = "Rosanna" or + this = "Clara" or + this = "Melissa" or + this = "Amy" or + this = "Isabel" or + this = "Jemima" or + this = "Cordelia" or + this = "Melinda" or + this = "Delila" or + this = "Jeremiah" or + this = "Elijah" or + this = "Hester" or + this = "Walter" or + this = "Oliver" or + this = "Hugh" or + this = "Aaron" or + this = "Reuben" or + this = "Eli" or + this = "Amos" or + this = "Augustus" or + this = "Theodore" or + this = "Ira" or + this = "Timothy" or + this = "Cyrus" or + this = "Horace" or + this = "Simon" or + this = "Asa" or + this = "Frank" or + this = "Nelson" or + this = "Leonard" or + this = "Harrison" or + this = "Anthony" or + this = "Louis" or + this = "Milton" or + this = "Noah" or + this = "Cornelius" or + this = "Abdul" or + this = "Warren" or + this = "Harvey" or + this = "Dennis" or + this = "Wesley" or + this = "Sylvester" or + this = "Gilbert" or + this = "Sullivan" or + this = "Edmund" or + this = "Wilson" or + this = "Perry" or + this = "Matthew" or + this = "Simba" or + this = "Nala" or + this = "Rafiki" or + this = "Shenzi" or + this = "Ernest" or + this = "Gertrude" or + this = "Oscar" or + this = "Lilian" or + this = "Raymond" or + this = "Elgar" or + this = "Elmer" or + this = "Herbert" or + this = "Maude" or + this = "Mae" or + this = "Otto" or + this = "Edwin" or + this = "Ophelia" or + this = "Parsley" or + this = "Sage" or + this = "Rosemary" or + this = "Thyme" or + this = "Garfunkel" or + this = "King Basil" or + this = "Stephen" + } + + /** Gets the hair color of the person. If the person is bald, there is no result. */ + string getHairColor() { + this = "Ronil" and result = "black" + or + this = "Dina" and result = "black" + or + this = "Ravi" and result = "black" + or + this = "Bruce" and result = "brown" + or + this = "Jo" and result = "red" + or + this = "Aida" and result = "blond" + or + this = "Esme" and result = "blond" + or + this = "Fred" and result = "gray" + or + this = "Meera" and result = "brown" + or + this = "Maya" and result = "brown" + or + this = "Chad" and result = "brown" + or + this = "Tiana" and result = "black" + or + this = "Laura" and result = "blond" + or + this = "George" and result = "blond" + or + this = "Will" and result = "blond" + or + this = "Mary" and result = "blond" + or + this = "Almira" and result = "black" + or + this = "Susannah" and result = "blond" + or + this = "Rhoda" and result = "blond" + or + this = "Cynthia" and result = "gray" + or + this = "Eunice" and result = "white" + or + this = "Olive" and result = "brown" + or + this = "Virginia" and result = "brown" + or + this = "Angeline" and result = "red" + or + this = "Helen" and result = "white" + or + this = "Cornelia" and result = "gray" + or + this = "Harriet" and result = "white" + or + this = "Mahala" and result = "black" + or + this = "Abby" and result = "red" + or + this = "Margaret" and result = "brown" + or + this = "Deb" and result = "brown" + or + this = "Minerva" and result = "brown" + or + this = "Severus" and result = "black" + or + this = "Lavina" and result = "brown" + or + this = "Adeline" and result = "brown" + or + this = "Cath" and result = "brown" + or + this = "Elisa" and result = "brown" + or + this = "Lucretia" and result = "gray" + or + this = "Anne" and result = "black" + or + this = "Eleanor" and result = "brown" + or + this = "Joanna" and result = "brown" + or + this = "Adam" and result = "black" + or + this = "Agnes" and result = "black" + or + this = "Rosanna" and result = "gray" + or + this = "Clara" and result = "blond" + or + this = "Melissa" and result = "brown" + or + this = "Amy" and result = "brown" + or + this = "Isabel" and result = "black" + or + this = "Jemima" and result = "red" + or + this = "Cordelia" and result = "red" + or + this = "Melinda" and result = "gray" + or + this = "Delila" and result = "white" + or + this = "Jeremiah" and result = "gray" + or + this = "Hester" and result = "black" + or + this = "Walter" and result = "black" + or + this = "Aaron" and result = "gray" + or + this = "Reuben" and result = "gray" + or + this = "Eli" and result = "gray" + or + this = "Amos" and result = "white" + or + this = "Augustus" and result = "white" + or + this = "Theodore" and result = "white" + or + this = "Timothy" and result = "brown" + or + this = "Cyrus" and result = "brown" + or + this = "Horace" and result = "brown" + or + this = "Simon" and result = "brown" + or + this = "Asa" and result = "brown" + or + this = "Frank" and result = "brown" + or + this = "Nelson" and result = "black" + or + this = "Leonard" and result = "black" + or + this = "Harrison" and result = "black" + or + this = "Anthony" and result = "black" + or + this = "Louis" and result = "black" + or + this = "Milton" and result = "blond" + or + this = "Noah" and result = "blond" + or + this = "Cornelius" and result = "red" + or + this = "Abdul" and result = "brown" + or + this = "Warren" and result = "red" + or + this = "Harvey" and result = "blond" + or + this = "Dennis" and result = "blond" + or + this = "Wesley" and result = "brown" + or + this = "Sylvester" and result = "brown" + or + this = "Gilbert" and result = "brown" + or + this = "Sullivan" and result = "brown" + or + this = "Edmund" and result = "brown" + or + this = "Wilson" and result = "blond" + or + this = "Perry" and result = "black" + or + this = "Simba" and result = "brown" + or + this = "Nala" and result = "brown" + or + this = "Rafiki" and result = "red" + or + this = "Shenzi" and result = "gray" + or + this = "Ernest" and result = "blond" + or + this = "Gertrude" and result = "brown" + or + this = "Oscar" and result = "blond" + or + this = "Lilian" and result = "brown" + or + this = "Raymond" and result = "brown" + or + this = "Elgar" and result = "brown" + or + this = "Elmer" and result = "brown" + or + this = "Herbert" and result = "brown" + or + this = "Maude" and result = "brown" + or + this = "Mae" and result = "brown" + or + this = "Otto" and result = "black" + or + this = "Edwin" and result = "black" + or + this = "Ophelia" and result = "brown" + or + this = "Parsley" and result = "brown" + or + this = "Sage" and result = "brown" + or + this = "Rosemary" and result = "brown" + or + this = "Thyme" and result = "brown" + or + this = "Garfunkel" and result = "brown" + or + this = "King Basil" and result = "brown" + or + this = "Stephen" and result = "black" + or + this = "Stephen" and result = "gray" + } + + /** Gets the age of the person (in years). If the person is deceased, there is no result. */ + int getAge() { + this = "Ronil" and result = 21 + or + this = "Dina" and result = 53 + or + this = "Ravi" and result = 16 + or + this = "Bruce" and result = 35 + or + this = "Jo" and result = 47 + or + this = "Aida" and result = 26 + or + this = "Esme" and result = 25 + or + this = "Charlie" and result = 31 + or + this = "Fred" and result = 68 + or + this = "Meera" and result = 62 + or + this = "Maya" and result = 29 + or + this = "Chad" and result = 49 + or + this = "Tiana" and result = 18 + or + this = "Laura" and result = 2 + or + this = "George" and result = 3 + or + this = "Will" and result = 41 + or + this = "Mary" and result = 51 + or + this = "Almira" and result = 1 + or + this = "Susannah" and result = 97 + or + this = "Rhoda" and result = 39 + or + this = "Cynthia" and result = 89 + or + this = "Eunice" and result = 83 + or + this = "Olive" and result = 25 + or + this = "Virginia" and result = 52 + or + this = "Angeline" and result = 22 + or + this = "Helen" and result = 79 + or + this = "Cornelia" and result = 59 + or + this = "Harriet" and result = 57 + or + this = "Mahala" and result = 61 + or + this = "Abby" and result = 24 + or + this = "Margaret" and result = 59 + or + this = "Deb" and result = 31 + or + this = "Minerva" and result = 72 + or + this = "Severus" and result = 61 + or + this = "Lavina" and result = 33 + or + this = "Adeline" and result = 17 + or + this = "Cath" and result = 22 + or + this = "Elisa" and result = 9 + or + this = "Lucretia" and result = 56 + or + this = "Anne" and result = 11 + or + this = "Eleanor" and result = 80 + or + this = "Joanna" and result = 43 + or + this = "Adam" and result = 37 + or + this = "Agnes" and result = 47 + or + this = "Rosanna" and result = 61 + or + this = "Clara" and result = 31 + or + this = "Melissa" and result = 37 + or + this = "Amy" and result = 12 + or + this = "Isabel" and result = 6 + or + this = "Jemima" and result = 16 + or + this = "Cordelia" and result = 21 + or + this = "Melinda" and result = 55 + or + this = "Delila" and result = 66 + or + this = "Jeremiah" and result = 54 + or + this = "Elijah" and result = 42 + or + this = "Hester" and result = 68 + or + this = "Walter" and result = 66 + or + this = "Oliver" and result = 33 + or + this = "Hugh" and result = 51 + or + this = "Aaron" and result = 49 + or + this = "Reuben" and result = 58 + or + this = "Eli" and result = 70 + or + this = "Amos" and result = 65 + or + this = "Augustus" and result = 56 + or + this = "Theodore" and result = 69 + or + this = "Ira" and result = 1 + or + this = "Timothy" and result = 54 + or + this = "Cyrus" and result = 78 + or + this = "Horace" and result = 34 + or + this = "Simon" and result = 23 + or + this = "Asa" and result = 28 + or + this = "Frank" and result = 59 + or + this = "Nelson" and result = 38 + or + this = "Leonard" and result = 58 + or + this = "Harrison" and result = 7 + or + this = "Anthony" and result = 2 + or + this = "Louis" and result = 34 + or + this = "Milton" and result = 36 + or + this = "Noah" and result = 48 + or + this = "Cornelius" and result = 41 + or + this = "Abdul" and result = 67 + or + this = "Warren" and result = 47 + or + this = "Harvey" and result = 31 + or + this = "Dennis" and result = 39 + or + this = "Wesley" and result = 13 + or + this = "Sylvester" and result = 19 + or + this = "Gilbert" and result = 16 + or + this = "Sullivan" and result = 17 + or + this = "Edmund" and result = 29 + or + this = "Wilson" and result = 27 + or + this = "Perry" and result = 31 + or + this = "Matthew" and result = 55 + or + this = "Simba" and result = 8 + or + this = "Nala" and result = 7 + or + this = "Rafiki" and result = 76 + or + this = "Shenzi" and result = 67 + } + + /** Gets the height of the person (in cm). If the person is deceased, there is no result. */ + float getHeight() { + this = "Ronil" and result = 183.0 + or + this = "Dina" and result = 155.1 + or + this = "Ravi" and result = 175.2 + or + this = "Bruce" and result = 191.3 + or + this = "Jo" and result = 163.4 + or + this = "Aida" and result = 182.6 + or + this = "Esme" and result = 176.9 + or + this = "Charlie" and result = 189.7 + or + this = "Fred" and result = 179.4 + or + this = "Meera" and result = 160.1 + or + this = "Maya" and result = 153.0 + or + this = "Chad" and result = 168.5 + or + this = "Tiana" and result = 149.7 + or + this = "Laura" and result = 87.5 + or + this = "George" and result = 96.4 + or + this = "Will" and result = 167.1 + or + this = "Mary" and result = 159.8 + or + this = "Almira" and result = 62.1 + or + this = "Susannah" and result = 145.8 + or + this = "Rhoda" and result = 180.1 + or + this = "Cynthia" and result = 161.8 + or + this = "Eunice" and result = 153.2 + or + this = "Olive" and result = 179.9 + or + this = "Virginia" and result = 165.1 + or + this = "Angeline" and result = 172.3 + or + this = "Helen" and result = 163.1 + or + this = "Cornelia" and result = 160.8 + or + this = "Harriet" and result = 163.2 + or + this = "Mahala" and result = 157.7 + or + this = "Abby" and result = 174.5 + or + this = "Margaret" and result = 165.6 + or + this = "Deb" and result = 171.6 + or + this = "Minerva" and result = 168.7 + or + this = "Severus" and result = 188.8 + or + this = "Lavina" and result = 155.1 + or + this = "Adeline" and result = 165.5 + or + this = "Cath" and result = 147.8 + or + this = "Elisa" and result = 129.4 + or + this = "Lucretia" and result = 153.6 + or + this = "Anne" and result = 140.4 + or + this = "Eleanor" and result = 151.1 + or + this = "Joanna" and result = 167.2 + or + this = "Adam" and result = 155.5 + or + this = "Agnes" and result = 156.8 + or + this = "Rosanna" and result = 162.4 + or + this = "Clara" and result = 158.6 + or + this = "Melissa" and result = 182.3 + or + this = "Amy" and result = 147.1 + or + this = "Isabel" and result = 121.4 + or + this = "Jemima" and result = 149.8 + or + this = "Cordelia" and result = 151.7 + or + this = "Melinda" and result = 154.4 + or + this = "Delila" and result = 163.4 + or + this = "Jeremiah" and result = 167.5 + or + this = "Elijah" and result = 184.5 + or + this = "Hester" and result = 152.7 + or + this = "Walter" and result = 159.6 + or + this = "Oliver" and result = 192.4 + or + this = "Hugh" and result = 173.1 + or + this = "Aaron" and result = 176.6 + or + this = "Reuben" and result = 169.9 + or + this = "Eli" and result = 180.4 + or + this = "Amos" and result = 167.4 + or + this = "Augustus" and result = 156.5 + or + this = "Theodore" and result = 176.6 + or + this = "Ira" and result = 54.1 + or + this = "Timothy" and result = 172.2 + or + this = "Cyrus" and result = 157.9 + or + this = "Horace" and result = 169.3 + or + this = "Simon" and result = 157.1 + or + this = "Asa" and result = 149.4 + or + this = "Frank" and result = 167.2 + or + this = "Nelson" and result = 173.0 + or + this = "Leonard" and result = 172.0 + or + this = "Harrison" and result = 126.0 + or + this = "Anthony" and result = 98.4 + or + this = "Louis" and result = 186.8 + or + this = "Milton" and result = 157.8 + or + this = "Noah" and result = 190.5 + or + this = "Cornelius" and result = 183.1 + or + this = "Abdul" and result = 182.0 + or + this = "Warren" and result = 175.0 + or + this = "Harvey" and result = 169.3 + or + this = "Dennis" and result = 160.4 + or + this = "Wesley" and result = 139.8 + or + this = "Sylvester" and result = 188.2 + or + this = "Gilbert" and result = 177.6 + or + this = "Sullivan" and result = 168.3 + or + this = "Edmund" and result = 159.2 + or + this = "Wilson" and result = 167.6 + or + this = "Perry" and result = 189.1 + or + this = "Matthew" and result = 167.2 + or + this = "Simba" and result = 140.1 + or + this = "Nala" and result = 138.0 + or + this = "Rafiki" and result = 139.3 + or + this = "Shenzi" and result = 171.1 + } + + /** Gets the location of the person's home ("north", "south", "east", or "west"). If the person is deceased, there is no result. */ + string getLocation() { + this = "Ronil" and result = "north" + or + this = "Dina" and result = "north" + or + this = "Ravi" and result = "north" + or + this = "Bruce" and result = "south" + or + this = "Jo" and result = "west" + or + this = "Aida" and result = "east" + or + this = "Esme" and result = "east" + or + this = "Charlie" and result = "south" + or + this = "Fred" and result = "west" + or + this = "Meera" and result = "south" + or + this = "Maya" and result = "south" + or + this = "Chad" and result = "south" + or + this = "Tiana" and result = "west" + or + this = "Laura" and result = "south" + or + this = "George" and result = "south" + or + this = "Will" and result = "south" + or + this = "Mary" and result = "south" + or + this = "Almira" and result = "south" + or + this = "Susannah" and result = "north" + or + this = "Rhoda" and result = "north" + or + this = "Cynthia" and result = "north" + or + this = "Eunice" and result = "north" + or + this = "Olive" and result = "west" + or + this = "Virginia" and result = "west" + or + this = "Angeline" and result = "west" + or + this = "Helen" and result = "west" + or + this = "Cornelia" and result = "east" + or + this = "Harriet" and result = "east" + or + this = "Mahala" and result = "east" + or + this = "Abby" and result = "east" + or + this = "Margaret" and result = "east" + or + this = "Deb" and result = "east" + or + this = "Minerva" and result = "south" + or + this = "Severus" and result = "north" + or + this = "Lavina" and result = "east" + or + this = "Adeline" and result = "west" + or + this = "Cath" and result = "east" + or + this = "Elisa" and result = "east" + or + this = "Lucretia" and result = "north" + or + this = "Anne" and result = "north" + or + this = "Eleanor" and result = "south" + or + this = "Joanna" and result = "south" + or + this = "Adam" and result = "east" + or + this = "Agnes" and result = "east" + or + this = "Rosanna" and result = "east" + or + this = "Clara" and result = "east" + or + this = "Melissa" and result = "west" + or + this = "Amy" and result = "west" + or + this = "Isabel" and result = "west" + or + this = "Jemima" and result = "west" + or + this = "Cordelia" and result = "west" + or + this = "Melinda" and result = "west" + or + this = "Delila" and result = "south" + or + this = "Jeremiah" and result = "north" + or + this = "Elijah" and result = "north" + or + this = "Hester" and result = "east" + or + this = "Walter" and result = "east" + or + this = "Oliver" and result = "east" + or + this = "Hugh" and result = "south" + or + this = "Aaron" and result = "south" + or + this = "Reuben" and result = "west" + or + this = "Eli" and result = "west" + or + this = "Amos" and result = "east" + or + this = "Augustus" and result = "south" + or + this = "Theodore" and result = "west" + or + this = "Ira" and result = "south" + or + this = "Timothy" and result = "north" + or + this = "Cyrus" and result = "north" + or + this = "Horace" and result = "east" + or + this = "Simon" and result = "east" + or + this = "Asa" and result = "east" + or + this = "Frank" and result = "west" + or + this = "Nelson" and result = "west" + or + this = "Leonard" and result = "west" + or + this = "Harrison" and result = "north" + or + this = "Anthony" and result = "north" + or + this = "Louis" and result = "north" + or + this = "Milton" and result = "south" + or + this = "Noah" and result = "south" + or + this = "Cornelius" and result = "east" + or + this = "Abdul" and result = "east" + or + this = "Warren" and result = "west" + or + this = "Harvey" and result = "west" + or + this = "Dennis" and result = "west" + or + this = "Wesley" and result = "west" + or + this = "Sylvester" and result = "south" + or + this = "Gilbert" and result = "east" + or + this = "Sullivan" and result = "east" + or + this = "Edmund" and result = "north" + or + this = "Wilson" and result = "north" + or + this = "Perry" and result = "west" + or + this = "Matthew" and result = "east" + or + this = "Simba" and result = "south" + or + this = "Nala" and result = "south" + or + this = "Rafiki" and result = "north" + or + this = "Shenzi" and result = "west" + } + + /** Holds if the person is deceased. */ + predicate isDeceased() { + this = "Ernest" or + this = "Gertrude" or + this = "Oscar" or + this = "Lilian" or + this = "Edwin" or + this = "Raymond" or + this = "Elgar" or + this = "Elmer" or + this = "Herbert" or + this = "Maude" or + this = "Mae" or + this = "Otto" or + this = "Ophelia" or + this = "Parsley" or + this = "Sage" or + this = "Rosemary" or + this = "Thyme" or + this = "Garfunkel" or + this = "King Basil" + } + + /** Gets a parent of the person (alive or deceased). */ + Person getAParent() { + this = "Stephen" and result = "Edmund" + or + this = "Edmund" and result = "Augustus" + or + this = "Augustus" and result = "Stephen" + or + this = "Abby" and result = "Cornelia" + or + this = "Abby" and result = "Amos" + or + this = "Abdul" and result = "Susannah" + or + this = "Adam" and result = "Amos" + or + this = "Adeline" and result = "Melinda" + or + this = "Adeline" and result = "Frank" + or + this = "Agnes" and result = "Abdul" + or + this = "Aida" and result = "Agnes" + or + this = "Almira" and result = "Sylvester" + or + this = "Amos" and result = "Eunice" + or + this = "Amy" and result = "Noah" + or + this = "Amy" and result = "Chad" + or + this = "Angeline" and result = "Reuben" + or + this = "Angeline" and result = "Lucretia" + or + this = "Anne" and result = "Rhoda" + or + this = "Anne" and result = "Louis" + or + this = "Anthony" and result = "Lavina" + or + this = "Anthony" and result = "Asa" + or + this = "Asa" and result = "Cornelia" + or + this = "Cath" and result = "Harriet" + or + this = "Charlie" and result = "Matthew" + or + this = "Clara" and result = "Ernest" + or + this = "Cornelia" and result = "Cynthia" + or + this = "Cornelius" and result = "Eli" + or + this = "Deb" and result = "Margaret" + or + this = "Dennis" and result = "Fred" + or + this = "Eli" and result = "Susannah" + or + this = "Elijah" and result = "Delila" + or + this = "Elisa" and result = "Deb" + or + this = "Elisa" and result = "Horace" + or + this = "Esme" and result = "Margaret" + or + this = "Frank" and result = "Eleanor" + or + this = "Frank" and result = "Cyrus" + or + this = "George" and result = "Maya" + or + this = "George" and result = "Wilson" + or + this = "Gilbert" and result = "Cornelius" + or + this = "Harriet" and result = "Cynthia" + or + this = "Harrison" and result = "Louis" + or + this = "Harvey" and result = "Fred" + or + this = "Helen" and result = "Susannah" + or + this = "Hester" and result = "Edwin" + or + this = "Hugh" and result = "Cyrus" + or + this = "Hugh" and result = "Helen" + or + this = "Ira" and result = "Maya" + or + this = "Ira" and result = "Wilson" + or + this = "Isabel" and result = "Perry" + or + this = "Isabel" and result = "Harvey" + or + this = "Jemima" and result = "Melinda" + or + this = "Jemima" and result = "Frank" + or + this = "Ernest" and result = "Lilian" + or + this = "Ernest" and result = "Oscar" + or + this = "Gertrude" and result = "Ophelia" + or + this = "Gertrude" and result = "Raymond" + or + this = "Lilian" and result = "Elgar" + or + this = "Lilian" and result = "Mae" + or + this = "Raymond" and result = "Elgar" + or + this = "Raymond" and result = "Mae" + or + this = "Elmer" and result = "Ophelia" + or + this = "Elmer" and result = "Raymond" + or + this = "Herbert" and result = "Ophelia" + or + this = "Herbert" and result = "Raymond" + or + this = "Maude" and result = "Ophelia" + or + this = "Maude" and result = "Raymond" + or + this = "Otto" and result = "Elgar" + or + this = "Otto" and result = "Mae" + or + this = "Edwin" and result = "Otto" + or + this = "Parsley" and result = "Simon" + or + this = "Parsley" and result = "Garfunkel" + or + this = "Sage" and result = "Simon" + or + this = "Sage" and result = "Garfunkel" + or + this = "Rosemary" and result = "Simon" + or + this = "Rosemary" and result = "Garfunkel" + or + this = "Thyme" and result = "Simon" + or + this = "Thyme" and result = "Garfunkel" + or + this = "King Basil" and result = "Ophelia" + or + this = "King Basil" and result = "Raymond" + or + this = "Jo" and result = "Theodore" + or + this = "Joanna" and result = "Shenzi" + or + this = "Laura" and result = "Maya" + or + this = "Laura" and result = "Wilson" + or + this = "Lavina" and result = "Mahala" + or + this = "Lavina" and result = "Walter" + or + this = "Leonard" and result = "Cyrus" + or + this = "Leonard" and result = "Helen" + or + this = "Lucretia" and result = "Eleanor" + or + this = "Lucretia" and result = "Cyrus" + or + this = "Mahala" and result = "Eunice" + or + this = "Margaret" and result = "Cynthia" + or + this = "Matthew" and result = "Cyrus" + or + this = "Matthew" and result = "Helen" + or + this = "Maya" and result = "Meera" + or + this = "Melinda" and result = "Rafiki" + or + this = "Melissa" and result = "Mahala" + or + this = "Melissa" and result = "Walter" + or + this = "Nala" and result = "Bruce" + or + this = "Nelson" and result = "Mahala" + or + this = "Nelson" and result = "Walter" + or + this = "Noah" and result = "Eli" + or + this = "Olive" and result = "Reuben" + or + this = "Olive" and result = "Lucretia" + or + this = "Oliver" and result = "Matthew" + or + this = "Perry" and result = "Leonard" + or + this = "Ravi" and result = "Dina" + or + this = "Simba" and result = "Will" + or + this = "Simon" and result = "Margaret" + or + this = "Sullivan" and result = "Cornelius" + or + this = "Sylvester" and result = "Timothy" + or + this = "Theodore" and result = "Susannah" + or + this = "Tiana" and result = "Jo" + or + this = "Virginia" and result = "Helen" + or + this = "Warren" and result = "Shenzi" + or + this = "Wesley" and result = "Warren" + or + this = "Wesley" and result = "Jo" + or + this = "Will" and result = "Eli" + } + + /** Holds if the person is allowed in the region. Initially, all villagers are allowed in every region. */ + predicate isAllowedIn(string region) { + region = "north" or + region = "south" or + region = "east" or + region = "west" + } +} + +/** Returns a parent of the person. */ +Person parentOf(Person p) { result = p.getAParent() } diff --git a/python/ql/src/Classes/UselessClass.ql b/python/ql/src/Classes/UselessClass.ql index 2695e9a7a1d..19d21c7e7ca 100644 --- a/python/ql/src/Classes/UselessClass.ql +++ b/python/ql/src/Classes/UselessClass.ql @@ -52,11 +52,7 @@ predicate is_stateful(Class c) { call.getFunc() = a and a.getName() = name | - name = "pop" or - name = "remove" or - name = "discard" or - name = "extend" or - name = "append" + name in ["pop", "remove", "discard", "extend", "append"] ) } diff --git a/python/ql/src/Diagnostics/ExtractionErrors.ql b/python/ql/src/Diagnostics/ExtractionErrors.ql deleted file mode 100644 index 5d9ddee6eb7..00000000000 --- a/python/ql/src/Diagnostics/ExtractionErrors.ql +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @name Python extraction errors - * @description List all extraction errors for Python files in the source code directory. - * @kind diagnostic - * @id py/diagnostics/extraction-errors - */ - -import python - -/** - * Gets the SARIF severity for errors. - * - * See point 3.27.10 in https://docs.oasis-open.org/sarif/sarif/v2.0/sarif-v2.0.html for - * what error means. - */ -int getErrorSeverity() { result = 2 } - -from SyntaxError error, File file -where - file = error.getFile() and - exists(file.getRelativePath()) -select error, "Extraction failed in " + file + " with error " + error.getMessage(), - getErrorSeverity() diff --git a/python/ql/src/Diagnostics/ExtractionWarnings.ql b/python/ql/src/Diagnostics/ExtractionWarnings.ql new file mode 100644 index 00000000000..553798a546e --- /dev/null +++ b/python/ql/src/Diagnostics/ExtractionWarnings.ql @@ -0,0 +1,36 @@ +/** + * @name Python extraction warnings + * @description List all extraction warnings for Python files in the source code directory. + * @kind diagnostic + * @id py/diagnostics/extraction-warnings + */ + +import python + +/** + * Gets the SARIF severity for warnings. + * + * See https://docs.oasis-open.org/sarif/sarif/v2.1.0/csprd01/sarif-v2.1.0-csprd01.html#_Toc10541338 + */ +int getWarningSeverity() { result = 1 } + +// The spec +// https://docs.oasis-open.org/sarif/sarif/v2.1.0/csprd01/sarif-v2.1.0-csprd01.html#_Toc10541338 +// defines error and warning as: +// +// "error": A serious problem was found. The condition encountered by the tool resulted +// in the analysis being halted or caused the results to be incorrect or incomplete. +// +// "warning": A problem that is not considered serious was found. The condition +// encountered by the tool is such that it is uncertain whether a problem occurred, or +// is such that the analysis might be incomplete but the results that were generated are +// probably valid. +// +// So SyntaxErrors are reported at the warning level, since analysis might be incomplete +// but the results that were generated are probably valid. +from SyntaxError error, File file +where + file = error.getFile() and + exists(file.getRelativePath()) +select error, "Extraction failed in " + file + " with error " + error.getMessage(), + getWarningSeverity() diff --git a/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.ql b/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.ql index b3c97e967f6..a99a66bca3b 100644 --- a/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.ql +++ b/python/ql/src/Functions/IncorrectRaiseInSpecialMethod.ql @@ -22,49 +22,14 @@ private predicate indexing_method(string name) { } private predicate arithmetic_method(string name) { - name = "__add__" or - name = "__sub__" or - name = "__div__" or - name = "__pos__" or - name = "__abs__" or - name = "__floordiv__" or - name = "__div__" or - name = "__divmod__" or - name = "__lshift__" or - name = "__and__" or - name = "__or__" or - name = "__xor__" or - name = "__rshift__" or - name = "__pow__" or - name = "__mul__" or - name = "__neg__" or - name = "__radd__" or - name = "__rsub__" or - name = "__rdiv__" or - name = "__rfloordiv__" or - name = "__rdiv__" or - name = "__rlshift__" or - name = "__rand__" or - name = "__ror__" or - name = "__rxor__" or - name = "__rrshift__" or - name = "__rpow__" or - name = "__rmul__" or - name = "__truediv__" or - name = "__rtruediv__" or - name = "__iadd__" or - name = "__isub__" or - name = "__idiv__" or - name = "__ifloordiv__" or - name = "__idiv__" or - name = "__ilshift__" or - name = "__iand__" or - name = "__ior__" or - name = "__ixor__" or - name = "__irshift__" or - name = "__ipow__" or - name = "__imul__" or - name = "__itruediv__" + name in [ + "__add__", "__sub__", "__or__", "__xor__", "__rshift__", "__pow__", "__mul__", "__neg__", + "__radd__", "__rsub__", "__rdiv__", "__rfloordiv__", "__div__", "__rdiv__", "__rlshift__", + "__rand__", "__ror__", "__rxor__", "__rrshift__", "__rpow__", "__rmul__", "__truediv__", + "__rtruediv__", "__pos__", "__iadd__", "__isub__", "__idiv__", "__ifloordiv__", "__idiv__", + "__ilshift__", "__iand__", "__ior__", "__ixor__", "__irshift__", "__abs__", "__ipow__", + "__imul__", "__itruediv__", "__floordiv__", "__div__", "__divmod__", "__lshift__", "__and__" + ] } private predicate ordering_method(string name) { diff --git a/python/ql/src/Functions/SignatureOverriddenMethod.ql b/python/ql/src/Functions/SignatureOverriddenMethod.ql index e695f2385ea..85f1f0c2eb1 100644 --- a/python/ql/src/Functions/SignatureOverriddenMethod.ql +++ b/python/ql/src/Functions/SignatureOverriddenMethod.ql @@ -24,7 +24,6 @@ where not derived.getScope().isSpecialMethod() and derived.getName() != "__init__" and derived.isNormalMethod() and - not derived.getScope().isSpecialMethod() and // call to overrides distributed for efficiency ( derived.overrides(base) and derived.minParameters() > base.maxParameters() diff --git a/python/ql/src/Functions/SignatureSpecialMethods.ql b/python/ql/src/Functions/SignatureSpecialMethods.ql index 87aeeae51ff..feedb6c94b6 100644 --- a/python/ql/src/Functions/SignatureSpecialMethods.ql +++ b/python/ql/src/Functions/SignatureSpecialMethods.ql @@ -13,98 +13,29 @@ import python predicate is_unary_op(string name) { - name = "__del__" or - name = "__repr__" or - name = "__str__" or - name = "__hash__" or - name = "__bool__" or - name = "__nonzero__" or - name = "__unicode__" or - name = "__len__" or - name = "__iter__" or - name = "__reversed__" or - name = "__neg__" or - name = "__pos__" or - name = "__abs__" or - name = "__invert__" or - name = "__complex__" or - name = "__int__" or - name = "__float__" or - name = "__long__" or - name = "__oct__" or - name = "__hex__" or - name = "__index__" or - name = "__enter__" + name in [ + "__del__", "__repr__", "__neg__", "__pos__", "__abs__", "__invert__", "__complex__", + "__int__", "__float__", "__long__", "__oct__", "__hex__", "__str__", "__index__", "__enter__", + "__hash__", "__bool__", "__nonzero__", "__unicode__", "__len__", "__iter__", "__reversed__" + ] } predicate is_binary_op(string name) { - name = "__lt__" or - name = "__le__" or - name = "__eq__" or - name = "__ne__" or - name = "__gt__" or - name = "__ge__" or - name = "__cmp__" or - name = "__rcmp__" or - name = "__getattr___" or - name = "__getattribute___" or - name = "__delattr__" or - name = "__delete__" or - name = "__instancecheck__" or - name = "__subclasscheck__" or - name = "__getitem__" or - name = "__delitem__" or - name = "__contains__" or - name = "__add__" or - name = "__sub__" or - name = "__mul__" or - name = "__floordiv__" or - name = "__div__" or - name = "__truediv__" or - name = "__mod__" or - name = "__divmod__" or - name = "__lshift__" or - name = "__rshift__" or - name = "__and__" or - name = "__xor__" or - name = "__or__" or - name = "__radd__" or - name = "__rsub__" or - name = "__rmul__" or - name = "__rfloordiv__" or - name = "__rdiv__" or - name = "__rtruediv__" or - name = "__rmod__" or - name = "__rdivmod__" or - name = "__rpow__" or - name = "__rlshift__" or - name = "__rrshift__" or - name = "__rand__" or - name = "__rxor__" or - name = "__ror__" or - name = "__iadd__" or - name = "__isub__" or - name = "__imul__" or - name = "__ifloordiv__" or - name = "__idiv__" or - name = "__itruediv__" or - name = "__imod__" or - name = "__idivmod__" or - name = "__ipow__" or - name = "__ilshift__" or - name = "__irshift__" or - name = "__iand__" or - name = "__ixor__" or - name = "__ior__" or - name = "__coerce__" + name in [ + "__lt__", "__le__", "__delattr__", "__delete__", "__instancecheck__", "__subclasscheck__", + "__getitem__", "__delitem__", "__contains__", "__add__", "__sub__", "__mul__", "__eq__", + "__floordiv__", "__div__", "__truediv__", "__mod__", "__divmod__", "__lshift__", "__rshift__", + "__and__", "__xor__", "__or__", "__ne__", "__radd__", "__rsub__", "__rmul__", "__rfloordiv__", + "__rdiv__", "__rtruediv__", "__rmod__", "__rdivmod__", "__rpow__", "__rlshift__", "__gt__", + "__rrshift__", "__rand__", "__rxor__", "__ror__", "__iadd__", "__isub__", "__imul__", + "__ifloordiv__", "__idiv__", "__itruediv__", "__ge__", "__imod__", "__idivmod__", "__ipow__", + "__ilshift__", "__irshift__", "__iand__", "__ixor__", "__ior__", "__coerce__", "__cmp__", + "__rcmp__", "__getattr___", "__getattribute___" + ] } predicate is_ternary_op(string name) { - name = "__setattr__" or - name = "__set__" or - name = "__setitem__" or - name = "__getslice__" or - name = "__delslice__" + name in ["__setattr__", "__set__", "__setitem__", "__getslice__", "__delslice__"] } predicate is_quad_op(string name) { name = "__setslice__" or name = "__exit__" } @@ -132,12 +63,12 @@ predicate incorrect_special_method_defn( else if required < func.minParameters() then message = "Too many parameters" and show_counts = true - else - if func.minParameters() < required and not func.getScope().hasVarArg() - then - message = (required - func.minParameters()) + " default values(s) will never be used" and - show_counts = false - else none() + else ( + func.minParameters() < required and + not func.getScope().hasVarArg() and + message = (required - func.minParameters()) + " default values(s) will never be used" and + show_counts = false + ) ) } diff --git a/python/ql/src/Lexical/CommentedOutCode.qll b/python/ql/src/Lexical/CommentedOutCode.qll index 97315321a79..94d8ca44e35 100644 --- a/python/ql/src/Lexical/CommentedOutCode.qll +++ b/python/ql/src/Lexical/CommentedOutCode.qll @@ -197,7 +197,7 @@ class CommentedOutCodeBlock extends @py_comment { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -297,41 +297,17 @@ private predicate file_or_url(Comment c) { c.getText().regexpMatch("#[^'\"]+(\\[a-zA-Z]\\w*)+\\.[a-zA-Z]+.*") } -private string operator_keyword() { - result = "import" or - result = "and" or - result = "is" or - result = "or" or - result = "in" or - result = "not" or - result = "as" -} +private string operator_keyword() { result in ["import", "and", "is", "or", "in", "not", "as"] } private string keyword_requiring_colon() { - result = "try" or - result = "while" or - result = "elif" or - result = "else" or - result = "if" or - result = "except" or - result = "def" or - result = "class" + result in ["try", "while", "elif", "else", "if", "except", "def", "class"] } private string other_keyword() { - result = "del" or - result = "lambda" or - result = "from" or - result = "global" or - result = "with" or - result = "assert" or - result = "yield" or - result = "finally" or - result = "print" or - result = "exec" or - result = "raise" or - result = "return" or - result = "for" + result in [ + "del", "lambda", "raise", "return", "for", "from", "global", "with", "assert", "yield", + "finally", "print", "exec" + ] } private string a_keyword() { diff --git a/python/ql/src/Metrics/Internal/Extents.qll b/python/ql/src/Metrics/Internal/Extents.qll index 1e38a4d544d..9ebe86deb36 100644 --- a/python/ql/src/Metrics/Internal/Extents.qll +++ b/python/ql/src/Metrics/Internal/Extents.qll @@ -20,7 +20,7 @@ class RangeFunction extends Function { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -40,7 +40,7 @@ class RangeClass extends Class { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp b/python/ql/src/Security/CWE-730/RegexInjection.qhelp similarity index 100% rename from python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp rename to python/ql/src/Security/CWE-730/RegexInjection.qhelp diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql b/python/ql/src/Security/CWE-730/RegexInjection.ql similarity index 58% rename from python/ql/src/experimental/Security/CWE-730/RegexInjection.ql rename to python/ql/src/Security/CWE-730/RegexInjection.ql index 7725f636eb0..0dfb5b00d52 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql +++ b/python/ql/src/Security/CWE-730/RegexInjection.ql @@ -5,25 +5,24 @@ * exponential time on certain inputs. * @kind path-problem * @problem.severity error + * @precision high * @id py/regex-injection * @tags security * external/cwe/cwe-730 * external/cwe/cwe-400 */ -// determine precision above import python -import experimental.semmle.python.security.injection.RegexInjection +private import semmle.python.Concepts +import semmle.python.security.injection.RegexInjection import DataFlow::PathGraph from - RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink, - RegexInjectionSink regexInjectionSink, Attribute methodAttribute + RegexInjection::Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink, + RegexExecution regexExecution where config.hasFlowPath(source, sink) and - regexInjectionSink = sink.getNode() and - methodAttribute = regexInjectionSink.getRegexMethod() + regexExecution = sink.getNode().(RegexInjection::Sink).getRegexExecution() select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@ and executed by $@.", sink.getNode(), "This", - source.getNode(), "user-provided value", methodAttribute, - regexInjectionSink.getRegexModule() + "." + methodAttribute.getName() + source.getNode(), "user-provided value", regexExecution, regexExecution.getName() diff --git a/python/ql/src/experimental/Security/CWE-730/re_bad.py b/python/ql/src/Security/CWE-730/re_bad.py similarity index 100% rename from python/ql/src/experimental/Security/CWE-730/re_bad.py rename to python/ql/src/Security/CWE-730/re_bad.py diff --git a/python/ql/src/experimental/Security/CWE-730/re_good.py b/python/ql/src/Security/CWE-730/re_good.py similarity index 100% rename from python/ql/src/experimental/Security/CWE-730/re_good.py rename to python/ql/src/Security/CWE-730/re_good.py diff --git a/python/ql/src/Security/CWE-798/HardcodedCredentials.ql b/python/ql/src/Security/CWE-798/HardcodedCredentials.ql index cd00908fe05..895352be75c 100644 --- a/python/ql/src/Security/CWE-798/HardcodedCredentials.ql +++ b/python/ql/src/Security/CWE-798/HardcodedCredentials.ql @@ -88,7 +88,7 @@ class CredentialSink extends TaintSink { CredentialSink() { exists(string name | name.regexpMatch(getACredentialRegex()) and - not name.suffix(name.length() - 4) = "file" + not name.matches("%file") | any(FunctionValue func).getNamedArgumentForCall(_, name) = this or diff --git a/python/ql/src/Statements/SideEffectInAssert.ql b/python/ql/src/Statements/SideEffectInAssert.ql index f96e04243af..21aff6ca646 100644 --- a/python/ql/src/Statements/SideEffectInAssert.ql +++ b/python/ql/src/Statements/SideEffectInAssert.ql @@ -15,16 +15,9 @@ import python predicate func_with_side_effects(Expr e) { exists(string name | name = e.(Attribute).getName() or name = e.(Name).getId() | - name = "print" or - name = "write" or - name = "append" or - name = "pop" or - name = "remove" or - name = "discard" or - name = "delete" or - name = "close" or - name = "open" or - name = "exit" + name in [ + "print", "write", "append", "pop", "remove", "discard", "delete", "close", "open", "exit" + ] ) } diff --git a/python/ql/src/analysis/AlertSuppression.ql b/python/ql/src/analysis/AlertSuppression.ql index c8fefc92cc1..9515c0aad8b 100644 --- a/python/ql/src/analysis/AlertSuppression.ql +++ b/python/ql/src/analysis/AlertSuppression.ql @@ -88,7 +88,7 @@ class SuppressionScope extends @py_comment { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/python/ql/src/analysis/Consistency.ql b/python/ql/src/analysis/Consistency.ql index a504216a252..ae48f126235 100644 --- a/python/ql/src/analysis/Consistency.ql +++ b/python/ql/src/analysis/Consistency.ql @@ -8,15 +8,10 @@ import python import DefinitionTracking predicate uniqueness_error(int number, string what, string problem) { - ( - what = "toString" or - what = "getLocation" or - what = "getNode" or - what = "getDefinition" or - what = "getEntryNode" or - what = "getOrigin" or - what = "getAnInferredType" - ) and + what in [ + "toString", "getLocation", "getNode", "getDefinition", "getEntryNode", "getOrigin", + "getAnInferredType" + ] and ( number = 0 and problem = "no results for " + what + "()" or @@ -141,7 +136,7 @@ predicate builtin_object_consistency(string clsname, string problem, string what or not exists(o.toString()) and problem = "no toString" and - not exists(string name | name.prefix(7) = "_semmle" | py_special_objects(o, name)) and + not exists(string name | name.matches("\\_semmle%") | py_special_objects(o, name)) and not o = unknownValue() ) } diff --git a/python/ql/src/analysis/DefinitionTracking.qll b/python/ql/src/analysis/DefinitionTracking.qll index 72dfb6e849c..b64f131e5bc 100644 --- a/python/ql/src/analysis/DefinitionTracking.qll +++ b/python/ql/src/analysis/DefinitionTracking.qll @@ -477,7 +477,7 @@ class NiceLocationExpr extends @py_expr { * The location spans column `bc` of line `bl` to * column `ec` of line `el` in file `f`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo(string f, int bl, int bc, int el, int ec) { /* Attribute location for x.y is that of 'y' so that url does not overlap with that of 'x' */ diff --git a/python/ql/src/experimental/Security/CWE-113/HeaderInjection.qhelp b/python/ql/src/experimental/Security/CWE-113/HeaderInjection.qhelp new file mode 100644 index 00000000000..33337294b9a --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-113/HeaderInjection.qhelp @@ -0,0 +1,26 @@ +<!DOCTYPE qhelp PUBLIC + "-//Semmle//qhelp//EN" + "qhelp.dtd"> +<qhelp> +<overview> +<p>If an HTTP Header is built using string concatenation or string formatting, and the +components of the concatenation include user input, a user +is likely to be able to manipulate the response.</p> +</overview> + +<recommendation> +<p>User input should not be included in an HTTP Header.</p> +</recommendation> + +<example> +<p>In the following example, the code appends a user-provided value into a header.</p> + +<sample src="header_injection.py" /> +</example> + +<references> +<li>OWASP: <a href="https://owasp.org/www-community/attacks/HTTP_Response_Splitting">HTTP Response Splitting</a>.</li> +<li>Python Security: <a href="https://python-security.readthedocs.io/vuln/http-header-injection.html">HTTP header injection</a>.</li> +<li>SonarSource: <a href="https://rules.sonarsource.com/python/RSPEC-5167">RSPEC-5167</a>.</li> +</references> +</qhelp> diff --git a/python/ql/src/experimental/Security/CWE-113/HeaderInjection.ql b/python/ql/src/experimental/Security/CWE-113/HeaderInjection.ql new file mode 100644 index 00000000000..3cb4a20d5de --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-113/HeaderInjection.ql @@ -0,0 +1,21 @@ +/** + * @name HTTP Header Injection + * @description User input should not be used in HTTP headers, otherwise a malicious user + * may be able to inject a value that could manipulate the response. + * @kind path-problem + * @problem.severity error + * @id py/header-injection + * @tags security + * external/cwe/cwe-113 + * external/cwe/cwe-079 + */ + +// determine precision above +import python +import experimental.semmle.python.security.injection.HTTPHeaders +import DataFlow::PathGraph + +from HeaderInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "$@ HTTP header is constructed from a $@.", sink.getNode(), + "This", source.getNode(), "user-provided value" diff --git a/python/ql/src/experimental/Security/CWE-113/header_injection.py b/python/ql/src/experimental/Security/CWE-113/header_injection.py new file mode 100644 index 00000000000..117383710e3 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-113/header_injection.py @@ -0,0 +1,9 @@ +from flask import Response, request, Flask, make_response + + +@app.route("/flask_Response") +def flask_Response(): + rfs_header = request.args["rfs_header"] + response = Response() + response.headers['HeaderName'] = rfs_header + return response diff --git a/python/ql/src/experimental/Security/CWE-117/LogInjection.qhelp b/python/ql/src/experimental/Security/CWE-117/LogInjection.qhelp new file mode 100644 index 00000000000..e5305220997 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-117/LogInjection.qhelp @@ -0,0 +1,49 @@ + +<!DOCTYPE qhelp PUBLIC + "-//Semmle//qhelp//EN" + "qhelp.dtd"> +<qhelp> + +<overview> + +<p>If unsanitized user input is written to a log entry, a malicious user may be able to forge new log entries.</p> + +<p>Forgery can occur if a user provides some input creating the appearance of multiple + log entries. This can include unescaped new-line characters, or HTML or other markup.</p> +</overview> + +<recommendation> +<p> +User input should be suitably sanitized before it is logged. +</p> +<p> +If the log entries are plain text then line breaks should be removed from user input, using for example +<code>replace(old, new)</code> or similar. Care should also be taken that user input is clearly marked +in log entries, and that a malicious user cannot cause confusion in other ways. +</p> +<p> +For log entries that will be displayed in HTML, user input should be HTML encoded before being logged, to prevent forgery and +other forms of HTML injection. +</p> +</recommendation> + +<example> +<p> +In the example, the name provided by the user is recorded using the log output function (<code>logging.info</code> or <code>app.logger.info</code>, etc.). +In these four cases, the name provided by the user is not provided The processing is recorded. If a malicious user provides <code>Guest%0D%0AUser name: Admin</code> +as a parameter, the log entry will be divided into two lines, the first line is <code>User name: Guest</code> code>, the second line is <code>User name: Admin</code>. +</p> +<sample src="LogInjectionBad.py" /> + +<p> +In a good example, the program uses the <code>replace</code> function to provide parameter processing to the user, and replace <code>\r\n</code> and <code>\n</code> +with empty characters. To a certain extent, the occurrence of log injection vulnerabilities is reduced. +</p> + +<sample src="LogInjectionGood.py" /> +</example> + +<references> +<li>OWASP: <a href="https://owasp.org/www-community/attacks/Log_Injection">Log Injection</a>.</li> +</references> +</qhelp> diff --git a/python/ql/src/experimental/Security/CWE-117/LogInjection.ql b/python/ql/src/experimental/Security/CWE-117/LogInjection.ql new file mode 100644 index 00000000000..452ef944825 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-117/LogInjection.ql @@ -0,0 +1,20 @@ +/** + * @name Log Injection + * @description Building log entries from user-controlled data is vulnerable to + * insertion of forged log entries by a malicious user. + * @kind path-problem + * @problem.severity error + * @precision high + * @id py/log-injection + * @tags security + * external/cwe/cwe-117 + */ + +import python +import experimental.semmle.python.security.injection.LogInjection +import DataFlow::PathGraph + +from LogInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "$@ flows to log entry.", source.getNode(), + "User-provided value" diff --git a/python/ql/src/experimental/Security/CWE-117/LogInjectionBad.py b/python/ql/src/experimental/Security/CWE-117/LogInjectionBad.py new file mode 100644 index 00000000000..0c0cbe1a8fe --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-117/LogInjectionBad.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +""" +@Desc :Log Injection +""" +from flask import Flask +from flask import request +from django.utils.log import request_logger +import logging + +logging.basicConfig(level=logging.DEBUG) + +app = Flask(__name__) + +@app.route('/bad1') +def bad1(): + name = request.args.get('name') + app.logger.info('User name: ' + name) # Bad + return 'bad1' + +@app.route('/bad2') +def bad2(): + name = request.args.get('name') + logging.info('User name: ' + name) # Bad + return 'bad2' + +@app.route('/bad3') +def bad3(): + name = request.args.get('name') + request_logger.warn('User name: ' + name) # Bad + return 'bad3' + +@app.route('/bad4') +def bad4(): + name = request.args.get('name') + logtest = logging.getLogger('test') + logtest.debug('User name: ' + name) # Bad + return 'bad4' + +if __name__ == '__main__': + app.debug = True + handler = logging.FileHandler('log') + app.logger.addHandler(handler) + app.run() diff --git a/python/ql/src/experimental/Security/CWE-117/LogInjectionGood.py b/python/ql/src/experimental/Security/CWE-117/LogInjectionGood.py new file mode 100644 index 00000000000..d9279f2e482 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-117/LogInjectionGood.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +""" +@Desc :Log Injection +""" +from flask import Flask +from flask import request +import logging + +logging.basicConfig(level=logging.DEBUG) + +app = Flask(__name__) + +@app.route('/good1') +def good1(): + name = request.args.get('name') + name = name.replace('\r\n','').replace('\n','') + logging.info('User name: ' + name) # Good + return 'good1' + +if __name__ == '__main__': + app.debug = True + handler = logging.FileHandler('log') + app.logger.addHandler(handler) + app.run() diff --git a/python/ql/src/experimental/Security/CWE-348/ClientSuppliedIpUsedInSecurityCheck.py b/python/ql/src/experimental/Security/CWE-348/ClientSuppliedIpUsedInSecurityCheck.py new file mode 100644 index 00000000000..5ae8d3dd1ab --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-348/ClientSuppliedIpUsedInSecurityCheck.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +""" +@Desc :ip address spoofing +""" +from flask import Flask +from flask import request + +app = Flask(__name__) + +@app.route('/bad1') +def bad1(): + client_ip = request.headers.get('x-forwarded-for') + if not client_ip.startswith('192.168.'): + raise Exception('ip illegal') + return 'bad1' + +@app.route('/bad2') +def bad2(): + client_ip = request.headers.get('x-forwarded-for') + if not client_ip == '127.0.0.1': + raise Exception('ip illegal') + return 'bad2' + +@app.route('/good1') +def good1(): + client_ip = request.headers.get('x-forwarded-for') + client_ip = client_ip.split(',')[client_ip.split(',').length - 1] + if not client_ip == '127.0.0.1': + raise Exception('ip illegal') + return 'good1' + +if __name__ == '__main__': + app.debug = True + app.run() \ No newline at end of file diff --git a/python/ql/src/experimental/Security/CWE-348/ClientSuppliedIpUsedInSecurityCheck.qhelp b/python/ql/src/experimental/Security/CWE-348/ClientSuppliedIpUsedInSecurityCheck.qhelp new file mode 100644 index 00000000000..653c93341c9 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-348/ClientSuppliedIpUsedInSecurityCheck.qhelp @@ -0,0 +1,35 @@ +<!DOCTYPE qhelp PUBLIC + "-//Semmle//qhelp//EN" + "qhelp.dtd"> +<qhelp> +<overview> +<p>An original client IP address is retrieved from an http header (<code>X-Forwarded-For</code> or <code>X-Real-IP</code> or <code>Proxy-Client-IP</code> +etc.), which is used to ensure security. Attackers can forge the value of these identifiers to +bypass a ban-list, for example.</p> + +</overview> +<recommendation> + +<p>Do not trust the values of HTTP headers allegedly identifying the originating IP. If you are aware your application will run behind some reverse proxies then the last entry of a <code>X-Forwarded-For</code> header value may be more trustworthy than the rest of it because some reverse proxies append the IP address they observed to the end of any remote-supplied header.</p> + +</recommendation> +<example> + +<p>The following examples show the bad case and the good case respectively. +In <code>bad1</code> method and <code>bad2</code> method, the client ip the <code>X-Forwarded-For</code> is split into comma-separated values, but the less-trustworthy first one is used. Both of these examples could be deceived by providing a forged HTTP header. The method +<code>good1</code> similarly splits an <code>X-Forwarded-For</code> value, but uses the last, more-trustworthy entry.</p> + +<sample src="ClientSuppliedIpUsedInSecurityCheck.py" /> + +</example> +<references> + +<li>Dennis Schneider: <a href="https://www.dennis-schneider.com/blog/prevent-ip-address-spoofing-with-x-forwarded-for-header-and-aws-elb-in-clojure-ring/"> +Prevent IP address spoofing with X-Forwarded-For header when using AWS ELB and Clojure Ring</a> +</li> + +<li>Security Rule Zero: <a href="https://www.f5.com/company/blog/security-rule-zero-a-warning-about-x-forwarded-for">A Warning about X-Forwarded-For</a> +</li> + +</references> +</qhelp> diff --git a/python/ql/src/experimental/Security/CWE-348/ClientSuppliedIpUsedInSecurityCheck.ql b/python/ql/src/experimental/Security/CWE-348/ClientSuppliedIpUsedInSecurityCheck.ql new file mode 100644 index 00000000000..af2d6f8bc16 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-348/ClientSuppliedIpUsedInSecurityCheck.ql @@ -0,0 +1,56 @@ +/** + * @name IP address spoofing + * @description A remote endpoint identifier is read from an HTTP header. Attackers can modify the value + * of the identifier to forge the client ip. + * @kind path-problem + * @problem.severity error + * @precision high + * @id py/ip-address-spoofing + * @tags security + * external/cwe/cwe-348 + */ + +import python +import semmle.python.dataflow.new.DataFlow +import semmle.python.dataflow.new.TaintTracking +import semmle.python.ApiGraphs +import ClientSuppliedIpUsedInSecurityCheckLib +import DataFlow::PathGraph + +/** + * Taint-tracking configuration tracing flow from obtaining a client ip from an HTTP header to a sensitive use. + */ +class ClientSuppliedIpUsedInSecurityCheckConfig extends TaintTracking::Configuration { + ClientSuppliedIpUsedInSecurityCheckConfig() { this = "ClientSuppliedIpUsedInSecurityCheckConfig" } + + override predicate isSource(DataFlow::Node source) { + source instanceof ClientSuppliedIpUsedInSecurityCheck + } + + override predicate isSink(DataFlow::Node sink) { sink instanceof PossibleSecurityCheck } + + override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { + exists(DataFlow::CallCfgNode ccn | + ccn = API::moduleImport("netaddr").getMember("IPAddress").getACall() and + ccn.getArg(0) = pred and + ccn = succ + ) + } + + override predicate isSanitizer(DataFlow::Node node) { + // `client_supplied_ip.split(",")[n]` for `n` > 0 + exists(Subscript ss | + not ss.getIndex().(IntegerLiteral).getText() = "0" and + ss.getObject().(Call).getFunc().(Attribute).getName() = "split" and + ss.getObject().(Call).getAnArg().(StrConst).getText() = "," and + ss = node.asExpr() + ) + } +} + +from + ClientSuppliedIpUsedInSecurityCheckConfig config, DataFlow::PathNode source, + DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "IP address spoofing might include code from $@.", + source.getNode(), "this user input" diff --git a/python/ql/src/experimental/Security/CWE-348/ClientSuppliedIpUsedInSecurityCheckLib.qll b/python/ql/src/experimental/Security/CWE-348/ClientSuppliedIpUsedInSecurityCheckLib.qll new file mode 100644 index 00000000000..97e9bbfd457 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-348/ClientSuppliedIpUsedInSecurityCheckLib.qll @@ -0,0 +1,152 @@ +private import python +private import semmle.python.Concepts +private import semmle.python.ApiGraphs +private import semmle.python.dataflow.new.DataFlow +private import semmle.python.dataflow.new.RemoteFlowSources + +/** + * A data flow source of the client ip obtained according to the remote endpoint identifier specified + * (`X-Forwarded-For`, `X-Real-IP`, `Proxy-Client-IP`, etc.) in the header. + * + * For example: `request.headers.get("X-Forwarded-For")`. + */ +abstract class ClientSuppliedIpUsedInSecurityCheck extends DataFlow::CallCfgNode { } + +private class FlaskClientSuppliedIpUsedInSecurityCheck extends ClientSuppliedIpUsedInSecurityCheck { + FlaskClientSuppliedIpUsedInSecurityCheck() { + exists(RemoteFlowSource rfs, DataFlow::AttrRead get | + rfs.getSourceType() = "flask.request" and this.getFunction() = get + | + // `get` is a call to request.headers.get or request.headers.get_all or request.headers.getlist + // request.headers + get.getObject() + .(DataFlow::AttrRead) + // request + .getObject() + .getALocalSource() = rfs and + get.getAttributeName() in ["get", "get_all", "getlist"] and + get.getObject().(DataFlow::AttrRead).getAttributeName() = "headers" and + this.getArg(0).asExpr().(StrConst).getText().toLowerCase() = clientIpParameterName() + ) + } +} + +private class DjangoClientSuppliedIpUsedInSecurityCheck extends ClientSuppliedIpUsedInSecurityCheck { + DjangoClientSuppliedIpUsedInSecurityCheck() { + exists(RemoteFlowSource rfs, DataFlow::AttrRead get | + rfs.getSourceType() = "django.http.request.HttpRequest" and this.getFunction() = get + | + // `get` is a call to request.headers.get or request.META.get + // request.headers + get.getObject() + .(DataFlow::AttrRead) + // request + .getObject() + .getALocalSource() = rfs and + get.getAttributeName() = "get" and + get.getObject().(DataFlow::AttrRead).getAttributeName() in ["headers", "META"] and + this.getArg(0).asExpr().(StrConst).getText().toLowerCase() = clientIpParameterName() + ) + } +} + +private class TornadoClientSuppliedIpUsedInSecurityCheck extends ClientSuppliedIpUsedInSecurityCheck { + TornadoClientSuppliedIpUsedInSecurityCheck() { + exists(RemoteFlowSource rfs, DataFlow::AttrRead get | + rfs.getSourceType() = "tornado.web.RequestHandler" and this.getFunction() = get + | + // `get` is a call to `rfs`.request.headers.get + // `rfs`.request.headers + get.getObject() + .(DataFlow::AttrRead) + // `rfs`.request + .getObject() + .(DataFlow::AttrRead) + // `rfs` + .getObject() + .getALocalSource() = rfs and + get.getAttributeName() in ["get", "get_list"] and + get.getObject().(DataFlow::AttrRead).getAttributeName() = "headers" and + this.getArg(0).asExpr().(StrConst).getText().toLowerCase() = clientIpParameterName() + ) + } +} + +private string clientIpParameterName() { + result in [ + "x-forwarded-for", "x_forwarded_for", "x-real-ip", "x_real_ip", "proxy-client-ip", + "proxy_client_ip", "wl-proxy-client-ip", "wl_proxy_client_ip", "http_x_forwarded_for", + "http-x-forwarded-for", "http_x_forwarded", "http_x_cluster_client_ip", "http_client_ip", + "http_forwarded_for", "http_forwarded", "http_via", "remote_addr" + ] +} + +/** A data flow sink for ip address forgery vulnerabilities. */ +abstract class PossibleSecurityCheck extends DataFlow::Node { } + +/** A data flow sink for sql operation. */ +private class SqlOperationAsSecurityCheck extends PossibleSecurityCheck { + SqlOperationAsSecurityCheck() { this = any(SqlExecution e).getSql() } +} + +/** + * A data flow sink for remote client ip comparison. + * + * For example: `if not ipAddr.startswith('192.168.') : ...` determine whether the client ip starts + * with `192.168.`, and the program can be deceived by forging the ip address. + */ +private class CompareSink extends PossibleSecurityCheck { + CompareSink() { + exists(Call call | + call.getFunc().(Attribute).getName() = "startswith" and + call.getArg(0).(StrConst).getText().regexpMatch(getIpAddressRegex()) and + not call.getArg(0).(StrConst).getText() = "0:0:0:0:0:0:0:1" and + call.getFunc().(Attribute).getObject() = this.asExpr() + ) + or + exists(Compare compare | + ( + compare.getOp(0) instanceof Eq or + compare.getOp(0) instanceof NotEq + ) and + ( + compare.getLeft() = this.asExpr() and + compare.getComparator(0).(StrConst).getText() instanceof PrivateHostName and + not compare.getComparator(0).(StrConst).getText() = "0:0:0:0:0:0:0:1" + or + compare.getComparator(0) = this.asExpr() and + compare.getLeft().(StrConst).getText() instanceof PrivateHostName and + not compare.getLeft().(StrConst).getText() = "0:0:0:0:0:0:0:1" + ) + ) + or + exists(Compare compare | + ( + compare.getOp(0) instanceof In or + compare.getOp(0) instanceof NotIn + ) and + ( + compare.getLeft() = this.asExpr() + or + compare.getComparator(0) = this.asExpr() and + not compare.getLeft().(StrConst).getText() in ["%", ",", "."] + ) + ) + } +} + +string getIpAddressRegex() { + result = + "^((10\\.((1\\d{2})?|(2[0-4]\\d)?|(25[0-5])?|([1-9]\\d|[0-9])?)(\\.)?)|(192\\.168\\.)|172\\.(1[6789]|2[0-9]|3[01])\\.)((1\\d{2})?|(2[0-4]\\d)?|(25[0-5])?|([1-9]\\d|[0-9])?)(\\.)?((1\\d{2})?|(2[0-4]\\d)?|(25[0-5])?|([1-9]\\d|[0-9])?)$" +} + +/** + * A string matching private host names of IPv4 and IPv6, which only matches the host portion therefore checking for port is not necessary. + * Several examples are localhost, reserved IPv4 IP addresses including 127.0.0.1, 10.x.x.x, 172.16.x,x, 192.168.x,x, and reserved IPv6 addresses including [0:0:0:0:0:0:0:1] and [::1] + */ +private class PrivateHostName extends string { + bindingset[this] + PrivateHostName() { + this.regexpMatch("(?i)localhost(?:[:/?#].*)?|127\\.0\\.0\\.1(?:[:/?#].*)?|10(?:\\.[0-9]+){3}(?:[:/?#].*)?|172\\.16(?:\\.[0-9]+){2}(?:[:/?#].*)?|192.168(?:\\.[0-9]+){2}(?:[:/?#].*)?|\\[?0:0:0:0:0:0:0:1\\]?(?:[:/?#].*)?|\\[?::1\\]?(?:[:/?#].*)?") + } +} diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 82bf7bde537..7bb988f1059 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -14,71 +14,34 @@ private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.TaintTracking private import experimental.semmle.python.Frameworks -/** Provides classes for modeling Regular Expression-related APIs. */ -module RegexExecution { +/** Provides classes for modeling log related APIs. */ +module LogOutput { /** - * A data-flow node that executes a regular expression. + * A data flow node for log output. * * Extend this class to model new APIs. If you want to refine existing API models, - * extend `RegexExecution` instead. + * extend `LogOutput` instead. */ abstract class Range extends DataFlow::Node { /** - * Gets the argument containing the executed expression. + * Get the parameter value of the log output function. */ - abstract DataFlow::Node getRegexNode(); - - /** - * Gets the library used to execute the regular expression. - */ - abstract string getRegexModule(); + abstract DataFlow::Node getAnInput(); } } /** - * A data-flow node that executes a regular expression. + * A data flow node for log output. * * Extend this class to refine existing API models. If you want to model new APIs, - * extend `RegexExecution::Range` instead. + * extend `LogOutput::Range` instead. */ -class RegexExecution extends DataFlow::Node { - RegexExecution::Range range; +class LogOutput extends DataFlow::Node { + LogOutput::Range range; - RegexExecution() { this = range } + LogOutput() { this = range } - DataFlow::Node getRegexNode() { result = range.getRegexNode() } - - string getRegexModule() { result = range.getRegexModule() } -} - -/** Provides classes for modeling Regular Expression escape-related APIs. */ -module RegexEscape { - /** - * A data-flow node that escapes a regular expression. - * - * Extend this class to model new APIs. If you want to refine existing API models, - * extend `RegexEscape` instead. - */ - abstract class Range extends DataFlow::Node { - /** - * Gets the argument containing the escaped expression. - */ - abstract DataFlow::Node getRegexNode(); - } -} - -/** - * A data-flow node that escapes a regular expression. - * - * Extend this class to refine existing API models. If you want to model new APIs, - * extend `RegexEscape::Range` instead. - */ -class RegexEscape extends DataFlow::Node { - RegexEscape::Range range; - - RegexEscape() { this = range } - - DataFlow::Node getRegexNode() { result = range.getRegexNode() } + DataFlow::Node getAnInput() { result = range.getAnInput() } } /** Provides classes for modeling XML parsing APIs. */ @@ -388,3 +351,46 @@ class NoSQLSanitizer extends DataFlow::Node { /** Gets the argument that specifies the NoSQL query to be sanitized. */ DataFlow::Node getAnInput() { result = range.getAnInput() } } + +/** Provides classes for modeling HTTP Header APIs. */ +module HeaderDeclaration { + /** + * A data-flow node that collects functions setting HTTP Headers. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `HeaderDeclaration` instead. + */ + abstract class Range extends DataFlow::Node { + /** + * Gets the argument containing the header name. + */ + abstract DataFlow::Node getNameArg(); + + /** + * Gets the argument containing the header value. + */ + abstract DataFlow::Node getValueArg(); + } +} + +/** + * A data-flow node that collects functions setting HTTP Headers. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `HeaderDeclaration::Range` instead. + */ +class HeaderDeclaration extends DataFlow::Node { + HeaderDeclaration::Range range; + + HeaderDeclaration() { this = range } + + /** + * Gets the argument containing the header name. + */ + DataFlow::Node getNameArg() { result = range.getNameArg() } + + /** + * Gets the argument containing the header value. + */ + DataFlow::Node getValueArg() { result = range.getValueArg() } +} diff --git a/python/ql/src/experimental/semmle/python/Frameworks.qll b/python/ql/src/experimental/semmle/python/Frameworks.qll index 18a66b71e64..9cdd0ee1de4 100644 --- a/python/ql/src/experimental/semmle/python/Frameworks.qll +++ b/python/ql/src/experimental/semmle/python/Frameworks.qll @@ -4,5 +4,9 @@ private import experimental.semmle.python.frameworks.Stdlib private import experimental.semmle.python.frameworks.XML +private import experimental.semmle.python.frameworks.Flask +private import experimental.semmle.python.frameworks.Django +private import experimental.semmle.python.frameworks.Werkzeug private import experimental.semmle.python.frameworks.LDAP private import experimental.semmle.python.frameworks.NoSQL +private import experimental.semmle.python.frameworks.Log diff --git a/python/ql/src/experimental/semmle/python/frameworks/Django.qll b/python/ql/src/experimental/semmle/python/frameworks/Django.qll new file mode 100644 index 00000000000..27ec7f6bd75 --- /dev/null +++ b/python/ql/src/experimental/semmle/python/frameworks/Django.qll @@ -0,0 +1,93 @@ +/** + * Provides classes modeling security-relevant aspects of the `django` PyPI package. + * See https://www.djangoproject.com/. + */ + +private import python +private import semmle.python.frameworks.Django +private import semmle.python.dataflow.new.DataFlow +private import experimental.semmle.python.Concepts +private import semmle.python.ApiGraphs +import semmle.python.dataflow.new.RemoteFlowSources + +private module PrivateDjango { + private module django { + API::Node http() { result = API::moduleImport("django").getMember("http") } + + module http { + API::Node response() { result = http().getMember("response") } + + API::Node request() { result = http().getMember("request") } + + module request { + module HttpRequest { + class DjangoGETParameter extends DataFlow::Node, RemoteFlowSource::Range { + DjangoGETParameter() { this = request().getMember("GET").getMember("get").getACall() } + + override string getSourceType() { result = "django.http.request.GET.get" } + } + } + } + + module response { + module HttpResponse { + API::Node baseClassRef() { + result = response().getMember("HttpResponse").getReturn() + or + // Handle `django.http.HttpResponse` alias + result = http().getMember("HttpResponse").getReturn() + } + + /** Gets a reference to a header instance. */ + private DataFlow::LocalSourceNode headerInstance(DataFlow::TypeTracker t) { + t.start() and + ( + exists(SubscriptNode subscript | + subscript.getObject() = baseClassRef().getAUse().asCfgNode() and + result.asCfgNode() = subscript + ) + or + result.(DataFlow::AttrRead).getObject() = baseClassRef().getAUse() + ) + or + exists(DataFlow::TypeTracker t2 | result = headerInstance(t2).track(t2, t)) + } + + /** Gets a reference to a header instance use. */ + private DataFlow::Node headerInstance() { + headerInstance(DataFlow::TypeTracker::end()).flowsTo(result) + } + + /** Gets a reference to a header instance call with `__setitem__`. */ + private DataFlow::Node headerSetItemCall() { + result = headerInstance() and + result.(DataFlow::AttrRead).getAttributeName() = "__setitem__" + } + + class DjangoResponseSetItemCall extends DataFlow::CallCfgNode, HeaderDeclaration::Range { + DjangoResponseSetItemCall() { this.getFunction() = headerSetItemCall() } + + override DataFlow::Node getNameArg() { result = this.getArg(0) } + + override DataFlow::Node getValueArg() { result = this.getArg(1) } + } + + class DjangoResponseDefinition extends DataFlow::Node, HeaderDeclaration::Range { + DataFlow::Node headerInput; + + DjangoResponseDefinition() { + this.asCfgNode().(DefinitionNode) = headerInstance().asCfgNode() and + headerInput.asCfgNode() = this.asCfgNode().(DefinitionNode).getValue() + } + + override DataFlow::Node getNameArg() { + result.asExpr() = this.asExpr().(Subscript).getIndex() + } + + override DataFlow::Node getValueArg() { result = headerInput } + } + } + } + } + } +} diff --git a/python/ql/src/experimental/semmle/python/frameworks/Flask.qll b/python/ql/src/experimental/semmle/python/frameworks/Flask.qll new file mode 100644 index 00000000000..9c66d9a4601 --- /dev/null +++ b/python/ql/src/experimental/semmle/python/frameworks/Flask.qll @@ -0,0 +1,84 @@ +/** + * Provides classes modeling security-relevant aspects of the `flask` PyPI package. + * See https://flask.palletsprojects.com/en/1.1.x/. + */ + +private import python +private import semmle.python.frameworks.Flask +private import semmle.python.dataflow.new.DataFlow +private import experimental.semmle.python.Concepts +private import semmle.python.ApiGraphs + +module ExperimentalFlask { + /** + * A reference to either `flask.make_response` function, or the `make_response` method on + * an instance of `flask.Flask`. This creates an instance of the `flask_response` + * class (class-attribute on a flask application), which by default is + * `flask.Response`. + * + * See + * - https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.make_response + * - https://flask.palletsprojects.com/en/1.1.x/api/#flask.make_response + */ + private API::Node flaskMakeResponse() { + result = + [API::moduleImport("flask"), Flask::FlaskApp::instance()] + .getMember(["make_response", "jsonify", "make_default_options_response"]) + } + + /** Gets a reference to a header instance. */ + private DataFlow::LocalSourceNode headerInstance(DataFlow::TypeTracker t) { + t.start() and + result.(DataFlow::AttrRead).getObject().getALocalSource() = + [Flask::Response::classRef(), flaskMakeResponse()].getReturn().getAUse() + or + exists(DataFlow::TypeTracker t2 | result = headerInstance(t2).track(t2, t)) + } + + /** Gets a reference to a header instance use. */ + private DataFlow::Node headerInstance() { + headerInstance(DataFlow::TypeTracker::end()).flowsTo(result) + } + + /** Gets a reference to a header instance call/subscript */ + private DataFlow::Node headerInstanceCall() { + headerInstance() in [result.(DataFlow::AttrRead), result.(DataFlow::AttrRead).getObject()] or + headerInstance().asExpr() = result.asExpr().(Subscript).getObject() + } + + class FlaskHeaderDefinition extends DataFlow::Node, HeaderDeclaration::Range { + DataFlow::Node headerInput; + + FlaskHeaderDefinition() { + this.asCfgNode().(DefinitionNode) = headerInstanceCall().asCfgNode() and + headerInput.asCfgNode() = this.asCfgNode().(DefinitionNode).getValue() + } + + override DataFlow::Node getNameArg() { result.asExpr() = this.asExpr().(Subscript).getIndex() } + + override DataFlow::Node getValueArg() { result = headerInput } + } + + private class FlaskMakeResponseExtend extends DataFlow::CallCfgNode, HeaderDeclaration::Range { + KeyValuePair item; + + FlaskMakeResponseExtend() { + this.getFunction() = headerInstanceCall() and + item = this.getArg(_).asExpr().(Dict).getAnItem() + } + + override DataFlow::Node getNameArg() { result.asExpr() = item.getKey() } + + override DataFlow::Node getValueArg() { result.asExpr() = item.getValue() } + } + + private class FlaskResponse extends DataFlow::CallCfgNode, HeaderDeclaration::Range { + KeyValuePair item; + + FlaskResponse() { this = Flask::Response::classRef().getACall() } + + override DataFlow::Node getNameArg() { result.asExpr() = item.getKey() } + + override DataFlow::Node getValueArg() { result.asExpr() = item.getValue() } + } +} diff --git a/python/ql/src/experimental/semmle/python/frameworks/Log.qll b/python/ql/src/experimental/semmle/python/frameworks/Log.qll new file mode 100644 index 00000000000..675b9be1c2d --- /dev/null +++ b/python/ql/src/experimental/semmle/python/frameworks/Log.qll @@ -0,0 +1,118 @@ +/** + * Provides classes modeling security-relevant aspects of the log libraries. + */ + +private import python +private import semmle.python.dataflow.new.DataFlow +private import semmle.python.dataflow.new.TaintTracking +private import semmle.python.dataflow.new.RemoteFlowSources +private import experimental.semmle.python.Concepts +private import semmle.python.frameworks.Flask +private import semmle.python.ApiGraphs + +/** + * Provides models for Python's log-related libraries. + */ +private module log { + /** + * Log output method list. + * + * See https://docs.python.org/3/library/logging.html#logger-objects + */ + private class LogOutputMethods extends string { + LogOutputMethods() { + this in ["info", "error", "warn", "warning", "debug", "critical", "exception", "log"] + } + } + + /** + * The class used to find the log output method of the `logging` module. + * + * See `LogOutputMethods` + */ + private class LoggingCall extends DataFlow::CallCfgNode, LogOutput::Range { + LoggingCall() { + this = API::moduleImport("logging").getMember(any(LogOutputMethods m)).getACall() + } + + override DataFlow::Node getAnInput() { + this.getFunction().(DataFlow::AttrRead).getAttributeName() != "log" and + result in [this.getArg(_), this.getArgByName(_)] // this includes the arg named "msg" + or + this.getFunction().(DataFlow::AttrRead).getAttributeName() = "log" and + result in [this.getArg(any(int i | i > 0)), this.getArgByName(any(string s | s != "level"))] + } + } + + /** + * The class used to find log output methods related to the `logging.getLogger` instance. + * + * See `LogOutputMethods` + */ + private class LoggerCall extends DataFlow::CallCfgNode, LogOutput::Range { + LoggerCall() { + this = + API::moduleImport("logging") + .getMember("getLogger") + .getReturn() + .getMember(any(LogOutputMethods m)) + .getACall() + } + + override DataFlow::Node getAnInput() { + this.getFunction().(DataFlow::AttrRead).getAttributeName() != "log" and + result in [this.getArg(_), this.getArgByName(_)] // this includes the arg named "msg" + or + this.getFunction().(DataFlow::AttrRead).getAttributeName() = "log" and + result in [this.getArg(any(int i | i > 0)), this.getArgByName(any(string s | s != "level"))] + } + } + + /** + * The class used to find the relevant log output method of the `flask.Flask.logger` instance (flask application). + * + * See `LogOutputMethods` + */ + private class FlaskLoggingCall extends DataFlow::CallCfgNode, LogOutput::Range { + FlaskLoggingCall() { + this = + Flask::FlaskApp::instance() + .getMember("logger") + .getMember(any(LogOutputMethods m)) + .getACall() + } + + override DataFlow::Node getAnInput() { + this.getFunction().(DataFlow::AttrRead).getAttributeName() != "log" and + result in [this.getArg(_), this.getArgByName(_)] // this includes the arg named "msg" + or + this.getFunction().(DataFlow::AttrRead).getAttributeName() = "log" and + result in [this.getArg(any(int i | i > 0)), this.getArgByName(any(string s | s != "level"))] + } + } + + /** + * The class used to find the relevant log output method of the `django.utils.log.request_logger` instance (django application). + * + * See `LogOutputMethods` + */ + private class DjangoLoggingCall extends DataFlow::CallCfgNode, LogOutput::Range { + DjangoLoggingCall() { + this = + API::moduleImport("django") + .getMember("utils") + .getMember("log") + .getMember("request_logger") + .getMember(any(LogOutputMethods m)) + .getACall() + } + + override DataFlow::Node getAnInput() { + this.getFunction().(DataFlow::AttrRead).getAttributeName() != "log" and + result in [this.getArg(_), this.getArgByName(_)] // this includes the arg named "msg" + or + this.getFunction().(DataFlow::AttrRead).getAttributeName() = "log" and + result in [this.getArg(any(int i | i > 0)), this.getArgByName(any(string s | s != "level"))] + } + } +} diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index b3b70f43394..420caf0d73b 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -9,91 +9,3 @@ private import semmle.python.dataflow.new.TaintTracking private import semmle.python.dataflow.new.RemoteFlowSources private import experimental.semmle.python.Concepts private import semmle.python.ApiGraphs - -/** - * Provides models for Python's `re` library. - * - * See https://docs.python.org/3/library/re.html - */ -private module Re { - /** - * List of `re` methods immediately executing an expression. - * - * See https://docs.python.org/3/library/re.html#module-contents - */ - private class RegexExecutionMethods extends string { - RegexExecutionMethods() { - this in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn"] - } - } - - /** - * A class to find `re` methods immediately executing an expression. - * - * See `RegexExecutionMethods` - */ - private class DirectRegex extends DataFlow::CallCfgNode, RegexExecution::Range { - DataFlow::Node regexNode; - - DirectRegex() { - this = API::moduleImport("re").getMember(any(RegexExecutionMethods m)).getACall() and - regexNode = this.getArg(0) - } - - override DataFlow::Node getRegexNode() { result = regexNode } - - override string getRegexModule() { result = "re" } - } - - /** - * A class to find `re` methods immediately executing a compiled expression by `re.compile`. - * - * Given the following example: - * - * ```py - * pattern = re.compile(input) - * pattern.match(s) - * ``` - * - * This class will identify that `re.compile` compiles `input` and afterwards - * executes `re`'s `match`. As a result, `this` will refer to `pattern.match(s)` - * and `this.getRegexNode()` will return the node for `input` (`re.compile`'s first argument) - * - * - * See `RegexExecutionMethods` - * - * See https://docs.python.org/3/library/re.html#regular-expression-objects - */ - private class CompiledRegex extends DataFlow::MethodCallNode, RegexExecution::Range { - DataFlow::Node regexNode; - - CompiledRegex() { - exists(DataFlow::MethodCallNode patternCall | - patternCall = API::moduleImport("re").getMember("compile").getACall() and - patternCall.flowsTo(this.getObject()) and - this.getMethodName() instanceof RegexExecutionMethods and - regexNode = patternCall.getArg(0) - ) - } - - override DataFlow::Node getRegexNode() { result = regexNode } - - override string getRegexModule() { result = "re" } - } - - /** - * A class to find `re` methods escaping an expression. - * - * See https://docs.python.org/3/library/re.html#re.escape - */ - class ReEscape extends DataFlow::CallCfgNode, RegexEscape::Range { - DataFlow::Node regexNode; - - ReEscape() { - this = API::moduleImport("re").getMember("escape").getACall() and - regexNode = this.getArg(0) - } - - override DataFlow::Node getRegexNode() { result = regexNode } - } -} diff --git a/python/ql/src/experimental/semmle/python/frameworks/Werkzeug.qll b/python/ql/src/experimental/semmle/python/frameworks/Werkzeug.qll new file mode 100644 index 00000000000..dce67c258fb --- /dev/null +++ b/python/ql/src/experimental/semmle/python/frameworks/Werkzeug.qll @@ -0,0 +1,33 @@ +/** + * Provides classes modeling security-relevant aspects of the `Werkzeug` PyPI package. + * See + * - https://pypi.org/project/Werkzeug/ + * - https://werkzeug.palletsprojects.com/en/1.0.x/#werkzeug + */ + +private import python +private import semmle.python.frameworks.Flask +private import semmle.python.dataflow.new.DataFlow +private import experimental.semmle.python.Concepts +private import semmle.python.ApiGraphs + +private module Werkzeug { + module datastructures { + module Headers { + class WerkzeugHeaderAddCall extends DataFlow::CallCfgNode, HeaderDeclaration::Range { + WerkzeugHeaderAddCall() { + this.getFunction().(DataFlow::AttrRead).getObject().getALocalSource() = + API::moduleImport("werkzeug") + .getMember("datastructures") + .getMember("Headers") + .getACall() and + this.getFunction().(DataFlow::AttrRead).getAttributeName() = "add" + } + + override DataFlow::Node getNameArg() { result = this.getArg(0) } + + override DataFlow::Node getValueArg() { result = this.getArg(1) } + } + } + } +} diff --git a/python/ql/src/experimental/semmle/python/security/injection/HTTPHeaders.qll b/python/ql/src/experimental/semmle/python/security/injection/HTTPHeaders.qll new file mode 100644 index 00000000000..4ba70cd37a2 --- /dev/null +++ b/python/ql/src/experimental/semmle/python/security/injection/HTTPHeaders.qll @@ -0,0 +1,20 @@ +import python +import experimental.semmle.python.Concepts +import semmle.python.dataflow.new.DataFlow +import semmle.python.dataflow.new.TaintTracking +import semmle.python.dataflow.new.RemoteFlowSources + +/** + * A taint-tracking configuration for detecting HTTP Header injections. + */ +class HeaderInjectionFlowConfig extends TaintTracking::Configuration { + HeaderInjectionFlowConfig() { this = "HeaderInjectionFlowConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { + exists(HeaderDeclaration headerDeclaration | + sink in [headerDeclaration.getNameArg(), headerDeclaration.getValueArg()] + ) + } +} diff --git a/python/ql/src/experimental/semmle/python/security/injection/LogInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/LogInjection.qll new file mode 100644 index 00000000000..4ee9c69a4a9 --- /dev/null +++ b/python/ql/src/experimental/semmle/python/security/injection/LogInjection.qll @@ -0,0 +1,24 @@ +import python +import semmle.python.Concepts +import experimental.semmle.python.Concepts +import semmle.python.dataflow.new.DataFlow +import semmle.python.dataflow.new.TaintTracking +import semmle.python.dataflow.new.RemoteFlowSources + +/** + * A taint-tracking configuration for tracking untrusted user input used in log entries. + */ +class LogInjectionFlowConfig extends TaintTracking::Configuration { + LogInjectionFlowConfig() { this = "LogInjectionFlowConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink = any(LogOutput logoutput).getAnInput() } + + override predicate isSanitizer(DataFlow::Node node) { + exists(CallNode call | + node.asCfgNode() = call.getFunction().(AttrNode).getObject("replace") and + call.getArg(0).getNode().(StrConst).getText() in ["\r\n", "\n"] + ) + } +} diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll deleted file mode 100644 index 7b7b08cacab..00000000000 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Provides a taint-tracking configuration for detecting regular expression injection - * vulnerabilities. - */ - -import python -import experimental.semmle.python.Concepts -import semmle.python.dataflow.new.DataFlow -import semmle.python.dataflow.new.TaintTracking -import semmle.python.dataflow.new.RemoteFlowSources - -/** - * A class to find methods executing regular expressions. - * - * See `RegexExecution` - */ -class RegexInjectionSink extends DataFlow::Node { - string regexModule; - Attribute regexMethod; - - RegexInjectionSink() { - exists(RegexExecution reExec | - this = reExec.getRegexNode() and - regexModule = reExec.getRegexModule() and - regexMethod = reExec.(DataFlow::CallCfgNode).getFunction().asExpr().(Attribute) - ) - } - - /** - * Gets the argument containing the executed expression. - */ - string getRegexModule() { result = regexModule } - - /** - * Gets the method used to execute the regular expression. - */ - Attribute getRegexMethod() { result = regexMethod } -} - -/** - * A taint-tracking configuration for detecting regular expression injections. - */ -class RegexInjectionFlowConfig extends TaintTracking::Configuration { - RegexInjectionFlowConfig() { this = "RegexInjectionFlowConfig" } - - override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - - override predicate isSink(DataFlow::Node sink) { sink instanceof RegexInjectionSink } - - override predicate isSanitizer(DataFlow::Node sanitizer) { - sanitizer = any(RegexEscape reEscape).getRegexNode() - } -} diff --git a/python/ql/src/external/DefectFilter.qll b/python/ql/src/external/DefectFilter.qll index 62704b9fd0e..1421c6bb475 100644 --- a/python/ql/src/external/DefectFilter.qll +++ b/python/ql/src/external/DefectFilter.qll @@ -8,7 +8,7 @@ import semmle.python.Files * column `startcol` of line `startline` to column `endcol` of line `endline` * in file `filepath`. * - * For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * For more information, see [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ external predicate defectResults( int id, string queryPath, string filepath, int startline, int startcol, int endline, int endcol, @@ -54,7 +54,7 @@ class DefectResult extends int { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/python/ql/src/external/Thrift.qll b/python/ql/src/external/Thrift.qll index f9f8d67701d..8ea9a7dc87f 100644 --- a/python/ql/src/external/Thrift.qll +++ b/python/ql/src/external/Thrift.qll @@ -38,7 +38,7 @@ class ThriftElement extends ExternalData { * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn diff --git a/python/ql/test/TestUtilities/InlineExpectationsTest.qll b/python/ql/test/TestUtilities/InlineExpectationsTest.qll index d351bac89a8..52a790cca28 100644 --- a/python/ql/test/TestUtilities/InlineExpectationsTest.qll +++ b/python/ql/test/TestUtilities/InlineExpectationsTest.qll @@ -4,7 +4,7 @@ * (in that the `.expected` file should always be empty). * * To add this framework to a new language: - * - Add a file `InlineExpectationsTestPrivate.qll` that defines a `LineComment` class. This class + * - Add a file `InlineExpectationsTestPrivate.qll` that defines a `ExpectationComment` class. This class * must support a `getContents` method that returns the contents of the given comment, _excluding_ * the comment indicator itself. It should also define `toString` and `getLocation` as usual. * @@ -60,8 +60,8 @@ * * Example: * ```cpp - * int i = x + 5; // $const=5 - * int j = y + (7 - 3) // $const=7 const=3 const=4 // The result of the subtraction is a constant. + * int i = x + 5; // $ const=5 + * int j = y + (7 - 3) // $ const=7 const=3 const=4 // The result of the subtraction is a constant. * ``` * * For tests that contain known missing and spurious results, it is possible to further @@ -194,7 +194,7 @@ private int getEndOfColumnPosition(int start, string content) { } private predicate getAnExpectation( - LineComment comment, TColumn column, string expectation, string tags, string value + ExpectationComment comment, TColumn column, string expectation, string tags, string value ) { exists(string content | content = comment.getContents().regexpCapture(expectationCommentPattern(), 1) and @@ -247,14 +247,14 @@ private newtype TFailureLocatable = ) { test.hasActualResult(location, element, tag, value) } or - TValidExpectation(LineComment comment, string tag, string value, string knownFailure) { + TValidExpectation(ExpectationComment comment, string tag, string value, string knownFailure) { exists(TColumn column, string tags | getAnExpectation(comment, column, _, tags, value) and tag = tags.splitAt(",") and knownFailure = getColumnString(column) ) } or - TInvalidExpectation(LineComment comment, string expectation) { + TInvalidExpectation(ExpectationComment comment, string expectation) { getAnExpectation(comment, _, expectation, _, _) and not expectation.regexpMatch(expectationPattern()) } @@ -292,7 +292,7 @@ class ActualResult extends FailureLocatable, TActualResult { } abstract private class Expectation extends FailureLocatable { - LineComment comment; + ExpectationComment comment; override string toString() { result = comment.toString() } diff --git a/python/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll b/python/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll index aaea45a3cce..9c2ca9335e3 100644 --- a/python/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll +++ b/python/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll @@ -4,4 +4,4 @@ import python * A class representing line comments in Python. As this is the only form of comment Python * permits, we simply reuse the `Comment` class. */ -class LineComment = Comment; +class ExpectationComment = Comment; diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected index 68cd9dc50ee..f89248985d7 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.expected +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.expected @@ -148,243 +148,245 @@ edges | test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:407:26:407:26 | ControlFlowNode for b [Dictionary element at key b] | | test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() | | test.py:412:39:412:44 | ControlFlowNode for SOURCE | test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | -| test.py:433:10:433:15 | ControlFlowNode for SOURCE | test.py:433:10:433:38 | ControlFlowNode for IfExp | -| test.py:441:34:441:39 | ControlFlowNode for SOURCE | test.py:441:10:441:39 | ControlFlowNode for IfExp | -| test.py:462:11:462:11 | ControlFlowNode for x | test.py:463:16:463:16 | ControlFlowNode for x | -| test.py:465:12:465:17 | ControlFlowNode for SOURCE | test.py:462:11:462:11 | ControlFlowNode for x | -| test.py:465:12:465:17 | ControlFlowNode for SOURCE | test.py:465:10:465:18 | ControlFlowNode for f() | -| test.py:469:19:469:19 | ControlFlowNode for b | test.py:470:16:470:16 | ControlFlowNode for b | -| test.py:472:28:472:33 | ControlFlowNode for SOURCE | test.py:469:19:469:19 | ControlFlowNode for b | -| test.py:472:28:472:33 | ControlFlowNode for SOURCE | test.py:472:10:472:34 | ControlFlowNode for second() | -| test.py:483:19:483:19 | ControlFlowNode for b | test.py:484:16:484:16 | ControlFlowNode for b | -| test.py:486:30:486:35 | ControlFlowNode for SOURCE | test.py:483:19:483:19 | ControlFlowNode for b | -| test.py:486:30:486:35 | ControlFlowNode for SOURCE | test.py:486:10:486:36 | ControlFlowNode for second() | -| test.py:497:19:497:19 | ControlFlowNode for b | test.py:498:16:498:16 | ControlFlowNode for b | -| test.py:500:10:500:43 | KwUnpacked b | test.py:497:19:497:19 | ControlFlowNode for b | -| test.py:500:10:500:43 | KwUnpacked b | test.py:500:10:500:43 | ControlFlowNode for second() | -| test.py:500:30:500:42 | ControlFlowNode for Dict [Dictionary element at key b] | test.py:500:10:500:43 | KwUnpacked b | -| test.py:500:36:500:41 | ControlFlowNode for SOURCE | test.py:500:30:500:42 | ControlFlowNode for Dict [Dictionary element at key b] | -| test.py:504:30:504:30 | ControlFlowNode for b [Tuple element at index 0] | test.py:504:33:504:33 | ControlFlowNode for b [Tuple element at index 0] | -| test.py:504:33:504:33 | ControlFlowNode for b [Tuple element at index 0] | test.py:504:33:504:36 | ControlFlowNode for Subscript | -| test.py:505:10:505:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:504:30:504:30 | ControlFlowNode for b [Tuple element at index 0] | -| test.py:505:10:505:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:505:10:505:39 | ControlFlowNode for f_extra_pos() | -| test.py:505:33:505:38 | ControlFlowNode for SOURCE | test.py:505:10:505:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | -| test.py:509:35:509:35 | ControlFlowNode for b [Dictionary element at key b] | test.py:509:38:509:38 | ControlFlowNode for b [Dictionary element at key b] | -| test.py:509:38:509:38 | ControlFlowNode for b [Dictionary element at key b] | test.py:509:38:509:43 | ControlFlowNode for Subscript | -| test.py:510:10:510:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:509:35:509:35 | ControlFlowNode for b [Dictionary element at key b] | -| test.py:510:10:510:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:510:10:510:45 | ControlFlowNode for f_extra_keyword() | -| test.py:510:39:510:44 | ControlFlowNode for SOURCE | test.py:510:10:510:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | -| test.py:522:9:522:14 | ControlFlowNode for SOURCE | test.py:524:10:524:10 | ControlFlowNode for a | -| test.py:522:9:522:14 | ControlFlowNode for SOURCE | test.py:529:10:529:10 | ControlFlowNode for b | -| test.py:534:10:534:15 | ControlFlowNode for SOURCE | test.py:534:10:534:26 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:534:10:534:26 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:535:5:535:8 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:535:5:535:5 | SSA variable a | test.py:536:10:536:10 | ControlFlowNode for a | -| test.py:535:5:535:8 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:535:5:535:5 | SSA variable a | -| test.py:542:10:542:15 | ControlFlowNode for SOURCE | test.py:542:10:542:36 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:542:10:542:36 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:543:5:543:13 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:542:10:542:36 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | test.py:543:5:543:13 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | -| test.py:542:19:542:35 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:542:10:542:36 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | -| test.py:542:30:542:35 | ControlFlowNode for SOURCE | test.py:542:19:542:35 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:543:5:543:5 | SSA variable a | test.py:544:10:544:10 | ControlFlowNode for a | -| test.py:543:5:543:13 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:543:5:543:5 | SSA variable a | -| test.py:543:5:543:13 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | test.py:543:9:543:12 | IterableSequence [Tuple element at index 1] | -| test.py:543:9:543:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:543:12:543:12 | SSA variable c | -| test.py:543:9:543:12 | IterableSequence [Tuple element at index 1] | test.py:543:9:543:12 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:543:12:543:12 | SSA variable c | test.py:546:10:546:10 | ControlFlowNode for c | -| test.py:551:9:551:33 | ControlFlowNode for List [List element, List element, List element, List element] | test.py:552:5:552:14 | IterableSequence [List element, List element, List element, List element] | -| test.py:551:10:551:21 | ControlFlowNode for List [List element, List element, List element] | test.py:551:9:551:33 | ControlFlowNode for List [List element, List element, List element, List element] | -| test.py:551:11:551:20 | ControlFlowNode for List [List element, List element] | test.py:551:10:551:21 | ControlFlowNode for List [List element, List element, List element] | -| test.py:551:12:551:19 | ControlFlowNode for List [List element] | test.py:551:11:551:20 | ControlFlowNode for List [List element, List element] | -| test.py:551:13:551:18 | ControlFlowNode for SOURCE | test.py:551:12:551:19 | ControlFlowNode for List [List element] | -| test.py:552:5:552:11 | ControlFlowNode for List [Tuple element at index 0, List element, List element] | test.py:552:6:552:10 | IterableSequence [List element, List element] | -| test.py:552:5:552:11 | IterableElement [List element, List element] | test.py:552:5:552:11 | ControlFlowNode for List [Tuple element at index 0, List element, List element] | -| test.py:552:5:552:11 | IterableSequence [List element, List element, List element] | test.py:552:5:552:11 | IterableElement [List element, List element] | -| test.py:552:5:552:14 | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] | test.py:552:5:552:11 | IterableSequence [List element, List element, List element] | -| test.py:552:5:552:14 | IterableElement [List element, List element, List element] | test.py:552:5:552:14 | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] | -| test.py:552:5:552:14 | IterableSequence [List element, List element, List element, List element] | test.py:552:5:552:14 | IterableElement [List element, List element, List element] | -| test.py:552:6:552:10 | ControlFlowNode for List [Tuple element at index 0, List element] | test.py:552:7:552:9 | IterableSequence [List element] | -| test.py:552:6:552:10 | IterableElement [List element] | test.py:552:6:552:10 | ControlFlowNode for List [Tuple element at index 0, List element] | -| test.py:552:6:552:10 | IterableSequence [List element, List element] | test.py:552:6:552:10 | IterableElement [List element] | -| test.py:552:7:552:9 | ControlFlowNode for List [Tuple element at index 0] | test.py:552:8:552:8 | SSA variable a | -| test.py:552:7:552:9 | IterableElement | test.py:552:7:552:9 | ControlFlowNode for List [Tuple element at index 0] | -| test.py:552:7:552:9 | IterableSequence [List element] | test.py:552:7:552:9 | IterableElement | -| test.py:552:8:552:8 | SSA variable a | test.py:553:10:553:10 | ControlFlowNode for a | -| test.py:559:10:559:15 | ControlFlowNode for SOURCE | test.py:559:10:559:34 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:559:10:559:34 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:560:5:560:12 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:559:10:559:34 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:560:5:560:12 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:559:18:559:23 | ControlFlowNode for SOURCE | test.py:559:10:559:34 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:560:5:560:5 | SSA variable a | test.py:561:10:561:10 | ControlFlowNode for a | -| test.py:560:5:560:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:560:5:560:5 | SSA variable a | -| test.py:560:5:560:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:560:8:560:9 | IterableElement | -| test.py:560:5:560:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:560:12:560:12 | SSA variable c | -| test.py:560:8:560:9 | IterableElement | test.py:560:8:560:9 | SSA variable b [List element] | -| test.py:560:8:560:9 | SSA variable b [List element] | test.py:563:10:563:10 | ControlFlowNode for b [List element] | -| test.py:560:12:560:12 | SSA variable c | test.py:564:12:564:12 | ControlFlowNode for c | -| test.py:563:10:563:10 | ControlFlowNode for b [List element] | test.py:563:10:563:13 | ControlFlowNode for Subscript | -| test.py:569:10:569:15 | ControlFlowNode for SOURCE | test.py:569:10:569:23 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:569:10:569:23 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:570:5:570:12 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:569:10:569:23 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:570:5:570:12 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:569:18:569:23 | ControlFlowNode for SOURCE | test.py:569:10:569:23 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:570:5:570:5 | SSA variable a | test.py:571:10:571:10 | ControlFlowNode for a | -| test.py:570:5:570:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:570:5:570:5 | SSA variable a | -| test.py:570:5:570:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:570:12:570:12 | SSA variable c | -| test.py:570:12:570:12 | SSA variable c | test.py:573:10:573:10 | ControlFlowNode for c | -| test.py:578:10:578:61 | ControlFlowNode for List [List element, List element] | test.py:581:6:581:23 | IterableSequence [List element, List element] | -| test.py:578:10:578:61 | ControlFlowNode for List [List element, List element] | test.py:589:5:589:24 | IterableSequence [List element, List element] | -| test.py:578:10:578:61 | ControlFlowNode for List [List element, List element] | test.py:597:6:597:23 | IterableSequence [List element, List element] | -| test.py:578:11:578:37 | ControlFlowNode for List [List element] | test.py:578:10:578:61 | ControlFlowNode for List [List element, List element] | -| test.py:578:12:578:17 | ControlFlowNode for SOURCE | test.py:578:11:578:37 | ControlFlowNode for List [List element] | -| test.py:578:31:578:36 | ControlFlowNode for SOURCE | test.py:578:11:578:37 | ControlFlowNode for List [List element] | -| test.py:578:40:578:47 | ControlFlowNode for List [List element] | test.py:578:10:578:61 | ControlFlowNode for List [List element, List element] | -| test.py:578:41:578:46 | ControlFlowNode for SOURCE | test.py:578:40:578:47 | ControlFlowNode for List [List element] | -| test.py:581:6:581:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | test.py:581:7:581:16 | IterableSequence [List element] | -| test.py:581:6:581:23 | IterableElement [List element] | test.py:581:6:581:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | -| test.py:581:6:581:23 | IterableSequence [List element, List element] | test.py:581:6:581:23 | IterableElement [List element] | -| test.py:581:7:581:8 | SSA variable a1 | test.py:582:10:582:11 | ControlFlowNode for a1 | -| test.py:581:7:581:16 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:581:7:581:8 | SSA variable a1 | -| test.py:581:7:581:16 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:581:11:581:12 | SSA variable a2 | -| test.py:581:7:581:16 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:581:15:581:16 | SSA variable a3 | -| test.py:581:7:581:16 | IterableElement | test.py:581:7:581:16 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:581:7:581:16 | IterableElement | test.py:581:7:581:16 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:581:7:581:16 | IterableElement | test.py:581:7:581:16 | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:581:7:581:16 | IterableSequence [List element] | test.py:581:7:581:16 | IterableElement | -| test.py:581:11:581:12 | SSA variable a2 | test.py:583:12:583:13 | ControlFlowNode for a2 | -| test.py:581:15:581:16 | SSA variable a3 | test.py:584:10:584:11 | ControlFlowNode for a3 | -| test.py:589:5:589:24 | ControlFlowNode for List [Tuple element at index 0, List element] | test.py:589:7:589:16 | IterableSequence [List element] | -| test.py:589:5:589:24 | IterableElement [List element] | test.py:589:5:589:24 | ControlFlowNode for List [Tuple element at index 0, List element] | -| test.py:589:5:589:24 | IterableSequence [List element, List element] | test.py:589:5:589:24 | IterableElement [List element] | -| test.py:589:7:589:8 | SSA variable a1 | test.py:590:10:590:11 | ControlFlowNode for a1 | -| test.py:589:7:589:16 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:589:7:589:8 | SSA variable a1 | -| test.py:589:7:589:16 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:589:11:589:12 | SSA variable a2 | -| test.py:589:7:589:16 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:589:15:589:16 | SSA variable a3 | -| test.py:589:7:589:16 | IterableElement | test.py:589:7:589:16 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:589:7:589:16 | IterableElement | test.py:589:7:589:16 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:589:7:589:16 | IterableElement | test.py:589:7:589:16 | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:589:7:589:16 | IterableSequence [List element] | test.py:589:7:589:16 | IterableElement | -| test.py:589:11:589:12 | SSA variable a2 | test.py:591:12:591:13 | ControlFlowNode for a2 | -| test.py:589:15:589:16 | SSA variable a3 | test.py:592:10:592:11 | ControlFlowNode for a3 | -| test.py:597:6:597:17 | ControlFlowNode for List [Tuple element at index 0] | test.py:597:7:597:8 | SSA variable a1 | -| test.py:597:6:597:17 | ControlFlowNode for List [Tuple element at index 1] | test.py:597:11:597:12 | SSA variable a2 | -| test.py:597:6:597:17 | ControlFlowNode for List [Tuple element at index 2] | test.py:597:15:597:16 | SSA variable a3 | -| test.py:597:6:597:17 | IterableElement | test.py:597:6:597:17 | ControlFlowNode for List [Tuple element at index 0] | -| test.py:597:6:597:17 | IterableElement | test.py:597:6:597:17 | ControlFlowNode for List [Tuple element at index 1] | -| test.py:597:6:597:17 | IterableElement | test.py:597:6:597:17 | ControlFlowNode for List [Tuple element at index 2] | -| test.py:597:6:597:17 | IterableSequence [List element] | test.py:597:6:597:17 | IterableElement | -| test.py:597:6:597:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | test.py:597:6:597:17 | IterableSequence [List element] | -| test.py:597:6:597:23 | IterableElement [List element] | test.py:597:6:597:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | -| test.py:597:6:597:23 | IterableSequence [List element, List element] | test.py:597:6:597:23 | IterableElement [List element] | -| test.py:597:7:597:8 | SSA variable a1 | test.py:598:10:598:11 | ControlFlowNode for a1 | -| test.py:597:11:597:12 | SSA variable a2 | test.py:599:12:599:13 | ControlFlowNode for a2 | -| test.py:597:15:597:16 | SSA variable a3 | test.py:600:10:600:11 | ControlFlowNode for a3 | -| test.py:606:11:606:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:609:5:609:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | -| test.py:606:11:606:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:618:6:618:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | -| test.py:606:11:606:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:627:5:627:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | -| test.py:606:11:606:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:636:6:636:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | -| test.py:606:11:606:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:609:5:609:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | -| test.py:606:11:606:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:618:6:618:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | -| test.py:606:11:606:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:627:5:627:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | -| test.py:606:11:606:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:636:6:636:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | -| test.py:606:12:606:17 | ControlFlowNode for SOURCE | test.py:606:12:606:36 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:606:12:606:36 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:606:11:606:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | -| test.py:606:12:606:36 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:606:11:606:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | -| test.py:606:31:606:36 | ControlFlowNode for SOURCE | test.py:606:12:606:36 | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:609:5:609:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | test.py:609:6:609:14 | IterableSequence [Tuple element at index 0] | -| test.py:609:5:609:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | test.py:609:6:609:14 | IterableSequence [Tuple element at index 2] | -| test.py:609:6:609:14 | ControlFlowNode for List [Tuple element at index 0] | test.py:609:7:609:8 | SSA variable a1 | -| test.py:609:6:609:14 | ControlFlowNode for List [Tuple element at index 2] | test.py:609:11:609:13 | IterableElement | -| test.py:609:6:609:14 | IterableSequence [Tuple element at index 0] | test.py:609:6:609:14 | ControlFlowNode for List [Tuple element at index 0] | -| test.py:609:6:609:14 | IterableSequence [Tuple element at index 2] | test.py:609:6:609:14 | ControlFlowNode for List [Tuple element at index 2] | +| test.py:429:15:429:20 | ControlFlowNode for SOURCE | test.py:429:10:429:20 | ControlFlowNode for BoolExpr | +| test.py:434:16:434:21 | ControlFlowNode for SOURCE | test.py:434:10:434:21 | ControlFlowNode for BoolExpr | +| test.py:445:10:445:15 | ControlFlowNode for SOURCE | test.py:445:10:445:38 | ControlFlowNode for IfExp | +| test.py:453:34:453:39 | ControlFlowNode for SOURCE | test.py:453:10:453:39 | ControlFlowNode for IfExp | +| test.py:474:11:474:11 | ControlFlowNode for x | test.py:475:16:475:16 | ControlFlowNode for x | +| test.py:477:12:477:17 | ControlFlowNode for SOURCE | test.py:474:11:474:11 | ControlFlowNode for x | +| test.py:477:12:477:17 | ControlFlowNode for SOURCE | test.py:477:10:477:18 | ControlFlowNode for f() | +| test.py:481:19:481:19 | ControlFlowNode for b | test.py:482:16:482:16 | ControlFlowNode for b | +| test.py:484:28:484:33 | ControlFlowNode for SOURCE | test.py:481:19:481:19 | ControlFlowNode for b | +| test.py:484:28:484:33 | ControlFlowNode for SOURCE | test.py:484:10:484:34 | ControlFlowNode for second() | +| test.py:495:19:495:19 | ControlFlowNode for b | test.py:496:16:496:16 | ControlFlowNode for b | +| test.py:498:30:498:35 | ControlFlowNode for SOURCE | test.py:495:19:495:19 | ControlFlowNode for b | +| test.py:498:30:498:35 | ControlFlowNode for SOURCE | test.py:498:10:498:36 | ControlFlowNode for second() | +| test.py:509:19:509:19 | ControlFlowNode for b | test.py:510:16:510:16 | ControlFlowNode for b | +| test.py:512:10:512:43 | KwUnpacked b | test.py:509:19:509:19 | ControlFlowNode for b | +| test.py:512:10:512:43 | KwUnpacked b | test.py:512:10:512:43 | ControlFlowNode for second() | +| test.py:512:30:512:42 | ControlFlowNode for Dict [Dictionary element at key b] | test.py:512:10:512:43 | KwUnpacked b | +| test.py:512:36:512:41 | ControlFlowNode for SOURCE | test.py:512:30:512:42 | ControlFlowNode for Dict [Dictionary element at key b] | +| test.py:516:30:516:30 | ControlFlowNode for b [Tuple element at index 0] | test.py:516:33:516:33 | ControlFlowNode for b [Tuple element at index 0] | +| test.py:516:33:516:33 | ControlFlowNode for b [Tuple element at index 0] | test.py:516:33:516:36 | ControlFlowNode for Subscript | +| test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:516:30:516:30 | ControlFlowNode for b [Tuple element at index 0] | +| test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() | +| test.py:517:33:517:38 | ControlFlowNode for SOURCE | test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | +| test.py:521:35:521:35 | ControlFlowNode for b [Dictionary element at key b] | test.py:521:38:521:38 | ControlFlowNode for b [Dictionary element at key b] | +| test.py:521:38:521:38 | ControlFlowNode for b [Dictionary element at key b] | test.py:521:38:521:43 | ControlFlowNode for Subscript | +| test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:521:35:521:35 | ControlFlowNode for b [Dictionary element at key b] | +| test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() | +| test.py:522:39:522:44 | ControlFlowNode for SOURCE | test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | +| test.py:534:9:534:14 | ControlFlowNode for SOURCE | test.py:536:10:536:10 | ControlFlowNode for a | +| test.py:534:9:534:14 | ControlFlowNode for SOURCE | test.py:541:10:541:10 | ControlFlowNode for b | +| test.py:546:10:546:15 | ControlFlowNode for SOURCE | test.py:546:10:546:26 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:546:10:546:26 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:547:5:547:8 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:547:5:547:5 | SSA variable a | test.py:548:10:548:10 | ControlFlowNode for a | +| test.py:547:5:547:8 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:547:5:547:5 | SSA variable a | +| test.py:554:10:554:15 | ControlFlowNode for SOURCE | test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | +| test.py:554:19:554:35 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | +| test.py:554:30:554:35 | ControlFlowNode for SOURCE | test.py:554:19:554:35 | ControlFlowNode for Tuple [Tuple element at index 1] | +| test.py:555:5:555:5 | SSA variable a | test.py:556:10:556:10 | ControlFlowNode for a | +| test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:555:5:555:5 | SSA variable a | +| test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | test.py:555:9:555:12 | IterableSequence [Tuple element at index 1] | +| test.py:555:9:555:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:555:12:555:12 | SSA variable c | +| test.py:555:9:555:12 | IterableSequence [Tuple element at index 1] | test.py:555:9:555:12 | ControlFlowNode for Tuple [Tuple element at index 1] | +| test.py:555:12:555:12 | SSA variable c | test.py:558:10:558:10 | ControlFlowNode for c | +| test.py:563:9:563:33 | ControlFlowNode for List [List element, List element, List element, List element] | test.py:564:5:564:14 | IterableSequence [List element, List element, List element, List element] | +| test.py:563:10:563:21 | ControlFlowNode for List [List element, List element, List element] | test.py:563:9:563:33 | ControlFlowNode for List [List element, List element, List element, List element] | +| test.py:563:11:563:20 | ControlFlowNode for List [List element, List element] | test.py:563:10:563:21 | ControlFlowNode for List [List element, List element, List element] | +| test.py:563:12:563:19 | ControlFlowNode for List [List element] | test.py:563:11:563:20 | ControlFlowNode for List [List element, List element] | +| test.py:563:13:563:18 | ControlFlowNode for SOURCE | test.py:563:12:563:19 | ControlFlowNode for List [List element] | +| test.py:564:5:564:11 | ControlFlowNode for List [Tuple element at index 0, List element, List element] | test.py:564:6:564:10 | IterableSequence [List element, List element] | +| test.py:564:5:564:11 | IterableElement [List element, List element] | test.py:564:5:564:11 | ControlFlowNode for List [Tuple element at index 0, List element, List element] | +| test.py:564:5:564:11 | IterableSequence [List element, List element, List element] | test.py:564:5:564:11 | IterableElement [List element, List element] | +| test.py:564:5:564:14 | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] | test.py:564:5:564:11 | IterableSequence [List element, List element, List element] | +| test.py:564:5:564:14 | IterableElement [List element, List element, List element] | test.py:564:5:564:14 | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] | +| test.py:564:5:564:14 | IterableSequence [List element, List element, List element, List element] | test.py:564:5:564:14 | IterableElement [List element, List element, List element] | +| test.py:564:6:564:10 | ControlFlowNode for List [Tuple element at index 0, List element] | test.py:564:7:564:9 | IterableSequence [List element] | +| test.py:564:6:564:10 | IterableElement [List element] | test.py:564:6:564:10 | ControlFlowNode for List [Tuple element at index 0, List element] | +| test.py:564:6:564:10 | IterableSequence [List element, List element] | test.py:564:6:564:10 | IterableElement [List element] | +| test.py:564:7:564:9 | ControlFlowNode for List [Tuple element at index 0] | test.py:564:8:564:8 | SSA variable a | +| test.py:564:7:564:9 | IterableElement | test.py:564:7:564:9 | ControlFlowNode for List [Tuple element at index 0] | +| test.py:564:7:564:9 | IterableSequence [List element] | test.py:564:7:564:9 | IterableElement | +| test.py:564:8:564:8 | SSA variable a | test.py:565:10:565:10 | ControlFlowNode for a | +| test.py:571:10:571:15 | ControlFlowNode for SOURCE | test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 1] | +| test.py:571:18:571:23 | ControlFlowNode for SOURCE | test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 1] | +| test.py:572:5:572:5 | SSA variable a | test.py:573:10:573:10 | ControlFlowNode for a | +| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:572:5:572:5 | SSA variable a | +| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:572:8:572:9 | IterableElement | +| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:572:12:572:12 | SSA variable c | +| test.py:572:8:572:9 | IterableElement | test.py:572:8:572:9 | SSA variable b [List element] | +| test.py:572:8:572:9 | SSA variable b [List element] | test.py:575:10:575:10 | ControlFlowNode for b [List element] | +| test.py:572:12:572:12 | SSA variable c | test.py:576:12:576:12 | ControlFlowNode for c | +| test.py:575:10:575:10 | ControlFlowNode for b [List element] | test.py:575:10:575:13 | ControlFlowNode for Subscript | +| test.py:581:10:581:15 | ControlFlowNode for SOURCE | test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 1] | +| test.py:581:18:581:23 | ControlFlowNode for SOURCE | test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 1] | +| test.py:582:5:582:5 | SSA variable a | test.py:583:10:583:10 | ControlFlowNode for a | +| test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:582:5:582:5 | SSA variable a | +| test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:582:12:582:12 | SSA variable c | +| test.py:582:12:582:12 | SSA variable c | test.py:585:10:585:10 | ControlFlowNode for c | +| test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | test.py:593:6:593:23 | IterableSequence [List element, List element] | +| test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | test.py:601:5:601:24 | IterableSequence [List element, List element] | +| test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | test.py:609:6:609:23 | IterableSequence [List element, List element] | +| test.py:590:11:590:37 | ControlFlowNode for List [List element] | test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | +| test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:590:11:590:37 | ControlFlowNode for List [List element] | +| test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:590:11:590:37 | ControlFlowNode for List [List element] | +| test.py:590:40:590:47 | ControlFlowNode for List [List element] | test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | +| test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:590:40:590:47 | ControlFlowNode for List [List element] | +| test.py:593:6:593:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | test.py:593:7:593:16 | IterableSequence [List element] | +| test.py:593:6:593:23 | IterableElement [List element] | test.py:593:6:593:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | +| test.py:593:6:593:23 | IterableSequence [List element, List element] | test.py:593:6:593:23 | IterableElement [List element] | +| test.py:593:7:593:8 | SSA variable a1 | test.py:594:10:594:11 | ControlFlowNode for a1 | +| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:593:7:593:8 | SSA variable a1 | +| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:593:11:593:12 | SSA variable a2 | +| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:593:15:593:16 | SSA variable a3 | +| test.py:593:7:593:16 | IterableElement | test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:593:7:593:16 | IterableElement | test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 1] | +| test.py:593:7:593:16 | IterableElement | test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 2] | +| test.py:593:7:593:16 | IterableSequence [List element] | test.py:593:7:593:16 | IterableElement | +| test.py:593:11:593:12 | SSA variable a2 | test.py:595:12:595:13 | ControlFlowNode for a2 | +| test.py:593:15:593:16 | SSA variable a3 | test.py:596:10:596:11 | ControlFlowNode for a3 | +| test.py:601:5:601:24 | ControlFlowNode for List [Tuple element at index 0, List element] | test.py:601:7:601:16 | IterableSequence [List element] | +| test.py:601:5:601:24 | IterableElement [List element] | test.py:601:5:601:24 | ControlFlowNode for List [Tuple element at index 0, List element] | +| test.py:601:5:601:24 | IterableSequence [List element, List element] | test.py:601:5:601:24 | IterableElement [List element] | +| test.py:601:7:601:8 | SSA variable a1 | test.py:602:10:602:11 | ControlFlowNode for a1 | +| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:601:7:601:8 | SSA variable a1 | +| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:601:11:601:12 | SSA variable a2 | +| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:601:15:601:16 | SSA variable a3 | +| test.py:601:7:601:16 | IterableElement | test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:601:7:601:16 | IterableElement | test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 1] | +| test.py:601:7:601:16 | IterableElement | test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 2] | +| test.py:601:7:601:16 | IterableSequence [List element] | test.py:601:7:601:16 | IterableElement | +| test.py:601:11:601:12 | SSA variable a2 | test.py:603:12:603:13 | ControlFlowNode for a2 | +| test.py:601:15:601:16 | SSA variable a3 | test.py:604:10:604:11 | ControlFlowNode for a3 | +| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 0] | test.py:609:7:609:8 | SSA variable a1 | +| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 1] | test.py:609:11:609:12 | SSA variable a2 | +| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 2] | test.py:609:15:609:16 | SSA variable a3 | +| test.py:609:6:609:17 | IterableElement | test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 0] | +| test.py:609:6:609:17 | IterableElement | test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 1] | +| test.py:609:6:609:17 | IterableElement | test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 2] | +| test.py:609:6:609:17 | IterableSequence [List element] | test.py:609:6:609:17 | IterableElement | +| test.py:609:6:609:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | test.py:609:6:609:17 | IterableSequence [List element] | +| test.py:609:6:609:23 | IterableElement [List element] | test.py:609:6:609:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | +| test.py:609:6:609:23 | IterableSequence [List element, List element] | test.py:609:6:609:23 | IterableElement [List element] | | test.py:609:7:609:8 | SSA variable a1 | test.py:610:10:610:11 | ControlFlowNode for a1 | -| test.py:609:11:609:13 | IterableElement | test.py:609:11:609:13 | SSA variable a2 [List element] | -| test.py:609:11:609:13 | SSA variable a2 [List element] | test.py:612:12:612:13 | ControlFlowNode for a2 [List element] | -| test.py:609:11:609:13 | SSA variable a2 [List element] | test.py:613:10:613:11 | ControlFlowNode for a2 [List element] | -| test.py:612:12:612:13 | ControlFlowNode for a2 [List element] | test.py:612:12:612:16 | ControlFlowNode for Subscript | -| test.py:613:10:613:11 | ControlFlowNode for a2 [List element] | test.py:613:10:613:14 | ControlFlowNode for Subscript | -| test.py:618:6:618:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:618:7:618:13 | IterableSequence [Tuple element at index 0] | -| test.py:618:6:618:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:618:7:618:13 | IterableSequence [Tuple element at index 2] | -| test.py:618:7:618:8 | SSA variable a1 | test.py:619:10:619:11 | ControlFlowNode for a1 | -| test.py:618:7:618:13 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:618:7:618:8 | SSA variable a1 | -| test.py:618:7:618:13 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:618:11:618:13 | IterableElement | -| test.py:618:7:618:13 | IterableSequence [Tuple element at index 0] | test.py:618:7:618:13 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:618:7:618:13 | IterableSequence [Tuple element at index 2] | test.py:618:7:618:13 | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:618:11:618:13 | IterableElement | test.py:618:11:618:13 | SSA variable a2 [List element] | -| test.py:618:11:618:13 | SSA variable a2 [List element] | test.py:621:12:621:13 | ControlFlowNode for a2 [List element] | -| test.py:618:11:618:13 | SSA variable a2 [List element] | test.py:622:10:622:11 | ControlFlowNode for a2 [List element] | -| test.py:621:12:621:13 | ControlFlowNode for a2 [List element] | test.py:621:12:621:16 | ControlFlowNode for Subscript | -| test.py:622:10:622:11 | ControlFlowNode for a2 [List element] | test.py:622:10:622:14 | ControlFlowNode for Subscript | -| test.py:627:5:627:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | test.py:627:7:627:13 | IterableSequence [Tuple element at index 0] | -| test.py:627:5:627:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | test.py:627:7:627:13 | IterableSequence [Tuple element at index 2] | -| test.py:627:7:627:8 | SSA variable a1 | test.py:628:10:628:11 | ControlFlowNode for a1 | -| test.py:627:7:627:13 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:627:7:627:8 | SSA variable a1 | -| test.py:627:7:627:13 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:627:11:627:13 | IterableElement | -| test.py:627:7:627:13 | IterableSequence [Tuple element at index 0] | test.py:627:7:627:13 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:627:7:627:13 | IterableSequence [Tuple element at index 2] | test.py:627:7:627:13 | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:627:11:627:13 | IterableElement | test.py:627:11:627:13 | SSA variable a2 [List element] | -| test.py:627:11:627:13 | SSA variable a2 [List element] | test.py:630:12:630:13 | ControlFlowNode for a2 [List element] | -| test.py:627:11:627:13 | SSA variable a2 [List element] | test.py:631:10:631:11 | ControlFlowNode for a2 [List element] | -| test.py:630:12:630:13 | ControlFlowNode for a2 [List element] | test.py:630:12:630:16 | ControlFlowNode for Subscript | -| test.py:631:10:631:11 | ControlFlowNode for a2 [List element] | test.py:631:10:631:14 | ControlFlowNode for Subscript | -| test.py:636:6:636:14 | ControlFlowNode for List [Tuple element at index 0] | test.py:636:7:636:8 | SSA variable a1 | -| test.py:636:6:636:14 | ControlFlowNode for List [Tuple element at index 2] | test.py:636:11:636:13 | IterableElement | -| test.py:636:6:636:14 | IterableSequence [Tuple element at index 0] | test.py:636:6:636:14 | ControlFlowNode for List [Tuple element at index 0] | -| test.py:636:6:636:14 | IterableSequence [Tuple element at index 2] | test.py:636:6:636:14 | ControlFlowNode for List [Tuple element at index 2] | -| test.py:636:6:636:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:636:6:636:14 | IterableSequence [Tuple element at index 0] | -| test.py:636:6:636:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:636:6:636:14 | IterableSequence [Tuple element at index 2] | -| test.py:636:7:636:8 | SSA variable a1 | test.py:637:10:637:11 | ControlFlowNode for a1 | -| test.py:636:11:636:13 | IterableElement | test.py:636:11:636:13 | SSA variable a2 [List element] | -| test.py:636:11:636:13 | SSA variable a2 [List element] | test.py:639:12:639:13 | ControlFlowNode for a2 [List element] | -| test.py:636:11:636:13 | SSA variable a2 [List element] | test.py:640:10:640:11 | ControlFlowNode for a2 [List element] | -| test.py:639:12:639:13 | ControlFlowNode for a2 [List element] | test.py:639:12:639:16 | ControlFlowNode for Subscript | -| test.py:640:10:640:11 | ControlFlowNode for a2 [List element] | test.py:640:10:640:14 | ControlFlowNode for Subscript | -| test.py:647:19:647:24 | ControlFlowNode for SOURCE | test.py:648:10:648:10 | ControlFlowNode for a | -| test.py:655:10:655:51 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:656:16:656:17 | ControlFlowNode for tl [List element, Tuple element at index 0] | -| test.py:655:12:655:17 | ControlFlowNode for SOURCE | test.py:655:12:655:28 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:655:12:655:28 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:655:10:655:51 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:655:33:655:38 | ControlFlowNode for SOURCE | test.py:655:33:655:49 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:655:33:655:49 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:655:10:655:51 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:656:9:656:9 | SSA variable x | test.py:657:14:657:14 | ControlFlowNode for x | -| test.py:656:9:656:11 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:656:9:656:9 | SSA variable x | -| test.py:656:9:656:11 | IterableSequence [Tuple element at index 0] | test.py:656:9:656:11 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:656:16:656:17 | ControlFlowNode for tl [List element, Tuple element at index 0] | test.py:656:9:656:11 | IterableSequence [Tuple element at index 0] | -| test.py:663:10:663:51 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:664:17:664:18 | ControlFlowNode for tl [List element, Tuple element at index 0] | -| test.py:663:12:663:17 | ControlFlowNode for SOURCE | test.py:663:12:663:28 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:663:12:663:28 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:663:10:663:51 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:663:33:663:38 | ControlFlowNode for SOURCE | test.py:663:33:663:49 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:663:33:663:49 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:663:10:663:51 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:664:9:664:10 | IterableElement | test.py:664:9:664:10 | SSA variable x [List element] | -| test.py:664:9:664:10 | SSA variable x [List element] | test.py:666:14:666:14 | ControlFlowNode for x [List element] | -| test.py:664:9:664:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:664:9:664:10 | IterableElement | -| test.py:664:9:664:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:664:12:664:12 | SSA variable y | -| test.py:664:9:664:12 | IterableSequence [Tuple element at index 0] | test.py:664:9:664:12 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:664:12:664:12 | SSA variable y | test.py:667:16:667:16 | ControlFlowNode for y | -| test.py:664:17:664:18 | ControlFlowNode for tl [List element, Tuple element at index 0] | test.py:664:9:664:12 | IterableSequence [Tuple element at index 0] | -| test.py:666:14:666:14 | ControlFlowNode for x [List element] | test.py:666:14:666:17 | ControlFlowNode for Subscript | -| test.py:672:10:672:51 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:673:19:673:20 | ControlFlowNode for tl [List element, Tuple element at index 0] | -| test.py:672:12:672:17 | ControlFlowNode for SOURCE | test.py:672:12:672:28 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:672:12:672:28 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:672:10:672:51 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:672:33:672:38 | ControlFlowNode for SOURCE | test.py:672:33:672:49 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:672:33:672:49 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:672:10:672:51 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:673:9:673:9 | SSA variable x | test.py:674:14:674:14 | ControlFlowNode for x | -| test.py:673:9:673:14 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:673:9:673:9 | SSA variable x | -| test.py:673:9:673:14 | IterableSequence [Tuple element at index 0] | test.py:673:9:673:14 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:673:19:673:20 | ControlFlowNode for tl [List element, Tuple element at index 0] | test.py:673:9:673:14 | IterableSequence [Tuple element at index 0] | -| test.py:678:39:678:42 | ControlFlowNode for args [Tuple element at index 0] | test.py:679:14:679:17 | ControlFlowNode for args [Tuple element at index 0] | -| test.py:678:39:678:42 | ControlFlowNode for args [Tuple element at index 1] | test.py:679:14:679:17 | ControlFlowNode for args [Tuple element at index 1] | -| test.py:679:7:679:9 | SSA variable arg | test.py:680:10:680:12 | ControlFlowNode for arg | -| test.py:679:14:679:17 | ControlFlowNode for args [Tuple element at index 0] | test.py:679:7:679:9 | SSA variable arg | -| test.py:679:14:679:17 | ControlFlowNode for args [Tuple element at index 1] | test.py:679:7:679:9 | SSA variable arg | -| test.py:685:7:685:12 | ControlFlowNode for SOURCE | test.py:686:51:686:51 | ControlFlowNode for s | -| test.py:686:3:686:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 0] | test.py:678:39:678:42 | ControlFlowNode for args [Tuple element at index 0] | -| test.py:686:3:686:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 1] | test.py:678:39:678:42 | ControlFlowNode for args [Tuple element at index 1] | -| test.py:686:43:686:48 | ControlFlowNode for SOURCE | test.py:686:3:686:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 0] | -| test.py:686:51:686:51 | ControlFlowNode for s | test.py:686:3:686:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 1] | -| test.py:757:16:757:21 | ControlFlowNode for SOURCE | test.py:760:10:760:36 | ControlFlowNode for return_from_inner_scope() | -| test.py:795:35:795:35 | ControlFlowNode for x | test.py:796:10:796:10 | ControlFlowNode for x | -| test.py:795:37:795:42 | ControlFlowNode for SOURCE | test.py:795:35:795:35 | ControlFlowNode for x | -| test.py:795:48:795:48 | ControlFlowNode for y | test.py:797:10:797:10 | ControlFlowNode for y | -| test.py:795:50:795:55 | ControlFlowNode for SOURCE | test.py:795:48:795:48 | ControlFlowNode for y | -| test.py:795:61:795:61 | ControlFlowNode for z | test.py:798:10:798:10 | ControlFlowNode for z | -| test.py:795:63:795:68 | ControlFlowNode for SOURCE | test.py:795:61:795:61 | ControlFlowNode for z | +| test.py:609:11:609:12 | SSA variable a2 | test.py:611:12:611:13 | ControlFlowNode for a2 | +| test.py:609:15:609:16 | SSA variable a3 | test.py:612:10:612:11 | ControlFlowNode for a3 | +| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | +| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | +| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | +| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | +| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | +| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | +| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | +| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | +| test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | +| test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | +| test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 2] | +| test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | test.py:621:6:621:14 | IterableSequence [Tuple element at index 0] | +| test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | test.py:621:6:621:14 | IterableSequence [Tuple element at index 2] | +| test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 0] | test.py:621:7:621:8 | SSA variable a1 | +| test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 2] | test.py:621:11:621:13 | IterableElement | +| test.py:621:6:621:14 | IterableSequence [Tuple element at index 0] | test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 0] | +| test.py:621:6:621:14 | IterableSequence [Tuple element at index 2] | test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 2] | +| test.py:621:7:621:8 | SSA variable a1 | test.py:622:10:622:11 | ControlFlowNode for a1 | +| test.py:621:11:621:13 | IterableElement | test.py:621:11:621:13 | SSA variable a2 [List element] | +| test.py:621:11:621:13 | SSA variable a2 [List element] | test.py:624:12:624:13 | ControlFlowNode for a2 [List element] | +| test.py:621:11:621:13 | SSA variable a2 [List element] | test.py:625:10:625:11 | ControlFlowNode for a2 [List element] | +| test.py:624:12:624:13 | ControlFlowNode for a2 [List element] | test.py:624:12:624:16 | ControlFlowNode for Subscript | +| test.py:625:10:625:11 | ControlFlowNode for a2 [List element] | test.py:625:10:625:14 | ControlFlowNode for Subscript | +| test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:630:7:630:13 | IterableSequence [Tuple element at index 0] | +| test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:630:7:630:13 | IterableSequence [Tuple element at index 2] | +| test.py:630:7:630:8 | SSA variable a1 | test.py:631:10:631:11 | ControlFlowNode for a1 | +| test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:630:7:630:8 | SSA variable a1 | +| test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:630:11:630:13 | IterableElement | +| test.py:630:7:630:13 | IterableSequence [Tuple element at index 0] | test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:630:7:630:13 | IterableSequence [Tuple element at index 2] | test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 2] | +| test.py:630:11:630:13 | IterableElement | test.py:630:11:630:13 | SSA variable a2 [List element] | +| test.py:630:11:630:13 | SSA variable a2 [List element] | test.py:633:12:633:13 | ControlFlowNode for a2 [List element] | +| test.py:630:11:630:13 | SSA variable a2 [List element] | test.py:634:10:634:11 | ControlFlowNode for a2 [List element] | +| test.py:633:12:633:13 | ControlFlowNode for a2 [List element] | test.py:633:12:633:16 | ControlFlowNode for Subscript | +| test.py:634:10:634:11 | ControlFlowNode for a2 [List element] | test.py:634:10:634:14 | ControlFlowNode for Subscript | +| test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | test.py:639:7:639:13 | IterableSequence [Tuple element at index 0] | +| test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | test.py:639:7:639:13 | IterableSequence [Tuple element at index 2] | +| test.py:639:7:639:8 | SSA variable a1 | test.py:640:10:640:11 | ControlFlowNode for a1 | +| test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:639:7:639:8 | SSA variable a1 | +| test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:639:11:639:13 | IterableElement | +| test.py:639:7:639:13 | IterableSequence [Tuple element at index 0] | test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:639:7:639:13 | IterableSequence [Tuple element at index 2] | test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 2] | +| test.py:639:11:639:13 | IterableElement | test.py:639:11:639:13 | SSA variable a2 [List element] | +| test.py:639:11:639:13 | SSA variable a2 [List element] | test.py:642:12:642:13 | ControlFlowNode for a2 [List element] | +| test.py:639:11:639:13 | SSA variable a2 [List element] | test.py:643:10:643:11 | ControlFlowNode for a2 [List element] | +| test.py:642:12:642:13 | ControlFlowNode for a2 [List element] | test.py:642:12:642:16 | ControlFlowNode for Subscript | +| test.py:643:10:643:11 | ControlFlowNode for a2 [List element] | test.py:643:10:643:14 | ControlFlowNode for Subscript | +| test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 0] | test.py:648:7:648:8 | SSA variable a1 | +| test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 2] | test.py:648:11:648:13 | IterableElement | +| test.py:648:6:648:14 | IterableSequence [Tuple element at index 0] | test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 0] | +| test.py:648:6:648:14 | IterableSequence [Tuple element at index 2] | test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 2] | +| test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:648:6:648:14 | IterableSequence [Tuple element at index 0] | +| test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:648:6:648:14 | IterableSequence [Tuple element at index 2] | +| test.py:648:7:648:8 | SSA variable a1 | test.py:649:10:649:11 | ControlFlowNode for a1 | +| test.py:648:11:648:13 | IterableElement | test.py:648:11:648:13 | SSA variable a2 [List element] | +| test.py:648:11:648:13 | SSA variable a2 [List element] | test.py:651:12:651:13 | ControlFlowNode for a2 [List element] | +| test.py:648:11:648:13 | SSA variable a2 [List element] | test.py:652:10:652:11 | ControlFlowNode for a2 [List element] | +| test.py:651:12:651:13 | ControlFlowNode for a2 [List element] | test.py:651:12:651:16 | ControlFlowNode for Subscript | +| test.py:652:10:652:11 | ControlFlowNode for a2 [List element] | test.py:652:10:652:14 | ControlFlowNode for Subscript | +| test.py:659:19:659:24 | ControlFlowNode for SOURCE | test.py:660:10:660:10 | ControlFlowNode for a | +| test.py:667:10:667:51 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:668:16:668:17 | ControlFlowNode for tl [List element, Tuple element at index 0] | +| test.py:667:12:667:17 | ControlFlowNode for SOURCE | test.py:667:12:667:28 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:667:12:667:28 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:667:10:667:51 | ControlFlowNode for List [List element, Tuple element at index 0] | +| test.py:667:33:667:38 | ControlFlowNode for SOURCE | test.py:667:33:667:49 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:667:33:667:49 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:667:10:667:51 | ControlFlowNode for List [List element, Tuple element at index 0] | +| test.py:668:9:668:9 | SSA variable x | test.py:669:14:669:14 | ControlFlowNode for x | +| test.py:668:9:668:11 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:668:9:668:9 | SSA variable x | +| test.py:668:9:668:11 | IterableSequence [Tuple element at index 0] | test.py:668:9:668:11 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:668:16:668:17 | ControlFlowNode for tl [List element, Tuple element at index 0] | test.py:668:9:668:11 | IterableSequence [Tuple element at index 0] | +| test.py:675:10:675:51 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:676:17:676:18 | ControlFlowNode for tl [List element, Tuple element at index 0] | +| test.py:675:12:675:17 | ControlFlowNode for SOURCE | test.py:675:12:675:28 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:675:12:675:28 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:675:10:675:51 | ControlFlowNode for List [List element, Tuple element at index 0] | +| test.py:675:33:675:38 | ControlFlowNode for SOURCE | test.py:675:33:675:49 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:675:33:675:49 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:675:10:675:51 | ControlFlowNode for List [List element, Tuple element at index 0] | +| test.py:676:9:676:10 | IterableElement | test.py:676:9:676:10 | SSA variable x [List element] | +| test.py:676:9:676:10 | SSA variable x [List element] | test.py:678:14:678:14 | ControlFlowNode for x [List element] | +| test.py:676:9:676:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:676:9:676:10 | IterableElement | +| test.py:676:9:676:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:676:12:676:12 | SSA variable y | +| test.py:676:9:676:12 | IterableSequence [Tuple element at index 0] | test.py:676:9:676:12 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:676:12:676:12 | SSA variable y | test.py:679:16:679:16 | ControlFlowNode for y | +| test.py:676:17:676:18 | ControlFlowNode for tl [List element, Tuple element at index 0] | test.py:676:9:676:12 | IterableSequence [Tuple element at index 0] | +| test.py:678:14:678:14 | ControlFlowNode for x [List element] | test.py:678:14:678:17 | ControlFlowNode for Subscript | +| test.py:684:10:684:51 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:685:19:685:20 | ControlFlowNode for tl [List element, Tuple element at index 0] | +| test.py:684:12:684:17 | ControlFlowNode for SOURCE | test.py:684:12:684:28 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:684:12:684:28 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:684:10:684:51 | ControlFlowNode for List [List element, Tuple element at index 0] | +| test.py:684:33:684:38 | ControlFlowNode for SOURCE | test.py:684:33:684:49 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:684:33:684:49 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:684:10:684:51 | ControlFlowNode for List [List element, Tuple element at index 0] | +| test.py:685:9:685:9 | SSA variable x | test.py:686:14:686:14 | ControlFlowNode for x | +| test.py:685:9:685:14 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:685:9:685:9 | SSA variable x | +| test.py:685:9:685:14 | IterableSequence [Tuple element at index 0] | test.py:685:9:685:14 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:685:19:685:20 | ControlFlowNode for tl [List element, Tuple element at index 0] | test.py:685:9:685:14 | IterableSequence [Tuple element at index 0] | +| test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 0] | test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 0] | +| test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 1] | test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 1] | +| test.py:691:7:691:9 | SSA variable arg | test.py:692:10:692:12 | ControlFlowNode for arg | +| test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 0] | test.py:691:7:691:9 | SSA variable arg | +| test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 1] | test.py:691:7:691:9 | SSA variable arg | +| test.py:697:7:697:12 | ControlFlowNode for SOURCE | test.py:698:51:698:51 | ControlFlowNode for s | +| test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 0] | test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 0] | +| test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 1] | test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 1] | +| test.py:698:43:698:48 | ControlFlowNode for SOURCE | test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 0] | +| test.py:698:51:698:51 | ControlFlowNode for s | test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 1] | +| test.py:769:16:769:21 | ControlFlowNode for SOURCE | test.py:772:10:772:36 | ControlFlowNode for return_from_inner_scope() | +| test.py:807:35:807:35 | ControlFlowNode for x | test.py:808:10:808:10 | ControlFlowNode for x | +| test.py:807:37:807:42 | ControlFlowNode for SOURCE | test.py:807:35:807:35 | ControlFlowNode for x | +| test.py:807:48:807:48 | ControlFlowNode for y | test.py:809:10:809:10 | ControlFlowNode for y | +| test.py:807:50:807:55 | ControlFlowNode for SOURCE | test.py:807:48:807:48 | ControlFlowNode for y | +| test.py:807:61:807:61 | ControlFlowNode for z | test.py:810:10:810:10 | ControlFlowNode for z | +| test.py:807:63:807:68 | ControlFlowNode for SOURCE | test.py:807:61:807:61 | ControlFlowNode for z | nodes | datamodel.py:35:7:35:7 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | | datamodel.py:36:10:36:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | @@ -567,272 +569,276 @@ nodes | test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() | | test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | semmle.label | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | | test.py:412:39:412:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:433:10:433:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:433:10:433:38 | ControlFlowNode for IfExp | semmle.label | ControlFlowNode for IfExp | -| test.py:441:10:441:39 | ControlFlowNode for IfExp | semmle.label | ControlFlowNode for IfExp | -| test.py:441:34:441:39 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:462:11:462:11 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:463:16:463:16 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:465:10:465:18 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() | -| test.py:465:12:465:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:469:19:469:19 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:470:16:470:16 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:472:10:472:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | -| test.py:472:28:472:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:483:19:483:19 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:484:16:484:16 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:486:10:486:36 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | -| test.py:486:30:486:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:497:19:497:19 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:498:16:498:16 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:500:10:500:43 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | -| test.py:500:10:500:43 | KwUnpacked b | semmle.label | KwUnpacked b | -| test.py:500:30:500:42 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] | -| test.py:500:36:500:41 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:504:30:504:30 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] | -| test.py:504:33:504:33 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] | -| test.py:504:33:504:36 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:505:10:505:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() | -| test.py:505:10:505:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | semmle.label | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | -| test.py:505:33:505:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:509:35:509:35 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] | -| test.py:509:38:509:38 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] | -| test.py:509:38:509:43 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:510:10:510:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() | -| test.py:510:10:510:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | semmle.label | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | -| test.py:510:39:510:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:522:9:522:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:524:10:524:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:529:10:529:10 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:534:10:534:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:534:10:534:26 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:535:5:535:5 | SSA variable a | semmle.label | SSA variable a | -| test.py:535:5:535:8 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:429:10:429:20 | ControlFlowNode for BoolExpr | semmle.label | ControlFlowNode for BoolExpr | +| test.py:429:15:429:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:434:10:434:21 | ControlFlowNode for BoolExpr | semmle.label | ControlFlowNode for BoolExpr | +| test.py:434:16:434:21 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:445:10:445:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:445:10:445:38 | ControlFlowNode for IfExp | semmle.label | ControlFlowNode for IfExp | +| test.py:453:10:453:39 | ControlFlowNode for IfExp | semmle.label | ControlFlowNode for IfExp | +| test.py:453:34:453:39 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:474:11:474:11 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:475:16:475:16 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:477:10:477:18 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() | +| test.py:477:12:477:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:481:19:481:19 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | +| test.py:482:16:482:16 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | +| test.py:484:10:484:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | +| test.py:484:28:484:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:495:19:495:19 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | +| test.py:496:16:496:16 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | +| test.py:498:10:498:36 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | +| test.py:498:30:498:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:509:19:509:19 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | +| test.py:510:16:510:16 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | +| test.py:512:10:512:43 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | +| test.py:512:10:512:43 | KwUnpacked b | semmle.label | KwUnpacked b | +| test.py:512:30:512:42 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] | +| test.py:512:36:512:41 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:516:30:516:30 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] | +| test.py:516:33:516:33 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] | +| test.py:516:33:516:36 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() | +| test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | semmle.label | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | +| test.py:517:33:517:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:521:35:521:35 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] | +| test.py:521:38:521:38 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] | +| test.py:521:38:521:43 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() | +| test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | semmle.label | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | +| test.py:522:39:522:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:534:9:534:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:536:10:536:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:542:10:542:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:542:10:542:36 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:542:10:542:36 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | -| test.py:542:19:542:35 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:542:30:542:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:543:5:543:5 | SSA variable a | semmle.label | SSA variable a | -| test.py:543:5:543:13 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:543:5:543:13 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | -| test.py:543:9:543:12 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:543:9:543:12 | IterableSequence [Tuple element at index 1] | semmle.label | IterableSequence [Tuple element at index 1] | -| test.py:543:12:543:12 | SSA variable c | semmle.label | SSA variable c | -| test.py:544:10:544:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:546:10:546:10 | ControlFlowNode for c | semmle.label | ControlFlowNode for c | -| test.py:551:9:551:33 | ControlFlowNode for List [List element, List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element, List element] | -| test.py:551:10:551:21 | ControlFlowNode for List [List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element] | -| test.py:551:11:551:20 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] | -| test.py:551:12:551:19 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:551:13:551:18 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:552:5:552:11 | ControlFlowNode for List [Tuple element at index 0, List element, List element] | semmle.label | ControlFlowNode for List [Tuple element at index 0, List element, List element] | -| test.py:552:5:552:11 | IterableElement [List element, List element] | semmle.label | IterableElement [List element, List element] | -| test.py:552:5:552:11 | IterableSequence [List element, List element, List element] | semmle.label | IterableSequence [List element, List element, List element] | -| test.py:552:5:552:14 | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] | -| test.py:552:5:552:14 | IterableElement [List element, List element, List element] | semmle.label | IterableElement [List element, List element, List element] | -| test.py:552:5:552:14 | IterableSequence [List element, List element, List element, List element] | semmle.label | IterableSequence [List element, List element, List element, List element] | -| test.py:552:6:552:10 | ControlFlowNode for List [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for List [Tuple element at index 0, List element] | -| test.py:552:6:552:10 | IterableElement [List element] | semmle.label | IterableElement [List element] | -| test.py:552:6:552:10 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] | -| test.py:552:7:552:9 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] | -| test.py:552:7:552:9 | IterableElement | semmle.label | IterableElement | -| test.py:552:7:552:9 | IterableSequence [List element] | semmle.label | IterableSequence [List element] | -| test.py:552:8:552:8 | SSA variable a | semmle.label | SSA variable a | -| test.py:553:10:553:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:559:10:559:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:559:10:559:34 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:559:10:559:34 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:559:18:559:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:560:5:560:5 | SSA variable a | semmle.label | SSA variable a | -| test.py:560:5:560:12 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:560:5:560:12 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:560:8:560:9 | IterableElement | semmle.label | IterableElement | -| test.py:560:8:560:9 | SSA variable b [List element] | semmle.label | SSA variable b [List element] | -| test.py:560:12:560:12 | SSA variable c | semmle.label | SSA variable c | -| test.py:561:10:561:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:563:10:563:10 | ControlFlowNode for b [List element] | semmle.label | ControlFlowNode for b [List element] | -| test.py:563:10:563:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:564:12:564:12 | ControlFlowNode for c | semmle.label | ControlFlowNode for c | -| test.py:569:10:569:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:569:10:569:23 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:569:10:569:23 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:569:18:569:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:570:5:570:5 | SSA variable a | semmle.label | SSA variable a | -| test.py:570:5:570:12 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:570:5:570:12 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:570:12:570:12 | SSA variable c | semmle.label | SSA variable c | -| test.py:571:10:571:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:573:10:573:10 | ControlFlowNode for c | semmle.label | ControlFlowNode for c | -| test.py:578:10:578:61 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] | -| test.py:578:11:578:37 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:578:12:578:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:578:31:578:36 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:578:40:578:47 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:578:41:578:46 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:581:6:581:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, List element] | -| test.py:581:6:581:23 | IterableElement [List element] | semmle.label | IterableElement [List element] | -| test.py:581:6:581:23 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] | -| test.py:581:7:581:8 | SSA variable a1 | semmle.label | SSA variable a1 | -| test.py:581:7:581:16 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:581:7:581:16 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:581:7:581:16 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:581:7:581:16 | IterableElement | semmle.label | IterableElement | -| test.py:581:7:581:16 | IterableSequence [List element] | semmle.label | IterableSequence [List element] | -| test.py:581:11:581:12 | SSA variable a2 | semmle.label | SSA variable a2 | -| test.py:581:15:581:16 | SSA variable a3 | semmle.label | SSA variable a3 | -| test.py:582:10:582:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | -| test.py:583:12:583:13 | ControlFlowNode for a2 | semmle.label | ControlFlowNode for a2 | -| test.py:584:10:584:11 | ControlFlowNode for a3 | semmle.label | ControlFlowNode for a3 | -| test.py:589:5:589:24 | ControlFlowNode for List [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for List [Tuple element at index 0, List element] | -| test.py:589:5:589:24 | IterableElement [List element] | semmle.label | IterableElement [List element] | -| test.py:589:5:589:24 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] | -| test.py:589:7:589:8 | SSA variable a1 | semmle.label | SSA variable a1 | -| test.py:589:7:589:16 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:589:7:589:16 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:589:7:589:16 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:589:7:589:16 | IterableElement | semmle.label | IterableElement | -| test.py:589:7:589:16 | IterableSequence [List element] | semmle.label | IterableSequence [List element] | -| test.py:589:11:589:12 | SSA variable a2 | semmle.label | SSA variable a2 | -| test.py:589:15:589:16 | SSA variable a3 | semmle.label | SSA variable a3 | -| test.py:590:10:590:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | -| test.py:591:12:591:13 | ControlFlowNode for a2 | semmle.label | ControlFlowNode for a2 | -| test.py:592:10:592:11 | ControlFlowNode for a3 | semmle.label | ControlFlowNode for a3 | -| test.py:597:6:597:17 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] | -| test.py:597:6:597:17 | ControlFlowNode for List [Tuple element at index 1] | semmle.label | ControlFlowNode for List [Tuple element at index 1] | -| test.py:597:6:597:17 | ControlFlowNode for List [Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 2] | -| test.py:597:6:597:17 | IterableElement | semmle.label | IterableElement | -| test.py:597:6:597:17 | IterableSequence [List element] | semmle.label | IterableSequence [List element] | -| test.py:597:6:597:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, List element] | -| test.py:597:6:597:23 | IterableElement [List element] | semmle.label | IterableElement [List element] | -| test.py:597:6:597:23 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] | -| test.py:597:7:597:8 | SSA variable a1 | semmle.label | SSA variable a1 | -| test.py:597:11:597:12 | SSA variable a2 | semmle.label | SSA variable a2 | -| test.py:597:15:597:16 | SSA variable a3 | semmle.label | SSA variable a3 | -| test.py:598:10:598:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | -| test.py:599:12:599:13 | ControlFlowNode for a2 | semmle.label | ControlFlowNode for a2 | -| test.py:600:10:600:11 | ControlFlowNode for a3 | semmle.label | ControlFlowNode for a3 | -| test.py:606:11:606:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | -| test.py:606:11:606:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | -| test.py:606:12:606:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:606:12:606:36 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:606:12:606:36 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:606:31:606:36 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:609:5:609:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | -| test.py:609:5:609:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | -| test.py:609:6:609:14 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] | -| test.py:609:6:609:14 | ControlFlowNode for List [Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 2] | -| test.py:609:6:609:14 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:609:6:609:14 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] | +| test.py:541:10:541:10 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | +| test.py:546:10:546:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:546:10:546:26 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:547:5:547:5 | SSA variable a | semmle.label | SSA variable a | +| test.py:547:5:547:8 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:548:10:548:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | +| test.py:554:10:554:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | +| test.py:554:19:554:35 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | +| test.py:554:30:554:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:555:5:555:5 | SSA variable a | semmle.label | SSA variable a | +| test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | +| test.py:555:9:555:12 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | +| test.py:555:9:555:12 | IterableSequence [Tuple element at index 1] | semmle.label | IterableSequence [Tuple element at index 1] | +| test.py:555:12:555:12 | SSA variable c | semmle.label | SSA variable c | +| test.py:556:10:556:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | +| test.py:558:10:558:10 | ControlFlowNode for c | semmle.label | ControlFlowNode for c | +| test.py:563:9:563:33 | ControlFlowNode for List [List element, List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element, List element] | +| test.py:563:10:563:21 | ControlFlowNode for List [List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element] | +| test.py:563:11:563:20 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] | +| test.py:563:12:563:19 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | +| test.py:563:13:563:18 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:564:5:564:11 | ControlFlowNode for List [Tuple element at index 0, List element, List element] | semmle.label | ControlFlowNode for List [Tuple element at index 0, List element, List element] | +| test.py:564:5:564:11 | IterableElement [List element, List element] | semmle.label | IterableElement [List element, List element] | +| test.py:564:5:564:11 | IterableSequence [List element, List element, List element] | semmle.label | IterableSequence [List element, List element, List element] | +| test.py:564:5:564:14 | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] | +| test.py:564:5:564:14 | IterableElement [List element, List element, List element] | semmle.label | IterableElement [List element, List element, List element] | +| test.py:564:5:564:14 | IterableSequence [List element, List element, List element, List element] | semmle.label | IterableSequence [List element, List element, List element, List element] | +| test.py:564:6:564:10 | ControlFlowNode for List [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for List [Tuple element at index 0, List element] | +| test.py:564:6:564:10 | IterableElement [List element] | semmle.label | IterableElement [List element] | +| test.py:564:6:564:10 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] | +| test.py:564:7:564:9 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] | +| test.py:564:7:564:9 | IterableElement | semmle.label | IterableElement | +| test.py:564:7:564:9 | IterableSequence [List element] | semmle.label | IterableSequence [List element] | +| test.py:564:8:564:8 | SSA variable a | semmle.label | SSA variable a | +| test.py:565:10:565:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | +| test.py:571:10:571:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | +| test.py:571:18:571:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:572:5:572:5 | SSA variable a | semmle.label | SSA variable a | +| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | +| test.py:572:8:572:9 | IterableElement | semmle.label | IterableElement | +| test.py:572:8:572:9 | SSA variable b [List element] | semmle.label | SSA variable b [List element] | +| test.py:572:12:572:12 | SSA variable c | semmle.label | SSA variable c | +| test.py:573:10:573:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | +| test.py:575:10:575:10 | ControlFlowNode for b [List element] | semmle.label | ControlFlowNode for b [List element] | +| test.py:575:10:575:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:576:12:576:12 | ControlFlowNode for c | semmle.label | ControlFlowNode for c | +| test.py:581:10:581:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | +| test.py:581:18:581:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:582:5:582:5 | SSA variable a | semmle.label | SSA variable a | +| test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | +| test.py:582:12:582:12 | SSA variable c | semmle.label | SSA variable c | +| test.py:583:10:583:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | +| test.py:585:10:585:10 | ControlFlowNode for c | semmle.label | ControlFlowNode for c | +| test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] | +| test.py:590:11:590:37 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | +| test.py:590:12:590:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:590:31:590:36 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:590:40:590:47 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | +| test.py:590:41:590:46 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:593:6:593:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, List element] | +| test.py:593:6:593:23 | IterableElement [List element] | semmle.label | IterableElement [List element] | +| test.py:593:6:593:23 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] | +| test.py:593:7:593:8 | SSA variable a1 | semmle.label | SSA variable a1 | +| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | +| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] | +| test.py:593:7:593:16 | IterableElement | semmle.label | IterableElement | +| test.py:593:7:593:16 | IterableSequence [List element] | semmle.label | IterableSequence [List element] | +| test.py:593:11:593:12 | SSA variable a2 | semmle.label | SSA variable a2 | +| test.py:593:15:593:16 | SSA variable a3 | semmle.label | SSA variable a3 | +| test.py:594:10:594:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | +| test.py:595:12:595:13 | ControlFlowNode for a2 | semmle.label | ControlFlowNode for a2 | +| test.py:596:10:596:11 | ControlFlowNode for a3 | semmle.label | ControlFlowNode for a3 | +| test.py:601:5:601:24 | ControlFlowNode for List [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for List [Tuple element at index 0, List element] | +| test.py:601:5:601:24 | IterableElement [List element] | semmle.label | IterableElement [List element] | +| test.py:601:5:601:24 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] | +| test.py:601:7:601:8 | SSA variable a1 | semmle.label | SSA variable a1 | +| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | +| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] | +| test.py:601:7:601:16 | IterableElement | semmle.label | IterableElement | +| test.py:601:7:601:16 | IterableSequence [List element] | semmle.label | IterableSequence [List element] | +| test.py:601:11:601:12 | SSA variable a2 | semmle.label | SSA variable a2 | +| test.py:601:15:601:16 | SSA variable a3 | semmle.label | SSA variable a3 | +| test.py:602:10:602:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | +| test.py:603:12:603:13 | ControlFlowNode for a2 | semmle.label | ControlFlowNode for a2 | +| test.py:604:10:604:11 | ControlFlowNode for a3 | semmle.label | ControlFlowNode for a3 | +| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] | +| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 1] | semmle.label | ControlFlowNode for List [Tuple element at index 1] | +| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 2] | +| test.py:609:6:609:17 | IterableElement | semmle.label | IterableElement | +| test.py:609:6:609:17 | IterableSequence [List element] | semmle.label | IterableSequence [List element] | +| test.py:609:6:609:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, List element] | +| test.py:609:6:609:23 | IterableElement [List element] | semmle.label | IterableElement [List element] | +| test.py:609:6:609:23 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] | | test.py:609:7:609:8 | SSA variable a1 | semmle.label | SSA variable a1 | -| test.py:609:11:609:13 | IterableElement | semmle.label | IterableElement | -| test.py:609:11:609:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] | +| test.py:609:11:609:12 | SSA variable a2 | semmle.label | SSA variable a2 | +| test.py:609:15:609:16 | SSA variable a3 | semmle.label | SSA variable a3 | | test.py:610:10:610:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | -| test.py:612:12:612:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:612:12:612:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:613:10:613:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:613:10:613:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:618:6:618:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | -| test.py:618:6:618:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | -| test.py:618:7:618:8 | SSA variable a1 | semmle.label | SSA variable a1 | -| test.py:618:7:618:13 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:618:7:618:13 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:618:7:618:13 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:618:7:618:13 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] | -| test.py:618:11:618:13 | IterableElement | semmle.label | IterableElement | -| test.py:618:11:618:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] | -| test.py:619:10:619:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | -| test.py:621:12:621:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:621:12:621:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:622:10:622:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:622:10:622:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:627:5:627:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | -| test.py:627:5:627:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | -| test.py:627:7:627:8 | SSA variable a1 | semmle.label | SSA variable a1 | -| test.py:627:7:627:13 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:627:7:627:13 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:627:7:627:13 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:627:7:627:13 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] | -| test.py:627:11:627:13 | IterableElement | semmle.label | IterableElement | -| test.py:627:11:627:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] | -| test.py:628:10:628:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | -| test.py:630:12:630:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:630:12:630:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:631:10:631:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:631:10:631:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:636:6:636:14 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] | -| test.py:636:6:636:14 | ControlFlowNode for List [Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 2] | -| test.py:636:6:636:14 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:636:6:636:14 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] | -| test.py:636:6:636:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | -| test.py:636:6:636:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | -| test.py:636:7:636:8 | SSA variable a1 | semmle.label | SSA variable a1 | -| test.py:636:11:636:13 | IterableElement | semmle.label | IterableElement | -| test.py:636:11:636:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] | -| test.py:637:10:637:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | -| test.py:639:12:639:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:639:12:639:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:640:10:640:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:640:10:640:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:647:19:647:24 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:648:10:648:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:655:10:655:51 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:655:12:655:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:655:12:655:28 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:655:33:655:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:655:33:655:49 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:656:9:656:9 | SSA variable x | semmle.label | SSA variable x | -| test.py:656:9:656:11 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:656:9:656:11 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:656:16:656:17 | ControlFlowNode for tl [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for tl [List element, Tuple element at index 0] | -| test.py:657:14:657:14 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:663:10:663:51 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:663:12:663:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:663:12:663:28 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:663:33:663:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:663:33:663:49 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:664:9:664:10 | IterableElement | semmle.label | IterableElement | -| test.py:664:9:664:10 | SSA variable x [List element] | semmle.label | SSA variable x [List element] | -| test.py:664:9:664:12 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:664:9:664:12 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:664:12:664:12 | SSA variable y | semmle.label | SSA variable y | -| test.py:664:17:664:18 | ControlFlowNode for tl [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for tl [List element, Tuple element at index 0] | -| test.py:666:14:666:14 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:666:14:666:17 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:667:16:667:16 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:672:10:672:51 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:672:12:672:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:672:12:672:28 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:672:33:672:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:672:33:672:49 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:673:9:673:9 | SSA variable x | semmle.label | SSA variable x | -| test.py:673:9:673:14 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:673:9:673:14 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:673:19:673:20 | ControlFlowNode for tl [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for tl [List element, Tuple element at index 0] | -| test.py:674:14:674:14 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:678:39:678:42 | ControlFlowNode for args [Tuple element at index 0] | semmle.label | ControlFlowNode for args [Tuple element at index 0] | -| test.py:678:39:678:42 | ControlFlowNode for args [Tuple element at index 1] | semmle.label | ControlFlowNode for args [Tuple element at index 1] | -| test.py:679:7:679:9 | SSA variable arg | semmle.label | SSA variable arg | -| test.py:679:14:679:17 | ControlFlowNode for args [Tuple element at index 0] | semmle.label | ControlFlowNode for args [Tuple element at index 0] | -| test.py:679:14:679:17 | ControlFlowNode for args [Tuple element at index 1] | semmle.label | ControlFlowNode for args [Tuple element at index 1] | -| test.py:680:10:680:12 | ControlFlowNode for arg | semmle.label | ControlFlowNode for arg | -| test.py:685:7:685:12 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:686:3:686:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 0] | semmle.label | PosOverflowNode for iterate_star_args() [Tuple element at index 0] | -| test.py:686:3:686:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 1] | semmle.label | PosOverflowNode for iterate_star_args() [Tuple element at index 1] | -| test.py:686:43:686:48 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:686:51:686:51 | ControlFlowNode for s | semmle.label | ControlFlowNode for s | -| test.py:757:16:757:21 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:760:10:760:36 | ControlFlowNode for return_from_inner_scope() | semmle.label | ControlFlowNode for return_from_inner_scope() | -| test.py:795:35:795:35 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:795:37:795:42 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:795:48:795:48 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:795:50:795:55 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:795:61:795:61 | ControlFlowNode for z | semmle.label | ControlFlowNode for z | -| test.py:795:63:795:68 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:796:10:796:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:797:10:797:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:798:10:798:10 | ControlFlowNode for z | semmle.label | ControlFlowNode for z | +| test.py:611:12:611:13 | ControlFlowNode for a2 | semmle.label | ControlFlowNode for a2 | +| test.py:612:10:612:11 | ControlFlowNode for a3 | semmle.label | ControlFlowNode for a3 | +| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | +| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | +| test.py:618:12:618:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] | +| test.py:618:31:618:36 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | +| test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | +| test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] | +| test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 2] | +| test.py:621:6:621:14 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | +| test.py:621:6:621:14 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] | +| test.py:621:7:621:8 | SSA variable a1 | semmle.label | SSA variable a1 | +| test.py:621:11:621:13 | IterableElement | semmle.label | IterableElement | +| test.py:621:11:621:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] | +| test.py:622:10:622:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | +| test.py:624:12:624:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | +| test.py:624:12:624:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:625:10:625:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | +| test.py:625:10:625:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | +| test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | +| test.py:630:7:630:8 | SSA variable a1 | semmle.label | SSA variable a1 | +| test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] | +| test.py:630:7:630:13 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | +| test.py:630:7:630:13 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] | +| test.py:630:11:630:13 | IterableElement | semmle.label | IterableElement | +| test.py:630:11:630:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] | +| test.py:631:10:631:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | +| test.py:633:12:633:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | +| test.py:633:12:633:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:634:10:634:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | +| test.py:634:10:634:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | +| test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | +| test.py:639:7:639:8 | SSA variable a1 | semmle.label | SSA variable a1 | +| test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] | +| test.py:639:7:639:13 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | +| test.py:639:7:639:13 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] | +| test.py:639:11:639:13 | IterableElement | semmle.label | IterableElement | +| test.py:639:11:639:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] | +| test.py:640:10:640:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | +| test.py:642:12:642:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | +| test.py:642:12:642:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:643:10:643:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | +| test.py:643:10:643:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] | +| test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 2] | +| test.py:648:6:648:14 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | +| test.py:648:6:648:14 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] | +| test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | +| test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | +| test.py:648:7:648:8 | SSA variable a1 | semmle.label | SSA variable a1 | +| test.py:648:11:648:13 | IterableElement | semmle.label | IterableElement | +| test.py:648:11:648:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] | +| test.py:649:10:649:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | +| test.py:651:12:651:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | +| test.py:651:12:651:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:652:10:652:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | +| test.py:652:10:652:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:659:19:659:24 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:660:10:660:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | +| test.py:667:10:667:51 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] | +| test.py:667:12:667:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:667:12:667:28 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:667:33:667:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:667:33:667:49 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:668:9:668:9 | SSA variable x | semmle.label | SSA variable x | +| test.py:668:9:668:11 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:668:9:668:11 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | +| test.py:668:16:668:17 | ControlFlowNode for tl [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for tl [List element, Tuple element at index 0] | +| test.py:669:14:669:14 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:675:10:675:51 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] | +| test.py:675:12:675:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:675:12:675:28 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:675:33:675:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:675:33:675:49 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:676:9:676:10 | IterableElement | semmle.label | IterableElement | +| test.py:676:9:676:10 | SSA variable x [List element] | semmle.label | SSA variable x [List element] | +| test.py:676:9:676:12 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:676:9:676:12 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | +| test.py:676:12:676:12 | SSA variable y | semmle.label | SSA variable y | +| test.py:676:17:676:18 | ControlFlowNode for tl [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for tl [List element, Tuple element at index 0] | +| test.py:678:14:678:14 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | +| test.py:678:14:678:17 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:679:16:679:16 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| test.py:684:10:684:51 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] | +| test.py:684:12:684:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:684:12:684:28 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:684:33:684:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:684:33:684:49 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:685:9:685:9 | SSA variable x | semmle.label | SSA variable x | +| test.py:685:9:685:14 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:685:9:685:14 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | +| test.py:685:19:685:20 | ControlFlowNode for tl [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for tl [List element, Tuple element at index 0] | +| test.py:686:14:686:14 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 0] | semmle.label | ControlFlowNode for args [Tuple element at index 0] | +| test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 1] | semmle.label | ControlFlowNode for args [Tuple element at index 1] | +| test.py:691:7:691:9 | SSA variable arg | semmle.label | SSA variable arg | +| test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 0] | semmle.label | ControlFlowNode for args [Tuple element at index 0] | +| test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 1] | semmle.label | ControlFlowNode for args [Tuple element at index 1] | +| test.py:692:10:692:12 | ControlFlowNode for arg | semmle.label | ControlFlowNode for arg | +| test.py:697:7:697:12 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 0] | semmle.label | PosOverflowNode for iterate_star_args() [Tuple element at index 0] | +| test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 1] | semmle.label | PosOverflowNode for iterate_star_args() [Tuple element at index 1] | +| test.py:698:43:698:48 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:698:51:698:51 | ControlFlowNode for s | semmle.label | ControlFlowNode for s | +| test.py:769:16:769:21 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:772:10:772:36 | ControlFlowNode for return_from_inner_scope() | semmle.label | ControlFlowNode for return_from_inner_scope() | +| test.py:807:35:807:35 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:807:37:807:42 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:807:48:807:48 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| test.py:807:50:807:55 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:807:61:807:61 | ControlFlowNode for z | semmle.label | ControlFlowNode for z | +| test.py:807:63:807:68 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:808:10:808:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:809:10:809:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| test.py:810:10:810:10 | ControlFlowNode for z | semmle.label | ControlFlowNode for z | subpaths | datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:35:7:35:7 | ControlFlowNode for a | datamodel.py:36:10:36:10 | ControlFlowNode for a | datamodel.py:38:6:38:17 | ControlFlowNode for f() | | datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:44:22:44:22 | ControlFlowNode for x | datamodel.py:46:16:46:16 | ControlFlowNode for x | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | @@ -844,12 +850,12 @@ subpaths | test.py:396:10:396:43 | KwUnpacked b | test.py:375:15:375:15 | ControlFlowNode for b | test.py:376:12:376:12 | ControlFlowNode for b | test.py:396:10:396:43 | ControlFlowNode for second() | | test.py:404:10:404:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:399:21:399:21 | ControlFlowNode for b [Tuple element at index 0] | test.py:400:12:400:15 | ControlFlowNode for Subscript | test.py:404:10:404:39 | ControlFlowNode for f_extra_pos() | | test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:407:26:407:26 | ControlFlowNode for b [Dictionary element at key b] | test.py:408:12:408:17 | ControlFlowNode for Subscript | test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() | -| test.py:465:12:465:17 | ControlFlowNode for SOURCE | test.py:462:11:462:11 | ControlFlowNode for x | test.py:463:16:463:16 | ControlFlowNode for x | test.py:465:10:465:18 | ControlFlowNode for f() | -| test.py:472:28:472:33 | ControlFlowNode for SOURCE | test.py:469:19:469:19 | ControlFlowNode for b | test.py:470:16:470:16 | ControlFlowNode for b | test.py:472:10:472:34 | ControlFlowNode for second() | -| test.py:486:30:486:35 | ControlFlowNode for SOURCE | test.py:483:19:483:19 | ControlFlowNode for b | test.py:484:16:484:16 | ControlFlowNode for b | test.py:486:10:486:36 | ControlFlowNode for second() | -| test.py:500:10:500:43 | KwUnpacked b | test.py:497:19:497:19 | ControlFlowNode for b | test.py:498:16:498:16 | ControlFlowNode for b | test.py:500:10:500:43 | ControlFlowNode for second() | -| test.py:505:10:505:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:504:30:504:30 | ControlFlowNode for b [Tuple element at index 0] | test.py:504:33:504:36 | ControlFlowNode for Subscript | test.py:505:10:505:39 | ControlFlowNode for f_extra_pos() | -| test.py:510:10:510:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:509:35:509:35 | ControlFlowNode for b [Dictionary element at key b] | test.py:509:38:509:43 | ControlFlowNode for Subscript | test.py:510:10:510:45 | ControlFlowNode for f_extra_keyword() | +| test.py:477:12:477:17 | ControlFlowNode for SOURCE | test.py:474:11:474:11 | ControlFlowNode for x | test.py:475:16:475:16 | ControlFlowNode for x | test.py:477:10:477:18 | ControlFlowNode for f() | +| test.py:484:28:484:33 | ControlFlowNode for SOURCE | test.py:481:19:481:19 | ControlFlowNode for b | test.py:482:16:482:16 | ControlFlowNode for b | test.py:484:10:484:34 | ControlFlowNode for second() | +| test.py:498:30:498:35 | ControlFlowNode for SOURCE | test.py:495:19:495:19 | ControlFlowNode for b | test.py:496:16:496:16 | ControlFlowNode for b | test.py:498:10:498:36 | ControlFlowNode for second() | +| test.py:512:10:512:43 | KwUnpacked b | test.py:509:19:509:19 | ControlFlowNode for b | test.py:510:16:510:16 | ControlFlowNode for b | test.py:512:10:512:43 | ControlFlowNode for second() | +| test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:516:30:516:30 | ControlFlowNode for b [Tuple element at index 0] | test.py:516:33:516:36 | ControlFlowNode for Subscript | test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() | +| test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:521:35:521:35 | ControlFlowNode for b [Dictionary element at key b] | test.py:521:38:521:43 | ControlFlowNode for Subscript | test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() | #select | datamodel.py:38:6:38:17 | ControlFlowNode for f() | datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:38:6:38:17 | ControlFlowNode for f() | Flow found | | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | Flow found | @@ -887,76 +893,78 @@ subpaths | test.py:396:10:396:43 | ControlFlowNode for second() | test.py:396:36:396:41 | ControlFlowNode for SOURCE | test.py:396:10:396:43 | ControlFlowNode for second() | Flow found | | test.py:404:10:404:39 | ControlFlowNode for f_extra_pos() | test.py:404:33:404:38 | ControlFlowNode for SOURCE | test.py:404:10:404:39 | ControlFlowNode for f_extra_pos() | Flow found | | test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() | test.py:412:39:412:44 | ControlFlowNode for SOURCE | test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() | Flow found | -| test.py:433:10:433:38 | ControlFlowNode for IfExp | test.py:433:10:433:15 | ControlFlowNode for SOURCE | test.py:433:10:433:38 | ControlFlowNode for IfExp | Flow found | -| test.py:441:10:441:39 | ControlFlowNode for IfExp | test.py:441:34:441:39 | ControlFlowNode for SOURCE | test.py:441:10:441:39 | ControlFlowNode for IfExp | Flow found | -| test.py:465:10:465:18 | ControlFlowNode for f() | test.py:465:12:465:17 | ControlFlowNode for SOURCE | test.py:465:10:465:18 | ControlFlowNode for f() | Flow found | -| test.py:472:10:472:34 | ControlFlowNode for second() | test.py:472:28:472:33 | ControlFlowNode for SOURCE | test.py:472:10:472:34 | ControlFlowNode for second() | Flow found | -| test.py:486:10:486:36 | ControlFlowNode for second() | test.py:486:30:486:35 | ControlFlowNode for SOURCE | test.py:486:10:486:36 | ControlFlowNode for second() | Flow found | -| test.py:500:10:500:43 | ControlFlowNode for second() | test.py:500:36:500:41 | ControlFlowNode for SOURCE | test.py:500:10:500:43 | ControlFlowNode for second() | Flow found | -| test.py:505:10:505:39 | ControlFlowNode for f_extra_pos() | test.py:505:33:505:38 | ControlFlowNode for SOURCE | test.py:505:10:505:39 | ControlFlowNode for f_extra_pos() | Flow found | -| test.py:510:10:510:45 | ControlFlowNode for f_extra_keyword() | test.py:510:39:510:44 | ControlFlowNode for SOURCE | test.py:510:10:510:45 | ControlFlowNode for f_extra_keyword() | Flow found | -| test.py:524:10:524:10 | ControlFlowNode for a | test.py:522:9:522:14 | ControlFlowNode for SOURCE | test.py:524:10:524:10 | ControlFlowNode for a | Flow found | -| test.py:529:10:529:10 | ControlFlowNode for b | test.py:522:9:522:14 | ControlFlowNode for SOURCE | test.py:529:10:529:10 | ControlFlowNode for b | Flow found | -| test.py:536:10:536:10 | ControlFlowNode for a | test.py:534:10:534:15 | ControlFlowNode for SOURCE | test.py:536:10:536:10 | ControlFlowNode for a | Flow found | -| test.py:544:10:544:10 | ControlFlowNode for a | test.py:542:10:542:15 | ControlFlowNode for SOURCE | test.py:544:10:544:10 | ControlFlowNode for a | Flow found | -| test.py:546:10:546:10 | ControlFlowNode for c | test.py:542:30:542:35 | ControlFlowNode for SOURCE | test.py:546:10:546:10 | ControlFlowNode for c | Flow found | -| test.py:553:10:553:10 | ControlFlowNode for a | test.py:551:13:551:18 | ControlFlowNode for SOURCE | test.py:553:10:553:10 | ControlFlowNode for a | Flow found | -| test.py:561:10:561:10 | ControlFlowNode for a | test.py:559:10:559:15 | ControlFlowNode for SOURCE | test.py:561:10:561:10 | ControlFlowNode for a | Flow found | -| test.py:563:10:563:13 | ControlFlowNode for Subscript | test.py:559:18:559:23 | ControlFlowNode for SOURCE | test.py:563:10:563:13 | ControlFlowNode for Subscript | Flow found | -| test.py:564:12:564:12 | ControlFlowNode for c | test.py:559:18:559:23 | ControlFlowNode for SOURCE | test.py:564:12:564:12 | ControlFlowNode for c | Flow found | -| test.py:571:10:571:10 | ControlFlowNode for a | test.py:569:10:569:15 | ControlFlowNode for SOURCE | test.py:571:10:571:10 | ControlFlowNode for a | Flow found | -| test.py:573:10:573:10 | ControlFlowNode for c | test.py:569:18:569:23 | ControlFlowNode for SOURCE | test.py:573:10:573:10 | ControlFlowNode for c | Flow found | -| test.py:582:10:582:11 | ControlFlowNode for a1 | test.py:578:12:578:17 | ControlFlowNode for SOURCE | test.py:582:10:582:11 | ControlFlowNode for a1 | Flow found | -| test.py:582:10:582:11 | ControlFlowNode for a1 | test.py:578:31:578:36 | ControlFlowNode for SOURCE | test.py:582:10:582:11 | ControlFlowNode for a1 | Flow found | -| test.py:582:10:582:11 | ControlFlowNode for a1 | test.py:578:41:578:46 | ControlFlowNode for SOURCE | test.py:582:10:582:11 | ControlFlowNode for a1 | Flow found | -| test.py:583:12:583:13 | ControlFlowNode for a2 | test.py:578:12:578:17 | ControlFlowNode for SOURCE | test.py:583:12:583:13 | ControlFlowNode for a2 | Flow found | -| test.py:583:12:583:13 | ControlFlowNode for a2 | test.py:578:31:578:36 | ControlFlowNode for SOURCE | test.py:583:12:583:13 | ControlFlowNode for a2 | Flow found | -| test.py:583:12:583:13 | ControlFlowNode for a2 | test.py:578:41:578:46 | ControlFlowNode for SOURCE | test.py:583:12:583:13 | ControlFlowNode for a2 | Flow found | -| test.py:584:10:584:11 | ControlFlowNode for a3 | test.py:578:12:578:17 | ControlFlowNode for SOURCE | test.py:584:10:584:11 | ControlFlowNode for a3 | Flow found | -| test.py:584:10:584:11 | ControlFlowNode for a3 | test.py:578:31:578:36 | ControlFlowNode for SOURCE | test.py:584:10:584:11 | ControlFlowNode for a3 | Flow found | -| test.py:584:10:584:11 | ControlFlowNode for a3 | test.py:578:41:578:46 | ControlFlowNode for SOURCE | test.py:584:10:584:11 | ControlFlowNode for a3 | Flow found | -| test.py:590:10:590:11 | ControlFlowNode for a1 | test.py:578:12:578:17 | ControlFlowNode for SOURCE | test.py:590:10:590:11 | ControlFlowNode for a1 | Flow found | -| test.py:590:10:590:11 | ControlFlowNode for a1 | test.py:578:31:578:36 | ControlFlowNode for SOURCE | test.py:590:10:590:11 | ControlFlowNode for a1 | Flow found | -| test.py:590:10:590:11 | ControlFlowNode for a1 | test.py:578:41:578:46 | ControlFlowNode for SOURCE | test.py:590:10:590:11 | ControlFlowNode for a1 | Flow found | -| test.py:591:12:591:13 | ControlFlowNode for a2 | test.py:578:12:578:17 | ControlFlowNode for SOURCE | test.py:591:12:591:13 | ControlFlowNode for a2 | Flow found | -| test.py:591:12:591:13 | ControlFlowNode for a2 | test.py:578:31:578:36 | ControlFlowNode for SOURCE | test.py:591:12:591:13 | ControlFlowNode for a2 | Flow found | -| test.py:591:12:591:13 | ControlFlowNode for a2 | test.py:578:41:578:46 | ControlFlowNode for SOURCE | test.py:591:12:591:13 | ControlFlowNode for a2 | Flow found | -| test.py:592:10:592:11 | ControlFlowNode for a3 | test.py:578:12:578:17 | ControlFlowNode for SOURCE | test.py:592:10:592:11 | ControlFlowNode for a3 | Flow found | -| test.py:592:10:592:11 | ControlFlowNode for a3 | test.py:578:31:578:36 | ControlFlowNode for SOURCE | test.py:592:10:592:11 | ControlFlowNode for a3 | Flow found | -| test.py:592:10:592:11 | ControlFlowNode for a3 | test.py:578:41:578:46 | ControlFlowNode for SOURCE | test.py:592:10:592:11 | ControlFlowNode for a3 | Flow found | -| test.py:598:10:598:11 | ControlFlowNode for a1 | test.py:578:12:578:17 | ControlFlowNode for SOURCE | test.py:598:10:598:11 | ControlFlowNode for a1 | Flow found | -| test.py:598:10:598:11 | ControlFlowNode for a1 | test.py:578:31:578:36 | ControlFlowNode for SOURCE | test.py:598:10:598:11 | ControlFlowNode for a1 | Flow found | -| test.py:598:10:598:11 | ControlFlowNode for a1 | test.py:578:41:578:46 | ControlFlowNode for SOURCE | test.py:598:10:598:11 | ControlFlowNode for a1 | Flow found | -| test.py:599:12:599:13 | ControlFlowNode for a2 | test.py:578:12:578:17 | ControlFlowNode for SOURCE | test.py:599:12:599:13 | ControlFlowNode for a2 | Flow found | -| test.py:599:12:599:13 | ControlFlowNode for a2 | test.py:578:31:578:36 | ControlFlowNode for SOURCE | test.py:599:12:599:13 | ControlFlowNode for a2 | Flow found | -| test.py:599:12:599:13 | ControlFlowNode for a2 | test.py:578:41:578:46 | ControlFlowNode for SOURCE | test.py:599:12:599:13 | ControlFlowNode for a2 | Flow found | -| test.py:600:10:600:11 | ControlFlowNode for a3 | test.py:578:12:578:17 | ControlFlowNode for SOURCE | test.py:600:10:600:11 | ControlFlowNode for a3 | Flow found | -| test.py:600:10:600:11 | ControlFlowNode for a3 | test.py:578:31:578:36 | ControlFlowNode for SOURCE | test.py:600:10:600:11 | ControlFlowNode for a3 | Flow found | -| test.py:600:10:600:11 | ControlFlowNode for a3 | test.py:578:41:578:46 | ControlFlowNode for SOURCE | test.py:600:10:600:11 | ControlFlowNode for a3 | Flow found | -| test.py:610:10:610:11 | ControlFlowNode for a1 | test.py:606:12:606:17 | ControlFlowNode for SOURCE | test.py:610:10:610:11 | ControlFlowNode for a1 | Flow found | -| test.py:612:12:612:16 | ControlFlowNode for Subscript | test.py:606:31:606:36 | ControlFlowNode for SOURCE | test.py:612:12:612:16 | ControlFlowNode for Subscript | Flow found | -| test.py:613:10:613:14 | ControlFlowNode for Subscript | test.py:606:31:606:36 | ControlFlowNode for SOURCE | test.py:613:10:613:14 | ControlFlowNode for Subscript | Flow found | -| test.py:619:10:619:11 | ControlFlowNode for a1 | test.py:606:12:606:17 | ControlFlowNode for SOURCE | test.py:619:10:619:11 | ControlFlowNode for a1 | Flow found | -| test.py:621:12:621:16 | ControlFlowNode for Subscript | test.py:606:31:606:36 | ControlFlowNode for SOURCE | test.py:621:12:621:16 | ControlFlowNode for Subscript | Flow found | -| test.py:622:10:622:14 | ControlFlowNode for Subscript | test.py:606:31:606:36 | ControlFlowNode for SOURCE | test.py:622:10:622:14 | ControlFlowNode for Subscript | Flow found | -| test.py:628:10:628:11 | ControlFlowNode for a1 | test.py:606:12:606:17 | ControlFlowNode for SOURCE | test.py:628:10:628:11 | ControlFlowNode for a1 | Flow found | -| test.py:630:12:630:16 | ControlFlowNode for Subscript | test.py:606:31:606:36 | ControlFlowNode for SOURCE | test.py:630:12:630:16 | ControlFlowNode for Subscript | Flow found | -| test.py:631:10:631:14 | ControlFlowNode for Subscript | test.py:606:31:606:36 | ControlFlowNode for SOURCE | test.py:631:10:631:14 | ControlFlowNode for Subscript | Flow found | -| test.py:637:10:637:11 | ControlFlowNode for a1 | test.py:606:12:606:17 | ControlFlowNode for SOURCE | test.py:637:10:637:11 | ControlFlowNode for a1 | Flow found | -| test.py:639:12:639:16 | ControlFlowNode for Subscript | test.py:606:31:606:36 | ControlFlowNode for SOURCE | test.py:639:12:639:16 | ControlFlowNode for Subscript | Flow found | -| test.py:640:10:640:14 | ControlFlowNode for Subscript | test.py:606:31:606:36 | ControlFlowNode for SOURCE | test.py:640:10:640:14 | ControlFlowNode for Subscript | Flow found | -| test.py:648:10:648:10 | ControlFlowNode for a | test.py:647:19:647:24 | ControlFlowNode for SOURCE | test.py:648:10:648:10 | ControlFlowNode for a | Flow found | -| test.py:657:14:657:14 | ControlFlowNode for x | test.py:655:12:655:17 | ControlFlowNode for SOURCE | test.py:657:14:657:14 | ControlFlowNode for x | Flow found | -| test.py:657:14:657:14 | ControlFlowNode for x | test.py:655:33:655:38 | ControlFlowNode for SOURCE | test.py:657:14:657:14 | ControlFlowNode for x | Flow found | -| test.py:666:14:666:17 | ControlFlowNode for Subscript | test.py:663:12:663:17 | ControlFlowNode for SOURCE | test.py:666:14:666:17 | ControlFlowNode for Subscript | Flow found | -| test.py:666:14:666:17 | ControlFlowNode for Subscript | test.py:663:33:663:38 | ControlFlowNode for SOURCE | test.py:666:14:666:17 | ControlFlowNode for Subscript | Flow found | -| test.py:667:16:667:16 | ControlFlowNode for y | test.py:663:12:663:17 | ControlFlowNode for SOURCE | test.py:667:16:667:16 | ControlFlowNode for y | Flow found | -| test.py:667:16:667:16 | ControlFlowNode for y | test.py:663:33:663:38 | ControlFlowNode for SOURCE | test.py:667:16:667:16 | ControlFlowNode for y | Flow found | -| test.py:674:14:674:14 | ControlFlowNode for x | test.py:672:12:672:17 | ControlFlowNode for SOURCE | test.py:674:14:674:14 | ControlFlowNode for x | Flow found | -| test.py:674:14:674:14 | ControlFlowNode for x | test.py:672:33:672:38 | ControlFlowNode for SOURCE | test.py:674:14:674:14 | ControlFlowNode for x | Flow found | -| test.py:680:10:680:12 | ControlFlowNode for arg | test.py:685:7:685:12 | ControlFlowNode for SOURCE | test.py:680:10:680:12 | ControlFlowNode for arg | Flow found | -| test.py:680:10:680:12 | ControlFlowNode for arg | test.py:686:43:686:48 | ControlFlowNode for SOURCE | test.py:680:10:680:12 | ControlFlowNode for arg | Flow found | -| test.py:760:10:760:36 | ControlFlowNode for return_from_inner_scope() | test.py:757:16:757:21 | ControlFlowNode for SOURCE | test.py:760:10:760:36 | ControlFlowNode for return_from_inner_scope() | Flow found | -| test.py:796:10:796:10 | ControlFlowNode for x | test.py:795:37:795:42 | ControlFlowNode for SOURCE | test.py:796:10:796:10 | ControlFlowNode for x | Flow found | -| test.py:797:10:797:10 | ControlFlowNode for y | test.py:795:50:795:55 | ControlFlowNode for SOURCE | test.py:797:10:797:10 | ControlFlowNode for y | Flow found | -| test.py:798:10:798:10 | ControlFlowNode for z | test.py:795:63:795:68 | ControlFlowNode for SOURCE | test.py:798:10:798:10 | ControlFlowNode for z | Flow found | +| test.py:429:10:429:20 | ControlFlowNode for BoolExpr | test.py:429:15:429:20 | ControlFlowNode for SOURCE | test.py:429:10:429:20 | ControlFlowNode for BoolExpr | Flow found | +| test.py:434:10:434:21 | ControlFlowNode for BoolExpr | test.py:434:16:434:21 | ControlFlowNode for SOURCE | test.py:434:10:434:21 | ControlFlowNode for BoolExpr | Flow found | +| test.py:445:10:445:38 | ControlFlowNode for IfExp | test.py:445:10:445:15 | ControlFlowNode for SOURCE | test.py:445:10:445:38 | ControlFlowNode for IfExp | Flow found | +| test.py:453:10:453:39 | ControlFlowNode for IfExp | test.py:453:34:453:39 | ControlFlowNode for SOURCE | test.py:453:10:453:39 | ControlFlowNode for IfExp | Flow found | +| test.py:477:10:477:18 | ControlFlowNode for f() | test.py:477:12:477:17 | ControlFlowNode for SOURCE | test.py:477:10:477:18 | ControlFlowNode for f() | Flow found | +| test.py:484:10:484:34 | ControlFlowNode for second() | test.py:484:28:484:33 | ControlFlowNode for SOURCE | test.py:484:10:484:34 | ControlFlowNode for second() | Flow found | +| test.py:498:10:498:36 | ControlFlowNode for second() | test.py:498:30:498:35 | ControlFlowNode for SOURCE | test.py:498:10:498:36 | ControlFlowNode for second() | Flow found | +| test.py:512:10:512:43 | ControlFlowNode for second() | test.py:512:36:512:41 | ControlFlowNode for SOURCE | test.py:512:10:512:43 | ControlFlowNode for second() | Flow found | +| test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() | test.py:517:33:517:38 | ControlFlowNode for SOURCE | test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() | Flow found | +| test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() | test.py:522:39:522:44 | ControlFlowNode for SOURCE | test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() | Flow found | +| test.py:536:10:536:10 | ControlFlowNode for a | test.py:534:9:534:14 | ControlFlowNode for SOURCE | test.py:536:10:536:10 | ControlFlowNode for a | Flow found | +| test.py:541:10:541:10 | ControlFlowNode for b | test.py:534:9:534:14 | ControlFlowNode for SOURCE | test.py:541:10:541:10 | ControlFlowNode for b | Flow found | +| test.py:548:10:548:10 | ControlFlowNode for a | test.py:546:10:546:15 | ControlFlowNode for SOURCE | test.py:548:10:548:10 | ControlFlowNode for a | Flow found | +| test.py:556:10:556:10 | ControlFlowNode for a | test.py:554:10:554:15 | ControlFlowNode for SOURCE | test.py:556:10:556:10 | ControlFlowNode for a | Flow found | +| test.py:558:10:558:10 | ControlFlowNode for c | test.py:554:30:554:35 | ControlFlowNode for SOURCE | test.py:558:10:558:10 | ControlFlowNode for c | Flow found | +| test.py:565:10:565:10 | ControlFlowNode for a | test.py:563:13:563:18 | ControlFlowNode for SOURCE | test.py:565:10:565:10 | ControlFlowNode for a | Flow found | +| test.py:573:10:573:10 | ControlFlowNode for a | test.py:571:10:571:15 | ControlFlowNode for SOURCE | test.py:573:10:573:10 | ControlFlowNode for a | Flow found | +| test.py:575:10:575:13 | ControlFlowNode for Subscript | test.py:571:18:571:23 | ControlFlowNode for SOURCE | test.py:575:10:575:13 | ControlFlowNode for Subscript | Flow found | +| test.py:576:12:576:12 | ControlFlowNode for c | test.py:571:18:571:23 | ControlFlowNode for SOURCE | test.py:576:12:576:12 | ControlFlowNode for c | Flow found | +| test.py:583:10:583:10 | ControlFlowNode for a | test.py:581:10:581:15 | ControlFlowNode for SOURCE | test.py:583:10:583:10 | ControlFlowNode for a | Flow found | +| test.py:585:10:585:10 | ControlFlowNode for c | test.py:581:18:581:23 | ControlFlowNode for SOURCE | test.py:585:10:585:10 | ControlFlowNode for c | Flow found | +| test.py:594:10:594:11 | ControlFlowNode for a1 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:594:10:594:11 | ControlFlowNode for a1 | Flow found | +| test.py:594:10:594:11 | ControlFlowNode for a1 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:594:10:594:11 | ControlFlowNode for a1 | Flow found | +| test.py:594:10:594:11 | ControlFlowNode for a1 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:594:10:594:11 | ControlFlowNode for a1 | Flow found | +| test.py:595:12:595:13 | ControlFlowNode for a2 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:595:12:595:13 | ControlFlowNode for a2 | Flow found | +| test.py:595:12:595:13 | ControlFlowNode for a2 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:595:12:595:13 | ControlFlowNode for a2 | Flow found | +| test.py:595:12:595:13 | ControlFlowNode for a2 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:595:12:595:13 | ControlFlowNode for a2 | Flow found | +| test.py:596:10:596:11 | ControlFlowNode for a3 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:596:10:596:11 | ControlFlowNode for a3 | Flow found | +| test.py:596:10:596:11 | ControlFlowNode for a3 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:596:10:596:11 | ControlFlowNode for a3 | Flow found | +| test.py:596:10:596:11 | ControlFlowNode for a3 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:596:10:596:11 | ControlFlowNode for a3 | Flow found | +| test.py:602:10:602:11 | ControlFlowNode for a1 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:602:10:602:11 | ControlFlowNode for a1 | Flow found | +| test.py:602:10:602:11 | ControlFlowNode for a1 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:602:10:602:11 | ControlFlowNode for a1 | Flow found | +| test.py:602:10:602:11 | ControlFlowNode for a1 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:602:10:602:11 | ControlFlowNode for a1 | Flow found | +| test.py:603:12:603:13 | ControlFlowNode for a2 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:603:12:603:13 | ControlFlowNode for a2 | Flow found | +| test.py:603:12:603:13 | ControlFlowNode for a2 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:603:12:603:13 | ControlFlowNode for a2 | Flow found | +| test.py:603:12:603:13 | ControlFlowNode for a2 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:603:12:603:13 | ControlFlowNode for a2 | Flow found | +| test.py:604:10:604:11 | ControlFlowNode for a3 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:604:10:604:11 | ControlFlowNode for a3 | Flow found | +| test.py:604:10:604:11 | ControlFlowNode for a3 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:604:10:604:11 | ControlFlowNode for a3 | Flow found | +| test.py:604:10:604:11 | ControlFlowNode for a3 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:604:10:604:11 | ControlFlowNode for a3 | Flow found | +| test.py:610:10:610:11 | ControlFlowNode for a1 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:610:10:610:11 | ControlFlowNode for a1 | Flow found | +| test.py:610:10:610:11 | ControlFlowNode for a1 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:610:10:610:11 | ControlFlowNode for a1 | Flow found | +| test.py:610:10:610:11 | ControlFlowNode for a1 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:610:10:610:11 | ControlFlowNode for a1 | Flow found | +| test.py:611:12:611:13 | ControlFlowNode for a2 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:611:12:611:13 | ControlFlowNode for a2 | Flow found | +| test.py:611:12:611:13 | ControlFlowNode for a2 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:611:12:611:13 | ControlFlowNode for a2 | Flow found | +| test.py:611:12:611:13 | ControlFlowNode for a2 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:611:12:611:13 | ControlFlowNode for a2 | Flow found | +| test.py:612:10:612:11 | ControlFlowNode for a3 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:612:10:612:11 | ControlFlowNode for a3 | Flow found | +| test.py:612:10:612:11 | ControlFlowNode for a3 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:612:10:612:11 | ControlFlowNode for a3 | Flow found | +| test.py:612:10:612:11 | ControlFlowNode for a3 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:612:10:612:11 | ControlFlowNode for a3 | Flow found | +| test.py:622:10:622:11 | ControlFlowNode for a1 | test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:622:10:622:11 | ControlFlowNode for a1 | Flow found | +| test.py:624:12:624:16 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:624:12:624:16 | ControlFlowNode for Subscript | Flow found | +| test.py:625:10:625:14 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:625:10:625:14 | ControlFlowNode for Subscript | Flow found | +| test.py:631:10:631:11 | ControlFlowNode for a1 | test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:631:10:631:11 | ControlFlowNode for a1 | Flow found | +| test.py:633:12:633:16 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:633:12:633:16 | ControlFlowNode for Subscript | Flow found | +| test.py:634:10:634:14 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:634:10:634:14 | ControlFlowNode for Subscript | Flow found | +| test.py:640:10:640:11 | ControlFlowNode for a1 | test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:640:10:640:11 | ControlFlowNode for a1 | Flow found | +| test.py:642:12:642:16 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:642:12:642:16 | ControlFlowNode for Subscript | Flow found | +| test.py:643:10:643:14 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:643:10:643:14 | ControlFlowNode for Subscript | Flow found | +| test.py:649:10:649:11 | ControlFlowNode for a1 | test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:649:10:649:11 | ControlFlowNode for a1 | Flow found | +| test.py:651:12:651:16 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:651:12:651:16 | ControlFlowNode for Subscript | Flow found | +| test.py:652:10:652:14 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:652:10:652:14 | ControlFlowNode for Subscript | Flow found | +| test.py:660:10:660:10 | ControlFlowNode for a | test.py:659:19:659:24 | ControlFlowNode for SOURCE | test.py:660:10:660:10 | ControlFlowNode for a | Flow found | +| test.py:669:14:669:14 | ControlFlowNode for x | test.py:667:12:667:17 | ControlFlowNode for SOURCE | test.py:669:14:669:14 | ControlFlowNode for x | Flow found | +| test.py:669:14:669:14 | ControlFlowNode for x | test.py:667:33:667:38 | ControlFlowNode for SOURCE | test.py:669:14:669:14 | ControlFlowNode for x | Flow found | +| test.py:678:14:678:17 | ControlFlowNode for Subscript | test.py:675:12:675:17 | ControlFlowNode for SOURCE | test.py:678:14:678:17 | ControlFlowNode for Subscript | Flow found | +| test.py:678:14:678:17 | ControlFlowNode for Subscript | test.py:675:33:675:38 | ControlFlowNode for SOURCE | test.py:678:14:678:17 | ControlFlowNode for Subscript | Flow found | +| test.py:679:16:679:16 | ControlFlowNode for y | test.py:675:12:675:17 | ControlFlowNode for SOURCE | test.py:679:16:679:16 | ControlFlowNode for y | Flow found | +| test.py:679:16:679:16 | ControlFlowNode for y | test.py:675:33:675:38 | ControlFlowNode for SOURCE | test.py:679:16:679:16 | ControlFlowNode for y | Flow found | +| test.py:686:14:686:14 | ControlFlowNode for x | test.py:684:12:684:17 | ControlFlowNode for SOURCE | test.py:686:14:686:14 | ControlFlowNode for x | Flow found | +| test.py:686:14:686:14 | ControlFlowNode for x | test.py:684:33:684:38 | ControlFlowNode for SOURCE | test.py:686:14:686:14 | ControlFlowNode for x | Flow found | +| test.py:692:10:692:12 | ControlFlowNode for arg | test.py:697:7:697:12 | ControlFlowNode for SOURCE | test.py:692:10:692:12 | ControlFlowNode for arg | Flow found | +| test.py:692:10:692:12 | ControlFlowNode for arg | test.py:698:43:698:48 | ControlFlowNode for SOURCE | test.py:692:10:692:12 | ControlFlowNode for arg | Flow found | +| test.py:772:10:772:36 | ControlFlowNode for return_from_inner_scope() | test.py:769:16:769:21 | ControlFlowNode for SOURCE | test.py:772:10:772:36 | ControlFlowNode for return_from_inner_scope() | Flow found | +| test.py:808:10:808:10 | ControlFlowNode for x | test.py:807:37:807:42 | ControlFlowNode for SOURCE | test.py:808:10:808:10 | ControlFlowNode for x | Flow found | +| test.py:809:10:809:10 | ControlFlowNode for y | test.py:807:50:807:55 | ControlFlowNode for SOURCE | test.py:809:10:809:10 | ControlFlowNode for y | Flow found | +| test.py:810:10:810:10 | ControlFlowNode for z | test.py:807:63:807:68 | ControlFlowNode for SOURCE | test.py:810:10:810:10 | ControlFlowNode for z | Flow found | diff --git a/python/ql/test/experimental/dataflow/coverage/test.py b/python/ql/test/experimental/dataflow/coverage/test.py index 906ece07952..751adde20d8 100644 --- a/python/ql/test/experimental/dataflow/coverage/test.py +++ b/python/ql/test/experimental/dataflow/coverage/test.py @@ -422,6 +422,18 @@ def test_call_extra_keyword_flow(): SINK(f_extra_keyword_flow(**{SOURCE: None})) #$ MISSING:flow="SOURCE -> f_extra_keyword(..)" +# 6.11. Boolean operations + +def test_or(x = False): + # if we don't know the value of the lhs, we should always add flow + SINK(x or SOURCE) #$ flow="SOURCE -> BoolExpr" + + +def test_and(x = True): + # if we don't know the value of the lhs, we should always add flow + SINK(x and SOURCE) #$ flow="SOURCE -> BoolExpr" + + # 6.12. Assignment expressions def test_assignment_expression(): x = NONSOURCE diff --git a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected index e288b7fd157..5968fdc596b 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected @@ -72,9 +72,14 @@ | test.py:6:15:6:15 | ControlFlowNode for x | test.py:6:15:6:15 | SSA variable x | | test.py:6:15:6:15 | SSA variable x | test.py:7:12:7:12 | ControlFlowNode for x | | test.py:7:12:7:12 | ControlFlowNode for x | test.py:7:29:7:29 | ControlFlowNode for x | +| test.py:7:12:7:24 | ControlFlowNode for Compare | test.py:7:12:7:78 | ControlFlowNode for BoolExpr | | test.py:7:29:7:29 | ControlFlowNode for x | test.py:7:47:7:47 | ControlFlowNode for x | +| test.py:7:29:7:42 | ControlFlowNode for Compare | test.py:7:12:7:78 | ControlFlowNode for BoolExpr | | test.py:7:47:7:47 | ControlFlowNode for x | test.py:7:58:7:58 | ControlFlowNode for x | +| test.py:7:47:7:53 | ControlFlowNode for Compare | test.py:7:12:7:78 | ControlFlowNode for BoolExpr | | test.py:7:58:7:58 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:58:7:66 | ControlFlowNode for Compare | test.py:7:12:7:78 | ControlFlowNode for BoolExpr | +| test.py:7:71:7:78 | ControlFlowNode for Compare | test.py:7:12:7:78 | ControlFlowNode for BoolExpr | | test.py:10:1:10:12 | ControlFlowNode for FunctionExpr | test.py:10:5:10:8 | GSSA Variable SINK | | test.py:10:1:10:12 | GSSA Variable is_source | test.py:11:8:11:16 | ControlFlowNode for is_source | | test.py:10:10:10:10 | ControlFlowNode for x | test.py:10:10:10:10 | SSA variable x | diff --git a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected index 1dde97a8d04..55f241c305d 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected @@ -30,20 +30,22 @@ | examples.py:0:0:0:0 | GSSA Variable object | examples.py:6:13:6:18 | ControlFlowNode for object | | examples.py:0:0:0:0 | GSSA Variable object | examples.py:11:17:11:22 | ControlFlowNode for object | | examples.py:0:0:0:0 | GSSA Variable object | examples.py:11:17:11:22 | ControlFlowNode for object | -| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | examples.py:13:20:13:24 | ControlFlowNode for MyObj | -| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | examples.py:13:20:13:24 | ControlFlowNode for MyObj | -| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | examples.py:54:11:54:15 | ControlFlowNode for MyObj | -| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | examples.py:54:11:54:15 | ControlFlowNode for MyObj | -| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module examples | examples.py:21:5:21:10 | ControlFlowNode for SINK_F | -| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module examples | examples.py:21:5:21:10 | ControlFlowNode for SINK_F | +| examples.py:0:0:0:0 | ModuleVariableNode for examples.MyObj | examples.py:13:20:13:24 | ControlFlowNode for MyObj | +| examples.py:0:0:0:0 | ModuleVariableNode for examples.MyObj | examples.py:13:20:13:24 | ControlFlowNode for MyObj | +| examples.py:0:0:0:0 | ModuleVariableNode for examples.MyObj | examples.py:54:11:54:15 | ControlFlowNode for MyObj | +| examples.py:0:0:0:0 | ModuleVariableNode for examples.MyObj | examples.py:54:11:54:15 | ControlFlowNode for MyObj | +| examples.py:0:0:0:0 | ModuleVariableNode for examples.SINK_F | examples.py:21:5:21:10 | ControlFlowNode for SINK_F | +| examples.py:0:0:0:0 | ModuleVariableNode for examples.SINK_F | examples.py:21:5:21:10 | ControlFlowNode for SINK_F | | examples.py:6:1:6:20 | ControlFlowNode for ClassExpr | examples.py:6:7:6:11 | GSSA Variable MyObj | | examples.py:6:1:6:20 | ControlFlowNode for ClassExpr | examples.py:6:7:6:11 | GSSA Variable MyObj | | examples.py:6:1:6:20 | ControlFlowNode for ClassExpr | examples.py:25:9:25:13 | ControlFlowNode for MyObj | | examples.py:6:1:6:20 | ControlFlowNode for ClassExpr | examples.py:25:9:25:13 | ControlFlowNode for MyObj | | examples.py:6:1:6:20 | ControlFlowNode for ClassExpr | examples.py:49:7:49:11 | ControlFlowNode for MyObj | | examples.py:6:1:6:20 | ControlFlowNode for ClassExpr | examples.py:49:7:49:11 | ControlFlowNode for MyObj | -| examples.py:6:7:6:11 | GSSA Variable MyObj | examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | -| examples.py:6:7:6:11 | GSSA Variable MyObj | examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | +| examples.py:6:7:6:11 | ControlFlowNode for MyObj | examples.py:0:0:0:0 | ModuleVariableNode for examples.MyObj | +| examples.py:6:7:6:11 | ControlFlowNode for MyObj | examples.py:0:0:0:0 | ModuleVariableNode for examples.MyObj | +| examples.py:6:7:6:11 | GSSA Variable MyObj | examples.py:0:0:0:0 | ModuleVariableNode for examples.MyObj | +| examples.py:6:7:6:11 | GSSA Variable MyObj | examples.py:0:0:0:0 | ModuleVariableNode for examples.MyObj | | examples.py:6:7:6:11 | GSSA Variable MyObj | examples.py:25:9:25:13 | ControlFlowNode for MyObj | | examples.py:6:7:6:11 | GSSA Variable MyObj | examples.py:25:9:25:13 | ControlFlowNode for MyObj | | examples.py:6:7:6:11 | GSSA Variable MyObj | examples.py:49:7:49:11 | ControlFlowNode for MyObj | @@ -470,78 +472,82 @@ | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | examples.py:53:28:53:28 | ControlFlowNode for x | | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:35:20:35:24 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:35:20:35:24 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:47:13:47:17 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:47:13:47:17 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:54:13:54:17 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:54:13:54:17 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:81:11:81:15 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:81:11:81:15 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:86:11:86:15 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:86:11:86:15 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:91:11:91:15 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:91:11:91:15 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | test.py:63:9:63:17 | ControlFlowNode for NestedObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | test.py:63:9:63:17 | ControlFlowNode for NestedObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | test.py:73:9:73:17 | ControlFlowNode for NestedObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | test.py:73:9:73:17 | ControlFlowNode for NestedObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:50:5:50:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:50:5:50:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:57:5:57:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:57:5:57:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:67:5:67:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:67:5:67:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:77:5:77:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:77:5:77:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:82:5:82:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:82:5:82:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:87:5:87:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:87:5:87:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:97:5:97:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:97:5:97:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module test | test.py:42:5:42:10 | ControlFlowNode for SINK_F | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module test | test.py:42:5:42:10 | ControlFlowNode for SINK_F | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:49:19:49:24 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:49:19:49:24 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:56:18:56:23 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:56:18:56:23 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:61:9:61:14 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:61:9:61:14 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:71:9:71:14 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:71:9:71:14 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:81:17:81:22 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:81:17:81:22 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:86:21:86:26 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:86:21:86:26 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:97:33:97:38 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:97:33:97:38 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | test.py:97:10:97:31 | ControlFlowNode for fields_with_local_flow | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | test.py:97:10:97:31 | ControlFlowNode for fields_with_local_flow | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable is_source in Module test | test.py:11:8:11:16 | ControlFlowNode for is_source | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable is_source in Module test | test.py:11:8:11:16 | ControlFlowNode for is_source | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable is_source in Module test | test.py:18:8:18:16 | ControlFlowNode for is_source | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable is_source in Module test | test.py:18:8:18:16 | ControlFlowNode for is_source | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable print in Module test | test.py:12:9:12:13 | ControlFlowNode for print | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable print in Module test | test.py:12:9:12:13 | ControlFlowNode for print | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable print in Module test | test.py:14:9:14:13 | ControlFlowNode for print | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable print in Module test | test.py:14:9:14:13 | ControlFlowNode for print | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable print in Module test | test.py:19:9:19:13 | ControlFlowNode for print | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable print in Module test | test.py:19:9:19:13 | ControlFlowNode for print | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable print in Module test | test.py:21:9:21:13 | ControlFlowNode for print | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable print in Module test | test.py:21:9:21:13 | ControlFlowNode for print | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | test.py:49:5:49:10 | ControlFlowNode for setFoo | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | test.py:49:5:49:10 | ControlFlowNode for setFoo | +| test.py:0:0:0:0 | ModuleVariableNode for test.MyObj | test.py:35:20:35:24 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for test.MyObj | test.py:35:20:35:24 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for test.MyObj | test.py:47:13:47:17 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for test.MyObj | test.py:47:13:47:17 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for test.MyObj | test.py:54:13:54:17 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for test.MyObj | test.py:54:13:54:17 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for test.MyObj | test.py:81:11:81:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for test.MyObj | test.py:81:11:81:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for test.MyObj | test.py:86:11:86:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for test.MyObj | test.py:86:11:86:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for test.MyObj | test.py:91:11:91:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for test.MyObj | test.py:91:11:91:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for test.NestedObj | test.py:63:9:63:17 | ControlFlowNode for NestedObj | +| test.py:0:0:0:0 | ModuleVariableNode for test.NestedObj | test.py:63:9:63:17 | ControlFlowNode for NestedObj | +| test.py:0:0:0:0 | ModuleVariableNode for test.NestedObj | test.py:73:9:73:17 | ControlFlowNode for NestedObj | +| test.py:0:0:0:0 | ModuleVariableNode for test.NestedObj | test.py:73:9:73:17 | ControlFlowNode for NestedObj | +| test.py:0:0:0:0 | ModuleVariableNode for test.SINK | test.py:50:5:50:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for test.SINK | test.py:50:5:50:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for test.SINK | test.py:57:5:57:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for test.SINK | test.py:57:5:57:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for test.SINK | test.py:67:5:67:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for test.SINK | test.py:67:5:67:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for test.SINK | test.py:77:5:77:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for test.SINK | test.py:77:5:77:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for test.SINK | test.py:82:5:82:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for test.SINK | test.py:82:5:82:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for test.SINK | test.py:87:5:87:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for test.SINK | test.py:87:5:87:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for test.SINK | test.py:97:5:97:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for test.SINK | test.py:97:5:97:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for test.SINK_F | test.py:42:5:42:10 | ControlFlowNode for SINK_F | +| test.py:0:0:0:0 | ModuleVariableNode for test.SINK_F | test.py:42:5:42:10 | ControlFlowNode for SINK_F | +| test.py:0:0:0:0 | ModuleVariableNode for test.SOURCE | test.py:49:19:49:24 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for test.SOURCE | test.py:49:19:49:24 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for test.SOURCE | test.py:56:18:56:23 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for test.SOURCE | test.py:56:18:56:23 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for test.SOURCE | test.py:61:9:61:14 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for test.SOURCE | test.py:61:9:61:14 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for test.SOURCE | test.py:71:9:71:14 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for test.SOURCE | test.py:71:9:71:14 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for test.SOURCE | test.py:81:17:81:22 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for test.SOURCE | test.py:81:17:81:22 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for test.SOURCE | test.py:86:21:86:26 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for test.SOURCE | test.py:86:21:86:26 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for test.SOURCE | test.py:97:33:97:38 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for test.SOURCE | test.py:97:33:97:38 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for test.fields_with_local_flow | test.py:97:10:97:31 | ControlFlowNode for fields_with_local_flow | +| test.py:0:0:0:0 | ModuleVariableNode for test.fields_with_local_flow | test.py:97:10:97:31 | ControlFlowNode for fields_with_local_flow | +| test.py:0:0:0:0 | ModuleVariableNode for test.is_source | test.py:11:8:11:16 | ControlFlowNode for is_source | +| test.py:0:0:0:0 | ModuleVariableNode for test.is_source | test.py:11:8:11:16 | ControlFlowNode for is_source | +| test.py:0:0:0:0 | ModuleVariableNode for test.is_source | test.py:18:8:18:16 | ControlFlowNode for is_source | +| test.py:0:0:0:0 | ModuleVariableNode for test.is_source | test.py:18:8:18:16 | ControlFlowNode for is_source | +| test.py:0:0:0:0 | ModuleVariableNode for test.print | test.py:12:9:12:13 | ControlFlowNode for print | +| test.py:0:0:0:0 | ModuleVariableNode for test.print | test.py:12:9:12:13 | ControlFlowNode for print | +| test.py:0:0:0:0 | ModuleVariableNode for test.print | test.py:14:9:14:13 | ControlFlowNode for print | +| test.py:0:0:0:0 | ModuleVariableNode for test.print | test.py:14:9:14:13 | ControlFlowNode for print | +| test.py:0:0:0:0 | ModuleVariableNode for test.print | test.py:19:9:19:13 | ControlFlowNode for print | +| test.py:0:0:0:0 | ModuleVariableNode for test.print | test.py:19:9:19:13 | ControlFlowNode for print | +| test.py:0:0:0:0 | ModuleVariableNode for test.print | test.py:21:9:21:13 | ControlFlowNode for print | +| test.py:0:0:0:0 | ModuleVariableNode for test.print | test.py:21:9:21:13 | ControlFlowNode for print | +| test.py:0:0:0:0 | ModuleVariableNode for test.setFoo | test.py:49:5:49:10 | ControlFlowNode for setFoo | +| test.py:0:0:0:0 | ModuleVariableNode for test.setFoo | test.py:49:5:49:10 | ControlFlowNode for setFoo | | test.py:2:13:2:26 | ControlFlowNode for Str | test.py:2:1:2:9 | GSSA Variable NONSOURCE | | test.py:2:13:2:26 | ControlFlowNode for Str | test.py:2:1:2:9 | GSSA Variable NONSOURCE | -| test.py:3:1:3:6 | GSSA Variable SOURCE | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | -| test.py:3:1:3:6 | GSSA Variable SOURCE | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | +| test.py:3:1:3:6 | ControlFlowNode for SOURCE | test.py:0:0:0:0 | ModuleVariableNode for test.SOURCE | +| test.py:3:1:3:6 | ControlFlowNode for SOURCE | test.py:0:0:0:0 | ModuleVariableNode for test.SOURCE | +| test.py:3:1:3:6 | GSSA Variable SOURCE | test.py:0:0:0:0 | ModuleVariableNode for test.SOURCE | +| test.py:3:1:3:6 | GSSA Variable SOURCE | test.py:0:0:0:0 | ModuleVariableNode for test.SOURCE | | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:3:1:3:6 | GSSA Variable SOURCE | | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:3:1:3:6 | GSSA Variable SOURCE | | test.py:6:1:6:17 | ControlFlowNode for FunctionExpr | test.py:6:5:6:13 | GSSA Variable is_source | | test.py:6:1:6:17 | ControlFlowNode for FunctionExpr | test.py:6:5:6:13 | GSSA Variable is_source | -| test.py:6:5:6:13 | GSSA Variable is_source | test.py:0:0:0:0 | ModuleVariableNode for Global Variable is_source in Module test | -| test.py:6:5:6:13 | GSSA Variable is_source | test.py:0:0:0:0 | ModuleVariableNode for Global Variable is_source in Module test | +| test.py:6:5:6:13 | ControlFlowNode for is_source | test.py:0:0:0:0 | ModuleVariableNode for test.is_source | +| test.py:6:5:6:13 | ControlFlowNode for is_source | test.py:0:0:0:0 | ModuleVariableNode for test.is_source | +| test.py:6:5:6:13 | GSSA Variable is_source | test.py:0:0:0:0 | ModuleVariableNode for test.is_source | +| test.py:6:5:6:13 | GSSA Variable is_source | test.py:0:0:0:0 | ModuleVariableNode for test.is_source | | test.py:6:15:6:15 | ControlFlowNode for x | test.py:6:15:6:15 | SSA variable x | | test.py:6:15:6:15 | ControlFlowNode for x | test.py:6:15:6:15 | SSA variable x | | test.py:6:15:6:15 | ControlFlowNode for x | test.py:6:15:6:15 | SSA variable x | @@ -602,6 +608,8 @@ | test.py:7:12:7:12 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | | test.py:7:12:7:12 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | | test.py:7:12:7:12 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:12:7:24 | ControlFlowNode for Compare | test.py:7:12:7:78 | ControlFlowNode for BoolExpr | +| test.py:7:12:7:24 | ControlFlowNode for Compare | test.py:7:12:7:78 | ControlFlowNode for BoolExpr | | test.py:7:12:7:78 | ControlFlowNode for BoolExpr | test.py:11:8:11:19 | ControlFlowNode for is_source() | | test.py:7:12:7:78 | ControlFlowNode for BoolExpr | test.py:11:8:11:19 | ControlFlowNode for is_source() | | test.py:7:12:7:78 | ControlFlowNode for BoolExpr | test.py:18:8:18:19 | ControlFlowNode for is_source() | @@ -618,6 +626,8 @@ | test.py:7:29:7:29 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | | test.py:7:29:7:29 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | | test.py:7:29:7:29 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:29:7:42 | ControlFlowNode for Compare | test.py:7:12:7:78 | ControlFlowNode for BoolExpr | +| test.py:7:29:7:42 | ControlFlowNode for Compare | test.py:7:12:7:78 | ControlFlowNode for BoolExpr | | test.py:7:47:7:47 | ControlFlowNode for x | test.py:7:58:7:58 | ControlFlowNode for x | | test.py:7:47:7:47 | ControlFlowNode for x | test.py:7:58:7:58 | ControlFlowNode for x | | test.py:7:47:7:47 | ControlFlowNode for x | test.py:7:58:7:58 | ControlFlowNode for x | @@ -626,16 +636,24 @@ | test.py:7:47:7:47 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | | test.py:7:47:7:47 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | | test.py:7:47:7:47 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:47:7:53 | ControlFlowNode for Compare | test.py:7:12:7:78 | ControlFlowNode for BoolExpr | +| test.py:7:47:7:53 | ControlFlowNode for Compare | test.py:7:12:7:78 | ControlFlowNode for BoolExpr | | test.py:7:58:7:58 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | | test.py:7:58:7:58 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | | test.py:7:58:7:58 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | | test.py:7:58:7:58 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:58:7:66 | ControlFlowNode for Compare | test.py:7:12:7:78 | ControlFlowNode for BoolExpr | +| test.py:7:58:7:66 | ControlFlowNode for Compare | test.py:7:12:7:78 | ControlFlowNode for BoolExpr | +| test.py:7:71:7:78 | ControlFlowNode for Compare | test.py:7:12:7:78 | ControlFlowNode for BoolExpr | +| test.py:7:71:7:78 | ControlFlowNode for Compare | test.py:7:12:7:78 | ControlFlowNode for BoolExpr | | test.py:10:1:10:12 | ControlFlowNode for FunctionExpr | test.py:10:5:10:8 | GSSA Variable SINK | | test.py:10:1:10:12 | ControlFlowNode for FunctionExpr | test.py:10:5:10:8 | GSSA Variable SINK | | test.py:10:1:10:12 | GSSA Variable is_source | test.py:11:8:11:16 | ControlFlowNode for is_source | | test.py:10:1:10:12 | GSSA Variable is_source | test.py:11:8:11:16 | ControlFlowNode for is_source | -| test.py:10:5:10:8 | GSSA Variable SINK | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | -| test.py:10:5:10:8 | GSSA Variable SINK | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | +| test.py:10:5:10:8 | ControlFlowNode for SINK | test.py:0:0:0:0 | ModuleVariableNode for test.SINK | +| test.py:10:5:10:8 | ControlFlowNode for SINK | test.py:0:0:0:0 | ModuleVariableNode for test.SINK | +| test.py:10:5:10:8 | GSSA Variable SINK | test.py:0:0:0:0 | ModuleVariableNode for test.SINK | +| test.py:10:5:10:8 | GSSA Variable SINK | test.py:0:0:0:0 | ModuleVariableNode for test.SINK | | test.py:10:10:10:10 | ControlFlowNode for x | test.py:10:10:10:10 | SSA variable x | | test.py:10:10:10:10 | ControlFlowNode for x | test.py:10:10:10:10 | SSA variable x | | test.py:10:10:10:10 | ControlFlowNode for x | test.py:10:10:10:10 | SSA variable x | @@ -684,8 +702,10 @@ | test.py:17:1:17:14 | ControlFlowNode for FunctionExpr | test.py:17:5:17:10 | GSSA Variable SINK_F | | test.py:17:1:17:14 | GSSA Variable is_source | test.py:18:8:18:16 | ControlFlowNode for is_source | | test.py:17:1:17:14 | GSSA Variable is_source | test.py:18:8:18:16 | ControlFlowNode for is_source | -| test.py:17:5:17:10 | GSSA Variable SINK_F | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module test | -| test.py:17:5:17:10 | GSSA Variable SINK_F | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module test | +| test.py:17:5:17:10 | ControlFlowNode for SINK_F | test.py:0:0:0:0 | ModuleVariableNode for test.SINK_F | +| test.py:17:5:17:10 | ControlFlowNode for SINK_F | test.py:0:0:0:0 | ModuleVariableNode for test.SINK_F | +| test.py:17:5:17:10 | GSSA Variable SINK_F | test.py:0:0:0:0 | ModuleVariableNode for test.SINK_F | +| test.py:17:5:17:10 | GSSA Variable SINK_F | test.py:0:0:0:0 | ModuleVariableNode for test.SINK_F | | test.py:17:12:17:12 | ControlFlowNode for x | test.py:17:12:17:12 | SSA variable x | | test.py:17:12:17:12 | ControlFlowNode for x | test.py:17:12:17:12 | SSA variable x | | test.py:17:12:17:12 | ControlFlowNode for x | test.py:17:12:17:12 | SSA variable x | @@ -722,8 +742,10 @@ | test.py:19:34:19:34 | [post arg] ControlFlowNode for x | test.py:42:12:42:18 | [post arg] ControlFlowNode for Attribute | | test.py:25:1:25:20 | ControlFlowNode for ClassExpr | test.py:25:7:25:11 | GSSA Variable MyObj | | test.py:25:1:25:20 | ControlFlowNode for ClassExpr | test.py:25:7:25:11 | GSSA Variable MyObj | -| test.py:25:7:25:11 | GSSA Variable MyObj | test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | -| test.py:25:7:25:11 | GSSA Variable MyObj | test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | +| test.py:25:7:25:11 | ControlFlowNode for MyObj | test.py:0:0:0:0 | ModuleVariableNode for test.MyObj | +| test.py:25:7:25:11 | ControlFlowNode for MyObj | test.py:0:0:0:0 | ModuleVariableNode for test.MyObj | +| test.py:25:7:25:11 | GSSA Variable MyObj | test.py:0:0:0:0 | ModuleVariableNode for test.MyObj | +| test.py:25:7:25:11 | GSSA Variable MyObj | test.py:0:0:0:0 | ModuleVariableNode for test.MyObj | | test.py:25:13:25:18 | ControlFlowNode for object | test.py:33:17:33:22 | ControlFlowNode for object | | test.py:25:13:25:18 | ControlFlowNode for object | test.py:33:17:33:22 | ControlFlowNode for object | | test.py:26:5:26:28 | ControlFlowNode for FunctionExpr | test.py:26:9:26:16 | SSA variable __init__ | @@ -805,8 +827,10 @@ | test.py:30:20:30:22 | ControlFlowNode for foo | test.py:30:9:30:12 | [post store] ControlFlowNode for self [Attribute foo] | | test.py:33:1:33:24 | ControlFlowNode for ClassExpr | test.py:33:7:33:15 | GSSA Variable NestedObj | | test.py:33:1:33:24 | ControlFlowNode for ClassExpr | test.py:33:7:33:15 | GSSA Variable NestedObj | -| test.py:33:7:33:15 | GSSA Variable NestedObj | test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | -| test.py:33:7:33:15 | GSSA Variable NestedObj | test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | +| test.py:33:7:33:15 | ControlFlowNode for NestedObj | test.py:0:0:0:0 | ModuleVariableNode for test.NestedObj | +| test.py:33:7:33:15 | ControlFlowNode for NestedObj | test.py:0:0:0:0 | ModuleVariableNode for test.NestedObj | +| test.py:33:7:33:15 | GSSA Variable NestedObj | test.py:0:0:0:0 | ModuleVariableNode for test.NestedObj | +| test.py:33:7:33:15 | GSSA Variable NestedObj | test.py:0:0:0:0 | ModuleVariableNode for test.NestedObj | | test.py:34:5:34:23 | ControlFlowNode for FunctionExpr | test.py:34:9:34:16 | SSA variable __init__ | | test.py:34:5:34:23 | ControlFlowNode for FunctionExpr | test.py:34:9:34:16 | SSA variable __init__ | | test.py:34:5:34:23 | GSSA Variable MyObj | test.py:35:20:35:24 | ControlFlowNode for MyObj | @@ -865,8 +889,10 @@ | test.py:41:1:41:19 | ControlFlowNode for FunctionExpr | test.py:41:5:41:10 | GSSA Variable setFoo | | test.py:41:1:41:19 | GSSA Variable SINK_F | test.py:42:5:42:10 | ControlFlowNode for SINK_F | | test.py:41:1:41:19 | GSSA Variable SINK_F | test.py:42:5:42:10 | ControlFlowNode for SINK_F | -| test.py:41:5:41:10 | GSSA Variable setFoo | test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | -| test.py:41:5:41:10 | GSSA Variable setFoo | test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | +| test.py:41:5:41:10 | ControlFlowNode for setFoo | test.py:0:0:0:0 | ModuleVariableNode for test.setFoo | +| test.py:41:5:41:10 | ControlFlowNode for setFoo | test.py:0:0:0:0 | ModuleVariableNode for test.setFoo | +| test.py:41:5:41:10 | GSSA Variable setFoo | test.py:0:0:0:0 | ModuleVariableNode for test.setFoo | +| test.py:41:5:41:10 | GSSA Variable setFoo | test.py:0:0:0:0 | ModuleVariableNode for test.setFoo | | test.py:41:12:41:14 | ControlFlowNode for obj | test.py:41:12:41:14 | SSA variable obj | | test.py:41:12:41:14 | ControlFlowNode for obj | test.py:41:12:41:14 | SSA variable obj | | test.py:41:12:41:14 | ControlFlowNode for obj | test.py:41:12:41:14 | SSA variable obj | @@ -1191,8 +1217,10 @@ | test.py:90:1:90:30 | ControlFlowNode for FunctionExpr | test.py:90:5:90:26 | GSSA Variable fields_with_local_flow | | test.py:90:1:90:30 | GSSA Variable MyObj | test.py:91:11:91:15 | ControlFlowNode for MyObj | | test.py:90:1:90:30 | GSSA Variable MyObj | test.py:91:11:91:15 | ControlFlowNode for MyObj | -| test.py:90:5:90:26 | GSSA Variable fields_with_local_flow | test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | -| test.py:90:5:90:26 | GSSA Variable fields_with_local_flow | test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | +| test.py:90:5:90:26 | ControlFlowNode for fields_with_local_flow | test.py:0:0:0:0 | ModuleVariableNode for test.fields_with_local_flow | +| test.py:90:5:90:26 | ControlFlowNode for fields_with_local_flow | test.py:0:0:0:0 | ModuleVariableNode for test.fields_with_local_flow | +| test.py:90:5:90:26 | GSSA Variable fields_with_local_flow | test.py:0:0:0:0 | ModuleVariableNode for test.fields_with_local_flow | +| test.py:90:5:90:26 | GSSA Variable fields_with_local_flow | test.py:0:0:0:0 | ModuleVariableNode for test.fields_with_local_flow | | test.py:90:28:90:28 | ControlFlowNode for x | test.py:90:28:90:28 | SSA variable x | | test.py:90:28:90:28 | ControlFlowNode for x | test.py:90:28:90:28 | SSA variable x | | test.py:90:28:90:28 | ControlFlowNode for x | test.py:90:28:90:28 | SSA variable x | diff --git a/python/ql/test/experimental/dataflow/module-initialization/base.py b/python/ql/test/experimental/dataflow/module-initialization/base.py new file mode 100644 index 00000000000..611e0a87735 --- /dev/null +++ b/python/ql/test/experimental/dataflow/module-initialization/base.py @@ -0,0 +1 @@ +foo = 3 diff --git a/python/ql/test/experimental/dataflow/module-initialization/localFlow.expected b/python/ql/test/experimental/dataflow/module-initialization/localFlow.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/experimental/dataflow/module-initialization/localFlow.ql b/python/ql/test/experimental/dataflow/module-initialization/localFlow.ql new file mode 100644 index 00000000000..635902e7045 --- /dev/null +++ b/python/ql/test/experimental/dataflow/module-initialization/localFlow.ql @@ -0,0 +1,36 @@ +// This query should be more focused yet. +import python +import experimental.dataflow.TestUtil.FlowTest +private import semmle.python.dataflow.new.internal.PrintNode +private import semmle.python.dataflow.new.internal.DataFlowPrivate as DP + +class ImportTimeLocalFlowTest extends FlowTest { + ImportTimeLocalFlowTest() { this = "ImportTimeLocalFlowTest" } + + override string flowTag() { result = "importTimeFlow" } + + override predicate relevantFlow(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + nodeFrom.getLocation().getFile().getBaseName() = "multiphase.py" and + // results are displayed next to `nodeTo`, so we need a line to write on + nodeTo.getLocation().getStartLine() > 0 and + nodeTo.asVar() instanceof GlobalSsaVariable and + DP::importTimeLocalFlowStep(nodeFrom, nodeTo) + } +} + +class RuntimeLocalFlowTest extends FlowTest { + RuntimeLocalFlowTest() { this = "RuntimeLocalFlowTest" } + + override string flowTag() { result = "runtimeFlow" } + + override predicate relevantFlow(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + nodeFrom.getLocation().getFile().getBaseName() = "multiphase.py" and + // results are displayed next to `nodeTo`, so we need a line to write on + nodeTo.getLocation().getStartLine() > 0 and + ( + nodeFrom instanceof DataFlow::ModuleVariableNode or + nodeTo instanceof DataFlow::ModuleVariableNode + ) and + DP::runtimeJumpStep(nodeFrom, nodeTo) + } +} diff --git a/python/ql/test/experimental/dataflow/module-initialization/m1.py b/python/ql/test/experimental/dataflow/module-initialization/m1.py new file mode 100644 index 00000000000..653dfb91997 --- /dev/null +++ b/python/ql/test/experimental/dataflow/module-initialization/m1.py @@ -0,0 +1,10 @@ +# constant +foo = 42 + +import base + +def passOn(x): + return x + +# depends on other constant +bar = passOn(base.foo) diff --git a/python/ql/test/experimental/dataflow/module-initialization/multiphase.py b/python/ql/test/experimental/dataflow/module-initialization/multiphase.py new file mode 100644 index 00000000000..1be7a55ff6b --- /dev/null +++ b/python/ql/test/experimental/dataflow/module-initialization/multiphase.py @@ -0,0 +1,42 @@ +import sys #$ importTimeFlow="ImportExpr -> GSSA Variable sys" +import os #$ importTimeFlow="ImportExpr -> GSSA Variable os" + +sys.path.append(os.path.dirname(os.path.dirname((__file__)))) +from testlib import * + +# These are defined so that we can evaluate the test code. +NONSOURCE = "not a source" #$ importTimeFlow="'not a source' -> GSSA Variable NONSOURCE" +SOURCE = "source" #$ importTimeFlow="'source' -> GSSA Variable SOURCE" + + +def is_source(x): #$ importTimeFlow="FunctionExpr -> GSSA Variable is_source" + return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j + + +def SINK(x): #$ importTimeFlow="FunctionExpr -> GSSA Variable SINK" + if is_source(x): #$ runtimeFlow="ModuleVariableNode for multiphase.is_source, l:-17 -> is_source" + print("OK") #$ runtimeFlow="ModuleVariableNode for multiphase.print, l:-18 -> print" + else: + print("Unexpected flow", x) #$ runtimeFlow="ModuleVariableNode for multiphase.print, l:-20 -> print" + + +def SINK_F(x): #$ importTimeFlow="FunctionExpr -> GSSA Variable SINK_F" + if is_source(x): #$ runtimeFlow="ModuleVariableNode for multiphase.is_source, l:-24 -> is_source" + print("Unexpected flow", x) #$ runtimeFlow="ModuleVariableNode for multiphase.print, l:-25 -> print" + else: + print("OK") #$ runtimeFlow="ModuleVariableNode for multiphase.print, l:-27 -> print" + +def set_foo(): #$ importTimeFlow="FunctionExpr -> GSSA Variable set_foo" + global foo + foo = SOURCE #$ runtimeFlow="ModuleVariableNode for multiphase.SOURCE, l:-31 -> SOURCE" # missing final definition of foo + +foo = NONSOURCE #$ importTimeFlow="NONSOURCE -> GSSA Variable foo" +set_foo() + +@expects(2) +def test_phases(): #$ importTimeFlow="expects(..)(..), l:-1 -> GSSA Variable test_phases" + global foo + SINK(foo) #$ runtimeFlow="ModuleVariableNode for multiphase.SINK, l:-39 -> SINK" runtimeFlow="ModuleVariableNode for multiphase.foo, l:-39 -> foo" + foo = NONSOURCE #$ runtimeFlow="ModuleVariableNode for multiphase.NONSOURCE, l:-40 -> NONSOURCE" + set_foo() #$ runtimeFlow="ModuleVariableNode for multiphase.set_foo, l:-41 -> set_foo" + SINK(foo) #$ runtimeFlow="ModuleVariableNode for multiphase.SINK, l:-42 -> SINK" runtimeFlow="ModuleVariableNode for multiphase.foo, l:-42 -> foo" diff --git a/python/ql/test/experimental/dataflow/module-initialization/test.py b/python/ql/test/experimental/dataflow/module-initialization/test.py new file mode 100644 index 00000000000..83531bdd92f --- /dev/null +++ b/python/ql/test/experimental/dataflow/module-initialization/test.py @@ -0,0 +1,33 @@ +# These are defined so that we can evaluate the test code. +NONSOURCE = "not a source" +SOURCE = "source" + + +def is_source(x): + return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j + + +def SINK(x): + if is_source(x): + print("OK") + else: + print("Unexpected flow", x) + + +def SINK_F(x): + if is_source(x): + print("Unexpected flow", x) + else: + print("OK") + +import base + +base.foo = 42 + +import m1 + +def test_const(): + SINK(m1.foo) + +def test_overwritten(): + SINK(m1.bar) diff --git a/python/ql/test/experimental/dataflow/module-initialization/testOnce.py b/python/ql/test/experimental/dataflow/module-initialization/testOnce.py new file mode 100644 index 00000000000..663f8acb997 --- /dev/null +++ b/python/ql/test/experimental/dataflow/module-initialization/testOnce.py @@ -0,0 +1,33 @@ +# These are defined so that we can evaluate the test code. +NONSOURCE = "not a source" +SOURCE = "source" + + +def is_source(x): + return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j + + +def SINK(x): + if is_source(x): + print("OK") + else: + print("Unexpected flow", x) + + +def SINK_F(x): + if is_source(x): + print("Unexpected flow", x) + else: + print("OK") + +import m1 + +import base + +base.foo = 42 + +def test_const(): + SINK(m1.foo) + +def test_unoverwritten(): + SINK_F(m1.bar) diff --git a/python/ql/test/experimental/dataflow/strange-essaflow/testFlow.expected b/python/ql/test/experimental/dataflow/strange-essaflow/testFlow.expected index 2d35c297fc0..81afa22a94d 100644 --- a/python/ql/test/experimental/dataflow/strange-essaflow/testFlow.expected +++ b/python/ql/test/experimental/dataflow/strange-essaflow/testFlow.expected @@ -2,5 +2,5 @@ os_import | test.py:2:8:2:9 | GSSA Variable os | flowstep jumpStep -| test.py:2:8:2:9 | GSSA Variable os | test.py:0:0:0:0 | ModuleVariableNode for Global Variable os in Module test | +| test.py:2:8:2:9 | GSSA Variable os | test.py:0:0:0:0 | ModuleVariableNode for test.os | essaFlowStep diff --git a/python/ql/test/experimental/dataflow/typetracking/moduleattr.expected b/python/ql/test/experimental/dataflow/typetracking/moduleattr.expected index adfc8c5a379..9720d759aa0 100644 --- a/python/ql/test/experimental/dataflow/typetracking/moduleattr.expected +++ b/python/ql/test/experimental/dataflow/typetracking/moduleattr.expected @@ -1,7 +1,7 @@ module_tracker | import_as_attr.py:1:6:1:11 | ControlFlowNode for ImportExpr | module_attr_tracker -| import_as_attr.py:0:0:0:0 | ModuleVariableNode for Global Variable attr_ref in Module import_as_attr | +| import_as_attr.py:0:0:0:0 | ModuleVariableNode for import_as_attr.attr_ref | | import_as_attr.py:1:20:1:35 | ControlFlowNode for ImportMember | | import_as_attr.py:1:28:1:35 | GSSA Variable attr_ref | | import_as_attr.py:3:1:3:1 | GSSA Variable x | diff --git a/python/ql/test/experimental/dataflow/validTest.py b/python/ql/test/experimental/dataflow/validTest.py index 2367344580b..aaa0e792f2c 100644 --- a/python/ql/test/experimental/dataflow/validTest.py +++ b/python/ql/test/experimental/dataflow/validTest.py @@ -56,3 +56,7 @@ if __name__ == "__main__": check_tests_valid("variable-capture.in") check_tests_valid("variable-capture.nonlocal") check_tests_valid("variable-capture.dict") + check_tests_valid("module-initialization.multiphase") + # The below fails when trying to import modules + # check_tests_valid("module-initialization.test") + # check_tests_valid("module-initialization.testOnce") diff --git a/python/ql/test/experimental/meta/ConceptsTest.qll b/python/ql/test/experimental/meta/ConceptsTest.qll index de53f053eb9..3ba52ddabde 100644 --- a/python/ql/test/experimental/meta/ConceptsTest.qll +++ b/python/ql/test/experimental/meta/ConceptsTest.qll @@ -96,7 +96,7 @@ class EncodingTest extends InlineExpectationsTest { class LoggingTest extends InlineExpectationsTest { LoggingTest() { this = "LoggingTest" } - override string getARelevantTag() { result in ["loggingInput"] } + override string getARelevantTag() { result = "loggingInput" } override predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and @@ -181,7 +181,7 @@ class EscapingTest extends InlineExpectationsTest { class HttpServerRouteSetupTest extends InlineExpectationsTest { HttpServerRouteSetupTest() { this = "HttpServerRouteSetupTest" } - override string getARelevantTag() { result in ["routeSetup"] } + override string getARelevantTag() { result = "routeSetup" } override predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and diff --git a/python/ql/test/experimental/query-tests/Security/CWE-113/HeaderInjection.expected b/python/ql/test/experimental/query-tests/Security/CWE-113/HeaderInjection.expected new file mode 100644 index 00000000000..48fc4dfab07 --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-113/HeaderInjection.expected @@ -0,0 +1,50 @@ +edges +| django_bad.py:5:18:5:58 | ControlFlowNode for Attribute() | django_bad.py:7:40:7:49 | ControlFlowNode for rfs_header | +| django_bad.py:12:18:12:58 | ControlFlowNode for Attribute() | django_bad.py:14:30:14:39 | ControlFlowNode for rfs_header | +| flask_bad.py:9:18:9:24 | ControlFlowNode for request | flask_bad.py:9:18:9:29 | ControlFlowNode for Attribute | +| flask_bad.py:9:18:9:29 | ControlFlowNode for Attribute | flask_bad.py:9:18:9:43 | ControlFlowNode for Subscript | +| flask_bad.py:9:18:9:43 | ControlFlowNode for Subscript | flask_bad.py:12:31:12:40 | ControlFlowNode for rfs_header | +| flask_bad.py:19:18:19:24 | ControlFlowNode for request | flask_bad.py:19:18:19:29 | ControlFlowNode for Attribute | +| flask_bad.py:19:18:19:29 | ControlFlowNode for Attribute | flask_bad.py:19:18:19:43 | ControlFlowNode for Subscript | +| flask_bad.py:19:18:19:43 | ControlFlowNode for Subscript | flask_bad.py:21:38:21:47 | ControlFlowNode for rfs_header | +| flask_bad.py:27:18:27:24 | ControlFlowNode for request | flask_bad.py:27:18:27:29 | ControlFlowNode for Attribute | +| flask_bad.py:27:18:27:29 | ControlFlowNode for Attribute | flask_bad.py:27:18:27:43 | ControlFlowNode for Subscript | +| flask_bad.py:27:18:27:43 | ControlFlowNode for Subscript | flask_bad.py:29:34:29:43 | ControlFlowNode for rfs_header | +| flask_bad.py:35:18:35:24 | ControlFlowNode for request | flask_bad.py:35:18:35:29 | ControlFlowNode for Attribute | +| flask_bad.py:35:18:35:29 | ControlFlowNode for Attribute | flask_bad.py:35:18:35:43 | ControlFlowNode for Subscript | +| flask_bad.py:35:18:35:43 | ControlFlowNode for Subscript | flask_bad.py:38:24:38:33 | ControlFlowNode for rfs_header | +| flask_bad.py:44:44:44:50 | ControlFlowNode for request | flask_bad.py:44:44:44:55 | ControlFlowNode for Attribute | +| flask_bad.py:44:44:44:55 | ControlFlowNode for Attribute | flask_bad.py:44:44:44:69 | ControlFlowNode for Subscript | +nodes +| django_bad.py:5:18:5:58 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| django_bad.py:7:40:7:49 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header | +| django_bad.py:12:18:12:58 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| django_bad.py:14:30:14:39 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header | +| flask_bad.py:9:18:9:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| flask_bad.py:9:18:9:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| flask_bad.py:9:18:9:43 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| flask_bad.py:12:31:12:40 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header | +| flask_bad.py:19:18:19:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| flask_bad.py:19:18:19:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| flask_bad.py:19:18:19:43 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| flask_bad.py:21:38:21:47 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header | +| flask_bad.py:27:18:27:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| flask_bad.py:27:18:27:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| flask_bad.py:27:18:27:43 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| flask_bad.py:29:34:29:43 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header | +| flask_bad.py:35:18:35:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| flask_bad.py:35:18:35:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| flask_bad.py:35:18:35:43 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| flask_bad.py:38:24:38:33 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header | +| flask_bad.py:44:44:44:50 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| flask_bad.py:44:44:44:55 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| flask_bad.py:44:44:44:69 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +subpaths +#select +| django_bad.py:7:40:7:49 | ControlFlowNode for rfs_header | django_bad.py:5:18:5:58 | ControlFlowNode for Attribute() | django_bad.py:7:40:7:49 | ControlFlowNode for rfs_header | $@ HTTP header is constructed from a $@. | django_bad.py:7:40:7:49 | ControlFlowNode for rfs_header | This | django_bad.py:5:18:5:58 | ControlFlowNode for Attribute() | user-provided value | +| django_bad.py:14:30:14:39 | ControlFlowNode for rfs_header | django_bad.py:12:18:12:58 | ControlFlowNode for Attribute() | django_bad.py:14:30:14:39 | ControlFlowNode for rfs_header | $@ HTTP header is constructed from a $@. | django_bad.py:14:30:14:39 | ControlFlowNode for rfs_header | This | django_bad.py:12:18:12:58 | ControlFlowNode for Attribute() | user-provided value | +| flask_bad.py:12:31:12:40 | ControlFlowNode for rfs_header | flask_bad.py:9:18:9:24 | ControlFlowNode for request | flask_bad.py:12:31:12:40 | ControlFlowNode for rfs_header | $@ HTTP header is constructed from a $@. | flask_bad.py:12:31:12:40 | ControlFlowNode for rfs_header | This | flask_bad.py:9:18:9:24 | ControlFlowNode for request | user-provided value | +| flask_bad.py:21:38:21:47 | ControlFlowNode for rfs_header | flask_bad.py:19:18:19:24 | ControlFlowNode for request | flask_bad.py:21:38:21:47 | ControlFlowNode for rfs_header | $@ HTTP header is constructed from a $@. | flask_bad.py:21:38:21:47 | ControlFlowNode for rfs_header | This | flask_bad.py:19:18:19:24 | ControlFlowNode for request | user-provided value | +| flask_bad.py:29:34:29:43 | ControlFlowNode for rfs_header | flask_bad.py:27:18:27:24 | ControlFlowNode for request | flask_bad.py:29:34:29:43 | ControlFlowNode for rfs_header | $@ HTTP header is constructed from a $@. | flask_bad.py:29:34:29:43 | ControlFlowNode for rfs_header | This | flask_bad.py:27:18:27:24 | ControlFlowNode for request | user-provided value | +| flask_bad.py:38:24:38:33 | ControlFlowNode for rfs_header | flask_bad.py:35:18:35:24 | ControlFlowNode for request | flask_bad.py:38:24:38:33 | ControlFlowNode for rfs_header | $@ HTTP header is constructed from a $@. | flask_bad.py:38:24:38:33 | ControlFlowNode for rfs_header | This | flask_bad.py:35:18:35:24 | ControlFlowNode for request | user-provided value | +| flask_bad.py:44:44:44:69 | ControlFlowNode for Subscript | flask_bad.py:44:44:44:50 | ControlFlowNode for request | flask_bad.py:44:44:44:69 | ControlFlowNode for Subscript | $@ HTTP header is constructed from a $@. | flask_bad.py:44:44:44:69 | ControlFlowNode for Subscript | This | flask_bad.py:44:44:44:50 | ControlFlowNode for request | user-provided value | diff --git a/python/ql/test/experimental/query-tests/Security/CWE-113/HeaderInjection.qlref b/python/ql/test/experimental/query-tests/Security/CWE-113/HeaderInjection.qlref new file mode 100644 index 00000000000..915175a7b6a --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-113/HeaderInjection.qlref @@ -0,0 +1 @@ +experimental/Security/CWE-113/HeaderInjection.ql diff --git a/python/ql/test/experimental/query-tests/Security/CWE-113/django_bad.py b/python/ql/test/experimental/query-tests/Security/CWE-113/django_bad.py new file mode 100644 index 00000000000..972b3344a50 --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-113/django_bad.py @@ -0,0 +1,15 @@ +import django.http + + +def django_setitem(): + rfs_header = django.http.request.GET.get("rfs_header") + response = django.http.HttpResponse() + response.__setitem__('HeaderName', rfs_header) + return response + + +def django_response(): + rfs_header = django.http.request.GET.get("rfs_header") + response = django.http.HttpResponse() + response['HeaderName'] = rfs_header + return response diff --git a/python/ql/test/experimental/query-tests/Security/CWE-113/flask_bad.py b/python/ql/test/experimental/query-tests/Security/CWE-113/flask_bad.py new file mode 100644 index 00000000000..6f2968efb55 --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-113/flask_bad.py @@ -0,0 +1,47 @@ +from flask import Response, request, Flask, make_response +from werkzeug.datastructures import Headers + +app = Flask(__name__) + + +@app.route('/werkzeug_headers') +def werkzeug_headers(): + rfs_header = request.args["rfs_header"] + response = Response() + headers = Headers() + headers.add("HeaderName", rfs_header) + response.headers = headers + return response + + +@app.route("/flask_Response") +def flask_Response(): + rfs_header = request.args["rfs_header"] + response = Response() + response.headers['HeaderName'] = rfs_header + return response + + +@app.route("/flask_make_response") +def flask_make_response(): + rfs_header = request.args["rfs_header"] + resp = make_response("hello") + resp.headers['HeaderName'] = rfs_header + return resp + + +@app.route("/flask_make_response_extend") +def flask_make_response_extend(): + rfs_header = request.args["rfs_header"] + resp = make_response("hello") + resp.headers.extend( + {'HeaderName': rfs_header}) + return resp + + +@app.route("/Response_arg") +def Response_arg(): + return Response(headers={'HeaderName': request.args["rfs_header"]}) + +# if __name__ == "__main__": +# app.run(debug=True) diff --git a/python/ql/test/experimental/query-tests/Security/CWE-117/LogInjection.expected b/python/ql/test/experimental/query-tests/Security/CWE-117/LogInjection.expected new file mode 100644 index 00000000000..e27b369e640 --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-117/LogInjection.expected @@ -0,0 +1,28 @@ +edges +| LogInjectionBad.py:17:12:17:18 | ControlFlowNode for request | LogInjectionBad.py:17:12:17:23 | ControlFlowNode for Attribute | +| LogInjectionBad.py:17:12:17:23 | ControlFlowNode for Attribute | LogInjectionBad.py:18:21:18:40 | ControlFlowNode for BinaryExpr | +| LogInjectionBad.py:23:12:23:18 | ControlFlowNode for request | LogInjectionBad.py:23:12:23:23 | ControlFlowNode for Attribute | +| LogInjectionBad.py:23:12:23:23 | ControlFlowNode for Attribute | LogInjectionBad.py:24:18:24:37 | ControlFlowNode for BinaryExpr | +| LogInjectionBad.py:29:12:29:18 | ControlFlowNode for request | LogInjectionBad.py:29:12:29:23 | ControlFlowNode for Attribute | +| LogInjectionBad.py:29:12:29:23 | ControlFlowNode for Attribute | LogInjectionBad.py:30:25:30:44 | ControlFlowNode for BinaryExpr | +| LogInjectionBad.py:35:12:35:18 | ControlFlowNode for request | LogInjectionBad.py:35:12:35:23 | ControlFlowNode for Attribute | +| LogInjectionBad.py:35:12:35:23 | ControlFlowNode for Attribute | LogInjectionBad.py:37:19:37:38 | ControlFlowNode for BinaryExpr | +nodes +| LogInjectionBad.py:17:12:17:18 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| LogInjectionBad.py:17:12:17:23 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| LogInjectionBad.py:18:21:18:40 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| LogInjectionBad.py:23:12:23:18 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| LogInjectionBad.py:23:12:23:23 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| LogInjectionBad.py:24:18:24:37 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| LogInjectionBad.py:29:12:29:18 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| LogInjectionBad.py:29:12:29:23 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| LogInjectionBad.py:30:25:30:44 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| LogInjectionBad.py:35:12:35:18 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| LogInjectionBad.py:35:12:35:23 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| LogInjectionBad.py:37:19:37:38 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +subpaths +#select +| LogInjectionBad.py:18:21:18:40 | ControlFlowNode for BinaryExpr | LogInjectionBad.py:17:12:17:18 | ControlFlowNode for request | LogInjectionBad.py:18:21:18:40 | ControlFlowNode for BinaryExpr | $@ flows to log entry. | LogInjectionBad.py:17:12:17:18 | ControlFlowNode for request | User-provided value | +| LogInjectionBad.py:24:18:24:37 | ControlFlowNode for BinaryExpr | LogInjectionBad.py:23:12:23:18 | ControlFlowNode for request | LogInjectionBad.py:24:18:24:37 | ControlFlowNode for BinaryExpr | $@ flows to log entry. | LogInjectionBad.py:23:12:23:18 | ControlFlowNode for request | User-provided value | +| LogInjectionBad.py:30:25:30:44 | ControlFlowNode for BinaryExpr | LogInjectionBad.py:29:12:29:18 | ControlFlowNode for request | LogInjectionBad.py:30:25:30:44 | ControlFlowNode for BinaryExpr | $@ flows to log entry. | LogInjectionBad.py:29:12:29:18 | ControlFlowNode for request | User-provided value | +| LogInjectionBad.py:37:19:37:38 | ControlFlowNode for BinaryExpr | LogInjectionBad.py:35:12:35:18 | ControlFlowNode for request | LogInjectionBad.py:37:19:37:38 | ControlFlowNode for BinaryExpr | $@ flows to log entry. | LogInjectionBad.py:35:12:35:18 | ControlFlowNode for request | User-provided value | diff --git a/python/ql/test/experimental/query-tests/Security/CWE-117/LogInjection.qlref b/python/ql/test/experimental/query-tests/Security/CWE-117/LogInjection.qlref new file mode 100644 index 00000000000..021cc357ac2 --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-117/LogInjection.qlref @@ -0,0 +1 @@ +experimental/Security/CWE-117/LogInjection.ql diff --git a/python/ql/test/experimental/query-tests/Security/CWE-117/LogInjectionBad.py b/python/ql/test/experimental/query-tests/Security/CWE-117/LogInjectionBad.py new file mode 100644 index 00000000000..0c0cbe1a8fe --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-117/LogInjectionBad.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +""" +@Desc :Log Injection +""" +from flask import Flask +from flask import request +from django.utils.log import request_logger +import logging + +logging.basicConfig(level=logging.DEBUG) + +app = Flask(__name__) + +@app.route('/bad1') +def bad1(): + name = request.args.get('name') + app.logger.info('User name: ' + name) # Bad + return 'bad1' + +@app.route('/bad2') +def bad2(): + name = request.args.get('name') + logging.info('User name: ' + name) # Bad + return 'bad2' + +@app.route('/bad3') +def bad3(): + name = request.args.get('name') + request_logger.warn('User name: ' + name) # Bad + return 'bad3' + +@app.route('/bad4') +def bad4(): + name = request.args.get('name') + logtest = logging.getLogger('test') + logtest.debug('User name: ' + name) # Bad + return 'bad4' + +if __name__ == '__main__': + app.debug = True + handler = logging.FileHandler('log') + app.logger.addHandler(handler) + app.run() diff --git a/python/ql/test/experimental/query-tests/Security/CWE-117/LogInjectionGood.py b/python/ql/test/experimental/query-tests/Security/CWE-117/LogInjectionGood.py new file mode 100644 index 00000000000..d9279f2e482 --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-117/LogInjectionGood.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +""" +@Desc :Log Injection +""" +from flask import Flask +from flask import request +import logging + +logging.basicConfig(level=logging.DEBUG) + +app = Flask(__name__) + +@app.route('/good1') +def good1(): + name = request.args.get('name') + name = name.replace('\r\n','').replace('\n','') + logging.info('User name: ' + name) # Good + return 'good1' + +if __name__ == '__main__': + app.debug = True + handler = logging.FileHandler('log') + app.logger.addHandler(handler) + app.run() diff --git a/python/ql/test/experimental/query-tests/Security/CWE-348/ClientSuppliedIpUsedInSecurityCheck.expected b/python/ql/test/experimental/query-tests/Security/CWE-348/ClientSuppliedIpUsedInSecurityCheck.expected new file mode 100644 index 00000000000..a432cf5053f --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-348/ClientSuppliedIpUsedInSecurityCheck.expected @@ -0,0 +1,16 @@ +edges +| flask_bad.py:13:17:13:54 | ControlFlowNode for Attribute() | flask_bad.py:14:12:14:20 | ControlFlowNode for client_ip | +| flask_bad.py:20:17:20:54 | ControlFlowNode for Attribute() | flask_bad.py:21:12:21:20 | ControlFlowNode for client_ip | +| tornado_bad.py:22:25:22:69 | ControlFlowNode for Attribute() | tornado_bad.py:23:16:23:24 | ControlFlowNode for client_ip | +nodes +| flask_bad.py:13:17:13:54 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| flask_bad.py:14:12:14:20 | ControlFlowNode for client_ip | semmle.label | ControlFlowNode for client_ip | +| flask_bad.py:20:17:20:54 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| flask_bad.py:21:12:21:20 | ControlFlowNode for client_ip | semmle.label | ControlFlowNode for client_ip | +| tornado_bad.py:22:25:22:69 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| tornado_bad.py:23:16:23:24 | ControlFlowNode for client_ip | semmle.label | ControlFlowNode for client_ip | +subpaths +#select +| flask_bad.py:14:12:14:20 | ControlFlowNode for client_ip | flask_bad.py:13:17:13:54 | ControlFlowNode for Attribute() | flask_bad.py:14:12:14:20 | ControlFlowNode for client_ip | IP address spoofing might include code from $@. | flask_bad.py:13:17:13:54 | ControlFlowNode for Attribute() | this user input | +| flask_bad.py:21:12:21:20 | ControlFlowNode for client_ip | flask_bad.py:20:17:20:54 | ControlFlowNode for Attribute() | flask_bad.py:21:12:21:20 | ControlFlowNode for client_ip | IP address spoofing might include code from $@. | flask_bad.py:20:17:20:54 | ControlFlowNode for Attribute() | this user input | +| tornado_bad.py:23:16:23:24 | ControlFlowNode for client_ip | tornado_bad.py:22:25:22:69 | ControlFlowNode for Attribute() | tornado_bad.py:23:16:23:24 | ControlFlowNode for client_ip | IP address spoofing might include code from $@. | tornado_bad.py:22:25:22:69 | ControlFlowNode for Attribute() | this user input | diff --git a/python/ql/test/experimental/query-tests/Security/CWE-348/ClientSuppliedIpUsedInSecurityCheck.qlref b/python/ql/test/experimental/query-tests/Security/CWE-348/ClientSuppliedIpUsedInSecurityCheck.qlref new file mode 100644 index 00000000000..2a1775fe06a --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-348/ClientSuppliedIpUsedInSecurityCheck.qlref @@ -0,0 +1 @@ +experimental/Security/CWE-348/ClientSuppliedIpUsedInSecurityCheck.ql \ No newline at end of file diff --git a/python/ql/test/experimental/query-tests/Security/CWE-348/flask_bad.py b/python/ql/test/experimental/query-tests/Security/CWE-348/flask_bad.py new file mode 100644 index 00000000000..b357a9316fd --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-348/flask_bad.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +""" +@Desc :ip address spoofing +""" +from flask import Flask +from flask import request + +app = Flask(__name__) + +@app.route('/bad1') +def bad1(): + client_ip = request.headers.get('x-forwarded-for') + if not client_ip.startswith('192.168.'): + raise Exception('ip illegal') + return 'bad1' + +@app.route('/bad2') +def bad2(): + client_ip = request.headers.get('x-forwarded-for') + if not client_ip == '127.0.0.1': + raise Exception('ip illegal') + return 'bad2' + +if __name__ == '__main__': + app.debug = True + app.run() diff --git a/python/ql/test/experimental/query-tests/Security/CWE-348/flask_good.py b/python/ql/test/experimental/query-tests/Security/CWE-348/flask_good.py new file mode 100644 index 00000000000..da42c59724a --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-348/flask_good.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +""" +@Desc :ip address spoofing +""" +from flask import Flask +from flask import request + +app = Flask(__name__) + +@app.route('/good1') +def good1(): + client_ip = request.headers.get('x-forwarded-for') + client_ip = client_ip.split(',')[len(client_ip.split(',')) - 1] + if not client_ip == '127.0.0.1': + raise Exception('ip illegal') + return 'good1' + +if __name__ == '__main__': + app.debug = True + app.run() diff --git a/python/ql/test/experimental/query-tests/Security/CWE-348/tornado_bad.py b/python/ql/test/experimental/query-tests/Security/CWE-348/tornado_bad.py new file mode 100644 index 00000000000..23ad29d8b09 --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-348/tornado_bad.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +""" +@Desc :ip address spoofing +""" +import tornado.httpserver +import tornado.options +import tornado.web +import tornado.ioloop + +from tornado.options import define, options + +define("port", default=8000, help="run on the given port,default 8000", type=int) + + +class IndexHandler(tornado.web.RequestHandler): + def get(self): + client_ip = self.request.headers.get('x-forwarded-for') + if client_ip: + client_ip = client_ip.split(',')[len(client_ip.split(',')) - 1] + else: + client_ip = self.request.headers.get('REMOTE_ADDR', None) + if not client_ip == '127.0.0.1': + raise Exception('ip illegal') + self.write("hello.") + +handlers = [(r"/", IndexHandler)] + +if __name__ == "__main__": + tornado.options.parse_command_line() + app = tornado.web.Application( + handlers + ) + http_server = tornado.httpserver.HTTPServer(app) + http_server.listen(options.port) + tornado.ioloop.IOLoop.instance().start() diff --git a/python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.qlref b/python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.qlref deleted file mode 100644 index c0c506c4707..00000000000 --- a/python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.qlref +++ /dev/null @@ -1 +0,0 @@ -experimental/Security/CWE-730/RegexInjection.ql diff --git a/python/ql/test/library-tests/PointsTo/api/Value.ql b/python/ql/test/library-tests/PointsTo/api/Value.ql index 5af0d1061e7..9c6e0739461 100644 --- a/python/ql/test/library-tests/PointsTo/api/Value.ql +++ b/python/ql/test/library-tests/PointsTo/api/Value.ql @@ -3,11 +3,5 @@ import python from Value val, string name where val = Value::named(name) and - ( - name = "bool" or - name = "sys" or - name = "sys.argv" or - name = "ValueError" or - name = "slice" - ) + name in ["bool", "sys", "sys.argv", "ValueError", "slice"] select val, name diff --git a/python/ql/test/library-tests/PointsTo/customise/test.ql b/python/ql/test/library-tests/PointsTo/customise/test.ql index c2fceb95225..f101e4f2a5d 100644 --- a/python/ql/test/library-tests/PointsTo/customise/test.ql +++ b/python/ql/test/library-tests/PointsTo/customise/test.ql @@ -11,7 +11,7 @@ class HasTypeFact extends CustomPointsToOriginFact { exists(FunctionObject func, string name | func.getACall() = this and name = func.getName() and - name.prefix("has_type_".length()) = "has_type_" + name.matches("has\\_type\\_%") ) } @@ -19,7 +19,7 @@ class HasTypeFact extends CustomPointsToOriginFact { exists(FunctionObject func, string name | func.getACall() = this and name = func.getName() and - name.prefix("has_type_".length()) = "has_type_" + name.matches("has\\_type\\_%") | cls.getName() = name.suffix("has_type_".length()) ) and diff --git a/python/ql/test/library-tests/PointsTo/new/Consistency.ql b/python/ql/test/library-tests/PointsTo/new/Consistency.ql index 282b96fc541..0ee1a392ef2 100644 --- a/python/ql/test/library-tests/PointsTo/new/Consistency.ql +++ b/python/ql/test/library-tests/PointsTo/new/Consistency.ql @@ -104,7 +104,7 @@ predicate ssa_consistency(string clsname, string problem, string what) { or exists(EssaDefinition def | clsname = def.getAQlClass() and - clsname.prefix(4) = "Essa" and + clsname.matches("Essa%") and what = " at " + def.getLocation() and problem = "not covered by Python-specific subclass." ) diff --git a/python/ql/test/library-tests/frameworks/crypto/test_sha3.py b/python/ql/test/library-tests/frameworks/crypto/test_sha3.py new file mode 100644 index 00000000000..426d0266fc4 --- /dev/null +++ b/python/ql/test/library-tests/frameworks/crypto/test_sha3.py @@ -0,0 +1,10 @@ +from Crypto.Hash import SHA3_224 + +hasher = SHA3_224.new(b"secret message") # $ CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=SHA3224 +print(hasher.hexdigest()) + + +hasher = SHA3_224.new() # $ CryptographicOperation CryptographicOperationAlgorithm=SHA3224 +hasher.update(b"secret") # $ CryptographicOperation CryptographicOperationInput=b"secret" CryptographicOperationAlgorithm=SHA3224 +hasher.update(b" message") # $ CryptographicOperation CryptographicOperationInput=b" message" CryptographicOperationAlgorithm=SHA3224 +print(hasher.hexdigest()) diff --git a/python/ql/test/library-tests/frameworks/cryptodome/test_sha3.py b/python/ql/test/library-tests/frameworks/cryptodome/test_sha3.py new file mode 100644 index 00000000000..2329fd7e1c0 --- /dev/null +++ b/python/ql/test/library-tests/frameworks/cryptodome/test_sha3.py @@ -0,0 +1,10 @@ +from Cryptodome.Hash import SHA3_224 + +hasher = SHA3_224.new(b"secret message") # $ CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=SHA3224 +print(hasher.hexdigest()) + + +hasher = SHA3_224.new() # $ CryptographicOperation CryptographicOperationAlgorithm=SHA3224 +hasher.update(b"secret") # $ CryptographicOperation CryptographicOperationInput=b"secret" CryptographicOperationAlgorithm=SHA3224 +hasher.update(b" message") # $ CryptographicOperation CryptographicOperationInput=b" message" CryptographicOperationAlgorithm=SHA3224 +print(hasher.hexdigest()) diff --git a/python/ql/test/library-tests/frameworks/dill/Decoding.py b/python/ql/test/library-tests/frameworks/dill/Decoding.py index 49eb551af04..bcbd6ff4b35 100644 --- a/python/ql/test/library-tests/frameworks/dill/Decoding.py +++ b/python/ql/test/library-tests/frameworks/dill/Decoding.py @@ -1,3 +1,6 @@ import dill -dill.loads(payload) # $decodeInput=payload decodeOutput=dill.loads(..) decodeFormat=dill decodeMayExecuteInput +dill.load(file_) # $ decodeInput=file_ decodeOutput=dill.load(..) decodeFormat=dill decodeMayExecuteInput +dill.load(file=file_) # $ decodeInput=file_ decodeOutput=dill.load(..) decodeFormat=dill decodeMayExecuteInput +dill.loads(payload) # $ decodeInput=payload decodeOutput=dill.loads(..) decodeFormat=dill decodeMayExecuteInput +dill.loads(str=payload) # $ decodeInput=payload decodeOutput=dill.loads(..) decodeFormat=dill decodeMayExecuteInput diff --git a/python/ql/test/library-tests/frameworks/ruamel.yaml/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/ruamel.yaml/ConceptsTest.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/frameworks/ruamel.yaml/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/ruamel.yaml/ConceptsTest.ql new file mode 100644 index 00000000000..b557a0bccb6 --- /dev/null +++ b/python/ql/test/library-tests/frameworks/ruamel.yaml/ConceptsTest.ql @@ -0,0 +1,2 @@ +import python +import experimental.meta.ConceptsTest diff --git a/python/ql/test/library-tests/frameworks/ruamel.yaml/Decoding.py b/python/ql/test/library-tests/frameworks/ruamel.yaml/Decoding.py new file mode 100644 index 00000000000..99648fbbad0 --- /dev/null +++ b/python/ql/test/library-tests/frameworks/ruamel.yaml/Decoding.py @@ -0,0 +1,33 @@ +import ruamel.yaml + +# Unsafe: +ruamel.yaml.load(payload) # $ decodeInput=payload decodeOutput=ruamel.yaml.load(..) decodeFormat=YAML decodeMayExecuteInput +ruamel.yaml.load(stream=payload) # $ decodeInput=payload decodeOutput=ruamel.yaml.load(..) decodeFormat=YAML decodeMayExecuteInput +ruamel.yaml.load(payload, ruamel.yaml.Loader) # $ decodeInput=payload decodeOutput=ruamel.yaml.load(..) decodeFormat=YAML decodeMayExecuteInput + +# Safe: +ruamel.yaml.load(payload, ruamel.yaml.SafeLoader) # $ decodeInput=payload decodeOutput=ruamel.yaml.load(..) decodeFormat=YAML +ruamel.yaml.load(payload, Loader=ruamel.yaml.SafeLoader) # $ decodeInput=payload decodeOutput=ruamel.yaml.load(..) decodeFormat=YAML +ruamel.yaml.load(payload, ruamel.yaml.BaseLoader) # $ decodeInput=payload decodeOutput=ruamel.yaml.load(..) decodeFormat=YAML +ruamel.yaml.safe_load(payload) # $ decodeInput=payload decodeOutput=ruamel.yaml.safe_load(..) decodeFormat=YAML + +################################################################################ +# load_all variants +################################################################################ + +# Unsafe: +ruamel.yaml.load_all(payload) # $ decodeInput=payload decodeOutput=ruamel.yaml.load_all(..) decodeFormat=YAML decodeMayExecuteInput + +# Safe: +ruamel.yaml.safe_load_all(payload) # $ decodeInput=payload decodeOutput=ruamel.yaml.safe_load_all(..) decodeFormat=YAML + +################################################################################ +# C-based loaders with `libyaml` +################################################################################ + +# Unsafe: +ruamel.yaml.load(payload, ruamel.yaml.CLoader) # $ decodeInput=payload decodeOutput=ruamel.yaml.load(..) decodeFormat=YAML decodeMayExecuteInput + +# Safe: +ruamel.yaml.load(payload, ruamel.yaml.CSafeLoader) # $ decodeInput=payload decodeOutput=ruamel.yaml.load(..) decodeFormat=YAML +ruamel.yaml.load(payload, ruamel.yaml.CBaseLoader) # $ decodeInput=payload decodeOutput=ruamel.yaml.load(..) decodeFormat=YAML diff --git a/python/ql/test/library-tests/frameworks/ruamel.yaml/PoC b/python/ql/test/library-tests/frameworks/ruamel.yaml/PoC new file mode 100644 index 00000000000..5618c463dd1 --- /dev/null +++ b/python/ql/test/library-tests/frameworks/ruamel.yaml/PoC @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 + +# this file doesn't have a .py extension so the extractor doesn't pick it up, so it +# doesn't have to be annotated + +# This file is just a Proof of Concept for how code execution can be triggered. + +import os +import ruamel.yaml + +class Exploit(object): + def __reduce__(self): + return (os.system, ('ls',)) + +data = Exploit() +serialized_data = ruamel.yaml.dump(data) + +# All these will execute `ls` +print("!!! ruamel.yaml.load") +ruamel.yaml.load(serialized_data) + +print("!!! ruamel.yaml.load kwarg") +ruamel.yaml.load(stream=serialized_data) + +print("!!! ruamel.yaml.load with Loader=ruamel.yaml.Loader") +ruamel.yaml.load(serialized_data, ruamel.yaml.Loader) + +print("!!! ruamel.yaml.load with Loader=ruamel.yaml.UnsafeLoader") +ruamel.yaml.load(serialized_data, ruamel.yaml.UnsafeLoader) + +print("!!! ruamel.yaml.load with Loader=ruamel.yaml.CLoader") +ruamel.yaml.load(serialized_data, ruamel.yaml.CLoader) + +# you need to iterate through the result for it to execute... but it still works +print("!!! ruamel.yaml.load_all") +for _ in ruamel.yaml.load_all(serialized_data): + pass + +# check that the safe version is actually safe +print("\n" + "-"*80) +print("safe versions") +print("-" * 80) + +print("!!! ruamel.yaml.safe_load") +try: + ruamel.yaml.safe_load(serialized_data) + raise Exception("should not happen") +except ruamel.yaml.constructor.ConstructorError: + pass + +print("!!! ruamel.yaml.load with Loader=ruamel.yaml.SafeLoader") +try: + ruamel.yaml.load(serialized_data, ruamel.yaml.SafeLoader) + raise Exception("should not happen") +except ruamel.yaml.constructor.ConstructorError: + pass + +print("!!! ruamel.yaml.load with Loader=ruamel.yaml.CSafeLoader") +try: + ruamel.yaml.load(serialized_data, ruamel.yaml.CSafeLoader) + raise Exception("should not happen") +except ruamel.yaml.constructor.ConstructorError: + pass diff --git a/python/ql/test/library-tests/frameworks/stdlib/Decoding.py b/python/ql/test/library-tests/frameworks/stdlib/Decoding.py index 5d157a61f6e..e9e811ffc97 100644 --- a/python/ql/test/library-tests/frameworks/stdlib/Decoding.py +++ b/python/ql/test/library-tests/frameworks/stdlib/Decoding.py @@ -1,10 +1,31 @@ import pickle import marshal +import shelve import base64 +pickle.load(file_) # $ decodeInput=file_ decodeOutput=pickle.load(..) decodeFormat=pickle decodeMayExecuteInput +pickle.load(file=file_) # $ decodeInput=file_ decodeOutput=pickle.load(..) decodeFormat=pickle decodeMayExecuteInput pickle.loads(payload) # $ decodeInput=payload decodeOutput=pickle.loads(..) decodeFormat=pickle decodeMayExecuteInput +# using this keyword argument is disallowed from Python 3.9 +pickle.loads(data=payload) # $ decodeInput=payload decodeOutput=pickle.loads(..) decodeFormat=pickle decodeMayExecuteInput + +# We don't really have a good way to model a decode happening over multiple statements +# like this. Since the important bit for `py/unsafe-deserialization` is the input, that +# is the main focus. We do a best effort to model the output though (but that will only +# work in local scope). +unpickler = pickle.Unpickler(file_) # $ decodeInput=file_ decodeFormat=pickle decodeMayExecuteInput +unpickler.load() # $ decodeOutput=unpickler.load() +unpickler = pickle.Unpickler(file=file_) # $ decodeInput=file_ decodeFormat=pickle decodeMayExecuteInput + +marshal.load(file_) # $ decodeInput=file_ decodeOutput=marshal.load(..) decodeFormat=marshal decodeMayExecuteInput marshal.loads(payload) # $ decodeInput=payload decodeOutput=marshal.loads(..) decodeFormat=marshal decodeMayExecuteInput + +# if the file opened has been controlled by an attacker, this can lead to code +# execution. (underlying file format is pickle) +shelve.open(filepath) # $ decodeInput=filepath decodeOutput=shelve.open(..) decodeFormat=pickle decodeMayExecuteInput getAPathArgument=filepath +shelve.open(filename=filepath) # $ decodeInput=filepath decodeOutput=shelve.open(..) decodeFormat=pickle decodeMayExecuteInput getAPathArgument=filepath + # TODO: These tests should be merged with python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_string.py base64.b64decode(payload) # $ decodeInput=payload decodeOutput=base64.b64decode(..) decodeFormat=Base64 base64.standard_b64decode(payload) # $ decodeInput=payload decodeOutput=base64.standard_b64decode(..) decodeFormat=Base64 diff --git a/python/ql/test/library-tests/frameworks/stdlib/FileSystemAccess.py b/python/ql/test/library-tests/frameworks/stdlib/FileSystemAccess.py index bcf6589ef85..64f8ad5010d 100644 --- a/python/ql/test/library-tests/frameworks/stdlib/FileSystemAccess.py +++ b/python/ql/test/library-tests/frameworks/stdlib/FileSystemAccess.py @@ -27,3 +27,10 @@ def through_function(open_file): open_file.write("foo") # $ fileWriteData="foo" getAPathArgument="path" through_function(f) + +from os import path +path.exists("filepath") # $ getAPathArgument="filepath" +path.isfile("filepath") # $ getAPathArgument="filepath" +path.isdir("filepath") # $ getAPathArgument="filepath" +path.islink("filepath") # $ getAPathArgument="filepath" +path.ismount("filepath") # $ getAPathArgument="filepath" diff --git a/python/ql/test/library-tests/frameworks/yaml/Decoding.py b/python/ql/test/library-tests/frameworks/yaml/Decoding.py index b3cc627883d..d76faa8b20b 100644 --- a/python/ql/test/library-tests/frameworks/yaml/Decoding.py +++ b/python/ql/test/library-tests/frameworks/yaml/Decoding.py @@ -2,6 +2,7 @@ import yaml # Unsafe: yaml.load(payload) # $decodeInput=payload decodeOutput=yaml.load(..) decodeFormat=YAML decodeMayExecuteInput +yaml.load(stream=payload) # $decodeInput=payload decodeOutput=yaml.load(..) decodeFormat=YAML decodeMayExecuteInput yaml.load(payload, yaml.Loader) # $decodeInput=payload decodeOutput=yaml.load(..) decodeFormat=YAML decodeMayExecuteInput yaml.unsafe_load(payload) # $ decodeInput=payload decodeOutput=yaml.unsafe_load(..) decodeFormat=YAML decodeMayExecuteInput yaml.full_load(payload) # $ decodeInput=payload decodeOutput=yaml.full_load(..) decodeFormat=YAML decodeMayExecuteInput diff --git a/python/ql/test/library-tests/frameworks/yaml/PoC b/python/ql/test/library-tests/frameworks/yaml/PoC new file mode 100644 index 00000000000..e51f873c484 --- /dev/null +++ b/python/ql/test/library-tests/frameworks/yaml/PoC @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 + +# this file doesn't have a .py extension so the extractor doesn't pick it up, so it +# doesn't have to be annotated + +# This file is just a Proof of Concept for how code execution can be triggered. + + +import os +import yaml + +class Exploit(object): + def __reduce__(self): + return (os.system, ('ls',)) + +data = Exploit() +serialized_data = yaml.dump(data) + +# All these will execute `ls` +print("!!! yaml.unsafe_load") +yaml.unsafe_load(serialized_data) + +print("!!! yaml.unsafe_load kwarg") +yaml.unsafe_load(stream=serialized_data) + +print("!!! yaml.load with Loader=yaml.UnsafeLoader") +yaml.load(serialized_data, yaml.UnsafeLoader) + +# you need to iterate through the result for it to execute... but it still works +print("!!! yaml.unsafe_load_all") +for _ in yaml.unsafe_load_all(serialized_data): + pass + +# check that the safe version is actually safe +print("\n" + "-"*80) +print("safe versions") +print("-" * 80) + +print("!!! yaml.load") +try: + yaml.load(serialized_data) + raise Exception("should not happen") +except yaml.constructor.ConstructorError: + pass + +print("!!! yaml.safe_load") +try: + yaml.safe_load(serialized_data) + raise Exception("should not happen") +except yaml.constructor.ConstructorError: + pass + +print("!!! yaml.load with Loader=yaml.SafeLoader") +try: + yaml.load(serialized_data, yaml.SafeLoader) + raise Exception("should not happen") +except yaml.constructor.ConstructorError: + pass diff --git a/python/ql/test/library-tests/objects/Name.ql b/python/ql/test/library-tests/objects/Name.ql index 900af0cf2e3..c274ed34554 100644 --- a/python/ql/test/library-tests/objects/Name.ql +++ b/python/ql/test/library-tests/objects/Name.ql @@ -3,19 +3,7 @@ import python from Object o, string name where o.hasLongName(name) and - ( - name = "sys.modules" - or - name = "test.n" - or - name = "test.l" - or - name = "test.d" - or - name = "test.C.meth" - or - name = "test.C.cmeth" - or - name = "test.C.smeth" - ) + name in [ + "sys.modules", "test.n", "test.l", "test.d", "test.C.meth", "test.C.cmeth", "test.C.smeth" + ] select name, o.toString() diff --git a/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll b/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll index 08ba0ce6e40..4ae53e94a38 100644 --- a/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll +++ b/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll @@ -28,7 +28,7 @@ class SimpleSource extends TaintSource { predicate visit_call(CallNode call, FunctionObject func) { exists(AttrNode attr, ClassObject cls, string name | - name.prefix(6) = "visit_" and + name.matches("visit\\_%") and func = cls.lookupAttribute(name) and attr.getObject("visit").refersTo(_, cls, _) and attr = call.getFunction() diff --git a/python/ql/test/query-tests/Diagnostics/ExtractionErrors.qlref b/python/ql/test/query-tests/Diagnostics/ExtractionErrors.qlref deleted file mode 100644 index 488db09ab05..00000000000 --- a/python/ql/test/query-tests/Diagnostics/ExtractionErrors.qlref +++ /dev/null @@ -1 +0,0 @@ -Diagnostics/ExtractionErrors.ql diff --git a/python/ql/test/query-tests/Diagnostics/ExtractionErrors.expected b/python/ql/test/query-tests/Diagnostics/ExtractionWarnings.expected similarity index 67% rename from python/ql/test/query-tests/Diagnostics/ExtractionErrors.expected rename to python/ql/test/query-tests/Diagnostics/ExtractionWarnings.expected index 4103e4d0cdc..026fbf9aa88 100644 --- a/python/ql/test/query-tests/Diagnostics/ExtractionErrors.expected +++ b/python/ql/test/query-tests/Diagnostics/ExtractionWarnings.expected @@ -1,2 +1,2 @@ -| bad_encoding.py:2:11:2:11 | Encoding Error | Extraction failed in bad_encoding.py with error 'utf-8' codec can't decode byte 0x9d in position 87: invalid start byte | 2 | -| syntax_error.py:1:31:1:31 | Syntax Error | Extraction failed in syntax_error.py with error Syntax Error | 2 | +| bad_encoding.py:2:11:2:11 | Encoding Error | Extraction failed in bad_encoding.py with error 'utf-8' codec can't decode byte 0x9d in position 87: invalid start byte | 1 | +| syntax_error.py:1:31:1:31 | Syntax Error | Extraction failed in syntax_error.py with error Syntax Error | 1 | diff --git a/python/ql/test/query-tests/Diagnostics/ExtractionWarnings.qlref b/python/ql/test/query-tests/Diagnostics/ExtractionWarnings.qlref new file mode 100644 index 00000000000..2df3d933e8a --- /dev/null +++ b/python/ql/test/query-tests/Diagnostics/ExtractionWarnings.qlref @@ -0,0 +1 @@ +Diagnostics/ExtractionWarnings.ql diff --git a/python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/WeakSensitiveDataHashing.expected b/python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/WeakSensitiveDataHashing.expected index afd49cad997..24de077ac8f 100644 --- a/python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/WeakSensitiveDataHashing.expected +++ b/python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/WeakSensitiveDataHashing.expected @@ -1,24 +1,24 @@ edges -| test_cryptodome.py:0:0:0:0 | ModuleVariableNode for Global Variable get_certificate in Module test_cryptodome | test_cryptodome.py:6:17:6:31 | ControlFlowNode for get_certificate | -| test_cryptodome.py:0:0:0:0 | ModuleVariableNode for Global Variable get_password in Module test_cryptodome | test_cryptodome.py:13:17:13:28 | ControlFlowNode for get_password | -| test_cryptodome.py:0:0:0:0 | ModuleVariableNode for Global Variable get_password in Module test_cryptodome | test_cryptodome.py:20:17:20:28 | ControlFlowNode for get_password | +| test_cryptodome.py:0:0:0:0 | ModuleVariableNode for test_cryptodome.get_certificate | test_cryptodome.py:6:17:6:31 | ControlFlowNode for get_certificate | +| test_cryptodome.py:0:0:0:0 | ModuleVariableNode for test_cryptodome.get_password | test_cryptodome.py:13:17:13:28 | ControlFlowNode for get_password | +| test_cryptodome.py:0:0:0:0 | ModuleVariableNode for test_cryptodome.get_password | test_cryptodome.py:20:17:20:28 | ControlFlowNode for get_password | | test_cryptodome.py:2:23:2:34 | ControlFlowNode for ImportMember | test_cryptodome.py:2:23:2:34 | GSSA Variable get_password | -| test_cryptodome.py:2:23:2:34 | GSSA Variable get_password | test_cryptodome.py:0:0:0:0 | ModuleVariableNode for Global Variable get_password in Module test_cryptodome | +| test_cryptodome.py:2:23:2:34 | GSSA Variable get_password | test_cryptodome.py:0:0:0:0 | ModuleVariableNode for test_cryptodome.get_password | | test_cryptodome.py:2:37:2:51 | ControlFlowNode for ImportMember | test_cryptodome.py:2:37:2:51 | GSSA Variable get_certificate | -| test_cryptodome.py:2:37:2:51 | GSSA Variable get_certificate | test_cryptodome.py:0:0:0:0 | ModuleVariableNode for Global Variable get_certificate in Module test_cryptodome | +| test_cryptodome.py:2:37:2:51 | GSSA Variable get_certificate | test_cryptodome.py:0:0:0:0 | ModuleVariableNode for test_cryptodome.get_certificate | | test_cryptodome.py:6:17:6:31 | ControlFlowNode for get_certificate | test_cryptodome.py:8:19:8:27 | ControlFlowNode for dangerous | | test_cryptodome.py:6:17:6:33 | ControlFlowNode for get_certificate() | test_cryptodome.py:8:19:8:27 | ControlFlowNode for dangerous | | test_cryptodome.py:13:17:13:28 | ControlFlowNode for get_password | test_cryptodome.py:15:19:15:27 | ControlFlowNode for dangerous | | test_cryptodome.py:13:17:13:30 | ControlFlowNode for get_password() | test_cryptodome.py:15:19:15:27 | ControlFlowNode for dangerous | | test_cryptodome.py:20:17:20:28 | ControlFlowNode for get_password | test_cryptodome.py:24:19:24:27 | ControlFlowNode for dangerous | | test_cryptodome.py:20:17:20:30 | ControlFlowNode for get_password() | test_cryptodome.py:24:19:24:27 | ControlFlowNode for dangerous | -| test_cryptography.py:0:0:0:0 | ModuleVariableNode for Global Variable get_certificate in Module test_cryptography | test_cryptography.py:7:17:7:31 | ControlFlowNode for get_certificate | -| test_cryptography.py:0:0:0:0 | ModuleVariableNode for Global Variable get_password in Module test_cryptography | test_cryptography.py:15:17:15:28 | ControlFlowNode for get_password | -| test_cryptography.py:0:0:0:0 | ModuleVariableNode for Global Variable get_password in Module test_cryptography | test_cryptography.py:23:17:23:28 | ControlFlowNode for get_password | +| test_cryptography.py:0:0:0:0 | ModuleVariableNode for test_cryptography.get_certificate | test_cryptography.py:7:17:7:31 | ControlFlowNode for get_certificate | +| test_cryptography.py:0:0:0:0 | ModuleVariableNode for test_cryptography.get_password | test_cryptography.py:15:17:15:28 | ControlFlowNode for get_password | +| test_cryptography.py:0:0:0:0 | ModuleVariableNode for test_cryptography.get_password | test_cryptography.py:23:17:23:28 | ControlFlowNode for get_password | | test_cryptography.py:3:23:3:34 | ControlFlowNode for ImportMember | test_cryptography.py:3:23:3:34 | GSSA Variable get_password | -| test_cryptography.py:3:23:3:34 | GSSA Variable get_password | test_cryptography.py:0:0:0:0 | ModuleVariableNode for Global Variable get_password in Module test_cryptography | +| test_cryptography.py:3:23:3:34 | GSSA Variable get_password | test_cryptography.py:0:0:0:0 | ModuleVariableNode for test_cryptography.get_password | | test_cryptography.py:3:37:3:51 | ControlFlowNode for ImportMember | test_cryptography.py:3:37:3:51 | GSSA Variable get_certificate | -| test_cryptography.py:3:37:3:51 | GSSA Variable get_certificate | test_cryptography.py:0:0:0:0 | ModuleVariableNode for Global Variable get_certificate in Module test_cryptography | +| test_cryptography.py:3:37:3:51 | GSSA Variable get_certificate | test_cryptography.py:0:0:0:0 | ModuleVariableNode for test_cryptography.get_certificate | | test_cryptography.py:7:17:7:31 | ControlFlowNode for get_certificate | test_cryptography.py:9:19:9:27 | ControlFlowNode for dangerous | | test_cryptography.py:7:17:7:33 | ControlFlowNode for get_certificate() | test_cryptography.py:9:19:9:27 | ControlFlowNode for dangerous | | test_cryptography.py:15:17:15:28 | ControlFlowNode for get_password | test_cryptography.py:17:19:17:27 | ControlFlowNode for dangerous | @@ -26,8 +26,8 @@ edges | test_cryptography.py:23:17:23:28 | ControlFlowNode for get_password | test_cryptography.py:27:19:27:27 | ControlFlowNode for dangerous | | test_cryptography.py:23:17:23:30 | ControlFlowNode for get_password() | test_cryptography.py:27:19:27:27 | ControlFlowNode for dangerous | nodes -| test_cryptodome.py:0:0:0:0 | ModuleVariableNode for Global Variable get_certificate in Module test_cryptodome | semmle.label | ModuleVariableNode for Global Variable get_certificate in Module test_cryptodome | -| test_cryptodome.py:0:0:0:0 | ModuleVariableNode for Global Variable get_password in Module test_cryptodome | semmle.label | ModuleVariableNode for Global Variable get_password in Module test_cryptodome | +| test_cryptodome.py:0:0:0:0 | ModuleVariableNode for test_cryptodome.get_certificate | semmle.label | ModuleVariableNode for test_cryptodome.get_certificate | +| test_cryptodome.py:0:0:0:0 | ModuleVariableNode for test_cryptodome.get_password | semmle.label | ModuleVariableNode for test_cryptodome.get_password | | test_cryptodome.py:2:23:2:34 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | | test_cryptodome.py:2:23:2:34 | GSSA Variable get_password | semmle.label | GSSA Variable get_password | | test_cryptodome.py:2:37:2:51 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | @@ -41,8 +41,8 @@ nodes | test_cryptodome.py:20:17:20:28 | ControlFlowNode for get_password | semmle.label | ControlFlowNode for get_password | | test_cryptodome.py:20:17:20:30 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() | | test_cryptodome.py:24:19:24:27 | ControlFlowNode for dangerous | semmle.label | ControlFlowNode for dangerous | -| test_cryptography.py:0:0:0:0 | ModuleVariableNode for Global Variable get_certificate in Module test_cryptography | semmle.label | ModuleVariableNode for Global Variable get_certificate in Module test_cryptography | -| test_cryptography.py:0:0:0:0 | ModuleVariableNode for Global Variable get_password in Module test_cryptography | semmle.label | ModuleVariableNode for Global Variable get_password in Module test_cryptography | +| test_cryptography.py:0:0:0:0 | ModuleVariableNode for test_cryptography.get_certificate | semmle.label | ModuleVariableNode for test_cryptography.get_certificate | +| test_cryptography.py:0:0:0:0 | ModuleVariableNode for test_cryptography.get_password | semmle.label | ModuleVariableNode for test_cryptography.get_password | | test_cryptography.py:3:23:3:34 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | | test_cryptography.py:3:23:3:34 | GSSA Variable get_password | semmle.label | GSSA Variable get_password | | test_cryptography.py:3:37:3:51 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | diff --git a/python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.expected b/python/ql/test/query-tests/Security/CWE-730-RegexInjection/RegexInjection.expected similarity index 93% rename from python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.expected rename to python/ql/test/query-tests/Security/CWE-730-RegexInjection/RegexInjection.expected index 07a01b5f9dc..598b60ae38a 100644 --- a/python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.expected +++ b/python/ql/test/query-tests/Security/CWE-730-RegexInjection/RegexInjection.expected @@ -23,6 +23,6 @@ nodes | re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | semmle.label | ControlFlowNode for unsafe_pattern | subpaths #select -| re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | re_bad.py:13:22:13:28 | ControlFlowNode for request | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | This | re_bad.py:13:22:13:28 | ControlFlowNode for request | user-provided value | re_bad.py:14:5:14:13 | Attribute | re.search | -| re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | re_bad.py:24:22:24:28 | ControlFlowNode for request | re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | This | re_bad.py:24:22:24:28 | ControlFlowNode for request | user-provided value | re_bad.py:26:5:26:27 | Attribute | re.search | -| re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | re_bad.py:36:22:36:28 | ControlFlowNode for request | re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | This | re_bad.py:36:22:36:28 | ControlFlowNode for request | user-provided value | re_bad.py:37:5:37:37 | Attribute | re.search | +| re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | re_bad.py:13:22:13:28 | ControlFlowNode for request | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | This | re_bad.py:13:22:13:28 | ControlFlowNode for request | user-provided value | re_bad.py:14:5:14:33 | ControlFlowNode for Attribute() | re.search | +| re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | re_bad.py:24:22:24:28 | ControlFlowNode for request | re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | This | re_bad.py:24:22:24:28 | ControlFlowNode for request | user-provided value | re_bad.py:26:5:26:31 | ControlFlowNode for Attribute() | re.search | +| re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | re_bad.py:36:22:36:28 | ControlFlowNode for request | re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | This | re_bad.py:36:22:36:28 | ControlFlowNode for request | user-provided value | re_bad.py:37:5:37:41 | ControlFlowNode for Attribute() | re.search | diff --git a/python/ql/test/query-tests/Security/CWE-730-RegexInjection/RegexInjection.qlref b/python/ql/test/query-tests/Security/CWE-730-RegexInjection/RegexInjection.qlref new file mode 100644 index 00000000000..53f8be2a625 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-730-RegexInjection/RegexInjection.qlref @@ -0,0 +1 @@ +Security/CWE-730/RegexInjection.ql diff --git a/python/ql/test/experimental/query-tests/Security/CWE-730/re_bad.py b/python/ql/test/query-tests/Security/CWE-730-RegexInjection/re_bad.py similarity index 100% rename from python/ql/test/experimental/query-tests/Security/CWE-730/re_bad.py rename to python/ql/test/query-tests/Security/CWE-730-RegexInjection/re_bad.py diff --git a/python/ql/test/experimental/query-tests/Security/CWE-730/re_good.py b/python/ql/test/query-tests/Security/CWE-730-RegexInjection/re_good.py similarity index 100% rename from python/ql/test/experimental/query-tests/Security/CWE-730/re_good.py rename to python/ql/test/query-tests/Security/CWE-730-RegexInjection/re_good.py diff --git a/ruby/.codeqlmanifest.json b/ruby/.codeqlmanifest.json new file mode 100644 index 00000000000..7effe0a77c5 --- /dev/null +++ b/ruby/.codeqlmanifest.json @@ -0,0 +1,10 @@ +{ + "provide": [ + "ql/lib/qlpack.yml", + "ql/src/qlpack.yml", + "ql/consistency-queries/qlpack.yml", + "ql/test/qlpack.yml", + "ql/examples/qlpack.yml", + "extractor-pack/codeql-extractor.yml" + ] +} diff --git a/ruby/.gitattributes b/ruby/.gitattributes new file mode 100644 index 00000000000..f4c10fb0a91 --- /dev/null +++ b/ruby/.gitattributes @@ -0,0 +1 @@ +Cargo.lock -diff -whitespace diff --git a/ruby/.gitignore b/ruby/.gitignore new file mode 100644 index 00000000000..2b3f9bb1af2 --- /dev/null +++ b/ruby/.gitignore @@ -0,0 +1,8 @@ +/target +extractor-pack +.vscode/launch.json +.cache +ql/test/**/*.testproj +ql/test/**/*.actual +ql/test/**/CONSISTENCY +.codeql diff --git a/ruby/.vscode/tasks.json b/ruby/.vscode/tasks.json new file mode 100644 index 00000000000..c28cd789fca --- /dev/null +++ b/ruby/.vscode/tasks.json @@ -0,0 +1,14 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "cargo", + "subcommand": "build", + "problemMatcher": [ + "$rustc" + ], + "group": "build", + "label": "Rust: cargo build" + } + ] +} \ No newline at end of file diff --git a/ruby/CONTRIBUTING.md b/ruby/CONTRIBUTING.md new file mode 100644 index 00000000000..792385e766e --- /dev/null +++ b/ruby/CONTRIBUTING.md @@ -0,0 +1,64 @@ +## Contributing + +Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great. + +Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [project's open source license](LICENSE). + +Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. + +## Building and testing + +See [Developer information](docs/HOWTO.md) for information on building the Ruby extractor. There is no need to rebuild the extractor if you are only developing queries. + +1. Install the CodeQL CLI as described in [Getting started with the CodeQL CLI](https://codeql.github.com/docs/codeql-cli/getting-started-with-the-codeql-cli/). + +2. Ensure that `<extraction-root>/codeql` is in your `PATH`. + +3. Clone this repository into `<extraction-root>/codeql-ruby` and change to this directory. + +4. To run all tests in a directory and its subdirectories, run `codeql test run <directory>`, for example `codeql test run ql/test/query-tests/security`. + +6. To run an individual test, run `codeql test run <filename>`, where `<filename>` is a `.ql` or `.qlref` file, for example `codeql test run ql/test/query-tests/security/cwe-078/CommandInjection.qlref`. + +## Adding a new query + +If you have an idea for a query that you would like to share with other CodeQL users, please open a pull request to add it to this repository. +Follow the steps below to help other users understand what your query does, and to ensure that your query is consistent with the other CodeQL queries. + +1. **Consult the documentation for query writers** + + There is lots of useful documentation to help you write CodeQL queries, ranging from information about query file structure to language-specific tutorials. For more information on the documentation available, see [Writing CodeQL queries](https://codeql.github.com/docs/writing-codeql-queries/) and the [CodeQL documentation](https://codeql.github.com/docs). + +2. **Format your code correctly** + + All of the standard CodeQL queries and libraries are uniformly formatted for clarity and consistency, so we strongly recommend that all contributions follow the same formatting guidelines. If you use the CodeQL extension for Visual Studio Code, you can auto-format your query using the [Format Document command](https://code.visualstudio.com/docs/editor/codebasics#_formatting). For more information, see the [QL style guide](https://github.com/github/codeql/blob/main/docs/ql-style-guide.md). + +3. **Make sure your query has the correct metadata** + + Query metadata is used to identify your query and make sure the query results are displayed properly. + The most important metadata to include are the `@name`, `@description`, and the `@kind`. + Other metadata properties (`@precision`, `@severity`, and `@tags`) are usually added after the query has been reviewed by the maintainers. + For more information on writing query metadata, see the [Query metadata style guide](https://github.com/github/codeql/blob/main/docs/query-metadata-style-guide.md). + +4. **Make sure the `select` statement is compatible with the query type** + + The `select` statement of your query must be compatible with the query type (determined by the `@kind` metadata property) for alert or path results to be displayed correctly in LGTM and Visual Studio Code. + For more information on `select` statement format, see [About CodeQL queries](https://codeql.github.com/docs/writing-codeql-queries/about-codeql-queries/#select-clause) on the [CodeQL documentation](https://codeql.github.com/docs) site. + +5. **Write a query help file** + + Query help files explain the purpose of your query to other users. Write your query help in a `.qhelp` file and save it in the same directory as your new query. + For more information on writing query help, see the [Query help style guide](https://github.com/github/codeql/blob/main/docs/query-help-style-guide.md). + +6. **Maintain backwards compatibility** + +The standard CodeQL libraries must evolve in a backwards compatible manner. If any backwards incompatible changes need to be made, the existing API must first be marked as deprecated. This is done by adding a `deprecated` annotation along with a QLDoc reference to the replacement API. Only after at least one full release cycle has elapsed may the old API be removed. + +In addition to contributions to our standard queries and libraries, we also welcome contributions of a more experimental nature, which do not need to fulfill all the requirements listed above. See the guidelines for [experimental queries and libraries](ql/docs/experimental.md) for details. + +## Resources + +- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) +- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/) +- [GitHub Help](https://help.github.com) +- [A Note About Git Commit Messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) diff --git a/ruby/Cargo.lock b/ruby/Cargo.lock new file mode 100644 index 00000000000..f2233f30072 Binary files /dev/null and b/ruby/Cargo.lock differ diff --git a/ruby/Cargo.toml b/ruby/Cargo.toml new file mode 100644 index 00000000000..7acbd36afa6 --- /dev/null +++ b/ruby/Cargo.toml @@ -0,0 +1,7 @@ +[workspace] +members = [ + "autobuilder", + "extractor", + "generator", + "node-types", +] diff --git a/ruby/Makefile b/ruby/Makefile new file mode 100644 index 00000000000..02d7c92a46e --- /dev/null +++ b/ruby/Makefile @@ -0,0 +1,71 @@ +all: extractor dbscheme + +ifeq ($(OS),Windows_NT) +EXE = .exe +CODEQL_PLATFORM = win64 +else +EXE = +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Linux) +CODEQL_PLATFORM = linux64 +endif +ifeq ($(UNAME_S),Darwin) +CODEQL_PLATFORM = osx64 +endif +endif + +FILES=codeql-extractor.yml\ + tools/qltest.cmd\ + tools/index-files.sh\ + tools/index-files.cmd\ + tools/autobuild.sh\ + tools/qltest.sh\ + tools/autobuild.cmd\ + ql/lib/ruby.dbscheme.stats\ + ql/lib/ruby.dbscheme + +BIN_FILES=target/release/ruby-extractor$(EXE) target/release/ruby-autobuilder$(EXE) + +extractor-common: + rm -rf build + mkdir build + mkdir build/codeql-extractor-ruby + cp codeql-extractor.yml ql/lib/ruby.dbscheme ql/lib/ruby.dbscheme.stats build/codeql-extractor-ruby + cp -r tools build/codeql-extractor-ruby/ + +.PHONY: tools +tools: $(BIN_FILES) + rm -rf tools/bin + mkdir tools/bin + cp -r target/release/ruby-autobuilder$(EXE) tools/bin/autobuilder$(EXE) + cp -r target/release/ruby-extractor$(EXE) tools/bin/extractor$(EXE) + +target/release/%$(EXE): + cargo build --release --bin $(basename $(notdir $@)) + +dbscheme: + cargo build --bin ruby-generator + cargo run -p ruby-generator -- --dbscheme ql/lib/ruby.dbscheme --library ql/lib/codeql/ruby/ast/internal/TreeSitter.qll + codeql query format -i ql/lib/codeql/ruby/ast/internal/TreeSitter.qll + +.PHONY: extractor +extractor: $(FILES) $(BIN_FILES) + rm -rf extractor-pack + mkdir extractor-pack + mkdir extractor-pack/tools + mkdir extractor-pack/tools/$(CODEQL_PLATFORM) + cp codeql-extractor.yml extractor-pack/codeql-extractor.yml + cp tools/qltest.cmd extractor-pack/tools/qltest.cmd + cp tools/index-files.sh extractor-pack/tools/index-files.sh + cp tools/index-files.cmd extractor-pack/tools/index-files.cmd + cp tools/autobuild.sh extractor-pack/tools/autobuild.sh + cp tools/qltest.sh extractor-pack/tools/qltest.sh + cp tools/autobuild.cmd extractor-pack/tools/autobuild.cmd + cp ql/lib/ruby.dbscheme.stats extractor-pack/ruby.dbscheme.stats + cp ql/lib/ruby.dbscheme extractor-pack/ruby.dbscheme + cp target/release/ruby-extractor$(EXE) extractor-pack/tools/$(CODEQL_PLATFORM)/extractor$(EXE) + cp target/release/ruby-autobuilder$(EXE) extractor-pack/tools/$(CODEQL_PLATFORM)/autobuilder$(EXE) + +test: extractor dbscheme + codeql pack install ql/test + codeql test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path . --consistency-queries ql/consistency-queries ql/test diff --git a/ruby/README.md b/ruby/README.md new file mode 100644 index 00000000000..8ce739089a4 --- /dev/null +++ b/ruby/README.md @@ -0,0 +1,50 @@ +# Ruby analysis support for CodeQL + +This open-source repository contains the extractor, CodeQL libraries, and queries that power Ruby +support in [LGTM](https://lgtm.com) and the other CodeQL products that [GitHub](https://github.com) +makes available to its customers worldwide. + +It contains two major components: + - an extractor, written in Rust, that parses Ruby source code and converts it into a database + that can be queried using CodeQL. + - static analysis libraries and queries written in [CodeQL](https://codeql.github.com/docs/) that can be + used to analyze such a database to find coding mistakes or security vulnerabilities. + +The goal of this project is to provide comprehensive static analysis support for Ruby in CodeQL. + +For the queries and libraries that power CodeQL support for other languages, visit [the CodeQL +repository](https://github.com/github/codeql). + +## Installation + +Simply clone this repository. There are no external dependencies. + +If you want to use the CodeQL extension for Visual Studio Code, import this repository into your VS +Code workspace. + +## Usage + +To analyze a Ruby codebase, either use the [CodeQL command-line +interface](https://codeql.github.com/docs/codeql-cli/) to create a database yourself, or +download a pre-built database from [LGTM.com](https://lgtm.com/). You can then run any of the +queries contained in this repository either on the command line or using the VS Code extension. + +Note that the [lgtm.com](https://github.com/github/codeql-ruby/tree/lgtm.com) branch of this +repository corresponds to the version of the queries that is currently deployed on LGTM.com. +The [main](https://github.com/github/codeql-ruby/tree/main) branch may contain changes that +have not been deployed yet, so you may need to upgrade databases downloaded from [LGTM.com](https://lgtm.com) before +running queries on them. + +## Contributions + +Contributions are welcome! Please see our [contribution guidelines](CONTRIBUTING.md) and our +[code of conduct](CODE_OF_CONDUCT.md) for details on how to participate in our community. + +## Licensing + +The code in this repository is licensed under the [MIT license](LICENSE). + +## Resources + +- [Writing CodeQL queries](https://codeql.github.com/docs/writing-codeql-queries/) +- [CodeQL documentation](https://codeql.github.com/docs/) diff --git a/ruby/actions/create-extractor-pack/action.yml b/ruby/actions/create-extractor-pack/action.yml new file mode 100644 index 00000000000..75faf327c13 --- /dev/null +++ b/ruby/actions/create-extractor-pack/action.yml @@ -0,0 +1,16 @@ +name: Build Ruby CodeQL pack +description: Builds the Ruby CodeQL pack +runs: + using: composite + steps: + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + ruby/target + key: ${{ runner.os }}-qltest-cargo-${{ hashFiles('ruby/**/Cargo.lock') }} + - name: Build Extractor + shell: bash + run: scripts/create-extractor-pack.sh + working-directory: ruby diff --git a/ruby/autobuilder/Cargo.toml b/ruby/autobuilder/Cargo.toml new file mode 100644 index 00000000000..c77b6895dff --- /dev/null +++ b/ruby/autobuilder/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ruby-autobuilder" +version = "0.1.0" +authors = ["GitHub"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/ruby/autobuilder/src/main.rs b/ruby/autobuilder/src/main.rs new file mode 100644 index 00000000000..18892ae2d5c --- /dev/null +++ b/ruby/autobuilder/src/main.rs @@ -0,0 +1,39 @@ +use std::env; +use std::path::PathBuf; +use std::process::Command; + +fn main() -> std::io::Result<()> { + let dist = env::var("CODEQL_DIST").expect("CODEQL_DIST not set"); + let db = env::var("CODEQL_EXTRACTOR_RUBY_WIP_DATABASE") + .expect("CODEQL_EXTRACTOR_RUBY_WIP_DATABASE not set"); + let codeql = if env::consts::OS == "windows" { + "codeql.exe" + } else { + "codeql" + }; + let codeql: PathBuf = [&dist, codeql].iter().collect(); + let mut cmd = Command::new(codeql); + cmd.arg("database") + .arg("index-files") + .arg("--include-extension=.rb") + .arg("--include-extension=.erb") + .arg("--include-extension=.gemspec") + .arg("--include=**/Gemfile") + .arg("--size-limit=5m") + .arg("--language=ruby") + .arg("--working-dir=.") + .arg(db); + + for line in env::var("LGTM_INDEX_FILTERS") + .unwrap_or_default() + .split('\n') + { + if let Some(stripped) = line.strip_prefix("include:") { + cmd.arg("--include").arg(stripped); + } else if let Some(stripped) = line.strip_prefix("exclude:") { + cmd.arg("--exclude").arg(stripped); + } + } + let exit = &cmd.spawn()?.wait()?; + std::process::exit(exit.code().unwrap_or(1)) +} diff --git a/ruby/change-notes/2021-10-14-codeql-ruby-beta.md b/ruby/change-notes/2021-10-14-codeql-ruby-beta.md new file mode 100644 index 00000000000..83c9a96980f --- /dev/null +++ b/ruby/change-notes/2021-10-14-codeql-ruby-beta.md @@ -0,0 +1,2 @@ +codescanning +* Open-sourcing the Ruby extractor and CodeQL queries as part of the [public beta](https://github.com/github/roadmap/issues/136) release. diff --git a/ruby/change-notes/2021-10-20-path-injection.md b/ruby/change-notes/2021-10-20-path-injection.md new file mode 100644 index 00000000000..e9463bc948a --- /dev/null +++ b/ruby/change-notes/2021-10-20-path-injection.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* A new query (`rb/path-injection`) has been added. The query finds file operations using paths that derive from user input without being sanitized. diff --git a/ruby/codeql-extractor.yml b/ruby/codeql-extractor.yml new file mode 100644 index 00000000000..534e91f74ac --- /dev/null +++ b/ruby/codeql-extractor.yml @@ -0,0 +1,14 @@ +name: "ruby" +display_name: "Ruby" +version: 0.1.0 +column_kind: "utf8" +legacy_qltest_extraction: true +file_types: + - name: ruby + display_name: Ruby files + extensions: + - .rb + - name: erb + display_name: Ruby templates + extensions: + - .erb diff --git a/ruby/doc/HOWTO.md b/ruby/doc/HOWTO.md new file mode 100644 index 00000000000..8952dc9561f --- /dev/null +++ b/ruby/doc/HOWTO.md @@ -0,0 +1,47 @@ +# Developer information + +This document contains information about common development tasks. + +## Building the tools from source + +[Install Rust](https://www.rust-lang.org/tools/install), then run: + +```bash +cargo build --release +``` + +## Generating the database schema and QL library + +The generated `ql/lib/ruby.dbscheme` and `ql/lib/codeql/ruby/ast/internal/TreeSitter.qll` files are included in the repository, but they can be re-generated as follows: + +```bash +# Run the generator +cargo run --release -p ruby-generator -- --dbscheme ql/lib/ruby.dbscheme --library ql/lib/codeql/ruby/ast/internal/TreeSitter.qll +# Then auto-format the QL library +codeql query format -i ql/lib/codeql/ruby/ast/internal/TreeSitter.qll +``` + +## Building a CodeQL database for a Ruby program + +First, get an extractor pack. There are two options: + +1. Either download the latest `codeql-ruby-pack` from Actions and unzip it twice, or +2. Run `scripts/create-extractor-pack.sh` (Linux/Mac) or `scripts\create-extractor-pack.ps1` (Windows PowerShell) and the pack will be created in the `extractor-pack` directory. + +Then run + +```bash +codeql database create <database-path> -l ruby -s <project-source-path> --search-path <extractor-pack-path> +``` + +## Running qltests + +Run + +```bash +codeql test run <test-path> --search-path <repository-root-path> +``` + +## Writing database upgrade scripts + +See [this guide](prepare-db-upgrade.md). diff --git a/ruby/doc/prepare-db-upgrade.md b/ruby/doc/prepare-db-upgrade.md new file mode 100644 index 00000000000..239eef3cd27 --- /dev/null +++ b/ruby/doc/prepare-db-upgrade.md @@ -0,0 +1,91 @@ +# Upgrading the Ruby database schema + +The schema (`ql/lib/ruby.dbscheme`) is automatically generated from tree-sitter's `node-types.json`. When the tree-sitter grammar changes, the database schema is likely to change as well, and we need to write an upgrade script. This document explains how to do that. + +## Process Overview + + 1. Commit the change to `ruby.dbscheme` (along with any library updates required to work with the change). + 2. Run `scripts/prepare-db-upgrade.sh`. + 3. Fill in the details in `upgrade.properties`, and add any required upgrade queries. + +It may be helpful to look at some of the existing upgrade scripts, to see how they work. + +## Details + +### Generating a Ruby database upgrade script + +Schema changes need to be accompanied by scripts that allow us to upgrade databases that were generated with older versions of the tools, so they can use new query functionality (albeit with possibly degraded results). + +#### The easy (mostly automatic) way + +The easy way to generate an upgrade script is to run the `scripts/prepare-db-upgrade.sh` script. This will generate a skeleton upgrade directory, leaving you to fill out the details in the `upgrade.properties` file. + +#### upgrade.properties + +It will look something like: + +``` +description: what it does +compatibility: partial +some_relation.rel: run some_relation.qlo +``` + +The `description` field is a textual description of the aim of the upgrade. + +The `compatibility` field takes one of four values: + + * **full**: results from the upgraded snapshot will be identical to results from a snapshot built with the new version of the toolchain. + + * **backwards**: the step is safe and preserves the meaning of the old database, but new features may not work correctly on the upgraded snapshot. + + * **partial**: the step is safe and preserves the meaning of the old database, but you would get better results if you rebuilt the snapshot with the new version of the toolchain. + + * **breaking**: the step is unsafe and will prevent certain queries from working. + +The `some_relation.rel` line(s) are the actions required to do the database upgrade. Do a diff on the the new vs old `.dbscheme` file to get an idea of what they have to achieve. Sometimes you won't need any upgrade commands – this happens when the dbscheme has changed in "cosmetic" ways, for example by adding/removing comments or changing union type relationships, but still retains the same on-disk format for all tables; the purpose of the upgrade script is then to document the fact that it's safe to replace the old dbscheme with the new one. + +Some typical upgrade commands look like this: + +``` +// Delete a relation that has been replaced in the new scheme +obsolete.rel: delete + +// Create a new version of a table by applying a simple RA expression to an +// existing table. The example duplicates the 'id' column of input.rel as +// the last column of etended.rel, perhaps to record our best guess at +// newly-populated "source declaration" information. +extended.rel: reorder input.rel (int id, string name, int parent) id name parent id + +// Create relationname.rel by running relationname.qlo and writing the query +// results as a .rel file. The query file should be named relationname.ql and +// should be placed in the upgrade directory. It should avoid using the default +// QLL library, and will run in the context of the *old* dbscheme. +relationname.rel: run relationname.qlo +``` + +#### Testing your upgrade script + +Upgrade scripts can be a little bit fiddly, so it's essential that you test them. You might do so as follows: + + 1. Create a snapshot of your favourite project using the old version of the code. + + 2. Switch to the new version of the code. + + 3. Try to run some queries that will depend on your upgrade script working correctly. + + 4. Observe the upgrade being performed in the query server log. + + 5. Verify that your queries produced sensible results. + +#### Doing the upgrade manually + +To create the upgrade directory manually, without using `scripts/prepare-db-upgrade.sh`: + +1. Get a hash of the old `.dbscheme` file, from just before your changes. You can do this by checking out the code prior to your changes and running `git hash-object ql/lib/ruby.dbscheme` + +2. Go back to your branch and create an upgrade directory with that hash as its name, for example: `mkdir ql/lib/upgrades/454f1e15151422355049dc4f1f0486a03baeffef` + +3. Copy the old `.dbscheme` file to that directory, using the name old.dbscheme. + `cp ql/lib/ruby.dbscheme ql/lib/upgrades/454f1e15151422355049dc4f1f0486a03baeffef/old.dbscheme` + +4. Put a copy of your new `.dbscheme` file in that directory and create an `upgrade.properties` file (as described above). diff --git a/ruby/extractor/Cargo.toml b/ruby/extractor/Cargo.toml new file mode 100644 index 00000000000..efc5a12159d --- /dev/null +++ b/ruby/extractor/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "ruby-extractor" +version = "0.1.0" +authors = ["GitHub"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +flate2 = "1.0" +node-types = { path = "../node-types" } +tree-sitter = "0.19" +tree-sitter-embedded-template = "0.19" +tree-sitter-ruby = { git = "https://github.com/tree-sitter/tree-sitter-ruby.git", rev = "bb6a42e42b048627a74a127d3e0184c1eef01de9" } +clap = "2.33" +tracing = "0.1" +tracing-subscriber = { version = "0.2", features = ["env-filter"] } +rayon = "1.5.0" +num_cpus = "1.13.0" +regex = "1.4.3" diff --git a/ruby/extractor/src/extractor.rs b/ruby/extractor/src/extractor.rs new file mode 100644 index 00000000000..7c83f51e0dc --- /dev/null +++ b/ruby/extractor/src/extractor.rs @@ -0,0 +1,829 @@ +use node_types::{EntryKind, Field, NodeTypeMap, Storage, TypeName}; +use std::borrow::Cow; +use std::collections::BTreeMap as Map; +use std::collections::BTreeSet as Set; +use std::fmt; +use std::io::Write; +use std::path::Path; + +use tracing::{error, info, span, Level}; +use tree_sitter::{Language, Node, Parser, Range, Tree}; + +pub struct TrapWriter { + /// The accumulated trap entries + trap_output: Vec<TrapEntry>, + /// A counter for generating fresh labels + counter: u32, + /// cache of global keys + global_keys: std::collections::HashMap<String, Label>, +} + +pub fn new_trap_writer() -> TrapWriter { + TrapWriter { + counter: 0, + trap_output: Vec::new(), + global_keys: std::collections::HashMap::new(), + } +} + +impl TrapWriter { + /// Gets a label that will hold the unique ID of the passed string at import time. + /// This can be used for incrementally importable TRAP files -- use globally unique + /// strings to compute a unique ID for table tuples. + /// + /// Note: You probably want to make sure that the key strings that you use are disjoint + /// for disjoint column types; the standard way of doing this is to prefix (or append) + /// the column type name to the ID. Thus, you might identify methods in Java by the + /// full ID "methods_com.method.package.DeclaringClass.method(argumentList)". + + fn fresh_id(&mut self) -> Label { + let label = Label(self.counter); + self.counter += 1; + self.trap_output.push(TrapEntry::FreshId(label)); + label + } + + fn global_id(&mut self, key: &str) -> (Label, bool) { + if let Some(label) = self.global_keys.get(key) { + return (*label, false); + } + let label = Label(self.counter); + self.counter += 1; + self.global_keys.insert(key.to_owned(), label); + self.trap_output + .push(TrapEntry::MapLabelToKey(label, key.to_owned())); + (label, true) + } + + fn add_tuple(&mut self, table_name: &str, args: Vec<Arg>) { + self.trap_output + .push(TrapEntry::GenericTuple(table_name.to_owned(), args)) + } + + fn populate_file(&mut self, absolute_path: &Path) -> Label { + let (file_label, fresh) = self.global_id(&full_id_for_file(absolute_path)); + if fresh { + self.add_tuple( + "files", + vec![ + Arg::Label(file_label), + Arg::String(normalize_path(absolute_path)), + ], + ); + self.populate_parent_folders(file_label, absolute_path.parent()); + } + file_label + } + + fn populate_empty_file(&mut self) -> Label { + let (file_label, fresh) = self.global_id("empty;sourcefile"); + if fresh { + self.add_tuple( + "files", + vec![Arg::Label(file_label), Arg::String("".to_string())], + ); + } + file_label + } + + pub fn populate_empty_location(&mut self) { + let file_label = self.populate_empty_file(); + self.location(file_label, 0, 0, 0, 0); + } + + fn populate_parent_folders(&mut self, child_label: Label, path: Option<&Path>) { + let mut path = path; + let mut child_label = child_label; + loop { + match path { + None => break, + Some(folder) => { + let (folder_label, fresh) = self.global_id(&full_id_for_folder(folder)); + self.add_tuple( + "containerparent", + vec![Arg::Label(folder_label), Arg::Label(child_label)], + ); + if fresh { + self.add_tuple( + "folders", + vec![ + Arg::Label(folder_label), + Arg::String(normalize_path(folder)), + ], + ); + path = folder.parent(); + child_label = folder_label; + } else { + break; + } + } + } + } + } + + fn location( + &mut self, + file_label: Label, + start_line: usize, + start_column: usize, + end_line: usize, + end_column: usize, + ) -> Label { + let (loc_label, fresh) = self.global_id(&format!( + "loc,{{{}}},{},{},{},{}", + file_label, start_line, start_column, end_line, end_column + )); + if fresh { + self.add_tuple( + "locations_default", + vec![ + Arg::Label(loc_label), + Arg::Label(file_label), + Arg::Int(start_line), + Arg::Int(start_column), + Arg::Int(end_line), + Arg::Int(end_column), + ], + ); + } + loc_label + } + + fn comment(&mut self, text: String) { + self.trap_output.push(TrapEntry::Comment(text)); + } + + pub fn output(self, writer: &mut dyn Write) -> std::io::Result<()> { + write!(writer, "{}", Program(self.trap_output)) + } +} + +/// Extracts the source file at `path`, which is assumed to be canonicalized. +pub fn extract( + language: Language, + language_prefix: &str, + schema: &NodeTypeMap, + trap_writer: &mut TrapWriter, + path: &Path, + source: &[u8], + ranges: &[Range], +) -> std::io::Result<()> { + let span = span!( + Level::TRACE, + "extract", + file = %path.display() + ); + + let _enter = span.enter(); + + info!("extracting: {}", path.display()); + + let mut parser = Parser::new(); + parser.set_language(language).unwrap(); + parser.set_included_ranges(ranges).unwrap(); + let tree = parser.parse(&source, None).expect("Failed to parse file"); + trap_writer.comment(format!("Auto-generated TRAP file for {}", path.display())); + let file_label = &trap_writer.populate_file(path); + let mut visitor = Visitor { + source, + trap_writer, + // TODO: should we handle path strings that are not valid UTF8 better? + path: format!("{}", path.display()), + file_label: *file_label, + toplevel_child_counter: 0, + stack: Vec::new(), + language_prefix, + schema, + }; + traverse(&tree, &mut visitor); + + parser.reset(); + Ok(()) +} + +/// Escapes a string for use in a TRAP key, by replacing special characters with +/// HTML entities. +fn escape_key<'a, S: Into<Cow<'a, str>>>(key: S) -> Cow<'a, str> { + fn needs_escaping(c: char) -> bool { + matches!(c, '&' | '{' | '}' | '"' | '@' | '#') + } + + let key = key.into(); + if key.contains(needs_escaping) { + let mut escaped = String::with_capacity(2 * key.len()); + for c in key.chars() { + match c { + '&' => escaped.push_str("&"), + '{' => escaped.push_str("{"), + '}' => escaped.push_str("}"), + '"' => escaped.push_str("""), + '@' => escaped.push_str("@"), + '#' => escaped.push_str("#"), + _ => escaped.push(c), + } + } + Cow::Owned(escaped) + } else { + key + } +} + +/// Normalizes the path according the common CodeQL specification. Assumes that +/// `path` has already been canonicalized using `std::fs::canonicalize`. +fn normalize_path(path: &Path) -> String { + if cfg!(windows) { + // The way Rust canonicalizes paths doesn't match the CodeQL spec, so we + // have to do a bit of work removing certain prefixes and replacing + // backslashes. + let mut components: Vec<String> = Vec::new(); + for component in path.components() { + match component { + std::path::Component::Prefix(prefix) => match prefix.kind() { + std::path::Prefix::Disk(letter) | std::path::Prefix::VerbatimDisk(letter) => { + components.push(format!("{}:", letter as char)); + } + std::path::Prefix::Verbatim(x) | std::path::Prefix::DeviceNS(x) => { + components.push(x.to_string_lossy().to_string()); + } + std::path::Prefix::UNC(server, share) + | std::path::Prefix::VerbatimUNC(server, share) => { + components.push(server.to_string_lossy().to_string()); + components.push(share.to_string_lossy().to_string()); + } + }, + std::path::Component::Normal(n) => { + components.push(n.to_string_lossy().to_string()); + } + std::path::Component::RootDir => {} + std::path::Component::CurDir => {} + std::path::Component::ParentDir => {} + } + } + components.join("/") + } else { + // For other operating systems, we can use the canonicalized path + // without modifications. + format!("{}", path.display()) + } +} + +fn full_id_for_file(path: &Path) -> String { + format!("{};sourcefile", escape_key(&normalize_path(path))) +} + +fn full_id_for_folder(path: &Path) -> String { + format!("{};folder", escape_key(&normalize_path(path))) +} + +struct ChildNode { + field_name: Option<&'static str>, + label: Label, + type_name: TypeName, +} + +struct Visitor<'a> { + /// The file path of the source code (as string) + path: String, + /// The label to use whenever we need to refer to the `@file` entity of this + /// source file. + file_label: Label, + /// The source code as a UTF-8 byte array + source: &'a [u8], + /// A TrapWriter to accumulate trap entries + trap_writer: &'a mut TrapWriter, + /// A counter for top-level child nodes + toplevel_child_counter: usize, + /// Language prefix + language_prefix: &'a str, + /// A lookup table from type name to node types + schema: &'a NodeTypeMap, + /// A stack for gathering information from child nodes. Whenever a node is + /// entered the parent's [Label], child counter, and an empty list is pushed. + /// All children append their data to the the list. When the visitor leaves a + /// node the list containing the child data is popped from the stack and + /// matched against the dbscheme for the node. If the expectations are met + /// the corresponding row definitions are added to the trap_output. + stack: Vec<(Label, usize, Vec<ChildNode>)>, +} + +impl Visitor<'_> { + fn record_parse_error( + &mut self, + error_message: String, + full_error_message: String, + loc: Label, + ) { + error!("{}", full_error_message); + let id = self.trap_writer.fresh_id(); + self.trap_writer.add_tuple( + "diagnostics", + vec![ + Arg::Label(id), + Arg::Int(40), // severity 40 = error + Arg::String("parse_error".to_string()), + Arg::String(error_message), + Arg::String(full_error_message), + Arg::Label(loc), + ], + ); + } + + fn record_parse_error_for_node( + &mut self, + error_message: String, + full_error_message: String, + node: Node, + ) { + let (start_line, start_column, end_line, end_column) = location_for(self.source, node); + let loc = self.trap_writer.location( + self.file_label, + start_line, + start_column, + end_line, + end_column, + ); + self.record_parse_error(error_message, full_error_message, loc); + } + + fn enter_node(&mut self, node: Node) -> bool { + if node.is_error() || node.is_missing() { + let error_message = if node.is_missing() { + format!("parse error: expecting '{}'", node.kind()) + } else { + "parse error".to_string() + }; + let full_error_message = format!( + "{}:{}: {}", + &self.path, + node.start_position().row + 1, + error_message + ); + self.record_parse_error_for_node(error_message, full_error_message, node); + return false; + } + + let id = self.trap_writer.fresh_id(); + + self.stack.push((id, 0, Vec::new())); + true + } + + fn leave_node(&mut self, field_name: Option<&'static str>, node: Node) { + if node.is_error() || node.is_missing() { + return; + } + let (id, _, child_nodes) = self.stack.pop().expect("Vistor: empty stack"); + let (start_line, start_column, end_line, end_column) = location_for(self.source, node); + let loc = self.trap_writer.location( + self.file_label, + start_line, + start_column, + end_line, + end_column, + ); + let table = self + .schema + .get(&TypeName { + kind: node.kind().to_owned(), + named: node.is_named(), + }) + .unwrap(); + let mut valid = true; + let (parent_id, parent_index) = match self.stack.last_mut() { + Some(p) if !node.is_extra() => { + p.1 += 1; + (p.0, p.1 - 1) + } + _ => { + self.toplevel_child_counter += 1; + (self.file_label, self.toplevel_child_counter - 1) + } + }; + match &table.kind { + EntryKind::Token { kind_id, .. } => { + self.trap_writer.add_tuple( + &format!("{}_ast_node_parent", self.language_prefix), + vec![ + Arg::Label(id), + Arg::Label(parent_id), + Arg::Int(parent_index), + ], + ); + self.trap_writer.add_tuple( + &format!("{}_tokeninfo", self.language_prefix), + vec![ + Arg::Label(id), + Arg::Int(*kind_id), + sliced_source_arg(self.source, node), + Arg::Label(loc), + ], + ); + } + EntryKind::Table { + fields, + name: table_name, + } => { + if let Some(args) = self.complex_node(&node, fields, &child_nodes, id) { + self.trap_writer.add_tuple( + &format!("{}_ast_node_parent", self.language_prefix), + vec![ + Arg::Label(id), + Arg::Label(parent_id), + Arg::Int(parent_index), + ], + ); + let mut all_args = vec![Arg::Label(id)]; + all_args.extend(args); + all_args.push(Arg::Label(loc)); + self.trap_writer.add_tuple(table_name, all_args); + } + } + _ => { + let error_message = format!("unknown table type: '{}'", node.kind()); + let full_error_message = format!( + "{}:{}: {}", + &self.path, + node.start_position().row + 1, + error_message + ); + self.record_parse_error(error_message, full_error_message, loc); + + valid = false; + } + } + if valid && !node.is_extra() { + // Extra nodes are independent root nodes and do not belong to the parent node + // Therefore we should not register them in the parent vector + if let Some(parent) = self.stack.last_mut() { + parent.2.push(ChildNode { + field_name, + label: id, + type_name: TypeName { + kind: node.kind().to_owned(), + named: node.is_named(), + }, + }); + }; + } + } + + fn complex_node( + &mut self, + node: &Node, + fields: &[Field], + child_nodes: &[ChildNode], + parent_id: Label, + ) -> Option<Vec<Arg>> { + let mut map: Map<&Option<String>, (&Field, Vec<Arg>)> = Map::new(); + for field in fields { + map.insert(&field.name, (field, Vec::new())); + } + for child_node in child_nodes { + if let Some((field, values)) = map.get_mut(&child_node.field_name.map(|x| x.to_owned())) + { + //TODO: handle error and missing nodes + if self.type_matches(&child_node.type_name, &field.type_info) { + if let node_types::FieldTypeInfo::ReservedWordInt(int_mapping) = + &field.type_info + { + // We can safely unwrap because type_matches checks the key is in the map. + let (int_value, _) = int_mapping.get(&child_node.type_name.kind).unwrap(); + values.push(Arg::Int(*int_value)); + } else { + values.push(Arg::Label(child_node.label)); + } + } else if field.name.is_some() { + let error_message = format!( + "type mismatch for field {}::{} with type {:?} != {:?}", + node.kind(), + child_node.field_name.unwrap_or("child"), + child_node.type_name, + field.type_info + ); + let full_error_message = format!( + "{}:{}: {}", + &self.path, + node.start_position().row + 1, + error_message + ); + self.record_parse_error_for_node(error_message, full_error_message, *node); + } + } else if child_node.field_name.is_some() || child_node.type_name.named { + let error_message = format!( + "value for unknown field: {}::{} and type {:?}", + node.kind(), + &child_node.field_name.unwrap_or("child"), + &child_node.type_name + ); + let full_error_message = format!( + "{}:{}: {}", + &self.path, + node.start_position().row + 1, + error_message + ); + self.record_parse_error_for_node(error_message, full_error_message, *node); + } + } + let mut args = Vec::new(); + let mut is_valid = true; + for field in fields { + let child_values = &map.get(&field.name).unwrap().1; + match &field.storage { + Storage::Column { name: column_name } => { + if child_values.len() == 1 { + args.push(child_values.first().unwrap().clone()); + } else { + is_valid = false; + let error_message = format!( + "{} for field: {}::{}", + if child_values.is_empty() { + "missing value" + } else { + "too many values" + }, + node.kind(), + column_name + ); + let full_error_message = format!( + "{}:{}: {}", + &self.path, + node.start_position().row + 1, + error_message + ); + self.record_parse_error_for_node(error_message, full_error_message, *node); + } + } + Storage::Table { + name: table_name, + has_index, + column_name: _, + } => { + for (index, child_value) in child_values.iter().enumerate() { + if !*has_index && index > 0 { + error!( + "{}:{}: too many values for field: {}::{}", + &self.path, + node.start_position().row + 1, + node.kind(), + table_name, + ); + break; + } + let mut args = vec![Arg::Label(parent_id)]; + if *has_index { + args.push(Arg::Int(index)) + } + args.push(child_value.clone()); + self.trap_writer.add_tuple(table_name, args); + } + } + } + } + if is_valid { + Some(args) + } else { + None + } + } + + fn type_matches(&self, tp: &TypeName, type_info: &node_types::FieldTypeInfo) -> bool { + match type_info { + node_types::FieldTypeInfo::Single(single_type) => { + if tp == single_type { + return true; + } + if let EntryKind::Union { members } = &self.schema.get(single_type).unwrap().kind { + if self.type_matches_set(tp, members) { + return true; + } + } + } + node_types::FieldTypeInfo::Multiple { types, .. } => { + return self.type_matches_set(tp, types); + } + + node_types::FieldTypeInfo::ReservedWordInt(int_mapping) => { + return !tp.named && int_mapping.contains_key(&tp.kind) + } + } + false + } + + fn type_matches_set(&self, tp: &TypeName, types: &Set<TypeName>) -> bool { + if types.contains(tp) { + return true; + } + for other in types.iter() { + if let EntryKind::Union { members } = &self.schema.get(other).unwrap().kind { + if self.type_matches_set(tp, members) { + return true; + } + } + } + false + } +} + +// Emit a slice of a source file as an Arg. +fn sliced_source_arg(source: &[u8], n: Node) -> Arg { + let range = n.byte_range(); + Arg::String(String::from_utf8_lossy(&source[range.start..range.end]).into_owned()) +} + +// Emit a pair of `TrapEntry`s for the provided node, appropriately calibrated. +// The first is the location and label definition, and the second is the +// 'Located' entry. +fn location_for(source: &[u8], n: Node) -> (usize, usize, usize, usize) { + // Tree-sitter row, column values are 0-based while CodeQL starts + // counting at 1. In addition Tree-sitter's row and column for the + // end position are exclusive while CodeQL's end positions are inclusive. + // This means that all values should be incremented by 1 and in addition the + // end position needs to be shift 1 to the left. In most cases this means + // simply incrementing all values except the end column except in cases where + // the end column is 0 (start of a line). In such cases the end position must be + // set to the end of the previous line. + let start_line = n.start_position().row + 1; + let start_col = n.start_position().column + 1; + let mut end_line = n.end_position().row + 1; + let mut end_col = n.end_position().column; + if start_line > end_line || start_line == end_line && start_col > end_col { + // the range is empty, clip it to sensible values + end_line = start_line; + end_col = start_col - 1; + } else if end_col == 0 { + // end_col = 0 means that we are at the start of a line + // unfortunately 0 is invalid as column number, therefore + // we should update the end location to be the end of the + // previous line + let mut index = n.end_byte(); + if index > 0 && index <= source.len() { + index -= 1; + if source[index] != b'\n' { + error!("expecting a line break symbol, but none found while correcting end column value"); + } + end_line -= 1; + end_col = 1; + while index > 0 && source[index - 1] != b'\n' { + index -= 1; + end_col += 1; + } + } else { + error!( + "cannot correct end column value: end_byte index {} is not in range [1,{}]", + index, + source.len() + ); + } + } + (start_line, start_col, end_line, end_col) +} + +fn traverse(tree: &Tree, visitor: &mut Visitor) { + let cursor = &mut tree.walk(); + visitor.enter_node(cursor.node()); + let mut recurse = true; + loop { + if recurse && cursor.goto_first_child() { + recurse = visitor.enter_node(cursor.node()); + } else { + visitor.leave_node(cursor.field_name(), cursor.node()); + + if cursor.goto_next_sibling() { + recurse = visitor.enter_node(cursor.node()); + } else if cursor.goto_parent() { + recurse = false; + } else { + break; + } + } + } +} + +pub struct Program(Vec<TrapEntry>); + +impl fmt::Display for Program { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut text = String::new(); + for trap_entry in &self.0 { + text.push_str(&format!("{}\n", trap_entry)); + } + write!(f, "{}", text) + } +} + +enum TrapEntry { + /// Maps the label to a fresh id, e.g. `#123=*`. + FreshId(Label), + /// Maps the label to a key, e.g. `#7=@"foo"`. + MapLabelToKey(Label, String), + /// foo_bar(arg*) + GenericTuple(String, Vec<Arg>), + Comment(String), +} +impl fmt::Display for TrapEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + TrapEntry::FreshId(label) => write!(f, "{}=*", label), + TrapEntry::MapLabelToKey(label, key) => { + write!(f, "{}=@\"{}\"", label, key.replace("\"", "\"\"")) + } + TrapEntry::GenericTuple(name, args) => { + write!(f, "{}(", name)?; + for (index, arg) in args.iter().enumerate() { + if index > 0 { + write!(f, ",")?; + } + write!(f, "{}", arg)?; + } + write!(f, ")") + } + TrapEntry::Comment(line) => write!(f, "// {}", line), + } + } +} + +#[derive(Debug, Copy, Clone)] +// Identifiers of the form #0, #1... +struct Label(u32); + +impl fmt::Display for Label { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "#{:x}", self.0) + } +} + +// Numeric indices. +#[derive(Debug, Copy, Clone)] +struct Index(usize); + +impl fmt::Display for Index { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +// Some untyped argument to a TrapEntry. +#[derive(Debug, Clone)] +enum Arg { + Label(Label), + Int(usize), + String(String), +} + +const MAX_STRLEN: usize = 1048576; + +impl fmt::Display for Arg { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Arg::Label(x) => write!(f, "{}", x), + Arg::Int(x) => write!(f, "{}", x), + Arg::String(x) => write!( + f, + "\"{}\"", + limit_string(x, MAX_STRLEN).replace("\"", "\"\"") + ), + } + } +} + +/// Limit the length (in bytes) of a string. If the string's length in bytes is +/// less than or equal to the limit then the entire string is returned. Otherwise +/// the string is sliced at the provided limit. If there is a multi-byte character +/// at the limit then the returned slice will be slightly shorter than the limit to +/// avoid splitting that multi-byte character. +fn limit_string(string: &str, max_size: usize) -> &str { + if string.len() <= max_size { + return string; + } + let p = string.as_bytes(); + let mut index = max_size; + // We want to clip the string at [max_size]; however, the character at that position + // may span several bytes. We need to find the first byte of the character. In UTF-8 + // encoded data any byte that matches the bit pattern 10XXXXXX is not a start byte. + // Therefore we decrement the index as long as there are bytes matching this pattern. + // This ensures we cut the string at the border between one character and another. + while index > 0 && (p[index] & 0b11000000) == 0b10000000 { + index -= 1; + } + &string[0..index] +} + +#[test] +fn limit_string_test() { + assert_eq!("hello", limit_string(&"hello world".to_owned(), 5)); + assert_eq!("hi ☹", limit_string(&"hi ☹☹".to_owned(), 6)); + assert_eq!("hi ", limit_string(&"hi ☹☹".to_owned(), 5)); +} + +#[test] +fn escape_key_test() { + assert_eq!("foo!", escape_key("foo!")); + assert_eq!("foo{}", escape_key("foo{}")); + assert_eq!("{}", escape_key("{}")); + assert_eq!("", escape_key("")); + assert_eq!("/path/to/foo.rb", escape_key("/path/to/foo.rb")); + assert_eq!( + "/path/to/foo&{}"@#.rb", + escape_key("/path/to/foo&{}\"@#.rb") + ); +} diff --git a/ruby/extractor/src/main.rs b/ruby/extractor/src/main.rs new file mode 100644 index 00000000000..9a8ef5cf93a --- /dev/null +++ b/ruby/extractor/src/main.rs @@ -0,0 +1,302 @@ +mod extractor; + +extern crate num_cpus; + +use flate2::write::GzEncoder; +use rayon::prelude::*; +use std::fs; +use std::io::{BufRead, BufWriter}; +use std::path::{Path, PathBuf}; +use tree_sitter::{Language, Parser, Range}; + +enum TrapCompression { + None, + Gzip, +} + +impl TrapCompression { + fn from_env() -> TrapCompression { + match std::env::var("CODEQL_RUBY_TRAP_COMPRESSION") { + Ok(method) => match TrapCompression::from_string(&method) { + Some(c) => c, + None => { + tracing::error!("Unknown compression method '{}'; using gzip.", &method); + TrapCompression::Gzip + } + }, + // Default compression method if the env var isn't set: + Err(_) => TrapCompression::Gzip, + } + } + + fn from_string(s: &str) -> Option<TrapCompression> { + match s.to_lowercase().as_ref() { + "none" => Some(TrapCompression::None), + "gzip" => Some(TrapCompression::Gzip), + _ => None, + } + } + + fn extension(&self) -> &str { + match self { + TrapCompression::None => "trap", + TrapCompression::Gzip => "trap.gz", + } + } +} + +/** + * Gets the number of threads the extractor should use, by reading the + * CODEQL_THREADS environment variable and using it as described in the + * extractor spec: + * + * "If the number is positive, it indicates the number of threads that should + * be used. If the number is negative or zero, it should be added to the number + * of cores available on the machine to determine how many threads to use + * (minimum of 1). If unspecified, should be considered as set to -1." + */ +fn num_codeql_threads() -> usize { + let threads_str = std::env::var("CODEQL_THREADS").unwrap_or_else(|_| "-1".to_owned()); + match threads_str.parse::<i32>() { + Ok(num) if num <= 0 => { + let reduction = -num as usize; + std::cmp::max(1, num_cpus::get() - reduction) + } + Ok(num) => num as usize, + + Err(_) => { + tracing::error!( + "Unable to parse CODEQL_THREADS value '{}'; defaulting to 1 thread.", + &threads_str + ); + 1 + } + } +} + +fn main() -> std::io::Result<()> { + tracing_subscriber::fmt() + .with_target(false) + .without_time() + .with_level(true) + .with_env_filter( + tracing_subscriber::EnvFilter::try_from_default_env() + .unwrap_or(tracing_subscriber::EnvFilter::new("ruby_extractor=warn")), + ) + .init(); + tracing::warn!("Support for Ruby is currently in Beta: https://git.io/codeql-language-support"); + let num_threads = num_codeql_threads(); + tracing::info!( + "Using {} {}", + num_threads, + if num_threads == 1 { + "thread" + } else { + "threads" + } + ); + rayon::ThreadPoolBuilder::new() + .num_threads(num_threads) + .build_global() + .unwrap(); + + let matches = clap::App::new("Ruby extractor") + .version("1.0") + .author("GitHub") + .about("CodeQL Ruby extractor") + .args_from_usage( + "--source-archive-dir=<DIR> 'Sets a custom source archive folder' + --output-dir=<DIR> 'Sets a custom trap folder' + --file-list=<FILE_LIST> 'A text files containing the paths of the files to extract'", + ) + .get_matches(); + let src_archive_dir = matches + .value_of("source-archive-dir") + .expect("missing --source-archive-dir"); + let src_archive_dir = PathBuf::from(src_archive_dir); + + let trap_dir = matches + .value_of("output-dir") + .expect("missing --output-dir"); + let trap_dir = PathBuf::from(trap_dir); + let trap_compression = TrapCompression::from_env(); + + let file_list = matches.value_of("file-list").expect("missing --file-list"); + let file_list = fs::File::open(file_list)?; + + let language = tree_sitter_ruby::language(); + let erb = tree_sitter_embedded_template::language(); + // Look up tree-sitter kind ids now, to avoid string comparisons when scanning ERB files. + let erb_directive_id = erb.id_for_node_kind("directive", true); + let erb_output_directive_id = erb.id_for_node_kind("output_directive", true); + let erb_code_id = erb.id_for_node_kind("code", true); + let schema = node_types::read_node_types_str("ruby", tree_sitter_ruby::NODE_TYPES)?; + let erb_schema = + node_types::read_node_types_str("erb", tree_sitter_embedded_template::NODE_TYPES)?; + let lines: std::io::Result<Vec<String>> = std::io::BufReader::new(file_list).lines().collect(); + let lines = lines?; + lines + .par_iter() + .try_for_each(|line| { + let path = PathBuf::from(line).canonicalize()?; + let src_archive_file = path_for(&src_archive_dir, &path, ""); + let mut source = std::fs::read(&path)?; + let code_ranges; + let mut trap_writer = extractor::new_trap_writer(); + if path.extension().map_or(false, |x| x == "erb") { + tracing::info!("scanning: {}", path.display()); + extractor::extract( + erb, + "erb", + &erb_schema, + &mut trap_writer, + &path, + &source, + &[], + )?; + + let (ranges, line_breaks) = scan_erb( + erb, + &source, + erb_directive_id, + erb_output_directive_id, + erb_code_id, + ); + for i in line_breaks { + if i < source.len() { + source[i] = b'\n'; + } + } + code_ranges = ranges; + } else { + code_ranges = vec![]; + } + extractor::extract( + language, + "ruby", + &schema, + &mut trap_writer, + &path, + &source, + &code_ranges, + )?; + std::fs::create_dir_all(&src_archive_file.parent().unwrap())?; + std::fs::copy(&path, &src_archive_file)?; + write_trap(&trap_dir, path, trap_writer, &trap_compression) + }) + .expect("failed to extract files"); + + let path = PathBuf::from("extras"); + let mut trap_writer = extractor::new_trap_writer(); + trap_writer.populate_empty_location(); + write_trap(&trap_dir, path, trap_writer, &trap_compression) +} + +fn write_trap( + trap_dir: &Path, + path: PathBuf, + trap_writer: extractor::TrapWriter, + trap_compression: &TrapCompression, +) -> std::io::Result<()> { + let trap_file = path_for(trap_dir, &path, trap_compression.extension()); + std::fs::create_dir_all(&trap_file.parent().unwrap())?; + let trap_file = std::fs::File::create(&trap_file)?; + let mut trap_file = BufWriter::new(trap_file); + match trap_compression { + TrapCompression::None => trap_writer.output(&mut trap_file), + TrapCompression::Gzip => { + let mut compressed_writer = GzEncoder::new(trap_file, flate2::Compression::fast()); + trap_writer.output(&mut compressed_writer) + } + } +} + +fn scan_erb( + erb: Language, + source: &[u8], + directive_id: u16, + output_directive_id: u16, + code_id: u16, +) -> (Vec<Range>, Vec<usize>) { + let mut parser = Parser::new(); + parser.set_language(erb).unwrap(); + let tree = parser.parse(&source, None).expect("Failed to parse file"); + let mut result = Vec::new(); + let mut line_breaks = vec![]; + + for n in tree.root_node().children(&mut tree.walk()) { + let kind_id = n.kind_id(); + if kind_id == directive_id || kind_id == output_directive_id { + for c in n.children(&mut tree.walk()) { + if c.kind_id() == code_id { + let mut range = c.range(); + if range.end_byte < source.len() { + line_breaks.push(range.end_byte); + range.end_byte += 1; + range.end_point.column += 1; + } + result.push(range); + } + } + } + } + if result.is_empty() { + let root = tree.root_node(); + // Add an empty range at the end of the file + result.push(Range { + start_byte: root.end_byte(), + end_byte: root.end_byte(), + start_point: root.end_position(), + end_point: root.end_position(), + }); + } + (result, line_breaks) +} + +fn path_for(dir: &Path, path: &Path, ext: &str) -> PathBuf { + let mut result = PathBuf::from(dir); + for component in path.components() { + match component { + std::path::Component::Prefix(prefix) => match prefix.kind() { + std::path::Prefix::Disk(letter) | std::path::Prefix::VerbatimDisk(letter) => { + result.push(format!("{}_", letter as char)) + } + std::path::Prefix::Verbatim(x) | std::path::Prefix::DeviceNS(x) => { + result.push(x); + } + std::path::Prefix::UNC(server, share) + | std::path::Prefix::VerbatimUNC(server, share) => { + result.push("unc"); + result.push(server); + result.push(share); + } + }, + std::path::Component::RootDir => { + // skip + } + std::path::Component::Normal(_) => { + result.push(component); + } + std::path::Component::CurDir => { + // skip + } + std::path::Component::ParentDir => { + result.pop(); + } + } + } + if !ext.is_empty() { + match result.extension() { + Some(x) => { + let mut new_ext = x.to_os_string(); + new_ext.push("."); + new_ext.push(ext); + result.set_extension(new_ext); + } + None => { + result.set_extension(ext); + } + } + } + result +} diff --git a/ruby/generator/Cargo.toml b/ruby/generator/Cargo.toml new file mode 100644 index 00000000000..1625fe88ead --- /dev/null +++ b/ruby/generator/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "ruby-generator" +version = "0.1.0" +authors = ["GitHub"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = "2.33" +node-types = { path = "../node-types" } +tracing = "0.1" +tracing-subscriber = { version = "0.2", features = ["env-filter"] } +tree-sitter-embedded-template = "0.19" +tree-sitter-ruby = { git = "https://github.com/tree-sitter/tree-sitter-ruby.git", rev = "bb6a42e42b048627a74a127d3e0184c1eef01de9" } diff --git a/ruby/generator/src/dbscheme.rs b/ruby/generator/src/dbscheme.rs new file mode 100644 index 00000000000..335eee1950c --- /dev/null +++ b/ruby/generator/src/dbscheme.rs @@ -0,0 +1,130 @@ +use crate::ql; +use std::collections::BTreeSet as Set; +use std::fmt; +/// Represents a distinct entry in the database schema. +pub enum Entry<'a> { + /// An entry defining a database table. + Table(Table<'a>), + /// An entry defining a database table. + Case(Case<'a>), + /// An entry defining type that is a union of other types. + Union(Union<'a>), +} + +/// A table in the database schema. +pub struct Table<'a> { + pub name: &'a str, + pub columns: Vec<Column<'a>>, + pub keysets: Option<Vec<&'a str>>, +} + +/// A union in the database schema. +pub struct Union<'a> { + pub name: &'a str, + pub members: Set<&'a str>, +} + +/// A table in the database schema. +pub struct Case<'a> { + pub name: &'a str, + pub column: &'a str, + pub branches: Vec<(usize, &'a str)>, +} + +/// A column in a table. +pub struct Column<'a> { + pub db_type: DbColumnType, + pub name: &'a str, + pub unique: bool, + pub ql_type: ql::Type<'a>, + pub ql_type_is_ref: bool, +} + +/// The database column type. +pub enum DbColumnType { + Int, + String, +} + +impl<'a> fmt::Display for Case<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "case @{}.{} of", &self.name, &self.column)?; + let mut sep = " "; + for (c, tp) in &self.branches { + writeln!(f, "{} {} = @{}", sep, c, tp)?; + sep = "|"; + } + writeln!(f, ";") + } +} + +impl<'a> fmt::Display for Table<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(keyset) = &self.keysets { + write!(f, "#keyset[")?; + for (key_index, key) in keyset.iter().enumerate() { + if key_index > 0 { + write!(f, ", ")?; + } + write!(f, "{}", key)?; + } + writeln!(f, "]")?; + } + + writeln!(f, "{}(", self.name)?; + for (column_index, column) in self.columns.iter().enumerate() { + write!(f, " ")?; + if column.unique { + write!(f, "unique ")?; + } + write!( + f, + "{} ", + match column.db_type { + DbColumnType::Int => "int", + DbColumnType::String => "string", + } + )?; + write!(f, "{}: {}", column.name, column.ql_type)?; + if column.ql_type_is_ref { + write!(f, " ref")?; + } + if column_index + 1 != self.columns.len() { + write!(f, ",")?; + } + writeln!(f)?; + } + write!(f, ");")?; + + Ok(()) + } +} + +impl<'a> fmt::Display for Union<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "@{} = ", self.name)?; + let mut first = true; + for member in &self.members { + if first { + first = false; + } else { + write!(f, " | ")?; + } + write!(f, "@{}", member)?; + } + Ok(()) + } +} + +/// Generates the dbscheme by writing the given dbscheme `entries` to the `file`. +pub fn write<'a>(file: &mut dyn std::io::Write, entries: &'a [Entry]) -> std::io::Result<()> { + for entry in entries { + match entry { + Entry::Case(case) => write!(file, "{}\n\n", case)?, + Entry::Table(table) => write!(file, "{}\n\n", table)?, + Entry::Union(union) => write!(file, "{}\n\n", union)?, + } + } + + Ok(()) +} diff --git a/ruby/generator/src/language.rs b/ruby/generator/src/language.rs new file mode 100644 index 00000000000..f0b0ed1790f --- /dev/null +++ b/ruby/generator/src/language.rs @@ -0,0 +1,4 @@ +pub struct Language { + pub name: String, + pub node_types: &'static str, +} diff --git a/ruby/generator/src/main.rs b/ruby/generator/src/main.rs new file mode 100644 index 00000000000..5d3dc75da4d --- /dev/null +++ b/ruby/generator/src/main.rs @@ -0,0 +1,671 @@ +mod dbscheme; +mod language; +mod ql; +mod ql_gen; + +use language::Language; +use std::collections::BTreeMap as Map; +use std::collections::BTreeSet as Set; +use std::fs::File; +use std::io::LineWriter; +use std::io::Write; +use std::path::PathBuf; + +/// Given the name of the parent node, and its field information, returns a pair, +/// the first of which is the field's type. The second is an optional dbscheme +/// entry that should be added. +fn make_field_type<'a>( + parent_name: &'a str, + field: &'a node_types::Field, + nodes: &'a node_types::NodeTypeMap, +) -> (ql::Type<'a>, Option<dbscheme::Entry<'a>>) { + match &field.type_info { + node_types::FieldTypeInfo::Multiple { + types, + dbscheme_union, + ql_class: _, + } => { + // This field can have one of several types. Create an ad-hoc QL union + // type to represent them. + let members: Set<&str> = types + .iter() + .map(|t| nodes.get(t).unwrap().dbscheme_name.as_str()) + .collect(); + ( + ql::Type::At(dbscheme_union), + Some(dbscheme::Entry::Union(dbscheme::Union { + name: dbscheme_union, + members, + })), + ) + } + node_types::FieldTypeInfo::Single(t) => { + let dbscheme_name = &nodes.get(t).unwrap().dbscheme_name; + (ql::Type::At(dbscheme_name), None) + } + node_types::FieldTypeInfo::ReservedWordInt(int_mapping) => { + // The field will be an `int` in the db, and we add a case split to + // create other db types for each integer value. + let mut branches: Vec<(usize, &'a str)> = Vec::new(); + for (value, name) in int_mapping.values() { + branches.push((*value, name)); + } + let case = dbscheme::Entry::Case(dbscheme::Case { + name: parent_name, + column: match &field.storage { + node_types::Storage::Column { name } => name, + node_types::Storage::Table { name, .. } => name, + }, + branches, + }); + (ql::Type::Int, Some(case)) + } + } +} + +fn add_field_for_table_storage<'a>( + field: &'a node_types::Field, + table_name: &'a str, + column_name: &'a str, + has_index: bool, + nodes: &'a node_types::NodeTypeMap, +) -> (dbscheme::Table<'a>, Option<dbscheme::Entry<'a>>) { + let parent_name = &nodes.get(&field.parent).unwrap().dbscheme_name; + // This field can appear zero or multiple times, so put + // it in an auxiliary table. + let (field_ql_type, field_type_entry) = make_field_type(parent_name, field, nodes); + let parent_column = dbscheme::Column { + unique: !has_index, + db_type: dbscheme::DbColumnType::Int, + name: parent_name, + ql_type: ql::Type::At(parent_name), + ql_type_is_ref: true, + }; + let index_column = dbscheme::Column { + unique: false, + db_type: dbscheme::DbColumnType::Int, + name: "index", + ql_type: ql::Type::Int, + ql_type_is_ref: true, + }; + let field_column = dbscheme::Column { + unique: true, + db_type: dbscheme::DbColumnType::Int, + name: column_name, + ql_type: field_ql_type, + ql_type_is_ref: true, + }; + let field_table = dbscheme::Table { + name: table_name, + columns: if has_index { + vec![parent_column, index_column, field_column] + } else { + vec![parent_column, field_column] + }, + // In addition to the field being unique, the combination of + // parent+index is unique, so add a keyset for them. + keysets: if has_index { + Some(vec![parent_name, "index"]) + } else { + None + }, + }; + (field_table, field_type_entry) +} + +fn add_field_for_column_storage<'a>( + parent_name: &'a str, + field: &'a node_types::Field, + column_name: &'a str, + nodes: &'a node_types::NodeTypeMap, +) -> (dbscheme::Column<'a>, Option<dbscheme::Entry<'a>>) { + // This field must appear exactly once, so we add it as + // a column to the main table for the node type. + let (field_ql_type, field_type_entry) = make_field_type(parent_name, field, nodes); + ( + dbscheme::Column { + unique: false, + db_type: dbscheme::DbColumnType::Int, + name: column_name, + ql_type: field_ql_type, + ql_type_is_ref: true, + }, + field_type_entry, + ) +} + +/// Converts the given tree-sitter node types into CodeQL dbscheme entries. +/// Returns a tuple containing: +/// +/// 1. A vector of dbscheme entries. +/// 2. A set of names of the members of the `<lang>_ast_node` union. +/// 3. A map where the keys are the dbscheme names for token kinds, and the +/// values are their integer representations. +fn convert_nodes( + nodes: &node_types::NodeTypeMap, +) -> (Vec<dbscheme::Entry>, Set<&str>, Map<&str, usize>) { + let mut entries: Vec<dbscheme::Entry> = Vec::new(); + let mut ast_node_members: Set<&str> = Set::new(); + let token_kinds: Map<&str, usize> = nodes + .iter() + .filter_map(|(_, node)| match &node.kind { + node_types::EntryKind::Token { kind_id } => { + Some((node.dbscheme_name.as_str(), *kind_id)) + } + _ => None, + }) + .collect(); + for node in nodes.values() { + match &node.kind { + node_types::EntryKind::Union { members: n_members } => { + // It's a tree-sitter supertype node, for which we create a union + // type. + let members: Set<&str> = n_members + .iter() + .map(|n| nodes.get(n).unwrap().dbscheme_name.as_str()) + .collect(); + entries.push(dbscheme::Entry::Union(dbscheme::Union { + name: &node.dbscheme_name, + members, + })); + } + node_types::EntryKind::Table { name, fields } => { + // It's a product type, defined by a table. + let mut main_table = dbscheme::Table { + name, + columns: vec![dbscheme::Column { + db_type: dbscheme::DbColumnType::Int, + name: "id", + unique: true, + ql_type: ql::Type::At(&node.dbscheme_name), + ql_type_is_ref: false, + }], + keysets: None, + }; + ast_node_members.insert(&node.dbscheme_name); + + // If the type also has fields or children, then we create either + // auxiliary tables or columns in the defining table for them. + for field in fields { + match &field.storage { + node_types::Storage::Column { name: column_name } => { + let (field_column, field_type_entry) = add_field_for_column_storage( + &node.dbscheme_name, + field, + column_name, + nodes, + ); + if let Some(field_type_entry) = field_type_entry { + entries.push(field_type_entry); + } + main_table.columns.push(field_column); + } + node_types::Storage::Table { + name, + has_index, + column_name, + } => { + let (field_table, field_type_entry) = add_field_for_table_storage( + field, + name, + column_name, + *has_index, + nodes, + ); + if let Some(field_type_entry) = field_type_entry { + entries.push(field_type_entry); + } + entries.push(dbscheme::Entry::Table(field_table)); + } + } + } + + if fields.is_empty() { + // There were no fields and no children, so it's a leaf node in + // the TS grammar. Add a column for the node text. + main_table.columns.push(dbscheme::Column { + unique: false, + db_type: dbscheme::DbColumnType::String, + name: "text", + ql_type: ql::Type::String, + ql_type_is_ref: true, + }); + } + + // Finally, the type's defining table also includes the location. + main_table.columns.push(dbscheme::Column { + unique: false, + db_type: dbscheme::DbColumnType::Int, + name: "loc", + ql_type: ql::Type::At("location"), + ql_type_is_ref: true, + }); + + entries.push(dbscheme::Entry::Table(main_table)); + } + node_types::EntryKind::Token { .. } => {} + } + } + + (entries, ast_node_members, token_kinds) +} + +/// Creates a dbscheme table entry representing the parent relation for AST nodes. +/// +/// # Arguments +/// - `name` - the name of both the table to create and the node parent type. +/// - `ast_node_name` - the name of the node child type. +fn create_ast_node_parent_table<'a>(name: &'a str, ast_node_name: &'a str) -> dbscheme::Table<'a> { + dbscheme::Table { + name, + columns: vec![ + dbscheme::Column { + db_type: dbscheme::DbColumnType::Int, + name: "child", + unique: false, + ql_type: ql::Type::At(ast_node_name), + ql_type_is_ref: true, + }, + dbscheme::Column { + db_type: dbscheme::DbColumnType::Int, + name: "parent", + unique: false, + ql_type: ql::Type::At(name), + ql_type_is_ref: true, + }, + dbscheme::Column { + unique: false, + db_type: dbscheme::DbColumnType::Int, + name: "parent_index", + ql_type: ql::Type::Int, + ql_type_is_ref: true, + }, + ], + keysets: Some(vec!["parent", "parent_index"]), + } +} + +fn create_tokeninfo<'a>(name: &'a str, type_name: &'a str) -> dbscheme::Table<'a> { + dbscheme::Table { + name, + keysets: None, + columns: vec![ + dbscheme::Column { + db_type: dbscheme::DbColumnType::Int, + name: "id", + unique: true, + ql_type: ql::Type::At(type_name), + ql_type_is_ref: false, + }, + dbscheme::Column { + unique: false, + db_type: dbscheme::DbColumnType::Int, + name: "kind", + ql_type: ql::Type::Int, + ql_type_is_ref: true, + }, + dbscheme::Column { + unique: false, + db_type: dbscheme::DbColumnType::String, + name: "value", + ql_type: ql::Type::String, + ql_type_is_ref: true, + }, + dbscheme::Column { + unique: false, + db_type: dbscheme::DbColumnType::Int, + name: "loc", + ql_type: ql::Type::At("location"), + ql_type_is_ref: true, + }, + ], + } +} + +fn create_token_case<'a>(name: &'a str, token_kinds: Map<&'a str, usize>) -> dbscheme::Case<'a> { + let branches: Vec<(usize, &str)> = token_kinds + .iter() + .map(|(&name, kind_id)| (*kind_id, name)) + .collect(); + dbscheme::Case { + name, + column: "kind", + branches, + } +} + +fn create_location_union<'a>() -> dbscheme::Entry<'a> { + dbscheme::Entry::Union(dbscheme::Union { + name: "location", + members: vec!["location_default"].into_iter().collect(), + }) +} + +fn create_files_table<'a>() -> dbscheme::Entry<'a> { + dbscheme::Entry::Table(dbscheme::Table { + name: "files", + keysets: None, + columns: vec![ + dbscheme::Column { + unique: true, + db_type: dbscheme::DbColumnType::Int, + name: "id", + ql_type: ql::Type::At("file"), + ql_type_is_ref: false, + }, + dbscheme::Column { + db_type: dbscheme::DbColumnType::String, + name: "name", + unique: false, + ql_type: ql::Type::String, + ql_type_is_ref: true, + }, + ], + }) +} +fn create_folders_table<'a>() -> dbscheme::Entry<'a> { + dbscheme::Entry::Table(dbscheme::Table { + name: "folders", + keysets: None, + columns: vec![ + dbscheme::Column { + unique: true, + db_type: dbscheme::DbColumnType::Int, + name: "id", + ql_type: ql::Type::At("folder"), + ql_type_is_ref: false, + }, + dbscheme::Column { + db_type: dbscheme::DbColumnType::String, + name: "name", + unique: false, + ql_type: ql::Type::String, + ql_type_is_ref: true, + }, + ], + }) +} + +fn create_locations_default_table<'a>() -> dbscheme::Entry<'a> { + dbscheme::Entry::Table(dbscheme::Table { + name: "locations_default", + keysets: None, + columns: vec![ + dbscheme::Column { + unique: true, + db_type: dbscheme::DbColumnType::Int, + name: "id", + ql_type: ql::Type::At("location_default"), + ql_type_is_ref: false, + }, + dbscheme::Column { + unique: false, + db_type: dbscheme::DbColumnType::Int, + name: "file", + ql_type: ql::Type::At("file"), + ql_type_is_ref: true, + }, + dbscheme::Column { + unique: false, + db_type: dbscheme::DbColumnType::Int, + name: "start_line", + ql_type: ql::Type::Int, + ql_type_is_ref: true, + }, + dbscheme::Column { + unique: false, + db_type: dbscheme::DbColumnType::Int, + name: "start_column", + ql_type: ql::Type::Int, + ql_type_is_ref: true, + }, + dbscheme::Column { + unique: false, + db_type: dbscheme::DbColumnType::Int, + name: "end_line", + ql_type: ql::Type::Int, + ql_type_is_ref: true, + }, + dbscheme::Column { + unique: false, + db_type: dbscheme::DbColumnType::Int, + name: "end_column", + ql_type: ql::Type::Int, + ql_type_is_ref: true, + }, + ], + }) +} + +fn create_container_union<'a>() -> dbscheme::Entry<'a> { + dbscheme::Entry::Union(dbscheme::Union { + name: "container", + members: vec!["folder", "file"].into_iter().collect(), + }) +} + +fn create_containerparent_table<'a>() -> dbscheme::Entry<'a> { + dbscheme::Entry::Table(dbscheme::Table { + name: "containerparent", + columns: vec![ + dbscheme::Column { + unique: false, + db_type: dbscheme::DbColumnType::Int, + name: "parent", + ql_type: ql::Type::At("container"), + ql_type_is_ref: true, + }, + dbscheme::Column { + unique: true, + db_type: dbscheme::DbColumnType::Int, + name: "child", + ql_type: ql::Type::At("container"), + ql_type_is_ref: true, + }, + ], + keysets: None, + }) +} + +fn create_source_location_prefix_table<'a>() -> dbscheme::Entry<'a> { + dbscheme::Entry::Table(dbscheme::Table { + name: "sourceLocationPrefix", + keysets: None, + columns: vec![dbscheme::Column { + unique: false, + db_type: dbscheme::DbColumnType::String, + name: "prefix", + ql_type: ql::Type::String, + ql_type_is_ref: true, + }], + }) +} + +fn create_diagnostics<'a>() -> (dbscheme::Case<'a>, dbscheme::Table<'a>) { + let table = dbscheme::Table { + name: "diagnostics", + keysets: None, + columns: vec![ + dbscheme::Column { + unique: true, + db_type: dbscheme::DbColumnType::Int, + name: "id", + ql_type: ql::Type::At("diagnostic"), + ql_type_is_ref: false, + }, + dbscheme::Column { + unique: false, + db_type: dbscheme::DbColumnType::Int, + name: "severity", + ql_type: ql::Type::Int, + ql_type_is_ref: true, + }, + dbscheme::Column { + unique: false, + db_type: dbscheme::DbColumnType::String, + name: "error_tag", + ql_type: ql::Type::String, + ql_type_is_ref: true, + }, + dbscheme::Column { + unique: false, + db_type: dbscheme::DbColumnType::String, + name: "error_message", + ql_type: ql::Type::String, + ql_type_is_ref: true, + }, + dbscheme::Column { + unique: false, + db_type: dbscheme::DbColumnType::String, + name: "full_error_message", + ql_type: ql::Type::String, + ql_type_is_ref: true, + }, + dbscheme::Column { + unique: false, + db_type: dbscheme::DbColumnType::Int, + name: "location", + ql_type: ql::Type::At("location_default"), + ql_type_is_ref: true, + }, + ], + }; + let severities: Vec<(usize, &str)> = vec![ + (10, "diagnostic_debug"), + (20, "diagnostic_info"), + (30, "diagnostic_warning"), + (40, "diagnostic_error"), + ]; + let case = dbscheme::Case { + name: "diagnostic", + column: "severity", + branches: severities, + }; + (case, table) +} + +fn main() -> std::io::Result<()> { + tracing_subscriber::fmt() + .with_target(false) + .without_time() + .with_level(true) + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .init(); + + let matches = clap::App::new("Ruby dbscheme generator") + .version("1.0") + .author("GitHub") + .about("CodeQL Ruby dbscheme generator") + .args_from_usage( + "--dbscheme=<FILE> 'Path of the generated dbscheme file' + --library=<FILE> 'Path of the generated QLL file'", + ) + .get_matches(); + let dbscheme_path = matches.value_of("dbscheme").expect("missing --dbscheme"); + let dbscheme_path = PathBuf::from(dbscheme_path); + + let ql_library_path = matches.value_of("library").expect("missing --library"); + let ql_library_path = PathBuf::from(ql_library_path); + + let languages = vec![ + Language { + name: "Ruby".to_owned(), + node_types: tree_sitter_ruby::NODE_TYPES, + }, + Language { + name: "Erb".to_owned(), + node_types: tree_sitter_embedded_template::NODE_TYPES, + }, + ]; + let mut dbscheme_writer = LineWriter::new(File::create(dbscheme_path)?); + write!( + dbscheme_writer, + "// CodeQL database schema for {}\n\ + // Automatically generated from the tree-sitter grammar; do not edit\n\n", + languages[0].name + )?; + let (diagnostics_case, diagnostics_table) = create_diagnostics(); + dbscheme::write( + &mut dbscheme_writer, + &[ + create_location_union(), + create_locations_default_table(), + create_files_table(), + create_folders_table(), + create_container_union(), + create_containerparent_table(), + create_source_location_prefix_table(), + dbscheme::Entry::Table(diagnostics_table), + dbscheme::Entry::Case(diagnostics_case), + ], + )?; + + let mut ql_writer = LineWriter::new(File::create(ql_library_path)?); + write!( + ql_writer, + "/*\n\ + * CodeQL library for {} + * Automatically generated from the tree-sitter grammar; do not edit\n\ + */\n\n", + languages[0].name + )?; + ql::write( + &mut ql_writer, + &[ + ql::TopLevel::Import("codeql.files.FileSystem"), + ql::TopLevel::Import("codeql.Locations"), + ], + )?; + + for language in languages { + let prefix = node_types::to_snake_case(&language.name); + let ast_node_name = format!("{}_ast_node", &prefix); + let ast_node_parent_name = format!("{}_ast_node_parent", &prefix); + let token_name = format!("{}_token", &prefix); + let tokeninfo_name = format!("{}_tokeninfo", &prefix); + let reserved_word_name = format!("{}_reserved_word", &prefix); + let nodes = node_types::read_node_types_str(&prefix, language.node_types)?; + let (dbscheme_entries, mut ast_node_members, token_kinds) = convert_nodes(&nodes); + ast_node_members.insert(&token_name); + dbscheme::write(&mut dbscheme_writer, &dbscheme_entries)?; + let token_case = create_token_case(&token_name, token_kinds); + dbscheme::write( + &mut dbscheme_writer, + &[ + dbscheme::Entry::Table(create_tokeninfo(&tokeninfo_name, &token_name)), + dbscheme::Entry::Case(token_case), + dbscheme::Entry::Union(dbscheme::Union { + name: &ast_node_name, + members: ast_node_members, + }), + dbscheme::Entry::Union(dbscheme::Union { + name: &ast_node_parent_name, + members: [&ast_node_name, "file"].iter().cloned().collect(), + }), + dbscheme::Entry::Table(create_ast_node_parent_table( + &ast_node_parent_name, + &ast_node_name, + )), + ], + )?; + + let mut body = vec![ + ql::TopLevel::Class(ql_gen::create_ast_node_class( + &ast_node_name, + &ast_node_parent_name, + )), + ql::TopLevel::Class(ql_gen::create_token_class(&token_name, &tokeninfo_name)), + ql::TopLevel::Class(ql_gen::create_reserved_word_class(&reserved_word_name)), + ]; + body.append(&mut ql_gen::convert_nodes(&nodes)); + ql::write( + &mut ql_writer, + &[ql::TopLevel::Module(ql::Module { + qldoc: None, + name: &language.name, + body, + })], + )?; + } + Ok(()) +} diff --git a/ruby/generator/src/ql.rs b/ruby/generator/src/ql.rs new file mode 100644 index 00000000000..c51903529eb --- /dev/null +++ b/ruby/generator/src/ql.rs @@ -0,0 +1,275 @@ +use std::collections::BTreeSet; +use std::fmt; + +#[derive(Clone, Eq, PartialEq, Hash)] +pub enum TopLevel<'a> { + Class(Class<'a>), + Import(&'a str), + Module(Module<'a>), +} + +impl<'a> fmt::Display for TopLevel<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + TopLevel::Import(x) => write!(f, "private import {}", x), + TopLevel::Class(cls) => write!(f, "{}", cls), + TopLevel::Module(m) => write!(f, "{}", m), + } + } +} + +#[derive(Clone, Eq, PartialEq, Hash)] +pub struct Class<'a> { + pub qldoc: Option<String>, + pub name: &'a str, + pub is_abstract: bool, + pub supertypes: BTreeSet<Type<'a>>, + pub characteristic_predicate: Option<Expression<'a>>, + pub predicates: Vec<Predicate<'a>>, +} + +impl<'a> fmt::Display for Class<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(qldoc) = &self.qldoc { + write!(f, "/** {} */", qldoc)?; + } + if self.is_abstract { + write!(f, "abstract ")?; + } + write!(f, "class {} extends ", &self.name)?; + for (index, supertype) in self.supertypes.iter().enumerate() { + if index > 0 { + write!(f, ", ")?; + } + write!(f, "{}", supertype)?; + } + writeln!(f, " {{ ")?; + + if let Some(charpred) = &self.characteristic_predicate { + writeln!( + f, + " {}", + Predicate { + qldoc: None, + name: self.name, + overridden: false, + return_type: None, + formal_parameters: vec![], + body: charpred.clone(), + } + )?; + } + + for predicate in &self.predicates { + writeln!(f, " {}", predicate)?; + } + + write!(f, "}}")?; + + Ok(()) + } +} + +#[derive(Clone, Eq, PartialEq, Hash)] +pub struct Module<'a> { + pub qldoc: Option<String>, + pub name: &'a str, + pub body: Vec<TopLevel<'a>>, +} + +impl<'a> fmt::Display for Module<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(qldoc) = &self.qldoc { + write!(f, "/** {} */", qldoc)?; + } + writeln!(f, "module {} {{ ", self.name)?; + for decl in &self.body { + writeln!(f, " {}", decl)?; + } + write!(f, "}}")?; + Ok(()) + } +} +// The QL type of a column. +#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub enum Type<'a> { + /// Primitive `int` type. + Int, + + /// Primitive `string` type. + String, + + /// A database type that will need to be referred to with an `@` prefix. + At(&'a str), + + /// A user-defined type. + Normal(&'a str), +} + +impl<'a> fmt::Display for Type<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Type::Int => write!(f, "int"), + Type::String => write!(f, "string"), + Type::Normal(name) => write!(f, "{}", name), + Type::At(name) => write!(f, "@{}", name), + } + } +} + +#[derive(Clone, Eq, PartialEq, Hash)] +pub enum Expression<'a> { + Var(&'a str), + String(&'a str), + Integer(usize), + Pred(&'a str, Vec<Expression<'a>>), + And(Vec<Expression<'a>>), + Or(Vec<Expression<'a>>), + Equals(Box<Expression<'a>>, Box<Expression<'a>>), + Dot(Box<Expression<'a>>, &'a str, Vec<Expression<'a>>), + Aggregate { + name: &'a str, + vars: Vec<FormalParameter<'a>>, + range: Option<Box<Expression<'a>>>, + expr: Box<Expression<'a>>, + second_expr: Option<Box<Expression<'a>>>, + }, +} + +impl<'a> fmt::Display for Expression<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Expression::Var(x) => write!(f, "{}", x), + Expression::String(s) => write!(f, "\"{}\"", s), + Expression::Integer(n) => write!(f, "{}", n), + Expression::Pred(n, args) => { + write!(f, "{}(", n)?; + for (index, arg) in args.iter().enumerate() { + if index > 0 { + write!(f, ", ")?; + } + write!(f, "{}", arg)?; + } + write!(f, ")") + } + Expression::And(conjuncts) => { + if conjuncts.is_empty() { + write!(f, "any()") + } else { + for (index, conjunct) in conjuncts.iter().enumerate() { + if index > 0 { + write!(f, " and ")?; + } + write!(f, "({})", conjunct)?; + } + Ok(()) + } + } + Expression::Or(disjuncts) => { + if disjuncts.is_empty() { + write!(f, "none()") + } else { + for (index, disjunct) in disjuncts.iter().enumerate() { + if index > 0 { + write!(f, " or ")?; + } + write!(f, "({})", disjunct)?; + } + Ok(()) + } + } + Expression::Equals(a, b) => write!(f, "{} = {}", a, b), + Expression::Dot(x, member_pred, args) => { + write!(f, "{}.{}(", x, member_pred)?; + for (index, arg) in args.iter().enumerate() { + if index > 0 { + write!(f, ", ")?; + } + write!(f, "{}", arg)?; + } + write!(f, ")") + } + Expression::Aggregate { + name, + vars, + range, + expr, + second_expr, + } => { + write!(f, "{}(", name)?; + if !vars.is_empty() { + for (index, var) in vars.iter().enumerate() { + if index > 0 { + write!(f, ", ")?; + } + write!(f, "{}", var)?; + } + write!(f, " | ")?; + } + if let Some(range) = range { + write!(f, "{} | ", range)?; + } + write!(f, "{}", expr)?; + if let Some(second_expr) = second_expr { + write!(f, ", {}", second_expr)?; + } + write!(f, ")") + } + } + } +} + +#[derive(Clone, Eq, PartialEq, Hash)] +pub struct Predicate<'a> { + pub qldoc: Option<String>, + pub name: &'a str, + pub overridden: bool, + pub return_type: Option<Type<'a>>, + pub formal_parameters: Vec<FormalParameter<'a>>, + pub body: Expression<'a>, +} + +impl<'a> fmt::Display for Predicate<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(qldoc) = &self.qldoc { + write!(f, "/** {} */", qldoc)?; + } + if self.overridden { + write!(f, "override ")?; + } + match &self.return_type { + None => write!(f, "predicate ")?, + Some(return_type) => write!(f, "{} ", return_type)?, + } + write!(f, "{}(", self.name)?; + for (index, param) in self.formal_parameters.iter().enumerate() { + if index > 0 { + write!(f, ", ")?; + } + write!(f, "{}", param)?; + } + write!(f, ") {{ {} }}", self.body)?; + + Ok(()) + } +} + +#[derive(Clone, Eq, PartialEq, Hash)] +pub struct FormalParameter<'a> { + pub name: &'a str, + pub param_type: Type<'a>, +} + +impl<'a> fmt::Display for FormalParameter<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} {}", self.param_type, self.name) + } +} + +/// Generates a QL library by writing the given `elements` to the `file`. +pub fn write<'a>(file: &mut dyn std::io::Write, elements: &'a [TopLevel]) -> std::io::Result<()> { + for element in elements { + write!(file, "{}\n\n", &element)?; + } + Ok(()) +} diff --git a/ruby/generator/src/ql_gen.rs b/ruby/generator/src/ql_gen.rs new file mode 100644 index 00000000000..a9a276fd9f6 --- /dev/null +++ b/ruby/generator/src/ql_gen.rs @@ -0,0 +1,610 @@ +use crate::ql; +use std::collections::BTreeSet; + +/// Creates the hard-coded `AstNode` class that acts as a supertype of all +/// classes we generate. +pub fn create_ast_node_class<'a>(ast_node: &'a str, ast_node_parent: &'a str) -> ql::Class<'a> { + // Default implementation of `toString` calls `this.getAPrimaryQlClass()` + let to_string = ql::Predicate { + qldoc: Some(String::from( + "Gets a string representation of this element.", + )), + name: "toString", + overridden: false, + return_type: Some(ql::Type::String), + formal_parameters: vec![], + body: ql::Expression::Equals( + Box::new(ql::Expression::Var("result")), + Box::new(ql::Expression::Dot( + Box::new(ql::Expression::Var("this")), + "getAPrimaryQlClass", + vec![], + )), + ), + }; + let get_location = create_none_predicate( + Some(String::from("Gets the location of this element.")), + "getLocation", + false, + Some(ql::Type::Normal("Location")), + ); + let get_a_field_or_child = create_none_predicate( + Some(String::from("Gets a field or child node of this node.")), + "getAFieldOrChild", + false, + Some(ql::Type::Normal("AstNode")), + ); + let get_parent = ql::Predicate { + qldoc: Some(String::from("Gets the parent of this element.")), + name: "getParent", + overridden: false, + return_type: Some(ql::Type::Normal("AstNode")), + formal_parameters: vec![], + body: ql::Expression::Pred( + ast_node_parent, + vec![ + ql::Expression::Var("this"), + ql::Expression::Var("result"), + ql::Expression::Var("_"), + ], + ), + }; + let get_parent_index = ql::Predicate { + qldoc: Some(String::from( + "Gets the index of this node among the children of its parent.", + )), + name: "getParentIndex", + overridden: false, + return_type: Some(ql::Type::Int), + formal_parameters: vec![], + body: ql::Expression::Pred( + ast_node_parent, + vec![ + ql::Expression::Var("this"), + ql::Expression::Var("_"), + ql::Expression::Var("result"), + ], + ), + }; + let get_a_primary_ql_class = ql::Predicate { + qldoc: Some(String::from( + "Gets the name of the primary QL class for this element.", + )), + name: "getAPrimaryQlClass", + overridden: false, + return_type: Some(ql::Type::String), + formal_parameters: vec![], + body: ql::Expression::Equals( + Box::new(ql::Expression::Var("result")), + Box::new(ql::Expression::String("???")), + ), + }; + let get_primary_ql_classes = ql::Predicate { + qldoc: Some( + "Gets a comma-separated list of the names of the primary CodeQL \ + classes to which this element belongs." + .to_owned(), + ), + name: "getPrimaryQlClasses", + overridden: false, + return_type: Some(ql::Type::String), + formal_parameters: vec![], + body: ql::Expression::Equals( + Box::new(ql::Expression::Var("result")), + Box::new(ql::Expression::Aggregate { + name: "concat", + vars: vec![], + range: None, + expr: Box::new(ql::Expression::Dot( + Box::new(ql::Expression::Var("this")), + "getAPrimaryQlClass", + vec![], + )), + second_expr: Some(Box::new(ql::Expression::String(","))), + }), + ), + }; + 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, + ], + } +} + +pub fn create_token_class<'a>(token_type: &'a str, tokeninfo: &'a str) -> ql::Class<'a> { + let tokeninfo_arity = 4; + let get_value = ql::Predicate { + qldoc: Some(String::from("Gets the value of this token.")), + name: "getValue", + overridden: false, + return_type: Some(ql::Type::String), + formal_parameters: vec![], + body: create_get_field_expr_for_column_storage("result", tokeninfo, 1, tokeninfo_arity), + }; + let get_location = ql::Predicate { + qldoc: Some(String::from("Gets the location of this token.")), + name: "getLocation", + overridden: true, + return_type: Some(ql::Type::Normal("Location")), + formal_parameters: vec![], + body: create_get_field_expr_for_column_storage("result", tokeninfo, 2, tokeninfo_arity), + }; + let to_string = ql::Predicate { + qldoc: Some(String::from( + "Gets a string representation of this element.", + )), + name: "toString", + overridden: true, + return_type: Some(ql::Type::String), + formal_parameters: vec![], + body: ql::Expression::Equals( + Box::new(ql::Expression::Var("result")), + Box::new(ql::Expression::Dot( + Box::new(ql::Expression::Var("this")), + "getValue", + vec![], + )), + ), + }; + 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, + get_location, + to_string, + create_get_a_primary_ql_class("Token"), + ], + } +} + +// Creates the `ReservedWord` class. +pub fn create_reserved_word_class(db_name: &str) -> ql::Class { + let class_name = "ReservedWord"; + let get_a_primary_ql_class = create_get_a_primary_ql_class(class_name); + 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")] + .into_iter() + .collect(), + characteristic_predicate: None, + predicates: vec![get_a_primary_ql_class], + } +} + +/// Creates a predicate whose body is `none()`. +fn create_none_predicate<'a>( + qldoc: Option<String>, + name: &'a str, + overridden: bool, + return_type: Option<ql::Type<'a>>, +) -> ql::Predicate<'a> { + ql::Predicate { + qldoc, + name, + overridden, + return_type, + formal_parameters: Vec::new(), + body: ql::Expression::Pred("none", vec![]), + } +} + +/// Creates an overridden `getAPrimaryQlClass` predicate that returns the given +/// name. +fn create_get_a_primary_ql_class(class_name: &str) -> ql::Predicate { + ql::Predicate { + qldoc: Some(String::from( + "Gets the name of the primary QL class for this element.", + )), + name: "getAPrimaryQlClass", + overridden: true, + return_type: Some(ql::Type::String), + formal_parameters: vec![], + body: ql::Expression::Equals( + Box::new(ql::Expression::Var("result")), + Box::new(ql::Expression::String(class_name)), + ), + } +} + +/// Creates the `getLocation` predicate. +/// +/// # Arguments +/// +/// `def_table` - the name of the table that defines the entity and its location. +/// `arity` - the total number of columns in the table +fn create_get_location_predicate(def_table: &str, arity: usize) -> ql::Predicate { + ql::Predicate { + qldoc: Some(String::from("Gets the location of this element.")), + name: "getLocation", + overridden: true, + return_type: Some(ql::Type::Normal("Location")), + formal_parameters: vec![], + // body of the form: foo_bar_def(_, _, ..., result) + body: ql::Expression::Pred( + def_table, + [ + vec![ql::Expression::Var("this")], + vec![ql::Expression::Var("_"); arity - 2], + vec![ql::Expression::Var("result")], + ] + .concat(), + ), + } +} + +/// Creates the `getText` predicate for a leaf node. +/// +/// # Arguments +/// +/// `def_table` - the name of the table that defines the entity and its text. +fn create_get_text_predicate(def_table: &str) -> ql::Predicate { + ql::Predicate { + qldoc: Some(String::from("Gets the text content of this element.")), + name: "getText", + overridden: false, + return_type: Some(ql::Type::String), + formal_parameters: vec![], + body: ql::Expression::Pred( + def_table, + vec![ + ql::Expression::Var("this"), + ql::Expression::Var("result"), + ql::Expression::Var("_"), + ], + ), + } +} + +/// Returns an expression to get a field that's defined as a column in the parent's table. +/// +/// # Arguments +/// +/// * `result_var_name` - the name of the variable to which the resulting value should be bound +/// * `table_name` - the name of parent's defining table +/// * `column_index` - the index in that table that defines the field +/// * `arity` - the total number of columns in the table +fn create_get_field_expr_for_column_storage<'a>( + result_var_name: &'a str, + table_name: &'a str, + column_index: usize, + arity: usize, +) -> ql::Expression<'a> { + let num_underscores_before = column_index; + let num_underscores_after = arity - 2 - num_underscores_before; + ql::Expression::Pred( + table_name, + [ + vec![ql::Expression::Var("this")], + vec![ql::Expression::Var("_"); num_underscores_before], + vec![ql::Expression::Var(result_var_name)], + vec![ql::Expression::Var("_"); num_underscores_after], + ] + .concat(), + ) +} + +/// Returns an expression to get the field with the given index from its +/// auxiliary table. The index name can be "_" so the expression will hold for +/// all indices. +fn create_get_field_expr_for_table_storage<'a>( + result_var_name: &'a str, + table_name: &'a str, + index_var_name: Option<&'a str>, +) -> ql::Expression<'a> { + ql::Expression::Pred( + table_name, + match index_var_name { + Some(index_var_name) => vec![ + ql::Expression::Var("this"), + ql::Expression::Var(index_var_name), + ql::Expression::Var(result_var_name), + ], + None => vec![ql::Expression::Var("this"), ql::Expression::Var("result")], + }, + ) +} + +/// Creates a pair consisting of a predicate to get the given field, and an +/// optional expression that will get the same field. When the field can occur +/// multiple times, the predicate will take an index argument, while the +/// expression will use the "don't care" expression to hold for all occurrences. +/// +/// # Arguments +/// +/// `main_table_name` - the name of the defining table for the parent node +/// `main_table_arity` - the number of columns in the main table +/// `main_table_column_index` - a mutable reference to a column index indicating +/// where the field is in the main table. If this is used (i.e. the field has +/// column storage), then the index is incremented. +/// `parent_name` - the name of the parent node +/// `field` - the field whose getters we are creating +/// `field_type` - the db name of the field's type (possibly being a union we created) +fn create_field_getters<'a>( + main_table_name: &'a str, + main_table_arity: usize, + main_table_column_index: &mut usize, + field: &'a node_types::Field, + nodes: &'a node_types::NodeTypeMap, +) -> (ql::Predicate<'a>, Option<ql::Expression<'a>>) { + let return_type = match &field.type_info { + node_types::FieldTypeInfo::Single(t) => { + Some(ql::Type::Normal(&nodes.get(t).unwrap().ql_class_name)) + } + node_types::FieldTypeInfo::Multiple { + types: _, + dbscheme_union: _, + ql_class, + } => Some(ql::Type::Normal(ql_class)), + node_types::FieldTypeInfo::ReservedWordInt(_) => Some(ql::Type::String), + }; + let formal_parameters = match &field.storage { + node_types::Storage::Column { .. } => vec![], + node_types::Storage::Table { has_index, .. } => { + if *has_index { + vec![ql::FormalParameter { + name: "i", + param_type: ql::Type::Int, + }] + } else { + vec![] + } + } + }; + + // For the expression to get a value, what variable name should the result + // be bound to? + let get_value_result_var_name = match &field.type_info { + node_types::FieldTypeInfo::ReservedWordInt(_) => "value", + node_types::FieldTypeInfo::Single(_) => "result", + node_types::FieldTypeInfo::Multiple { .. } => "result", + }; + + // Two expressions for getting the value. One that's suitable use in the + // getter predicate (where there may be a specific index), and another for + // use in `getAFieldOrChild` (where we use a "don't care" expression to + // match any index). + let (get_value, get_value_any_index) = match &field.storage { + node_types::Storage::Column { name: _ } => { + let column_index = *main_table_column_index; + *main_table_column_index += 1; + ( + create_get_field_expr_for_column_storage( + get_value_result_var_name, + main_table_name, + column_index, + main_table_arity, + ), + create_get_field_expr_for_column_storage( + get_value_result_var_name, + main_table_name, + column_index, + main_table_arity, + ), + ) + } + node_types::Storage::Table { + name: field_table_name, + has_index, + column_name: _, + } => ( + create_get_field_expr_for_table_storage( + get_value_result_var_name, + field_table_name, + if *has_index { Some("i") } else { None }, + ), + create_get_field_expr_for_table_storage( + get_value_result_var_name, + field_table_name, + if *has_index { Some("_") } else { None }, + ), + ), + }; + let (body, optional_expr) = match &field.type_info { + node_types::FieldTypeInfo::ReservedWordInt(int_mapping) => { + // Create an expression that binds the corresponding string to `result` for each `value`, e.g.: + // result = "foo" and value = 0 or + // result = "bar" and value = 1 or + // result = "baz" and value = 2 + let disjuncts = int_mapping + .iter() + .map(|(token_str, (value, _))| { + ql::Expression::And(vec![ + ql::Expression::Equals( + Box::new(ql::Expression::Var("result")), + Box::new(ql::Expression::String(token_str)), + ), + ql::Expression::Equals( + Box::new(ql::Expression::Var("value")), + Box::new(ql::Expression::Integer(*value)), + ), + ]) + }) + .collect(); + ( + ql::Expression::Aggregate { + name: "exists", + vars: vec![ql::FormalParameter { + name: "value", + param_type: ql::Type::Int, + }], + range: Some(Box::new(get_value)), + expr: Box::new(ql::Expression::Or(disjuncts)), + second_expr: None, + }, + // Since the getter returns a string and not an AstNode, it won't be part of getAFieldOrChild: + None, + ) + } + node_types::FieldTypeInfo::Single(_) | node_types::FieldTypeInfo::Multiple { .. } => { + (get_value, Some(get_value_any_index)) + } + }; + let qldoc = match &field.name { + Some(name) => format!("Gets the node corresponding to the field `{}`.", name), + None => { + if formal_parameters.is_empty() { + "Gets the child of this node.".to_owned() + } else { + "Gets the `i`th child of this node.".to_owned() + } + } + }; + ( + ql::Predicate { + qldoc: Some(qldoc), + name: &field.getter_name, + overridden: false, + return_type, + formal_parameters, + body, + }, + optional_expr, + ) +} + +/// Converts the given node types into CodeQL classes wrapping the dbscheme. +pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec<ql::TopLevel> { + let mut classes: Vec<ql::TopLevel> = Vec::new(); + let mut token_kinds = BTreeSet::new(); + for (type_name, node) in nodes { + if let node_types::EntryKind::Token { .. } = &node.kind { + if type_name.named { + token_kinds.insert(&type_name.kind); + } + } + } + + for (type_name, node) in nodes { + match &node.kind { + node_types::EntryKind::Token { kind_id: _ } => { + if type_name.named { + let get_a_primary_ql_class = create_get_a_primary_ql_class(&node.ql_class_name); + let mut supertypes: BTreeSet<ql::Type> = BTreeSet::new(); + supertypes.insert(ql::Type::At(&node.dbscheme_name)); + supertypes.insert(ql::Type::Normal("Token")); + 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, + supertypes, + characteristic_predicate: None, + predicates: vec![get_a_primary_ql_class], + })); + } + } + node_types::EntryKind::Union { members: _ } => { + // It's a tree-sitter supertype node, so we're wrapping a dbscheme + // union type. + classes.push(ql::TopLevel::Class(ql::Class { + qldoc: None, + name: &node.ql_class_name, + is_abstract: false, + supertypes: vec![ + ql::Type::At(&node.dbscheme_name), + ql::Type::Normal("AstNode"), + ] + .into_iter() + .collect(), + characteristic_predicate: None, + predicates: vec![], + })); + } + node_types::EntryKind::Table { + name: main_table_name, + fields, + } => { + // Count how many columns there will be in the main table. + // There will be: + // - one for the id + // - one for the location + // - one for each field that's stored as a column + // - if there are no fields, one for the text column. + let main_table_arity = 2 + if fields.is_empty() { + 1 + } else { + fields + .iter() + .filter(|&f| matches!(f.storage, node_types::Storage::Column { .. })) + .count() + }; + + let main_class_name = &node.ql_class_name; + let mut main_class = ql::Class { + qldoc: Some(format!("A class representing `{}` nodes.", type_name.kind)), + name: main_class_name, + is_abstract: false, + supertypes: vec![ + ql::Type::At(&node.dbscheme_name), + ql::Type::Normal("AstNode"), + ] + .into_iter() + .collect(), + characteristic_predicate: None, + predicates: vec![ + create_get_a_primary_ql_class(main_class_name), + create_get_location_predicate(main_table_name, main_table_arity), + ], + }; + + if fields.is_empty() { + main_class + .predicates + .push(create_get_text_predicate(main_table_name)); + } else { + let mut main_table_column_index: usize = 0; + let mut get_child_exprs: Vec<ql::Expression> = Vec::new(); + + // Iterate through the fields, creating: + // - classes to wrap union types if fields need them, + // - predicates to access the fields, + // - the QL expressions to access the fields that will be part of getAFieldOrChild. + for field in fields { + let (get_pred, get_child_expr) = create_field_getters( + main_table_name, + main_table_arity, + &mut main_table_column_index, + field, + nodes, + ); + main_class.predicates.push(get_pred); + if let Some(get_child_expr) = get_child_expr { + get_child_exprs.push(get_child_expr) + } + } + + main_class.predicates.push(ql::Predicate { + qldoc: Some(String::from("Gets a field or child node of this node.")), + name: "getAFieldOrChild", + overridden: true, + return_type: Some(ql::Type::Normal("AstNode")), + formal_parameters: vec![], + body: ql::Expression::Or(get_child_exprs), + }); + } + + classes.push(ql::TopLevel::Class(main_class)); + } + } + } + + classes +} diff --git a/ruby/node-types/Cargo.toml b/ruby/node-types/Cargo.toml new file mode 100644 index 00000000000..c751d7360d6 --- /dev/null +++ b/ruby/node-types/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "node-types" +version = "0.1.0" +authors = ["GitHub"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" \ No newline at end of file diff --git a/ruby/node-types/src/lib.rs b/ruby/node-types/src/lib.rs new file mode 100644 index 00000000000..9c1011e6b88 --- /dev/null +++ b/ruby/node-types/src/lib.rs @@ -0,0 +1,440 @@ +use serde::Deserialize; +use std::collections::BTreeMap; +use std::path::Path; + +use std::collections::BTreeSet as Set; +use std::fs; + +/// A lookup table from TypeName to Entry. +pub type NodeTypeMap = BTreeMap<TypeName, Entry>; + +#[derive(Debug)] +pub struct Entry { + pub dbscheme_name: String, + pub ql_class_name: String, + pub kind: EntryKind, +} + +#[derive(Debug)] +pub enum EntryKind { + Union { members: Set<TypeName> }, + Table { name: String, fields: Vec<Field> }, + Token { kind_id: usize }, +} + +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] +pub struct TypeName { + pub kind: String, + pub named: bool, +} + +#[derive(Debug)] +pub enum FieldTypeInfo { + /// The field has a single type. + Single(TypeName), + + /// The field can take one of several types, so we also provide the name of + /// the database union type that wraps them, and the corresponding QL class + /// name. + Multiple { + types: Set<TypeName>, + dbscheme_union: String, + ql_class: String, + }, + + /// The field can be one of several tokens, so the db type will be an `int` + /// with a `case @foo.kind` for each possiblity. + ReservedWordInt(BTreeMap<String, (usize, String)>), +} + +#[derive(Debug)] +pub struct Field { + pub parent: TypeName, + pub type_info: FieldTypeInfo, + /// The name of the field or None for the anonymous 'children' + /// entry from node_types.json + pub name: Option<String>, + /// The name of the predicate to get this field. + pub getter_name: String, + pub storage: Storage, +} + +fn name_for_field_or_child(name: &Option<String>) -> String { + match name { + Some(name) => name.clone(), + None => "child".to_owned(), + } +} + +#[derive(Debug)] +pub enum Storage { + /// the field is stored as a column in the parent table + Column { name: String }, + /// the field is stored in a link table + Table { + /// the name of the table + name: String, + /// the name of the column for the field in the dbscheme + column_name: String, + /// does it have an associated index column? + has_index: bool, + }, +} + +pub fn read_node_types(prefix: &str, node_types_path: &Path) -> std::io::Result<NodeTypeMap> { + let file = fs::File::open(node_types_path)?; + let node_types: Vec<NodeInfo> = serde_json::from_reader(file)?; + Ok(convert_nodes(prefix, &node_types)) +} + +pub fn read_node_types_str(prefix: &str, node_types_json: &str) -> std::io::Result<NodeTypeMap> { + let node_types: Vec<NodeInfo> = serde_json::from_str(node_types_json)?; + Ok(convert_nodes(prefix, &node_types)) +} + +fn convert_type(node_type: &NodeType) -> TypeName { + TypeName { + kind: node_type.kind.to_string(), + named: node_type.named, + } +} + +fn convert_types(node_types: &[NodeType]) -> Set<TypeName> { + node_types.iter().map(convert_type).collect() +} + +pub fn convert_nodes(prefix: &str, nodes: &[NodeInfo]) -> NodeTypeMap { + let mut entries = NodeTypeMap::new(); + let mut token_kinds = Set::new(); + + // First, find all the token kinds + for node in nodes { + if node.subtypes.is_none() + && node.fields.as_ref().map_or(0, |x| x.len()) == 0 + && node.children.is_none() + { + let type_name = TypeName { + kind: node.kind.clone(), + named: node.named, + }; + token_kinds.insert(type_name); + } + } + + for node in nodes { + let flattened_name = &node_type_name(&node.kind, node.named); + let dbscheme_name = escape_name(flattened_name); + let ql_class_name = dbscheme_name_to_class_name(&dbscheme_name); + let dbscheme_name = format!("{}_{}", prefix, &dbscheme_name); + if let Some(subtypes) = &node.subtypes { + // It's a tree-sitter supertype node, for which we create a union + // type. + entries.insert( + TypeName { + kind: node.kind.clone(), + named: node.named, + }, + Entry { + dbscheme_name, + ql_class_name, + kind: EntryKind::Union { + members: convert_types(subtypes), + }, + }, + ); + } else if node.fields.as_ref().map_or(0, |x| x.len()) == 0 && node.children.is_none() { + // Token kind, handled above. + } else { + // It's a product type, defined by a table. + let type_name = TypeName { + kind: node.kind.clone(), + named: node.named, + }; + let table_name = escape_name(&(format!("{}_def", &flattened_name))); + let table_name = format!("{}_{}", prefix, &table_name); + + let mut fields = Vec::new(); + + // If the type also has fields or children, then we create either + // auxiliary tables or columns in the defining table for them. + if let Some(node_fields) = &node.fields { + for (field_name, field_info) in node_fields { + add_field( + prefix, + &type_name, + Some(field_name.to_string()), + field_info, + &mut fields, + &token_kinds, + ); + } + } + if let Some(children) = &node.children { + // Treat children as if they were a field called 'child'. + add_field( + prefix, + &type_name, + None, + children, + &mut fields, + &token_kinds, + ); + } + entries.insert( + type_name, + Entry { + dbscheme_name, + ql_class_name, + kind: EntryKind::Table { + name: table_name, + fields, + }, + }, + ); + } + } + let mut counter = 0; + for type_name in token_kinds { + let entry = if type_name.named { + counter += 1; + let unprefixed_name = node_type_name(&type_name.kind, true); + Entry { + dbscheme_name: escape_name(&format!("{}_token_{}", &prefix, &unprefixed_name)), + ql_class_name: dbscheme_name_to_class_name(&escape_name(&unprefixed_name)), + kind: EntryKind::Token { kind_id: counter }, + } + } else { + Entry { + dbscheme_name: format!("{}_reserved_word", &prefix), + ql_class_name: "ReservedWord".to_owned(), + kind: EntryKind::Token { kind_id: 0 }, + } + }; + entries.insert(type_name, entry); + } + entries +} + +fn add_field( + prefix: &str, + parent_type_name: &TypeName, + field_name: Option<String>, + field_info: &FieldInfo, + fields: &mut Vec<Field>, + token_kinds: &Set<TypeName>, +) { + let parent_flattened_name = node_type_name(&parent_type_name.kind, parent_type_name.named); + let column_name = escape_name(&name_for_field_or_child(&field_name)); + let storage = if !field_info.multiple && field_info.required { + // This field must appear exactly once, so we add it as + // a column to the main table for the node type. + Storage::Column { name: column_name } + } else { + // Put the field in an auxiliary table. + let has_index = field_info.multiple; + let field_table_name = escape_name(&format!( + "{}_{}_{}", + &prefix, + parent_flattened_name, + &name_for_field_or_child(&field_name) + )); + Storage::Table { + has_index, + name: field_table_name, + column_name, + } + }; + let converted_types = convert_types(&field_info.types); + let type_info = if field_info + .types + .iter() + .all(|t| !t.named && token_kinds.contains(&convert_type(t))) + { + // All possible types for this field are reserved words. The db + // representation will be an `int` with a `case @foo.field = ...` to + // enumerate the possible values. + let mut field_token_ints: BTreeMap<String, (usize, String)> = BTreeMap::new(); + for (counter, t) in converted_types.into_iter().enumerate() { + let dbscheme_variant_name = + escape_name(&format!("{}_{}_{}", &prefix, parent_flattened_name, t.kind)); + field_token_ints.insert(t.kind.to_owned(), (counter, dbscheme_variant_name)); + } + FieldTypeInfo::ReservedWordInt(field_token_ints) + } else if field_info.types.len() == 1 { + FieldTypeInfo::Single(converted_types.into_iter().next().unwrap()) + } else { + // The dbscheme type for this field will be a union. In QL, it'll just be AstNode. + FieldTypeInfo::Multiple { + types: converted_types, + dbscheme_union: format!( + "{}_{}_{}_type", + &prefix, + &parent_flattened_name, + &name_for_field_or_child(&field_name) + ), + ql_class: "AstNode".to_owned(), + } + }; + let getter_name = format!( + "get{}", + dbscheme_name_to_class_name(&escape_name(&name_for_field_or_child(&field_name))) + ); + fields.push(Field { + parent: TypeName { + kind: parent_type_name.kind.to_string(), + named: parent_type_name.named, + }, + type_info, + name: field_name, + getter_name, + storage, + }); +} +#[derive(Deserialize)] +pub struct NodeInfo { + #[serde(rename = "type")] + pub kind: String, + pub named: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub fields: Option<BTreeMap<String, FieldInfo>>, + #[serde(skip_serializing_if = "Option::is_none")] + pub children: Option<FieldInfo>, + #[serde(skip_serializing_if = "Option::is_none")] + pub subtypes: Option<Vec<NodeType>>, +} + +#[derive(Deserialize)] +pub struct NodeType { + #[serde(rename = "type")] + pub kind: String, + pub named: bool, +} + +#[derive(Deserialize)] +pub struct FieldInfo { + pub multiple: bool, + pub required: bool, + pub types: Vec<NodeType>, +} + +/// Given a tree-sitter node type's (kind, named) pair, returns a single string +/// representing the (unescaped) name we'll use to refer to corresponding QL +/// type. +fn node_type_name(kind: &str, named: bool) -> String { + if named { + kind.to_string() + } else { + format!("{}_unnamed", kind) + } +} + +const RESERVED_KEYWORDS: [&str; 14] = [ + "boolean", "case", "date", "float", "int", "key", "of", "order", "ref", "string", "subtype", + "type", "unique", "varchar", +]; + +/// Returns a string that's a copy of `name` but suitably escaped to be a valid +/// QL identifier. +fn escape_name(name: &str) -> String { + let mut result = String::new(); + + // If there's a leading underscore, replace it with 'underscore_'. + if let Some(c) = name.chars().next() { + if c == '_' { + result.push_str("underscore"); + } + } + for c in name.chars() { + match c { + '{' => result.push_str("lbrace"), + '}' => result.push_str("rbrace"), + '<' => result.push_str("langle"), + '>' => result.push_str("rangle"), + '[' => result.push_str("lbracket"), + ']' => result.push_str("rbracket"), + '(' => result.push_str("lparen"), + ')' => result.push_str("rparen"), + '|' => result.push_str("pipe"), + '=' => result.push_str("equal"), + '~' => result.push_str("tilde"), + '?' => result.push_str("question"), + '`' => result.push_str("backtick"), + '^' => result.push_str("caret"), + '!' => result.push_str("bang"), + '#' => result.push_str("hash"), + '%' => result.push_str("percent"), + '&' => result.push_str("ampersand"), + '.' => result.push_str("dot"), + ',' => result.push_str("comma"), + '/' => result.push_str("slash"), + ':' => result.push_str("colon"), + ';' => result.push_str("semicolon"), + '"' => result.push_str("dquote"), + '*' => result.push_str("star"), + '+' => result.push_str("plus"), + '-' => result.push_str("minus"), + '@' => result.push_str("at"), + _ if c.is_uppercase() => { + result.push('_'); + result.push_str(&c.to_lowercase().to_string()) + } + _ => result.push(c), + } + } + + for &keyword in &RESERVED_KEYWORDS { + if result == keyword { + result.push_str("__"); + break; + } + } + + result +} + +pub fn to_snake_case(word: &str) -> String { + let mut prev_upper = true; + let mut result = String::new(); + for c in word.chars() { + if c.is_uppercase() { + if !prev_upper { + result.push('_') + } + prev_upper = true; + result.push(c.to_ascii_lowercase()); + } else { + prev_upper = false; + result.push(c); + } + } + result +} +/// Given a valid dbscheme name (i.e. in snake case), produces the equivalent QL +/// name (i.e. in CamelCase). For example, "foo_bar_baz" becomes "FooBarBaz". +fn dbscheme_name_to_class_name(dbscheme_name: &str) -> String { + fn to_title_case(word: &str) -> String { + let mut first = true; + let mut result = String::new(); + for c in word.chars() { + if first { + first = false; + result.push(c.to_ascii_uppercase()); + } else { + result.push(c); + } + } + result + } + dbscheme_name + .split('_') + .map(|word| to_title_case(word)) + .collect::<Vec<String>>() + .join("") +} + +#[test] +fn to_snake_case_test() { + assert_eq!("ruby", to_snake_case("Ruby")); + assert_eq!("erb", to_snake_case("ERB")); + assert_eq!("embedded_template", to_snake_case("EmbeddedTemplate")); +} diff --git a/ruby/ql/consistency-queries/AstConsistency.ql b/ruby/ql/consistency-queries/AstConsistency.ql new file mode 100644 index 00000000000..8a5ebcdcda7 --- /dev/null +++ b/ruby/ql/consistency-queries/AstConsistency.ql @@ -0,0 +1,25 @@ +import codeql.ruby.AST +import codeql.ruby.ast.internal.Synthesis + +query predicate missingParent(AstNode node, string cls) { + not exists(node.getParent()) and + node.getLocation().getFile().getExtension() != "erb" and + not node instanceof Toplevel and + cls = node.getPrimaryQlClasses() +} + +pragma[noinline] +private AstNode parent(AstNode child, int desugarLevel) { + result = child.getParent() and + desugarLevel = desugarLevel(result) +} + +query predicate multipleParents(AstNode node, AstNode parent, string cls) { + parent = node.getParent() and + cls = parent.getPrimaryQlClasses() and + exists(AstNode one, AstNode two, int desugarLevel | + one = parent(node, desugarLevel) and + two = parent(node, desugarLevel) and + one != two + ) +} diff --git a/ruby/ql/consistency-queries/CfgConsistency.ql b/ruby/ql/consistency-queries/CfgConsistency.ql new file mode 100644 index 00000000000..c2aaaad0ac1 --- /dev/null +++ b/ruby/ql/consistency-queries/CfgConsistency.ql @@ -0,0 +1 @@ +import codeql.ruby.controlflow.internal.ControlFlowGraphImplShared::Consistency diff --git a/ruby/ql/consistency-queries/DataFlowConsistency.ql b/ruby/ql/consistency-queries/DataFlowConsistency.ql new file mode 100644 index 00000000000..f5bc9552ab6 --- /dev/null +++ b/ruby/ql/consistency-queries/DataFlowConsistency.ql @@ -0,0 +1 @@ +import codeql.ruby.dataflow.internal.DataFlowImplConsistency::Consistency diff --git a/ruby/ql/consistency-queries/SsaConsistency.ql b/ruby/ql/consistency-queries/SsaConsistency.ql new file mode 100644 index 00000000000..79289273f95 --- /dev/null +++ b/ruby/ql/consistency-queries/SsaConsistency.ql @@ -0,0 +1,22 @@ +import ruby +import codeql.ruby.dataflow.SSA +import codeql.ruby.controlflow.ControlFlowGraph + +query predicate nonUniqueDef(CfgNode read, Ssa::Definition def) { + read = def.getARead() and + exists(Ssa::Definition other | read = other.getARead() and other != def) +} + +query predicate readWithoutDef(LocalVariableReadAccess read) { + exists(CfgNode node | + node = read.getAControlFlowNode() and + not node = any(Ssa::Definition def).getARead() + ) +} + +query predicate deadDef(Ssa::Definition def, LocalVariable v) { + v = def.getSourceVariable() and + not v.isCaptured() and + not exists(def.getARead()) and + not def = any(Ssa::PhiNode phi).getAnInput() +} diff --git a/ruby/ql/consistency-queries/VariablesConsistency.ql b/ruby/ql/consistency-queries/VariablesConsistency.ql new file mode 100644 index 00000000000..ed2183340d9 --- /dev/null +++ b/ruby/ql/consistency-queries/VariablesConsistency.ql @@ -0,0 +1,6 @@ +import codeql.ruby.ast.Variable + +query predicate ambiguousVariable(VariableAccess access, Variable variable) { + access.getVariable() = variable and + count(access.getVariable()) > 1 +} diff --git a/ruby/ql/consistency-queries/qlpack.yml b/ruby/ql/consistency-queries/qlpack.yml new file mode 100644 index 00000000000..fa76023b646 --- /dev/null +++ b/ruby/ql/consistency-queries/qlpack.yml @@ -0,0 +1,5 @@ +name: codeql/ruby-consistency-queries +version: 0.0.1 +dependencies: + codeql/ruby-all: 0.0.1 + diff --git a/ruby/ql/docs/experimental.md b/ruby/ql/docs/experimental.md new file mode 100644 index 00000000000..cd356e913d3 --- /dev/null +++ b/ruby/ql/docs/experimental.md @@ -0,0 +1,37 @@ +# Experimental CodeQL queries and libraries + +In addition to our standard CodeQL queries and libraries, this repository may also contain queries and libraries of a more experimental nature. Experimental queries and libraries can be improved incrementally and may eventually reach a sufficient maturity to be included in our standard libraries and queries. + +Experimental queries and libraries may not be actively maintained as the standard libraries evolve. They may also be changed in backwards-incompatible ways or may be removed entirely in the future without deprecation warnings. + +## Requirements + +1. **Directory structure** + + - Experimental queries and libraries are stored in the `ql/src/experimental` subdirectory, and any corresponding tests in `ql/test/experimental`. + - The structure of an `experimental` subdirectory mirrors the structure of standard queries and libraries (or tests) in the parent directory. + +2. **Query metadata** + + - The query `@id` must not clash with any other queries in the repository. + - The query must have a `@name` and `@description` to explain its purpose. + - The query must have a `@kind` and `@problem.severity` as required by CodeQL tools. + + For details, see the [guide on query metadata](https://github.com/github/codeql/blob/master/docs/query-metadata-style-guide.md). + +3. **Formatting** + + - The queries and libraries must be [autoformatted](https://code.visualstudio.com/docs/editor/codebasics#_formatting). + +4. **Compilation** + + - Compilation of the query and any associated libraries and tests must be resilient to future development of the standard libraries. This means that the functionality cannot use internal APIs, cannot depend on the output of `getAQlClass`, and cannot make use of regexp matching on `toString`. + - The query and any associated libraries and tests must not cause any compiler warnings to be emitted (such as use of deprecated functionality or missing `override` annotations). + +5. **Results** + + - The query must have at least one true positive result on some revision of a real project. + +## Non-requirements + +Other criteria typically required for our standard queries and libraries are not required for experimental queries and libraries. In particular, fully disciplined query [metadata](https://github.com/github/codeql/blob/master/docs/query-metadata-style-guide.md), query [help](https://github.com/github/codeql/blob/master/docs/query-help-style-guide.md), tests, a low false positive rate and performance tuning are not required (but nonetheless recommended). diff --git a/ruby/ql/examples/qlpack.lock.yml b/ruby/ql/examples/qlpack.lock.yml new file mode 100644 index 00000000000..06dd07fc7dc --- /dev/null +++ b/ruby/ql/examples/qlpack.lock.yml @@ -0,0 +1,4 @@ +--- +dependencies: {} +compiled: false +lockVersion: 1.0.0 diff --git a/ruby/ql/examples/qlpack.yml b/ruby/ql/examples/qlpack.yml new file mode 100644 index 00000000000..87a6ffae9c1 --- /dev/null +++ b/ruby/ql/examples/qlpack.yml @@ -0,0 +1,4 @@ +name: codeql/ruby-examples +version: 0.0.2 +dependencies: + codeql/ruby-all: ^0.0.2 diff --git a/ruby/ql/examples/queries.xml b/ruby/ql/examples/queries.xml new file mode 100644 index 00000000000..a7ce9735ef4 --- /dev/null +++ b/ruby/ql/examples/queries.xml @@ -0,0 +1 @@ +<queries language="ruby"/> diff --git a/ruby/ql/examples/snippets/emptythen.ql b/ruby/ql/examples/snippets/emptythen.ql new file mode 100644 index 00000000000..531556fc7fa --- /dev/null +++ b/ruby/ql/examples/snippets/emptythen.ql @@ -0,0 +1,18 @@ +/** + * @name If statements with empty then branch + * @description Finds 'if' statements where the 'then' branch is + * an empty block statement + * @id ruby/examples/emptythen + * @tags if + * then + * empty + * conditional + * branch + * statement + */ + +import ruby + +from IfExpr i +where not exists(i.getThen().getAChild()) +select i diff --git a/ruby/ql/lib/codeql/IDEContextual.qll b/ruby/ql/lib/codeql/IDEContextual.qll new file mode 100644 index 00000000000..0e58b1d878b --- /dev/null +++ b/ruby/ql/lib/codeql/IDEContextual.qll @@ -0,0 +1,19 @@ +private import codeql.files.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) { + // The name provided for a file in the source archive by the VS Code extension + // has some differences from the absolute path in the database: + // 1. colons are replaced by underscores + // 2. there's a leading slash, even for Windows paths: "C:/foo/bar" -> + // "/C_/foo/bar" + // 3. double slashes in UNC prefixes are replaced with a single slash + // We can handle 2 and 3 together by unconditionally adding a leading slash + // before replacing double slashes. + name = ("/" + result.getAbsolutePath().replaceAll(":", "_")).replaceAll("//", "/") +} diff --git a/ruby/ql/lib/codeql/Locations.qll b/ruby/ql/lib/codeql/Locations.qll new file mode 100644 index 00000000000..8bed8d904f1 --- /dev/null +++ b/ruby/ql/lib/codeql/Locations.qll @@ -0,0 +1,66 @@ +/** Provides classes for working with locations. */ + +import files.FileSystem + +/** + * A location as given by a file, a start line, a start column, + * an end line, and an end column. + * + * For more information about locations see [LGTM locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +class Location extends @location { + /** Gets the file for this location. */ + File getFile() { locations_default(this, result, _, _, _, _) } + + /** Gets the 1-based line number (inclusive) where this location starts. */ + int getStartLine() { locations_default(this, _, result, _, _, _) } + + /** Gets the 1-based column number (inclusive) where this location starts. */ + int getStartColumn() { locations_default(this, _, _, result, _, _) } + + /** Gets the 1-based line number (inclusive) where this location ends. */ + int getEndLine() { locations_default(this, _, _, _, result, _) } + + /** Gets the 1-based column number (inclusive) where this location ends. */ + int getEndColumn() { locations_default(this, _, _, _, _, result) } + + /** Gets the number of lines covered by this location. */ + int getNumLines() { result = this.getEndLine() - this.getStartLine() + 1 } + + /** Gets a textual representation of this element. */ + string toString() { + exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | + this.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and + result = filepath + "@" + startline + ":" + startcolumn + ":" + endline + ":" + endcolumn + ) + } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [LGTM locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + exists(File f | + locations_default(this, f, startline, startcolumn, endline, endcolumn) and + filepath = f.getAbsolutePath() + ) + } + + /** Holds if this location starts strictly before the specified location. */ + pragma[inline] + predicate strictlyBefore(Location other) { + this.getStartLine() < other.getStartLine() + or + this.getStartLine() = other.getStartLine() and this.getStartColumn() < other.getStartColumn() + } +} + +/** An entity representing an empty location. */ +class EmptyLocation extends Location { + EmptyLocation() { this.hasLocationInfo("", 0, 0, 0, 0) } +} diff --git a/ruby/ql/lib/codeql/files/FileSystem.qll b/ruby/ql/lib/codeql/files/FileSystem.qll new file mode 100644 index 00000000000..552b85a4673 --- /dev/null +++ b/ruby/ql/lib/codeql/files/FileSystem.qll @@ -0,0 +1,177 @@ +/** Provides classes for working with files and folders. */ + +private import codeql.Locations + +/** A file or folder. */ +abstract class Container extends @container { + /** Gets a file or sub-folder in this container. */ + Container getAChildContainer() { this = result.getParentContainer() } + + /** Gets a file in this container. */ + File getAFile() { result = this.getAChildContainer() } + + /** Gets a sub-folder in this container. */ + Folder getAFolder() { result = this.getAChildContainer() } + + /** + * Gets the absolute, canonical path of this container, using forward slashes + * as path separator. + * + * The path starts with a _root prefix_ followed by zero or more _path + * segments_ separated by forward slashes. + * + * The root prefix is of one of the following forms: + * + * 1. A single forward slash `/` (Unix-style) + * 2. An upper-case drive letter followed by a colon and a forward slash, + * such as `C:/` (Windows-style) + * 3. Two forward slashes, a computer name, and then another forward slash, + * such as `//FileServer/` (UNC-style) + * + * Path segments are never empty (that is, absolute paths never contain two + * contiguous slashes, except as part of a UNC-style root prefix). Also, path + * segments never contain forward slashes, and no path segment is of the + * form `.` (one dot) or `..` (two dots). + * + * Note that an absolute path never ends with a forward slash, except if it is + * a bare root prefix, that is, the path has no path segments. A container + * whose absolute path has no segments is always a `Folder`, not a `File`. + */ + abstract string getAbsolutePath(); + + /** + * Gets the base name of this container including extension, that is, the last + * segment of its absolute path, or the empty string if it has no segments. + * + * Here are some examples of absolute paths and the corresponding base names + * (surrounded with quotes to avoid ambiguity): + * + * <table border="1"> + * <tr><th>Absolute path</th><th>Base name</th></tr> + * <tr><td>"/tmp/tst.go"</td><td>"tst.go"</td></tr> + * <tr><td>"C:/Program Files (x86)"</td><td>"Program Files (x86)"</td></tr> + * <tr><td>"/"</td><td>""</td></tr> + * <tr><td>"C:/"</td><td>""</td></tr> + * <tr><td>"D:/"</td><td>""</td></tr> + * <tr><td>"//FileServer/"</td><td>""</td></tr> + * </table> + */ + string getBaseName() { + result = this.getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1) + } + + /** + * Gets the extension of this container, that is, the suffix of its base name + * after the last dot character, if any. + * + * In particular, + * + * - if the name does not include a dot, there is no extension, so this + * predicate has no result; + * - if the name ends in a dot, the extension is the empty string; + * - if the name contains multiple dots, the extension follows the last dot. + * + * Here are some examples of absolute paths and the corresponding extensions + * (surrounded with quotes to avoid ambiguity): + * + * <table border="1"> + * <tr><th>Absolute path</th><th>Extension</th></tr> + * <tr><td>"/tmp/tst.go"</td><td>"go"</td></tr> + * <tr><td>"/tmp/.classpath"</td><td>"classpath"</td></tr> + * <tr><td>"/bin/bash"</td><td>not defined</td></tr> + * <tr><td>"/tmp/tst2."</td><td>""</td></tr> + * <tr><td>"/tmp/x.tar.gz"</td><td>"gz"</td></tr> + * </table> + */ + string getExtension() { + result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) + } + + /** Gets the file in this container that has the given `baseName`, if any. */ + File getFile(string baseName) { + result = this.getAFile() and + result.getBaseName() = baseName + } + + /** Gets the sub-folder in this container that has the given `baseName`, if any. */ + Folder getFolder(string baseName) { + result = this.getAFolder() and + result.getBaseName() = baseName + } + + /** Gets the parent container of this file or folder, if any. */ + Container getParentContainer() { containerparent(result, this) } + + /** + * Gets the relative path of this file or folder from the root folder of the + * analyzed source location. The relative path of the root folder itself is + * the empty string. + * + * This has no result if the container is outside the source root, that is, + * if the root folder is not a reflexive, transitive parent of this container. + */ + string getRelativePath() { + exists(string absPath, string pref | + absPath = this.getAbsolutePath() and sourceLocationPrefix(pref) + | + absPath = pref and result = "" + or + absPath = pref.regexpReplaceAll("/$", "") + "/" + result and + not result.matches("/%") + ) + } + + /** + * Gets the stem of this container, that is, the prefix of its base name up to + * (but not including) the last dot character if there is one, or the entire + * base name if there is not. + * + * Here are some examples of absolute paths and the corresponding stems + * (surrounded with quotes to avoid ambiguity): + * + * <table border="1"> + * <tr><th>Absolute path</th><th>Stem</th></tr> + * <tr><td>"/tmp/tst.go"</td><td>"tst"</td></tr> + * <tr><td>"/tmp/.classpath"</td><td>""</td></tr> + * <tr><td>"/bin/bash"</td><td>"bash"</td></tr> + * <tr><td>"/tmp/tst2."</td><td>"tst2"</td></tr> + * <tr><td>"/tmp/x.tar.gz"</td><td>"x.tar"</td></tr> + * </table> + */ + string getStem() { + result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) + } + + /** + * Gets a URL representing the location of this container. + * + * For more information see https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/#providing-urls. + */ + abstract string getURL(); + + /** + * Gets a textual representation of the path of this container. + * + * This is the absolute path of the container. + */ + string toString() { result = this.getAbsolutePath() } +} + +/** A folder. */ +class Folder extends Container, @folder { + override string getAbsolutePath() { folders(this, result) } + + /** Gets the URL of this folder. */ + override string getURL() { result = "folder://" + this.getAbsolutePath() } +} + +/** A file. */ +class File extends Container, @file { + override string getAbsolutePath() { files(this, result) } + + /** Gets the URL of this file. */ + override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" } + + /** Holds if this file was extracted from ordinary source code. */ + predicate fromSource() { any() } +} diff --git a/ruby/ql/lib/codeql/ruby/AST.qll b/ruby/ql/lib/codeql/ruby/AST.qll new file mode 100644 index 00000000000..2d006b6312a --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/AST.qll @@ -0,0 +1,141 @@ +import codeql.Locations +import ast.Call +import ast.Control +import ast.Constant +import ast.Erb +import ast.Expr +import ast.Literal +import ast.Method +import ast.Module +import ast.Parameter +import ast.Operation +import ast.Pattern +import ast.Scope +import ast.Statement +import ast.Variable +private import ast.internal.AST +private import ast.internal.Scope +private import ast.internal.Synthesis +private import ast.internal.TreeSitter + +/** + * A node in the abstract syntax tree. This class is the base class for all Ruby + * program elements. + */ +class AstNode extends TAstNode { + /** + * Gets the name of a primary CodeQL class to which this node belongs. + * + * This predicate always has a result. If no primary class can be + * determined, the result is `"???"`. If multiple primary classes match, + * this predicate can have multiple results. + */ + string getAPrimaryQlClass() { result = "???" } + + /** + * Gets a comma-separated list of the names of the primary CodeQL classes to + * which this element belongs. + */ + final string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") } + + /** Gets the enclosing module, if any. */ + ModuleBase getEnclosingModule() { + exists(Scope::Range s | + s = scopeOf(toGeneratedInclSynth(this)) and + toGeneratedInclSynth(result) = s.getEnclosingModule() + ) + } + + /** Gets the enclosing method, if any. */ + MethodBase getEnclosingMethod() { + exists(Scope::Range s | + s = scopeOf(toGeneratedInclSynth(this)) and + toGeneratedInclSynth(result) = s.getEnclosingMethod() + ) + } + + /** Gets a textual representation of this node. */ + cached + string toString() { none() } + + /** Gets the location of this node. */ + Location getLocation() { result = getLocation(this) } + + /** Gets the file of this node. */ + final File getFile() { result = this.getLocation().getFile() } + + /** Gets a child node of this `AstNode`. */ + final AstNode getAChild() { result = this.getAChild(_) } + + /** Gets the parent of this `AstNode`, if this node is not a root node. */ + final AstNode getParent() { result.getAChild() = this } + + /** + * Gets a child of this node, which can also be retrieved using a predicate + * named `pred`. + */ + cached + AstNode getAChild(string pred) { + pred = "getDesugared" and + result = this.getDesugared() + } + + /** + * Holds if this node was synthesized to represent an implicit AST node not + * present in the source code. In the following example method call, the + * receiver is an implicit `self` reference, for which there is a synthesized + * `Self` node. + * + * ```rb + * foo(123) + * ``` + */ + final predicate isSynthesized() { this = getSynthChild(_, _) } + + /** + * Gets the desugared version of this AST node, if any. + * + * For example, the desugared version of + * + * ```rb + * x += y + * ``` + * + * is + * + * ```rb + * x = x + y + * ``` + * + * when `x` is a variable. Whenever an AST node can be desugared, + * then the desugared version is used in the control-flow graph. + */ + final AstNode getDesugared() { result = getSynthChild(this, -1) } +} + +/** A Ruby source file */ +class RubyFile extends File { + RubyFile() { ruby_ast_node_parent(_, this, _) } + + /** Gets a token in this file. */ + private Ruby::Token getAToken() { result.getLocation().getFile() = this } + + /** Holds if `line` contains a token. */ + private predicate line(int line, boolean comment) { + exists(Ruby::Token token, Location l | + token = this.getAToken() and + l = token.getLocation() and + line in [l.getStartLine() .. l.getEndLine()] and + if token instanceof @ruby_token_comment then comment = true else comment = false + ) + } + + /** Gets the number of lines in this file. */ + int getNumberOfLines() { result = max([0, this.getAToken().getLocation().getEndLine()]) } + + /** Gets the number of lines of code in this file. */ + int getNumberOfLinesOfCode() { result = count(int line | this.line(line, false)) } + + /** Gets the number of lines of comments in this file. */ + int getNumberOfLinesOfComments() { result = count(int line | this.line(line, true)) } +} diff --git a/ruby/ql/lib/codeql/ruby/ApiGraphs.qll b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll new file mode 100644 index 00000000000..4a0bee21544 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll @@ -0,0 +1,410 @@ +/** + * Provides an implementation of _API graphs_, which are an abstract representation of the API + * surface used and/or defined by a code base. + * + * The nodes of the API graph represent definitions and uses of API components. The edges are + * directed and labeled; they specify how the components represented by nodes relate to each other. + */ + +private import ruby +import codeql.ruby.DataFlow +import codeql.ruby.typetracking.TypeTracker +import codeql.ruby.ast.internal.Module +private import codeql.ruby.controlflow.CfgNodes + +/** + * Provides classes and predicates for working with APIs used in a database. + */ +module API { + /** + * An abstract representation of a definition or use of an API component such as a Ruby module, + * or the result of a method call. + */ + class Node extends Impl::TApiNode { + /** + * Gets a data-flow node corresponding to a use of the API component represented by this node. + * + * For example, `Kernel.format "%s world!", "Hello"` is a use of the return of the `format` function of + * the `Kernel` module. + * + * This includes indirect uses found via data flow. + */ + DataFlow::Node getAUse() { + exists(DataFlow::LocalSourceNode src | Impl::use(this, src) | + Impl::trackUseNode(src).flowsTo(result) + ) + } + + /** + * Gets an immediate use of the API component represented by this node. + * + * Unlike `getAUse()`, this predicate only gets the immediate references, not the indirect uses + * found via data flow. + */ + DataFlow::LocalSourceNode getAnImmediateUse() { Impl::use(this, result) } + + /** + * Gets a call to a method on the receiver represented by this API component. + */ + DataFlow::CallNode getAMethodCall(string method) { + result = this.getReturn(method).getAnImmediateUse() + } + + /** + * Gets a node representing member `m` of this API component. + * + * For example, a member can be: + * + * - A submodule of a module + * - An attribute of an object + */ + bindingset[m] + bindingset[result] + Node getMember(string m) { result = this.getASuccessor(Label::member(m)) } + + /** + * Gets a node representing a member of this API component where the name of the member is + * not known statically. + */ + Node getUnknownMember() { result = this.getASuccessor(Label::unknownMember()) } + + /** + * Gets a node representing a member of this API component where the name of the member may + * or may not be known statically. + */ + Node getAMember() { + result = this.getASuccessor(Label::member(_)) or + result = this.getUnknownMember() + } + + /** + * Gets a node representing an instance of this API component, that is, an object whose + * constructor is the function represented by this node. + * + * For example, if this node represents a use of some class `A`, then there might be a node + * representing instances of `A`, typically corresponding to expressions `new A()` at the + * source level. + * + * This predicate may have multiple results when there are multiple constructor calls invoking this API component. + * Consider using `getAnInstantiation()` if there is a need to distinguish between individual constructor calls. + */ + Node getInstance() { result = this.getASuccessor(Label::instance()) } + + /** + * Gets a node representing the result of calling a method on the receiver represented by this node. + */ + Node getReturn(string method) { result = this.getASuccessor(Label::return(method)) } + + /** + * Gets a `new` call to the function represented by this API component. + */ + DataFlow::Node getAnInstantiation() { result = this.getInstance().getAnImmediateUse() } + + /** + * Gets a node representing a subclass of the class represented by this node. + */ + Node getASubclass() { result = this.getASuccessor(Label::subclass()) } + + /** + * Gets a string representation of the lexicographically least among all shortest access paths + * from the root to this node. + */ + string getPath() { + result = min(string p | p = this.getAPath(Impl::distanceFromRoot(this)) | p) + } + + /** + * Gets a node such that there is an edge in the API graph between this node and the other + * one, and that edge is labeled with `lbl`. + */ + Node getASuccessor(string lbl) { Impl::edge(this, lbl, result) } + + /** + * Gets a node such that there is an edge in the API graph between that other node and + * this one, and that edge is labeled with `lbl` + */ + Node getAPredecessor(string lbl) { this = result.getASuccessor(lbl) } + + /** + * Gets a node such that there is an edge in the API graph between this node and the other + * one. + */ + Node getAPredecessor() { result = this.getAPredecessor(_) } + + /** + * Gets a node such that there is an edge in the API graph between that other node and + * this one. + */ + Node getASuccessor() { result = this.getASuccessor(_) } + + /** + * Gets the data-flow node that gives rise to this node, if any. + */ + DataFlow::Node getInducingNode() { this = Impl::MkUse(result) } + + /** Gets the location of this node. */ + Location getLocation() { + result = this.getInducingNode().getLocation() + or + // For nodes that do not have a meaningful location, `path` is the empty string and all other + // parameters are zero. + not exists(this.getInducingNode()) and + result instanceof EmptyLocation + } + + /** + * Gets a textual representation of this element. + */ + abstract string toString(); + + /** + * Gets a path of the given `length` from the root to this node. + */ + private string getAPath(int length) { + this instanceof Impl::MkRoot and + length = 0 and + result = "" + or + exists(Node pred, string lbl, string predpath | + Impl::edge(pred, lbl, this) and + lbl != "" and + predpath = pred.getAPath(length - 1) and + exists(string dot | if length = 1 then dot = "" else dot = "." | + result = predpath + dot + lbl and + // avoid producing strings longer than 1MB + result.length() < 1000 * 1000 + ) + ) and + length in [1 .. Impl::distanceFromRoot(this)] + } + + /** Gets the shortest distance from the root to this node in the API graph. */ + int getDepth() { result = Impl::distanceFromRoot(this) } + } + + /** The root node of an API graph. */ + class Root extends Node, Impl::MkRoot { + override string toString() { result = "root" } + } + + /** A node corresponding to the use of an API component. */ + class Use extends Node, Impl::MkUse { + override string toString() { + exists(string type | type = "Use " | + result = type + this.getPath() + or + not exists(this.getPath()) and result = type + "with no path" + ) + } + } + + /** Gets the root node. */ + Root root() { any() } + + /** + * Gets a node corresponding to a top-level member `m` (typically a module). + * + * This is equivalent to `root().getAMember("m")`. + * + * Note: You should only use this predicate for top level modules or classes. If you want nodes corresponding to a nested module or class, + * you should use `.getMember` on the parent module/class. For example, for nodes corresponding to the class `Gem::Version`, + * use `getTopLevelMember("Gem").getMember("Version")`. + */ + Node getTopLevelMember(string m) { result = root().getMember(m) } + + /** + * Provides the actual implementation of API graphs, cached for performance. + * + * Ideally, we'd like nodes to correspond to (global) access paths, with edge labels + * corresponding to extending the access path by one element. We also want to be able to map + * nodes to their definitions and uses in the data-flow graph, and this should happen modulo + * (inter-procedural) data flow. + * + * This, however, is not easy to implement, since access paths can have unbounded length + * and we need some way of recognizing cycles to avoid non-termination. Unfortunately, expressing + * a condition like "this node hasn't been involved in constructing any predecessor of + * this node in the API graph" without negative recursion is tricky. + * + * So instead most nodes are directly associated with a data-flow node, representing + * either a use or a definition of an API component. This ensures that we only have a finite + * number of nodes. However, we can now have multiple nodes with the same access + * path, which are essentially indistinguishable for a client of the API. + * + * On the other hand, a single node can have multiple access paths (which is, of + * course, unavoidable). We pick as canonical the alphabetically least access path with + * shortest length. + */ + cached + private module Impl { + cached + newtype TApiNode = + /** The root of the API graph. */ + MkRoot() or + /** A use of an API member at the node `nd`. */ + MkUse(DataFlow::Node nd) { isUse(nd) } + + private string resolveTopLevel(ConstantReadAccess read) { + TResolved(result) = resolveScopeExpr(read) and + not result.matches("%::%") + } + + /** + * Holds if `ref` is a use of a node that should have an incoming edge from the root + * node labeled `lbl` in the API graph. + */ + cached + predicate useRoot(string lbl, DataFlow::Node ref) { + exists(string name, ExprNodes::ConstantAccessCfgNode access, ConstantReadAccess read | + access = ref.asExpr() and + lbl = Label::member(read.getName()) and + read = access.getExpr() + | + name = resolveTopLevel(read) + or + name = read.getName() and + not exists(resolveTopLevel(read)) and + not exists(read.getScopeExpr()) + ) + } + + /** + * Holds if `ref` is a use of a node that should have an incoming edge from use node + * `base` labeled `lbl` in the API graph. + */ + cached + predicate useUse(DataFlow::LocalSourceNode base, string lbl, DataFlow::Node ref) { + exists(ExprCfgNode node | + // First, we find a predecessor of the node `ref` that we want to determine. The predecessor + // is any node that is a type-tracked use of a data flow node (`src`), which is itself a + // reference to the API node `base`. Thus, `pred` and `src` both represent uses of `base`. + // + // Once we have identified the predecessor, we define its relation to the successor `ref` as + // well as the label on the edge from `pred` to `ref`. This label describes the nature of + // the relationship between `pred` and `ref`. + useExpr(node, base) + | + // // Referring to an attribute on a node that is a use of `base`: + // pred = `Rails` part of `Rails::Whatever` + // lbl = `Whatever` + // ref = `Rails::Whatever` + exists(ExprNodes::ConstantAccessCfgNode c, ConstantReadAccess read | + not exists(resolveTopLevel(read)) and + node = c.getScopeExpr() and + lbl = Label::member(read.getName()) and + ref.asExpr() = c and + read = c.getExpr() + ) + or + // Calling a method on a node that is a use of `base` + exists(ExprNodes::MethodCallCfgNode call, string name | + node = call.getReceiver() and + name = call.getExpr().getMethodName() and + lbl = Label::return(name) and + name != "new" and + ref.asExpr() = call + ) + or + // Calling the `new` method on a node that is a use of `base`, which creates a new instance + exists(ExprNodes::MethodCallCfgNode call | + node = call.getReceiver() and + lbl = Label::instance() and + call.getExpr().getMethodName() = "new" and + ref.asExpr() = call + ) + ) + } + + pragma[nomagic] + private predicate isUse(DataFlow::Node nd) { + useRoot(_, nd) + or + useUse(_, _, nd) + } + + pragma[nomagic] + private predicate useExpr(ExprCfgNode node, DataFlow::LocalSourceNode src) { + exists(DataFlow::LocalSourceNode pred | + pred = trackUseNode(src) and + pred.flowsTo(any(DataFlow::ExprNode n | n.getExprNode() = node)) + ) + } + + /** + * Holds if `ref` is a use of node `nd`. + */ + cached + predicate use(TApiNode nd, DataFlow::Node ref) { nd = MkUse(ref) } + + /** + * Gets a data-flow node to which `src`, which is a use of an API-graph node, flows. + * + * The flow from `src` to that node may be inter-procedural. + */ + private DataFlow::LocalSourceNode trackUseNode(DataFlow::Node src, TypeTracker t) { + // Declaring `src` to be a `LocalSourceNode` currently causes a redundant check in the + // recursive case, so instead we check it explicitly here. + src instanceof DataFlow::LocalSourceNode and + t.start() and + isUse(src) and + result = src + or + exists(TypeTracker t2 | result = trackUseNode(src, t2).track(t2, t)) + } + + /** + * Gets a data-flow node to which `src`, which is a use of an API-graph node, flows. + * + * The flow from `src` to that node may be inter-procedural. + */ + cached + DataFlow::LocalSourceNode trackUseNode(DataFlow::LocalSourceNode src) { + result = trackUseNode(src, TypeTracker::end()) + } + + /** + * Holds if there is an edge from `pred` to `succ` in the API graph that is labeled with `lbl`. + */ + cached + predicate edge(TApiNode pred, string lbl, TApiNode succ) { + /* Every node that is a use of an API component is itself added to the API graph. */ + exists(DataFlow::LocalSourceNode ref | succ = MkUse(ref) | + pred = MkRoot() and + useRoot(lbl, ref) + or + exists(DataFlow::Node nd | + pred = MkUse(nd) and + useUse(nd, lbl, ref) + ) + ) + } + + /** + * Holds if there is an edge from `pred` to `succ` in the API graph. + */ + private predicate edge(TApiNode pred, TApiNode succ) { edge(pred, _, succ) } + + /** Gets the shortest distance from the root to `nd` in the API graph. */ + cached + int distanceFromRoot(TApiNode nd) = shortestDistances(MkRoot/0, edge/2)(_, nd, result) + } +} + +private module Label { + /** Gets the `member` edge label for member `m`. */ + bindingset[m] + bindingset[result] + string member(string m) { result = "getMember(\"" + m + "\")" } + + /** Gets the `member` edge label for the unknown member. */ + string unknownMember() { result = "getUnknownMember()" } + + /** Gets the `instance` edge label. */ + string instance() { result = "instance" } + + /** Gets the `return` edge label. */ + bindingset[m] + bindingset[result] + string return(string m) { result = "getReturn(\"" + m + "\")" } + + string subclass() { result = "getASubclass()" } +} diff --git a/ruby/ql/lib/codeql/ruby/CFG.qll b/ruby/ql/lib/codeql/ruby/CFG.qll new file mode 100644 index 00000000000..77507b05a7f --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/CFG.qll @@ -0,0 +1,5 @@ +/** Provides classes representing the control flow graph. */ + +import controlflow.ControlFlowGraph +import controlflow.CfgNodes as CfgNodes +import controlflow.BasicBlocks diff --git a/ruby/ql/lib/codeql/ruby/Concepts.qll b/ruby/ql/lib/codeql/ruby/Concepts.qll new file mode 100644 index 00000000000..9308c1ed1a1 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/Concepts.qll @@ -0,0 +1,603 @@ +/** + * Provides abstract classes representing generic concepts such as file system + * access or system command execution, for which individual framework libraries + * provide concrete subclasses. + */ + +private import codeql.ruby.AST +private import codeql.ruby.CFG +private import codeql.ruby.DataFlow +private import codeql.ruby.Frameworks +private import codeql.ruby.dataflow.RemoteFlowSources +private import codeql.ruby.ApiGraphs + +/** + * A data-flow node that executes SQL statements. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `SqlExecution::Range` instead. + */ +class SqlExecution extends DataFlow::Node instanceof SqlExecution::Range { + /** Gets the argument that specifies the SQL statements to be executed. */ + DataFlow::Node getSql() { result = super.getSql() } +} + +/** Provides a class for modeling new SQL execution APIs. */ +module SqlExecution { + /** + * A data-flow node that executes SQL statements. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `SqlExecution` instead. + */ + abstract class Range extends DataFlow::Node { + /** Gets the argument that specifies the SQL statements to be executed. */ + abstract DataFlow::Node getSql(); + } +} + +/** + * A data flow node that performs a file system access, including reading and writing data, + * creating and deleting files and folders, checking and updating permissions, and so on. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `FileSystemAccess::Range` instead. + */ +class FileSystemAccess extends DataFlow::Node instanceof FileSystemAccess::Range { + /** Gets an argument to this file system access that is interpreted as a path. */ + DataFlow::Node getAPathArgument() { result = super.getAPathArgument() } +} + +/** Provides a class for modeling new file system access APIs. */ +module FileSystemAccess { + /** + * A data-flow node that performs a file system access, including reading and writing data, + * creating and deleting files and folders, checking and updating permissions, and so on. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `FileSystemAccess` instead. + */ + abstract class Range extends DataFlow::Node { + /** Gets an argument to this file system access that is interpreted as a path. */ + abstract DataFlow::Node getAPathArgument(); + } +} + +/** + * A data flow node that reads data from the file system. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `FileSystemReadAccess::Range` instead. + */ +class FileSystemReadAccess extends FileSystemAccess instanceof FileSystemReadAccess::Range { + /** + * Gets a node that represents data read from the file system access. + */ + DataFlow::Node getADataNode() { result = FileSystemReadAccess::Range.super.getADataNode() } +} + +/** Provides a class for modeling new file system reads. */ +module FileSystemReadAccess { + /** + * A data flow node that reads data from the file system. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `FileSystemReadAccess` instead. + */ + abstract class Range extends FileSystemAccess::Range { + /** + * Gets a node that represents data read from the file system. + */ + abstract DataFlow::Node getADataNode(); + } +} + +/** + * A data flow node that sets the permissions for one or more files. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `FileSystemPermissionModification::Range` instead. + */ +class FileSystemPermissionModification extends DataFlow::Node instanceof FileSystemPermissionModification::Range { + /** + * Gets an argument to this permission modification that is interpreted as a + * set of permissions. + */ + DataFlow::Node getAPermissionNode() { result = super.getAPermissionNode() } +} + +/** Provides a class for modeling new file system permission modifications. */ +module FileSystemPermissionModification { + /** + * A data-flow node that sets permissions for a one or more files. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `FileSystemPermissionModification` instead. + */ + abstract class Range extends DataFlow::Node { + /** + * Gets an argument to this permission modification that is interpreted as a + * set of permissions. + */ + abstract DataFlow::Node getAPermissionNode(); + } +} + +/** + * A data flow node that contains a file name or an array of file names from the local file system. + */ +abstract class FileNameSource extends DataFlow::Node { } + +/** + * A data-flow node that escapes meta-characters, which could be used to prevent + * injection attacks. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `Escaping::Range` instead. + */ +class Escaping extends DataFlow::Node instanceof Escaping::Range { + Escaping() { + // escapes that don't have _both_ input/output defined are not valid + exists(super.getAnInput()) and + exists(super.getOutput()) + } + + /** Gets an input that will be escaped. */ + DataFlow::Node getAnInput() { result = super.getAnInput() } + + /** Gets the output that contains the escaped data. */ + DataFlow::Node getOutput() { result = super.getOutput() } + + /** + * Gets the context that this function escapes for, such as `html`, or `url`. + */ + string getKind() { result = super.getKind() } +} + +/** Provides a class for modeling new escaping APIs. */ +module Escaping { + /** + * A data-flow node that escapes meta-characters, which could be used to prevent + * injection attacks. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `Escaping` instead. + */ + abstract class Range extends DataFlow::Node { + /** Gets an input that will be escaped. */ + abstract DataFlow::Node getAnInput(); + + /** Gets the output that contains the escaped data. */ + abstract DataFlow::Node getOutput(); + + /** + * Gets the context that this function escapes for. + * + * While kinds are represented as strings, this should not be relied upon. Use the + * predicates in the `Escaping` module, such as `getHtmlKind`. + */ + abstract string getKind(); + } + + /** Gets the escape-kind for escaping a string so it can safely be included in HTML. */ + string getHtmlKind() { result = "html" } +} + +/** + * An escape of a string so it can be safely included in + * the body of an HTML element, for example, replacing `{}` in + * `<p>{}</p>`. + */ +class HtmlEscaping extends Escaping { + HtmlEscaping() { super.getKind() = Escaping::getHtmlKind() } +} + +/** Provides classes for modeling HTTP-related APIs. */ +module HTTP { + /** Provides classes for modeling HTTP servers. */ + module Server { + /** + * A data-flow node that sets up a route on a server. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `RouteSetup::Range` instead. + */ + class RouteSetup extends DataFlow::Node instanceof RouteSetup::Range { + /** Gets the URL pattern for this route, if it can be statically determined. */ + string getUrlPattern() { result = super.getUrlPattern() } + + /** + * Gets a function that will handle incoming requests for this route, if any. + * + * NOTE: This will be modified in the near future to have a `RequestHandler` result, instead of a `Method`. + */ + Method getARequestHandler() { result = super.getARequestHandler() } + + /** + * Gets a parameter that will receive parts of the url when handling incoming + * requests for this route, if any. These automatically become a `RemoteFlowSource`. + */ + Parameter getARoutedParameter() { result = super.getARoutedParameter() } + + /** Gets a string that identifies the framework used for this route setup. */ + string getFramework() { result = super.getFramework() } + } + + /** Provides a class for modeling new HTTP routing APIs. */ + module RouteSetup { + /** + * A data-flow node that sets up a route on a server. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `RouteSetup` instead. + */ + abstract class Range extends DataFlow::Node { + /** Gets the argument used to set the URL pattern. */ + abstract DataFlow::Node getUrlPatternArg(); + + /** Gets the URL pattern for this route, if it can be statically determined. */ + string getUrlPattern() { + exists(CfgNodes::ExprNodes::StringlikeLiteralCfgNode strNode | + this.getUrlPatternArg().getALocalSource() = DataFlow::exprNode(strNode) and + result = strNode.getExpr().getValueText() + ) + } + + /** + * Gets a function that will handle incoming requests for this route, if any. + * + * NOTE: This will be modified in the near future to have a `RequestHandler` result, instead of a `Method`. + */ + abstract Method getARequestHandler(); + + /** + * Gets a parameter that will receive parts of the url when handling incoming + * requests for this route, if any. These automatically become a `RemoteFlowSource`. + */ + abstract Parameter getARoutedParameter(); + + /** Gets a string that identifies the framework used for this route setup. */ + abstract string getFramework(); + } + } + + /** + * A function that will handle incoming HTTP requests. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `RequestHandler::Range` instead. + */ + class RequestHandler extends Method instanceof RequestHandler::Range { + /** + * Gets a parameter that could receive parts of the url when handling incoming + * requests, if any. These automatically become a `RemoteFlowSource`. + */ + Parameter getARoutedParameter() { result = super.getARoutedParameter() } + + /** Gets a string that identifies the framework used for this route setup. */ + string getFramework() { result = super.getFramework() } + } + + /** Provides a class for modeling new HTTP request handlers. */ + module RequestHandler { + /** + * A function that will handle incoming HTTP requests. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `RequestHandler` instead. + * + * Only extend this class if you can't provide a `RouteSetup`, since we handle that case automatically. + */ + abstract class Range extends Method { + /** + * Gets a parameter that could receive parts of the url when handling incoming + * requests, if any. These automatically become a `RemoteFlowSource`. + */ + abstract Parameter getARoutedParameter(); + + /** Gets a string that identifies the framework used for this request handler. */ + abstract string getFramework(); + } + } + + private class RequestHandlerFromRouteSetup extends RequestHandler::Range { + RouteSetup rs; + + RequestHandlerFromRouteSetup() { this = rs.getARequestHandler() } + + override Parameter getARoutedParameter() { + result = rs.getARoutedParameter() and + result = this.getAParameter() + } + + override string getFramework() { result = rs.getFramework() } + } + + /** A parameter that will receive parts of the url when handling an incoming request. */ + private class RoutedParameter extends RemoteFlowSource::Range, DataFlow::ParameterNode { + RequestHandler handler; + + RoutedParameter() { this.getParameter() = handler.getARoutedParameter() } + + override string getSourceType() { result = handler.getFramework() + " RoutedParameter" } + } + + /** + * A data-flow node that creates a HTTP response on a server. + * + * Note: we don't require that this response must be sent to a client (a kind of + * "if a tree falls in a forest and nobody hears it" situation). + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `HttpResponse::Range` instead. + */ + class HttpResponse extends DataFlow::Node instanceof HttpResponse::Range { + /** Gets the data-flow node that specifies the body of this HTTP response. */ + DataFlow::Node getBody() { result = super.getBody() } + + /** Gets the mimetype of this HTTP response, if it can be statically determined. */ + string getMimetype() { result = super.getMimetype() } + } + + /** Provides a class for modeling new HTTP response APIs. */ + module HttpResponse { + /** + * A data-flow node that creates a HTTP response on a server. + * + * Note: we don't require that this response must be sent to a client (a kind of + * "if a tree falls in a forest and nobody hears it" situation). + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `HttpResponse` instead. + */ + abstract class Range extends DataFlow::Node { + /** Gets the data-flow node that specifies the body of this HTTP response. */ + abstract DataFlow::Node getBody(); + + /** Gets the data-flow node that specifies the content-type/mimetype of this HTTP response, if any. */ + abstract DataFlow::Node getMimetypeOrContentTypeArg(); + + /** Gets the default mimetype that should be used if `getMimetypeOrContentTypeArg` has no results. */ + abstract string getMimetypeDefault(); + + /** Gets the mimetype of this HTTP response, if it can be statically determined. */ + string getMimetype() { + exists(CfgNodes::ExprNodes::StringlikeLiteralCfgNode strNode | + this.getMimetypeOrContentTypeArg().getALocalSource() = DataFlow::exprNode(strNode) and + result = strNode.getExpr().getValueText().splitAt(";", 0) + ) + or + not exists(this.getMimetypeOrContentTypeArg()) and + result = this.getMimetypeDefault() + } + } + } + + /** + * A data-flow node that creates a HTTP redirect response on a server. + * + * Note: we don't require that this redirect must be sent to a client (a kind of + * "if a tree falls in a forest and nobody hears it" situation). + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `HttpRedirectResponse::Range` instead. + */ + class HttpRedirectResponse extends HttpResponse instanceof HttpRedirectResponse::Range { + /** Gets the data-flow node that specifies the location of this HTTP redirect response. */ + DataFlow::Node getRedirectLocation() { result = super.getRedirectLocation() } + } + + /** Provides a class for modeling new HTTP redirect response APIs. */ + module HttpRedirectResponse { + /** + * A data-flow node that creates a HTTP redirect response on a server. + * + * Note: we don't require that this redirect must be sent to a client (a kind of + * "if a tree falls in a forest and nobody hears it" situation). + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `HttpResponse` instead. + */ + abstract class Range extends HTTP::Server::HttpResponse::Range { + /** Gets the data-flow node that specifies the location of this HTTP redirect response. */ + abstract DataFlow::Node getRedirectLocation(); + } + } + } + + /** Provides classes for modeling HTTP clients. */ + module Client { + /** + * A method call that makes an outgoing HTTP request. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `Request::Range` instead. + */ + class Request extends MethodCall instanceof Request::Range { + /** Gets a node which returns the body of the response */ + DataFlow::Node getResponseBody() { result = super.getResponseBody() } + + /** Gets a string that identifies the framework used for this request. */ + string getFramework() { result = super.getFramework() } + + /** + * Holds if this request is made using a mode that disables SSL/TLS + * certificate validation, where `disablingNode` represents the point at + * which the validation was disabled. + */ + predicate disablesCertificateValidation(DataFlow::Node disablingNode) { + super.disablesCertificateValidation(disablingNode) + } + } + + /** Provides a class for modeling new HTTP requests. */ + module Request { + /** + * A method call that makes an outgoing HTTP request. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `Request` instead. + */ + abstract class Range extends MethodCall { + /** Gets a node which returns the body of the response */ + abstract DataFlow::Node getResponseBody(); + + /** Gets a string that identifies the framework used for this request. */ + abstract string getFramework(); + + /** + * Holds if this request is made using a mode that disables SSL/TLS + * certificate validation, where `disablingNode` represents the point at + * which the validation was disabled. + */ + abstract predicate disablesCertificateValidation(DataFlow::Node disablingNode); + } + } + + /** The response body from an outgoing HTTP request, considered as a remote flow source */ + private class RequestResponseBody extends RemoteFlowSource::Range, DataFlow::Node { + Request request; + + RequestResponseBody() { this = request.getResponseBody() } + + override string getSourceType() { result = request.getFramework() } + } + } +} + +/** + * A data flow node that executes an operating system command, + * for instance by spawning a new process. + */ +class SystemCommandExecution extends DataFlow::Node instanceof SystemCommandExecution::Range { + /** Holds if a shell interprets `arg`. */ + predicate isShellInterpreted(DataFlow::Node arg) { super.isShellInterpreted(arg) } + + /** Gets an argument to this execution that specifies the command or an argument to it. */ + DataFlow::Node getAnArgument() { result = super.getAnArgument() } +} + +/** Provides a class for modeling new operating system command APIs. */ +module SystemCommandExecution { + /** + * A data flow node that executes an operating system command, for instance by spawning a new + * process. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `SystemCommandExecution` instead. + */ + abstract class Range extends DataFlow::Node { + /** Gets an argument to this execution that specifies the command or an argument to it. */ + abstract DataFlow::Node getAnArgument(); + + /** Holds if a shell interprets `arg`. */ + predicate isShellInterpreted(DataFlow::Node arg) { none() } + } +} + +/** + * A data-flow node that dynamically executes Ruby code. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `CodeExecution::Range` instead. + */ +class CodeExecution extends DataFlow::Node instanceof CodeExecution::Range { + /** Gets the argument that specifies the code to be executed. */ + DataFlow::Node getCode() { result = super.getCode() } +} + +/** Provides a class for modeling new dynamic code execution APIs. */ +module CodeExecution { + /** + * A data-flow node that dynamically executes Ruby code. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `CodeExecution` instead. + */ + abstract class Range extends DataFlow::Node { + /** Gets the argument that specifies the code to be executed. */ + abstract DataFlow::Node getCode(); + } +} + +/** + * A data-flow node that parses XML content. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `XmlParserCall::Range` instead. + */ +class XmlParserCall extends DataFlow::Node { + XmlParserCall::Range range; + + XmlParserCall() { this = range } + + /** Gets the argument that specifies the XML content to be parsed. */ + DataFlow::Node getInput() { result = range.getInput() } + + /** Holds if this XML parser call is configured to process external entities */ + predicate externalEntitiesEnabled() { range.externalEntitiesEnabled() } +} + +/** Provides a class for modeling new XML parsing APIs. */ +module XmlParserCall { + /** + * A data-flow node that parses XML content. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `class XmlParserCall` instead. + */ + abstract class Range extends DataFlow::Node { + /** Gets the argument that specifies the XML content to be parsed. */ + abstract DataFlow::Node getInput(); + + /** Holds if this XML parser call is configured to process external entities */ + abstract predicate externalEntitiesEnabled(); + } +} + +/** + * A data-flow node that may represent a database object in an ORM system. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `OrmInstantiation::Range` instead. + */ +class OrmInstantiation extends DataFlow::Node instanceof OrmInstantiation::Range { + /** Holds if a call to `methodName` on this instance may return a field of this ORM object. */ + bindingset[methodName] + predicate methodCallMayAccessField(string methodName) { + super.methodCallMayAccessField(methodName) + } +} + +/** Provides a class for modeling new ORM object instantiation APIs. */ +module OrmInstantiation { + /** + * A data-flow node that may represent a database object in an ORM system. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `OrmInstantiation` instead. + */ + abstract class Range extends DataFlow::Node { + /** Holds if a call to `methodName` on this instance may return a field of this ORM object. */ + bindingset[methodName] + abstract predicate methodCallMayAccessField(string methodName); + } +} + +/** Provides classes for modeling path-related APIs. */ +module Path { + /** + * A data-flow node that performs path sanitization. This is often needed in order + * to safely access paths. + */ + class PathSanitization extends DataFlow::Node instanceof PathSanitization::Range { } + + /** Provides a class for modeling new path sanitization APIs. */ + module PathSanitization { + /** + * A data-flow node that performs path sanitization. This is often needed in order + * to safely access paths. + */ + abstract class Range extends DataFlow::Node { } + } +} diff --git a/ruby/ql/lib/codeql/ruby/DataFlow.qll b/ruby/ql/lib/codeql/ruby/DataFlow.qll new file mode 100644 index 00000000000..e7645ce0c10 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/DataFlow.qll @@ -0,0 +1,7 @@ +/** + * Provides classes for performing local (intra-procedural) and + * global (inter-procedural) data flow analyses. + */ +module DataFlow { + import codeql.ruby.dataflow.internal.DataFlowImpl +} diff --git a/ruby/ql/lib/codeql/ruby/DataFlow2.qll b/ruby/ql/lib/codeql/ruby/DataFlow2.qll new file mode 100644 index 00000000000..7486f52052d --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/DataFlow2.qll @@ -0,0 +1,7 @@ +/** + * Provides classes for performing local (intra-procedural) and + * global (inter-procedural) data flow analyses. + */ +module DataFlow2 { + import codeql.ruby.dataflow.internal.DataFlowImpl2 +} diff --git a/ruby/ql/lib/codeql/ruby/Diagnostics.qll b/ruby/ql/lib/codeql/ruby/Diagnostics.qll new file mode 100644 index 00000000000..b8995c01bc2 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/Diagnostics.qll @@ -0,0 +1,52 @@ +private import codeql.Locations + +/** A diagnostic emitted during extraction, such as a parse error */ +class Diagnostic extends @diagnostic { + int severity; + string tag; + string message; + string fullMessage; + Location location; + + Diagnostic() { diagnostics(this, severity, tag, message, fullMessage, location) } + + /** + * Gets the numerical severity level associated with this diagnostic. + */ + int getSeverity() { result = severity } + + /** Gets a string representation of the severity of this diagnostic. */ + string getSeverityText() { + severity = 10 and result = "Debug" + or + severity = 20 and result = "Info" + or + severity = 30 and result = "Warning" + or + severity = 40 and result = "Error" + } + + /** Gets the error code associated with this diagnostic, e.g. parse_error. */ + string getTag() { result = tag } + + /** + * Gets the error message text associated with this diagnostic. + */ + string getMessage() { result = message } + + /** + * Gets the full error message text associated with this diagnostic. + */ + string getFullMessage() { result = fullMessage } + + /** Gets the source location of this diagnostic. */ + Location getLocation() { result = location } + + /** Gets a textual representation of this diagnostic. */ + string toString() { result = this.getMessage() } +} + +/** A diagnostic relating to a particular error in extracting a file. */ +class ExtractionError extends Diagnostic, @diagnostic_error { + ExtractionError() { this.getTag() = "parse_error" } +} diff --git a/ruby/ql/lib/codeql/ruby/Frameworks.qll b/ruby/ql/lib/codeql/ruby/Frameworks.qll new file mode 100644 index 00000000000..36f7c0de25b --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/Frameworks.qll @@ -0,0 +1,12 @@ +/** + * Helper file that imports all framework modeling. + */ + +private import codeql.ruby.frameworks.ActionController +private import codeql.ruby.frameworks.ActiveRecord +private import codeql.ruby.frameworks.ActiveStorage +private import codeql.ruby.frameworks.ActionView +private import codeql.ruby.frameworks.StandardLibrary +private import codeql.ruby.frameworks.Files +private import codeql.ruby.frameworks.HttpClients +private import codeql.ruby.frameworks.XmlParsing diff --git a/ruby/ql/lib/codeql/ruby/TaintTracking.qll b/ruby/ql/lib/codeql/ruby/TaintTracking.qll new file mode 100755 index 00000000000..e443b294273 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/TaintTracking.qll @@ -0,0 +1,7 @@ +/** + * Provides classes for performing local (intra-procedural) and + * global (inter-procedural) taint-tracking analyses. + */ +module TaintTracking { + import codeql.ruby.dataflow.internal.tainttracking1.TaintTrackingImpl +} diff --git a/ruby/ql/lib/codeql/ruby/ast/Call.qll b/ruby/ql/lib/codeql/ruby/ast/Call.qll new file mode 100644 index 00000000000..d34034f14cd --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/Call.qll @@ -0,0 +1,215 @@ +private import codeql.ruby.AST +private import internal.AST +private import internal.Call +private import internal.TreeSitter +private import codeql.ruby.dataflow.internal.DataFlowDispatch +private import codeql.ruby.dataflow.internal.DataFlowImplCommon + +/** + * A call. + */ +class Call extends Expr instanceof CallImpl { + override string getAPrimaryQlClass() { result = "Call" } + + /** + * Gets the `n`th argument of this method call. In the following example, the + * result for n=0 is the `IntegerLiteral` 0, while for n=1 the result is a + * `Pair` (whose `getKey` returns the `SymbolLiteral` for `bar`, and + * `getValue` returns the `IntegerLiteral` 1). Keyword arguments like this + * can be accessed more naturally using the + * `getKeywordArgument(string keyword)` predicate. + * ```rb + * foo(0, bar: 1) + * yield 0, bar: 1 + * ``` + */ + final Expr getArgument(int n) { result = super.getArgumentImpl(n) } + + /** + * Gets an argument of this method call. + */ + final Expr getAnArgument() { result = this.getArgument(_) } + + /** + * Gets the value of the keyword argument whose key is `keyword`, if any. For + * example, the result for `getKeywordArgument("qux")` in the following + * example is the `IntegerLiteral` 123. + * ```rb + * foo :bar "baz", qux: 123 + * ``` + */ + final Expr getKeywordArgument(string keyword) { + exists(Pair p | + p = this.getAnArgument() and + p.getKey().(SymbolLiteral).getValueText() = keyword and + result = p.getValue() + ) + } + + /** + * Gets the number of arguments of this method call. + */ + final int getNumberOfArguments() { result = super.getNumberOfArgumentsImpl() } + + /** Gets a potential target of this call, if any. */ + final Callable getATarget() { + exists(DataFlowCall c | this = c.asCall().getExpr() | + TCfgScope(result) = [viableCallable(c), viableCallableLambda(c, _)] + ) + } + + override AstNode getAChild(string pred) { + result = Expr.super.getAChild(pred) + or + pred = "getArgument" and result = this.getArgument(_) + } +} + +/** + * A method call. + */ +class MethodCall extends Call instanceof MethodCallImpl { + override string getAPrimaryQlClass() { result = "MethodCall" } + + /** + * Gets the receiver of this call, if any. For example: + * + * ```rb + * foo.bar + * Baz::qux + * corge() + * ``` + * + * The result for the call to `bar` is the `Expr` for `foo`; the result for + * the call to `qux` is the `Expr` for `Baz`; for the call to `corge` there + * is no result. + */ + final Expr getReceiver() { result = super.getReceiverImpl() } + + /** + * Gets the name of the method being called. For example, in: + * + * ```rb + * foo.bar x, y + * ``` + * + * the result is `"bar"`. + */ + final string getMethodName() { result = super.getMethodNameImpl() } + + /** + * Gets the block of this method call, if any. + * ```rb + * foo.each { |x| puts x } + * ``` + */ + final Block getBlock() { result = super.getBlockImpl() } + + override string toString() { result = "call to " + this.getMethodName() } + + override AstNode getAChild(string pred) { + result = Call.super.getAChild(pred) + or + pred = "getReceiver" and result = this.getReceiver() + or + pred = "getBlock" and result = this.getBlock() + } +} + +/** + * A call to a setter method. + * ```rb + * self.foo = 10 + * a[0] = 10 + * ``` + */ +class SetterMethodCall extends MethodCall, TMethodCallSynth { + SetterMethodCall() { this = TMethodCallSynth(_, _, _, true, _) } + + final override string getAPrimaryQlClass() { result = "SetterMethodCall" } +} + +/** + * An element reference; a call to the `[]` method. + * ```rb + * a[0] + * ``` + */ +class ElementReference extends MethodCall instanceof ElementReferenceImpl { + final override string getAPrimaryQlClass() { result = "ElementReference" } + + final override string toString() { result = "...[...]" } +} + +/** + * A call to `yield`. + * ```rb + * yield x, y + * ``` + */ +class YieldCall extends Call instanceof YieldCallImpl { + final override string getAPrimaryQlClass() { result = "YieldCall" } + + final override string toString() { result = "yield ..." } +} + +/** + * A call to `super`. + * ```rb + * class Foo < Bar + * def baz + * super + * end + * end + * ``` + */ +class SuperCall extends MethodCall instanceof SuperCallImpl { + final override string getAPrimaryQlClass() { result = "SuperCall" } +} + +/** + * A block argument in a method call. + * ```rb + * foo(&block) + * ``` + */ +class BlockArgument extends Expr, TBlockArgument { + private Ruby::BlockArgument g; + + BlockArgument() { this = TBlockArgument(g) } + + final override string getAPrimaryQlClass() { result = "BlockArgument" } + + /** + * Gets the underlying expression representing the block. In the following + * example, the result is the `Expr` for `bar`: + * ```rb + * foo(&bar) + * ``` + */ + final Expr getValue() { toGenerated(result) = g.getChild() } + + final override string toString() { result = "&..." } + + final override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getValue" and result = this.getValue() + } +} + +/** + * A `...` expression that contains forwarded arguments. + * ```rb + * foo(...) + * ``` + */ +class ForwardedArguments extends Expr, TForwardArgument { + private Ruby::ForwardArgument g; + + ForwardedArguments() { this = TForwardArgument(g) } + + final override string getAPrimaryQlClass() { result = "ForwardedArguments" } + + final override string toString() { result = "..." } +} diff --git a/ruby/ql/lib/codeql/ruby/ast/Constant.qll b/ruby/ql/lib/codeql/ruby/ast/Constant.qll new file mode 100644 index 00000000000..11683d694b7 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/Constant.qll @@ -0,0 +1,210 @@ +private import codeql.ruby.AST +private import internal.AST +private import internal.Module +private import internal.Variable +private import internal.TreeSitter + +/** An access to a constant. */ +class ConstantAccess extends Expr, TConstantAccess { + /** Gets the name of the constant being accessed. */ + string getName() { none() } + + /** Holds if the name of the constant being accessed is `name`. */ + final predicate hasName(string name) { this.getName() = name } + + /** + * Gets the expression used in the access's scope resolution operation, if + * any. In the following example, the result is the `Call` expression for + * `foo()`. + * + * ```rb + * foo()::MESSAGE + * ``` + * + * However, there is no result for the following example, since there is no + * scope resolution operation. + * + * ```rb + * MESSAGE + * ``` + */ + Expr getScopeExpr() { none() } + + /** + * Holds if the access uses the scope resolution operator to refer to the + * global scope, as in this example: + * + * ```rb + * ::MESSAGE + * ``` + */ + predicate hasGlobalScope() { none() } + + override string toString() { result = this.getName() } + + override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getScopeExpr" and result = this.getScopeExpr() + } +} + +private class TokenConstantAccess extends ConstantAccess, TTokenConstantAccess { + private Ruby::Constant g; + + TokenConstantAccess() { this = TTokenConstantAccess(g) } + + final override string getName() { result = g.getValue() } +} + +private class ScopeResolutionConstantAccess extends ConstantAccess, TScopeResolutionConstantAccess { + private Ruby::ScopeResolution g; + private Ruby::Constant constant; + + ScopeResolutionConstantAccess() { this = TScopeResolutionConstantAccess(g, constant) } + + final override string getName() { result = constant.getValue() } + + final override Expr getScopeExpr() { toGenerated(result) = g.getScope() } + + final override predicate hasGlobalScope() { not exists(g.getScope()) } +} + +private class ConstantReadAccessSynth extends ConstantAccess, TConstantReadAccessSynth { + private string value; + + ConstantReadAccessSynth() { this = TConstantReadAccessSynth(_, _, value) } + + final override string getName() { + if this.hasGlobalScope() then result = value.suffix(2) else result = value + } + + final override Expr getScopeExpr() { synthChild(this, 0, result) } + + final override predicate hasGlobalScope() { value.matches("::%") } +} + +/** + * A use (read) of a constant. + * + * For example, the right-hand side of the assignment in: + * + * ```rb + * x = Foo + * ``` + * + * Or the superclass `Bar` in this example: + * + * ```rb + * class Foo < Bar + * end + * ``` + */ +class ConstantReadAccess extends ConstantAccess { + ConstantReadAccess() { + not this instanceof ConstantWriteAccess + or + // `X` in `X ||= 10` is considered both a read and a write + this = any(AssignOperation a).getLeftOperand() + or + this instanceof TConstantReadAccessSynth + } + + /** + * Gets the value being read, if any. For example, in + * + * ```rb + * module M + * CONST = "const" + * end + * + * puts M::CONST + * ``` + * + * the value being read at `M::CONST` is `"const"`. + */ + Expr getValue() { + not exists(this.getScopeExpr()) and + result = lookupConst(this.getEnclosingModule+().getModule(), this.getName()) and + // For now, we restrict the scope of top-level declarations to their file. + // This may remove some plausible targets, but also removes a lot of + // implausible targets + if result.getEnclosingModule() instanceof Toplevel + then result.getFile() = this.getFile() + else any() + or + this.hasGlobalScope() and + result = lookupConst(TResolved("Object"), this.getName()) + or + result = lookupConst(resolveScopeExpr(this.getScopeExpr()), this.getName()) + } + + override string getValueText() { result = this.getValue().getValueText() } + + final override string getAPrimaryQlClass() { result = "ConstantReadAccess" } +} + +/** + * A definition of a constant. + * + * Examples: + * + * ```rb + * Foo = 1 # defines constant Foo as an integer + * M::Foo = 1 # defines constant Foo as an integer in module M + * + * class Bar; end # defines constant Bar as a class + * class M::Bar; end # defines constant Bar as a class in module M + * + * module Baz; end # defines constant Baz as a module + * module M::Baz; end # defines constant Baz as a module in module M + * ``` + */ +class ConstantWriteAccess extends ConstantAccess { + ConstantWriteAccess() { + explicitAssignmentNode(toGenerated(this), _) or this instanceof TNamespace + } + + override string getAPrimaryQlClass() { result = "ConstantWriteAccess" } + + /** + * Gets the fully qualified name for this constant, based on the context in + * which it is defined. + * + * For example, given + * ```rb + * module Foo + * module Bar + * class Baz + * end + * end + * CONST_A = "a" + * end + * ``` + * + * the constant `Baz` has the fully qualified name `Foo::Bar::Baz`, and + * `CONST_A` has the fully qualified name `Foo::CONST_A`. + */ + string getQualifiedName() { + /* get the qualified name for the parent module, then append w */ + exists(ConstantWriteAccess parent | parent = this.getEnclosingModule() | + result = parent.getQualifiedName() + "::" + this.getName() + ) + or + /* base case - there's no parent module */ + not exists(ConstantWriteAccess parent | parent = this.getEnclosingModule()) and + result = this.getName() + } +} + +/** + * A definition of a constant via assignment. For example, the left-hand + * operand in the following example: + * + * ```rb + * MAX_SIZE = 100 + * ``` + */ +class ConstantAssignment extends ConstantWriteAccess, LhsExpr { + override string getAPrimaryQlClass() { result = "ConstantAssignment" } +} diff --git a/ruby/ql/lib/codeql/ruby/ast/Control.qll b/ruby/ql/lib/codeql/ruby/ast/Control.qll new file mode 100644 index 00000000000..d209e2861c0 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/Control.qll @@ -0,0 +1,611 @@ +private import codeql.ruby.AST +private import internal.AST +private import internal.TreeSitter + +/** + * A control expression that can be any of the following: + * - `case` + * - `if`/`unless` (including expression-modifier variants) + * - ternary-if (`?:`) + * - `while`/`until` (including expression-modifier variants) + * - `for` + */ +class ControlExpr extends Expr, TControlExpr { } + +/** + * A conditional expression: `if`/`unless` (including expression-modifier + * variants), and ternary-if (`?:`) expressions. + */ +class ConditionalExpr extends ControlExpr, TConditionalExpr { + /** + * Gets the condition expression. For example, the result is `foo` in the + * following: + * ```rb + * if foo + * bar = 1 + * end + * ``` + */ + Expr getCondition() { none() } + + /** + * Gets the branch of this conditional expression that is taken when the + * condition evaluates to `cond`, if any. + */ + Stmt getBranch(boolean cond) { none() } + + override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getCondition" and result = this.getCondition() + or + pred = "getBranch" and result = this.getBranch(_) + } +} + +/** + * An `if` or `elsif` expression. + * ```rb + * if x + * a += 1 + * elsif y + * a += 2 + * end + * ``` + */ +class IfExpr extends ConditionalExpr, TIfExpr { + final override string getAPrimaryQlClass() { result = "IfExpr" } + + /** Holds if this is an `elsif` expression. */ + predicate isElsif() { none() } + + /** Gets the 'then' branch of this `if`/`elsif` expression. */ + Stmt getThen() { none() } + + /** + * Gets the `elsif`/`else` branch of this `if`/`elsif` expression, if any. In + * the following example, the result is a `StmtSequence` containing `b`. + * ```rb + * if foo + * a + * else + * b + * end + * ``` + * But there is no result for the following: + * ```rb + * if foo + * a + * end + * ``` + * There can be at most one result, since `elsif` branches nest. In the + * following example, `ifExpr.getElse()` returns an `ElsifExpr`, and the + * `else` branch is nested inside that. To get the `StmtSequence` for the + * `else` branch, i.e. the one containing `c`, use + * `getElse().(ElsifExpr).getElse()`. + * ```rb + * if foo + * a + * elsif bar + * b + * else + * c + * end + * ``` + */ + Stmt getElse() { none() } + + final override Stmt getBranch(boolean cond) { + cond = true and result = this.getThen() + or + cond = false and result = this.getElse() + } + + override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getThen" and result = this.getThen() + or + pred = "getElse" and result = this.getElse() + } +} + +private class If extends IfExpr, TIf { + private Ruby::If g; + + If() { this = TIf(g) } + + final override Expr getCondition() { toGenerated(result) = g.getCondition() } + + final override Stmt getThen() { toGenerated(result) = g.getConsequence() } + + final override Stmt getElse() { toGenerated(result) = g.getAlternative() } + + final override string toString() { result = "if ..." } +} + +private class Elsif extends IfExpr, TElsif { + private Ruby::Elsif g; + + Elsif() { this = TElsif(g) } + + final override predicate isElsif() { any() } + + final override Expr getCondition() { toGenerated(result) = g.getCondition() } + + final override Stmt getThen() { toGenerated(result) = g.getConsequence() } + + final override Stmt getElse() { toGenerated(result) = g.getAlternative() } + + final override string toString() { result = "elsif ..." } +} + +/** + * An `unless` expression. + * ```rb + * unless x == 0 + * y /= x + * end + * ``` + */ +class UnlessExpr extends ConditionalExpr, TUnlessExpr { + private Ruby::Unless g; + + UnlessExpr() { this = TUnlessExpr(g) } + + final override string getAPrimaryQlClass() { result = "UnlessExpr" } + + final override Expr getCondition() { toGenerated(result) = g.getCondition() } + + /** + * Gets the 'then' branch of this `unless` expression. In the following + * example, the result is the `StmtSequence` containing `foo`. + * ```rb + * unless a == b then + * foo + * else + * bar + * end + * ``` + */ + final Stmt getThen() { toGenerated(result) = g.getConsequence() } + + /** + * Gets the 'else' branch of this `unless` expression. In the following + * example, the result is the `StmtSequence` containing `bar`. + * ```rb + * unless a == b then + * foo + * else + * bar + * end + * ``` + */ + final Stmt getElse() { toGenerated(result) = g.getAlternative() } + + final override Expr getBranch(boolean cond) { + cond = false and result = this.getThen() + or + cond = true and result = this.getElse() + } + + final override string toString() { result = "unless ..." } + + override AstNode getAChild(string pred) { + result = ConditionalExpr.super.getAChild(pred) + or + pred = "getThen" and result = this.getThen() + or + pred = "getElse" and result = this.getElse() + } +} + +/** + * An expression modified using `if`. + * ```rb + * foo if bar + * ``` + */ +class IfModifierExpr extends ConditionalExpr, TIfModifierExpr { + private Ruby::IfModifier g; + + IfModifierExpr() { this = TIfModifierExpr(g) } + + final override string getAPrimaryQlClass() { result = "IfModifierExpr" } + + final override Expr getCondition() { toGenerated(result) = g.getCondition() } + + final override Stmt getBranch(boolean cond) { cond = true and result = this.getBody() } + + /** + * Gets the statement that is conditionally evaluated. In the following + * example, the result is the `Expr` for `foo`. + * ```rb + * foo if bar + * ``` + */ + final Stmt getBody() { toGenerated(result) = g.getBody() } + + final override string toString() { result = "... if ..." } + + override AstNode getAChild(string pred) { + result = ConditionalExpr.super.getAChild(pred) + or + pred = "getBody" and result = this.getBody() + } +} + +/** + * An expression modified using `unless`. + * ```rb + * y /= x unless x == 0 + * ``` + */ +class UnlessModifierExpr extends ConditionalExpr, TUnlessModifierExpr { + private Ruby::UnlessModifier g; + + UnlessModifierExpr() { this = TUnlessModifierExpr(g) } + + final override string getAPrimaryQlClass() { result = "UnlessModifierExpr" } + + final override Expr getCondition() { toGenerated(result) = g.getCondition() } + + final override Stmt getBranch(boolean cond) { cond = false and result = this.getBody() } + + /** + * Gets the statement that is conditionally evaluated. In the following + * example, the result is the `Expr` for `foo`. + * ```rb + * foo unless bar + * ``` + */ + final Stmt getBody() { toGenerated(result) = g.getBody() } + + final override string toString() { result = "... unless ..." } + + override AstNode getAChild(string pred) { + result = ConditionalExpr.super.getAChild(pred) + or + pred = "getBody" and result = this.getBody() + } +} + +/** + * A conditional expression using the ternary (`?:`) operator. + * ```rb + * (a > b) ? a : b + * ``` + */ +class TernaryIfExpr extends ConditionalExpr, TTernaryIfExpr { + private Ruby::Conditional g; + + TernaryIfExpr() { this = TTernaryIfExpr(g) } + + final override string getAPrimaryQlClass() { result = "TernaryIfExpr" } + + final override Expr getCondition() { toGenerated(result) = g.getCondition() } + + /** Gets the 'then' branch of this ternary if expression. */ + final Stmt getThen() { toGenerated(result) = g.getConsequence() } + + /** Gets the 'else' branch of this ternary if expression. */ + final Stmt getElse() { toGenerated(result) = g.getAlternative() } + + final override Stmt getBranch(boolean cond) { + cond = true and result = this.getThen() + or + cond = false and result = this.getElse() + } + + final override string toString() { result = "... ? ... : ..." } + + override AstNode getAChild(string pred) { + result = ConditionalExpr.super.getAChild(pred) + or + pred = "getThen" and result = this.getThen() + or + pred = "getElse" and result = this.getElse() + } +} + +class CaseExpr extends ControlExpr, TCaseExpr { + private Ruby::Case g; + + CaseExpr() { this = TCaseExpr(g) } + + final override string getAPrimaryQlClass() { result = "CaseExpr" } + + /** + * Gets the expression being compared, if any. For example, `foo` in the following example. + * ```rb + * case foo + * when 0 + * puts 'zero' + * when 1 + * puts 'one' + * end + * ``` + * There is no result for the following example: + * ```rb + * case + * when a then 0 + * when b then 1 + * else 2 + * end + * ``` + */ + final Expr getValue() { toGenerated(result) = g.getValue() } + + /** + * Gets the `n`th branch of this case expression, either a `WhenExpr` or a + * `StmtSequence`. + */ + final Expr getBranch(int n) { toGenerated(result) = g.getChild(n) } + + /** + * Gets a branch of this case expression, either a `WhenExpr` or an + * `ElseExpr`. + */ + final Expr getABranch() { result = this.getBranch(_) } + + /** Gets a `when` branch of this case expression. */ + final WhenExpr getAWhenBranch() { result = this.getABranch() } + + /** Gets the `else` branch of this case expression, if any. */ + final StmtSequence getElseBranch() { result = this.getABranch() } + + /** + * Gets the number of branches of this case expression. + */ + final int getNumberOfBranches() { result = count(this.getBranch(_)) } + + final override string toString() { result = "case ..." } + + override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getValue" and result = this.getValue() + or + pred = "getBranch" and result = this.getBranch(_) + } +} + +/** + * A `when` branch of a `case` expression. + * ```rb + * case + * when a > b then x + * end + * ``` + */ +class WhenExpr extends Expr, TWhenExpr { + private Ruby::When g; + + WhenExpr() { this = TWhenExpr(g) } + + final override string getAPrimaryQlClass() { result = "WhenExpr" } + + /** Gets the body of this case-when expression. */ + final Stmt getBody() { toGenerated(result) = g.getBody() } + + /** + * Gets the `n`th pattern (or condition) in this case-when expression. In the + * following example, the 0th pattern is `x`, the 1st pattern is `y`, and the + * 2nd pattern is `z`. + * ```rb + * case foo + * when x, y, z + * puts 'x/y/z' + * end + * ``` + */ + final Expr getPattern(int n) { toGenerated(result) = g.getPattern(n).getChild() } + + /** + * Gets a pattern (or condition) in this case-when expression. + */ + final Expr getAPattern() { result = this.getPattern(_) } + + /** + * Gets the number of patterns in this case-when expression. + */ + final int getNumberOfPatterns() { result = count(this.getPattern(_)) } + + final override string toString() { result = "when ..." } + + override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getBody" and result = this.getBody() + or + pred = "getPattern" and result = this.getPattern(_) + } +} + +/** + * A loop. That is, a `for` loop, a `while` or `until` loop, or their + * expression-modifier variants. + */ +class Loop extends ControlExpr, TLoop { + /** Gets the body of this loop. */ + Stmt getBody() { none() } + + override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getBody" and result = this.getBody() + } +} + +/** + * A loop using a condition expression. That is, a `while` or `until` loop, or + * their expression-modifier variants. + */ +class ConditionalLoop extends Loop, TConditionalLoop { + /** Gets the condition expression of this loop. */ + Expr getCondition() { none() } + + override AstNode getAChild(string pred) { + result = Loop.super.getAChild(pred) + or + pred = "getCondition" and result = this.getCondition() + } + + /** Holds if the loop body is entered when the condition is `condValue`. */ + predicate entersLoopWhenConditionIs(boolean condValue) { none() } +} + +/** + * A `while` loop. + * ```rb + * while a < b + * p a + * a += 2 + * end + * ``` + */ +class WhileExpr extends ConditionalLoop, TWhileExpr { + private Ruby::While g; + + WhileExpr() { this = TWhileExpr(g) } + + final override string getAPrimaryQlClass() { result = "WhileExpr" } + + /** Gets the body of this `while` loop. */ + final override Stmt getBody() { toGenerated(result) = g.getBody() } + + final override Expr getCondition() { toGenerated(result) = g.getCondition() } + + /** + * Holds if the loop body is entered when the condition is `condValue`. For + * `while` loops, this holds when `condValue` is true. + */ + final override predicate entersLoopWhenConditionIs(boolean condValue) { condValue = true } + + final override string toString() { result = "while ..." } +} + +/** + * An `until` loop. + * ```rb + * until a >= b + * p a + * a += 1 + * end + * ``` + */ +class UntilExpr extends ConditionalLoop, TUntilExpr { + private Ruby::Until g; + + UntilExpr() { this = TUntilExpr(g) } + + final override string getAPrimaryQlClass() { result = "UntilExpr" } + + /** Gets the body of this `until` loop. */ + final override Stmt getBody() { toGenerated(result) = g.getBody() } + + final override Expr getCondition() { toGenerated(result) = g.getCondition() } + + /** + * Holds if the loop body is entered when the condition is `condValue`. For + * `until` loops, this holds when `condValue` is false. + */ + final override predicate entersLoopWhenConditionIs(boolean condValue) { condValue = false } + + final override string toString() { result = "until ..." } +} + +/** + * An expression looped using the `while` modifier. + * ```rb + * foo while bar + * ``` + */ +class WhileModifierExpr extends ConditionalLoop, TWhileModifierExpr { + private Ruby::WhileModifier g; + + WhileModifierExpr() { this = TWhileModifierExpr(g) } + + final override Stmt getBody() { toGenerated(result) = g.getBody() } + + final override Expr getCondition() { toGenerated(result) = g.getCondition() } + + /** + * Holds if the loop body is entered when the condition is `condValue`. For + * `while`-modifier loops, this holds when `condValue` is true. + */ + final override predicate entersLoopWhenConditionIs(boolean condValue) { condValue = true } + + final override string getAPrimaryQlClass() { result = "WhileModifierExpr" } + + final override string toString() { result = "... while ..." } +} + +/** + * An expression looped using the `until` modifier. + * ```rb + * foo until bar + * ``` + */ +class UntilModifierExpr extends ConditionalLoop, TUntilModifierExpr { + private Ruby::UntilModifier g; + + UntilModifierExpr() { this = TUntilModifierExpr(g) } + + final override Stmt getBody() { toGenerated(result) = g.getBody() } + + final override Expr getCondition() { toGenerated(result) = g.getCondition() } + + /** + * Holds if the loop body is entered when the condition is `condValue`. For + * `until`-modifier loops, this holds when `condValue` is false. + */ + final override predicate entersLoopWhenConditionIs(boolean condValue) { condValue = false } + + final override string getAPrimaryQlClass() { result = "UntilModifierExpr" } + + final override string toString() { result = "... until ..." } +} + +/** + * A `for` loop. + * ```rb + * for val in 1..n + * sum += val + * end + * ``` + */ +class ForExpr extends Loop, TForExpr { + private Ruby::For g; + + ForExpr() { this = TForExpr(g) } + + final override string getAPrimaryQlClass() { result = "ForExpr" } + + /** Gets the body of this `for` loop. */ + final override Stmt getBody() { toGenerated(result) = g.getBody() } + + /** Gets the pattern representing the iteration argument. */ + final Pattern getPattern() { toGenerated(result) = g.getPattern() } + + /** + * Gets the value being iterated over. In the following example, the result + * is the expression `1..10`: + * ```rb + * for n in 1..10 do + * puts n + * end + * ``` + */ + final Expr getValue() { toGenerated(result) = g.getValue().getChild() } + + final override string toString() { result = "for ... in ..." } + + override AstNode getAChild(string pred) { + result = Loop.super.getAChild(pred) + or + pred = "getPattern" and result = this.getPattern() + or + pred = "getValue" and result = this.getValue() + } +} diff --git a/ruby/ql/lib/codeql/ruby/ast/Erb.qll b/ruby/ql/lib/codeql/ruby/ast/Erb.qll new file mode 100644 index 00000000000..52b14b70aa6 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/Erb.qll @@ -0,0 +1,313 @@ +private import codeql.Locations +private import codeql.ruby.AST +private import internal.Erb +private import internal.TreeSitter + +/** + * A node in the ERB abstract syntax tree. This class is the base class for all + * ERB elements. + */ +class ErbAstNode extends TAstNode { + /** Gets a textual representation of this node. */ + cached + string toString() { none() } + + /** Gets the location of this node. */ + Location getLocation() { result = getLocation(this) } + + /** + * Gets the name of a primary CodeQL class to which this node belongs. + * + * This predicate always has a result. If no primary class can be + * determined, the result is `"???"`. If multiple primary classes match, + * this predicate can have multiple results. + */ + string getAPrimaryQlClass() { result = "???" } +} + +/** + * An ERB template. This can contain multiple directives to be executed when + * the template is compiled. + */ +class ErbTemplate extends TTemplate, ErbAstNode { + private Erb::Template g; + + ErbTemplate() { this = TTemplate(g) } + + override string toString() { result = "erb template" } + + final override string getAPrimaryQlClass() { result = "ErbTemplate" } + + ErbAstNode getAChildNode() { toGenerated(result) = g.getChild(_) } +} + +// Truncate the token string value to 32 char max +bindingset[val] +private string displayToken(string val) { + val.length() <= 32 and result = val + or + val.length() > 32 and result = val.prefix(29) + "..." +} + +/** + * An ERB token. This could be embedded code, a comment, or arbitrary text. + */ +class ErbToken extends TTokenNode, ErbAstNode { + override string toString() { result = displayToken(this.getValue()) } + + /** Gets the string value of this token. */ + string getValue() { exists(Erb::Token g | this = fromGenerated(g) | result = g.getValue()) } + + override string getAPrimaryQlClass() { result = "ErbToken" } +} + +/** + * An ERB token appearing within a comment directive. + */ +class ErbComment extends ErbToken { + private Erb::Comment g; + + ErbComment() { this = TComment(g) } + + override string getValue() { result = g.getValue() } + + final override string getAPrimaryQlClass() { result = "ErbComment" } +} + +/** + * An ERB token appearing within a code directive. This will typically be + * interpreted as Ruby code or a GraphQL query, depending on context. + */ +class ErbCode extends ErbToken { + private Erb::Code g; + + ErbCode() { this = TCode(g) } + + override string getValue() { result = g.getValue() } + + final override string getAPrimaryQlClass() { result = "ErbCode" } +} + +bindingset[line, col] +private predicate locationIncludesPosition(Location loc, int line, int col) { + // position between start and end line, exclusive + line > loc.getStartLine() and + line < loc.getEndLine() + or + // position on start line, multi line location + line = loc.getStartLine() and + not loc.getStartLine() = loc.getEndLine() and + col >= loc.getStartColumn() + or + // position on end line, multi line location + line = loc.getEndLine() and + not loc.getStartLine() = loc.getEndLine() and + col <= loc.getEndColumn() + or + // single line location, position between start and end column + line = loc.getStartLine() and + loc.getStartLine() = loc.getEndLine() and + col >= loc.getStartColumn() and + col <= loc.getEndColumn() +} + +/** A file containing an ERB directive. */ +private class ErbDirectiveFile extends File { + pragma[nomagic] + ErbDirectiveFile() { this = any(ErbDirective dir).getLocation().getFile() } + + /** Gets a statement in this file. */ + pragma[nomagic] + Stmt getAStmt(int startLine, int startColumn) { + exists(Location loc | + result.getLocation() = loc and + loc.getFile() = this and + loc.getStartLine() = startLine and + loc.getStartColumn() = startColumn + ) + } +} + +/** + * A directive in an ERB template. + */ +class ErbDirective extends TDirectiveNode, ErbAstNode { + /** Holds if this directive spans line `line` in the file `file`. */ + pragma[nomagic] + private predicate spans(ErbDirectiveFile file, int line) { + exists(Location loc | + loc = this.getLocation() and + file = loc.getFile() and + line in [loc.getStartLine() .. loc.getEndLine()] + ) + } + + private predicate containsStmtStart(Stmt s) { + // `Toplevel` statements are not contained within individual directives, + // though their start location may appear within a directive location + not s instanceof Toplevel and + exists(ErbDirectiveFile file, int startLine, int startColumn | + this.spans(file, startLine) and + s = file.getAStmt(startLine, startColumn) and + locationIncludesPosition(this.getLocation(), startLine, startColumn) + ) + } + + /** + * Gets a statement that starts in directive that is not a child of any other + * statement starting in this directive. + */ + Stmt getAChildStmt() { + this.containsStmtStart(result) and + not this.containsStmtStart(result.getParent()) + } + + /** + * Gets the last child statement in this directive. + * See `getAChildStmt` for more details. + */ + Stmt getTerminalStmt() { + result = this.getAChildStmt() and + forall(Stmt s | s = this.getAChildStmt() and not s = result | + s.getLocation().strictlyBefore(result.getLocation()) + ) + } + + /** Gets the child token of this directive. */ + ErbToken getToken() { + exists(Erb::Directive g | this = fromGenerated(g) | toGenerated(result) = g.getChild()) + } + + override string toString() { result = "erb directive" } + + override string getAPrimaryQlClass() { result = "ErbDirective" } +} + +/** + * A comment directive in an ERB template. + * ```erb + * <%#= 2 + 2 %> + * <%# for x in xs do %> + * ``` + */ +class ErbCommentDirective extends ErbDirective { + private Erb::CommentDirective g; + + ErbCommentDirective() { this = TCommentDirective(g) } + + override ErbComment getToken() { toGenerated(result) = g.getChild() } + + final override string toString() { result = "<%#" + this.getToken().toString() + "%>" } + + final override string getAPrimaryQlClass() { result = "ErbCommentDirective" } +} + +/** + * A GraphQL directive in an ERB template. + * ```erb + * <%graphql + * fragment Foo on Bar { + * some { + * queryText + * moreProperties + * } + * } + * %> + * ``` + */ +class ErbGraphqlDirective extends ErbDirective { + private Erb::GraphqlDirective g; + + ErbGraphqlDirective() { this = TGraphqlDirective(g) } + + override ErbCode getToken() { toGenerated(result) = g.getChild() } + + final override string toString() { result = "<%graphql" + this.getToken().toString() + "%>" } + + final override string getAPrimaryQlClass() { result = "ErbGraphqlDirective" } +} + +/** + * An output directive in an ERB template. + * ```erb + * <%= + * fragment Foo on Bar { + * some { + * queryText + * moreProperties + * } + * } + * %> + * ``` + */ +class ErbOutputDirective extends ErbDirective { + private Erb::OutputDirective g; + + ErbOutputDirective() { this = TOutputDirective(g) } + + override ErbCode getToken() { toGenerated(result) = g.getChild() } + + final override string toString() { result = "<%=" + this.getToken().toString() + "%>" } + + final override string getAPrimaryQlClass() { result = "ErbOutputDirective" } +} + +/** + * An execution directive in an ERB template. + * This code will be executed as Ruby, but not rendered. + * ```erb + * <% books = author.books + * for book in books do %> + * ``` + */ +class ErbExecutionDirective extends ErbDirective { + private Erb::Directive g; + + ErbExecutionDirective() { this = TDirective(g) } + + final override string toString() { result = "<%" + this.getToken().toString() + "%>" } + + final override string getAPrimaryQlClass() { result = "ErbExecutionDirective" } +} + +/** + * A `File` containing an Embedded Ruby template. + * This is typically a file containing snippets of Ruby code that can be + * evaluated to create a compiled version of the file. + */ +class ErbFile extends File { + private ErbTemplate template; + + ErbFile() { this = template.getLocation().getFile() } + + /** + * Holds if the file represents a partial to be rendered in the context of + * another template. + */ + predicate isPartial() { this.getStem().charAt(0) = "_" } + + /** + * Gets the base template name associated with this ERB file. + * For instance, a file named `foo.html.erb` has a template name of `foo`. + * A partial template file named `_item.html.erb` has a template name of `item`. + */ + string getTemplateName() { none() } + + /** + * Gets the erb template contained within this file. + */ + ErbTemplate getTemplate() { result = template } +} + +private class PartialErbFile extends ErbFile { + PartialErbFile() { this.isPartial() } + + // Drop the leading underscore + override string getTemplateName() { result = this.getStem().splitAt(".", 0).suffix(1) } +} + +private class FullErbFile extends ErbFile { + FullErbFile() { not this.isPartial() } + + override string getTemplateName() { result = this.getStem().splitAt(".", 0) } +} diff --git a/ruby/ql/lib/codeql/ruby/ast/Expr.qll b/ruby/ql/lib/codeql/ruby/ast/Expr.qll new file mode 100644 index 00000000000..35bdb1d8911 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/Expr.qll @@ -0,0 +1,456 @@ +private import codeql.ruby.AST +private import codeql.ruby.CFG +private import internal.AST +private import internal.TreeSitter + +/** + * An expression. + * + * This is the root QL class for all expressions. + */ +class Expr extends Stmt, TExpr { + /** Gets the textual (constant) value of this expression, if any. */ + string getValueText() { + forex(CfgNodes::ExprCfgNode n | n = this.getAControlFlowNode() | result = n.getValueText()) + } +} + +/** + * A reference to the current object. For example: + * - `self == other` + * - `self.method_name` + * - `def self.method_name ... end` + * + * This also includes implicit references to the current object in method + * calls. For example, the method call `foo(123)` has an implicit `self` + * receiver, and is equivalent to the explicit `self.foo(123)`. + */ +class Self extends Expr, TSelf { + final override string getAPrimaryQlClass() { result = "Self" } + + final override string toString() { result = "self" } +} + +/** + * A sequence of expressions in the right-hand side of an assignment or + * a `return`, `break` or `next` statement. + * ```rb + * x = 1, *items, 3, *more + * return 1, 2 + * next *list + * break **map + * return 1, 2, *items, k: 5, **map + * ``` + */ +class ArgumentList extends Expr, TArgumentList { + private Ruby::AstNode g; + + ArgumentList() { this = TArgumentList(g) } + + /** Gets the `i`th element in this argument list. */ + Expr getElement(int i) { + toGenerated(result) in [ + g.(Ruby::ArgumentList).getChild(i), g.(Ruby::RightAssignmentList).getChild(i) + ] + } + + final override string getAPrimaryQlClass() { result = "ArgumentList" } + + final override string toString() { result = "..., ..." } + + final override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getElement" and result = this.getElement(_) + } +} + +/** A sequence of expressions. */ +class StmtSequence extends Expr, TStmtSequence { + override string getAPrimaryQlClass() { result = "StmtSequence" } + + /** Gets the `n`th statement in this sequence. */ + Stmt getStmt(int n) { none() } + + /** Gets a statement in this sequence. */ + final Stmt getAStmt() { result = this.getStmt(_) } + + /** Gets the last statement in this sequence, if any. */ + final Stmt getLastStmt() { result = this.getStmt(this.getNumberOfStatements() - 1) } + + /** Gets the number of statements in this sequence. */ + final int getNumberOfStatements() { result = count(this.getAStmt()) } + + /** Holds if this sequence has no statements. */ + final predicate isEmpty() { this.getNumberOfStatements() = 0 } + + override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getStmt" and result = this.getStmt(_) + } +} + +private class StmtSequenceSynth extends StmtSequence, TStmtSequenceSynth { + final override Stmt getStmt(int n) { synthChild(this, n, result) } + + final override string toString() { result = "..." } +} + +private class Then extends StmtSequence, TThen { + private Ruby::Then g; + + Then() { this = TThen(g) } + + override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } + + final override string toString() { result = "then ..." } +} + +private class Else extends StmtSequence, TElse { + private Ruby::Else g; + + Else() { this = TElse(g) } + + override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } + + final override string toString() { result = "else ..." } +} + +private class Do extends StmtSequence, TDo { + private Ruby::Do g; + + Do() { this = TDo(g) } + + override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } + + final override string toString() { result = "do ..." } +} + +private class Ensure extends StmtSequence, TEnsure { + private Ruby::Ensure g; + + Ensure() { this = TEnsure(g) } + + override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } + + final override string toString() { result = "ensure ..." } +} + +/** + * A sequence of statements representing the body of a method, class, module, + * or do-block. That is, any body that may also include rescue/ensure/else + * statements. + */ +class BodyStmt extends StmtSequence, TBodyStmt { + // Not defined by dispatch, as it should not be exposed + private Ruby::AstNode getChild(int i) { + result = any(Ruby::Method g | this = TMethod(g)).getChild(i) + or + result = any(Ruby::SingletonMethod g | this = TSingletonMethod(g)).getChild(i) + or + exists(Ruby::Lambda g | this = TLambda(g) | + result = g.getBody().(Ruby::DoBlock).getChild(i) or + result = g.getBody().(Ruby::Block).getChild(i) + ) + or + result = any(Ruby::DoBlock g | this = TDoBlock(g)).getChild(i) + or + result = any(Ruby::Program g | this = TToplevel(g)).getChild(i) and + not result instanceof Ruby::BeginBlock + or + result = any(Ruby::Class g | this = TClassDeclaration(g)).getChild(i) + or + result = any(Ruby::SingletonClass g | this = TSingletonClass(g)).getChild(i) + or + result = any(Ruby::Module g | this = TModuleDeclaration(g)).getChild(i) + or + result = any(Ruby::Begin g | this = TBeginExpr(g)).getChild(i) + } + + final override Stmt getStmt(int n) { + result = + rank[n + 1](AstNode node, int i | + toGenerated(node) = this.getChild(i) and + not node instanceof Else and + not node instanceof RescueClause and + not node instanceof Ensure + | + node order by i + ) + } + + /** Gets the `n`th rescue clause in this block. */ + final RescueClause getRescue(int n) { + result = + rank[n + 1](RescueClause node, int i | toGenerated(node) = this.getChild(i) | node order by i) + } + + /** Gets a rescue clause in this block. */ + final RescueClause getARescue() { result = this.getRescue(_) } + + /** Gets the `else` clause in this block, if any. */ + final StmtSequence getElse() { result = unique(Else s | toGenerated(s) = getChild(_)) } + + /** Gets the `ensure` clause in this block, if any. */ + final StmtSequence getEnsure() { result = unique(Ensure s | toGenerated(s) = getChild(_)) } + + final predicate hasEnsure() { exists(this.getEnsure()) } + + override AstNode getAChild(string pred) { + result = StmtSequence.super.getAChild(pred) + or + pred = "getRescue" and result = this.getRescue(_) + or + pred = "getElse" and result = this.getElse() + or + pred = "getEnsure" and result = this.getEnsure() + } +} + +/** + * A parenthesized expression sequence, typically containing a single expression: + * ```rb + * (x + 1) + * ``` + * However, they can also contain multiple expressions (the value of the parenthesized + * expression is the last expression): + * ```rb + * (foo; bar) + * ``` + * or even an empty sequence (value is `nil`): + * ```rb + * () + * ``` + */ +class ParenthesizedExpr extends StmtSequence, TParenthesizedExpr { + private Ruby::ParenthesizedStatements g; + + ParenthesizedExpr() { this = TParenthesizedExpr(g) } + + final override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } + + final override string getAPrimaryQlClass() { result = "ParenthesizedExpr" } + + final override string toString() { result = "( ... )" } +} + +/** + * A pair expression. For example, in a hash: + * ```rb + * { foo: bar } + * ``` + * Or a keyword argument: + * ```rb + * baz(qux: 1) + * ``` + */ +class Pair extends Expr, TPair { + private Ruby::Pair g; + + Pair() { this = TPair(g) } + + final override string getAPrimaryQlClass() { result = "Pair" } + + /** + * Gets the key expression of this pair. For example, the `SymbolLiteral` + * representing the keyword `foo` in the following example: + * ```rb + * bar(foo: 123) + * ``` + * Or the `StringLiteral` for `'foo'` in the following hash pair: + * ```rb + * { 'foo' => 123 } + * ``` + */ + final Expr getKey() { toGenerated(result) = g.getKey() } + + /** + * Gets the value expression of this pair. For example, the `InteralLiteral` + * 123 in the following hash pair: + * ```rb + * { 'foo' => 123 } + * ``` + */ + final Expr getValue() { toGenerated(result) = g.getValue() } + + final override string toString() { result = "Pair" } + + override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getKey" and result = this.getKey() + or + pred = "getValue" and result = this.getValue() + } +} + +/** + * A rescue clause. For example: + * ```rb + * begin + * write_file + * rescue StandardError => msg + * puts msg + * end + */ +class RescueClause extends Expr, TRescueClause { + private Ruby::Rescue g; + + RescueClause() { this = TRescueClause(g) } + + final override string getAPrimaryQlClass() { result = "RescueClause" } + + /** + * Gets the `n`th exception to match, if any. For example `FirstError` or `SecondError` in: + * ```rb + * begin + * do_something + * rescue FirstError, SecondError => e + * handle_error(e) + * end + * ``` + */ + final Expr getException(int n) { toGenerated(result) = g.getExceptions().getChild(n) } + + /** + * Gets an exception to match, if any. For example `FirstError` or `SecondError` in: + * ```rb + * begin + * do_something + * rescue FirstError, SecondError => e + * handle_error(e) + * end + * ``` + */ + final Expr getAnException() { result = this.getException(_) } + + /** + * Gets the variable to which to assign the matched exception, if any. + * For example `err` in: + * ```rb + * begin + * do_something + * rescue StandardError => err + * handle_error(err) + * end + * ``` + */ + final LhsExpr getVariableExpr() { toGenerated(result) = g.getVariable().getChild() } + + /** + * Gets the exception handler body. + */ + final StmtSequence getBody() { toGenerated(result) = g.getBody() } + + final override string toString() { result = "rescue ..." } + + override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getException" and result = this.getException(_) + or + pred = "getVariableExpr" and result = this.getVariableExpr() + or + pred = "getBody" and result = this.getBody() + } +} + +/** + * An expression with a `rescue` modifier. For example: + * ```rb + * contents = read_file rescue "" + * ``` + */ +class RescueModifierExpr extends Expr, TRescueModifierExpr { + private Ruby::RescueModifier g; + + RescueModifierExpr() { this = TRescueModifierExpr(g) } + + final override string getAPrimaryQlClass() { result = "RescueModifierExpr" } + + /** + * Gets the body of this `RescueModifierExpr`. + * ```rb + * body rescue handler + * ``` + */ + final Stmt getBody() { toGenerated(result) = g.getBody() } + + /** + * Gets the exception handler of this `RescueModifierExpr`. + * ```rb + * body rescue handler + * ``` + */ + final Stmt getHandler() { toGenerated(result) = g.getHandler() } + + final override string toString() { result = "... rescue ..." } + + override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getBody" and result = this.getBody() + or + pred = "getHandler" and result = this.getHandler() + } +} + +/** + * A concatenation of string literals. + * + * ```rb + * "foo" "bar" "baz" + * ``` + */ +class StringConcatenation extends Expr, TStringConcatenation { + private Ruby::ChainedString g; + + StringConcatenation() { this = TStringConcatenation(g) } + + final override string getAPrimaryQlClass() { result = "StringConcatenation" } + + /** Gets the `n`th string literal in this concatenation. */ + final StringLiteral getString(int n) { toGenerated(result) = g.getChild(n) } + + /** Gets a string literal in this concatenation. */ + final StringLiteral getAString() { result = this.getString(_) } + + /** Gets the number of string literals in this concatenation. */ + final int getNumberOfStrings() { result = count(this.getString(_)) } + + /** + * Gets the result of concatenating all the string literals, if and only if + * they do not contain any interpolations. + * + * For the following example, the result is `"foobar"`: + * + * ```rb + * "foo" 'bar' + * ``` + * + * And for the following example, where one of the string literals includes + * an interpolation, there is no result: + * + * ```rb + * "foo" "bar#{ n }" + * ``` + */ + final string getConcatenatedValueText() { + forall(StringLiteral c | c = this.getString(_) | exists(c.getValueText())) and + result = + concat(string valueText, int i | + valueText = this.getString(i).getValueText() + | + valueText order by i + ) + } + + final override string toString() { result = "\"...\" \"...\"" } + + override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getString" and result = this.getString(_) + } +} diff --git a/ruby/ql/lib/codeql/ruby/ast/Literal.qll b/ruby/ql/lib/codeql/ruby/ast/Literal.qll new file mode 100644 index 00000000000..b541f2e28ec --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/Literal.qll @@ -0,0 +1,892 @@ +private import codeql.ruby.AST +private import codeql.ruby.regexp.RegExpTreeView as RETV +private import internal.AST +private import internal.Scope +private import internal.TreeSitter + +/** + * A literal. + * + * This is the QL root class for all literals. + */ +class Literal extends Expr, TLiteral { + /** + * Gets the source text for this literal, if this is a simple literal. + * + * For complex literals, such as arrays, hashes, and strings with + * interpolations, this predicate has no result. + */ + override string getValueText() { none() } +} + +/** + * A numeric literal, i.e. an integer, floating-point, rational, or complex + * value. + * + * ```rb + * 123 + * 0xff + * 3.14159 + * 1.0E2 + * 7r + * 1i + * ``` + */ +class NumericLiteral extends Literal, TNumericLiteral { } + +/** + * An integer literal. + * + * ```rb + * 123 + * 0xff + * ``` + */ +class IntegerLiteral extends NumericLiteral, TIntegerLiteral { + /** Gets the numerical value of this integer literal. */ + int getValue() { none() } + + final override string toString() { result = this.getValueText() } + + final override string getAPrimaryQlClass() { result = "IntegerLiteral" } +} + +private class IntegerLiteralReal extends IntegerLiteral, TIntegerLiteralReal { + private Ruby::Integer g; + + IntegerLiteralReal() { this = TIntegerLiteralReal(g) } + + final override string getValueText() { result = g.getValue() } + + final override int getValue() { + exists(string s, string values, string str | + s = this.getValueText().toLowerCase() and + ( + s.matches("0b%") and + values = "01" and + str = s.suffix(2) + or + s.matches("0x%") and + values = "0123456789abcdef" and + str = s.suffix(2) + or + s.charAt(0) = "0" and + not s.charAt(1) = ["b", "x", "o"] and + values = "01234567" and + str = s.suffix(1) + or + s.matches("0o%") and + values = "01234567" and + str = s.suffix(2) + or + s.charAt(0) != "0" and values = "0123456789" and str = s + ) + | + result = + sum(int index, string c, int v, int exp | + c = str.replaceAll("_", "").charAt(index) and + v = values.indexOf(c.toLowerCase()) and + exp = str.replaceAll("_", "").length() - index - 1 + | + v * values.length().pow(exp) + ) + ) + } +} + +private class IntegerLiteralSynth extends IntegerLiteral, TIntegerLiteralSynth { + private int value; + + IntegerLiteralSynth() { this = TIntegerLiteralSynth(_, _, value) } + + final override string getValueText() { result = value.toString() } + + final override int getValue() { result = value } +} + +/** + * A floating-point literal. + * + * ```rb + * 1.3 + * 2.7e+5 + * ``` + */ +class FloatLiteral extends NumericLiteral, TFloatLiteral { + private Ruby::Float g; + + FloatLiteral() { this = TFloatLiteral(g) } + + final override string getValueText() { result = g.getValue() } + + final override string toString() { result = this.getValueText() } + + final override string getAPrimaryQlClass() { result = "FloatLiteral" } +} + +/** + * A rational literal. + * + * ```rb + * 123r + * ``` + */ +class RationalLiteral extends NumericLiteral, TRationalLiteral { + private Ruby::Rational g; + + RationalLiteral() { this = TRationalLiteral(g) } + + final override string getValueText() { result = g.getChild().(Ruby::Token).getValue() + "r" } + + final override string toString() { result = this.getValueText() } + + final override string getAPrimaryQlClass() { result = "RationalLiteral" } +} + +/** + * A complex literal. + * + * ```rb + * 1i + * ``` + */ +class ComplexLiteral extends NumericLiteral, TComplexLiteral { + private Ruby::Complex g; + + ComplexLiteral() { this = TComplexLiteral(g) } + + final override string getValueText() { result = g.getValue() } + + final override string toString() { result = this.getValueText() } + + final override string getAPrimaryQlClass() { result = "ComplexLiteral" } +} + +/** A `nil` literal. */ +class NilLiteral extends Literal, TNilLiteral { + private Ruby::Nil g; + + NilLiteral() { this = TNilLiteral(g) } + + final override string getValueText() { result = g.getValue() } + + final override string toString() { result = this.getValueText() } + + final override string getAPrimaryQlClass() { result = "NilLiteral" } +} + +/** + * A Boolean literal. + * ```rb + * true + * false + * TRUE + * FALSE + * ``` + */ +class BooleanLiteral extends Literal, TBooleanLiteral { + final override string getAPrimaryQlClass() { result = "BooleanLiteral" } + + final override string toString() { result = this.getValueText() } + + /** Holds if the Boolean literal is `true` or `TRUE`. */ + predicate isTrue() { none() } + + /** Holds if the Boolean literal is `false` or `FALSE`. */ + predicate isFalse() { none() } + + /** Gets the value of this Boolean literal. */ + boolean getValue() { + this.isTrue() and result = true + or + this.isFalse() and result = false + } +} + +private class TrueLiteral extends BooleanLiteral, TTrueLiteral { + private Ruby::True g; + + TrueLiteral() { this = TTrueLiteral(g) } + + final override string getValueText() { result = g.getValue() } + + final override predicate isTrue() { any() } +} + +private class FalseLiteral extends BooleanLiteral, TFalseLiteral { + private Ruby::False g; + + FalseLiteral() { this = TFalseLiteral(g) } + + final override string getValueText() { result = g.getValue() } + + final override predicate isFalse() { any() } +} + +/** + * The base class for a component of a string: `StringTextComponent`, + * `StringEscapeSequenceComponent`, or `StringInterpolationComponent`. + */ +class StringComponent extends AstNode, TStringComponent { + /** + * Gets the source text for this string component. Has no result if this is + * a `StringInterpolationComponent`. + */ + string getValueText() { none() } +} + +/** + * A component of a string (or string-like) literal that is simply text. + * + * For example, the following string literals all contain `StringTextComponent` + * components whose `getValueText()` returns `"foo"`: + * + * ```rb + * 'foo' + * "#{ bar() }foo" + * "foo#{ bar() } baz" + * ``` + */ +class StringTextComponent extends StringComponent, TStringTextComponent { + private Ruby::Token g; + + StringTextComponent() { this = TStringTextComponent(g) } + + final override string toString() { result = g.getValue() } + + final override string getValueText() { result = g.getValue() } + + final override string getAPrimaryQlClass() { result = "StringTextComponent" } +} + +/** + * An escape sequence component of a string or string-like literal. + */ +class StringEscapeSequenceComponent extends StringComponent, TStringEscapeSequenceComponent { + private Ruby::EscapeSequence g; + + StringEscapeSequenceComponent() { this = TStringEscapeSequenceComponent(g) } + + final override string toString() { result = g.getValue() } + + final override string getValueText() { result = g.getValue() } + + final override string getAPrimaryQlClass() { result = "StringEscapeSequenceComponent" } +} + +/** + * An interpolation expression component of a string or string-like literal. + */ +class StringInterpolationComponent extends StringComponent, StmtSequence, + TStringInterpolationComponent { + private Ruby::Interpolation g; + + StringInterpolationComponent() { this = TStringInterpolationComponent(g) } + + final override string toString() { result = "#{...}" } + + final override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } + + final override string getValueText() { none() } + + final override string getAPrimaryQlClass() { result = "StringInterpolationComponent" } +} + +/** + * A string, symbol, regexp, or subshell literal. + */ +class StringlikeLiteral extends Literal, TStringlikeLiteral { + /** + * Gets the `n`th component of this string or string-like literal. The result + * will be one of `StringTextComponent`, `StringInterpolationComponent`, and + * `StringEscapeSequenceComponent`. + * + * In the following example, the result for `n = 0` is the + * `StringTextComponent` for `foo_`, and the result for `n = 1` is the + * `StringInterpolationComponent` for `Time.now`. + * + * ```rb + * "foo_#{ Time.now }" + * ``` + */ + StringComponent getComponent(int n) { none() } + + /** + * Gets the number of components in this string or string-like literal. + * + * For the empty string `""`, the result is 0. + * + * For the string `"foo"`, the result is 1: there is a single + * `StringTextComponent`. + * + * For the following example, the result is 3: there is a + * `StringTextComponent` for the substring `"foo_"`; a + * `StringEscapeSequenceComponent` for the escaped quote; and a + * `StringInterpolationComponent` for the interpolation. + * + * ```rb + * "foo\"#{bar}" + * ``` + */ + final int getNumberOfComponents() { result = count(this.getComponent(_)) } + + private string getStartDelimiter() { + this instanceof TStringLiteral and + result = "\"" + or + this instanceof TRegExpLiteral and + result = "/" + or + this instanceof TSimpleSymbolLiteral and + result = ":" + or + this instanceof TComplexSymbolLiteral and + result = ":\"" + or + this instanceof THashKeySymbolLiteral and + result = "" + or + this instanceof TSubshellLiteral and + result = "`" + or + this instanceof THereDoc and + result = "" + } + + private string getEndDelimiter() { + this instanceof TStringLiteral and + result = "\"" + or + this instanceof TRegExpLiteral and + result = "/" + or + this instanceof TSimpleSymbolLiteral and + result = "" + or + this instanceof TComplexSymbolLiteral and + result = "\"" + or + this instanceof THashKeySymbolLiteral and + result = "" + or + this instanceof TSubshellLiteral and + result = "`" + or + this instanceof THereDoc and + result = "" + } + + override string getValueText() { + // 0 components should result in the empty string + // if there are any interpolations, there should be no result + // otherwise, concatenate all the components + forall(StringComponent c | c = this.getComponent(_) | + not c instanceof StringInterpolationComponent + ) and + result = + concat(StringComponent c, int i | c = this.getComponent(i) | c.getValueText() order by i) + } + + override string toString() { + exists(string full, string summary | + full = + concat(StringComponent c, int i, string s | + c = this.getComponent(i) and + ( + s = toGenerated(c).(Ruby::Token).getValue() + or + not toGenerated(c) instanceof Ruby::Token and + s = "#{...}" + ) + | + s order by i + ) and + ( + // summary should be 32 chars max (incl. ellipsis) + full.length() > 32 and summary = full.substring(0, 29) + "..." + or + full.length() <= 32 and summary = full + ) and + result = this.getStartDelimiter() + summary + this.getEndDelimiter() + ) + } + + final override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getComponent" and result = this.getComponent(_) + } +} + +/** + * A string literal. + * + * ```rb + * 'hello' + * "hello, #{name}" + * ``` + */ +class StringLiteral extends StringlikeLiteral, TStringLiteral { + final override string getAPrimaryQlClass() { result = "StringLiteral" } +} + +private class RegularStringLiteral extends StringLiteral, TRegularStringLiteral { + private Ruby::String g; + + RegularStringLiteral() { this = TRegularStringLiteral(g) } + + final override StringComponent getComponent(int n) { toGenerated(result) = g.getChild(n) } +} + +private class BareStringLiteral extends StringLiteral, TBareStringLiteral { + private Ruby::BareString g; + + BareStringLiteral() { this = TBareStringLiteral(g) } + + final override StringComponent getComponent(int n) { toGenerated(result) = g.getChild(n) } +} + +/** + * A regular expression literal. + * + * ```rb + * /[a-z]+/ + * ``` + */ +class RegExpLiteral extends StringlikeLiteral, TRegExpLiteral { + private Ruby::Regex g; + + RegExpLiteral() { this = TRegExpLiteral(g) } + + final override string getAPrimaryQlClass() { result = "RegExpLiteral" } + + final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) } + + /** + * Gets the regexp flags as a string. + * + * ```rb + * /foo/ # => "" + * /foo/i # => "i" + * /foo/imxo # => "imxo" + */ + final string getFlagString() { + // For `/foo/i`, there should be an `/i` token in the database with `this` + // as its parents. Strip the delimiter, which can vary. + result = + max(Ruby::Token t | t.getParent() = g | t.getValue().suffix(1) order by t.getParentIndex()) + } + + /** + * Holds if the regexp was specified using the `i` flag to indicate case + * insensitivity, as in the following example: + * + * ```rb + * /foo/i + * ``` + */ + final predicate hasCaseInsensitiveFlag() { this.getFlagString().charAt(_) = "i" } + + /** + * Holds if the regex was specified using the `m` flag to indicate multiline + * mode. For example: + * + * ```rb + * /foo/m + * ``` + */ + final predicate hasMultilineFlag() { this.getFlagString().charAt(_) = "m" } + + /** + * Holds if the regex was specified using the `x` flag to indicate + * 'free-spacing' mode (also known as 'extended' mode), meaning that + * whitespace and comments in the pattern are ignored. For example: + * + * ```rb + * %r{ + * [a-zA-Z_] # starts with a letter or underscore + * \w* # and then zero or more letters/digits/underscores + * }/x + * ``` + */ + final predicate hasFreeSpacingFlag() { this.getFlagString().charAt(_) = "x" } + + /** Returns the root node of the parse tree of this regular expression. */ + final RETV::RegExpTerm getParsed() { result = RETV::getParsedRegExp(this) } +} + +/** + * A symbol literal. + * + * ```rb + * :foo + * :"foo bar" + * :"foo bar #{baz}" + * ``` + */ +class SymbolLiteral extends StringlikeLiteral, TSymbolLiteral { + final override string getAPrimaryQlClass() { + not this instanceof MethodName and result = "SymbolLiteral" + } +} + +private class SimpleSymbolLiteral extends SymbolLiteral, TSimpleSymbolLiteral { + private Ruby::SimpleSymbol g; + + SimpleSymbolLiteral() { this = TSimpleSymbolLiteral(g) } + + // Tree-sitter gives us value text including the colon, which we skip. + final override string getValueText() { result = g.getValue().suffix(1) } + + final override string toString() { result = g.getValue() } +} + +private class ComplexSymbolLiteral extends SymbolLiteral, TComplexSymbolLiteral { } + +private class DelimitedSymbolLiteral extends ComplexSymbolLiteral, TDelimitedSymbolLiteral { + private Ruby::DelimitedSymbol g; + + DelimitedSymbolLiteral() { this = TDelimitedSymbolLiteral(g) } + + final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) } +} + +private class BareSymbolLiteral extends ComplexSymbolLiteral, TBareSymbolLiteral { + private Ruby::BareSymbol g; + + BareSymbolLiteral() { this = TBareSymbolLiteral(g) } + + final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) } +} + +private class HashKeySymbolLiteral extends SymbolLiteral, THashKeySymbolLiteral { + private Ruby::HashKeySymbol g; + + HashKeySymbolLiteral() { this = THashKeySymbolLiteral(g) } + + final override string getValueText() { result = g.getValue() } + + final override string toString() { result = ":" + this.getValueText() } +} + +/** + * A subshell literal. + * + * ```rb + * `ls -l` + * %x(/bin/sh foo.sh) + * ``` + */ +class SubshellLiteral extends StringlikeLiteral, TSubshellLiteral { + private Ruby::Subshell g; + + SubshellLiteral() { this = TSubshellLiteral(g) } + + final override string getAPrimaryQlClass() { result = "SubshellLiteral" } + + final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) } +} + +/** + * A character literal. + * + * ```rb + * ?a + * ?\u{61} + * ``` + */ +class CharacterLiteral extends Literal, TCharacterLiteral { + private Ruby::Character g; + + CharacterLiteral() { this = TCharacterLiteral(g) } + + final override string getValueText() { result = g.getValue() } + + final override string toString() { result = g.getValue() } + + final override string getAPrimaryQlClass() { result = "CharacterLiteral" } +} + +/** + * A "here document". For example: + * ```rb + * query = <<SQL + * SELECT * FROM person + * WHERE age > 21 + * SQL + * ``` + */ +class HereDoc extends StringlikeLiteral, THereDoc { + private Ruby::HeredocBeginning g; + + HereDoc() { this = THereDoc(g) } + + final override string getAPrimaryQlClass() { result = "HereDoc" } + + /** + * Holds if this here document is executed in a subshell. + * ```rb + * <<`COMMAND` + * echo "Hello world!" + * COMMAND + * ``` + */ + final predicate isSubShell() { this.getQuoteStyle() = "`" } + + /** + * Gets the quotation mark (`"`, `'` or `` ` ``) that surrounds the here document identifier, if any. + * ```rb + * <<"IDENTIFIER" + * <<'IDENTIFIER' + * <<`IDENTIFIER` + * ``` + */ + final string getQuoteStyle() { + exists(string s | + s = g.getValue() and + s.charAt(s.length() - 1) = result and + result = ["'", "`", "\""] + ) + } + + /** + * Gets the indentation modifier (`-` or `~`) of the here document identifier, if any. + * ```rb + * <<~IDENTIFIER + * <<-IDENTIFIER + * <<IDENTIFIER + * ``` + */ + final string getIndentationModifier() { + exists(string s | + s = g.getValue() and + s.charAt(2) = result and + result = ["-", "~"] + ) + } + + final override StringComponent getComponent(int n) { + toGenerated(result) = getHereDocBody(g).getChild(n) + } + + final override string toString() { result = g.getValue() } +} + +/** + * An array literal. + * + * ```rb + * [123, 'foo', bar()] + * %w(foo bar) + * %i(foo bar) + * ``` + */ +class ArrayLiteral extends Literal, TArrayLiteral { + final override string getAPrimaryQlClass() { result = "ArrayLiteral" } + + /** Gets the `n`th element in this array literal. */ + final Expr getElement(int n) { result = this.(ArrayLiteralImpl).getElementImpl(n) } + + /** Gets an element in this array literal. */ + final Expr getAnElement() { result = this.getElement(_) } + + /** Gets the number of elements in this array literal. */ + final int getNumberOfElements() { result = this.(ArrayLiteralImpl).getNumberOfElementsImpl() } + + final override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getElement" and result = this.getElement(_) + } +} + +abstract private class ArrayLiteralImpl extends ArrayLiteral { + abstract Expr getElementImpl(int n); + + abstract int getNumberOfElementsImpl(); +} + +private class RegularArrayLiteral extends ArrayLiteralImpl, TRegularArrayLiteral { + private Ruby::Array g; + + RegularArrayLiteral() { this = TRegularArrayLiteral(g) } + + final override Expr getElementImpl(int i) { toGenerated(result) = g.getChild(i) } + + final override int getNumberOfElementsImpl() { result = count(g.getChild(_)) } + + final override string toString() { result = "[...]" } +} + +private class StringArrayLiteral extends ArrayLiteralImpl, TStringArrayLiteral { + private Ruby::StringArray g; + + StringArrayLiteral() { this = TStringArrayLiteral(g) } + + final override Expr getElementImpl(int i) { toGenerated(result) = g.getChild(i) } + + final override int getNumberOfElementsImpl() { result = count(g.getChild(_)) } + + final override string toString() { result = "%w(...)" } +} + +private class SymbolArrayLiteral extends ArrayLiteralImpl, TSymbolArrayLiteral { + private Ruby::SymbolArray g; + + SymbolArrayLiteral() { this = TSymbolArrayLiteral(g) } + + final override Expr getElementImpl(int i) { toGenerated(result) = g.getChild(i) } + + final override int getNumberOfElementsImpl() { result = count(g.getChild(_)) } + + final override string toString() { result = "%i(...)" } +} + +/** + * A hash literal. + * + * ```rb + * { foo: 123, bar: 456 } + * ``` + */ +class HashLiteral extends Literal, THashLiteral { + private Ruby::Hash g; + + HashLiteral() { this = THashLiteral(g) } + + final override string getAPrimaryQlClass() { result = "HashLiteral" } + + /** + * Gets the `n`th element in this hash literal. + * + * In the following example, the 0th element is a `Pair`, and the 1st element + * is a `HashSplatExpr`. + * + * ```rb + * { foo: 123, **bar } + * ``` + */ + final Expr getElement(int n) { toGenerated(result) = g.getChild(n) } + + /** Gets an element in this hash literal. */ + final Expr getAnElement() { result = this.getElement(_) } + + /** Gets a key-value `Pair` in this hash literal. */ + final Pair getAKeyValuePair() { result = this.getAnElement() } + + /** Gets the number of elements in this hash literal. */ + final int getNumberOfElements() { result = count(this.getAnElement()) } + + final override string toString() { result = "{...}" } + + final override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getElement" and result = this.getElement(_) + } +} + +/** + * A range literal. + * + * ```rb + * (1..10) + * (1024...2048) + * ``` + */ +class RangeLiteral extends Literal, TRangeLiteral { + final override string getAPrimaryQlClass() { result = "RangeLiteral" } + + /** Gets the begin expression of this range, if any. */ + Expr getBegin() { none() } + + /** Gets the end expression of this range, if any. */ + Expr getEnd() { none() } + + /** + * Holds if the range is inclusive of the end value, i.e. uses the `..` + * operator. + */ + predicate isInclusive() { none() } + + /** + * Holds if the range is exclusive of the end value, i.e. uses the `...` + * operator. + */ + predicate isExclusive() { none() } + + final override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getBegin" and result = this.getBegin() + or + pred = "getEnd" and result = this.getEnd() + } + + final override string toString() { + exists(string op | + this.isInclusive() and op = ".." + or + this.isExclusive() and op = "..." + | + result = "_ " + op + " _" + ) + } +} + +private class RangeLiteralReal extends RangeLiteral, TRangeLiteralReal { + private Ruby::Range g; + + RangeLiteralReal() { this = TRangeLiteralReal(g) } + + final override Expr getBegin() { toGenerated(result) = g.getBegin() } + + final override Expr getEnd() { toGenerated(result) = g.getEnd() } + + final override predicate isInclusive() { g instanceof @ruby_range_dotdot } + + final override predicate isExclusive() { g instanceof @ruby_range_dotdotdot } +} + +private class RangeLiteralSynth extends RangeLiteral, TRangeLiteralSynth { + private boolean inclusive; + + RangeLiteralSynth() { this = TRangeLiteralSynth(_, _, inclusive) } + + final override Expr getBegin() { result = TIntegerLiteralSynth(this, 0, _) } + + final override Expr getEnd() { result = TIntegerLiteralSynth(this, 1, _) } + + final override predicate isInclusive() { inclusive = true } + + final override predicate isExclusive() { inclusive = false } +} + +/** + * A method name literal. For example: + * ```rb + * method_name # a normal name + * + # an operator + * :method_name # a symbol + * :"eval_#{name}" # a complex symbol + * ``` + */ +class MethodName extends Literal { + MethodName() { MethodName::range(toGenerated(this)) } + + final override string getAPrimaryQlClass() { result = "MethodName" } +} + +private class TokenMethodName extends MethodName, TTokenMethodName { + private MethodName::Token g; + + TokenMethodName() { this = TTokenMethodName(g) } + + final override string getValueText() { + result = g.(Ruby::Token).getValue() + or + result = g.(Ruby::Setter).getName().getValue() + "=" + } + + final override string toString() { result = this.getValueText() } +} diff --git a/ruby/ql/lib/codeql/ruby/ast/Method.qll b/ruby/ql/lib/codeql/ruby/ast/Method.qll new file mode 100644 index 00000000000..bf40d77034e --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/Method.qll @@ -0,0 +1,228 @@ +private import codeql.ruby.AST +private import codeql.ruby.controlflow.ControlFlowGraph +private import internal.AST +private import internal.TreeSitter + +/** A callable. */ +class Callable extends StmtSequence, Expr, Scope, TCallable { + /** Gets the number of parameters of this callable. */ + final int getNumberOfParameters() { result = count(this.getAParameter()) } + + /** Gets a parameter of this callable. */ + final Parameter getAParameter() { result = this.getParameter(_) } + + /** Gets the `n`th parameter of this callable. */ + Parameter getParameter(int n) { none() } + + override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getParameter" and result = this.getParameter(_) + } +} + +/** A method. */ +class MethodBase extends Callable, BodyStmt, Scope, TMethodBase { + /** Gets the name of this method. */ + string getName() { none() } + + /** Holds if the name of this method is `name`. */ + final predicate hasName(string name) { this.getName() = name } + + override AstNode getAChild(string pred) { + result = Callable.super.getAChild(pred) + or + result = BodyStmt.super.getAChild(pred) + } +} + +/** A call to `private`. */ +private class Private extends MethodCall { + Private() { this.getMethodName() = "private" } +} + +/** A normal method. */ +class Method extends MethodBase, TMethod { + private Ruby::Method g; + + Method() { this = TMethod(g) } + + final override string getAPrimaryQlClass() { result = "Method" } + + final override string getName() { + result = g.getName().(Ruby::Token).getValue() or + result = g.getName().(Ruby::Setter).getName().getValue() + "=" + } + + /** + * Holds if this is a setter method, as in the following example: + * ```rb + * class Person + * def name=(n) + * @name = n + * end + * end + * ``` + */ + final predicate isSetter() { g.getName() instanceof Ruby::Setter } + + /** + * Holds if this method is private. All methods with the name prefix + * `private` are private below: + * + * ```rb + * class C + * private def private1 + * end + * + * def public + * end + * + * def private2 + * end + * private :private2 + * + * private + * + * def private3 + * end + * + * def private4 + * end + * end + * ``` + */ + predicate isPrivate() { + this = any(Private p).getArgument(0) + or + exists(ClassDeclaration c, Private p, SymbolLiteral s | + p.getArgument(0) = s and + p = c.getAStmt() and + this.getName() = s.getValueText() and + this = c.getAStmt() + ) + or + exists(ClassDeclaration c, int i, int j | + c.getStmt(i).(Private).getNumberOfArguments() = 0 and + this = c.getStmt(j) and + j > i + ) + or + // Top-level methods are private members of the Object class + this.getEnclosingModule() instanceof Toplevel + } + + final override Parameter getParameter(int n) { + toGenerated(result) = g.getParameters().getChild(n) + } + + final override string toString() { result = this.getName() } +} + +/** A singleton method. */ +class SingletonMethod extends MethodBase, TSingletonMethod { + private Ruby::SingletonMethod g; + + SingletonMethod() { this = TSingletonMethod(g) } + + final override string getAPrimaryQlClass() { result = "SingletonMethod" } + + /** Gets the object of this singleton method. */ + final Expr getObject() { toGenerated(result) = g.getObject() } + + final override string getName() { + result = g.getName().(Ruby::Token).getValue() + or + result = g.getName().(Ruby::Setter).getName().getValue() + "=" + } + + final override Parameter getParameter(int n) { + toGenerated(result) = g.getParameters().getChild(n) + } + + final override string toString() { result = this.getName() } + + final override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getObject" and result = this.getObject() + } +} + +/** + * A lambda (anonymous method). For example: + * ```rb + * -> (x) { x + 1 } + * ``` + */ +class Lambda extends Callable, BodyStmt, TLambda { + private Ruby::Lambda g; + + Lambda() { this = TLambda(g) } + + final override string getAPrimaryQlClass() { result = "Lambda" } + + final override Parameter getParameter(int n) { + toGenerated(result) = g.getParameters().getChild(n) + } + + final override string toString() { result = "-> { ... }" } + + final override AstNode getAChild(string pred) { + result = Callable.super.getAChild(pred) + or + result = BodyStmt.super.getAChild(pred) + } +} + +/** A block. */ +class Block extends Callable, StmtSequence, Scope, TBlock { + override AstNode getAChild(string pred) { + result = Callable.super.getAChild(pred) + or + result = StmtSequence.super.getAChild(pred) + } +} + +/** A block enclosed within `do` and `end`. */ +class DoBlock extends Block, BodyStmt, TDoBlock { + private Ruby::DoBlock g; + + DoBlock() { this = TDoBlock(g) } + + final override Parameter getParameter(int n) { + toGenerated(result) = g.getParameters().getChild(n) + } + + final override string toString() { result = "do ... end" } + + final override AstNode getAChild(string pred) { + result = Block.super.getAChild(pred) + or + result = BodyStmt.super.getAChild(pred) + } + + final override string getAPrimaryQlClass() { result = "DoBlock" } +} + +/** + * A block defined using curly braces, e.g. in the following code: + * ```rb + * names.each { |name| puts name } + * ``` + */ +class BraceBlock extends Block, TBraceBlock { + private Ruby::Block g; + + BraceBlock() { this = TBraceBlock(g) } + + final override Parameter getParameter(int n) { + toGenerated(result) = g.getParameters().getChild(n) + } + + final override Stmt getStmt(int i) { toGenerated(result) = g.getChild(i) } + + final override string toString() { result = "{ ... }" } + + final override string getAPrimaryQlClass() { result = "BraceBlock" } +} diff --git a/ruby/ql/lib/codeql/ruby/ast/Module.qll b/ruby/ql/lib/codeql/ruby/ast/Module.qll new file mode 100644 index 00000000000..8ac85668481 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/Module.qll @@ -0,0 +1,365 @@ +private import codeql.ruby.AST +private import codeql.ruby.ast.Constant +private import internal.AST +private import internal.Module +private import internal.TreeSitter + +/** + * A representation of a run-time `module` or `class` value. + */ +class Module extends TModule { + /** Gets a declaration of this module, if any. */ + ModuleBase getADeclaration() { result.getModule() = this } + + /** Gets the super class of this module, if any. */ + Module getSuperClass() { result = getSuperClass(this) } + + /** Gets a `prepend`ed module. */ + Module getAPrependedModule() { result = getAPrependedModule(this) } + + /** Gets an `include`d module. */ + Module getAnIncludedModule() { result = getAnIncludedModule(this) } + + /** Holds if this module is a class. */ + pragma[noinline] + predicate isClass() { this.getADeclaration() instanceof ClassDeclaration } + + /** Gets a textual representation of this module. */ + string toString() { + this = TResolved(result) + or + exists(Namespace n | this = TUnresolved(n) and result = "...::" + n.toString()) + } + + /** Gets the location of this module. */ + Location getLocation() { + exists(Namespace n | this = TUnresolved(n) and result = n.getLocation()) + or + result = + min(Namespace n, string qName, Location loc, int weight | + this = TResolved(qName) and + qName = namespaceDeclaration(n) and + loc = n.getLocation() and + if exists(loc.getFile().getRelativePath()) then weight = 0 else weight = 1 + | + loc + order by + weight, count(n.getAStmt()) desc, loc.getFile().getAbsolutePath(), loc.getStartLine(), + loc.getStartColumn() + ) + } +} + +/** + * The base class for classes, singleton classes, and modules. + */ +class ModuleBase extends BodyStmt, Scope, TModuleBase { + /** Gets a method defined in this module/class. */ + MethodBase getAMethod() { result = this.getAStmt() } + + /** Gets the method named `name` in this module/class, if any. */ + MethodBase getMethod(string name) { result = this.getAMethod() and result.getName() = name } + + /** Gets a class defined in this module/class. */ + ClassDeclaration getAClass() { result = this.getAStmt() } + + /** Gets the class named `name` in this module/class, if any. */ + ClassDeclaration getClass(string name) { result = this.getAClass() and result.getName() = name } + + /** Gets a module defined in this module/class. */ + ModuleDeclaration getAModule() { result = this.getAStmt() } + + /** Gets the module named `name` in this module/class, if any. */ + ModuleDeclaration getModule(string name) { + result = this.getAModule() and result.getName() = name + } + + /** + * Gets the value of the constant named `name`, if any. + * + * For example, the value of `CONST` is `"const"` in + * ```rb + * module M + * CONST = "const" + * end + * ``` + */ + Expr getConstant(string name) { + exists(AssignExpr ae, ConstantWriteAccess w | + ae = this.getAStmt() and + w = ae.getLeftOperand() and + w.getName() = name and + not exists(w.getScopeExpr()) and + result = ae.getRightOperand() + ) + } + + /** Gets the representation of the run-time value of this module or class. */ + Module getModule() { none() } +} + +/** + * A Ruby source file. + * + * ```rb + * def main + * puts "hello world!" + * end + * main + * ``` + */ +class Toplevel extends ModuleBase, TToplevel { + private Ruby::Program g; + + Toplevel() { this = TToplevel(g) } + + final override string getAPrimaryQlClass() { result = "Toplevel" } + + /** + * Gets the `n`th `BEGIN` block. + */ + final BeginBlock getBeginBlock(int n) { + toGenerated(result) = rank[n + 1](int i, Ruby::BeginBlock b | b = g.getChild(i) | b order by i) + } + + /** + * Gets a `BEGIN` block. + */ + final BeginBlock getABeginBlock() { result = this.getBeginBlock(_) } + + final override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getBeginBlock" and result = this.getBeginBlock(_) + } + + final override Module getModule() { result = TResolved("Object") } + + final override string toString() { result = g.getLocation().getFile().getBaseName() } +} + +/** + * A class or module definition. + * + * ```rb + * class Foo + * def bar + * end + * end + * module Bar + * class Baz + * end + * end + * ``` + */ +class Namespace extends ModuleBase, ConstantWriteAccess, TNamespace { + override string getAPrimaryQlClass() { result = "Namespace" } + + /** + * Gets the name of the module/class. In the following example, the result is + * `"Foo"`. + * ```rb + * class Foo + * end + * ``` + * + * N.B. in the following example, where the module/class name uses the scope + * resolution operator, the result is the name being resolved, i.e. `"Bar"`. + * Use `getScopeExpr` to get the `Foo` for `Foo`. + * ```rb + * module Foo::Bar + * end + * ``` + */ + override string getName() { none() } + + /** + * Gets the scope expression used in the module/class name's scope resolution + * operation, if any. + * + * In the following example, the result is the `Expr` for `Foo`. + * + * ```rb + * module Foo::Bar + * end + * ``` + * + * However, there is no result for the following example, since there is no + * scope resolution operation. + * + * ```rb + * module Baz + * end + * ``` + */ + override Expr getScopeExpr() { none() } + + /** + * Holds if the module/class name uses the scope resolution operator to access the + * global scope, as in this example: + * + * ```rb + * class ::Foo + * end + * ``` + */ + override predicate hasGlobalScope() { none() } + + final override Module getModule() { + result = any(string qName | qName = namespaceDeclaration(this) | TResolved(qName)) + or + result = TUnresolved(this) + } + + override AstNode getAChild(string pred) { + result = ModuleBase.super.getAChild(pred) or + result = ConstantWriteAccess.super.getAChild(pred) + } + + final override string toString() { result = ConstantWriteAccess.super.toString() } +} + +/** + * A class definition. + * + * ```rb + * class Foo + * def bar + * end + * end + * ``` + */ +class ClassDeclaration extends Namespace, TClassDeclaration { + private Ruby::Class g; + + ClassDeclaration() { this = TClassDeclaration(g) } + + final override string getAPrimaryQlClass() { result = "ClassDeclaration" } + + /** + * Gets the `Expr` used as the superclass in the class definition, if any. + * + * In the following example, the result is a `ConstantReadAccess`. + * ```rb + * class Foo < Bar + * end + * ``` + * + * In the following example, where the superclass is a call expression, the + * result is a `Call`. + * ```rb + * class C < foo() + * end + * ``` + */ + final Expr getSuperclassExpr() { toGenerated(result) = g.getSuperclass().getChild() } + + final override string getName() { + result = g.getName().(Ruby::Token).getValue() or + result = g.getName().(Ruby::ScopeResolution).getName().(Ruby::Token).getValue() + } + + final override Expr getScopeExpr() { + toGenerated(result) = g.getName().(Ruby::ScopeResolution).getScope() + } + + final override predicate hasGlobalScope() { + exists(Ruby::ScopeResolution sr | + sr = g.getName() and + not exists(sr.getScope()) + ) + } + + final override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getSuperclassExpr" and result = this.getSuperclassExpr() + } +} + +/** + * A definition of a singleton class on an object. + * + * ```rb + * class << foo + * def bar + * p 'bar' + * end + * end + * ``` + */ +class SingletonClass extends ModuleBase, TSingletonClass { + private Ruby::SingletonClass g; + + SingletonClass() { this = TSingletonClass(g) } + + final override string getAPrimaryQlClass() { result = "SingletonClass" } + + /** + * Gets the expression resulting in the object on which the singleton class + * is defined. In the following example, the result is the `Expr` for `foo`: + * + * ```rb + * class << foo + * end + * ``` + */ + final Expr getValue() { toGenerated(result) = g.getValue() } + + final override string toString() { result = "class << ..." } + + final override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getValue" and result = this.getValue() + } +} + +/** + * A module definition. + * + * ```rb + * module Foo + * class Bar + * end + * end + * ``` + * + * N.B. this class represents a single instance of a module definition. In the + * following example, classes `Bar` and `Baz` are both defined in the module + * `Foo`, but in two syntactically distinct definitions, meaning that there + * will be two instances of `ModuleDeclaration` in the database. + * + * ```rb + * module Foo + * class Bar; end + * end + * + * module Foo + * class Baz; end + * end + * ``` + */ +class ModuleDeclaration extends Namespace, TModuleDeclaration { + private Ruby::Module g; + + ModuleDeclaration() { this = TModuleDeclaration(g) } + + final override string getAPrimaryQlClass() { result = "ModuleDeclaration" } + + final override string getName() { + result = g.getName().(Ruby::Token).getValue() or + result = g.getName().(Ruby::ScopeResolution).getName().(Ruby::Token).getValue() + } + + final override Expr getScopeExpr() { + toGenerated(result) = g.getName().(Ruby::ScopeResolution).getScope() + } + + final override predicate hasGlobalScope() { + exists(Ruby::ScopeResolution sr | + sr = g.getName() and + not exists(sr.getScope()) + ) + } +} diff --git a/ruby/ql/lib/codeql/ruby/ast/Operation.qll b/ruby/ql/lib/codeql/ruby/ast/Operation.qll new file mode 100644 index 00000000000..6c30224b3b1 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/Operation.qll @@ -0,0 +1,620 @@ +private import codeql.ruby.AST +private import internal.AST +private import internal.TreeSitter +private import internal.Operation + +/** + * An operation. + * + * This is the QL root class for all operations. + */ +class Operation extends Expr instanceof OperationImpl { + /** Gets the operator of this operation. */ + final string getOperator() { result = super.getOperatorImpl() } + + /** Gets an operand of this operation. */ + final Expr getAnOperand() { result = super.getAnOperandImpl() } + + override AstNode getAChild(string pred) { + result = Expr.super.getAChild(pred) + or + pred = "getAnOperand" and result = this.getAnOperand() + } +} + +/** A unary operation. */ +class UnaryOperation extends Operation, MethodCall instanceof UnaryOperationImpl { + /** Gets the operand of this unary operation. */ + final Expr getOperand() { result = super.getOperandImpl() } + + final override AstNode getAChild(string pred) { + result = Operation.super.getAChild(pred) + or + result = MethodCall.super.getAChild(pred) + or + pred = "getOperand" and result = this.getOperand() + } + + final override string toString() { result = this.getOperator() + " ..." } +} + +/** A unary logical operation. */ +class UnaryLogicalOperation extends UnaryOperation, TUnaryLogicalOperation { } + +/** + * A logical NOT operation, using either `!` or `not`. + * ```rb + * !x.nil? + * not params.empty? + * ``` + */ +class NotExpr extends UnaryLogicalOperation, TNotExpr { + final override string getAPrimaryQlClass() { result = "NotExpr" } +} + +/** A unary arithmetic operation. */ +class UnaryArithmeticOperation extends UnaryOperation, TUnaryArithmeticOperation { } + +/** + * A unary plus expression. + * ```rb + * + a + * ``` + */ +class UnaryPlusExpr extends UnaryArithmeticOperation, TUnaryPlusExpr { + final override string getAPrimaryQlClass() { result = "UnaryPlusExpr" } +} + +/** + * A unary minus expression. + * ```rb + * - a + * ``` + */ +class UnaryMinusExpr extends UnaryArithmeticOperation, TUnaryMinusExpr { + final override string getAPrimaryQlClass() { result = "UnaryMinusExpr" } +} + +/** + * A splat expression. + * ```rb + * foo(*args) + * ``` + */ +class SplatExpr extends UnaryOperation, TSplatExpr { + final override string getAPrimaryQlClass() { result = "SplatExpr" } +} + +/** + * A hash-splat (or 'double-splat') expression. + * ```rb + * foo(**options) + * ``` + */ +class HashSplatExpr extends UnaryOperation, THashSplatExpr { + private Ruby::HashSplatArgument g; + + HashSplatExpr() { this = THashSplatExpr(g) } + + final override string getAPrimaryQlClass() { result = "HashSplatExpr" } +} + +/** A unary bitwise operation. */ +class UnaryBitwiseOperation extends UnaryOperation, TUnaryBitwiseOperation { } + +/** + * A complement (bitwise NOT) expression. + * ```rb + * ~x + * ``` + */ +class ComplementExpr extends UnaryBitwiseOperation, TComplementExpr { + final override string getAPrimaryQlClass() { result = "ComplementExpr" } +} + +/** + * A call to the special `defined?` operator. + * ```rb + * defined? some_method + * ``` + */ +class DefinedExpr extends UnaryOperation, TDefinedExpr { + final override string getAPrimaryQlClass() { result = "DefinedExpr" } +} + +/** A binary operation. */ +class BinaryOperation extends Operation, MethodCall instanceof BinaryOperationImpl { + final override string toString() { result = "... " + this.getOperator() + " ..." } + + override AstNode getAChild(string pred) { + result = Operation.super.getAChild(pred) + or + result = MethodCall.super.getAChild(pred) + or + pred = "getLeftOperand" and result = this.getLeftOperand() + or + pred = "getRightOperand" and result = this.getRightOperand() + } + + /** Gets the left operand of this binary operation. */ + final Stmt getLeftOperand() { result = super.getLeftOperandImpl() } + + /** Gets the right operand of this binary operation. */ + final Stmt getRightOperand() { result = super.getRightOperandImpl() } +} + +/** + * A binary arithmetic operation. + */ +class BinaryArithmeticOperation extends BinaryOperation, TBinaryArithmeticOperation { } + +/** + * An add expression. + * ```rb + * x + 1 + * ``` + */ +class AddExpr extends BinaryArithmeticOperation, TAddExpr { + final override string getAPrimaryQlClass() { result = "AddExpr" } +} + +/** + * A subtract expression. + * ```rb + * x - 3 + * ``` + */ +class SubExpr extends BinaryArithmeticOperation, TSubExpr { + final override string getAPrimaryQlClass() { result = "SubExpr" } +} + +/** + * A multiply expression. + * ```rb + * x * 10 + * ``` + */ +class MulExpr extends BinaryArithmeticOperation, TMulExpr { + final override string getAPrimaryQlClass() { result = "MulExpr" } +} + +/** + * A divide expression. + * ```rb + * x / y + * ``` + */ +class DivExpr extends BinaryArithmeticOperation, TDivExpr { + final override string getAPrimaryQlClass() { result = "DivExpr" } +} + +/** + * A modulo expression. + * ```rb + * x % 2 + * ``` + */ +class ModuloExpr extends BinaryArithmeticOperation, TModuloExpr { + final override string getAPrimaryQlClass() { result = "ModuloExpr" } +} + +/** + * An exponent expression. + * ```rb + * x ** 2 + * ``` + */ +class ExponentExpr extends BinaryArithmeticOperation, TExponentExpr { + final override string getAPrimaryQlClass() { result = "ExponentExpr" } +} + +/** + * A binary logical operation. + */ +class BinaryLogicalOperation extends BinaryOperation, TBinaryLogicalOperation { } + +/** + * A logical AND operation, using either `and` or `&&`. + * ```rb + * x and y + * a && b + * ``` + */ +class LogicalAndExpr extends BinaryLogicalOperation, TLogicalAndExpr { + final override string getAPrimaryQlClass() { result = "LogicalAndExpr" } +} + +/** + * A logical OR operation, using either `or` or `||`. + * ```rb + * x or y + * a || b + * ``` + */ +class LogicalOrExpr extends BinaryLogicalOperation, TLogicalOrExpr { + final override string getAPrimaryQlClass() { result = "LogicalOrExpr" } +} + +/** + * A binary bitwise operation. + */ +class BinaryBitwiseOperation extends BinaryOperation, TBinaryBitwiseOperation { } + +/** + * A left-shift operation. + * ```rb + * x << n + * ``` + */ +class LShiftExpr extends BinaryBitwiseOperation, TLShiftExpr { + final override string getAPrimaryQlClass() { result = "LShiftExpr" } +} + +/** + * A right-shift operation. + * ```rb + * x >> n + * ``` + */ +class RShiftExpr extends BinaryBitwiseOperation, TRShiftExpr { + final override string getAPrimaryQlClass() { result = "RShiftExpr" } +} + +/** + * A bitwise AND operation. + * ```rb + * x & 0xff + * ``` + */ +class BitwiseAndExpr extends BinaryBitwiseOperation, TBitwiseAndExpr { + final override string getAPrimaryQlClass() { result = "BitwiseAndExpr" } +} + +/** + * A bitwise OR operation. + * ```rb + * x | 0x01 + * ``` + */ +class BitwiseOrExpr extends BinaryBitwiseOperation, TBitwiseOrExpr { + final override string getAPrimaryQlClass() { result = "BitwiseOrExpr" } +} + +/** + * An XOR (exclusive OR) operation. + * ```rb + * x ^ y + * ``` + */ +class BitwiseXorExpr extends BinaryBitwiseOperation, TBitwiseXorExpr { + final override string getAPrimaryQlClass() { result = "BitwiseXorExpr" } +} + +/** + * A comparison operation. That is, either an equality operation or a + * relational operation. + */ +class ComparisonOperation extends BinaryOperation, TComparisonOperation { } + +/** + * An equality operation. + */ +class EqualityOperation extends ComparisonOperation, TEqualityOperation { } + +/** + * An equals expression. + * ```rb + * x == y + * ``` + */ +class EqExpr extends EqualityOperation, TEqExpr { + final override string getAPrimaryQlClass() { result = "EqExpr" } +} + +/** + * A not-equals expression. + * ```rb + * x != y + * ``` + */ +class NEExpr extends EqualityOperation, TNEExpr { + final override string getAPrimaryQlClass() { result = "NEExpr" } +} + +/** + * A case-equality (or 'threequals') expression. + * ```rb + * String === "foo" + * ``` + */ +class CaseEqExpr extends EqualityOperation, TCaseEqExpr { + final override string getAPrimaryQlClass() { result = "CaseEqExpr" } +} + +/** + * A relational operation, that is, one of `<=`, `<`, `>`, or `>=`. + */ +class RelationalOperation extends ComparisonOperation, TRelationalOperation { + /** Gets the greater operand. */ + Expr getGreaterOperand() { none() } + + /** Gets the lesser operand. */ + Expr getLesserOperand() { none() } + + final override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getGreaterOperand" and result = this.getGreaterOperand() + or + pred = "getLesserOperand" and result = this.getLesserOperand() + } +} + +/** + * A greater-than expression. + * ```rb + * x > 0 + * ``` + */ +class GTExpr extends RelationalOperation, TGTExpr { + final override string getAPrimaryQlClass() { result = "GTExpr" } + + final override Expr getGreaterOperand() { result = this.getLeftOperand() } + + final override Expr getLesserOperand() { result = this.getRightOperand() } +} + +/** + * A greater-than-or-equal expression. + * ```rb + * x >= 0 + * ``` + */ +class GEExpr extends RelationalOperation, TGEExpr { + final override string getAPrimaryQlClass() { result = "GEExpr" } + + final override Expr getGreaterOperand() { result = this.getLeftOperand() } + + final override Expr getLesserOperand() { result = this.getRightOperand() } +} + +/** + * A less-than expression. + * ```rb + * x < 10 + * ``` + */ +class LTExpr extends RelationalOperation, TLTExpr { + final override string getAPrimaryQlClass() { result = "LTExpr" } + + final override Expr getGreaterOperand() { result = this.getRightOperand() } + + final override Expr getLesserOperand() { result = this.getLeftOperand() } +} + +/** + * A less-than-or-equal expression. + * ```rb + * x <= 10 + * ``` + */ +class LEExpr extends RelationalOperation, TLEExpr { + final override string getAPrimaryQlClass() { result = "LEExpr" } + + final override Expr getGreaterOperand() { result = this.getRightOperand() } + + final override Expr getLesserOperand() { result = this.getLeftOperand() } +} + +/** + * A three-way comparison ('spaceship') expression. + * ```rb + * a <=> b + * ``` + */ +class SpaceshipExpr extends BinaryOperation, TSpaceshipExpr { + final override string getAPrimaryQlClass() { result = "SpaceshipExpr" } +} + +/** + * A regexp match expression. + * ```rb + * input =~ /\d/ + * ``` + */ +class RegExpMatchExpr extends BinaryOperation, TRegExpMatchExpr { + final override string getAPrimaryQlClass() { result = "RegExpMatchExpr" } +} + +/** + * A regexp-doesn't-match expression. + * ```rb + * input !~ /\d/ + * ``` + */ +class NoRegExpMatchExpr extends BinaryOperation, TNoRegExpMatchExpr { + final override string getAPrimaryQlClass() { result = "NoRegExpMatchExpr" } +} + +/** + * A binary assignment operation, including `=`, `+=`, `&=`, etc. + * + * This is a QL base class for all assignments. + */ +class Assignment extends Operation instanceof AssignmentImpl { + /** Gets the left hand side of this assignment. */ + final Pattern getLeftOperand() { result = super.getLeftOperandImpl() } + + /** Gets the right hand side of this assignment. */ + final Expr getRightOperand() { result = super.getRightOperandImpl() } + + final override string toString() { result = "... " + this.getOperator() + " ..." } + + override AstNode getAChild(string pred) { + result = Operation.super.getAChild(pred) + or + pred = "getLeftOperand" and result = this.getLeftOperand() + or + pred = "getRightOperand" and result = this.getRightOperand() + } +} + +/** + * An assignment operation with the operator `=`. + * ```rb + * x = 123 + * ``` + */ +class AssignExpr extends Assignment, TAssignExpr { + final override string getAPrimaryQlClass() { result = "AssignExpr" } +} + +/** + * A binary assignment operation other than `=`. + */ +class AssignOperation extends Assignment instanceof AssignOperationImpl { } + +/** + * An arithmetic assignment operation: `+=`, `-=`, `*=`, `/=`, `**=`, and `%=`. + */ +class AssignArithmeticOperation extends AssignOperation, TAssignArithmeticOperation { } + +/** + * A `+=` assignment expression. + * ```rb + * x += 1 + * ``` + */ +class AssignAddExpr extends AssignArithmeticOperation, TAssignAddExpr { + final override string getAPrimaryQlClass() { result = "AssignAddExpr" } +} + +/** + * A `-=` assignment expression. + * ```rb + * x -= 3 + * ``` + */ +class AssignSubExpr extends AssignArithmeticOperation, TAssignSubExpr { + final override string getAPrimaryQlClass() { result = "AssignSubExpr" } +} + +/** + * A `*=` assignment expression. + * ```rb + * x *= 10 + * ``` + */ +class AssignMulExpr extends AssignArithmeticOperation, TAssignMulExpr { + final override string getAPrimaryQlClass() { result = "AssignMulExpr" } +} + +/** + * A `/=` assignment expression. + * ```rb + * x /= y + * ``` + */ +class AssignDivExpr extends AssignArithmeticOperation, TAssignDivExpr { + final override string getAPrimaryQlClass() { result = "AssignDivExpr" } +} + +/** + * A `%=` assignment expression. + * ```rb + * x %= 4 + * ``` + */ +class AssignModuloExpr extends AssignArithmeticOperation, TAssignModuloExpr { + final override string getAPrimaryQlClass() { result = "AssignModuloExpr" } +} + +/** + * A `**=` assignment expression. + * ```rb + * x **= 2 + * ``` + */ +class AssignExponentExpr extends AssignArithmeticOperation, TAssignExponentExpr { + final override string getAPrimaryQlClass() { result = "AssignExponentExpr" } +} + +/** + * A logical assignment operation: `&&=` and `||=`. + */ +class AssignLogicalOperation extends AssignOperation, TAssignLogicalOperation { } + +/** + * A logical AND assignment operation. + * ```rb + * x &&= y.even? + * ``` + */ +class AssignLogicalAndExpr extends AssignLogicalOperation, TAssignLogicalAndExpr { + final override string getAPrimaryQlClass() { result = "AssignLogicalAndExpr" } +} + +/** + * A logical OR assignment operation. + * ```rb + * x ||= y + * ``` + */ +class AssignLogicalOrExpr extends AssignLogicalOperation, TAssignLogicalOrExpr { + final override string getAPrimaryQlClass() { result = "AssignLogicalOrExpr" } +} + +/** + * A bitwise assignment operation: `<<=`, `>>=`, `&=`, `|=` and `^=`. + */ +class AssignBitwiseOperation extends AssignOperation, TAssignBitwiseOperation { } + +/** + * A left-shift assignment operation. + * ```rb + * x <<= 3 + * ``` + */ +class AssignLShiftExpr extends AssignBitwiseOperation, TAssignLShiftExpr { + final override string getAPrimaryQlClass() { result = "AssignLShiftExpr" } +} + +/** + * A right-shift assignment operation. + * ```rb + * x >>= 3 + * ``` + */ +class AssignRShiftExpr extends AssignBitwiseOperation, TAssignRShiftExpr { + final override string getAPrimaryQlClass() { result = "AssignRShiftExpr" } +} + +/** + * A bitwise AND assignment operation. + * ```rb + * x &= 0xff + * ``` + */ +class AssignBitwiseAndExpr extends AssignBitwiseOperation, TAssignBitwiseAndExpr { + final override string getAPrimaryQlClass() { result = "AssignBitwiseAndExpr" } +} + +/** + * A bitwise OR assignment operation. + * ```rb + * x |= 0x01 + * ``` + */ +class AssignBitwiseOrExpr extends AssignBitwiseOperation, TAssignBitwiseOrExpr { + final override string getAPrimaryQlClass() { result = "AssignBitwiseOrExpr" } +} + +/** + * An XOR (exclusive OR) assignment operation. + * ```rb + * x ^= y + * ``` + */ +class AssignBitwiseXorExpr extends AssignBitwiseOperation, TAssignBitwiseXorExpr { + final override string getAPrimaryQlClass() { result = "AssignBitwiseXorExpr" } +} diff --git a/ruby/ql/lib/codeql/ruby/ast/Parameter.qll b/ruby/ql/lib/codeql/ruby/ast/Parameter.qll new file mode 100644 index 00000000000..6e6b5395d43 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/Parameter.qll @@ -0,0 +1,248 @@ +private import codeql.ruby.AST +private import internal.AST +private import internal.Variable +private import internal.Parameter +private import internal.TreeSitter + +/** A parameter. */ +class Parameter extends AstNode, TParameter { + /** Gets the callable that this parameter belongs to. */ + final Callable getCallable() { result.getAParameter() = this } + + /** Gets the zero-based position of this parameter. */ + final int getPosition() { this = any(Callable c).getParameter(result) } + + /** Gets a variable introduced by this parameter. */ + LocalVariable getAVariable() { none() } + + /** Gets the variable named `name` introduced by this parameter. */ + final LocalVariable getVariable(string name) { + result = this.getAVariable() and + result.getName() = name + } +} + +/** + * A parameter defined using a pattern. + * + * This includes both simple parameters and tuple parameters. + */ +class PatternParameter extends Parameter, Pattern, TPatternParameter { + override LocalVariable getAVariable() { result = Pattern.super.getAVariable() } +} + +/** A parameter defined using a tuple pattern. */ +class TuplePatternParameter extends PatternParameter, TuplePattern, TTuplePatternParameter { + final override LocalVariable getAVariable() { result = TuplePattern.super.getAVariable() } + + final override string getAPrimaryQlClass() { result = "TuplePatternParameter" } + + override AstNode getAChild(string pred) { result = TuplePattern.super.getAChild(pred) } +} + +/** A named parameter. */ +class NamedParameter extends Parameter, TNamedParameter { + /** Gets the name of this parameter. */ + string getName() { none() } + + /** Holds if the name of this parameter is `name`. */ + final predicate hasName(string name) { this.getName() = name } + + /** Gets the variable introduced by this parameter. */ + LocalVariable getVariable() { none() } + + override LocalVariable getAVariable() { result = this.getVariable() } + + /** Gets an access to this parameter. */ + final VariableAccess getAnAccess() { result = this.getVariable().getAnAccess() } + + /** Gets the access that defines the underlying local variable. */ + final VariableAccess getDefiningAccess() { result = this.getVariable().getDefiningAccess() } + + override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getDefiningAccess" and + result = this.getDefiningAccess() + } +} + +/** A simple (normal) parameter. */ +class SimpleParameter extends NamedParameter, PatternParameter, VariablePattern, TSimpleParameter { + private Ruby::Identifier g; + + SimpleParameter() { this = TSimpleParameter(g) } + + final override string getName() { result = g.getValue() } + + final override LocalVariable getVariable() { result = TLocalVariableReal(_, _, g) } + + final override LocalVariable getAVariable() { result = this.getVariable() } + + final override string getAPrimaryQlClass() { result = "SimpleParameter" } + + final override string toString() { result = this.getName() } +} + +/** + * A parameter that is a block. For example, `&bar` in the following code: + * ```rb + * def foo(&bar) + * bar.call if block_given? + * end + * ``` + */ +class BlockParameter extends NamedParameter, TBlockParameter { + private Ruby::BlockParameter g; + + BlockParameter() { this = TBlockParameter(g) } + + final override string getName() { result = g.getName().getValue() } + + final override LocalVariable getVariable() { result = TLocalVariableReal(_, _, g.getName()) } + + final override string toString() { result = "&" + this.getName() } + + final override string getAPrimaryQlClass() { result = "BlockParameter" } +} + +/** + * A hash-splat (or double-splat) parameter. For example, `**options` in the + * following code: + * ```rb + * def foo(bar, **options) + * ... + * end + * ``` + */ +class HashSplatParameter extends NamedParameter, THashSplatParameter { + private Ruby::HashSplatParameter g; + + HashSplatParameter() { this = THashSplatParameter(g) } + + final override string getAPrimaryQlClass() { result = "HashSplatParameter" } + + final override LocalVariable getVariable() { result = TLocalVariableReal(_, _, g.getName()) } + + final override string toString() { result = "**" + this.getName() } + + final override string getName() { result = g.getName().getValue() } +} + +/** + * A keyword parameter, including a default value if the parameter is optional. + * For example, in the following example, `foo` is a keyword parameter with a + * default value of `0`, and `bar` is a mandatory keyword parameter with no + * default value mandatory parameter). + * ```rb + * def f(foo: 0, bar:) + * foo * 10 + bar + * end + * ``` + */ +class KeywordParameter extends NamedParameter, TKeywordParameter { + private Ruby::KeywordParameter g; + + KeywordParameter() { this = TKeywordParameter(g) } + + final override string getAPrimaryQlClass() { result = "KeywordParameter" } + + final override LocalVariable getVariable() { result = TLocalVariableReal(_, _, g.getName()) } + + /** + * Gets the default value, i.e. the value assigned to the parameter when one + * is not provided by the caller. If the parameter is mandatory and does not + * have a default value, this predicate has no result. + */ + final Expr getDefaultValue() { toGenerated(result) = g.getValue() } + + /** + * Holds if the parameter is optional. That is, there is a default value that + * is used when the caller omits this parameter. + */ + final predicate isOptional() { exists(this.getDefaultValue()) } + + final override string toString() { result = this.getName() } + + final override string getName() { result = g.getName().getValue() } + + final override Location getLocation() { result = g.getName().getLocation() } + + final override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getDefaultValue" and result = this.getDefaultValue() + } +} + +/** + * An optional parameter. For example, the parameter `name` in the following + * code: + * ```rb + * def say_hello(name = 'Anon') + * puts "hello #{name}" + * end + * ``` + */ +class OptionalParameter extends NamedParameter, TOptionalParameter { + private Ruby::OptionalParameter g; + + OptionalParameter() { this = TOptionalParameter(g) } + + final override string getAPrimaryQlClass() { result = "OptionalParameter" } + + /** + * Gets the default value, i.e. the value assigned to the parameter when one + * is not provided by the caller. + */ + final Expr getDefaultValue() { toGenerated(result) = g.getValue() } + + final override LocalVariable getVariable() { result = TLocalVariableReal(_, _, g.getName()) } + + final override string toString() { result = this.getName() } + + final override string getName() { result = g.getName().getValue() } + + final override Location getLocation() { result = g.getName().getLocation() } + + final override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getDefaultValue" and result = this.getDefaultValue() + } +} + +/** + * A splat parameter. For example, `*values` in the following code: + * ```rb + * def foo(bar, *values) + * ... + * end + * ``` + */ +class SplatParameter extends NamedParameter, TSplatParameter { + private Ruby::SplatParameter g; + + SplatParameter() { this = TSplatParameter(g) } + + final override string getAPrimaryQlClass() { result = "SplatParameter" } + + final override LocalVariable getVariable() { result = TLocalVariableReal(_, _, g.getName()) } + + final override string toString() { result = "*" + this.getName() } + + final override string getName() { result = g.getName().getValue() } +} + +/** + * A special `...` parameter that forwards positional/keyword/block arguments: + * ```rb + * def foo(...) + * end + * ``` + */ +class ForwardParameter extends Parameter, TForwardParameter { + final override string getAPrimaryQlClass() { result = "ForwardParameter" } + + final override string toString() { result = "..." } +} diff --git a/ruby/ql/lib/codeql/ruby/ast/Pattern.qll b/ruby/ql/lib/codeql/ruby/ast/Pattern.qll new file mode 100644 index 00000000000..b275a8d813a --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/Pattern.qll @@ -0,0 +1,96 @@ +private import codeql.ruby.AST +private import codeql.Locations +private import internal.AST +private import internal.Pattern +private import internal.TreeSitter +private import internal.Variable + +/** A pattern. */ +class Pattern extends AstNode { + Pattern() { + explicitAssignmentNode(toGenerated(this), _) + or + implicitAssignmentNode(toGenerated(this)) + or + implicitParameterAssignmentNode(toGenerated(this), _) + or + this = getSynthChild(any(AssignExpr ae), 0) + } + + /** Gets a variable used in (or introduced by) this pattern. */ + Variable getAVariable() { none() } +} + +private class LhsExpr_ = + TVariableAccess or TTokenConstantAccess or TScopeResolutionConstantAccess or TMethodCall or + TSimpleParameter; + +/** + * A "left-hand-side" expression. An `LhsExpr` can occur on the left-hand side of + * operator assignments (`AssignOperation`), in patterns (`Pattern`) on the left-hand side of + * an assignment (`AssignExpr`) or for loop (`ForExpr`), and as the exception + * variable of a `rescue` clause (`RescueClause`). + * + * An `LhsExpr` can be a simple variable, a constant, a call, or an element reference: + * ```rb + * var = 1 + * var += 1 + * E = 1 + * foo.bar = 1 + * foo[0] = 1 + * rescue E => var + * ``` + */ +class LhsExpr extends Pattern, LhsExpr_, Expr { + override Variable getAVariable() { result = this.(VariableAccess).getVariable() } +} + +private class TVariablePattern = TVariableAccess or TSimpleParameter; + +/** A simple variable pattern. */ +class VariablePattern extends Pattern, LhsExpr, TVariablePattern { } + +/** + * A tuple pattern. + * + * This includes both tuple patterns in parameters and assignments. Example patterns: + * ```rb + * a, self.b = value + * (a, b), c[3] = value + * a, b, *rest, c, d = value + * ``` + */ +class TuplePattern extends Pattern, TTuplePattern { + override string getAPrimaryQlClass() { result = "TuplePattern" } + + private TuplePatternImpl getImpl() { result = toGenerated(this) } + + private Ruby::AstNode getChild(int i) { result = this.getImpl().getChildNode(i) } + + /** Gets the `i`th pattern in this tuple pattern. */ + final Pattern getElement(int i) { + exists(Ruby::AstNode c | c = this.getChild(i) | + toGenerated(result) = c.(Ruby::RestAssignment).getChild() + or + toGenerated(result) = c + ) + } + + /** Gets a sub pattern in this tuple pattern. */ + final Pattern getAnElement() { result = this.getElement(_) } + + /** + * Gets the index of the pattern with the `*` marker on it, if it exists. + * In the example below the index is `2`. + * ```rb + * a, b, *rest, c, d = value + * ``` + */ + final int getRestIndex() { result = this.getImpl().getRestIndex() } + + override Variable getAVariable() { result = this.getElement(_).getAVariable() } + + override string toString() { result = "(..., ...)" } + + override AstNode getAChild(string pred) { pred = "getElement" and result = this.getElement(_) } +} diff --git a/ruby/ql/lib/codeql/ruby/ast/Scope.qll b/ruby/ql/lib/codeql/ruby/ast/Scope.qll new file mode 100644 index 00000000000..fe3b1b45cd1 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/Scope.qll @@ -0,0 +1,24 @@ +private import codeql.ruby.AST +private import internal.AST +private import internal.Scope +private import internal.TreeSitter + +class Scope extends AstNode, TScopeType { + private Scope::Range range; + + Scope() { range = toGenerated(this) } + + /** Gets the scope in which this scope is nested, if any. */ + Scope getOuterScope() { toGenerated(result) = range.getOuterScope() } + + /** Gets a variable that is declared in this scope. */ + final Variable getAVariable() { result.getDeclaringScope() = this } + + /** Gets the variable declared in this scope with the given name, if any. */ + final Variable getVariable(string name) { + result = this.getAVariable() and + result.getName() = name + } +} + +class SelfScope extends Scope, TSelfScopeType { } diff --git a/ruby/ql/lib/codeql/ruby/ast/Statement.qll b/ruby/ql/lib/codeql/ruby/ast/Statement.qll new file mode 100644 index 00000000000..feddd8fa3f0 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/Statement.qll @@ -0,0 +1,251 @@ +private import codeql.ruby.AST +private import codeql.ruby.CFG +private import internal.AST +private import internal.TreeSitter +private import internal.Variable +private import codeql.ruby.controlflow.internal.ControlFlowGraphImpl + +/** + * A statement. + * + * This is the root QL class for all statements. + */ +class Stmt extends AstNode, TStmt { + /** Gets a control-flow node for this statement, if any. */ + CfgNodes::AstCfgNode getAControlFlowNode() { result.getNode() = this } + + /** Gets a control-flow entry node for this statement, if any */ + AstNode getAControlFlowEntryNode() { result = getAControlFlowEntryNode(this) } + + /** Gets the control-flow scope of this statement, if any. */ + CfgScope getCfgScope() { result = getCfgScope(this) } + + /** Gets the enclosing callable, if any. */ + Callable getEnclosingCallable() { result = this.getCfgScope() } +} + +/** + * An empty statement (`;`). + */ +class EmptyStmt extends Stmt, TEmptyStmt { + final override string getAPrimaryQlClass() { result = "EmptyStmt" } + + final override string toString() { result = ";" } +} + +/** + * A `begin` statement. + * ```rb + * begin + * puts "hello world" + * end + * ``` + */ +class BeginExpr extends BodyStmt, TBeginExpr { + final override string getAPrimaryQlClass() { result = "BeginExpr" } + + final override string toString() { result = "begin ... " } +} + +/** + * A `BEGIN` block. + * ```rb + * BEGIN { puts "starting ..." } + * ``` + */ +class BeginBlock extends StmtSequence, TBeginBlock { + private Ruby::BeginBlock g; + + BeginBlock() { this = TBeginBlock(g) } + + final override string getAPrimaryQlClass() { result = "BeginBlock" } + + final override string toString() { result = "BEGIN { ... }" } + + final override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } +} + +/** + * An `END` block. + * ```rb + * END { puts "shutting down" } + * ``` + */ +class EndBlock extends StmtSequence, TEndBlock { + private Ruby::EndBlock g; + + EndBlock() { this = TEndBlock(g) } + + final override string getAPrimaryQlClass() { result = "EndBlock" } + + final override string toString() { result = "END { ... }" } + + final override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } +} + +/** + * An `undef` statement. For example: + * ```rb + * - undef method_name + * - undef &&, :method_name + * - undef :"method_#{ name }" + * ``` + */ +class UndefStmt extends Stmt, TUndefStmt { + private Ruby::Undef g; + + UndefStmt() { this = TUndefStmt(g) } + + /** Gets the `n`th method name to undefine. */ + final MethodName getMethodName(int n) { toGenerated(result) = g.getChild(n) } + + /** Gets a method name to undefine. */ + final MethodName getAMethodName() { result = this.getMethodName(_) } + + final override string getAPrimaryQlClass() { result = "UndefStmt" } + + final override string toString() { result = "undef ..." } + + final override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getMethodName" and result = this.getMethodName(_) + } +} + +/** + * An `alias` statement. For example: + * ```rb + * - alias alias_name method_name + * - alias foo :method_name + * - alias bar :"method_#{ name }" + * ``` + */ +class AliasStmt extends Stmt, TAliasStmt { + private Ruby::Alias g; + + AliasStmt() { this = TAliasStmt(g) } + + /** Gets the new method name. */ + final MethodName getNewName() { toGenerated(result) = g.getName() } + + /** Gets the original method name. */ + final MethodName getOldName() { toGenerated(result) = g.getAlias() } + + final override string getAPrimaryQlClass() { result = "AliasStmt" } + + final override string toString() { result = "alias ..." } + + final override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getNewName" and result = this.getNewName() + or + pred = "getOldName" and result = this.getOldName() + } +} + +/** + * A statement that may return a value: `return`, `break` and `next`. + * + * ```rb + * return + * return value + * break + * break value + * next + * next value + * ``` + */ +class ReturningStmt extends Stmt, TReturningStmt { + private Ruby::ArgumentList getArgumentList() { + result = any(Ruby::Return g | this = TReturnStmt(g)).getChild() + or + result = any(Ruby::Break g | this = TBreakStmt(g)).getChild() + or + result = any(Ruby::Next g | this = TNextStmt(g)).getChild() + } + + /** Gets the returned value, if any. */ + final Expr getValue() { + toGenerated(result) = + any(Ruby::AstNode res | + exists(Ruby::ArgumentList a, int c | + a = this.getArgumentList() and c = count(a.getChild(_)) + | + res = a.getChild(0) and c = 1 + or + res = a and c > 1 + ) + ) + } + + final override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "getValue" and result = this.getValue() + } +} + +/** + * A `return` statement. + * ```rb + * return + * return value + * ``` + */ +class ReturnStmt extends ReturningStmt, TReturnStmt { + final override string getAPrimaryQlClass() { result = "ReturnStmt" } + + final override string toString() { result = "return" } +} + +/** + * A `break` statement. + * ```rb + * break + * break value + * ``` + */ +class BreakStmt extends ReturningStmt, TBreakStmt { + final override string getAPrimaryQlClass() { result = "BreakStmt" } + + final override string toString() { result = "break" } +} + +/** + * A `next` statement. + * ```rb + * next + * next value + * ``` + */ +class NextStmt extends ReturningStmt, TNextStmt { + final override string getAPrimaryQlClass() { result = "NextStmt" } + + final override string toString() { result = "next" } +} + +/** + * A `redo` statement. + * ```rb + * redo + * ``` + */ +class RedoStmt extends Stmt, TRedoStmt { + final override string getAPrimaryQlClass() { result = "RedoStmt" } + + final override string toString() { result = "redo" } +} + +/** + * A `retry` statement. + * ```rb + * retry + * ``` + */ +class RetryStmt extends Stmt, TRetryStmt { + final override string getAPrimaryQlClass() { result = "RetryStmt" } + + final override string toString() { result = "retry" } +} diff --git a/ruby/ql/lib/codeql/ruby/ast/Variable.qll b/ruby/ql/lib/codeql/ruby/ast/Variable.qll new file mode 100644 index 00000000000..12be2f99f5b --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/Variable.qll @@ -0,0 +1,204 @@ +/** Provides classes for modeling program variables. */ + +private import codeql.ruby.AST +private import codeql.Locations +private import internal.AST +private import internal.TreeSitter +private import internal.Variable + +/** A variable declared in a scope. */ +class Variable instanceof VariableImpl { + /** Gets the name of this variable. */ + final string getName() { result = super.getNameImpl() } + + /** Holds if the name of this variable is `name`. */ + final predicate hasName(string name) { this.getName() = name } + + /** Gets a textual representation of this variable. */ + final string toString() { result = this.getName() } + + /** Gets the location of this variable. */ + final Location getLocation() { result = super.getLocationImpl() } + + /** Gets the scope this variable is declared in. */ + final Scope getDeclaringScope() { + toGenerated(result) = this.(VariableReal).getDeclaringScopeImpl() + } + + /** Gets an access to this variable. */ + VariableAccess getAnAccess() { result.getVariable() = this } +} + +/** A local variable. */ +class LocalVariable extends Variable, TLocalVariable { + override LocalVariableAccess getAnAccess() { result.getVariable() = this } + + /** Gets the access where this local variable is first introduced. */ + VariableAccess getDefiningAccess() { result = this.(LocalVariableReal).getDefiningAccessImpl() } + + /** + * Holds if this variable is captured. For example in + * + * ```rb + * def m x + * x.times do |y| + * puts x + * end + * puts x + * end + * ``` + * + * `x` is a captured variable, whereas `y` is not. + */ + predicate isCaptured() { this.getAnAccess().isCapturedAccess() } +} + +/** A global variable. */ +class GlobalVariable extends Variable instanceof GlobalVariableImpl { + final override GlobalVariableAccess getAnAccess() { result.getVariable() = this } +} + +/** An instance variable. */ +class InstanceVariable extends Variable instanceof InstanceVariableImpl { + /** Holds is this variable is a class instance variable. */ + final predicate isClassInstanceVariable() { super.isClassInstanceVariable() } + + final override InstanceVariableAccess getAnAccess() { result.getVariable() = this } +} + +/** A class variable. */ +class ClassVariable extends Variable instanceof ClassVariableImpl { + final override ClassVariableAccess getAnAccess() { result.getVariable() = this } +} + +/** A `self` variable. */ +class SelfVariable extends LocalVariable instanceof SelfVariableImpl { } + +/** An access to a variable. */ +class VariableAccess extends Expr instanceof VariableAccessImpl { + /** Gets the variable this identifier refers to. */ + final Variable getVariable() { result = super.getVariableImpl() } + + /** + * Holds if this access is a write access belonging to the explicit + * assignment `assignment`. For example, in + * + * ```rb + * a, b = foo + * ``` + * + * both `a` and `b` are write accesses belonging to the same assignment. + */ + predicate isExplicitWrite(AstNode assignment) { + explicitWriteAccess(toGenerated(this), toGenerated(assignment)) + or + this = assignment.(AssignExpr).getLeftOperand() + } + + /** + * Holds if this access is a write access belonging to an implicit assignment. + * For example, in + * + * ```rb + * def m elements + * for e in elements do + * puts e + * end + * end + * ``` + * + * the access to `elements` in the parameter list is an implicit assignment, + * as is the first access to `e`. + */ + predicate isImplicitWrite() { implicitWriteAccess(toGenerated(this)) } + + final override string toString() { result = VariableAccessImpl.super.toString() } +} + +/** An access to a variable where the value is updated. */ +class VariableWriteAccess extends VariableAccess { + VariableWriteAccess() { + this.isExplicitWrite(_) or + this.isImplicitWrite() + } +} + +/** An access to a variable where the value is read. */ +class VariableReadAccess extends VariableAccess { + VariableReadAccess() { not this instanceof VariableWriteAccess } +} + +/** An access to a local variable. */ +class LocalVariableAccess extends VariableAccess instanceof LocalVariableAccessImpl { + override string getAPrimaryQlClass() { result = "LocalVariableAccess" } + + /** + * Holds if this access is a captured variable access. For example in + * + * ```rb + * def m x + * x.times do |y| + * puts x + * end + * puts x + * end + * ``` + * + * the access to `x` in the first `puts x` is a captured access, while + * the access to `x` in the second `puts x` is not. + */ + predicate isCapturedAccess() { isCapturedAccess(this) } +} + +/** An access to a local variable where the value is updated. */ +class LocalVariableWriteAccess extends LocalVariableAccess, VariableWriteAccess { } + +/** An access to a local variable where the value is read. */ +class LocalVariableReadAccess extends LocalVariableAccess, VariableReadAccess { } + +/** An access to a global variable. */ +class GlobalVariableAccess extends VariableAccess instanceof GlobalVariableAccessImpl { + final override string getAPrimaryQlClass() { result = "GlobalVariableAccess" } +} + +/** An access to a global variable where the value is updated. */ +class GlobalVariableWriteAccess extends GlobalVariableAccess, VariableWriteAccess { } + +/** An access to a global variable where the value is read. */ +class GlobalVariableReadAccess extends GlobalVariableAccess, VariableReadAccess { } + +/** An access to an instance variable. */ +class InstanceVariableAccess extends VariableAccess instanceof InstanceVariableAccessImpl { + final override string getAPrimaryQlClass() { result = "InstanceVariableAccess" } +} + +/** An access to an instance variable where the value is updated. */ +class InstanceVariableWriteAccess extends InstanceVariableAccess, VariableWriteAccess { } + +/** An access to an instance variable where the value is read. */ +class InstanceVariableReadAccess extends InstanceVariableAccess, VariableReadAccess { } + +/** An access to a class variable. */ +class ClassVariableAccess extends VariableAccess instanceof ClassVariableAccessRealImpl { + final override string getAPrimaryQlClass() { result = "ClassVariableAccess" } +} + +/** An access to a class variable where the value is updated. */ +class ClassVariableWriteAccess extends ClassVariableAccess, VariableWriteAccess { } + +/** An access to a class variable where the value is read. */ +class ClassVariableReadAccess extends ClassVariableAccess, VariableReadAccess { } + +/** An access to the `self` variable */ +class SelfVariableAccess extends LocalVariableAccess instanceof SelfVariableAccessImpl { + final override string getAPrimaryQlClass() { result = "SelfVariableAccess" } +} + +/** An access to the `self` variable where the value is read. */ +class SelfVariableReadAccess extends SelfVariableAccess, VariableReadAccess { + // We override the definition in `LocalVariableAccess` because it gives the + // wrong result for synthesised `self` variables. + override predicate isCapturedAccess() { + this.getVariable().getDeclaringScope() != this.getCfgScope() + } +} diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/AST.qll b/ruby/ql/lib/codeql/ruby/ast/internal/AST.qll new file mode 100644 index 00000000000..11ca079c735 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/internal/AST.qll @@ -0,0 +1,710 @@ +import codeql.Locations +private import TreeSitter +private import codeql.ruby.ast.internal.Call +private import codeql.ruby.ast.internal.Parameter +private import codeql.ruby.ast.internal.Variable +private import codeql.ruby.AST as AST +private import Synthesis + +module MethodName { + predicate range(Ruby::UnderscoreMethodName g) { + exists(Ruby::Undef u | u.getChild(_) = g) + or + exists(Ruby::Alias a | a.getName() = g or a.getAlias() = g) + } + + class Token = + @ruby_setter or @ruby_token_class_variable or @ruby_token_constant or + @ruby_token_global_variable or @ruby_token_identifier or @ruby_token_instance_variable or + @ruby_token_operator; +} + +private predicate mkSynthChild(SynthKind kind, AST::AstNode parent, int i) { + any(Synthesis s).child(parent, i, SynthChild(kind)) +} + +cached +private module Cached { + cached + newtype TAstNode = + TAddExprReal(Ruby::Binary g) { g instanceof @ruby_binary_plus } or + TAddExprSynth(AST::AstNode parent, int i) { mkSynthChild(AddExprKind(), parent, i) } or + TAliasStmt(Ruby::Alias g) or + TArgumentList(Ruby::AstNode g) { + ( + g.getParent() instanceof Ruby::Break or + g.getParent() instanceof Ruby::Return or + g.getParent() instanceof Ruby::Next or + g.getParent() instanceof Ruby::Assignment or + g.getParent() instanceof Ruby::OperatorAssignment + ) and + ( + strictcount(g.(Ruby::ArgumentList).getChild(_)) > 1 + or + g instanceof Ruby::RightAssignmentList + ) + } or + TAssignAddExpr(Ruby::OperatorAssignment g) { g instanceof @ruby_operator_assignment_plusequal } or + TAssignBitwiseAndExpr(Ruby::OperatorAssignment g) { + g instanceof @ruby_operator_assignment_ampersandequal + } or + TAssignBitwiseOrExpr(Ruby::OperatorAssignment g) { + g instanceof @ruby_operator_assignment_pipeequal + } or + TAssignBitwiseXorExpr(Ruby::OperatorAssignment g) { + g instanceof @ruby_operator_assignment_caretequal + } or + TAssignDivExpr(Ruby::OperatorAssignment g) { g instanceof @ruby_operator_assignment_slashequal } or + TAssignExponentExpr(Ruby::OperatorAssignment g) { + g instanceof @ruby_operator_assignment_starstarequal + } or + TAssignExprReal(Ruby::Assignment g) or + TAssignExprSynth(AST::AstNode parent, int i) { mkSynthChild(AssignExprKind(), parent, i) } or + TAssignLShiftExpr(Ruby::OperatorAssignment g) { + g instanceof @ruby_operator_assignment_langlelangleequal + } or + TAssignLogicalAndExpr(Ruby::OperatorAssignment g) { + g instanceof @ruby_operator_assignment_ampersandampersandequal + } or + TAssignLogicalOrExpr(Ruby::OperatorAssignment g) { + g instanceof @ruby_operator_assignment_pipepipeequal + } or + TAssignModuloExpr(Ruby::OperatorAssignment g) { + g instanceof @ruby_operator_assignment_percentequal + } or + TAssignMulExpr(Ruby::OperatorAssignment g) { g instanceof @ruby_operator_assignment_starequal } or + TAssignRShiftExpr(Ruby::OperatorAssignment g) { + g instanceof @ruby_operator_assignment_ranglerangleequal + } or + TAssignSubExpr(Ruby::OperatorAssignment g) { g instanceof @ruby_operator_assignment_minusequal } or + TBareStringLiteral(Ruby::BareString g) or + TBareSymbolLiteral(Ruby::BareSymbol g) or + TBeginBlock(Ruby::BeginBlock g) or + TBeginExpr(Ruby::Begin g) or + TBitwiseAndExprReal(Ruby::Binary g) { g instanceof @ruby_binary_ampersand } or + TBitwiseAndExprSynth(AST::AstNode parent, int i) { + mkSynthChild(BitwiseAndExprKind(), parent, i) + } or + TBitwiseOrExprReal(Ruby::Binary g) { g instanceof @ruby_binary_pipe } or + TBitwiseOrExprSynth(AST::AstNode parent, int i) { mkSynthChild(BitwiseOrExprKind(), parent, i) } or + TBitwiseXorExprReal(Ruby::Binary g) { g instanceof @ruby_binary_caret } or + TBitwiseXorExprSynth(AST::AstNode parent, int i) { + mkSynthChild(BitwiseXorExprKind(), parent, i) + } or + TBlockArgument(Ruby::BlockArgument g) or + TBlockParameter(Ruby::BlockParameter g) or + TBraceBlock(Ruby::Block g) { not g.getParent() instanceof Ruby::Lambda } or + TBreakStmt(Ruby::Break g) or + TCaseEqExpr(Ruby::Binary g) { g instanceof @ruby_binary_equalequalequal } or + TCaseExpr(Ruby::Case g) or + TCharacterLiteral(Ruby::Character g) or + TClassDeclaration(Ruby::Class g) or + TClassVariableAccessReal(Ruby::ClassVariable g, AST::ClassVariable v) { + ClassVariableAccess::range(g, v) + } or + TClassVariableAccessSynth(AST::AstNode parent, int i, AST::ClassVariable v) { + mkSynthChild(ClassVariableAccessKind(v), parent, i) + } or + TComplementExpr(Ruby::Unary g) { g instanceof @ruby_unary_tilde } or + TComplexLiteral(Ruby::Complex g) or + TConstantReadAccessSynth(AST::AstNode parent, int i, string value) { + mkSynthChild(ConstantReadAccessKind(value), parent, i) + } or + TDefinedExpr(Ruby::Unary g) { g instanceof @ruby_unary_definedquestion } or + TDelimitedSymbolLiteral(Ruby::DelimitedSymbol g) or + TDestructuredLeftAssignment(Ruby::DestructuredLeftAssignment g) { + not strictcount(int i | exists(g.getParent().(Ruby::LeftAssignmentList).getChild(i))) = 1 + } or + TDivExprReal(Ruby::Binary g) { g instanceof @ruby_binary_slash } or + TDivExprSynth(AST::AstNode parent, int i) { mkSynthChild(DivExprKind(), parent, i) } or + TDo(Ruby::Do g) or + TDoBlock(Ruby::DoBlock g) { not g.getParent() instanceof Ruby::Lambda } or + TElementReference(Ruby::ElementReference g) or + TElse(Ruby::Else g) or + TElsif(Ruby::Elsif g) or + TEmptyStmt(Ruby::EmptyStatement g) or + TEndBlock(Ruby::EndBlock g) or + TEnsure(Ruby::Ensure g) or + TEqExpr(Ruby::Binary g) { g instanceof @ruby_binary_equalequal } or + TExponentExprReal(Ruby::Binary g) { g instanceof @ruby_binary_starstar } or + TExponentExprSynth(AST::AstNode parent, int i) { mkSynthChild(ExponentExprKind(), parent, i) } or + TFalseLiteral(Ruby::False g) or + TFloatLiteral(Ruby::Float g) { not any(Ruby::Rational r).getChild() = g } or + TForExpr(Ruby::For g) or + TForIn(Ruby::In g) or // TODO REMOVE + TForwardParameter(Ruby::ForwardParameter g) or + TForwardArgument(Ruby::ForwardArgument g) or + TGEExpr(Ruby::Binary g) { g instanceof @ruby_binary_rangleequal } or + TGTExpr(Ruby::Binary g) { g instanceof @ruby_binary_rangle } or + TGlobalVariableAccessReal(Ruby::GlobalVariable g, AST::GlobalVariable v) { + GlobalVariableAccess::range(g, v) + } or + TGlobalVariableAccessSynth(AST::AstNode parent, int i, AST::GlobalVariable v) { + mkSynthChild(GlobalVariableAccessKind(v), parent, i) + } or + THashKeySymbolLiteral(Ruby::HashKeySymbol g) or + THashLiteral(Ruby::Hash g) or + THashSplatExpr(Ruby::HashSplatArgument g) or + THashSplatParameter(Ruby::HashSplatParameter g) or + THereDoc(Ruby::HeredocBeginning g) or + TIdentifierMethodCall(Ruby::Identifier g) { isIdentifierMethodCall(g) } or + TIf(Ruby::If g) or + TIfModifierExpr(Ruby::IfModifier g) or + TInstanceVariableAccessReal(Ruby::InstanceVariable g, AST::InstanceVariable v) { + InstanceVariableAccess::range(g, v) + } or + TInstanceVariableAccessSynth(AST::AstNode parent, int i, AST::InstanceVariable v) { + mkSynthChild(InstanceVariableAccessKind(v), parent, i) + } or + TIntegerLiteralReal(Ruby::Integer g) { not any(Ruby::Rational r).getChild() = g } or + TIntegerLiteralSynth(AST::AstNode parent, int i, int value) { + mkSynthChild(IntegerLiteralKind(value), parent, i) + } or + TKeywordParameter(Ruby::KeywordParameter g) or + TLEExpr(Ruby::Binary g) { g instanceof @ruby_binary_langleequal } or + TLShiftExprReal(Ruby::Binary g) { g instanceof @ruby_binary_langlelangle } or + TLShiftExprSynth(AST::AstNode parent, int i) { mkSynthChild(LShiftExprKind(), parent, i) } or + TLTExpr(Ruby::Binary g) { g instanceof @ruby_binary_langle } or + TLambda(Ruby::Lambda g) or + TLeftAssignmentList(Ruby::LeftAssignmentList g) or + TLocalVariableAccessReal(Ruby::Identifier g, AST::LocalVariable v) { + LocalVariableAccess::range(g, v) + } or + TLocalVariableAccessSynth(AST::AstNode parent, int i, AST::LocalVariable v) { + mkSynthChild(LocalVariableAccessRealKind(v), parent, i) + or + mkSynthChild(LocalVariableAccessSynthKind(v), parent, i) + } or + TLogicalAndExprReal(Ruby::Binary g) { + g instanceof @ruby_binary_and or g instanceof @ruby_binary_ampersandampersand + } or + TLogicalAndExprSynth(AST::AstNode parent, int i) { + mkSynthChild(LogicalAndExprKind(), parent, i) + } or + TLogicalOrExprReal(Ruby::Binary g) { + g instanceof @ruby_binary_or or g instanceof @ruby_binary_pipepipe + } or + TLogicalOrExprSynth(AST::AstNode parent, int i) { mkSynthChild(LogicalOrExprKind(), parent, i) } or + TMethod(Ruby::Method g) or + TMethodCallSynth(AST::AstNode parent, int i, string name, boolean setter, int arity) { + mkSynthChild(MethodCallKind(name, setter, arity), parent, i) + } or + TModuleDeclaration(Ruby::Module g) or + TModuloExprReal(Ruby::Binary g) { g instanceof @ruby_binary_percent } or + TModuloExprSynth(AST::AstNode parent, int i) { mkSynthChild(ModuloExprKind(), parent, i) } or + TMulExprReal(Ruby::Binary g) { g instanceof @ruby_binary_star } or + TMulExprSynth(AST::AstNode parent, int i) { mkSynthChild(MulExprKind(), parent, i) } or + TNEExpr(Ruby::Binary g) { g instanceof @ruby_binary_bangequal } or + TNextStmt(Ruby::Next g) or + TNilLiteral(Ruby::Nil g) or + TNoRegExpMatchExpr(Ruby::Binary g) { g instanceof @ruby_binary_bangtilde } or + TNotExpr(Ruby::Unary g) { g instanceof @ruby_unary_bang or g instanceof @ruby_unary_not } or + TOptionalParameter(Ruby::OptionalParameter g) or + TPair(Ruby::Pair g) or + TParenthesizedExpr(Ruby::ParenthesizedStatements g) or + TRShiftExprReal(Ruby::Binary g) { g instanceof @ruby_binary_ranglerangle } or + TRShiftExprSynth(AST::AstNode parent, int i) { mkSynthChild(RShiftExprKind(), parent, i) } or + TRangeLiteralReal(Ruby::Range g) or + TRangeLiteralSynth(AST::AstNode parent, int i, boolean inclusive) { + mkSynthChild(RangeLiteralKind(inclusive), parent, i) + } or + TRationalLiteral(Ruby::Rational g) or + TRedoStmt(Ruby::Redo g) or + TRegExpLiteral(Ruby::Regex g) or + TRegExpMatchExpr(Ruby::Binary g) { g instanceof @ruby_binary_equaltilde } or + TRegularArrayLiteral(Ruby::Array g) or + TRegularMethodCall(Ruby::Call g) { isRegularMethodCall(g) } or + TRegularStringLiteral(Ruby::String g) or + TRegularSuperCall(Ruby::Call g) { g.getMethod() instanceof Ruby::Super } or + TRescueClause(Ruby::Rescue g) or + TRescueModifierExpr(Ruby::RescueModifier g) or + TRetryStmt(Ruby::Retry g) or + TReturnStmt(Ruby::Return g) or + TScopeResolutionConstantAccess(Ruby::ScopeResolution g, Ruby::Constant constant) { + constant = g.getName() and + ( + // A tree-sitter `scope_resolution` node with a `constant` name field is a + // read of that constant in any context where an identifier would be a + // vcall. + vcall(g) + or + explicitAssignmentNode(g, _) + ) + } or + TScopeResolutionMethodCall(Ruby::ScopeResolution g, Ruby::Identifier i) { + isScopeResolutionMethodCall(g, i) + } or + TSelfReal(Ruby::Self g) or + TSelfSynth(AST::AstNode parent, int i, AST::SelfVariable v) { + mkSynthChild(SelfKind(v), parent, i) + } or + TSimpleParameter(Ruby::Identifier g) { g instanceof Parameter::Range } or + TSimpleSymbolLiteral(Ruby::SimpleSymbol g) or + TSingletonClass(Ruby::SingletonClass g) or + TSingletonMethod(Ruby::SingletonMethod g) or + TSpaceshipExpr(Ruby::Binary g) { g instanceof @ruby_binary_langleequalrangle } or + TSplatExprReal(Ruby::SplatArgument g) or + TSplatExprSynth(AST::AstNode parent, int i) { mkSynthChild(SplatExprKind(), parent, i) } or + TSplatParameter(Ruby::SplatParameter g) or + TStmtSequenceSynth(AST::AstNode parent, int i) { mkSynthChild(StmtSequenceKind(), parent, i) } or + TStringArrayLiteral(Ruby::StringArray g) or + TStringConcatenation(Ruby::ChainedString g) or + TStringEscapeSequenceComponent(Ruby::EscapeSequence g) or + TStringInterpolationComponent(Ruby::Interpolation g) or + TStringTextComponent(Ruby::Token g) { + g instanceof Ruby::StringContent or g instanceof Ruby::HeredocContent + } or + TSubExprReal(Ruby::Binary g) { g instanceof @ruby_binary_minus } or + TSubExprSynth(AST::AstNode parent, int i) { mkSynthChild(SubExprKind(), parent, i) } or + TSubshellLiteral(Ruby::Subshell g) or + TSymbolArrayLiteral(Ruby::SymbolArray g) or + TTernaryIfExpr(Ruby::Conditional g) or + TThen(Ruby::Then g) or + TTokenConstantAccess(Ruby::Constant g) { + // A tree-sitter `constant` token is a read of that constant in any context + // where an identifier would be a vcall. + vcall(g) + or + explicitAssignmentNode(g, _) + } or + TTokenMethodName(MethodName::Token g) { MethodName::range(g) } or + TTokenSuperCall(Ruby::Super g) { vcall(g) } or + TToplevel(Ruby::Program g) or + TTrueLiteral(Ruby::True g) or + TTuplePatternParameter(Ruby::DestructuredParameter g) or + TUnaryMinusExpr(Ruby::Unary g) { g instanceof @ruby_unary_minus } or + TUnaryPlusExpr(Ruby::Unary g) { g instanceof @ruby_unary_plus } or + TUndefStmt(Ruby::Undef g) or + TUnlessExpr(Ruby::Unless g) or + TUnlessModifierExpr(Ruby::UnlessModifier g) or + TUntilExpr(Ruby::Until g) or + TUntilModifierExpr(Ruby::UntilModifier g) or + TWhenExpr(Ruby::When g) or + TWhileExpr(Ruby::While g) or + TWhileModifierExpr(Ruby::WhileModifier g) or + TYieldCall(Ruby::Yield g) + + /** + * Gets the underlying TreeSitter entity for a given AST node. This does not + * include synthesized AST nodes, because they are not the primary AST node + * for any given generated node. + */ + cached + Ruby::AstNode toGenerated(AST::AstNode n) { + n = TAddExprReal(result) or + n = TAliasStmt(result) or + n = TArgumentList(result) or + n = TAssignAddExpr(result) or + n = TAssignBitwiseAndExpr(result) or + n = TAssignBitwiseOrExpr(result) or + n = TAssignBitwiseXorExpr(result) or + n = TAssignDivExpr(result) or + n = TAssignExponentExpr(result) or + n = TAssignExprReal(result) or + n = TAssignLShiftExpr(result) or + n = TAssignLogicalAndExpr(result) or + n = TAssignLogicalOrExpr(result) or + n = TAssignModuloExpr(result) or + n = TAssignMulExpr(result) or + n = TAssignRShiftExpr(result) or + n = TAssignSubExpr(result) or + n = TBareStringLiteral(result) or + n = TBareSymbolLiteral(result) or + n = TBeginBlock(result) or + n = TBeginExpr(result) or + n = TBitwiseAndExprReal(result) or + n = TBitwiseOrExprReal(result) or + n = TBitwiseXorExprReal(result) or + n = TBlockArgument(result) or + n = TBlockParameter(result) or + n = TBraceBlock(result) or + n = TBreakStmt(result) or + n = TCaseEqExpr(result) or + n = TCaseExpr(result) or + n = TCharacterLiteral(result) or + n = TClassDeclaration(result) or + n = TClassVariableAccessReal(result, _) or + n = TComplementExpr(result) or + n = TComplexLiteral(result) or + n = TDefinedExpr(result) or + n = TDelimitedSymbolLiteral(result) or + n = TDestructuredLeftAssignment(result) or + n = TDivExprReal(result) or + n = TDo(result) or + n = TDoBlock(result) or + n = TElementReference(result) or + n = TElse(result) or + n = TElsif(result) or + n = TEmptyStmt(result) or + n = TEndBlock(result) or + n = TEnsure(result) or + n = TEqExpr(result) or + n = TExponentExprReal(result) or + n = TFalseLiteral(result) or + n = TFloatLiteral(result) or + n = TForExpr(result) or + n = TForIn(result) or // TODO REMOVE + n = TForwardArgument(result) or + n = TForwardParameter(result) or + n = TGEExpr(result) or + n = TGTExpr(result) or + n = TGlobalVariableAccessReal(result, _) or + n = THashKeySymbolLiteral(result) or + n = THashLiteral(result) or + n = THashSplatExpr(result) or + n = THashSplatParameter(result) or + n = THereDoc(result) or + n = TIdentifierMethodCall(result) or + n = TIf(result) or + n = TIfModifierExpr(result) or + n = TInstanceVariableAccessReal(result, _) or + n = TIntegerLiteralReal(result) or + n = TKeywordParameter(result) or + n = TLEExpr(result) or + n = TLShiftExprReal(result) or + n = TLTExpr(result) or + n = TLambda(result) or + n = TLeftAssignmentList(result) or + n = TLocalVariableAccessReal(result, _) or + n = TLogicalAndExprReal(result) or + n = TLogicalOrExprReal(result) or + n = TMethod(result) or + n = TModuleDeclaration(result) or + n = TModuloExprReal(result) or + n = TMulExprReal(result) or + n = TNEExpr(result) or + n = TNextStmt(result) or + n = TNilLiteral(result) or + n = TNoRegExpMatchExpr(result) or + n = TNotExpr(result) or + n = TOptionalParameter(result) or + n = TPair(result) or + n = TParenthesizedExpr(result) or + n = TRShiftExprReal(result) or + n = TRangeLiteralReal(result) or + n = TRationalLiteral(result) or + n = TRedoStmt(result) or + n = TRegExpLiteral(result) or + n = TRegExpMatchExpr(result) or + n = TRegularArrayLiteral(result) or + n = TRegularMethodCall(result) or + n = TRegularStringLiteral(result) or + n = TRegularSuperCall(result) or + n = TRescueClause(result) or + n = TRescueModifierExpr(result) or + n = TRetryStmt(result) or + n = TReturnStmt(result) or + n = TScopeResolutionConstantAccess(result, _) or + n = TScopeResolutionMethodCall(result, _) or + n = TSelfReal(result) or + n = TSimpleParameter(result) or + n = TSimpleSymbolLiteral(result) or + n = TSingletonClass(result) or + n = TSingletonMethod(result) or + n = TSpaceshipExpr(result) or + n = TSplatExprReal(result) or + n = TSplatParameter(result) or + n = TStringArrayLiteral(result) or + n = TStringConcatenation(result) or + n = TStringEscapeSequenceComponent(result) or + n = TStringInterpolationComponent(result) or + n = TStringTextComponent(result) or + n = TSubExprReal(result) or + n = TSubshellLiteral(result) or + n = TSymbolArrayLiteral(result) or + n = TTernaryIfExpr(result) or + n = TThen(result) or + n = TTokenConstantAccess(result) or + n = TTokenMethodName(result) or + n = TTokenSuperCall(result) or + n = TToplevel(result) or + n = TTrueLiteral(result) or + n = TTuplePatternParameter(result) or + n = TUnaryMinusExpr(result) or + n = TUnaryPlusExpr(result) or + n = TUndefStmt(result) or + n = TUnlessExpr(result) or + n = TUnlessModifierExpr(result) or + n = TUntilExpr(result) or + n = TUntilModifierExpr(result) or + n = TWhenExpr(result) or + n = TWhileExpr(result) or + n = TWhileModifierExpr(result) or + n = TYieldCall(result) + } + + /** Gets the `i`th synthesized child of `parent`. */ + cached + AST::AstNode getSynthChild(AST::AstNode parent, int i) { + result = TAddExprSynth(parent, i) + or + result = TAssignExprSynth(parent, i) + or + result = TBitwiseAndExprSynth(parent, i) + or + result = TBitwiseOrExprSynth(parent, i) + or + result = TBitwiseXorExprSynth(parent, i) + or + result = TClassVariableAccessSynth(parent, i, _) + or + result = TConstantReadAccessSynth(parent, i, _) + or + result = TDivExprSynth(parent, i) + or + result = TExponentExprSynth(parent, i) + or + result = TGlobalVariableAccessSynth(parent, i, _) + or + result = TInstanceVariableAccessSynth(parent, i, _) + or + result = TIntegerLiteralSynth(parent, i, _) + or + result = TLShiftExprSynth(parent, i) + or + result = TLocalVariableAccessSynth(parent, i, _) + or + result = TLogicalAndExprSynth(parent, i) + or + result = TLogicalOrExprSynth(parent, i) + or + result = TMethodCallSynth(parent, i, _, _, _) + or + result = TModuloExprSynth(parent, i) + or + result = TMulExprSynth(parent, i) + or + result = TRangeLiteralSynth(parent, i, _) + or + result = TRShiftExprSynth(parent, i) + or + result = TSelfSynth(parent, i, _) + or + result = TSplatExprSynth(parent, i) + or + result = TStmtSequenceSynth(parent, i) + or + result = TSubExprSynth(parent, i) + } + + /** + * Holds if the `i`th child of `parent` is `child`. Either `parent` or + * `child` (or both) is a synthesized node. + */ + cached + predicate synthChild(AST::AstNode parent, int i, AST::AstNode child) { + child = getSynthChild(parent, i) + or + any(Synthesis s).child(parent, i, RealChild(child)) + } + + /** + * Like `toGenerated`, but also returns generated nodes for synthesized AST + * nodes. + */ + cached + Ruby::AstNode toGeneratedInclSynth(AST::AstNode n) { + result = toGenerated(n) + or + not exists(toGenerated(n)) and + exists(AST::AstNode parent | + synthChild(parent, _, n) and + result = toGeneratedInclSynth(parent) + ) + } + + cached + Location getLocation(AST::AstNode n) { + synthLocation(n, result) + or + n.isSynthesized() and + not synthLocation(n, _) and + result = getLocation(n.getParent()) + or + result = toGenerated(n).getLocation() + } +} + +import Cached + +TAstNode fromGenerated(Ruby::AstNode n) { n = toGenerated(result) } + +class TCall = TMethodCall or TYieldCall; + +class TMethodCall = + TMethodCallSynth or TIdentifierMethodCall or TScopeResolutionMethodCall or TRegularMethodCall or + TElementReference or TSuperCall or TUnaryOperation or TBinaryOperation; + +class TSuperCall = TTokenSuperCall or TRegularSuperCall; + +class TConstantAccess = + TTokenConstantAccess or TScopeResolutionConstantAccess or TNamespace or TConstantReadAccessSynth; + +class TControlExpr = TConditionalExpr or TCaseExpr or TLoop; + +class TConditionalExpr = + TIfExpr or TUnlessExpr or TIfModifierExpr or TUnlessModifierExpr or TTernaryIfExpr; + +class TIfExpr = TIf or TElsif; + +class TConditionalLoop = TWhileExpr or TUntilExpr or TWhileModifierExpr or TUntilModifierExpr; + +class TLoop = TConditionalLoop or TForExpr; + +class TSelf = TSelfReal or TSelfSynth; + +class TExpr = + TSelf or TArgumentList or TRescueClause or TRescueModifierExpr or TPair or TStringConcatenation or + TCall or TBlockArgument or TConstantAccess or TControlExpr or TWhenExpr or TLiteral or + TCallable or TVariableAccess or TStmtSequence or TOperation or TSimpleParameter or + TForwardArgument; + +class TSplatExpr = TSplatExprReal or TSplatExprSynth; + +class TStmtSequence = + TBeginBlock or TEndBlock or TThen or TElse or TDo or TEnsure or TStringInterpolationComponent or + TBlock or TBodyStmt or TParenthesizedExpr or TStmtSequenceSynth; + +class TBodyStmt = TBeginExpr or TModuleBase or TMethod or TLambda or TDoBlock or TSingletonMethod; + +class TLiteral = + TNumericLiteral or TNilLiteral or TBooleanLiteral or TStringlikeLiteral or TCharacterLiteral or + TArrayLiteral or THashLiteral or TRangeLiteral or TTokenMethodName; + +class TNumericLiteral = TIntegerLiteral or TFloatLiteral or TRationalLiteral or TComplexLiteral; + +class TIntegerLiteral = TIntegerLiteralReal or TIntegerLiteralSynth; + +class TBooleanLiteral = TTrueLiteral or TFalseLiteral; + +class TStringComponent = + TStringTextComponent or TStringEscapeSequenceComponent or TStringInterpolationComponent; + +class TStringlikeLiteral = + TStringLiteral or TRegExpLiteral or TSymbolLiteral or TSubshellLiteral or THereDoc; + +class TStringLiteral = TRegularStringLiteral or TBareStringLiteral; + +class TSymbolLiteral = TSimpleSymbolLiteral or TComplexSymbolLiteral or THashKeySymbolLiteral; + +class TComplexSymbolLiteral = TDelimitedSymbolLiteral or TBareSymbolLiteral; + +class TArrayLiteral = TRegularArrayLiteral or TStringArrayLiteral or TSymbolArrayLiteral; + +class TCallable = TMethodBase or TLambda or TBlock; + +class TMethodBase = TMethod or TSingletonMethod; + +class TBlock = TDoBlock or TBraceBlock; + +class TModuleBase = TToplevel or TNamespace or TSingletonClass; + +class TNamespace = TClassDeclaration or TModuleDeclaration; + +class TOperation = TUnaryOperation or TBinaryOperation or TAssignment; + +class TUnaryOperation = + TUnaryLogicalOperation or TUnaryArithmeticOperation or TUnaryBitwiseOperation or TDefinedExpr or + TSplatExpr or THashSplatExpr; + +class TUnaryLogicalOperation = TNotExpr; + +class TUnaryArithmeticOperation = TUnaryPlusExpr or TUnaryMinusExpr; + +class TUnaryBitwiseOperation = TComplementExpr; + +class TBinaryOperation = + TBinaryArithmeticOperation or TBinaryLogicalOperation or TBinaryBitwiseOperation or + TComparisonOperation or TSpaceshipExpr or TRegExpMatchExpr or TNoRegExpMatchExpr; + +class TBinaryArithmeticOperation = + TAddExpr or TSubExpr or TMulExpr or TDivExpr or TModuloExpr or TExponentExpr; + +class TAddExpr = TAddExprReal or TAddExprSynth; + +class TSubExpr = TSubExprReal or TSubExprSynth; + +class TMulExpr = TMulExprReal or TMulExprSynth; + +class TDivExpr = TDivExprReal or TDivExprSynth; + +class TModuloExpr = TModuloExprReal or TModuloExprSynth; + +class TExponentExpr = TExponentExprReal or TExponentExprSynth; + +class TBinaryLogicalOperation = TLogicalAndExpr or TLogicalOrExpr; + +class TLogicalAndExpr = TLogicalAndExprReal or TLogicalAndExprSynth; + +class TLogicalOrExpr = TLogicalOrExprReal or TLogicalOrExprSynth; + +class TBinaryBitwiseOperation = + TLShiftExpr or TRShiftExpr or TBitwiseAndExpr or TBitwiseOrExpr or TBitwiseXorExpr; + +class TLShiftExpr = TLShiftExprReal or TLShiftExprSynth; + +class TRangeLiteral = TRangeLiteralReal or TRangeLiteralSynth; + +class TRShiftExpr = TRShiftExprReal or TRShiftExprSynth; + +class TBitwiseAndExpr = TBitwiseAndExprReal or TBitwiseAndExprSynth; + +class TBitwiseOrExpr = TBitwiseOrExprReal or TBitwiseOrExprSynth; + +class TBitwiseXorExpr = TBitwiseXorExprReal or TBitwiseXorExprSynth; + +class TComparisonOperation = TEqualityOperation or TRelationalOperation; + +class TEqualityOperation = TEqExpr or TNEExpr or TCaseEqExpr; + +class TRelationalOperation = TGTExpr or TGEExpr or TLTExpr or TLEExpr; + +class TAssignExpr = TAssignExprReal or TAssignExprSynth; + +class TAssignment = TAssignExpr or TAssignOperation; + +class TAssignOperation = + TAssignArithmeticOperation or TAssignLogicalOperation or TAssignBitwiseOperation; + +class TAssignArithmeticOperation = + TAssignAddExpr or TAssignSubExpr or TAssignMulExpr or TAssignDivExpr or TAssignModuloExpr or + TAssignExponentExpr; + +class TAssignLogicalOperation = TAssignLogicalAndExpr or TAssignLogicalOrExpr; + +class TAssignBitwiseOperation = + TAssignLShiftExpr or TAssignRShiftExpr or TAssignBitwiseAndExpr or TAssignBitwiseOrExpr or + TAssignBitwiseXorExpr; + +class TStmt = + TEmptyStmt or TBodyStmt or TStmtSequence or TUndefStmt or TAliasStmt or TReturningStmt or + TRedoStmt or TRetryStmt or TExpr; + +class TReturningStmt = TReturnStmt or TBreakStmt or TNextStmt; + +class TParameter = + TPatternParameter or TBlockParameter or THashSplatParameter or TKeywordParameter or + TOptionalParameter or TSplatParameter or TForwardParameter; + +class TPatternParameter = TSimpleParameter or TTuplePatternParameter; + +class TNamedParameter = + TSimpleParameter or TBlockParameter or THashSplatParameter or TKeywordParameter or + TOptionalParameter or TSplatParameter; + +class TTuplePattern = TTuplePatternParameter or TDestructuredLeftAssignment or TLeftAssignmentList; + +class TVariableAccess = + TLocalVariableAccess or TGlobalVariableAccess or TInstanceVariableAccess or + TClassVariableAccess or TSelfVariableAccess; + +class TLocalVariableAccess = + TLocalVariableAccessReal or TLocalVariableAccessSynth or TSelfVariableAccess; + +class TGlobalVariableAccess = TGlobalVariableAccessReal or TGlobalVariableAccessSynth; + +class TInstanceVariableAccess = TInstanceVariableAccessReal or TInstanceVariableAccessSynth; + +class TClassVariableAccess = TClassVariableAccessReal or TClassVariableAccessSynth; + +class TSelfVariableAccess = TSelfReal or TSelfSynth; diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Call.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Call.qll new file mode 100644 index 00000000000..eea4392a4fb --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Call.qll @@ -0,0 +1,186 @@ +private import TreeSitter +private import Variable +private import codeql.ruby.AST +private import codeql.ruby.ast.internal.AST + +predicate isIdentifierMethodCall(Ruby::Identifier g) { vcall(g) and not access(g, _) } + +predicate isRegularMethodCall(Ruby::Call g) { not g.getMethod() instanceof Ruby::Super } + +predicate isScopeResolutionMethodCall(Ruby::ScopeResolution g, Ruby::Identifier i) { + i = g.getName() and + not exists(Ruby::Call c | c.getMethod() = g) +} + +abstract class CallImpl extends Expr, TCall { + abstract AstNode getArgumentImpl(int n); + + /** + * It is not possible to define this predicate as + * + * ```ql + * result = count(this.getArgumentImpl(_)) + * ``` + * + * since that will result in a non-monotonicity error. + */ + abstract int getNumberOfArgumentsImpl(); +} + +abstract class MethodCallImpl extends CallImpl, TMethodCall { + abstract AstNode getReceiverImpl(); + + abstract string getMethodNameImpl(); + + abstract Block getBlockImpl(); +} + +class MethodCallSynth extends MethodCallImpl, TMethodCallSynth { + final override string getMethodNameImpl() { + exists(boolean setter, string name | this = TMethodCallSynth(_, _, name, setter, _) | + setter = true and result = name + "=" + or + setter = false and result = name + ) + } + + final override AstNode getReceiverImpl() { synthChild(this, 0, result) } + + final override AstNode getArgumentImpl(int n) { synthChild(this, n + 1, result) and n >= 0 } + + final override int getNumberOfArgumentsImpl() { this = TMethodCallSynth(_, _, _, _, result) } + + final override Block getBlockImpl() { none() } +} + +class IdentifierMethodCall extends MethodCallImpl, TIdentifierMethodCall { + private Ruby::Identifier g; + + IdentifierMethodCall() { this = TIdentifierMethodCall(g) } + + final override string getMethodNameImpl() { result = g.getValue() } + + final override AstNode getReceiverImpl() { result = TSelfSynth(this, 0, _) } + + final override Expr getArgumentImpl(int n) { none() } + + final override int getNumberOfArgumentsImpl() { result = 0 } + + final override Block getBlockImpl() { none() } +} + +class ScopeResolutionMethodCall extends MethodCallImpl, TScopeResolutionMethodCall { + private Ruby::ScopeResolution g; + private Ruby::Identifier i; + + ScopeResolutionMethodCall() { this = TScopeResolutionMethodCall(g, i) } + + final override string getMethodNameImpl() { result = i.getValue() } + + final override Expr getReceiverImpl() { toGenerated(result) = g.getScope() } + + final override Expr getArgumentImpl(int n) { none() } + + final override int getNumberOfArgumentsImpl() { result = 0 } + + final override Block getBlockImpl() { none() } +} + +class RegularMethodCall extends MethodCallImpl, TRegularMethodCall { + private Ruby::Call g; + + RegularMethodCall() { this = TRegularMethodCall(g) } + + final override Expr getReceiverImpl() { + toGenerated(result) = g.getReceiver() + or + not exists(g.getReceiver()) and + toGenerated(result) = g.getMethod().(Ruby::ScopeResolution).getScope() + or + result = TSelfSynth(this, 0, _) + } + + final override string getMethodNameImpl() { + isRegularMethodCall(g) and + ( + result = "call" and g.getMethod() instanceof Ruby::ArgumentList + or + result = g.getMethod().(Ruby::Token).getValue() + or + result = g.getMethod().(Ruby::ScopeResolution).getName().(Ruby::Token).getValue() + ) + } + + final override Expr getArgumentImpl(int n) { + toGenerated(result) = g.getArguments().getChild(n) + or + toGenerated(result) = g.getMethod().(Ruby::ArgumentList).getChild(n) + } + + final override int getNumberOfArgumentsImpl() { + result = + count(g.getArguments().getChild(_)) + count(g.getMethod().(Ruby::ArgumentList).getChild(_)) + } + + final override Block getBlockImpl() { toGenerated(result) = g.getBlock() } +} + +class ElementReferenceImpl extends MethodCallImpl, TElementReference { + private Ruby::ElementReference g; + + ElementReferenceImpl() { this = TElementReference(g) } + + final override Expr getReceiverImpl() { toGenerated(result) = g.getObject() } + + final override Expr getArgumentImpl(int n) { toGenerated(result) = g.getChild(n) } + + final override int getNumberOfArgumentsImpl() { result = count(g.getChild(_)) } + + final override string getMethodNameImpl() { result = "[]" } + + final override Block getBlockImpl() { none() } +} + +abstract class SuperCallImpl extends MethodCallImpl, TSuperCall { } + +class TokenSuperCall extends SuperCallImpl, TTokenSuperCall { + private Ruby::Super g; + + TokenSuperCall() { this = TTokenSuperCall(g) } + + final override string getMethodNameImpl() { result = g.getValue() } + + final override Expr getReceiverImpl() { none() } + + final override Expr getArgumentImpl(int n) { none() } + + final override int getNumberOfArgumentsImpl() { result = 0 } + + final override Block getBlockImpl() { none() } +} + +class RegularSuperCall extends SuperCallImpl, TRegularSuperCall { + private Ruby::Call g; + + RegularSuperCall() { this = TRegularSuperCall(g) } + + final override string getMethodNameImpl() { result = g.getMethod().(Ruby::Super).getValue() } + + final override Expr getReceiverImpl() { none() } + + final override Expr getArgumentImpl(int n) { toGenerated(result) = g.getArguments().getChild(n) } + + final override int getNumberOfArgumentsImpl() { result = count(g.getArguments().getChild(_)) } + + final override Block getBlockImpl() { toGenerated(result) = g.getBlock() } +} + +class YieldCallImpl extends CallImpl, TYieldCall { + Ruby::Yield g; + + YieldCallImpl() { this = TYieldCall(g) } + + final override Expr getArgumentImpl(int n) { toGenerated(result) = g.getChild().getChild(n) } + + final override int getNumberOfArgumentsImpl() { result = count(g.getChild().getChild(_)) } +} diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Erb.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Erb.qll new file mode 100644 index 00000000000..7a69bf5b783 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Erb.qll @@ -0,0 +1,43 @@ +import codeql.Locations +private import TreeSitter +private import codeql.ruby.ast.Erb + +cached +private module Cached { + cached + newtype TAstNode = + TCommentDirective(Erb::CommentDirective g) or + TDirective(Erb::Directive g) or + TGraphqlDirective(Erb::GraphqlDirective g) or + TOutputDirective(Erb::OutputDirective g) or + TTemplate(Erb::Template g) or + TToken(Erb::Token g) or + TComment(Erb::Comment g) or + TCode(Erb::Code g) + + /** + * Gets the underlying TreeSitter entity for a given erb AST node. + */ + cached + Erb::AstNode toGenerated(ErbAstNode n) { + n = TCommentDirective(result) or + n = TDirective(result) or + n = TGraphqlDirective(result) or + n = TOutputDirective(result) or + n = TTemplate(result) or + n = TToken(result) or + n = TComment(result) or + n = TCode(result) + } + + cached + Location getLocation(ErbAstNode n) { result = toGenerated(n).getLocation() } +} + +import Cached + +TAstNode fromGenerated(Erb::AstNode n) { n = toGenerated(result) } + +class TDirectiveNode = TCommentDirective or TDirective or TGraphqlDirective or TOutputDirective; + +class TTokenNode = TToken or TComment or TCode; diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Module.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Module.qll new file mode 100644 index 00000000000..247573b59e5 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Module.qll @@ -0,0 +1,409 @@ +private import codeql.Locations +private import codeql.ruby.AST +private import codeql.ruby.ast.Call +private import codeql.ruby.ast.Constant +private import codeql.ruby.ast.Expr +private import codeql.ruby.ast.Module +private import codeql.ruby.ast.Operation +private import codeql.ruby.ast.Scope + +// Names of built-in modules and classes +private string builtin() { + result = + [ + "Object", "Kernel", "BasicObject", "Class", "Module", "NilClass", "FalseClass", "TrueClass", + "Numeric", "Integer", "Float", "Rational", "Complex", "Array", "Hash", "Symbol", "Proc" + ] +} + +cached +private module Cached { + cached + newtype TModule = + TResolved(string qName) { + qName = builtin() + or + qName = namespaceDeclaration(_) + } or + TUnresolved(Namespace n) { not exists(namespaceDeclaration(n)) } + + cached + string namespaceDeclaration(Namespace n) { + isToplevel(n) and result = n.getName() + or + not isToplevel(n) and + not exists(n.getScopeExpr()) and + result = scopeAppend(namespaceDeclaration(n.getEnclosingModule()), n.getName()) + or + exists(string container | + TResolved(container) = resolveScopeExpr(n.getScopeExpr()) and + result = scopeAppend(container, n.getName()) + ) + } + + cached + Module getSuperClass(Module cls) { + cls = TResolved("Object") and result = TResolved("BasicObject") + or + cls = TResolved(["Module", "Numeric", "Array", "Hash", "FalseClass", "TrueClass", "NilClass"]) and + result = TResolved("Object") + or + cls = TResolved(["Integer", "Float", "Rational", "Complex"]) and + result = TResolved("Numeric") + or + cls = TResolved("Class") and + result = TResolved("Module") + or + not cls = TResolved(builtin()) and + ( + exists(ClassDeclaration d | + d = cls.getADeclaration() and + result = resolveScopeExpr(d.getSuperclassExpr()) + ) + or + result = TResolved("Object") and + forex(ClassDeclaration d | d = cls.getADeclaration() | + not exists(resolveScopeExpr(d.getSuperclassExpr())) + ) + ) + } + + cached + Module getAnIncludedModule(Module m) { + m = TResolved("Object") and result = TResolved("Kernel") + or + exists(IncludeOrPrependCall c | + c.getMethodName() = "include" and + ( + m = resolveScopeExpr(c.getReceiver()) + or + m = enclosingModule(c).getModule() and + c.getReceiver() instanceof Self + ) and + result = resolveScopeExpr(c.getAnArgument()) + ) + } + + cached + Module getAPrependedModule(Module m) { + exists(IncludeOrPrependCall c | + c.getMethodName() = "prepend" and + ( + m = resolveScopeExpr(c.getReceiver()) + or + m = enclosingModule(c).getModule() and + c.getReceiver() instanceof Self + ) and + result = resolveScopeExpr(c.getAnArgument()) + ) + } + + /** + * Resolve class or module read access to a qualified module name. + */ + cached + TResolved resolveScopeExpr(ConstantReadAccess r) { + exists(string qname | qname = resolveConstant(r) and result = TResolved(qname)) + } + + /** + * Resolve constant access (class, module or otherwise) to a qualified module name. + * `resolveScopeExpr/1` picks the best (lowest priority number) result of + * `resolveScopeExpr/2` that resolves to a constant definition. If the constant + * definition is a Namespace then it is returned, if it's a constant assignment then + * the right-hand side of the assignment is resolved. + */ + cached + string resolveConstant(ConstantReadAccess r) { + exists(string qname | + qname = + min(string qn, int p | + isDefinedConstant(qn) and + qn = resolveScopeExpr(r, p) and + // prevent classes/modules that contain/extend themselves + not exists(ConstantWriteAccess w | qn = constantDefinition0(w) | + r = w.getScopeExpr() + or + r = w.(ClassDeclaration).getSuperclassExpr() + ) + | + qn order by p + ) + | + result = qname + or + exists(ConstantAssignment a | + qname = constantDefinition0(a) and + result = resolveConstant(a.getParent().(Assignment).getRightOperand()) + ) + ) + } + + cached + Method lookupMethod(Module m, string name) { TMethod(result) = lookupMethodOrConst(m, name) } + + cached + Expr lookupConst(Module m, string name) { + TExpr(result) = lookupMethodOrConst(m, name) + or + exists(AssignExpr ae, ConstantWriteAccess w | + w = ae.getLeftOperand() and + w.getName() = name and + m = resolveScopeExpr(w.getScopeExpr()) and + result = ae.getRightOperand() + ) + } +} + +import Cached + +private predicate isToplevel(ConstantAccess n) { + not exists(n.getScopeExpr()) and + ( + n.hasGlobalScope() + or + n.getEnclosingModule() instanceof Toplevel + ) +} + +private predicate isDefinedConstant(string qualifiedModuleName) { + qualifiedModuleName = [builtin(), constantDefinition0(_)] +} + +private int maxDepth() { result = 1 + max(int level | exists(enclosing(_, level))) } + +private ModuleBase enclosing(ModuleBase m, int level) { + result = m and level = 0 + or + result = enclosing(m.getEnclosingModule(), level - 1) +} + +pragma[noinline] +private Namespace enclosingNameSpaceConstantReadAccess( + ConstantReadAccess c, int priority, string name +) { + result = enclosing(c.getEnclosingModule(), priority) and + name = c.getName() +} + +/** + * Resolve constant read access (typically a scope expression) to a qualified name. The + * `priority` value indicates the precedence of the solution with respect to the lookup order. + * A constant name without scope specifier is resolved against its enclosing modules (inner-most first); + * if the constant is not found in any of the enclosing modules, then the constant will be resolved + * with respect to the ancestors (prepends, includes, super classes, and their ancestors) of the + * directly enclosing module. + */ +private string resolveScopeExpr(ConstantReadAccess c, int priority) { + c.hasGlobalScope() and result = c.getName() and priority = 0 + or + exists(string name | + result = qualifiedModuleName(resolveScopeExprConstantReadAccess(c, priority, name), name) + ) + or + not exists(c.getScopeExpr()) and + not c.hasGlobalScope() and + ( + exists(string name | + exists(Namespace n | + n = enclosingNameSpaceConstantReadAccess(c, priority, name) and + result = qualifiedModuleName(constantDefinition0(n), name) + ) + or + result = + qualifiedModuleName(ancestors(qualifiedModuleNameConstantReadAccess(c, name), + priority - maxDepth()), name) + ) + or + priority = maxDepth() + 4 and + qualifiedModuleNameConstantReadAccess(c, result) != "BasicObject" + ) +} + +pragma[nomagic] +private string resolveScopeExprConstantReadAccess(ConstantReadAccess c, int priority, string name) { + result = resolveScopeExpr(c.getScopeExpr(), priority) and + name = c.getName() +} + +bindingset[qualifier, name] +private string scopeAppend(string qualifier, string name) { + if qualifier = "Object" then result = name else result = qualifier + "::" + name +} + +private string qualifiedModuleName(ModuleBase m) { + result = "Object" and m instanceof Toplevel + or + result = constantDefinition0(m) +} + +pragma[noinline] +private string qualifiedModuleNameConstantWriteAccess(ConstantWriteAccess c, string name) { + result = qualifiedModuleName(c.getEnclosingModule()) and + name = c.getName() +} + +pragma[noinline] +private string qualifiedModuleNameConstantReadAccess(ConstantReadAccess c, string name) { + result = qualifiedModuleName(c.getEnclosingModule()) and + name = c.getName() +} + +/** + * Get a qualified name for a constant definition. May return multiple qualified + * names because we over-approximate when resolving scope resolutions and ignore + * lookup order precedence. Taking lookup order into account here would lead to + * non-monotonic recursion. + */ +private string constantDefinition0(ConstantWriteAccess c) { + c.hasGlobalScope() and result = c.getName() + or + result = scopeAppend(resolveScopeExpr(c.getScopeExpr(), _), c.getName()) + or + not exists(c.getScopeExpr()) and + not c.hasGlobalScope() and + exists(string name | result = scopeAppend(qualifiedModuleNameConstantWriteAccess(c, name), name)) +} + +/** + * The qualified names of the ancestors of a class/module. The ancestors should be an ordered list + * of the ancestores of `prepend`ed modules, the module itself , the ancestors or `include`d modules + * and the ancestors of the super class. The priority value only distinguishes the kind of ancestor, + * it does not order the ancestors within a group of the same kind. This is an over-approximation, however, + * computing the precise order is tricky because it depends on the evaluation/file loading order. + */ +// TODO: the order of super classes can be determined more precisely even without knowing the evaluation +// order, so we should be able to make this more precise. +private string ancestors(string qname, int priority) { + result = ancestors(prepends(qname), _) and priority = 0 + or + result = qname and priority = 1 and isDefinedConstant(qname) + or + result = ancestors(includes(qname), _) and priority = 2 + or + result = ancestors(superclass(qname), _) and priority = 3 +} + +private class IncludeOrPrependCall extends MethodCall { + IncludeOrPrependCall() { this.getMethodName() = ["include", "prepend"] } + + string getAModule() { result = resolveScopeExpr(this.getAnArgument(), _) } + + string getTarget() { + result = resolveScopeExpr(this.getReceiver(), _) + or + result = qualifiedModuleName(enclosingModule(this)) and + ( + this.getReceiver() instanceof Self + or + not exists(this.getReceiver()) + ) + } +} + +/** + * A variant of AstNode::getEnclosingModule that excludes + * results that are enclosed in a block. This is a bit wrong because + * it could lead to false negatives. However, `include` statements in + * blocks are very rare in normal code. The majority of cases are in calls + * to methods like `module_eval` and `Rspec.describe` / `Rspec.context`. These + * methods evaluate the block in the context of some other module/class instead of + * the enclosing one. + */ +private ModuleBase enclosingModule(AstNode node) { result = parent*(node).getParent() } + +private AstNode parent(AstNode n) { + result = n.getParent() and + not result instanceof ModuleBase and + not result instanceof Block +} + +private string prepends(string qname) { + exists(IncludeOrPrependCall m | + m.getMethodName() = "prepend" and + qname = m.getTarget() and + result = m.getAModule() + ) +} + +private string includes(string qname) { + qname = "Object" and + result = "Kernel" + or + exists(IncludeOrPrependCall m | + m.getMethodName() = "include" and + qname = m.getTarget() and + result = m.getAModule() + ) +} + +private Expr superexpr(string qname) { + exists(ClassDeclaration c | qname = constantDefinition0(c) and result = c.getSuperclassExpr()) +} + +private string superclass(string qname) { + qname = "Object" and result = "BasicObject" + or + result = resolveScopeExpr(superexpr(qname), _) +} + +private string qualifiedModuleName(string container, string name) { + isDefinedConstant(result) and + ( + container = result.regexpCapture("(.+)::([^:]+)", 1) and + name = result.regexpCapture("(.+)::([^:]+)", 2) + or + container = "Object" and name = result + ) +} + +private Module getAncestors(Module m) { + result = m or + result = getAncestors(m.getAnIncludedModule()) or + result = getAncestors(m.getAPrependedModule()) +} + +private newtype TMethodOrExpr = + TMethod(Method m) or + TExpr(Expr e) + +private TMethodOrExpr getMethodOrConst(TModule owner, string name) { + exists(ModuleBase m | m.getModule() = owner | + result = TMethod(m.getMethod(name)) + or + result = TExpr(m.getConstant(name)) + ) +} + +module ExposedForTestingOnly { + Method getMethod(TModule owner, string name) { TMethod(result) = getMethodOrConst(owner, name) } + + Expr getConst(TModule owner, string name) { TExpr(result) = getMethodOrConst(owner, name) } +} + +private TMethodOrExpr lookupMethodOrConst0(Module m, string name) { + result = lookupMethodOrConst0(m.getAPrependedModule(), name) + or + not exists(getMethodOrConst(getAncestors(m.getAPrependedModule()), name)) and + ( + result = getMethodOrConst(m, name) + or + not exists(getMethodOrConst(m, name)) and + result = lookupMethodOrConst0(m.getAnIncludedModule(), name) + ) +} + +private AstNode getNode(TMethodOrExpr e) { e = TMethod(result) or e = TExpr(result) } + +private TMethodOrExpr lookupMethodOrConst(Module m, string name) { + result = lookupMethodOrConst0(m, name) + or + not exists(lookupMethodOrConst0(m, name)) and + result = lookupMethodOrConst(m.getSuperClass(), name) and + // For now, we restrict the scope of top-level declarations to their file. + // This may remove some plausible targets, but also removes a lot of + // implausible targets + if getNode(result).getEnclosingModule() instanceof Toplevel + then getNode(result).getFile() = m.getADeclaration().getFile() + else any() +} diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Operation.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Operation.qll new file mode 100644 index 00000000000..3571c97e9dc --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Operation.qll @@ -0,0 +1,198 @@ +private import codeql.ruby.AST +private import AST +private import TreeSitter +private import Call + +abstract class OperationImpl extends Expr, TOperation { + abstract string getOperatorImpl(); + + abstract Expr getAnOperandImpl(); +} + +abstract class UnaryOperationImpl extends OperationImpl, MethodCallImpl, TUnaryOperation { + abstract Expr getOperandImpl(); + + final override Expr getAnOperandImpl() { result = this.getOperandImpl() } + + final override string getMethodNameImpl() { result = this.getOperatorImpl() } + + final override AstNode getReceiverImpl() { result = this.getOperandImpl() } + + final override Expr getArgumentImpl(int n) { none() } + + final override int getNumberOfArgumentsImpl() { result = 0 } + + final override Block getBlockImpl() { none() } +} + +class UnaryOperationGenerated extends UnaryOperationImpl { + private Ruby::Unary g; + + UnaryOperationGenerated() { g = toGenerated(this) } + + final override Expr getOperandImpl() { toGenerated(result) = g.getOperand() } + + final override string getOperatorImpl() { result = g.getOperator() } +} + +class SplatExprReal extends UnaryOperationImpl, TSplatExprReal { + private Ruby::SplatArgument g; + + SplatExprReal() { this = TSplatExprReal(g) } + + final override string getOperatorImpl() { result = "*" } + + final override Expr getOperandImpl() { toGenerated(result) = g.getChild() } +} + +class SplatExprSynth extends UnaryOperationImpl, TSplatExprSynth { + final override string getOperatorImpl() { result = "*" } + + final override Expr getOperandImpl() { synthChild(this, 0, result) } +} + +class HashSplatExprImpl extends UnaryOperationImpl, THashSplatExpr { + private Ruby::HashSplatArgument g; + + HashSplatExprImpl() { this = THashSplatExpr(g) } + + final override Expr getOperandImpl() { toGenerated(result) = g.getChild() } + + final override string getOperatorImpl() { result = "**" } +} + +abstract class BinaryOperationImpl extends OperationImpl, MethodCallImpl, TBinaryOperation { + abstract Stmt getLeftOperandImpl(); + + abstract Stmt getRightOperandImpl(); + + final override Expr getAnOperandImpl() { + result = this.getLeftOperandImpl() + or + result = this.getRightOperandImpl() + } + + final override string getMethodNameImpl() { result = this.getOperatorImpl() } + + final override AstNode getReceiverImpl() { result = this.getLeftOperandImpl() } + + final override Expr getArgumentImpl(int n) { n = 0 and result = this.getRightOperandImpl() } + + final override int getNumberOfArgumentsImpl() { result = 1 } + + final override Block getBlockImpl() { none() } +} + +class BinaryOperationReal extends BinaryOperationImpl { + private Ruby::Binary g; + + BinaryOperationReal() { g = toGenerated(this) } + + final override string getOperatorImpl() { result = g.getOperator() } + + final override Stmt getLeftOperandImpl() { toGenerated(result) = g.getLeft() } + + final override Stmt getRightOperandImpl() { toGenerated(result) = g.getRight() } +} + +abstract class BinaryOperationSynth extends BinaryOperationImpl { + final override Stmt getLeftOperandImpl() { synthChild(this, 0, result) } + + final override Stmt getRightOperandImpl() { synthChild(this, 1, result) } +} + +class AddExprSynth extends BinaryOperationSynth, TAddExprSynth { + final override string getOperatorImpl() { result = "+" } +} + +class SubExprSynth extends BinaryOperationSynth, TSubExprSynth { + final override string getOperatorImpl() { result = "-" } +} + +class MulExprSynth extends BinaryOperationSynth, TMulExprSynth { + final override string getOperatorImpl() { result = "*" } +} + +class DivExprSynth extends BinaryOperationSynth, TDivExprSynth { + final override string getOperatorImpl() { result = "/" } +} + +class ModuloExprSynth extends BinaryOperationSynth, TModuloExprSynth { + final override string getOperatorImpl() { result = "%" } +} + +class ExponentExprSynth extends BinaryOperationSynth, TExponentExprSynth { + final override string getOperatorImpl() { result = "**" } +} + +class LogicalAndExprSynth extends BinaryOperationSynth, TLogicalAndExprSynth { + final override string getOperatorImpl() { result = "&&" } +} + +class LogicalOrExprSynth extends BinaryOperationSynth, TLogicalOrExprSynth { + final override string getOperatorImpl() { result = "||" } +} + +class LShiftExprSynth extends BinaryOperationSynth, TLShiftExprSynth { + final override string getOperatorImpl() { result = "<<" } +} + +class RShiftExprSynth extends BinaryOperationSynth, TRShiftExprSynth { + final override string getOperatorImpl() { result = ">>" } +} + +class BitwiseAndSynthExpr extends BinaryOperationSynth, TBitwiseAndExprSynth { + final override string getOperatorImpl() { result = "&" } +} + +class BitwiseOrSynthExpr extends BinaryOperationSynth, TBitwiseOrExprSynth { + final override string getOperatorImpl() { result = "|" } +} + +class BitwiseXorSynthExpr extends BinaryOperationSynth, TBitwiseXorExprSynth { + final override string getOperatorImpl() { result = "^" } +} + +abstract class AssignmentImpl extends OperationImpl, TAssignment { + abstract Pattern getLeftOperandImpl(); + + abstract Expr getRightOperandImpl(); + + final override Expr getAnOperandImpl() { + result = this.getLeftOperandImpl() + or + result = this.getRightOperandImpl() + } +} + +class AssignExprReal extends AssignmentImpl, TAssignExprReal { + private Ruby::Assignment g; + + AssignExprReal() { this = TAssignExprReal(g) } + + final override string getOperatorImpl() { result = "=" } + + final override Pattern getLeftOperandImpl() { toGenerated(result) = g.getLeft() } + + final override Expr getRightOperandImpl() { toGenerated(result) = g.getRight() } +} + +class AssignExprSynth extends AssignmentImpl, TAssignExprSynth { + final override string getOperatorImpl() { result = "=" } + + final override Pattern getLeftOperandImpl() { synthChild(this, 0, result) } + + final override Expr getRightOperandImpl() { synthChild(this, 1, result) } +} + +class AssignOperationImpl extends AssignmentImpl, TAssignOperation { + Ruby::OperatorAssignment g; + + AssignOperationImpl() { g = toGenerated(this) } + + final override string getOperatorImpl() { result = g.getOperator() } + + final override Pattern getLeftOperandImpl() { toGenerated(result) = g.getLeft() } + + final override Expr getRightOperandImpl() { toGenerated(result) = g.getRight() } +} diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Parameter.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Parameter.qll new file mode 100644 index 00000000000..f888d89c1ac --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Parameter.qll @@ -0,0 +1,19 @@ +private import codeql.ruby.AST +private import AST +private import TreeSitter + +module Parameter { + class Range extends Ruby::AstNode { + private int pos; + + Range() { + this = any(Ruby::BlockParameters bp).getChild(pos) + or + this = any(Ruby::MethodParameters mp).getChild(pos) + or + this = any(Ruby::LambdaParameters lp).getChild(pos) + } + + int getPosition() { result = pos } + } +} diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Pattern.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Pattern.qll new file mode 100644 index 00000000000..ce18e77f222 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Pattern.qll @@ -0,0 +1,32 @@ +private import codeql.ruby.AST +private import AST +private import TreeSitter + +abstract class TuplePatternImpl extends Ruby::AstNode { + abstract Ruby::AstNode getChildNode(int i); + + final int getRestIndex() { + result = unique(int i | this.getChildNode(i) instanceof Ruby::RestAssignment) + } +} + +class TuplePatternParameterImpl extends TuplePatternImpl, Ruby::DestructuredParameter { + override Ruby::AstNode getChildNode(int i) { result = this.getChild(i) } +} + +class DestructuredLeftAssignmentImpl extends TuplePatternImpl, Ruby::DestructuredLeftAssignment { + override Ruby::AstNode getChildNode(int i) { result = this.getChild(i) } +} + +class LeftAssignmentListImpl extends TuplePatternImpl, Ruby::LeftAssignmentList { + override Ruby::AstNode getChildNode(int i) { + this = + any(Ruby::LeftAssignmentList lal | + if + strictcount(int j | exists(lal.getChild(j))) = 1 and + lal.getChild(0) instanceof Ruby::DestructuredLeftAssignment + then result = lal.getChild(0).(Ruby::DestructuredLeftAssignment).getChild(i) + else result = lal.getChild(i) + ) + } +} diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Scope.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Scope.qll new file mode 100644 index 00000000000..cab6a7ad3b1 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Scope.qll @@ -0,0 +1,130 @@ +private import TreeSitter +private import codeql.ruby.ast.Scope +private import codeql.ruby.ast.internal.AST +private import codeql.ruby.ast.internal.Parameter + +class TScopeType = TMethodBase or TModuleLike or TBlockLike; + +/** + * The scope of a `self` variable. + * This differs from a normal scope because it is not affected by blocks or lambdas. + */ +class TSelfScopeType = TMethodBase or TModuleBase; + +private class TBlockLike = TDoBlock or TLambda or TBlock or TEndBlock; + +private class TModuleLike = TToplevel or TModuleDeclaration or TClassDeclaration or TSingletonClass; + +module Scope { + class TypeRange = Callable::TypeRange or ModuleBase::TypeRange or @ruby_end_block; + + class Range extends Ruby::AstNode, TypeRange { + Range() { not this = any(Ruby::Lambda l).getBody() } + + ModuleBase::Range getEnclosingModule() { + result = this + or + not this instanceof ModuleBase::Range and result = this.getOuterScope().getEnclosingModule() + } + + MethodBase::Range getEnclosingMethod() { + result = this + or + not this instanceof MethodBase::Range and + not this instanceof ModuleBase::Range and + result = this.getOuterScope().getEnclosingMethod() + } + + SelfBase::Range getEnclosingSelfScope() { + this instanceof SelfBase::Range and result = this + or + not this instanceof SelfBase::Range and result = this.getOuterScope().getEnclosingSelfScope() + } + + Range getOuterScope() { result = scopeOf(this) } + } +} + +module MethodBase { + class TypeRange = @ruby_method or @ruby_singleton_method; + + class Range extends Scope::Range, TypeRange { } +} + +module Callable { + class TypeRange = MethodBase::TypeRange or @ruby_do_block or @ruby_lambda or @ruby_block; + + class Range extends Scope::Range, TypeRange { + Parameter::Range getParameter(int i) { + result = this.(Ruby::Method).getParameters().getChild(i) or + result = this.(Ruby::SingletonMethod).getParameters().getChild(i) or + result = this.(Ruby::DoBlock).getParameters().getChild(i) or + result = this.(Ruby::Lambda).getParameters().getChild(i) or + result = this.(Ruby::Block).getParameters().getChild(i) + } + } +} + +module ModuleBase { + class TypeRange = @ruby_program or @ruby_module or @ruby_class or @ruby_singleton_class; + + class Range extends Scope::Range, TypeRange { } +} + +module SelfBase { + class TypeRange = MethodBase::TypeRange or ModuleBase::TypeRange; + + /** + * A `self` variable can appear in a class, module, method or at the top level. + */ + class Range extends Scope::Range, TypeRange { } +} + +pragma[noinline] +private predicate rankHeredocBody(File f, Ruby::HeredocBody b, int i) { + b = + rank[i](Ruby::HeredocBody b0 | + f = b0.getLocation().getFile() + | + b0 order by b0.getLocation().getStartLine(), b0.getLocation().getStartColumn() + ) +} + +Ruby::HeredocBody getHereDocBody(Ruby::HeredocBeginning g) { + exists(int i, File f | + g = + rank[i](Ruby::HeredocBeginning b | + f = b.getLocation().getFile() + | + b order by b.getLocation().getStartLine(), b.getLocation().getStartColumn() + ) and + rankHeredocBody(f, result, i) + ) +} + +private Ruby::AstNode parentOf(Ruby::AstNode n) { + n = getHereDocBody(result) + or + exists(Ruby::AstNode parent | parent = n.getParent() | + if + n = + [ + parent.(Ruby::Module).getName(), parent.(Ruby::Class).getName(), + parent.(Ruby::Class).getSuperclass(), parent.(Ruby::SingletonClass).getValue(), + parent.(Ruby::Method).getName(), parent.(Ruby::SingletonMethod).getName(), + parent.(Ruby::SingletonMethod).getObject() + ] + then result = parent.getParent() + else result = parent + ) +} + +/** Gets the enclosing scope of a node */ +cached +Scope::Range scopeOf(Ruby::AstNode n) { + exists(Ruby::AstNode p | p = parentOf(n) | + p = result + or + not p instanceof Scope::Range and result = scopeOf(p) + ) +} diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll new file mode 100644 index 00000000000..d1ef6db61c4 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll @@ -0,0 +1,798 @@ +/** Provides predicates for synthesizing AST nodes. */ + +private import AST +private import TreeSitter +private import codeql.ruby.ast.internal.Call +private import codeql.ruby.ast.internal.Variable +private import codeql.ruby.ast.internal.Pattern +private import codeql.ruby.ast.internal.Scope +private import codeql.ruby.AST + +/** A synthesized AST node kind. */ +newtype SynthKind = + AddExprKind() or + AssignExprKind() or + BitwiseAndExprKind() or + BitwiseOrExprKind() or + BitwiseXorExprKind() or + ClassVariableAccessKind(ClassVariable v) or + DivExprKind() or + ExponentExprKind() or + GlobalVariableAccessKind(GlobalVariable v) or + InstanceVariableAccessKind(InstanceVariable v) or + IntegerLiteralKind(int i) { i in [-1000 .. 1000] } or + LShiftExprKind() or + LocalVariableAccessRealKind(LocalVariableReal v) or + LocalVariableAccessSynthKind(TLocalVariableSynth v) or + LogicalAndExprKind() or + LogicalOrExprKind() or + MethodCallKind(string name, boolean setter, int arity) { + any(Synthesis s).methodCall(name, setter, arity) + } or + ModuloExprKind() or + MulExprKind() or + RangeLiteralKind(boolean inclusive) { inclusive in [false, true] } or + RShiftExprKind() or + SplatExprKind() or + StmtSequenceKind() or + SelfKind(SelfVariable v) or + SubExprKind() or + ConstantReadAccessKind(string value) { any(Synthesis s).constantReadAccess(value) } + +/** + * An AST child. + * + * Either a new synthesized node or a reference to an existing node. + */ +newtype Child = + SynthChild(SynthKind k) or + RealChild(AstNode n) + +private newtype TSynthesis = MkSynthesis() + +/** A class used for synthesizing AST nodes. */ +class Synthesis extends TSynthesis { + /** + * Holds if a node should be synthesized as the `i`th child of `parent`, or if + * a non-synthesized node should be the `i`th child of synthesized node `parent`. + * + * `i = -1` is used to represent that the synthesized node is a desugared version + * of its parent. + */ + predicate child(AstNode parent, int i, Child child) { none() } + + /** + * Holds if synthesized node `n` should have location `l`. Synthesized nodes for + * which this predicate does not hold, inherit their location (recursively) from + * their parent node. + */ + predicate location(AstNode n, Location l) { none() } + + /** + * Holds if a local variable, identified by `i`, should be synthesized for AST + * node `n`. + */ + predicate localVariable(AstNode n, int i) { none() } + + /** + * Holds if a method call to `name` with arity `arity` is needed. + */ + predicate methodCall(string name, boolean setter, int arity) { none() } + + /** + * Holds if a constant read access of `name` is needed. + */ + predicate constantReadAccess(string name) { none() } + + /** + * Holds if `n` should be excluded from `ControlFlowTree` in the CFG construction. + */ + predicate excludeFromControlFlowTree(AstNode n) { none() } + + final string toString() { none() } +} + +private class Desugared extends AstNode { + Desugared() { this = any(AstNode sugar).getDesugared() } + + AstNode getADescendant() { result = this.getAChild*() } +} + +/** + * Gets the desugaring level of `n`. That is, the number of desugaring + * transformations required before the context in which `n` occurs is + * fully desugared. + */ +int desugarLevel(AstNode n) { result = count(Desugared desugared | n = desugared.getADescendant()) } + +/** + * Use this predicate in `Synthesis::child` to generate an assignment of `value` to + * synthesized variable `v`, where the assignment is a child of `assignParent` at + * index `assignIndex`. + */ +bindingset[v, assignParent, assignIndex, value] +private predicate assign( + AstNode parent, int i, Child child, TLocalVariableSynth v, AstNode assignParent, int assignIndex, + AstNode value +) { + parent = assignParent and + i = assignIndex and + child = SynthChild(AssignExprKind()) + or + parent = TAssignExprSynth(assignParent, assignIndex) and + ( + i = 0 and + child = SynthChild(LocalVariableAccessSynthKind(v)) + or + i = 1 and + child = RealChild(value) + ) +} + +/** Holds if synthesized node `n` should have location `l`. */ +predicate synthLocation(AstNode n, Location l) { + n.isSynthesized() and any(Synthesis s).location(n, l) +} + +private predicate hasLocation(AstNode n, Location l) { + l = toGenerated(n).getLocation() + or + synthLocation(n, l) +} + +private module ImplicitSelfSynthesis { + pragma[nomagic] + private predicate identifierMethodCallSelfSynthesis(AstNode mc, int i, Child child) { + child = SynthChild(SelfKind(TSelfVariable(scopeOf(toGenerated(mc)).getEnclosingSelfScope()))) and + mc = TIdentifierMethodCall(_) and + i = 0 + } + + private class IdentifierMethodCallSelfSynthesis extends Synthesis { + final override predicate child(AstNode parent, int i, Child child) { + identifierMethodCallSelfSynthesis(parent, i, child) + } + } + + pragma[nomagic] + private predicate regularMethodCallSelfSynthesis(TRegularMethodCall mc, int i, Child child) { + exists(Ruby::AstNode g | + mc = TRegularMethodCall(g) and + // If there's no explicit receiver (or scope resolution that acts like a + // receiver), then the receiver is implicitly `self`. N.B. `::Foo()` is + // not valid Ruby. + not exists(g.(Ruby::Call).getReceiver()) and + not exists(g.(Ruby::Call).getMethod().(Ruby::ScopeResolution).getScope()) + ) and + child = SynthChild(SelfKind(TSelfVariable(scopeOf(toGenerated(mc)).getEnclosingSelfScope()))) and + i = 0 + } + + private class RegularMethodCallSelfSynthesis extends Synthesis { + final override predicate child(AstNode parent, int i, Child child) { + regularMethodCallSelfSynthesis(parent, i, child) + } + } +} + +private module SetterDesugar { + /** An assignment where the left-hand side is a method call. */ + private class SetterAssignExpr extends AssignExpr { + private MethodCall mc; + + pragma[nomagic] + SetterAssignExpr() { mc = this.getLeftOperand() } + + MethodCall getMethodCall() { result = mc } + + pragma[nomagic] + MethodCallKind getCallKind(boolean setter, int arity) { + result = MethodCallKind(mc.getMethodName(), setter, arity) + } + + pragma[nomagic] + Expr getReceiver() { result = mc.getReceiver() } + + pragma[nomagic] + Expr getArgument(int i) { result = mc.getArgument(i) } + + pragma[nomagic] + int getNumberOfArguments() { result = mc.getNumberOfArguments() } + + pragma[nomagic] + Location getMethodCallLocation() { hasLocation(mc, result) } + } + + pragma[nomagic] + private predicate setterMethodCallSynthesis(AstNode parent, int i, Child child) { + exists(SetterAssignExpr sae | + parent = sae and + i = -1 and + child = SynthChild(StmtSequenceKind()) + or + exists(AstNode seq | seq = TStmtSequenceSynth(sae, -1) | + parent = seq and + i = 0 and + child = SynthChild(sae.getCallKind(true, sae.getNumberOfArguments() + 1)) + or + exists(AstNode call | call = TMethodCallSynth(seq, 0, _, _, _) | + parent = call and + i = 0 and + child = RealChild(sae.getReceiver()) + or + parent = call and + child = RealChild(sae.getArgument(i - 1)) + or + exists(int valueIndex | valueIndex = sae.getNumberOfArguments() + 1 | + parent = call and + i = valueIndex and + child = SynthChild(AssignExprKind()) + or + parent = TAssignExprSynth(call, valueIndex) and + ( + i = 0 and + child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(sae, 0))) + or + i = 1 and + child = RealChild(sae.getRightOperand()) + ) + ) + ) + or + parent = seq and + i = 1 and + child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(sae, 0))) + ) + ) + } + + /** + * ```rb + * x.foo = y + * ``` + * + * desugars to + * + * ```rb + * x.foo=(__synth_0 = y); + * __synth_0; + * ``` + */ + private class SetterMethodCallSynthesis extends Synthesis { + final override predicate child(AstNode parent, int i, Child child) { + setterMethodCallSynthesis(parent, i, child) + } + + final override predicate location(AstNode n, Location l) { + exists(SetterAssignExpr sae, StmtSequence seq | + seq = sae.getDesugared() and + l = sae.getMethodCallLocation() and + n = seq.getAStmt() + ) + } + + final override predicate excludeFromControlFlowTree(AstNode n) { + n = any(SetterAssignExpr sae).getMethodCall() + } + + final override predicate localVariable(AstNode n, int i) { + n instanceof SetterAssignExpr and + i = 0 + } + + final override predicate methodCall(string name, boolean setter, int arity) { + exists(SetterAssignExpr sae | + name = sae.getMethodCall().getMethodName() and + setter = true and + arity = sae.getNumberOfArguments() + 1 + ) + } + } +} + +private module AssignOperationDesugar { + /** + * Gets the operator kind to synthesize for operator assignment `ao`. + */ + private SynthKind getKind(AssignOperation ao) { + ao instanceof AssignAddExpr and result = AddExprKind() + or + ao instanceof AssignSubExpr and result = SubExprKind() + or + ao instanceof AssignMulExpr and result = MulExprKind() + or + ao instanceof AssignDivExpr and result = DivExprKind() + or + ao instanceof AssignModuloExpr and result = ModuloExprKind() + or + ao instanceof AssignExponentExpr and result = ExponentExprKind() + or + ao instanceof AssignLogicalAndExpr and result = LogicalAndExprKind() + or + ao instanceof AssignLogicalOrExpr and result = LogicalOrExprKind() + or + ao instanceof AssignLShiftExpr and result = LShiftExprKind() + or + ao instanceof AssignRShiftExpr and result = RShiftExprKind() + or + ao instanceof AssignBitwiseAndExpr and result = BitwiseAndExprKind() + or + ao instanceof AssignBitwiseOrExpr and result = BitwiseOrExprKind() + or + ao instanceof AssignBitwiseXorExpr and result = BitwiseXorExprKind() + } + + private Location getAssignOperationLocation(AssignOperation ao) { + exists(Ruby::OperatorAssignment g, Ruby::Token op | + g = toGenerated(ao) and + op.getParent() = g and + op.getParentIndex() = 1 and + result = op.getLocation() + ) + } + + /** An assignment operation where the left-hand side is a variable. */ + private class VariableAssignOperation extends AssignOperation { + private Variable v; + + pragma[nomagic] + VariableAssignOperation() { v = this.getLeftOperand().(VariableAccess).getVariable() } + + pragma[nomagic] + SynthKind getVariableAccessKind() { + result in [ + LocalVariableAccessRealKind(v).(SynthKind), InstanceVariableAccessKind(v), + ClassVariableAccessKind(v), GlobalVariableAccessKind(v) + ] + } + } + + pragma[nomagic] + private predicate variableAssignOperationSynthesis(AstNode parent, int i, Child child) { + exists(VariableAssignOperation vao | + parent = vao and + i = -1 and + child = SynthChild(AssignExprKind()) + or + exists(AstNode assign | assign = TAssignExprSynth(vao, -1) | + parent = assign and + i = 0 and + child = RealChild(vao.getLeftOperand()) + or + parent = assign and + i = 1 and + child = SynthChild(getKind(vao)) + or + parent = getSynthChild(assign, 1) and + ( + i = 0 and + child = SynthChild(vao.getVariableAccessKind()) + or + i = 1 and + child = RealChild(vao.getRightOperand()) + ) + ) + ) + } + + /** + * ```rb + * x += y + * ``` + * + * desugars to + * + * ```rb + * x = x + y + * ``` + * + * when `x` is a variable. + */ + private class VariableAssignOperationSynthesis extends Synthesis { + final override predicate child(AstNode parent, int i, Child child) { + variableAssignOperationSynthesis(parent, i, child) + } + + final override predicate location(AstNode n, Location l) { + exists(VariableAssignOperation vao, BinaryOperation bo | + bo = vao.getDesugared().(AssignExpr).getRightOperand() + | + n = bo and + l = getAssignOperationLocation(vao) + or + n = bo.getLeftOperand() and + hasLocation(vao.getLeftOperand(), l) + ) + } + } + + /** An assignment operation where the left-hand side is a method call. */ + private class SetterAssignOperation extends AssignOperation { + private MethodCall mc; + + pragma[nomagic] + SetterAssignOperation() { mc = this.getLeftOperand() } + + MethodCall getMethodCall() { result = mc } + + pragma[nomagic] + MethodCallKind getCallKind(boolean setter, int arity) { + result = MethodCallKind(mc.getMethodName(), setter, arity) + } + + pragma[nomagic] + Expr getReceiver() { result = mc.getReceiver() } + + pragma[nomagic] + Expr getArgument(int i) { result = mc.getArgument(i) } + + pragma[nomagic] + int getNumberOfArguments() { result = mc.getNumberOfArguments() } + + pragma[nomagic] + Location getMethodCallLocation() { hasLocation(mc, result) } + } + + pragma[nomagic] + private predicate methodCallAssignOperationSynthesis(AstNode parent, int i, Child child) { + exists(SetterAssignOperation sao | + parent = sao and + i = -1 and + child = SynthChild(StmtSequenceKind()) + or + exists(AstNode seq | seq = TStmtSequenceSynth(sao, -1) | + // `__synth__0 = foo` + assign(parent, i, child, TLocalVariableSynth(sao, 0), seq, 0, sao.getReceiver()) + or + // `__synth__1 = bar` + exists(Expr arg, int j | arg = sao.getArgument(j - 1) | + assign(parent, i, child, TLocalVariableSynth(sao, j), seq, j, arg) + ) + or + // `__synth__2 = __synth__0.[](__synth__1) + y` + exists(int opAssignIndex | opAssignIndex = sao.getNumberOfArguments() + 1 | + parent = seq and + i = opAssignIndex and + child = SynthChild(AssignExprKind()) + or + exists(AstNode assign | assign = TAssignExprSynth(seq, opAssignIndex) | + parent = assign and + i = 0 and + child = + SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(sao, opAssignIndex))) + or + parent = assign and + i = 1 and + child = SynthChild(getKind(sao)) + or + // `__synth__0.[](__synth__1) + y` + exists(AstNode op | op = getSynthChild(assign, 1) | + parent = op and + i = 0 and + child = SynthChild(sao.getCallKind(false, sao.getNumberOfArguments())) + or + parent = TMethodCallSynth(op, 0, _, _, _) and + child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(sao, i))) and + i in [0 .. sao.getNumberOfArguments()] + or + parent = op and + i = 1 and + child = RealChild(sao.getRightOperand()) + ) + ) + or + // `__synth__0.[]=(__synth__1, __synth__2);` + parent = seq and + i = opAssignIndex + 1 and + child = SynthChild(sao.getCallKind(true, opAssignIndex)) + or + exists(AstNode setter | setter = TMethodCallSynth(seq, opAssignIndex + 1, _, _, _) | + parent = setter and + child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(sao, i))) and + i in [0 .. sao.getNumberOfArguments()] + or + parent = setter and + i = opAssignIndex + 1 and + child = + SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(sao, opAssignIndex))) + ) + or + parent = seq and + i = opAssignIndex + 2 and + child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(sao, opAssignIndex))) + ) + ) + ) + } + + /** + * ```rb + * foo[bar] += y + * ``` + * + * desugars to + * + * ```rb + * __synth__0 = foo; + * __synth__1 = bar; + * __synth__2 = __synth__0.[](__synth__1) + y; + * __synth__0.[]=(__synth__1, __synth__2); + * __synth__2; + * ``` + */ + private class MethodCallAssignOperationSynthesis extends Synthesis { + final override predicate child(AstNode parent, int i, Child child) { + methodCallAssignOperationSynthesis(parent, i, child) + } + + final override predicate location(AstNode n, Location l) { + exists(SetterAssignOperation sao, StmtSequence seq | seq = sao.getDesugared() | + n = seq.getStmt(0) and + hasLocation(sao.getReceiver(), l) + or + exists(int i | + n = seq.getStmt(i + 1) and + hasLocation(sao.getArgument(i), l) + ) + or + exists(AssignExpr ae, int opAssignIndex | + opAssignIndex = sao.getNumberOfArguments() + 1 and + ae = seq.getStmt(opAssignIndex) + | + l = getAssignOperationLocation(sao) and + n = ae + or + exists(BinaryOperation bo | bo = ae.getRightOperand() | + n = bo.getLeftOperand() and + l = sao.getMethodCallLocation() + or + exists(MethodCall mc | mc = bo.getLeftOperand() | + n = mc.getReceiver() and + hasLocation(sao.getReceiver(), l) + or + exists(int i | + n = mc.getArgument(i) and + hasLocation(sao.getArgument(i), l) + ) + ) + ) + or + exists(MethodCall mc | mc = seq.getStmt(opAssignIndex + 1) | + n = mc and + l = sao.getMethodCallLocation() + or + n = mc.getReceiver() and + hasLocation(sao.getReceiver(), l) + or + exists(int i | n = mc.getArgument(i) | + hasLocation(sao.getArgument(i), l) + or + i = opAssignIndex and + l = getAssignOperationLocation(sao) + ) + ) + or + n = seq.getStmt(opAssignIndex + 2) and + l = getAssignOperationLocation(sao) + ) + ) + } + + final override predicate localVariable(AstNode n, int i) { + n = any(SetterAssignOperation sao | i in [0 .. sao.getNumberOfArguments() + 1]) + } + + final override predicate methodCall(string name, boolean setter, int arity) { + exists(SetterAssignOperation sao | name = sao.getMethodCall().getMethodName() | + setter = false and + arity = sao.getNumberOfArguments() + or + setter = true and + arity = sao.getNumberOfArguments() + 1 + ) + } + + final override predicate excludeFromControlFlowTree(AstNode n) { + n = any(SetterAssignOperation sao).getMethodCall() + } + } +} + +private module CompoundAssignDesugar { + /** An assignment where the left-hand side is a tuple pattern. */ + private class TupleAssignExpr extends AssignExpr { + private TuplePattern tp; + + pragma[nomagic] + TupleAssignExpr() { tp = this.getLeftOperand() } + + TuplePattern getTuplePattern() { result = tp } + + pragma[nomagic] + Pattern getElement(int i) { result = tp.getElement(i) } + + pragma[nomagic] + int getNumberOfElements() { + toGenerated(tp) = any(TuplePatternImpl impl | result = count(impl.getChildNode(_))) + } + + pragma[nomagic] + int getRestIndexOrNumberOfElements() { + result = tp.getRestIndex() + or + toGenerated(tp) = any(TuplePatternImpl impl | not exists(impl.getRestIndex())) and + result = this.getNumberOfElements() + } + } + + pragma[nomagic] + private predicate compoundAssignSynthesis(AstNode parent, int i, Child child) { + exists(TupleAssignExpr tae | + parent = tae and + i = -1 and + child = SynthChild(StmtSequenceKind()) + or + exists(AstNode seq | seq = TStmtSequenceSynth(tae, -1) | + parent = seq and + i = 0 and + child = SynthChild(AssignExprKind()) + or + exists(AstNode assign | assign = TAssignExprSynth(seq, 0) | + parent = assign and + i = 0 and + child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(tae, 0))) + or + parent = assign and + i = 1 and + child = SynthChild(SplatExprKind()) + or + parent = TSplatExprSynth(assign, 1) and + i = 0 and + child = RealChild(tae.getRightOperand()) + ) + or + exists(Pattern p, int j, int restIndex | + p = tae.getElement(j) and + restIndex = tae.getRestIndexOrNumberOfElements() + | + parent = seq and + i = j + 1 and + child = SynthChild(AssignExprKind()) + or + exists(AstNode assign | assign = TAssignExprSynth(seq, j + 1) | + parent = assign and + i = 0 and + child = RealChild(p) + or + parent = assign and + i = 1 and + child = SynthChild(MethodCallKind("[]", false, 1)) + or + parent = TMethodCallSynth(assign, 1, _, _, _) and + i = 0 and + child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(tae, 0))) + or + j < restIndex and + parent = TMethodCallSynth(assign, 1, _, _, _) and + i = 1 and + child = SynthChild(IntegerLiteralKind(j)) + or + j = restIndex and + ( + parent = TMethodCallSynth(assign, 1, _, _, _) and + i = 1 and + child = SynthChild(RangeLiteralKind(true)) + or + exists(AstNode call | + call = TMethodCallSynth(assign, 1, _, _, _) and + parent = TRangeLiteralSynth(call, 1, _) + | + i = 0 and + child = SynthChild(IntegerLiteralKind(j)) + or + i = 1 and + child = SynthChild(IntegerLiteralKind(restIndex - tae.getNumberOfElements())) + ) + ) + or + j > restIndex and + parent = TMethodCallSynth(assign, 1, _, _, _) and + i = 1 and + child = SynthChild(IntegerLiteralKind(j - tae.getNumberOfElements())) + ) + ) + ) + ) + } + + /** + * ```rb + * x, *y, z = w + * ``` + * desugars to + * + * ```rb + * __synth__0 = *w; + * x = __synth__0[0]; + * y = __synth__0[1..-2]; + * z = __synth__0[-1]; + * ``` + */ + private class CompoundAssignSynthesis extends Synthesis { + final override predicate child(AstNode parent, int i, Child child) { + compoundAssignSynthesis(parent, i, child) + } + + final override predicate location(AstNode n, Location l) { + exists(TupleAssignExpr tae, StmtSequence seq | seq = tae.getDesugared() | + n = seq.getStmt(0) and + hasLocation(tae.getRightOperand(), l) + or + exists(Pattern p, int j | + p = tae.getElement(j) and + n = seq.getStmt(j + 1) and + hasLocation(p, l) + ) + ) + } + + final override predicate localVariable(AstNode n, int i) { + n instanceof TupleAssignExpr and + i = 0 + } + + final override predicate methodCall(string name, boolean setter, int arity) { + name = "[]" and + setter = false and + arity = 1 + } + + final override predicate excludeFromControlFlowTree(AstNode n) { + n = any(TupleAssignExpr tae).getTuplePattern() + } + } +} + +private module ArrayLiteralDesugar { + pragma[nomagic] + private predicate arrayLiteralSynthesis(AstNode parent, int i, Child child) { + exists(ArrayLiteral al | + parent = al and + i = -1 and + child = SynthChild(MethodCallKind("[]", false, al.getNumberOfElements() + 1)) + or + exists(AstNode mc | mc = TMethodCallSynth(al, -1, _, _, _) | + parent = mc and + i = 0 and + child = SynthChild(ConstantReadAccessKind("::Array")) + or + parent = mc and + child = RealChild(al.getElement(i - 1)) + ) + ) + } + + /** + * ```rb + * [1, 2, 3] + * ``` + * desugars to + * + * ```rb + * ::Array.[](1, 2, 3) + * ``` + */ + private class CompoundAssignSynthesis extends Synthesis { + final override predicate child(AstNode parent, int i, Child child) { + arrayLiteralSynthesis(parent, i, child) + } + + final override predicate methodCall(string name, boolean setter, int arity) { + name = "[]" and + setter = false and + arity = any(ArrayLiteral al).getNumberOfElements() + 1 + } + + final override predicate constantReadAccess(string name) { name = "::Array" } + } +} diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll b/ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll new file mode 100644 index 00000000000..da02db918db --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll @@ -0,0 +1,2000 @@ +/* + * CodeQL library for Ruby + * Automatically generated from the tree-sitter grammar; do not edit + */ + +private import codeql.files.FileSystem +private import codeql.Locations + +module Ruby { + /** The base class for all AST nodes */ + class AstNode extends @ruby_ast_node { + /** Gets a string representation of this element. */ + string toString() { result = this.getAPrimaryQlClass() } + + /** Gets the location of this element. */ + Location getLocation() { none() } + + /** Gets the parent of this element. */ + AstNode getParent() { ruby_ast_node_parent(this, result, _) } + + /** Gets the index of this node among the children of its parent. */ + int getParentIndex() { ruby_ast_node_parent(this, _, result) } + + /** Gets a field or child node of this node. */ + AstNode getAFieldOrChild() { none() } + + /** Gets the name of the primary QL class for this element. */ + string getAPrimaryQlClass() { result = "???" } + + /** Gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs. */ + string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") } + } + + /** A token. */ + class Token extends @ruby_token, AstNode { + /** Gets the value of this token. */ + string getValue() { ruby_tokeninfo(this, _, result, _) } + + /** Gets the location of this token. */ + override Location getLocation() { ruby_tokeninfo(this, _, _, result) } + + /** Gets a string representation of this element. */ + override string toString() { result = this.getValue() } + + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Token" } + } + + /** A reserved word. */ + class ReservedWord extends @ruby_reserved_word, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "ReservedWord" } + } + + class UnderscoreArg extends @ruby_underscore_arg, AstNode { } + + class UnderscoreLhs extends @ruby_underscore_lhs, AstNode { } + + class UnderscoreMethodName extends @ruby_underscore_method_name, AstNode { } + + class UnderscorePrimary extends @ruby_underscore_primary, AstNode { } + + class UnderscoreStatement extends @ruby_underscore_statement, AstNode { } + + class UnderscoreVariable extends @ruby_underscore_variable, AstNode { } + + /** A class representing `alias` nodes. */ + class Alias extends @ruby_alias, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Alias" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_alias_def(this, _, _, result) } + + /** Gets the node corresponding to the field `alias`. */ + UnderscoreMethodName getAlias() { ruby_alias_def(this, result, _, _) } + + /** Gets the node corresponding to the field `name`. */ + UnderscoreMethodName getName() { ruby_alias_def(this, _, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_alias_def(this, result, _, _) or ruby_alias_def(this, _, result, _) + } + } + + /** A class representing `argument_list` nodes. */ + class ArgumentList extends @ruby_argument_list, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "ArgumentList" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_argument_list_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_argument_list_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_argument_list_child(this, _, result) } + } + + /** A class representing `array` nodes. */ + class Array extends @ruby_array, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Array" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_array_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_array_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_array_child(this, _, result) } + } + + /** A class representing `assignment` nodes. */ + class Assignment extends @ruby_assignment, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Assignment" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_assignment_def(this, _, _, result) } + + /** Gets the node corresponding to the field `left`. */ + AstNode getLeft() { ruby_assignment_def(this, result, _, _) } + + /** Gets the node corresponding to the field `right`. */ + AstNode getRight() { ruby_assignment_def(this, _, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_assignment_def(this, result, _, _) or ruby_assignment_def(this, _, result, _) + } + } + + /** A class representing `bare_string` nodes. */ + class BareString extends @ruby_bare_string, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "BareString" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_bare_string_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_bare_string_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_bare_string_child(this, _, result) } + } + + /** A class representing `bare_symbol` nodes. */ + class BareSymbol extends @ruby_bare_symbol, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "BareSymbol" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_bare_symbol_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_bare_symbol_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_bare_symbol_child(this, _, result) } + } + + /** A class representing `begin` nodes. */ + class Begin extends @ruby_begin, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Begin" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_begin_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_begin_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_begin_child(this, _, result) } + } + + /** A class representing `begin_block` nodes. */ + class BeginBlock extends @ruby_begin_block, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "BeginBlock" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_begin_block_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_begin_block_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_begin_block_child(this, _, result) } + } + + /** A class representing `binary` nodes. */ + class Binary extends @ruby_binary, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Binary" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_binary_def(this, _, _, _, result) } + + /** Gets the node corresponding to the field `left`. */ + AstNode getLeft() { ruby_binary_def(this, result, _, _, _) } + + /** Gets the node corresponding to the field `operator`. */ + string getOperator() { + exists(int value | ruby_binary_def(this, _, value, _, _) | + result = "!=" and value = 0 + or + result = "!~" and value = 1 + or + result = "%" and value = 2 + or + result = "&" and value = 3 + or + result = "&&" and value = 4 + or + result = "*" and value = 5 + or + result = "**" and value = 6 + or + result = "+" and value = 7 + or + result = "-" and value = 8 + or + result = "/" and value = 9 + or + result = "<" and value = 10 + or + result = "<<" and value = 11 + or + result = "<=" and value = 12 + or + result = "<=>" and value = 13 + or + result = "==" and value = 14 + or + result = "===" and value = 15 + or + result = "=~" and value = 16 + or + result = ">" and value = 17 + or + result = ">=" and value = 18 + or + result = ">>" and value = 19 + or + result = "^" and value = 20 + or + result = "and" and value = 21 + or + result = "or" and value = 22 + or + result = "|" and value = 23 + or + result = "||" and value = 24 + ) + } + + /** Gets the node corresponding to the field `right`. */ + AstNode getRight() { ruby_binary_def(this, _, _, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_binary_def(this, result, _, _, _) or ruby_binary_def(this, _, _, result, _) + } + } + + /** A class representing `block` nodes. */ + class Block extends @ruby_block, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Block" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_block_def(this, result) } + + /** Gets the node corresponding to the field `parameters`. */ + BlockParameters getParameters() { ruby_block_parameters(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_block_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_block_parameters(this, result) or ruby_block_child(this, _, result) + } + } + + /** A class representing `block_argument` nodes. */ + class BlockArgument extends @ruby_block_argument, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "BlockArgument" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_block_argument_def(this, _, result) } + + /** Gets the child of this node. */ + UnderscoreArg getChild() { ruby_block_argument_def(this, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_block_argument_def(this, result, _) } + } + + /** A class representing `block_parameter` nodes. */ + class BlockParameter extends @ruby_block_parameter, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "BlockParameter" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_block_parameter_def(this, _, result) } + + /** Gets the node corresponding to the field `name`. */ + Identifier getName() { ruby_block_parameter_def(this, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_block_parameter_def(this, result, _) } + } + + /** A class representing `block_parameters` nodes. */ + class BlockParameters extends @ruby_block_parameters, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "BlockParameters" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_block_parameters_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_block_parameters_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_block_parameters_child(this, _, result) } + } + + /** A class representing `break` nodes. */ + class Break extends @ruby_break, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Break" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_break_def(this, result) } + + /** Gets the child of this node. */ + ArgumentList getChild() { ruby_break_child(this, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_break_child(this, result) } + } + + /** A class representing `call` nodes. */ + class Call extends @ruby_call, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Call" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_call_def(this, _, result) } + + /** Gets the node corresponding to the field `arguments`. */ + ArgumentList getArguments() { ruby_call_arguments(this, result) } + + /** Gets the node corresponding to the field `block`. */ + AstNode getBlock() { ruby_call_block(this, result) } + + /** Gets the node corresponding to the field `method`. */ + AstNode getMethod() { ruby_call_def(this, result, _) } + + /** Gets the node corresponding to the field `receiver`. */ + AstNode getReceiver() { ruby_call_receiver(this, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_call_arguments(this, result) or + ruby_call_block(this, result) or + ruby_call_def(this, result, _) or + ruby_call_receiver(this, result) + } + } + + /** A class representing `case` nodes. */ + class Case extends @ruby_case__, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Case" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_case_def(this, result) } + + /** Gets the node corresponding to the field `value`. */ + UnderscoreStatement getValue() { ruby_case_value(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_case_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_case_value(this, result) or ruby_case_child(this, _, result) + } + } + + /** A class representing `chained_string` nodes. */ + class ChainedString extends @ruby_chained_string, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "ChainedString" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_chained_string_def(this, result) } + + /** Gets the `i`th child of this node. */ + String getChild(int i) { ruby_chained_string_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_chained_string_child(this, _, result) } + } + + /** A class representing `character` tokens. */ + class Character extends @ruby_token_character, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Character" } + } + + /** A class representing `class` nodes. */ + class Class extends @ruby_class, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Class" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_class_def(this, _, result) } + + /** Gets the node corresponding to the field `name`. */ + AstNode getName() { ruby_class_def(this, result, _) } + + /** Gets the node corresponding to the field `superclass`. */ + Superclass getSuperclass() { ruby_class_superclass(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_class_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_class_def(this, result, _) or + ruby_class_superclass(this, result) or + ruby_class_child(this, _, result) + } + } + + /** A class representing `class_variable` tokens. */ + class ClassVariable extends @ruby_token_class_variable, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "ClassVariable" } + } + + /** A class representing `comment` tokens. */ + class Comment extends @ruby_token_comment, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Comment" } + } + + /** A class representing `complex` tokens. */ + class Complex extends @ruby_token_complex, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Complex" } + } + + /** A class representing `conditional` nodes. */ + class Conditional extends @ruby_conditional, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Conditional" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_conditional_def(this, _, _, _, result) } + + /** Gets the node corresponding to the field `alternative`. */ + UnderscoreArg getAlternative() { ruby_conditional_def(this, result, _, _, _) } + + /** Gets the node corresponding to the field `condition`. */ + UnderscoreArg getCondition() { ruby_conditional_def(this, _, result, _, _) } + + /** Gets the node corresponding to the field `consequence`. */ + UnderscoreArg getConsequence() { ruby_conditional_def(this, _, _, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_conditional_def(this, result, _, _, _) or + ruby_conditional_def(this, _, result, _, _) or + ruby_conditional_def(this, _, _, result, _) + } + } + + /** A class representing `constant` tokens. */ + class Constant extends @ruby_token_constant, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Constant" } + } + + /** A class representing `delimited_symbol` nodes. */ + class DelimitedSymbol extends @ruby_delimited_symbol, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "DelimitedSymbol" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_delimited_symbol_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_delimited_symbol_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_delimited_symbol_child(this, _, result) } + } + + /** A class representing `destructured_left_assignment` nodes. */ + class DestructuredLeftAssignment extends @ruby_destructured_left_assignment, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "DestructuredLeftAssignment" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_destructured_left_assignment_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_destructured_left_assignment_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_destructured_left_assignment_child(this, _, result) } + } + + /** A class representing `destructured_parameter` nodes. */ + class DestructuredParameter extends @ruby_destructured_parameter, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "DestructuredParameter" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_destructured_parameter_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_destructured_parameter_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_destructured_parameter_child(this, _, result) } + } + + /** A class representing `do` nodes. */ + class Do extends @ruby_do, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Do" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_do_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_do_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_do_child(this, _, result) } + } + + /** A class representing `do_block` nodes. */ + class DoBlock extends @ruby_do_block, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "DoBlock" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_do_block_def(this, result) } + + /** Gets the node corresponding to the field `parameters`. */ + BlockParameters getParameters() { ruby_do_block_parameters(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_do_block_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_do_block_parameters(this, result) or ruby_do_block_child(this, _, result) + } + } + + /** A class representing `element_reference` nodes. */ + class ElementReference extends @ruby_element_reference, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "ElementReference" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_element_reference_def(this, _, result) } + + /** Gets the node corresponding to the field `object`. */ + UnderscorePrimary getObject() { ruby_element_reference_def(this, result, _) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_element_reference_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_element_reference_def(this, result, _) or ruby_element_reference_child(this, _, result) + } + } + + /** A class representing `else` nodes. */ + class Else extends @ruby_else, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Else" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_else_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_else_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_else_child(this, _, result) } + } + + /** A class representing `elsif` nodes. */ + class Elsif extends @ruby_elsif, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Elsif" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_elsif_def(this, _, result) } + + /** Gets the node corresponding to the field `alternative`. */ + AstNode getAlternative() { ruby_elsif_alternative(this, result) } + + /** Gets the node corresponding to the field `condition`. */ + UnderscoreStatement getCondition() { ruby_elsif_def(this, result, _) } + + /** Gets the node corresponding to the field `consequence`. */ + Then getConsequence() { ruby_elsif_consequence(this, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_elsif_alternative(this, result) or + ruby_elsif_def(this, result, _) or + ruby_elsif_consequence(this, result) + } + } + + /** A class representing `empty_statement` tokens. */ + class EmptyStatement extends @ruby_token_empty_statement, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "EmptyStatement" } + } + + /** A class representing `end_block` nodes. */ + class EndBlock extends @ruby_end_block, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "EndBlock" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_end_block_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_end_block_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_end_block_child(this, _, result) } + } + + /** A class representing `ensure` nodes. */ + class Ensure extends @ruby_ensure, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Ensure" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_ensure_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_ensure_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_ensure_child(this, _, result) } + } + + /** A class representing `escape_sequence` tokens. */ + class EscapeSequence extends @ruby_token_escape_sequence, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "EscapeSequence" } + } + + /** A class representing `exception_variable` nodes. */ + class ExceptionVariable extends @ruby_exception_variable, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "ExceptionVariable" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_exception_variable_def(this, _, result) } + + /** Gets the child of this node. */ + UnderscoreLhs getChild() { ruby_exception_variable_def(this, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_exception_variable_def(this, result, _) } + } + + /** A class representing `exceptions` nodes. */ + class Exceptions extends @ruby_exceptions, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Exceptions" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_exceptions_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_exceptions_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_exceptions_child(this, _, result) } + } + + /** A class representing `false` tokens. */ + class False extends @ruby_token_false, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "False" } + } + + /** A class representing `float` tokens. */ + class Float extends @ruby_token_float, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Float" } + } + + /** A class representing `for` nodes. */ + class For extends @ruby_for, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "For" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_for_def(this, _, _, _, result) } + + /** Gets the node corresponding to the field `body`. */ + Do getBody() { ruby_for_def(this, result, _, _, _) } + + /** Gets the node corresponding to the field `pattern`. */ + AstNode getPattern() { ruby_for_def(this, _, result, _, _) } + + /** Gets the node corresponding to the field `value`. */ + In getValue() { ruby_for_def(this, _, _, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_for_def(this, result, _, _, _) or + ruby_for_def(this, _, result, _, _) or + ruby_for_def(this, _, _, result, _) + } + } + + /** A class representing `forward_argument` tokens. */ + class ForwardArgument extends @ruby_token_forward_argument, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "ForwardArgument" } + } + + /** A class representing `forward_parameter` tokens. */ + class ForwardParameter extends @ruby_token_forward_parameter, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "ForwardParameter" } + } + + /** A class representing `global_variable` tokens. */ + class GlobalVariable extends @ruby_token_global_variable, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "GlobalVariable" } + } + + /** A class representing `hash` nodes. */ + class Hash extends @ruby_hash, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Hash" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_hash_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_hash_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_hash_child(this, _, result) } + } + + /** A class representing `hash_key_symbol` tokens. */ + class HashKeySymbol extends @ruby_token_hash_key_symbol, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "HashKeySymbol" } + } + + /** A class representing `hash_splat_argument` nodes. */ + class HashSplatArgument extends @ruby_hash_splat_argument, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "HashSplatArgument" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_hash_splat_argument_def(this, _, result) } + + /** Gets the child of this node. */ + UnderscoreArg getChild() { ruby_hash_splat_argument_def(this, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_hash_splat_argument_def(this, result, _) } + } + + /** A class representing `hash_splat_parameter` nodes. */ + class HashSplatParameter extends @ruby_hash_splat_parameter, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "HashSplatParameter" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_hash_splat_parameter_def(this, result) } + + /** Gets the node corresponding to the field `name`. */ + Identifier getName() { ruby_hash_splat_parameter_name(this, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_hash_splat_parameter_name(this, result) } + } + + /** A class representing `heredoc_beginning` tokens. */ + class HeredocBeginning extends @ruby_token_heredoc_beginning, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "HeredocBeginning" } + } + + /** A class representing `heredoc_body` nodes. */ + class HeredocBody extends @ruby_heredoc_body, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "HeredocBody" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_heredoc_body_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_heredoc_body_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_heredoc_body_child(this, _, result) } + } + + /** A class representing `heredoc_content` tokens. */ + class HeredocContent extends @ruby_token_heredoc_content, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "HeredocContent" } + } + + /** A class representing `heredoc_end` tokens. */ + class HeredocEnd extends @ruby_token_heredoc_end, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "HeredocEnd" } + } + + /** A class representing `identifier` tokens. */ + class Identifier extends @ruby_token_identifier, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Identifier" } + } + + /** A class representing `if` nodes. */ + class If extends @ruby_if, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "If" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_if_def(this, _, result) } + + /** Gets the node corresponding to the field `alternative`. */ + AstNode getAlternative() { ruby_if_alternative(this, result) } + + /** Gets the node corresponding to the field `condition`. */ + UnderscoreStatement getCondition() { ruby_if_def(this, result, _) } + + /** Gets the node corresponding to the field `consequence`. */ + Then getConsequence() { ruby_if_consequence(this, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_if_alternative(this, result) or + ruby_if_def(this, result, _) or + ruby_if_consequence(this, result) + } + } + + /** A class representing `if_modifier` nodes. */ + class IfModifier extends @ruby_if_modifier, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "IfModifier" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_if_modifier_def(this, _, _, result) } + + /** Gets the node corresponding to the field `body`. */ + UnderscoreStatement getBody() { ruby_if_modifier_def(this, result, _, _) } + + /** Gets the node corresponding to the field `condition`. */ + AstNode getCondition() { ruby_if_modifier_def(this, _, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_if_modifier_def(this, result, _, _) or ruby_if_modifier_def(this, _, result, _) + } + } + + /** A class representing `in` nodes. */ + class In extends @ruby_in, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "In" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_in_def(this, _, result) } + + /** Gets the child of this node. */ + UnderscoreArg getChild() { ruby_in_def(this, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_in_def(this, result, _) } + } + + /** A class representing `instance_variable` tokens. */ + class InstanceVariable extends @ruby_token_instance_variable, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "InstanceVariable" } + } + + /** A class representing `integer` tokens. */ + class Integer extends @ruby_token_integer, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Integer" } + } + + /** A class representing `interpolation` nodes. */ + class Interpolation extends @ruby_interpolation, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Interpolation" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_interpolation_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_interpolation_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_interpolation_child(this, _, result) } + } + + /** A class representing `keyword_parameter` nodes. */ + class KeywordParameter extends @ruby_keyword_parameter, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "KeywordParameter" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_keyword_parameter_def(this, _, result) } + + /** Gets the node corresponding to the field `name`. */ + Identifier getName() { ruby_keyword_parameter_def(this, result, _) } + + /** Gets the node corresponding to the field `value`. */ + UnderscoreArg getValue() { ruby_keyword_parameter_value(this, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_keyword_parameter_def(this, result, _) or ruby_keyword_parameter_value(this, result) + } + } + + /** A class representing `lambda` nodes. */ + class Lambda extends @ruby_lambda, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Lambda" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_lambda_def(this, _, result) } + + /** Gets the node corresponding to the field `body`. */ + AstNode getBody() { ruby_lambda_def(this, result, _) } + + /** Gets the node corresponding to the field `parameters`. */ + LambdaParameters getParameters() { ruby_lambda_parameters(this, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_lambda_def(this, result, _) or ruby_lambda_parameters(this, result) + } + } + + /** A class representing `lambda_parameters` nodes. */ + class LambdaParameters extends @ruby_lambda_parameters, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "LambdaParameters" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_lambda_parameters_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_lambda_parameters_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_lambda_parameters_child(this, _, result) } + } + + /** A class representing `left_assignment_list` nodes. */ + class LeftAssignmentList extends @ruby_left_assignment_list, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "LeftAssignmentList" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_left_assignment_list_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_left_assignment_list_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_left_assignment_list_child(this, _, result) } + } + + /** A class representing `method` nodes. */ + class Method extends @ruby_method, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Method" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_method_def(this, _, result) } + + /** Gets the node corresponding to the field `name`. */ + UnderscoreMethodName getName() { ruby_method_def(this, result, _) } + + /** Gets the node corresponding to the field `parameters`. */ + MethodParameters getParameters() { ruby_method_parameters(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_method_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_method_def(this, result, _) or + ruby_method_parameters(this, result) or + ruby_method_child(this, _, result) + } + } + + /** A class representing `method_parameters` nodes. */ + class MethodParameters extends @ruby_method_parameters, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "MethodParameters" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_method_parameters_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_method_parameters_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_method_parameters_child(this, _, result) } + } + + /** A class representing `module` nodes. */ + class Module extends @ruby_module, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Module" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_module_def(this, _, result) } + + /** Gets the node corresponding to the field `name`. */ + AstNode getName() { ruby_module_def(this, result, _) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_module_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_module_def(this, result, _) or ruby_module_child(this, _, result) + } + } + + /** A class representing `next` nodes. */ + class Next extends @ruby_next, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Next" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_next_def(this, result) } + + /** Gets the child of this node. */ + ArgumentList getChild() { ruby_next_child(this, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_next_child(this, result) } + } + + /** A class representing `nil` tokens. */ + class Nil extends @ruby_token_nil, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Nil" } + } + + /** A class representing `operator` tokens. */ + class Operator extends @ruby_token_operator, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Operator" } + } + + /** A class representing `operator_assignment` nodes. */ + class OperatorAssignment extends @ruby_operator_assignment, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "OperatorAssignment" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_operator_assignment_def(this, _, _, _, result) } + + /** Gets the node corresponding to the field `left`. */ + UnderscoreLhs getLeft() { ruby_operator_assignment_def(this, result, _, _, _) } + + /** Gets the node corresponding to the field `operator`. */ + string getOperator() { + exists(int value | ruby_operator_assignment_def(this, _, value, _, _) | + result = "%=" and value = 0 + or + result = "&&=" and value = 1 + or + result = "&=" and value = 2 + or + result = "**=" and value = 3 + or + result = "*=" and value = 4 + or + result = "+=" and value = 5 + or + result = "-=" and value = 6 + or + result = "/=" and value = 7 + or + result = "<<=" and value = 8 + or + result = ">>=" and value = 9 + or + result = "^=" and value = 10 + or + result = "|=" and value = 11 + or + result = "||=" and value = 12 + ) + } + + /** Gets the node corresponding to the field `right`. */ + AstNode getRight() { ruby_operator_assignment_def(this, _, _, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_operator_assignment_def(this, result, _, _, _) or + ruby_operator_assignment_def(this, _, _, result, _) + } + } + + /** A class representing `optional_parameter` nodes. */ + class OptionalParameter extends @ruby_optional_parameter, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "OptionalParameter" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_optional_parameter_def(this, _, _, result) } + + /** Gets the node corresponding to the field `name`. */ + Identifier getName() { ruby_optional_parameter_def(this, result, _, _) } + + /** Gets the node corresponding to the field `value`. */ + UnderscoreArg getValue() { ruby_optional_parameter_def(this, _, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_optional_parameter_def(this, result, _, _) or + ruby_optional_parameter_def(this, _, result, _) + } + } + + /** A class representing `pair` nodes. */ + class Pair extends @ruby_pair, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Pair" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_pair_def(this, _, _, result) } + + /** Gets the node corresponding to the field `key`. */ + AstNode getKey() { ruby_pair_def(this, result, _, _) } + + /** Gets the node corresponding to the field `value`. */ + UnderscoreArg getValue() { ruby_pair_def(this, _, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_pair_def(this, result, _, _) or ruby_pair_def(this, _, result, _) + } + } + + /** A class representing `parenthesized_statements` nodes. */ + class ParenthesizedStatements extends @ruby_parenthesized_statements, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "ParenthesizedStatements" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_parenthesized_statements_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_parenthesized_statements_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_parenthesized_statements_child(this, _, result) } + } + + /** A class representing `pattern` nodes. */ + class Pattern extends @ruby_pattern, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Pattern" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_pattern_def(this, _, result) } + + /** Gets the child of this node. */ + AstNode getChild() { ruby_pattern_def(this, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_pattern_def(this, result, _) } + } + + /** A class representing `program` nodes. */ + class Program extends @ruby_program, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Program" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_program_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_program_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_program_child(this, _, result) } + } + + /** A class representing `range` nodes. */ + class Range extends @ruby_range, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Range" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_range_def(this, _, result) } + + /** Gets the node corresponding to the field `begin`. */ + UnderscoreArg getBegin() { ruby_range_begin(this, result) } + + /** Gets the node corresponding to the field `end`. */ + UnderscoreArg getEnd() { ruby_range_end(this, result) } + + /** Gets the node corresponding to the field `operator`. */ + string getOperator() { + exists(int value | ruby_range_def(this, value, _) | + result = ".." and value = 0 + or + result = "..." and value = 1 + ) + } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_range_begin(this, result) or ruby_range_end(this, result) + } + } + + /** A class representing `rational` nodes. */ + class Rational extends @ruby_rational, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Rational" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_rational_def(this, _, result) } + + /** Gets the child of this node. */ + AstNode getChild() { ruby_rational_def(this, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_rational_def(this, result, _) } + } + + /** A class representing `redo` nodes. */ + class Redo extends @ruby_redo, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Redo" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_redo_def(this, result) } + + /** Gets the child of this node. */ + ArgumentList getChild() { ruby_redo_child(this, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_redo_child(this, result) } + } + + /** A class representing `regex` nodes. */ + class Regex extends @ruby_regex, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Regex" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_regex_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_regex_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_regex_child(this, _, result) } + } + + /** A class representing `rescue` nodes. */ + class Rescue extends @ruby_rescue, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Rescue" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_rescue_def(this, result) } + + /** Gets the node corresponding to the field `body`. */ + Then getBody() { ruby_rescue_body(this, result) } + + /** Gets the node corresponding to the field `exceptions`. */ + Exceptions getExceptions() { ruby_rescue_exceptions(this, result) } + + /** Gets the node corresponding to the field `variable`. */ + ExceptionVariable getVariable() { ruby_rescue_variable(this, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_rescue_body(this, result) or + ruby_rescue_exceptions(this, result) or + ruby_rescue_variable(this, result) + } + } + + /** A class representing `rescue_modifier` nodes. */ + class RescueModifier extends @ruby_rescue_modifier, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "RescueModifier" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_rescue_modifier_def(this, _, _, result) } + + /** Gets the node corresponding to the field `body`. */ + UnderscoreStatement getBody() { ruby_rescue_modifier_def(this, result, _, _) } + + /** Gets the node corresponding to the field `handler`. */ + AstNode getHandler() { ruby_rescue_modifier_def(this, _, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_rescue_modifier_def(this, result, _, _) or ruby_rescue_modifier_def(this, _, result, _) + } + } + + /** A class representing `rest_assignment` nodes. */ + class RestAssignment extends @ruby_rest_assignment, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "RestAssignment" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_rest_assignment_def(this, result) } + + /** Gets the child of this node. */ + UnderscoreLhs getChild() { ruby_rest_assignment_child(this, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_rest_assignment_child(this, result) } + } + + /** A class representing `retry` nodes. */ + class Retry extends @ruby_retry, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Retry" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_retry_def(this, result) } + + /** Gets the child of this node. */ + ArgumentList getChild() { ruby_retry_child(this, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_retry_child(this, result) } + } + + /** A class representing `return` nodes. */ + class Return extends @ruby_return, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Return" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_return_def(this, result) } + + /** Gets the child of this node. */ + ArgumentList getChild() { ruby_return_child(this, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_return_child(this, result) } + } + + /** A class representing `right_assignment_list` nodes. */ + class RightAssignmentList extends @ruby_right_assignment_list, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "RightAssignmentList" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_right_assignment_list_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_right_assignment_list_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_right_assignment_list_child(this, _, result) } + } + + /** A class representing `scope_resolution` nodes. */ + class ScopeResolution extends @ruby_scope_resolution, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "ScopeResolution" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_scope_resolution_def(this, _, result) } + + /** Gets the node corresponding to the field `name`. */ + AstNode getName() { ruby_scope_resolution_def(this, result, _) } + + /** Gets the node corresponding to the field `scope`. */ + UnderscorePrimary getScope() { ruby_scope_resolution_scope(this, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_scope_resolution_def(this, result, _) or ruby_scope_resolution_scope(this, result) + } + } + + /** A class representing `self` tokens. */ + class Self extends @ruby_token_self, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Self" } + } + + /** A class representing `setter` nodes. */ + class Setter extends @ruby_setter, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Setter" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_setter_def(this, _, result) } + + /** Gets the node corresponding to the field `name`. */ + Identifier getName() { ruby_setter_def(this, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_setter_def(this, result, _) } + } + + /** A class representing `simple_symbol` tokens. */ + class SimpleSymbol extends @ruby_token_simple_symbol, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "SimpleSymbol" } + } + + /** A class representing `singleton_class` nodes. */ + class SingletonClass extends @ruby_singleton_class, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "SingletonClass" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_singleton_class_def(this, _, result) } + + /** Gets the node corresponding to the field `value`. */ + UnderscoreArg getValue() { ruby_singleton_class_def(this, result, _) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_singleton_class_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_singleton_class_def(this, result, _) or ruby_singleton_class_child(this, _, result) + } + } + + /** A class representing `singleton_method` nodes. */ + class SingletonMethod extends @ruby_singleton_method, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "SingletonMethod" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_singleton_method_def(this, _, _, result) } + + /** Gets the node corresponding to the field `name`. */ + UnderscoreMethodName getName() { ruby_singleton_method_def(this, result, _, _) } + + /** Gets the node corresponding to the field `object`. */ + AstNode getObject() { ruby_singleton_method_def(this, _, result, _) } + + /** Gets the node corresponding to the field `parameters`. */ + MethodParameters getParameters() { ruby_singleton_method_parameters(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_singleton_method_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_singleton_method_def(this, result, _, _) or + ruby_singleton_method_def(this, _, result, _) or + ruby_singleton_method_parameters(this, result) or + ruby_singleton_method_child(this, _, result) + } + } + + /** A class representing `splat_argument` nodes. */ + class SplatArgument extends @ruby_splat_argument, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "SplatArgument" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_splat_argument_def(this, _, result) } + + /** Gets the child of this node. */ + UnderscoreArg getChild() { ruby_splat_argument_def(this, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_splat_argument_def(this, result, _) } + } + + /** A class representing `splat_parameter` nodes. */ + class SplatParameter extends @ruby_splat_parameter, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "SplatParameter" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_splat_parameter_def(this, result) } + + /** Gets the node corresponding to the field `name`. */ + Identifier getName() { ruby_splat_parameter_name(this, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_splat_parameter_name(this, result) } + } + + /** A class representing `string` nodes. */ + class String extends @ruby_string__, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "String" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_string_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_string_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_string_child(this, _, result) } + } + + /** A class representing `string_array` nodes. */ + class StringArray extends @ruby_string_array, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "StringArray" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_string_array_def(this, result) } + + /** Gets the `i`th child of this node. */ + BareString getChild(int i) { ruby_string_array_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_string_array_child(this, _, result) } + } + + /** A class representing `string_content` tokens. */ + class StringContent extends @ruby_token_string_content, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "StringContent" } + } + + /** A class representing `subshell` nodes. */ + class Subshell extends @ruby_subshell, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Subshell" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_subshell_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_subshell_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_subshell_child(this, _, result) } + } + + /** A class representing `super` tokens. */ + class Super extends @ruby_token_super, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Super" } + } + + /** A class representing `superclass` nodes. */ + class Superclass extends @ruby_superclass, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Superclass" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_superclass_def(this, _, result) } + + /** Gets the child of this node. */ + AstNode getChild() { ruby_superclass_def(this, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_superclass_def(this, result, _) } + } + + /** A class representing `symbol_array` nodes. */ + class SymbolArray extends @ruby_symbol_array, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "SymbolArray" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_symbol_array_def(this, result) } + + /** Gets the `i`th child of this node. */ + BareSymbol getChild(int i) { ruby_symbol_array_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_symbol_array_child(this, _, result) } + } + + /** A class representing `then` nodes. */ + class Then extends @ruby_then, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Then" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_then_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { ruby_then_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_then_child(this, _, result) } + } + + /** A class representing `true` tokens. */ + class True extends @ruby_token_true, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "True" } + } + + /** A class representing `unary` nodes. */ + class Unary extends @ruby_unary, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Unary" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_unary_def(this, _, _, result) } + + /** Gets the node corresponding to the field `operand`. */ + AstNode getOperand() { ruby_unary_def(this, result, _, _) } + + /** Gets the node corresponding to the field `operator`. */ + string getOperator() { + exists(int value | ruby_unary_def(this, _, value, _) | + result = "!" and value = 0 + or + result = "+" and value = 1 + or + result = "-" and value = 2 + or + result = "defined?" and value = 3 + or + result = "not" and value = 4 + or + result = "~" and value = 5 + ) + } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_unary_def(this, result, _, _) } + } + + /** A class representing `undef` nodes. */ + class Undef extends @ruby_undef, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Undef" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_undef_def(this, result) } + + /** Gets the `i`th child of this node. */ + UnderscoreMethodName getChild(int i) { ruby_undef_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_undef_child(this, _, result) } + } + + /** A class representing `uninterpreted` tokens. */ + class Uninterpreted extends @ruby_token_uninterpreted, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Uninterpreted" } + } + + /** A class representing `unless` nodes. */ + class Unless extends @ruby_unless, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Unless" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_unless_def(this, _, result) } + + /** Gets the node corresponding to the field `alternative`. */ + AstNode getAlternative() { ruby_unless_alternative(this, result) } + + /** Gets the node corresponding to the field `condition`. */ + UnderscoreStatement getCondition() { ruby_unless_def(this, result, _) } + + /** Gets the node corresponding to the field `consequence`. */ + Then getConsequence() { ruby_unless_consequence(this, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_unless_alternative(this, result) or + ruby_unless_def(this, result, _) or + ruby_unless_consequence(this, result) + } + } + + /** A class representing `unless_modifier` nodes. */ + class UnlessModifier extends @ruby_unless_modifier, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "UnlessModifier" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_unless_modifier_def(this, _, _, result) } + + /** Gets the node corresponding to the field `body`. */ + UnderscoreStatement getBody() { ruby_unless_modifier_def(this, result, _, _) } + + /** Gets the node corresponding to the field `condition`. */ + AstNode getCondition() { ruby_unless_modifier_def(this, _, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_unless_modifier_def(this, result, _, _) or ruby_unless_modifier_def(this, _, result, _) + } + } + + /** A class representing `until` nodes. */ + class Until extends @ruby_until, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Until" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_until_def(this, _, _, result) } + + /** Gets the node corresponding to the field `body`. */ + Do getBody() { ruby_until_def(this, result, _, _) } + + /** Gets the node corresponding to the field `condition`. */ + UnderscoreStatement getCondition() { ruby_until_def(this, _, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_until_def(this, result, _, _) or ruby_until_def(this, _, result, _) + } + } + + /** A class representing `until_modifier` nodes. */ + class UntilModifier extends @ruby_until_modifier, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "UntilModifier" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_until_modifier_def(this, _, _, result) } + + /** Gets the node corresponding to the field `body`. */ + UnderscoreStatement getBody() { ruby_until_modifier_def(this, result, _, _) } + + /** Gets the node corresponding to the field `condition`. */ + AstNode getCondition() { ruby_until_modifier_def(this, _, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_until_modifier_def(this, result, _, _) or ruby_until_modifier_def(this, _, result, _) + } + } + + /** A class representing `when` nodes. */ + class When extends @ruby_when, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "When" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_when_def(this, result) } + + /** Gets the node corresponding to the field `body`. */ + Then getBody() { ruby_when_body(this, result) } + + /** Gets the node corresponding to the field `pattern`. */ + Pattern getPattern(int i) { ruby_when_pattern(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_when_body(this, result) or ruby_when_pattern(this, _, result) + } + } + + /** A class representing `while` nodes. */ + class While extends @ruby_while, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "While" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_while_def(this, _, _, result) } + + /** Gets the node corresponding to the field `body`. */ + Do getBody() { ruby_while_def(this, result, _, _) } + + /** Gets the node corresponding to the field `condition`. */ + UnderscoreStatement getCondition() { ruby_while_def(this, _, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_while_def(this, result, _, _) or ruby_while_def(this, _, result, _) + } + } + + /** A class representing `while_modifier` nodes. */ + class WhileModifier extends @ruby_while_modifier, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "WhileModifier" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_while_modifier_def(this, _, _, result) } + + /** Gets the node corresponding to the field `body`. */ + UnderscoreStatement getBody() { ruby_while_modifier_def(this, result, _, _) } + + /** Gets the node corresponding to the field `condition`. */ + AstNode getCondition() { ruby_while_modifier_def(this, _, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { + ruby_while_modifier_def(this, result, _, _) or ruby_while_modifier_def(this, _, result, _) + } + } + + /** A class representing `yield` nodes. */ + class Yield extends @ruby_yield, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Yield" } + + /** Gets the location of this element. */ + override Location getLocation() { ruby_yield_def(this, result) } + + /** Gets the child of this node. */ + ArgumentList getChild() { ruby_yield_child(this, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { ruby_yield_child(this, result) } + } +} + +module Erb { + /** The base class for all AST nodes */ + class AstNode extends @erb_ast_node { + /** Gets a string representation of this element. */ + string toString() { result = this.getAPrimaryQlClass() } + + /** Gets the location of this element. */ + Location getLocation() { none() } + + /** Gets the parent of this element. */ + AstNode getParent() { erb_ast_node_parent(this, result, _) } + + /** Gets the index of this node among the children of its parent. */ + int getParentIndex() { erb_ast_node_parent(this, _, result) } + + /** Gets a field or child node of this node. */ + AstNode getAFieldOrChild() { none() } + + /** Gets the name of the primary QL class for this element. */ + string getAPrimaryQlClass() { result = "???" } + + /** Gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs. */ + string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") } + } + + /** A token. */ + class Token extends @erb_token, AstNode { + /** Gets the value of this token. */ + string getValue() { erb_tokeninfo(this, _, result, _) } + + /** Gets the location of this token. */ + override Location getLocation() { erb_tokeninfo(this, _, _, result) } + + /** Gets a string representation of this element. */ + override string toString() { result = this.getValue() } + + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Token" } + } + + /** A reserved word. */ + class ReservedWord extends @erb_reserved_word, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "ReservedWord" } + } + + /** A class representing `code` tokens. */ + class Code extends @erb_token_code, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Code" } + } + + /** A class representing `comment` tokens. */ + class Comment extends @erb_token_comment, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Comment" } + } + + /** A class representing `comment_directive` nodes. */ + class CommentDirective extends @erb_comment_directive, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "CommentDirective" } + + /** Gets the location of this element. */ + override Location getLocation() { erb_comment_directive_def(this, _, result) } + + /** Gets the child of this node. */ + Comment getChild() { erb_comment_directive_def(this, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { erb_comment_directive_def(this, result, _) } + } + + /** A class representing `content` tokens. */ + class Content extends @erb_token_content, Token { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Content" } + } + + /** A class representing `directive` nodes. */ + class Directive extends @erb_directive, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Directive" } + + /** Gets the location of this element. */ + override Location getLocation() { erb_directive_def(this, _, result) } + + /** Gets the child of this node. */ + Code getChild() { erb_directive_def(this, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { erb_directive_def(this, result, _) } + } + + /** A class representing `graphql_directive` nodes. */ + class GraphqlDirective extends @erb_graphql_directive, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "GraphqlDirective" } + + /** Gets the location of this element. */ + override Location getLocation() { erb_graphql_directive_def(this, _, result) } + + /** Gets the child of this node. */ + Code getChild() { erb_graphql_directive_def(this, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { erb_graphql_directive_def(this, result, _) } + } + + /** A class representing `output_directive` nodes. */ + class OutputDirective extends @erb_output_directive, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "OutputDirective" } + + /** Gets the location of this element. */ + override Location getLocation() { erb_output_directive_def(this, _, result) } + + /** Gets the child of this node. */ + Code getChild() { erb_output_directive_def(this, result, _) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { erb_output_directive_def(this, result, _) } + } + + /** A class representing `template` nodes. */ + class Template extends @erb_template, AstNode { + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Template" } + + /** Gets the location of this element. */ + override Location getLocation() { erb_template_def(this, result) } + + /** Gets the `i`th child of this node. */ + AstNode getChild(int i) { erb_template_child(this, i, result) } + + /** Gets a field or child node of this node. */ + override AstNode getAFieldOrChild() { erb_template_child(this, _, result) } + } +} diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Variable.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Variable.qll new file mode 100644 index 00000000000..8d108bbbd52 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Variable.qll @@ -0,0 +1,642 @@ +private import TreeSitter +private import codeql.Locations +private import codeql.ruby.AST +private import codeql.ruby.ast.internal.AST +private import codeql.ruby.ast.internal.Parameter +private import codeql.ruby.ast.internal.Scope +private import codeql.ruby.ast.internal.Synthesis + +/** + * Holds if `n` is in the left-hand-side of an explicit assignment `assignment`. + */ +predicate explicitAssignmentNode(Ruby::AstNode n, Ruby::AstNode assignment) { + n = assignment.(Ruby::Assignment).getLeft() + or + n = assignment.(Ruby::OperatorAssignment).getLeft() + or + exists(Ruby::AstNode parent | + parent = n.getParent() and + explicitAssignmentNode(parent, assignment) + | + parent instanceof Ruby::DestructuredLeftAssignment + or + parent instanceof Ruby::LeftAssignmentList + or + parent instanceof Ruby::RestAssignment + ) +} + +/** Holds if `n` is inside an implicit assignment. */ +predicate implicitAssignmentNode(Ruby::AstNode n) { + n = any(Ruby::ExceptionVariable ev).getChild() + or + n = any(Ruby::For for).getPattern() + or + implicitAssignmentNode(n.getParent()) +} + +/** Holds if `n` is inside a parameter. */ +predicate implicitParameterAssignmentNode(Ruby::AstNode n, Callable::Range c) { + n = c.getParameter(_) + or + implicitParameterAssignmentNode(n.getParent().(Ruby::DestructuredParameter), c) +} + +private predicate instanceVariableAccess( + Ruby::InstanceVariable var, string name, Scope::Range scope, boolean instance +) { + name = var.getValue() and + scope = enclosingModuleOrClass(var) and + if hasEnclosingMethod(var) then instance = true else instance = false +} + +private predicate classVariableAccess(Ruby::ClassVariable var, string name, Scope::Range scope) { + name = var.getValue() and + scope = enclosingModuleOrClass(var) +} + +private predicate hasEnclosingMethod(Ruby::AstNode node) { + exists(Scope::Range s | scopeOf(node) = s and exists(s.getEnclosingMethod())) +} + +private ModuleBase::Range enclosingModuleOrClass(Ruby::AstNode node) { + exists(Scope::Range s | scopeOf(node) = s and result = s.getEnclosingModule()) +} + +private predicate parameterAssignment(Callable::Range scope, string name, Ruby::Identifier i) { + implicitParameterAssignmentNode(i, scope) and + name = i.getValue() +} + +/** Holds if `scope` defines `name` in its parameter declaration at `i`. */ +private predicate scopeDefinesParameterVariable( + Callable::Range scope, string name, Ruby::Identifier i +) { + // In case of overlapping parameter names (e.g. `_`), only the first + // parameter will give rise to a variable + i = + min(Ruby::Identifier other | + parameterAssignment(scope, name, other) + | + other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn() + ) + or + exists(Parameter::Range p | + p = scope.getParameter(_) and + name = i.getValue() + | + i = p.(Ruby::BlockParameter).getName() or + i = p.(Ruby::HashSplatParameter).getName() or + i = p.(Ruby::KeywordParameter).getName() or + i = p.(Ruby::OptionalParameter).getName() or + i = p.(Ruby::SplatParameter).getName() + ) +} + +/** Holds if `name` is assigned in `scope` at `i`. */ +private predicate scopeAssigns(Scope::Range scope, string name, Ruby::Identifier i) { + (explicitAssignmentNode(i, _) or implicitAssignmentNode(i)) and + name = i.getValue() and + scope = scopeOf(i) +} + +cached +private module Cached { + cached + newtype TVariable = + TGlobalVariable(string name) { name = any(Ruby::GlobalVariable var).getValue() } or + TClassVariable(Scope::Range scope, string name, Ruby::AstNode decl) { + decl = + min(Ruby::ClassVariable other | + classVariableAccess(other, name, scope) + | + other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn() + ) + } or + TInstanceVariable(Scope::Range scope, string name, boolean instance, Ruby::AstNode decl) { + decl = + min(Ruby::InstanceVariable other | + instanceVariableAccess(other, name, scope, instance) + | + other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn() + ) + } or + TLocalVariableReal(Scope::Range scope, string name, Ruby::Identifier i) { + scopeDefinesParameterVariable(scope, name, i) + or + i = + min(Ruby::Identifier other | + scopeAssigns(scope, name, other) + | + other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn() + ) and + not scopeDefinesParameterVariable(scope, name, _) and + not inherits(scope, name, _) + } or + TSelfVariable(SelfBase::Range scope) or + TLocalVariableSynth(AstNode n, int i) { any(Synthesis s).localVariable(n, i) } + + // Db types that can be vcalls + private class VcallToken = + @ruby_scope_resolution or @ruby_token_constant or @ruby_token_identifier or @ruby_token_super; + + /** + * Holds if `i` is an `identifier` node occurring in the context where it + * should be considered a VCALL. VCALL is the term that MRI/Ripper uses + * internally when there's an identifier without arguments or parentheses, + * i.e. it *might* be a method call, but it might also be a variable access, + * depending on the bindings in the current scope. + * ```rb + * foo # in MRI this is a VCALL, and the predicate should hold for this + * bar() # in MRI this would be an FCALL. Tree-sitter gives us a `call` node, + * # and the `method` field will be an `identifier`, but this predicate + * # will not hold for that identifier. + * ``` + */ + cached + predicate vcall(VcallToken i) { + i = any(Ruby::ArgumentList x).getChild(_) + or + i = any(Ruby::Array x).getChild(_) + or + i = any(Ruby::Assignment x).getRight() + or + i = any(Ruby::Begin x).getChild(_) + or + i = any(Ruby::BeginBlock x).getChild(_) + or + i = any(Ruby::Binary x).getLeft() + or + i = any(Ruby::Binary x).getRight() + or + i = any(Ruby::Block x).getChild(_) + or + i = any(Ruby::BlockArgument x).getChild() + or + i = any(Ruby::Call x).getReceiver() + or + i = any(Ruby::Case x).getValue() + or + i = any(Ruby::Class x).getChild(_) + or + i = any(Ruby::Conditional x).getCondition() + or + i = any(Ruby::Conditional x).getConsequence() + or + i = any(Ruby::Conditional x).getAlternative() + or + i = any(Ruby::Do x).getChild(_) + or + i = any(Ruby::DoBlock x).getChild(_) + or + i = any(Ruby::ElementReference x).getChild(_) + or + i = any(Ruby::ElementReference x).getObject() + or + i = any(Ruby::Else x).getChild(_) + or + i = any(Ruby::Elsif x).getCondition() + or + i = any(Ruby::EndBlock x).getChild(_) + or + i = any(Ruby::Ensure x).getChild(_) + or + i = any(Ruby::Exceptions x).getChild(_) + or + i = any(Ruby::HashSplatArgument x).getChild() + or + i = any(Ruby::If x).getCondition() + or + i = any(Ruby::IfModifier x).getCondition() + or + i = any(Ruby::IfModifier x).getBody() + or + i = any(Ruby::In x).getChild() + or + i = any(Ruby::Interpolation x).getChild(_) + or + i = any(Ruby::KeywordParameter x).getValue() + or + i = any(Ruby::Method x).getChild(_) + or + i = any(Ruby::Module x).getChild(_) + or + i = any(Ruby::OperatorAssignment x).getRight() + or + i = any(Ruby::OptionalParameter x).getValue() + or + i = any(Ruby::Pair x).getKey() + or + i = any(Ruby::Pair x).getValue() + or + i = any(Ruby::ParenthesizedStatements x).getChild(_) + or + i = any(Ruby::Pattern x).getChild() + or + i = any(Ruby::Program x).getChild(_) + or + i = any(Ruby::Range x).getBegin() + or + i = any(Ruby::Range x).getEnd() + or + i = any(Ruby::RescueModifier x).getBody() + or + i = any(Ruby::RescueModifier x).getHandler() + or + i = any(Ruby::RightAssignmentList x).getChild(_) + or + i = any(Ruby::ScopeResolution x).getScope() + or + i = any(Ruby::SingletonClass x).getValue() + or + i = any(Ruby::SingletonClass x).getChild(_) + or + i = any(Ruby::SingletonMethod x).getChild(_) + or + i = any(Ruby::SingletonMethod x).getObject() + or + i = any(Ruby::SplatArgument x).getChild() + or + i = any(Ruby::Superclass x).getChild() + or + i = any(Ruby::Then x).getChild(_) + or + i = any(Ruby::Unary x).getOperand() + or + i = any(Ruby::Unless x).getCondition() + or + i = any(Ruby::UnlessModifier x).getCondition() + or + i = any(Ruby::UnlessModifier x).getBody() + or + i = any(Ruby::Until x).getCondition() + or + i = any(Ruby::UntilModifier x).getCondition() + or + i = any(Ruby::UntilModifier x).getBody() + or + i = any(Ruby::While x).getCondition() + or + i = any(Ruby::WhileModifier x).getCondition() + or + i = any(Ruby::WhileModifier x).getBody() + } + + cached + predicate access(Ruby::Identifier access, VariableReal variable) { + exists(string name | + variable.getNameImpl() = name and + name = access.getValue() + | + variable.getDeclaringScopeImpl() = scopeOf(access) and + not access.getLocation().strictlyBefore(variable.getLocationImpl()) and + // In case of overlapping parameter names, later parameters should not + // be considered accesses to the first parameter + if parameterAssignment(_, _, access) + then scopeDefinesParameterVariable(_, _, access) + else any() + or + exists(Scope::Range declScope | + variable.getDeclaringScopeImpl() = declScope and + inherits(scopeOf(access), name, declScope) + ) + ) + } + + private class Access extends Ruby::Token { + Access() { + access(this, _) or + this instanceof Ruby::GlobalVariable or + this instanceof Ruby::InstanceVariable or + this instanceof Ruby::ClassVariable or + this instanceof Ruby::Self + } + } + + cached + predicate explicitWriteAccess(Access access, Ruby::AstNode assignment) { + explicitAssignmentNode(access, assignment) + } + + cached + predicate implicitWriteAccess(Access access) { + implicitAssignmentNode(access) + or + scopeDefinesParameterVariable(_, _, access) + } + + cached + predicate isCapturedAccess(LocalVariableAccess access) { + toGenerated(access.getVariable().getDeclaringScope()) != scopeOf(toGenerated(access)) + } + + cached + predicate instanceVariableAccess(Ruby::InstanceVariable var, InstanceVariable v) { + exists(string name, Scope::Range scope, boolean instance | + v = TInstanceVariable(scope, name, instance, _) and + instanceVariableAccess(var, name, scope, instance) + ) + } + + cached + predicate classVariableAccess(Ruby::ClassVariable var, ClassVariable variable) { + exists(Scope::Range scope, string name | + variable = TClassVariable(scope, name, _) and + classVariableAccess(var, name, scope) + ) + } +} + +import Cached + +/** Holds if this scope inherits `name` from an outer scope `outer`. */ +private predicate inherits(Scope::Range scope, string name, Scope::Range outer) { + (scope instanceof Ruby::Block or scope instanceof Ruby::DoBlock) and + not scopeDefinesParameterVariable(scope, name, _) and + ( + outer = scope.getOuterScope() and + ( + scopeDefinesParameterVariable(outer, name, _) + or + exists(Ruby::Identifier i | + scopeAssigns(outer, name, i) and + i.getLocation().strictlyBefore(scope.getLocation()) + ) + ) + or + inherits(scope.getOuterScope(), name, outer) + ) +} + +abstract class VariableImpl extends TVariable { + abstract string getNameImpl(); + + final string toString() { result = this.getNameImpl() } + + abstract Location getLocationImpl(); +} + +class TVariableReal = + TGlobalVariable or TClassVariable or TInstanceVariable or TLocalVariableReal or TSelfVariable; + +class TLocalVariable = TLocalVariableReal or TLocalVariableSynth or TSelfVariable; + +/** + * This class only exists to avoid negative recursion warnings. Ideally, + * we would use `VariableImpl` directly, but that results in incorrect + * negative recursion warnings. Adding new root-defs for the predicates + * below works around this. + */ +abstract class VariableReal extends TVariableReal { + abstract string getNameImpl(); + + abstract Location getLocationImpl(); + + abstract Scope::Range getDeclaringScopeImpl(); + + final string toString() { result = this.getNameImpl() } +} + +// Convert extensions of `VariableReal` into extensions of `VariableImpl` +private class VariableRealAdapter extends VariableImpl, TVariableReal instanceof VariableReal { + final override string getNameImpl() { result = VariableReal.super.getNameImpl() } + + final override Location getLocationImpl() { result = VariableReal.super.getLocationImpl() } +} + +class LocalVariableReal extends VariableReal, TLocalVariableReal { + private Scope::Range scope; + private string name; + private Ruby::Identifier i; + + LocalVariableReal() { this = TLocalVariableReal(scope, name, i) } + + final override string getNameImpl() { result = name } + + final override Location getLocationImpl() { result = i.getLocation() } + + final override Scope::Range getDeclaringScopeImpl() { result = scope } + + final VariableAccess getDefiningAccessImpl() { toGenerated(result) = i } +} + +class LocalVariableSynth extends VariableImpl, TLocalVariableSynth { + private AstNode n; + private int i; + + LocalVariableSynth() { this = TLocalVariableSynth(n, i) } + + final override string getNameImpl() { + exists(int level | level = desugarLevel(n) | + if level > 0 then result = "__synth__" + i + "__" + level else result = "__synth__" + i + ) + } + + final override Location getLocationImpl() { result = n.getLocation() } +} + +class GlobalVariableImpl extends VariableReal, TGlobalVariable { + private string name; + + GlobalVariableImpl() { this = TGlobalVariable(name) } + + final override string getNameImpl() { result = name } + + final override Location getLocationImpl() { none() } + + final override Scope::Range getDeclaringScopeImpl() { none() } +} + +class InstanceVariableImpl extends VariableReal, TInstanceVariable { + private ModuleBase::Range scope; + private boolean instance; + private string name; + private Ruby::AstNode decl; + + InstanceVariableImpl() { this = TInstanceVariable(scope, name, instance, decl) } + + final override string getNameImpl() { result = name } + + final predicate isClassInstanceVariable() { instance = false } + + final override Location getLocationImpl() { result = decl.getLocation() } + + final override Scope::Range getDeclaringScopeImpl() { result = scope } +} + +class ClassVariableImpl extends VariableReal, TClassVariable { + private ModuleBase::Range scope; + private string name; + private Ruby::AstNode decl; + + ClassVariableImpl() { this = TClassVariable(scope, name, decl) } + + final override string getNameImpl() { result = name } + + final override Location getLocationImpl() { result = decl.getLocation() } + + final override Scope::Range getDeclaringScopeImpl() { result = scope } +} + +class SelfVariableImpl extends VariableReal, TSelfVariable { + private SelfBase::Range scope; + + SelfVariableImpl() { this = TSelfVariable(scope) } + + final override string getNameImpl() { result = "self" } + + final override Location getLocationImpl() { result = scope.getLocation() } + + final override Scope::Range getDeclaringScopeImpl() { result = scope } +} + +abstract class VariableAccessImpl extends Expr, TVariableAccess { + abstract VariableImpl getVariableImpl(); +} + +module LocalVariableAccess { + predicate range(Ruby::Identifier id, LocalVariable v) { + access(id, v) and + ( + explicitWriteAccess(id, _) + or + implicitWriteAccess(id) + or + vcall(id) + ) + } +} + +class TVariableAccessReal = + TLocalVariableAccessReal or TGlobalVariableAccess or TInstanceVariableAccess or + TClassVariableAccess; + +abstract class LocalVariableAccessImpl extends VariableAccessImpl, TLocalVariableAccess { } + +private class LocalVariableAccessReal extends LocalVariableAccessImpl, TLocalVariableAccessReal { + private Ruby::Identifier g; + private LocalVariable v; + + LocalVariableAccessReal() { this = TLocalVariableAccessReal(g, v) } + + final override LocalVariable getVariableImpl() { result = v } + + final override string toString() { result = g.getValue() } +} + +private class LocalVariableAccessSynth extends LocalVariableAccessImpl, TLocalVariableAccessSynth { + private LocalVariable v; + + LocalVariableAccessSynth() { this = TLocalVariableAccessSynth(_, _, v) } + + final override LocalVariable getVariableImpl() { result = v } + + final override string toString() { result = v.getName() } +} + +module GlobalVariableAccess { + predicate range(Ruby::GlobalVariable n, GlobalVariableImpl v) { n.getValue() = v.getNameImpl() } +} + +abstract class GlobalVariableAccessImpl extends VariableAccessImpl, TGlobalVariableAccess { } + +private class GlobalVariableAccessReal extends GlobalVariableAccessImpl, TGlobalVariableAccessReal { + private Ruby::GlobalVariable g; + private GlobalVariable v; + + GlobalVariableAccessReal() { this = TGlobalVariableAccessReal(g, v) } + + final override GlobalVariable getVariableImpl() { result = v } + + final override string toString() { result = g.getValue() } +} + +private class GlobalVariableAccessSynth extends GlobalVariableAccessImpl, TGlobalVariableAccessSynth { + private GlobalVariable v; + + GlobalVariableAccessSynth() { this = TGlobalVariableAccessSynth(_, _, v) } + + final override GlobalVariable getVariableImpl() { result = v } + + final override string toString() { result = v.getName() } +} + +module InstanceVariableAccess { + predicate range(Ruby::InstanceVariable n, InstanceVariable v) { instanceVariableAccess(n, v) } +} + +abstract class InstanceVariableAccessImpl extends VariableAccessImpl, TInstanceVariableAccess { } + +private class InstanceVariableAccessReal extends InstanceVariableAccessImpl, + TInstanceVariableAccessReal { + private Ruby::InstanceVariable g; + private InstanceVariable v; + + InstanceVariableAccessReal() { this = TInstanceVariableAccessReal(g, v) } + + final override InstanceVariable getVariableImpl() { result = v } + + final override string toString() { result = g.getValue() } +} + +private class InstanceVariableAccessSynth extends InstanceVariableAccessImpl, + TInstanceVariableAccessSynth { + private InstanceVariable v; + + InstanceVariableAccessSynth() { this = TInstanceVariableAccessSynth(_, _, v) } + + final override InstanceVariable getVariableImpl() { result = v } + + final override string toString() { result = v.getName() } +} + +module ClassVariableAccess { + predicate range(Ruby::ClassVariable n, ClassVariable v) { classVariableAccess(n, v) } +} + +abstract class ClassVariableAccessRealImpl extends VariableAccessImpl, TClassVariableAccess { } + +private class ClassVariableAccessReal extends ClassVariableAccessRealImpl, TClassVariableAccessReal { + private Ruby::ClassVariable g; + private ClassVariable v; + + ClassVariableAccessReal() { this = TClassVariableAccessReal(g, v) } + + final override ClassVariable getVariableImpl() { result = v } + + final override string toString() { result = g.getValue() } +} + +private class ClassVariableAccessSynth extends ClassVariableAccessRealImpl, + TClassVariableAccessSynth { + private ClassVariable v; + + ClassVariableAccessSynth() { this = TClassVariableAccessSynth(_, _, v) } + + final override ClassVariable getVariableImpl() { result = v } + + final override string toString() { result = v.getName() } +} + +abstract class SelfVariableAccessImpl extends LocalVariableAccessImpl, TSelfVariableAccess { } + +private class SelfVariableAccessReal extends SelfVariableAccessImpl, TSelfReal { + private Ruby::Self self; + private SelfVariable var; + + SelfVariableAccessReal() { this = TSelfReal(self) and var = TSelfVariable(scopeOf(self)) } + + final override SelfVariable getVariableImpl() { result = var } + + final override string toString() { result = var.toString() } +} + +private class SelfVariableAccessSynth extends SelfVariableAccessImpl, TSelfSynth { + private SelfVariable v; + + SelfVariableAccessSynth() { this = TSelfSynth(_, _, v) } + + final override LocalVariable getVariableImpl() { result = v } + + final override string toString() { result = v.getName() } +} diff --git a/ruby/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll b/ruby/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll new file mode 100644 index 00000000000..e0e1199b41c --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll @@ -0,0 +1,414 @@ +/** Provides classes representing basic blocks. */ + +private import codeql.Locations +private import codeql.ruby.AST +private import codeql.ruby.ast.internal.AST +private import codeql.ruby.ast.internal.TreeSitter +private import codeql.ruby.controlflow.ControlFlowGraph +private import internal.ControlFlowGraphImpl +private import CfgNodes +private import SuccessorTypes + +/** + * A basic block, that is, a maximal straight-line sequence of control flow nodes + * without branches or joins. + */ +class BasicBlock extends TBasicBlockStart { + /** Gets the scope of this basic block. */ + CfgScope getScope() { result = this.getAPredecessor().getScope() } + + /** Gets an immediate successor of this basic block, if any. */ + BasicBlock getASuccessor() { result = this.getASuccessor(_) } + + /** Gets an immediate successor of this basic block of a given type, if any. */ + BasicBlock getASuccessor(SuccessorType t) { + result.getFirstNode() = this.getLastNode().getASuccessor(t) + } + + /** Gets an immediate predecessor of this basic block, if any. */ + BasicBlock getAPredecessor() { result.getASuccessor() = this } + + /** Gets an immediate predecessor of this basic block of a given type, if any. */ + BasicBlock getAPredecessor(SuccessorType t) { result.getASuccessor(t) = this } + + /** Gets the control flow node at a specific (zero-indexed) position in this basic block. */ + CfgNode getNode(int pos) { bbIndex(this.getFirstNode(), result, pos) } + + /** Gets a control flow node in this basic block. */ + CfgNode getANode() { result = this.getNode(_) } + + /** Gets the first control flow node in this basic block. */ + CfgNode getFirstNode() { this = TBasicBlockStart(result) } + + /** Gets the last control flow node in this basic block. */ + CfgNode getLastNode() { result = this.getNode(this.length() - 1) } + + /** Gets the length of this basic block. */ + int length() { result = strictcount(this.getANode()) } + + /** + * Holds if this basic block immediately dominates basic block `bb`. + * + * That is, all paths reaching basic block `bb` from some entry point + * basic block must go through this basic block (which is an immediate + * predecessor of `bb`). + * + * Example: + * + * ```rb + * def m b + * if b + * return 0 + * end + * return 1 + * end + * ``` + * + * The basic block starting on line 2 immediately dominates the + * basic block on line 5 (all paths from the entry point of `m` + * to `return 1` must go through the `if` block). + */ + predicate immediatelyDominates(BasicBlock bb) { bbIDominates(this, bb) } + + /** + * Holds if this basic block strictly dominates basic block `bb`. + * + * That is, all paths reaching basic block `bb` from some entry point + * basic block must go through this basic block (which must be different + * from `bb`). + * + * Example: + * + * ```rb + * def m b + * if b + * return 0 + * end + * return 1 + * end + * ``` + * + * The basic block starting on line 2 strictly dominates the + * basic block on line 5 (all paths from the entry point of `m` + * to `return 1` must go through the `if` block). + */ + predicate strictlyDominates(BasicBlock bb) { bbIDominates+(this, bb) } + + /** + * Holds if this basic block dominates basic block `bb`. + * + * That is, all paths reaching basic block `bb` from some entry point + * basic block must go through this basic block. + * + * Example: + * + * ```rb + * def m b + * if b + * return 0 + * end + * return 1 + * end + * ``` + * + * The basic block starting on line 2 dominates the basic + * basic block on line 5 (all paths from the entry point of `m` + * to `return 1` must go through the `if` block). + */ + predicate dominates(BasicBlock bb) { + bb = this or + this.strictlyDominates(bb) + } + + /** + * Holds if `df` is in the dominance frontier of this basic block. + * That is, this basic block dominates a predecessor of `df`, but + * does not dominate `df` itself. + * + * Example: + * + * ```rb + * def m x + * if x < 0 + * x = -x + * if x > 10 + * x = x - 1 + * end + * end + * puts x + * end + * ``` + * + * The basic block on line 8 is in the dominance frontier + * of the basic block starting on line 3 because that block + * dominates the basic block on line 4, which is a predecessor of + * `puts x`. Also, the basic block starting on line 3 does not + * dominate the basic block on line 8. + */ + predicate inDominanceFrontier(BasicBlock df) { + this.dominatesPredecessor(df) and + not this.strictlyDominates(df) + } + + /** + * Holds if this basic block dominates a predecessor of `df`. + */ + private predicate dominatesPredecessor(BasicBlock df) { this.dominates(df.getAPredecessor()) } + + /** + * Gets the basic block that immediately dominates this basic block, if any. + * + * That is, all paths reaching this basic block from some entry point + * basic block must go through the result, which is an immediate basic block + * predecessor of this basic block. + * + * Example: + * + * ```rb + * def m b + * if b + * return 0 + * end + * return 1 + * end + * ``` + * + * The basic block starting on line 2 is an immediate dominator of + * the basic block on line 5 (all paths from the entry point of `m` + * to `return 1` must go through the `if` block, and the `if` block + * is an immediate predecessor of `return 1`). + */ + BasicBlock getImmediateDominator() { bbIDominates(result, this) } + + /** + * Holds if this basic block strictly post-dominates basic block `bb`. + * + * That is, all paths reaching a normal exit point basic block from basic + * block `bb` must go through this basic block (which must be different + * from `bb`). + * + * Example: + * + * ```rb + * def m b + * if b + * puts "b" + * end + * puts "m" + * end + * ``` + * + * The basic block on line 5 strictly post-dominates the basic block on + * line 3 (all paths to the exit point of `m` from `puts "b"` must go + * through `puts "m"`). + */ + predicate strictlyPostDominates(BasicBlock bb) { bbIPostDominates+(this, bb) } + + /** + * Holds if this basic block post-dominates basic block `bb`. + * + * That is, all paths reaching a normal exit point basic block from basic + * block `bb` must go through this basic block. + * + * Example: + * + * ```rb + * def m b + * if b + * puts "b" + * end + * puts "m" + * end + * ``` + * + * The basic block on line 5 post-dominates the basic block on line 3 + * (all paths to the exit point of `m` from `puts "b"` must go through + * `puts "m"`). + */ + predicate postDominates(BasicBlock bb) { + this.strictlyPostDominates(bb) or + this = bb + } + + /** Holds if this basic block is in a loop in the control flow graph. */ + predicate inLoop() { this.getASuccessor+() = this } + + /** Gets a textual representation of this basic block. */ + string toString() { result = this.getFirstNode().toString() } + + /** Gets the location of this basic block. */ + Location getLocation() { result = this.getFirstNode().getLocation() } +} + +cached +private module Cached { + /** Internal representation of basic blocks. */ + cached + newtype TBasicBlock = TBasicBlockStart(CfgNode cfn) { startsBB(cfn) } + + /** Holds if `cfn` starts a new basic block. */ + private predicate startsBB(CfgNode cfn) { + not exists(cfn.getAPredecessor()) and exists(cfn.getASuccessor()) + or + cfn.isJoin() + or + cfn.getAPredecessor().isBranch() + } + + /** + * Holds if `succ` is a control flow successor of `pred` within + * the same basic block. + */ + private predicate intraBBSucc(CfgNode pred, CfgNode succ) { + succ = pred.getASuccessor() and + not startsBB(succ) + } + + /** + * Holds if `cfn` is the `i`th node in basic block `bb`. + * + * In other words, `i` is the shortest distance from a node `bb` + * that starts a basic block to `cfn` along the `intraBBSucc` relation. + */ + cached + predicate bbIndex(CfgNode bbStart, CfgNode cfn, int i) = + shortestDistances(startsBB/1, intraBBSucc/2)(bbStart, cfn, i) + + /** + * Holds if the first node of basic block `succ` is a control flow + * successor of the last node of basic block `pred`. + */ + private predicate succBB(BasicBlock pred, BasicBlock succ) { succ = pred.getASuccessor() } + + /** Holds if `dom` is an immediate dominator of `bb`. */ + cached + predicate bbIDominates(BasicBlock dom, BasicBlock bb) = + idominance(entryBB/1, succBB/2)(_, dom, bb) + + /** Holds if `pred` is a basic block predecessor of `succ`. */ + private predicate predBB(BasicBlock succ, BasicBlock pred) { succBB(pred, succ) } + + /** Holds if `bb` is an exit basic block that represents normal exit. */ + private predicate normalExitBB(BasicBlock bb) { bb.getANode().(AnnotatedExitNode).isNormal() } + + /** Holds if `dom` is an immediate post-dominator of `bb`. */ + cached + predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) = + idominance(normalExitBB/1, predBB/2)(_, dom, bb) + + /** + * Gets the `i`th predecessor of join block `jb`, with respect to some + * arbitrary order. + */ + cached + JoinBlockPredecessor getJoinBlockPredecessor(JoinBlock jb, int i) { + result = + rank[i + 1](JoinBlockPredecessor jbp | + jbp = jb.getAPredecessor() + | + jbp order by JoinBlockPredecessors::getId(jbp), JoinBlockPredecessors::getSplitString(jbp) + ) + } +} + +private import Cached + +/** Holds if `bb` is an entry basic block. */ +private predicate entryBB(BasicBlock bb) { bb.getFirstNode() instanceof EntryNode } + +/** + * An entry basic block, that is, a basic block whose first node is + * an entry node. + */ +class EntryBasicBlock extends BasicBlock { + EntryBasicBlock() { entryBB(this) } + + override CfgScope getScope() { this.getFirstNode() = TEntryNode(result) } +} + +/** + * An annotated exit basic block, that is, a basic block whose last node is + * an annotated exit node. + */ +class AnnotatedExitBasicBlock extends BasicBlock { + private boolean normal; + + AnnotatedExitBasicBlock() { + exists(AnnotatedExitNode n | + n = this.getANode() and + if n.isNormal() then normal = true else normal = false + ) + } + + /** Holds if this block represent a normal exit. */ + final predicate isNormal() { normal = true } +} + +/** + * An exit basic block, that is, a basic block whose last node is + * an exit node. + */ +class ExitBasicBlock extends BasicBlock { + ExitBasicBlock() { this.getLastNode() instanceof ExitNode } +} + +private module JoinBlockPredecessors { + private predicate id(Ruby::AstNode x, Ruby::AstNode y) { x = y } + + private predicate idOf(Ruby::AstNode x, int y) = equivalenceRelation(id/2)(x, y) + + int getId(JoinBlockPredecessor jbp) { + idOf(toGeneratedInclSynth(jbp.getFirstNode().(AstCfgNode).getNode()), result) + or + idOf(toGeneratedInclSynth(jbp.(EntryBasicBlock).getScope()), result) + } + + string getSplitString(JoinBlockPredecessor jbp) { + result = jbp.getFirstNode().(AstCfgNode).getSplitsString() + or + not exists(jbp.getFirstNode().(AstCfgNode).getSplitsString()) and + result = "" + } +} + +/** A basic block with more than one predecessor. */ +class JoinBlock extends BasicBlock { + JoinBlock() { this.getFirstNode().isJoin() } + + /** + * Gets the `i`th predecessor of this join block, with respect to some + * arbitrary order. + */ + JoinBlockPredecessor getJoinBlockPredecessor(int i) { result = getJoinBlockPredecessor(this, i) } +} + +/** A basic block that is an immediate predecessor of a join block. */ +class JoinBlockPredecessor extends BasicBlock { + JoinBlockPredecessor() { this.getASuccessor() instanceof JoinBlock } +} + +/** A basic block that terminates in a condition, splitting the subsequent control flow. */ +class ConditionBlock extends BasicBlock { + ConditionBlock() { this.getLastNode().isCondition() } + + /** + * Holds if basic block `succ` is immediately controlled by this basic + * block with conditional value `s`. That is, `succ` is an immediate + * successor of this block, and `succ` can only be reached from + * the callable entry point by going via the `s` edge out of this basic block. + */ + pragma[nomagic] + predicate immediatelyControls(BasicBlock succ, BooleanSuccessor s) { + succ = this.getASuccessor(s) and + forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != this | succ.dominates(pred)) + } + + /** + * Holds if basic block `controlled` is controlled by this basic block with + * conditional value `s`. That is, `controlled` can only be reached from + * the callable entry point by going via the `s` edge out of this basic block. + */ + predicate controls(BasicBlock controlled, BooleanSuccessor s) { + exists(BasicBlock succ | this.immediatelyControls(succ, s) | succ.dominates(controlled)) + } +} diff --git a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll new file mode 100644 index 00000000000..f41cdf35924 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll @@ -0,0 +1,484 @@ +/** Provides classes representing nodes in a control flow graph. */ + +private import codeql.ruby.AST +private import codeql.ruby.controlflow.BasicBlocks +private import codeql.ruby.dataflow.SSA +private import ControlFlowGraph +private import internal.ControlFlowGraphImpl +private import internal.Splitting + +/** An entry node for a given scope. */ +class EntryNode extends CfgNode, TEntryNode { + private CfgScope scope; + + EntryNode() { this = TEntryNode(scope) } + + final override EntryBasicBlock getBasicBlock() { result = CfgNode.super.getBasicBlock() } + + final override Location getLocation() { result = scope.getLocation() } + + final override string toString() { result = "enter " + scope } +} + +/** An exit node for a given scope, annotated with the type of exit. */ +class AnnotatedExitNode extends CfgNode, TAnnotatedExitNode { + private CfgScope scope; + private boolean normal; + + AnnotatedExitNode() { this = TAnnotatedExitNode(scope, normal) } + + /** Holds if this node represent a normal exit. */ + final predicate isNormal() { normal = true } + + final override AnnotatedExitBasicBlock getBasicBlock() { result = CfgNode.super.getBasicBlock() } + + final override Location getLocation() { result = scope.getLocation() } + + final override string toString() { + exists(string s | + normal = true and s = "normal" + or + normal = false and s = "abnormal" + | + result = "exit " + scope + " (" + s + ")" + ) + } +} + +/** An exit node for a given scope. */ +class ExitNode extends CfgNode, TExitNode { + private CfgScope scope; + + ExitNode() { this = TExitNode(scope) } + + final override Location getLocation() { result = scope.getLocation() } + + final override string toString() { result = "exit " + scope } +} + +/** + * A node for an AST node. + * + * Each AST node maps to zero or more `AstCfgNode`s: zero when the node is unreachable + * (dead) code or not important for control flow, and multiple when there are different + * splits for the AST node. + */ +class AstCfgNode extends CfgNode, TElementNode { + private Splits splits; + private AstNode n; + + AstCfgNode() { this = TElementNode(n, splits) } + + final override AstNode getNode() { result = n } + + override Location getLocation() { result = n.getLocation() } + + final override string toString() { + exists(string s | s = n.(AstNode).toString() | + result = "[" + this.getSplitsString() + "] " + s + or + not exists(this.getSplitsString()) and result = s + ) + } + + /** Gets a comma-separated list of strings for each split in this node, if any. */ + final string getSplitsString() { + result = splits.toString() and + result != "" + } + + /** Gets a split for this control flow node, if any. */ + final Split getASplit() { result = splits.getASplit() } +} + +/** A control-flow node that wraps an AST expression. */ +class ExprCfgNode extends AstCfgNode { + Expr e; + + ExprCfgNode() { e = this.getNode() } + + /** Gets the underlying expression. */ + Expr getExpr() { result = e } + + private ExprCfgNode getSource() { + exists(Ssa::WriteDefinition def | + def.assigns(result) and + this = def.getARead() + ) + } + + /** Gets the textual (constant) value of this expression, if any. */ + string getValueText() { result = this.getSource().getValueText() } +} + +/** A control-flow node that wraps a return-like statement. */ +class ReturningCfgNode extends AstCfgNode { + ReturningStmt s; + + ReturningCfgNode() { s = this.getNode() } + + /** Gets the node of the returned value, if any. */ + ExprCfgNode getReturnedValueNode() { + result = this.getAPredecessor() and + result.getNode() = s.getValue() + } +} + +/** A control-flow node that wraps a `StringComponent` AST expression. */ +class StringComponentCfgNode extends AstCfgNode { + StringComponentCfgNode() { this.getNode() instanceof StringComponent } +} + +private Expr desugar(Expr n) { + result = n.getDesugared() + or + not exists(n.getDesugared()) and + result = n +} + +/** + * A class for mapping parent-child AST nodes to parent-child CFG nodes. + */ +abstract private class ExprChildMapping extends Expr { + /** + * Holds if `child` is a (possibly nested) child of this expression + * for which we would like to find a matching CFG child. + */ + abstract predicate relevantChild(Expr child); + + pragma[nomagic] + private predicate reachesBasicBlock(Expr child, CfgNode cfn, BasicBlock bb) { + this.relevantChild(child) and + cfn = this.getAControlFlowNode() and + bb.getANode() = cfn + or + exists(BasicBlock mid | + this.reachesBasicBlock(child, cfn, mid) and + bb = mid.getAPredecessor() and + not mid.getANode().getNode() = child + ) + } + + /** + * Holds if there is a control-flow path from `cfn` to `cfnChild`, where `cfn` + * is a control-flow node for this expression, and `cfnChild` is a control-flow + * node for `child`. + * + * The path never escapes the syntactic scope of this expression. + */ + cached + predicate hasCfgChild(Expr child, CfgNode cfn, CfgNode cfnChild) { + this.reachesBasicBlock(child, cfn, cfnChild.getBasicBlock()) and + cfnChild = desugar(child).getAControlFlowNode() + } +} + +/** Provides classes for control-flow nodes that wrap AST expressions. */ +module ExprNodes { + private class LiteralChildMapping extends ExprChildMapping, Literal { + override predicate relevantChild(Expr e) { none() } + } + + /** A control-flow node that wraps an `ArrayLiteral` AST expression. */ + class LiteralCfgNode extends ExprCfgNode { + override LiteralChildMapping e; + + override Literal getExpr() { result = super.getExpr() } + + override string getValueText() { result = e.getValueText() } + } + + private class AssignExprChildMapping extends ExprChildMapping, AssignExpr { + override predicate relevantChild(Expr e) { e = this.getAnOperand() } + } + + /** A control-flow node that wraps an `AssignExpr` AST expression. */ + class AssignExprCfgNode extends ExprCfgNode { + override AssignExprChildMapping e; + + final override AssignExpr getExpr() { result = ExprCfgNode.super.getExpr() } + + /** Gets the LHS of this assignment. */ + final ExprCfgNode getLhs() { e.hasCfgChild(e.getLeftOperand(), this, result) } + + /** Gets the RHS of this assignment. */ + final ExprCfgNode getRhs() { e.hasCfgChild(e.getRightOperand(), this, result) } + } + + private class OperationExprChildMapping extends ExprChildMapping, Operation { + override predicate relevantChild(Expr e) { e = this.getAnOperand() } + } + + /** A control-flow node that wraps an `Operation` AST expression. */ + class OperationCfgNode extends ExprCfgNode { + override OperationExprChildMapping e; + + override Operation getExpr() { result = super.getExpr() } + + /** Gets an operand of this operation. */ + final ExprCfgNode getAnOperand() { e.hasCfgChild(e.getAnOperand(), this, result) } + } + + /** A control-flow node that wraps a `BinaryOperation` AST expression. */ + class BinaryOperationCfgNode extends OperationCfgNode { + private BinaryOperation bo; + + BinaryOperationCfgNode() { e = bo } + + override BinaryOperation getExpr() { result = super.getExpr() } + + /** Gets the left operand of this binary operation. */ + final ExprCfgNode getLeftOperand() { e.hasCfgChild(bo.getLeftOperand(), this, result) } + + /** Gets the right operand of this binary operation. */ + final ExprCfgNode getRightOperand() { e.hasCfgChild(bo.getRightOperand(), this, result) } + + final override string getValueText() { + exists(string left, string right, string op | + left = this.getLeftOperand().getValueText() and + right = this.getRightOperand().getValueText() and + op = this.getExpr().getOperator() + | + op = "+" and + ( + result = (left.toInt() + right.toInt()).toString() + or + not (exists(left.toInt()) and exists(right.toInt())) and + result = (left.toFloat() + right.toFloat()).toString() + or + not (exists(left.toFloat()) and exists(right.toFloat())) and + result = left + right + ) + or + op = "-" and + ( + result = (left.toInt() - right.toInt()).toString() + or + not (exists(left.toInt()) and exists(right.toInt())) and + result = (left.toFloat() - right.toFloat()).toString() + ) + or + op = "*" and + ( + result = (left.toInt() * right.toInt()).toString() + or + not (exists(left.toInt()) and exists(right.toInt())) and + result = (left.toFloat() * right.toFloat()).toString() + ) + or + op = "/" and + ( + result = (left.toInt() / right.toInt()).toString() + or + not (exists(left.toInt()) and exists(right.toInt())) and + result = (left.toFloat() / right.toFloat()).toString() + ) + ) + } + } + + private class BlockArgumentChildMapping extends ExprChildMapping, BlockArgument { + override predicate relevantChild(Expr e) { e = this.getValue() } + } + + /** A control-flow node that wraps a `BlockArgument` AST expression. */ + class BlockArgumentCfgNode extends ExprCfgNode { + override BlockArgumentChildMapping e; + + final override BlockArgument getExpr() { result = ExprCfgNode.super.getExpr() } + + /** Gets the value of this block argument. */ + final ExprCfgNode getValue() { e.hasCfgChild(e.getValue(), this, result) } + } + + private class CallExprChildMapping extends ExprChildMapping, Call { + override predicate relevantChild(Expr e) { + e = [this.getAnArgument(), this.(MethodCall).getReceiver(), this.(MethodCall).getBlock()] + } + } + + /** A control-flow node that wraps a `Call` AST expression. */ + class CallCfgNode extends ExprCfgNode { + override CallExprChildMapping e; + + override Call getExpr() { result = super.getExpr() } + + /** Gets the `n`th argument of this call. */ + final ExprCfgNode getArgument(int n) { e.hasCfgChild(e.getArgument(n), this, result) } + + /** Gets the the keyword argument whose key is `keyword` of this call. */ + final ExprCfgNode getKeywordArgument(string keyword) { + e.hasCfgChild(e.getKeywordArgument(keyword), this, result) + } + + /** Gets the number of arguments of this call. */ + final int getNumberOfArguments() { result = e.getNumberOfArguments() } + + /** Gets the receiver of this call. */ + final ExprCfgNode getReceiver() { e.hasCfgChild(e.(MethodCall).getReceiver(), this, result) } + + /** Gets the block of this call. */ + final ExprCfgNode getBlock() { e.hasCfgChild(e.(MethodCall).getBlock(), this, result) } + } + + private class CaseExprChildMapping extends ExprChildMapping, CaseExpr { + override predicate relevantChild(Expr e) { e = this.getValue() or e = this.getBranch(_) } + } + + /** A control-flow node that wraps a `MethodCall` AST expression. */ + class MethodCallCfgNode extends CallCfgNode { + MethodCallCfgNode() { super.getExpr() instanceof MethodCall } + + override MethodCall getExpr() { result = super.getExpr() } + } + + /** A control-flow node that wraps a `CaseExpr` AST expression. */ + class CaseExprCfgNode extends ExprCfgNode { + override CaseExprChildMapping e; + + final override CaseExpr getExpr() { result = ExprCfgNode.super.getExpr() } + + /** Gets the expression being compared, if any. */ + final ExprCfgNode getValue() { e.hasCfgChild(e.getValue(), this, result) } + + /** + * Gets the `n`th branch of this case expression. + */ + final ExprCfgNode getBranch(int n) { e.hasCfgChild(e.getBranch(n), this, result) } + } + + private class ConditionalExprChildMapping extends ExprChildMapping, ConditionalExpr { + override predicate relevantChild(Expr e) { e = this.getCondition() or e = this.getBranch(_) } + } + + /** A control-flow node that wraps a `ConditionalExpr` AST expression. */ + class ConditionalExprCfgNode extends ExprCfgNode { + override ConditionalExprChildMapping e; + + final override ConditionalExpr getExpr() { result = ExprCfgNode.super.getExpr() } + + /** Gets the condition expression. */ + final ExprCfgNode getCondition() { e.hasCfgChild(e.getCondition(), this, result) } + + /** + * Gets the branch of this conditional expression that is taken when the condition + * evaluates to cond, if any. + */ + final ExprCfgNode getBranch(boolean cond) { e.hasCfgChild(e.getBranch(cond), this, result) } + } + + private class ConstantAccessChildMapping extends ExprChildMapping, ConstantAccess { + override predicate relevantChild(Expr e) { e = this.getScopeExpr() } + } + + /** A control-flow node that wraps a `ConditionalExpr` AST expression. */ + class ConstantAccessCfgNode extends ExprCfgNode { + override ConstantAccessChildMapping e; + + final override ConstantAccess getExpr() { result = super.getExpr() } + + /** Gets the scope expression. */ + final ExprCfgNode getScopeExpr() { e.hasCfgChild(e.getScopeExpr(), this, result) } + } + + private class StmtSequenceChildMapping extends ExprChildMapping, StmtSequence { + override predicate relevantChild(Expr e) { e = this.getLastStmt() } + } + + /** A control-flow node that wraps a `StmtSequence` AST expression. */ + class StmtSequenceCfgNode extends ExprCfgNode { + override StmtSequenceChildMapping e; + + final override StmtSequence getExpr() { result = ExprCfgNode.super.getExpr() } + + /** Gets the last statement in this sequence, if any. */ + final ExprCfgNode getLastStmt() { e.hasCfgChild(e.getLastStmt(), this, result) } + } + + private class ForExprChildMapping extends ExprChildMapping, ForExpr { + override predicate relevantChild(Expr e) { e = this.getValue() } + } + + /** A control-flow node that wraps a `ForExpr` AST expression. */ + class ForExprCfgNode extends ExprCfgNode { + override ForExprChildMapping e; + + final override ForExpr getExpr() { result = ExprCfgNode.super.getExpr() } + + /** Gets the value being iterated over. */ + final ExprCfgNode getValue() { e.hasCfgChild(e.getValue(), this, result) } + } + + /** A control-flow node that wraps a `ParenthesizedExpr` AST expression. */ + class ParenthesizedExprCfgNode extends StmtSequenceCfgNode { + ParenthesizedExprCfgNode() { this.getExpr() instanceof ParenthesizedExpr } + } + + /** A control-flow node that wraps a `VariableReadAccess` AST expression. */ + class VariableReadAccessCfgNode extends ExprCfgNode { + override VariableReadAccess e; + + final override VariableReadAccess getExpr() { result = ExprCfgNode.super.getExpr() } + } + + /** A control-flow node that wraps a `InstanceVariableWriteAccess` AST expression. */ + class InstanceVariableWriteAccessCfgNode extends ExprCfgNode { + override InstanceVariableWriteAccess e; + + final override InstanceVariableWriteAccess getExpr() { result = ExprCfgNode.super.getExpr() } + } + + /** A control-flow node that wraps a `StringInterpolationComponent` AST expression. */ + class StringInterpolationComponentCfgNode extends StmtSequenceCfgNode { + StringInterpolationComponentCfgNode() { this.getNode() instanceof StringInterpolationComponent } + } + + private class StringlikeLiteralChildMapping extends ExprChildMapping, StringlikeLiteral { + override predicate relevantChild(Expr e) { e = this.getComponent(_) } + } + + /** A control-flow node that wraps a `StringlikeLiteral` AST expression. */ + class StringlikeLiteralCfgNode extends ExprCfgNode { + override StringlikeLiteralChildMapping e; + + final override StringlikeLiteral getExpr() { result = super.getExpr() } + + /** Gets a component of this `StringlikeLiteral` */ + StringComponentCfgNode getAComponent() { e.hasCfgChild(e.getComponent(_), this, result) } + } + + /** A control-flow node that wraps a `StringLiteral` AST expression. */ + class StringLiteralCfgNode extends ExprCfgNode { + override StringLiteral e; + + final override StringLiteral getExpr() { result = super.getExpr() } + } + + /** A control-flow node that wraps a `RegExpLiteral` AST expression. */ + class RegExpLiteralCfgNode extends ExprCfgNode { + override RegExpLiteral e; + + final override RegExpLiteral getExpr() { result = super.getExpr() } + } + + /** A control-flow node that wraps a `ComparisonOperation` AST expression. */ + class ComparisonOperationCfgNode extends BinaryOperationCfgNode { + ComparisonOperationCfgNode() { e instanceof ComparisonOperation } + + override ComparisonOperation getExpr() { result = super.getExpr() } + } + + /** A control-flow node that wraps a `RelationalOperation` AST expression. */ + class RelationalOperationCfgNode extends ComparisonOperationCfgNode { + RelationalOperationCfgNode() { e instanceof RelationalOperation } + + final override RelationalOperation getExpr() { result = super.getExpr() } + } + + /** A control-flow node that wraps an `ElementReference` AST expression. */ + class ElementReferenceCfgNode extends MethodCallCfgNode { + ElementReferenceCfgNode() { e instanceof ElementReference } + + final override ElementReference getExpr() { result = super.getExpr() } + } +} diff --git a/ruby/ql/lib/codeql/ruby/controlflow/ControlFlowGraph.qll b/ruby/ql/lib/codeql/ruby/controlflow/ControlFlowGraph.qll new file mode 100644 index 00000000000..4b2785265cf --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/controlflow/ControlFlowGraph.qll @@ -0,0 +1,341 @@ +/** Provides classes representing the control flow graph. */ + +private import codeql.Locations +private import codeql.ruby.AST +private import codeql.ruby.controlflow.BasicBlocks +private import SuccessorTypes +private import internal.ControlFlowGraphImpl +private import internal.Splitting +private import internal.Completion + +/** An AST node with an associated control-flow graph. */ +class CfgScope extends Scope instanceof CfgScope::Range_ { + /** Gets the CFG scope that this scope is nested under, if any. */ + final CfgScope getOuterCfgScope() { + exists(AstNode parent | + parent = this.getParent() and + result = getCfgScope(parent) + ) + } +} + +/** + * A control flow node. + * + * A control flow node is a node in the control flow graph (CFG). There is a + * many-to-one relationship between CFG nodes and AST nodes. + * + * Only nodes that can be reached from an entry point are included in the CFG. + */ +class CfgNode extends TNode { + /** Gets a textual representation of this control flow node. */ + string toString() { none() } + + /** Gets the AST node that this node corresponds to, if any. */ + AstNode getNode() { none() } + + /** Gets the location of this control flow node. */ + Location getLocation() { none() } + + /** Gets the file of this control flow node. */ + final File getFile() { result = this.getLocation().getFile() } + + /** Holds if this control flow node has conditional successors. */ + final predicate isCondition() { exists(this.getASuccessor(any(BooleanSuccessor bs))) } + + /** Gets the scope of this node. */ + final CfgScope getScope() { result = this.getBasicBlock().getScope() } + + /** Gets the basic block that this control flow node belongs to. */ + BasicBlock getBasicBlock() { result.getANode() = this } + + /** Gets a successor node of a given type, if any. */ + final CfgNode getASuccessor(SuccessorType t) { result = getASuccessor(this, t) } + + /** Gets an immediate successor, if any. */ + final CfgNode getASuccessor() { result = this.getASuccessor(_) } + + /** Gets an immediate predecessor node of a given flow type, if any. */ + final CfgNode getAPredecessor(SuccessorType t) { result.getASuccessor(t) = this } + + /** Gets an immediate predecessor, if any. */ + final CfgNode getAPredecessor() { result = this.getAPredecessor(_) } + + /** Holds if this node has more than one predecessor. */ + final predicate isJoin() { strictcount(this.getAPredecessor()) > 1 } + + /** Holds if this node has more than one successor. */ + final predicate isBranch() { strictcount(this.getASuccessor()) > 1 } +} + +/** The type of a control flow successor. */ +class SuccessorType extends TSuccessorType { + /** Gets a textual representation of successor type. */ + string toString() { none() } +} + +/** Provides different types of control flow successor types. */ +module SuccessorTypes { + /** A normal control flow successor. */ + class NormalSuccessor extends SuccessorType, TSuccessorSuccessor { + final override string toString() { result = "successor" } + } + + /** + * A conditional control flow successor. Either a Boolean successor (`BooleanSuccessor`), + * an emptiness successor (`EmptinessSuccessor`), or a matching successor + * (`MatchingSuccessor`) + */ + class ConditionalSuccessor extends SuccessorType { + boolean value; + + ConditionalSuccessor() { + this = TBooleanSuccessor(value) or + this = TEmptinessSuccessor(value) or + this = TMatchingSuccessor(value) + } + + /** Gets the Boolean value of this successor. */ + final boolean getValue() { result = value } + + override string toString() { result = this.getValue().toString() } + } + + /** + * A Boolean control flow successor. + * + * For example, in + * + * ```rb + * if x >= 0 + * puts "positive" + * else + * puts "negative" + * end + * ``` + * + * `x >= 0` has both a `true` successor and a `false` successor. + */ + class BooleanSuccessor extends ConditionalSuccessor, TBooleanSuccessor { } + + /** + * An emptiness control flow successor. + * + * For example, this program fragment: + * + * ```rb + * for arg in args do + * puts arg + * end + * puts "done"; + * ``` + * + * has a control flow graph containing emptiness successors: + * + * ``` + * args + * | + * for------<----- + * / \ \ + * / \ | + * / \ | + * / \ | + * empty non-empty | + * | \ | + * puts "done" \ | + * arg | + * | | + * puts arg | + * \___/ + * ``` + */ + class EmptinessSuccessor extends ConditionalSuccessor, TEmptinessSuccessor { + override string toString() { if value = true then result = "empty" else result = "non-empty" } + } + + /** + * A matching control flow successor. + * + * For example, this program fragment: + * + * ```rb + * case x + * when 1 then puts "one" + * else puts "not one" + * end + * ``` + * + * has a control flow graph containing matching successors: + * + * ``` + * x + * | + * 1 + * / \ + * / \ + * / \ + * / \ + * match non-match + * | | + * puts "one" puts "not one" + * ``` + */ + class MatchingSuccessor extends ConditionalSuccessor, TMatchingSuccessor { + override string toString() { if value = true then result = "match" else result = "no-match" } + } + + /** + * A `return` control flow successor. + * + * Example: + * + * ```rb + * def sum(x,y) + * return x + y + * end + * ``` + * + * The exit node of `sum` is a `return` successor of the `return x + y` + * statement. + */ + class ReturnSuccessor extends SuccessorType, TReturnSuccessor { + final override string toString() { result = "return" } + } + + /** + * A `break` control flow successor. + * + * Example: + * + * ```rb + * def m + * while x >= 0 + * x -= 1 + * if num > 100 + * break + * end + * end + * puts "done" + * end + * ``` + * + * The node `puts "done"` is `break` successor of the node `break`. + */ + class BreakSuccessor extends SuccessorType, TBreakSuccessor { + final override string toString() { result = "break" } + } + + /** + * A `next` control flow successor. + * + * Example: + * + * ```rb + * def m + * while x >= 0 + * x -= 1 + * if num > 100 + * next + * end + * end + * puts "done" + * end + * ``` + * + * The node `x >= 0` is `next` successor of the node `next`. + */ + class NextSuccessor extends SuccessorType, TNextSuccessor { + final override string toString() { result = "next" } + } + + /** + * A `redo` control flow successor. + * + * Example: + * + * Example: + * + * ```rb + * def m + * while x >= 0 + * x -= 1 + * if num > 100 + * redo + * end + * end + * puts "done" + * end + * ``` + * + * The node `x -= 1` is `redo` successor of the node `redo`. + */ + class RedoSuccessor extends SuccessorType, TRedoSuccessor { + final override string toString() { result = "redo" } + } + + /** + * A `retry` control flow successor. + * + * Example: + * + * Example: + * + * ```rb + * def m + * begin + * puts "Retry" + * raise + * rescue + * retry + * end + * end + * ``` + * + * The node `puts "Retry"` is `retry` successor of the node `retry`. + */ + class RetrySuccessor extends SuccessorType, TRetrySuccessor { + final override string toString() { result = "retry" } + } + + /** + * An exceptional control flow successor. + * + * Example: + * + * ```rb + * def m x + * if x > 2 + * raise "x > 2" + * end + * puts "x <= 2" + * end + * ``` + * + * The exit node of `m` is an exceptional successor of the node + * `raise "x > 2"`. + */ + class RaiseSuccessor extends SuccessorType, TRaiseSuccessor { + final override string toString() { result = "raise" } + } + + /** + * An exit control flow successor. + * + * Example: + * + * ```rb + * def m x + * if x > 2 + * exit 1 + * end + * puts "x <= 2" + * end + * ``` + * + * The exit node of `m` is an exit successor of the node + * `exit 1`. + */ + class ExitSuccessor extends SuccessorType, TExitSuccessor { + final override string toString() { result = "exit" } + } +} diff --git a/ruby/ql/lib/codeql/ruby/controlflow/internal/Completion.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/Completion.qll new file mode 100644 index 00000000000..e7f64d1318e --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/controlflow/internal/Completion.qll @@ -0,0 +1,507 @@ +/** + * Provides classes representing control flow completions. + * + * A completion represents how a statement or expression terminates. + */ + +private import codeql.ruby.AST +private import codeql.ruby.ast.internal.AST +private import codeql.ruby.controlflow.ControlFlowGraph +private import ControlFlowGraphImpl +private import NonReturning +private import SuccessorTypes + +private newtype TCompletion = + TSimpleCompletion() or + TBooleanCompletion(boolean b) { b in [false, true] } or + TEmptinessCompletion(boolean isEmpty) { isEmpty in [false, true] } or + TMatchingCompletion(boolean isMatch) { isMatch in [false, true] } or + TReturnCompletion() or + TBreakCompletion() or + TNextCompletion() or + TRedoCompletion() or + TRetryCompletion() or + TRaiseCompletion() or // TODO: Add exception type? + TExitCompletion() or + TNestedCompletion(Completion inner, Completion outer, int nestLevel) { + inner = TBreakCompletion() and + outer instanceof NonNestedNormalCompletion and + nestLevel = 0 + or + inner instanceof NormalCompletion and + nestedEnsureCompletion(outer, nestLevel) + } + +pragma[noinline] +private predicate nestedEnsureCompletion(Completion outer, int nestLevel) { + ( + outer = TReturnCompletion() + or + outer = TBreakCompletion() + or + outer = TNextCompletion() + or + outer = TRedoCompletion() + or + outer = TRetryCompletion() + or + outer = TRaiseCompletion() + or + outer = TExitCompletion() + ) and + nestLevel = any(Trees::BodyStmtTree t).getNestLevel() +} + +pragma[noinline] +private predicate completionIsValidForStmt(AstNode n, Completion c) { + n = TForIn(_) and + c instanceof EmptinessCompletion + or + n instanceof BreakStmt and + c = TBreakCompletion() + or + n instanceof NextStmt and + c = TNextCompletion() + or + n instanceof RedoStmt and + c = TRedoCompletion() + or + n instanceof ReturnStmt and + c = TReturnCompletion() +} + +/** + * Holds if `c` happens in an exception-aware context, that is, it may be + * `rescue`d or `ensure`d. In such cases, we assume that the target of `c` + * may raise an exception (in addition to evaluating normally). + */ +private predicate mayRaise(Call c) { + exists(Trees::BodyStmtTree bst | c = bst.getBodyChild(_, true).getAChild*() | + exists(bst.getARescue()) + or + exists(bst.getEnsure()) + ) +} + +/** A completion of a statement or an expression. */ +abstract class Completion extends TCompletion { + /** Holds if this completion is valid for node `n`. */ + predicate isValidFor(AstNode n) { + this = n.(NonReturningCall).getACompletion() + or + completionIsValidForStmt(n, this) + or + mustHaveBooleanCompletion(n) and + ( + exists(boolean value | isBooleanConstant(n, value) | this = TBooleanCompletion(value)) + or + not isBooleanConstant(n, _) and + this = TBooleanCompletion(_) + ) + or + mustHaveMatchingCompletion(n) and + this = TMatchingCompletion(_) + or + n = any(RescueModifierExpr parent).getBody() and this = TRaiseCompletion() + or + mayRaise(n) and + this = TRaiseCompletion() + or + not n instanceof NonReturningCall and + not completionIsValidForStmt(n, _) and + not mustHaveBooleanCompletion(n) and + not mustHaveMatchingCompletion(n) and + this = TSimpleCompletion() + } + + /** + * Holds if this completion will continue a loop when it is the completion + * of a loop body. + */ + predicate continuesLoop() { + this instanceof NormalCompletion or + this instanceof NextCompletion + } + + /** + * Gets the inner completion. This is either the inner completion, + * when the completion is nested, or the completion itself. + */ + Completion getInnerCompletion() { result = this } + + /** + * Gets the outer completion. This is either the outer completion, + * when the completion is nested, or the completion itself. + */ + Completion getOuterCompletion() { result = this } + + /** Gets a successor type that matches this completion. */ + abstract SuccessorType getAMatchingSuccessorType(); + + /** Gets a textual representation of this completion. */ + abstract string toString(); +} + +/** Holds if node `n` has the Boolean constant value `value`. */ +private predicate isBooleanConstant(AstNode n, boolean value) { + mustHaveBooleanCompletion(n) and + ( + n.(BooleanLiteral).isTrue() and + value = true + or + n.(BooleanLiteral).isFalse() and + value = false + ) +} + +/** + * Holds if a normal completion of `n` must be a Boolean completion. + */ +private predicate mustHaveBooleanCompletion(AstNode n) { + inBooleanContext(n) and + not n instanceof NonReturningCall +} + +/** + * Holds if `n` is used in a Boolean context. That is, the value + * that `n` evaluates to determines a true/false branch successor. + */ +private predicate inBooleanContext(AstNode n) { + exists(ConditionalExpr i | + n = i.getCondition() + or + inBooleanContext(i) and + n = i.getBranch(_) + ) + or + n = any(ConditionalLoop parent).getCondition() + or + exists(LogicalAndExpr parent | + n = parent.getLeftOperand() + or + inBooleanContext(parent) and + n = parent.getRightOperand() + ) + or + exists(LogicalOrExpr parent | + n = parent.getLeftOperand() + or + inBooleanContext(parent) and + n = parent.getRightOperand() + ) + or + n = any(NotExpr parent | inBooleanContext(parent)).getOperand() + or + n = any(StmtSequence parent | inBooleanContext(parent)).getLastStmt() + or + exists(CaseExpr c, WhenExpr w | + not exists(c.getValue()) and + c.getAWhenBranch() = w and + w.getPattern(_) = n + ) +} + +/** + * Holds if a normal completion of `n` must be a matching completion. + */ +private predicate mustHaveMatchingCompletion(AstNode n) { + inMatchingContext(n) and + not n instanceof NonReturningCall +} + +/** + * Holds if `n` is used in a matching context. That is, whether or + * not the value of `n` matches, determines the successor. + */ +private predicate inMatchingContext(AstNode n) { + n = any(RescueClause r).getException(_) + or + exists(CaseExpr c, WhenExpr w | + exists(c.getValue()) and + c.getAWhenBranch() = w and + w.getPattern(_) = n + ) + or + n.(Trees::DefaultValueParameterTree).hasDefaultValue() +} + +/** + * A completion that represents normal evaluation of a statement or an + * expression. + */ +abstract class NormalCompletion extends Completion { } + +abstract private class NonNestedNormalCompletion extends NormalCompletion { } + +/** A simple (normal) completion. */ +class SimpleCompletion extends NonNestedNormalCompletion, TSimpleCompletion { + override NormalSuccessor getAMatchingSuccessorType() { any() } + + override string toString() { result = "simple" } +} + +/** + * A completion that represents evaluation of an expression, whose value determines + * the successor. Either a Boolean completion (`BooleanCompletion`), an emptiness + * completion (`EmptinessCompletion`), or a matching completion (`MatchingCompletion`). + */ +abstract class ConditionalCompletion extends NonNestedNormalCompletion { + boolean value; + + bindingset[value] + ConditionalCompletion() { any() } + + /** Gets the Boolean value of this conditional completion. */ + final boolean getValue() { result = value } +} + +/** + * A completion that represents evaluation of an expression + * with a Boolean value. + */ +class BooleanCompletion extends ConditionalCompletion, TBooleanCompletion { + BooleanCompletion() { this = TBooleanCompletion(value) } + + /** Gets the dual Boolean completion. */ + BooleanCompletion getDual() { result = TBooleanCompletion(value.booleanNot()) } + + override BooleanSuccessor getAMatchingSuccessorType() { result.getValue() = value } + + override string toString() { result = value.toString() } +} + +/** A Boolean `true` completion. */ +class TrueCompletion extends BooleanCompletion { + TrueCompletion() { this.getValue() = true } +} + +/** A Boolean `false` completion. */ +class FalseCompletion extends BooleanCompletion { + FalseCompletion() { this.getValue() = false } +} + +/** + * A completion that represents evaluation of an emptiness test, for example + * a test in a `for in` statement. + */ +class EmptinessCompletion extends ConditionalCompletion, TEmptinessCompletion { + EmptinessCompletion() { this = TEmptinessCompletion(value) } + + override EmptinessSuccessor getAMatchingSuccessorType() { result.getValue() = value } + + override string toString() { if value = true then result = "empty" else result = "non-empty" } +} + +/** + * A completion that represents evaluation of a matching test, for example + * a test in a `rescue` statement. + */ +class MatchingCompletion extends ConditionalCompletion, TMatchingCompletion { + MatchingCompletion() { this = TMatchingCompletion(value) } + + override MatchingSuccessor getAMatchingSuccessorType() { result.getValue() = value } + + override string toString() { if value = true then result = "match" else result = "no-match" } +} + +/** + * A completion that represents evaluation of a statement or an + * expression resulting in a return. + */ +class ReturnCompletion extends Completion { + ReturnCompletion() { + this = TReturnCompletion() or + this = TNestedCompletion(_, TReturnCompletion(), _) + } + + override ReturnSuccessor getAMatchingSuccessorType() { any() } + + override string toString() { + // `NestedCompletion` defines `toString()` for the other case + this = TReturnCompletion() and result = "return" + } +} + +/** + * A completion that represents evaluation of a statement or an + * expression resulting in a break from a loop. + */ +class BreakCompletion extends Completion { + BreakCompletion() { + this = TBreakCompletion() or + this = TNestedCompletion(_, TBreakCompletion(), _) + } + + override BreakSuccessor getAMatchingSuccessorType() { any() } + + override string toString() { + // `NestedCompletion` defines `toString()` for the other case + this = TBreakCompletion() and result = "break" + } +} + +/** + * A completion that represents evaluation of a statement or an + * expression resulting in a continuation of a loop. + */ +class NextCompletion extends Completion { + NextCompletion() { + this = TNextCompletion() or + this = TNestedCompletion(_, TNextCompletion(), _) + } + + override NextSuccessor getAMatchingSuccessorType() { any() } + + override string toString() { + // `NestedCompletion` defines `toString()` for the other case + this = TNextCompletion() and result = "next" + } +} + +/** + * A completion that represents evaluation of a statement or an + * expression resulting in a redo of a loop iteration. + */ +class RedoCompletion extends Completion { + RedoCompletion() { + this = TRedoCompletion() or + this = TNestedCompletion(_, TRedoCompletion(), _) + } + + override RedoSuccessor getAMatchingSuccessorType() { any() } + + override string toString() { + // `NestedCompletion` defines `toString()` for the other case + this = TRedoCompletion() and result = "redo" + } +} + +/** + * A completion that represents evaluation of a statement or an + * expression resulting in a retry. + */ +class RetryCompletion extends Completion { + RetryCompletion() { + this = TRetryCompletion() or + this = TNestedCompletion(_, TRetryCompletion(), _) + } + + override RetrySuccessor getAMatchingSuccessorType() { any() } + + override string toString() { + // `NestedCompletion` defines `toString()` for the other case + this = TRetryCompletion() and result = "retry" + } +} + +/** + * A completion that represents evaluation of a statement or an + * expression resulting in a thrown exception. + */ +class RaiseCompletion extends Completion { + RaiseCompletion() { + this = TRaiseCompletion() or + this = TNestedCompletion(_, TRaiseCompletion(), _) + } + + override RaiseSuccessor getAMatchingSuccessorType() { any() } + + override string toString() { + // `NestedCompletion` defines `toString()` for the other case + this = TRaiseCompletion() and result = "raise" + } +} + +/** + * A completion that represents evaluation of a statement or an + * expression resulting in an abort/exit. + */ +class ExitCompletion extends Completion { + ExitCompletion() { + this = TExitCompletion() or + this = TNestedCompletion(_, TExitCompletion(), _) + } + + override ExitSuccessor getAMatchingSuccessorType() { any() } + + override string toString() { + // `NestedCompletion` defines `toString()` for the other case + this = TExitCompletion() and result = "exit" + } +} + +/** + * A nested completion. For example, in + * + * ```rb + * def m + * while x >= 0 + * x -= 1 + * if num > 100 + * break + * end + * end + * puts "done" + * end + * ``` + * + * the `while` loop can have a nested completion where the inner completion + * is a `break` and the outer completion is a simple successor. + */ +abstract class NestedCompletion extends Completion, TNestedCompletion { + Completion inner; + Completion outer; + int nestLevel; + + NestedCompletion() { this = TNestedCompletion(inner, outer, nestLevel) } + + /** Gets a completion that is compatible with the inner completion. */ + abstract Completion getAnInnerCompatibleCompletion(); + + /** Gets the level of this nested completion. */ + final int getNestLevel() { result = nestLevel } + + override string toString() { result = outer + " [" + inner + "] (" + nestLevel + ")" } +} + +class NestedBreakCompletion extends NormalCompletion, NestedCompletion { + NestedBreakCompletion() { + inner = TBreakCompletion() and + outer instanceof NonNestedNormalCompletion + } + + override BreakCompletion getInnerCompletion() { result = inner } + + override NonNestedNormalCompletion getOuterCompletion() { result = outer } + + override Completion getAnInnerCompatibleCompletion() { + result = inner and + outer = TSimpleCompletion() + or + result = TNestedCompletion(outer, inner, _) + } + + override SuccessorType getAMatchingSuccessorType() { + outer instanceof SimpleCompletion and + result instanceof BreakSuccessor + or + result = outer.(ConditionalCompletion).getAMatchingSuccessorType() + } +} + +class NestedEnsureCompletion extends NestedCompletion { + NestedEnsureCompletion() { + inner instanceof NormalCompletion and + nestedEnsureCompletion(outer, nestLevel) + } + + override NormalCompletion getInnerCompletion() { result = inner } + + override Completion getOuterCompletion() { result = outer } + + override Completion getAnInnerCompatibleCompletion() { + result.getOuterCompletion() = this.getInnerCompletion() + } + + override SuccessorType getAMatchingSuccessorType() { none() } +} diff --git a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll new file mode 100644 index 00000000000..800b16a71e2 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -0,0 +1,1171 @@ +/** + * Provides auxiliary classes and predicates used to construct the basic successor + * relation on control flow elements. + * + * The implementation is centered around the concept of a _completion_, which + * models how the execution of a statement or expression terminates. + * Completions are represented as an algebraic data type `Completion` defined in + * `Completion.qll`. + * + * The CFG is built by structural recursion over the AST. To achieve this the + * CFG edges related to a given AST node, `n`, are divided into three categories: + * + * 1. The in-going edge that points to the first CFG node to execute when + * `n` is going to be executed. + * 2. The out-going edges for control flow leaving `n` that are going to some + * other node in the surrounding context of `n`. + * 3. The edges that have both of their end-points entirely within the AST + * node and its children. + * + * The edges in (1) and (2) are inherently non-local and are therefore + * initially calculated as half-edges, that is, the single node, `k`, of the + * edge contained within `n`, by the predicates `k = first(n)` and `k = last(n, _)`, + * respectively. The edges in (3) can then be enumerated directly by the predicate + * `succ` by calling `first` and `last` recursively on the children of `n` and + * connecting the end-points. This yields the entire CFG, since all edges are in + * (3) for _some_ AST node. + * + * The second parameter of `last` is the completion, which is necessary to distinguish + * the out-going edges from `n`. Note that the completion changes as the calculation of + * `last` proceeds outward through the AST; for example, a `BreakCompletion` is + * caught up by its surrounding loop and turned into a `NormalCompletion`. + */ + +private import codeql.ruby.AST +private import codeql.ruby.ast.internal.AST as ASTInternal +private import codeql.ruby.ast.internal.Scope +private import codeql.ruby.ast.Scope +private import codeql.ruby.ast.internal.TreeSitter +private import codeql.ruby.ast.internal.Variable +private import codeql.ruby.controlflow.ControlFlowGraph +private import Completion +import ControlFlowGraphImplShared + +module CfgScope { + abstract class Range_ extends AstNode { + abstract predicate entry(AstNode first); + + abstract predicate exit(AstNode last, Completion c); + } + + private class ToplevelScope extends Range_, Toplevel { + final override predicate entry(AstNode first) { first(this, first) } + + final override predicate exit(AstNode last, Completion c) { last(this, last, c) } + } + + private class EndBlockScope extends Range_, EndBlock { + final override predicate entry(AstNode first) { + first(this.(Trees::EndBlockTree).getBodyChild(0, _), first) + } + + final override predicate exit(AstNode last, Completion c) { + last(this.(Trees::EndBlockTree).getLastBodyChild(), last, c) + } + } + + private class BodyStmtCallableScope extends Range_, ASTInternal::TBodyStmt, Callable { + final override predicate entry(AstNode first) { this.(Trees::BodyStmtTree).firstInner(first) } + + final override predicate exit(AstNode last, Completion c) { + this.(Trees::BodyStmtTree).lastInner(last, c) + } + } + + private class BraceBlockScope extends Range_, BraceBlock { + final override predicate entry(AstNode first) { + first(this.(Trees::BraceBlockTree).getBodyChild(0, _), first) + } + + final override predicate exit(AstNode last, Completion c) { + last(this.(Trees::BraceBlockTree).getLastBodyChild(), last, c) + } + } +} + +/** Holds if `first` is first executed when entering `scope`. */ +pragma[nomagic] +predicate succEntry(CfgScope::Range_ scope, AstNode first) { scope.entry(first) } + +/** Holds if `last` with completion `c` can exit `scope`. */ +pragma[nomagic] +predicate succExit(CfgScope::Range_ scope, AstNode last, Completion c) { scope.exit(last, c) } + +// TODO: remove this class; it should be replaced with an implicit non AST node +private class ForIn extends AstNode, ASTInternal::TForIn { + final override string toString() { result = "In" } +} + +// TODO: remove this class; it should be replaced with an implicit non AST node +private class ForRange extends ForExpr { + override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + pred = "<in>" and + result = this.getIn() + } + + ForIn getIn() { + result = ASTInternal::TForIn(ASTInternal::toGenerated(this).(Ruby::For).getValue()) + } +} + +/** Defines the CFG by dispatch on the various AST types. */ +module Trees { + private class AliasStmtTree extends StandardPreOrderTree, AliasStmt { + final override ControlFlowTree getChildElement(int i) { + result = this.getNewName() and i = 0 + or + result = this.getOldName() and i = 1 + } + } + + private class ArgumentListTree extends StandardTree, ArgumentList { + final override ControlFlowTree getChildElement(int i) { result = this.getElement(i) } + + final override predicate first(AstNode first) { first(this.getFirstChildElement(), first) } + + final override predicate last(AstNode last, Completion c) { + last(this.getLastChildElement(), last, c) + } + } + + private class AssignExprTree extends StandardPostOrderTree, AssignExpr { + AssignExprTree() { + exists(Expr left | left = this.getLeftOperand() | + left instanceof VariableAccess or + left instanceof ConstantAccess + ) + } + + final override ControlFlowTree getChildElement(int i) { + result = this.getLeftOperand() and i = 0 + or + result = this.getRightOperand() and i = 1 + } + } + + private class BeginTree extends BodyStmtTree, BeginExpr { + final override predicate first(AstNode first) { this.firstInner(first) } + + final override predicate last(AstNode last, Completion c) { this.lastInner(last, c) } + + final override predicate propagatesAbnormal(AstNode child) { none() } + } + + private class BlockArgumentTree extends StandardPostOrderTree, BlockArgument { + final override ControlFlowTree getChildElement(int i) { result = this.getValue() and i = 0 } + } + + abstract private class NonDefaultValueParameterTree extends ControlFlowTree, NamedParameter { + final override predicate first(AstNode first) { + this.getDefiningAccess().(ControlFlowTree).first(first) + } + + final override predicate last(AstNode last, Completion c) { + this.getDefiningAccess().(ControlFlowTree).last(last, c) + } + + override predicate propagatesAbnormal(AstNode child) { + this.getDefiningAccess().(ControlFlowTree).propagatesAbnormal(child) + } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { none() } + } + + private class BlockParameterTree extends NonDefaultValueParameterTree, BlockParameter { } + + abstract class BodyStmtTree extends StmtSequenceTree, BodyStmt { + override predicate first(AstNode first) { first = this } + + predicate firstInner(AstNode first) { + first(this.getBodyChild(0, _), first) + or + not exists(this.getBodyChild(_, _)) and + ( + first(this.getRescue(_), first) + or + not exists(this.getRescue(_)) and + first(this.getEnsure(), first) + ) + } + + predicate lastInner(AstNode last, Completion c) { + exists(boolean ensurable | last = this.getAnEnsurePredecessor(c, ensurable) | + not this.hasEnsure() + or + ensurable = false + ) + or + // If the body completes normally, take the completion from the `ensure` block + this.lastEnsure(last, c, any(NormalCompletion nc), _) + or + // If the `ensure` block completes normally, it inherits any non-normal + // completion from the body + c = + any(NestedEnsureCompletion nec | + this.lastEnsure(last, nec.getAnInnerCompatibleCompletion(), nec.getOuterCompletion(), + nec.getNestLevel()) + ) + or + not exists(this.getBodyChild(_, _)) and + not exists(this.getRescue(_)) and + this.lastEnsure0(last, c) + or + last([this.getEnsure(), this.getBodyChild(_, false)], last, c) and + not c instanceof NormalCompletion + } + + override predicate succ(AstNode pred, AstNode succ, Completion c) { + // Normal left-to-right evaluation in the body + exists(int i | + last(this.getBodyChild(i, _), pred, c) and + first(this.getBodyChild(i + 1, _), succ) and + c instanceof NormalCompletion + ) + or + // Exceptional flow from body to first `rescue` + this.lastBody(pred, c, true) and + first(this.getRescue(0), succ) and + c instanceof RaiseCompletion + or + // Flow from one `rescue` clause to the next when there is no match + exists(RescueTree rescue, int i | rescue = this.getRescue(i) | + rescue.lastNoMatch(pred, c) and + first(this.getRescue(i + 1), succ) + ) + or + // Flow from body to `else` block when no exception + this.lastBody(pred, c, _) and + first(this.getElse(), succ) and + c instanceof NormalCompletion + or + // Flow into `ensure` block + pred = this.getAnEnsurePredecessor(c, true) and + first(this.getEnsure(), succ) + } + + /** + * Gets a last element from this block that may finish with completion `c`, such + * that control may be transferred to the `ensure` block (if it exists), but only + * if `ensurable = true`. + */ + pragma[nomagic] + private AstNode getAnEnsurePredecessor(Completion c, boolean ensurable) { + this.lastBody(result, c, ensurable) and + ( + // Any non-throw completion will always continue directly to the `ensure` block, + // unless there is an `else` block + not c instanceof RaiseCompletion and + not exists(this.getElse()) + or + // Any completion will continue to the `ensure` block when there are no `rescue` + // blocks + not exists(this.getRescue(_)) + ) + or + // Last element from any matching `rescue` block continues to the `ensure` block + this.getRescue(_).(RescueTree).lastMatch(result, c) and + ensurable = true + or + // If the last `rescue` block does not match, continue to the `ensure` block + exists(int lst, MatchingCompletion mc | + this.getRescue(lst).(RescueTree).lastNoMatch(result, mc) and + mc.getValue() = false and + not exists(this.getRescue(lst + 1)) and + c = + any(NestedEnsureCompletion nec | + nec.getOuterCompletion() instanceof RaiseCompletion and + nec.getInnerCompletion() = mc and + nec.getNestLevel() = 0 + ) and + ensurable = true + ) + or + // Last element of `else` block continues to the `ensure` block + last(this.getElse(), result, c) and + ensurable = true + } + + pragma[nomagic] + private predicate lastEnsure0(AstNode last, Completion c) { last(this.getEnsure(), last, c) } + + /** + * Gets a descendant that belongs to the `ensure` block of this block, if any. + * Nested `ensure` blocks are not included. + */ + pragma[nomagic] + AstNode getAnEnsureDescendant() { + result = this.getEnsure() + or + exists(AstNode mid | + mid = this.getAnEnsureDescendant() and + result = mid.getAChild() and + getCfgScope(result) = getCfgScope(mid) and + not exists(BodyStmt nestedBlock | + result = nestedBlock.getEnsure() and + nestedBlock != this + ) + ) + } + + /** + * Holds if `innerBlock` has an `ensure` block and is immediately nested inside the + * `ensure` block of this block. + */ + private predicate nestedEnsure(BodyStmtTree innerBlock) { + exists(StmtSequence innerEnsure | + innerEnsure = this.getAnEnsureDescendant().getAChild() and + getCfgScope(innerEnsure) = getCfgScope(this) and + innerEnsure = innerBlock.(BodyStmt).getEnsure() + ) + } + + /** + * Gets the `ensure`-nesting level of this block. That is, the number of `ensure` + * blocks that this block is nested under. + */ + int getNestLevel() { result = count(BodyStmtTree outer | outer.nestedEnsure+(this)) } + + pragma[nomagic] + private predicate lastEnsure( + AstNode last, NormalCompletion ensure, Completion outer, int nestLevel + ) { + this.lastEnsure0(last, ensure) and + exists( + this.getAnEnsurePredecessor(any(Completion c0 | outer = c0.getOuterCompletion()), true) + ) and + nestLevel = this.getNestLevel() + } + + /** + * Holds if `last` is a last element in the body of this block. `ensurable` + * indicates whether `last` may be a predecessor of an `ensure` block. + */ + pragma[nomagic] + private predicate lastBody(AstNode last, Completion c, boolean ensurable) { + exists(boolean rescuable | + if c instanceof RaiseCompletion then ensurable = rescuable else ensurable = true + | + last(this.getBodyChild(_, rescuable), last, c) and + not c instanceof NormalCompletion + or + exists(int lst | + last(this.getBodyChild(lst, rescuable), last, c) and + not exists(this.getBodyChild(lst + 1, _)) + ) + ) + } + } + + private class BooleanLiteralTree extends LeafTree, BooleanLiteral { } + + class BraceBlockTree extends StmtSequenceTree, BraceBlock { + final override predicate propagatesAbnormal(AstNode child) { none() } + + final override AstNode getBodyChild(int i, boolean rescuable) { + result = this.getParameter(i) and rescuable = false + or + result = StmtSequenceTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) + } + + override predicate first(AstNode first) { first = this } + + override predicate succ(AstNode pred, AstNode succ, Completion c) { + // Normal left-to-right evaluation in the body + exists(int i | + last(this.getBodyChild(i, _), pred, c) and + first(this.getBodyChild(i + 1, _), succ) and + c instanceof NormalCompletion + ) + } + } + + private class CallTree extends StandardPostOrderTree, Call { + CallTree() { + // Logical operations are handled separately + not this instanceof UnaryLogicalOperation and + not this instanceof BinaryLogicalOperation + } + + override ControlFlowTree getChildElement(int i) { result = this.getArgument(i) } + } + + private class CaseTree extends PreOrderTree, CaseExpr { + final override predicate propagatesAbnormal(AstNode child) { + child = this.getValue() or child = this.getABranch() + } + + final override predicate last(AstNode last, Completion c) { + last(this.getValue(), last, c) and not exists(this.getABranch()) + or + last(this.getAWhenBranch().getBody(), last, c) + or + exists(int i, ControlFlowTree lastBranch | + lastBranch = this.getBranch(i) and + not exists(this.getBranch(i + 1)) and + last(lastBranch, last, c) + ) + } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + exists(AstNode next | + pred = this and + first(next, succ) and + c instanceof SimpleCompletion + | + next = this.getValue() + or + not exists(this.getValue()) and + next = this.getBranch(0) + ) + or + last(this.getValue(), pred, c) and + first(this.getBranch(0), succ) and + c instanceof SimpleCompletion + or + exists(int i, WhenTree branch | branch = this.getBranch(i) | + last(branch.getLastPattern(), pred, c) and + first(this.getBranch(i + 1), succ) and + c.(ConditionalCompletion).getValue() = false + ) + } + } + + private class CharacterTree extends LeafTree, CharacterLiteral { } + + private class ClassDeclarationTree extends NamespaceTree, ClassDeclaration { + /** Gets the `i`th child in the body of this block. */ + final override AstNode getBodyChild(int i, boolean rescuable) { + result = this.getScopeExpr() and i = 0 and rescuable = false + or + result = this.getSuperclassExpr() and + i = count(this.getScopeExpr()) and + rescuable = true + or + result = + super + .getBodyChild(i - count(this.getScopeExpr()) - count(this.getSuperclassExpr()), + rescuable) + } + } + + private class ClassVariableTree extends LeafTree, ClassVariableAccess { } + + private class ConditionalExprTree extends PostOrderTree, ConditionalExpr { + final override predicate propagatesAbnormal(AstNode child) { + child = this.getCondition() or child = this.getBranch(_) + } + + final override predicate first(AstNode first) { first(this.getCondition(), first) } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + exists(boolean b | + last(this.getCondition(), pred, c) and + b = c.(BooleanCompletion).getValue() + | + first(this.getBranch(b), succ) + or + not exists(this.getBranch(b)) and + succ = this + ) + or + last(this.getBranch(_), pred, c) and + succ = this and + c instanceof NormalCompletion + } + } + + private class ConditionalLoopTree extends PostOrderTree, ConditionalLoop { + final override predicate propagatesAbnormal(AstNode child) { child = this.getCondition() } + + final override predicate first(AstNode first) { first(this.getCondition(), first) } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + last(this.getCondition(), pred, c) and + this.entersLoopWhenConditionIs(c.(BooleanCompletion).getValue()) and + first(this.getBody(), succ) + or + last(this.getBody(), pred, c) and + first(this.getCondition(), succ) and + c.continuesLoop() + or + last(this.getBody(), pred, c) and + first(this.getBody(), succ) and + c instanceof RedoCompletion + or + succ = this and + ( + last(this.getCondition(), pred, c) and + this.entersLoopWhenConditionIs(c.(BooleanCompletion).getValue().booleanNot()) + or + last(this.getBody(), pred, c) and + not c.continuesLoop() and + not c instanceof BreakCompletion and + not c instanceof RedoCompletion + or + last(this.getBody(), pred, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion()) + ) + } + } + + private class ConstantAccessTree extends PostOrderTree, ConstantAccess { + ConstantAccessTree() { + not this instanceof ClassDeclaration and + not this instanceof ModuleDeclaration + } + + final override predicate propagatesAbnormal(AstNode child) { child = this.getScopeExpr() } + + final override predicate first(AstNode first) { + first(this.getScopeExpr(), first) + or + not exists(this.getScopeExpr()) and + first = this + } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + last(this.getScopeExpr(), pred, c) and + succ = this and + c instanceof NormalCompletion + } + } + + /** A parameter that may have a default value. */ + abstract class DefaultValueParameterTree extends ControlFlowTree { + abstract Expr getDefaultValueExpr(); + + abstract AstNode getAccessNode(); + + predicate hasDefaultValue() { exists(this.getDefaultValueExpr()) } + + final override predicate propagatesAbnormal(AstNode child) { + child = this.getDefaultValueExpr() or child = this.getAccessNode() + } + + final override predicate first(AstNode first) { first = this.getAccessNode() } + + final override predicate last(AstNode last, Completion c) { + last(this.getDefaultValueExpr(), last, c) and + c instanceof NormalCompletion + or + last = this.getAccessNode() and + ( + not this.hasDefaultValue() and + c instanceof SimpleCompletion + or + this.hasDefaultValue() and + c.(MatchingCompletion).getValue() = true + ) + } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + pred = this.getAccessNode() and + first(this.getDefaultValueExpr(), succ) and + c.(MatchingCompletion).getValue() = false + } + } + + private class DesugaredTree extends ControlFlowTree { + ControlFlowTree desugared; + + DesugaredTree() { desugared = this.getDesugared() } + + final override predicate propagatesAbnormal(AstNode child) { + desugared.propagatesAbnormal(child) + } + + final override predicate first(AstNode first) { desugared.first(first) } + + final override predicate last(AstNode last, Completion c) { desugared.last(last, c) } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { none() } + } + + private class DoBlockTree extends BodyStmtTree, DoBlock { + /** Gets the `i`th child in the body of this block. */ + final override AstNode getBodyChild(int i, boolean rescuable) { + result = this.getParameter(i) and rescuable = false + or + result = BodyStmtTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) + } + + override predicate propagatesAbnormal(AstNode child) { none() } + } + + private class EmptyStatementTree extends LeafTree, EmptyStmt { } + + class EndBlockTree extends StmtSequenceTree, EndBlock { + override predicate first(AstNode first) { first = this } + + override predicate succ(AstNode pred, AstNode succ, Completion c) { + // Normal left-to-right evaluation in the body + exists(int i | + last(this.getBodyChild(i, _), pred, c) and + first(this.getBodyChild(i + 1, _), succ) and + c instanceof NormalCompletion + ) + } + } + + private class ForInTree extends LeafTree, ForIn { } + + /** + * Control flow of a for-in loop + * + * For example, this program fragment: + * + * ```rb + * for arg in args do + * puts arg + * end + * puts "done"; + * ``` + * + * has the following control flow graph: + * + * ``` + * args + * | + * in------<----- + * / \ \ + * / \ | + * / \ | + * / \ | + * empty non-empty | + * | \ | + * for \ | + * | arg | + * | | | + * puts "done" puts arg | + * \___/ + * ``` + */ + private class ForTree extends PostOrderTree, ForRange { + final override predicate propagatesAbnormal(AstNode child) { + child = this.getPattern() or child = this.getValue() + } + + final override predicate first(AstNode first) { first(this.getValue(), first) } + + /** + * for pattern in array do body end + * ``` + * array +-> in +--[non empty]--> pattern -> body -> in + * |--[empty]--> for + * ``` + */ + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + last(this.getValue(), pred, c) and + first(this.getIn(), succ) and + c instanceof SimpleCompletion + or + last(this.getIn(), pred, c) and + first(this.getPattern(), succ) and + c.(EmptinessCompletion).getValue() = false + or + last(this.getPattern(), pred, c) and + first(this.getBody(), succ) and + c instanceof NormalCompletion + or + last(this.getBody(), pred, c) and + first(this.getIn(), succ) and + c.continuesLoop() + or + last(this.getBody(), pred, c) and + first(this.getBody(), succ) and + c instanceof RedoCompletion + or + succ = this and + ( + last(this.getIn(), pred, c) and + c.(EmptinessCompletion).getValue() = true + or + last(this.getBody(), pred, c) and + not c.continuesLoop() and + not c instanceof BreakCompletion and + not c instanceof RedoCompletion + or + last(this.getBody(), pred, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion()) + ) + } + } + + private class GlobalVariableTree extends LeafTree, GlobalVariableAccess { } + + private class HashLiteralTree extends StandardPostOrderTree, HashLiteral { + final override ControlFlowTree getChildElement(int i) { result = this.getElement(i) } + } + + private class HashSplatParameterTree extends NonDefaultValueParameterTree, HashSplatParameter { } + + private class HereDocTree extends StandardPreOrderTree, HereDoc { + final override ControlFlowTree getChildElement(int i) { result = this.getComponent(i) } + } + + private class InstanceVariableTree extends LeafTree, InstanceVariableAccess { } + + private class KeywordParameterTree extends DefaultValueParameterTree, KeywordParameter { + final override Expr getDefaultValueExpr() { result = this.getDefaultValue() } + + final override AstNode getAccessNode() { result = this.getDefiningAccess() } + } + + private class LambdaTree extends BodyStmtTree, Lambda { + final override predicate propagatesAbnormal(AstNode child) { none() } + + /** Gets the `i`th child in the body of this block. */ + final override AstNode getBodyChild(int i, boolean rescuable) { + result = this.getParameter(i) and rescuable = false + or + result = BodyStmtTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) + } + } + + private class LocalVariableAccessTree extends LeafTree, LocalVariableAccess { } + + private class LogicalAndTree extends PostOrderTree, LogicalAndExpr { + final override predicate propagatesAbnormal(AstNode child) { child = this.getAnOperand() } + + final override predicate first(AstNode first) { first(this.getLeftOperand(), first) } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + last(this.getLeftOperand(), pred, c) and + c instanceof TrueCompletion and + first(this.getRightOperand(), succ) + or + last(this.getLeftOperand(), pred, c) and + c instanceof FalseCompletion and + succ = this + or + last(this.getRightOperand(), pred, c) and + c instanceof NormalCompletion and + succ = this + } + } + + private class LogicalNotTree extends PostOrderTree, NotExpr { + final override predicate propagatesAbnormal(AstNode child) { child = this.getOperand() } + + final override predicate first(AstNode first) { first(this.getOperand(), first) } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + succ = this and + last(this.getOperand(), pred, c) and + c instanceof NormalCompletion + } + } + + private class LogicalOrTree extends PostOrderTree, LogicalOrExpr { + final override predicate propagatesAbnormal(AstNode child) { child = this.getAnOperand() } + + final override predicate first(AstNode first) { first(this.getLeftOperand(), first) } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + last(this.getLeftOperand(), pred, c) and + c instanceof FalseCompletion and + first(this.getRightOperand(), succ) + or + last(this.getLeftOperand(), pred, c) and + c instanceof TrueCompletion and + succ = this + or + last(this.getRightOperand(), pred, c) and + c instanceof NormalCompletion and + succ = this + } + } + + private class MethodCallTree extends CallTree, MethodCall { + final override ControlFlowTree getChildElement(int i) { + result = this.getReceiver() and i = 0 + or + result = this.getArgument(i - 1) + or + result = this.getBlock() and i = 1 + this.getNumberOfArguments() + } + } + + private class MethodNameTree extends LeafTree, MethodName, ASTInternal::TTokenMethodName { } + + private class MethodTree extends BodyStmtTree, Method { + final override predicate propagatesAbnormal(AstNode child) { none() } + + /** Gets the `i`th child in the body of this block. */ + final override AstNode getBodyChild(int i, boolean rescuable) { + result = this.getParameter(i) and rescuable = false + or + result = BodyStmtTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) + } + } + + private class ModuleDeclarationTree extends NamespaceTree, ModuleDeclaration { + /** Gets the `i`th child in the body of this block. */ + final override AstNode getBodyChild(int i, boolean rescuable) { + result = this.getScopeExpr() and i = 0 and rescuable = false + or + result = NamespaceTree.super.getBodyChild(i - count(this.getScopeExpr()), rescuable) + } + } + + /** + * Namespaces (i.e. modules or classes) behave like other `BodyStmt`s except they are + * executed in pre-order rather than post-order. We do this in order to insert a write for + * `self` before any subsequent reads in the namespace body. + */ + private class NamespaceTree extends BodyStmtTree, Namespace { + final override predicate first(AstNode first) { first = this } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + BodyStmtTree.super.succ(pred, succ, c) + or + pred = this and + this.firstInner(succ) and + c instanceof SimpleCompletion + } + + final override predicate last(AstNode last, Completion c) { + this.lastInner(last, c) + or + not exists(this.getAChild(_)) and + last = this and + c.isValidFor(this) + } + } + + private class NilTree extends LeafTree, NilLiteral { } + + private class NumericLiteralTree extends LeafTree, NumericLiteral { } + + private class OptionalParameterTree extends DefaultValueParameterTree, OptionalParameter { + final override Expr getDefaultValueExpr() { result = this.getDefaultValue() } + + final override AstNode getAccessNode() { result = this.getDefiningAccess() } + } + + private class PairTree extends StandardPostOrderTree, Pair { + final override ControlFlowTree getChildElement(int i) { + result = this.getKey() and i = 0 + or + result = this.getValue() and i = 1 + } + } + + private class RangeLiteralTree extends StandardPostOrderTree, RangeLiteral { + final override ControlFlowTree getChildElement(int i) { + result = this.getBegin() and i = 0 + or + result = this.getEnd() and i = 1 + } + } + + private class RedoStmtTree extends LeafTree, RedoStmt { } + + private class RescueModifierTree extends PreOrderTree, RescueModifierExpr { + final override predicate propagatesAbnormal(AstNode child) { child = this.getHandler() } + + final override predicate last(AstNode last, Completion c) { + last(this.getBody(), last, c) and + not c instanceof RaiseCompletion + or + last(this.getHandler(), last, c) + } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + pred = this and + first(this.getBody(), succ) and + c instanceof SimpleCompletion + or + last(this.getBody(), pred, c) and + c instanceof RaiseCompletion and + first(this.getHandler(), succ) + } + } + + private class RescueTree extends PreOrderTree, RescueClause { + final override predicate propagatesAbnormal(AstNode child) { child = this.getAnException() } + + private Expr getLastException() { + exists(int i | result = this.getException(i) and not exists(this.getException(i + 1))) + } + + predicate lastMatch(AstNode last, Completion c) { + last(this.getBody(), last, c) + or + not exists(this.getBody()) and + ( + last(this.getVariableExpr(), last, c) + or + not exists(this.getVariableExpr()) and + ( + last(this.getAnException(), last, c) and + c.(MatchingCompletion).getValue() = true + or + not exists(this.getAnException()) and + last = this and + c.isValidFor(this) + ) + ) + } + + predicate lastNoMatch(AstNode last, Completion c) { + last(this.getLastException(), last, c) and + c.(MatchingCompletion).getValue() = false + } + + final override predicate last(AstNode last, Completion c) { + this.lastNoMatch(last, c) + or + this.lastMatch(last, c) + } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + exists(AstNode next | + pred = this and + first(next, succ) and + c instanceof SimpleCompletion + | + next = this.getException(0) + or + not exists(this.getException(0)) and + ( + next = this.getVariableExpr() + or + not exists(this.getVariableExpr()) and + next = this.getBody() + ) + ) + or + exists(AstNode next | + last(this.getAnException(), pred, c) and + first(next, succ) and + c.(MatchingCompletion).getValue() = true + | + next = this.getVariableExpr() + or + not exists(this.getVariableExpr()) and + next = this.getBody() + ) + or + exists(int i | + last(this.getException(i), pred, c) and + c.(MatchingCompletion).getValue() = false and + first(this.getException(i + 1), succ) + ) + or + last(this.getVariableExpr(), pred, c) and + first(this.getBody(), succ) and + c instanceof NormalCompletion + } + } + + private class RetryStmtTree extends LeafTree, RetryStmt { } + + private class ReturningStmtTree extends StandardPostOrderTree, ReturningStmt { + final override ControlFlowTree getChildElement(int i) { result = this.getValue() and i = 0 } + } + + private class SimpleParameterTree extends NonDefaultValueParameterTree, SimpleParameter { } + + // Corner case: For duplicated '_' parameters, only the first occurence has a defining + // access. For subsequent parameters we simply include the parameter itself in the CFG + private class SimpleParameterTreeDupUnderscore extends LeafTree, SimpleParameter { + SimpleParameterTreeDupUnderscore() { not exists(this.getDefiningAccess()) } + } + + private class SingletonClassTree extends BodyStmtTree, SingletonClass { + final override predicate first(AstNode first) { + this.firstInner(first) + or + not exists(this.getAChild(_)) and + first = this + } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + BodyStmtTree.super.succ(pred, succ, c) + or + succ = this and + this.lastInner(pred, c) + } + + /** Gets the `i`th child in the body of this block. */ + final override AstNode getBodyChild(int i, boolean rescuable) { + ( + result = this.getValue() and i = 0 and rescuable = false + or + result = BodyStmtTree.super.getBodyChild(i - 1, rescuable) + ) + } + } + + private class SingletonMethodTree extends BodyStmtTree, SingletonMethod { + final override predicate propagatesAbnormal(AstNode child) { none() } + + /** Gets the `i`th child in the body of this block. */ + final override AstNode getBodyChild(int i, boolean rescuable) { + result = this.getParameter(i) and rescuable = false + or + result = BodyStmtTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) + } + + override predicate first(AstNode first) { first(this.getObject(), first) } + + override predicate succ(AstNode pred, AstNode succ, Completion c) { + BodyStmtTree.super.succ(pred, succ, c) + or + last(this.getObject(), pred, c) and + succ = this and + c instanceof NormalCompletion + } + } + + private class SplatParameterTree extends NonDefaultValueParameterTree, SplatParameter { } + + class StmtSequenceTree extends PostOrderTree, StmtSequence { + override predicate propagatesAbnormal(AstNode child) { child = this.getAStmt() } + + override predicate first(AstNode first) { first(this.getStmt(0), first) } + + /** Gets the `i`th child in the body of this body statement. */ + AstNode getBodyChild(int i, boolean rescuable) { + result = this.getStmt(i) and + rescuable = true + } + + final AstNode getLastBodyChild() { + exists(int i | + result = this.getBodyChild(i, _) and + not exists(this.getBodyChild(i + 1, _)) + ) + } + + override predicate succ(AstNode pred, AstNode succ, Completion c) { + // Normal left-to-right evaluation in the body + exists(int i | + last(this.getBodyChild(i, _), pred, c) and + first(this.getBodyChild(i + 1, _), succ) and + c instanceof NormalCompletion + ) + or + succ = this and + last(this.getLastBodyChild(), pred, c) and + c instanceof NormalCompletion + } + } + + private class StringConcatenationTree extends StandardTree, StringConcatenation { + final override ControlFlowTree getChildElement(int i) { result = this.getString(i) } + + final override predicate first(AstNode first) { first(this.getFirstChildElement(), first) } + + final override predicate last(AstNode last, Completion c) { + last(this.getLastChildElement(), last, c) + } + } + + private class StringlikeLiteralTree extends StandardPostOrderTree, StringlikeLiteral { + StringlikeLiteralTree() { not this instanceof HereDoc } + + final override ControlFlowTree getChildElement(int i) { result = this.getComponent(i) } + } + + private class ToplevelTree extends BodyStmtTree, Toplevel { + final override AstNode getBodyChild(int i, boolean rescuable) { + result = this.getBeginBlock(i) and rescuable = true + or + result = BodyStmtTree.super.getBodyChild(i - count(this.getABeginBlock()), rescuable) + } + + final override predicate first(AstNode first) { this.firstInner(first) } + + final override predicate last(AstNode last, Completion c) { this.lastInner(last, c) } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + BodyStmtTree.super.succ(pred, succ, c) + } + } + + private class TuplePatternTree extends StandardPostOrderTree, TuplePattern { + final override ControlFlowTree getChildElement(int i) { result = this.getElement(i) } + } + + private class UndefStmtTree extends StandardPreOrderTree, UndefStmt { + final override ControlFlowTree getChildElement(int i) { result = this.getMethodName(i) } + } + + private class WhenTree extends PreOrderTree, WhenExpr { + final override predicate propagatesAbnormal(AstNode child) { child = this.getAPattern() } + + final Expr getLastPattern() { + exists(int i | + result = this.getPattern(i) and + not exists(this.getPattern(i + 1)) + ) + } + + final override predicate last(AstNode last, Completion c) { + last(this.getLastPattern(), last, c) and + c.(ConditionalCompletion).getValue() = false + or + last(this.getBody(), last, c) + } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + pred = this and + first(this.getPattern(0), succ) and + c instanceof SimpleCompletion + or + exists(int i, Expr p, boolean b | + p = this.getPattern(i) and + last(p, pred, c) and + b = c.(ConditionalCompletion).getValue() + | + b = true and + first(this.getBody(), succ) + or + b = false and + first(this.getPattern(i + 1), succ) + ) + } + } +} + +private Scope parent(Scope n) { + result = n.getOuterScope() and + not n instanceof CfgScope::Range_ +} + +/** Gets the CFG scope of node `n`. */ +pragma[inline] +CfgScope getCfgScope(AstNode n) { + exists(AstNode n0 | + pragma[only_bind_into](n0) = n and + pragma[only_bind_into](result) = getCfgScopeImpl(n0) + ) +} + +cached +private module Cached { + /** Gets the CFG scope of node `n`. */ + cached + CfgScope getCfgScopeImpl(AstNode n) { + forceCachingInSameStage() and + result = parent*(ASTInternal::fromGenerated(scopeOf(ASTInternal::toGeneratedInclSynth(n)))) + } + + cached + newtype TSuccessorType = + TSuccessorSuccessor() or + TBooleanSuccessor(boolean b) { b in [false, true] } or + TEmptinessSuccessor(boolean isEmpty) { isEmpty in [false, true] } or + TMatchingSuccessor(boolean isMatch) { isMatch in [false, true] } or + TReturnSuccessor() or + TBreakSuccessor() or + TNextSuccessor() or + TRedoSuccessor() or + TRetrySuccessor() or + TRaiseSuccessor() or // TODO: Add exception type? + TExitSuccessor() +} + +import Cached diff --git a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll new file mode 100644 index 00000000000..050a9384729 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll @@ -0,0 +1,945 @@ +/** Provides language-independent definitions for AST-to-CFG construction. */ + +private import ControlFlowGraphImplSpecific + +/** An element with associated control flow. */ +abstract class ControlFlowTree extends ControlFlowTreeBase { + /** Holds if `first` is the first element executed within this element. */ + pragma[nomagic] + abstract predicate first(ControlFlowElement first); + + /** + * Holds if `last` with completion `c` is a potential last element executed + * within this element. + */ + pragma[nomagic] + abstract predicate last(ControlFlowElement last, Completion c); + + /** Holds if abnormal execution of `child` should propagate upwards. */ + abstract predicate propagatesAbnormal(ControlFlowElement child); + + /** + * Holds if `succ` is a control flow successor for `pred`, given that `pred` + * finishes with completion `c`. + */ + pragma[nomagic] + abstract predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c); +} + +/** + * Holds if `first` is the first element executed within control flow + * element `cft`. + */ +predicate first(ControlFlowTree cft, ControlFlowElement first) { cft.first(first) } + +/** + * Holds if `last` with completion `c` is a potential last element executed + * within control flow element `cft`. + */ +predicate last(ControlFlowTree cft, ControlFlowElement last, Completion c) { + cft.last(last, c) + or + exists(ControlFlowElement cfe | + cft.propagatesAbnormal(cfe) and + last(cfe, last, c) and + not completionIsNormal(c) + ) +} + +/** + * Holds if `succ` is a control flow successor for `pred`, given that `pred` + * finishes with completion `c`. + */ +pragma[nomagic] +predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + any(ControlFlowTree cft).succ(pred, succ, c) +} + +/** An element that is executed in pre-order. */ +abstract class PreOrderTree extends ControlFlowTree { + final override predicate first(ControlFlowElement first) { first = this } +} + +/** An element that is executed in post-order. */ +abstract class PostOrderTree extends ControlFlowTree { + override predicate last(ControlFlowElement last, Completion c) { + last = this and + completionIsValidFor(c, last) + } +} + +/** + * An element where the children are evaluated following a standard left-to-right + * evaluation. The actual evaluation order is determined by the predicate + * `getChildElement()`. + */ +abstract class StandardTree extends ControlFlowTree { + /** Gets the `i`th child element, in order of evaluation. */ + abstract ControlFlowElement getChildElement(int i); + + private ControlFlowElement getChildElementRanked(int i) { + result = + rank[i + 1](ControlFlowElement child, int j | + child = this.getChildElement(j) + | + child order by j + ) + } + + /** Gets the first child node of this element. */ + final ControlFlowElement getFirstChildElement() { result = this.getChildElementRanked(0) } + + /** Gets the last child node of this node. */ + final ControlFlowElement getLastChildElement() { + exists(int last | + result = this.getChildElementRanked(last) and + not exists(this.getChildElementRanked(last + 1)) + ) + } + + /** Holds if this element has no children. */ + predicate isLeafElement() { not exists(this.getFirstChildElement()) } + + override predicate propagatesAbnormal(ControlFlowElement child) { + child = this.getChildElement(_) + } + + pragma[nomagic] + override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + exists(int i | + last(this.getChildElementRanked(i), pred, c) and + completionIsNormal(c) and + first(this.getChildElementRanked(i + 1), succ) + ) + } +} + +/** A standard element that is executed in pre-order. */ +abstract class StandardPreOrderTree extends StandardTree, PreOrderTree { + override predicate last(ControlFlowElement last, Completion c) { + last(this.getLastChildElement(), last, c) + or + this.isLeafElement() and + completionIsValidFor(c, this) and + last = this + } + + override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + StandardTree.super.succ(pred, succ, c) + or + pred = this and + first(this.getFirstChildElement(), succ) and + completionIsSimple(c) + } +} + +/** A standard element that is executed in post-order. */ +abstract class StandardPostOrderTree extends StandardTree, PostOrderTree { + override predicate first(ControlFlowElement first) { + first(this.getFirstChildElement(), first) + or + not exists(this.getFirstChildElement()) and + first = this + } + + override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + StandardTree.super.succ(pred, succ, c) + or + last(this.getLastChildElement(), pred, c) and + succ = this and + completionIsNormal(c) + } +} + +/** An element that is a leaf in the control flow graph. */ +abstract class LeafTree extends PreOrderTree, PostOrderTree { + override predicate propagatesAbnormal(ControlFlowElement child) { none() } + + override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { none() } +} + +/** + * Holds if split kinds `sk1` and `sk2` may overlap. That is, they may apply + * to at least one common AST node inside `scope`. + */ +private predicate overlapping(CfgScope scope, SplitKind sk1, SplitKind sk2) { + exists(ControlFlowElement e | + sk1.appliesTo(e) and + sk2.appliesTo(e) and + scope = getCfgScope(e) + ) +} + +/** + * A split kind. Each control flow node can have at most one split of a + * given kind. + */ +abstract class SplitKind extends SplitKindBase { + /** Gets a split of this kind. */ + SplitImpl getASplit() { result.getKind() = this } + + /** Holds if some split of this kind applies to AST node `n`. */ + predicate appliesTo(ControlFlowElement n) { this.getASplit().appliesTo(n) } + + /** + * Gets a unique integer representing this split kind. The integer is used + * to represent sets of splits as ordered lists. + */ + abstract int getListOrder(); + + /** Gets the rank of this split kind among all overlapping kinds for `c`. */ + private int getRank(CfgScope scope) { + this = rank[result](SplitKind sk | overlapping(scope, this, sk) | sk order by sk.getListOrder()) + } + + /** + * Holds if this split kind is enabled for AST node `n`. For performance reasons, + * the number of splits is restricted by the `maxSplits()` predicate. + */ + predicate isEnabled(ControlFlowElement n) { + this.appliesTo(n) and + this.getRank(getCfgScope(n)) <= maxSplits() + } + + /** + * Gets the rank of this split kind among all the split kinds that apply to + * AST node `n`. The rank is based on the order defined by `getListOrder()`. + */ + int getListRank(ControlFlowElement n) { + this.isEnabled(n) and + this = rank[result](SplitKind sk | sk.appliesTo(n) | sk order by sk.getListOrder()) + } + + /** Gets a textual representation of this split kind. */ + abstract string toString(); +} + +/** Provides the interface for implementing an entity to split on. */ +abstract class SplitImpl extends Split { + /** Gets the kind of this split. */ + abstract SplitKind getKind(); + + /** + * Holds if this split is entered when control passes from `pred` to `succ` with + * completion `c`. + * + * Invariant: `hasEntry(pred, succ, c) implies succ(pred, succ, c)`. + */ + abstract predicate hasEntry(ControlFlowElement pred, ControlFlowElement succ, Completion c); + + /** + * Holds if this split is entered when control passes from `scope` to the entry point + * `first`. + * + * Invariant: `hasEntryScope(scope, first) implies scopeFirst(scope, first)`. + */ + abstract predicate hasEntryScope(CfgScope scope, ControlFlowElement first); + + /** + * Holds if this split is left when control passes from `pred` to `succ` with + * completion `c`. + * + * Invariant: `hasExit(pred, succ, c) implies succ(pred, succ, c)`. + */ + abstract predicate hasExit(ControlFlowElement pred, ControlFlowElement succ, Completion c); + + /** + * Holds if this split is left when control passes from `last` out of the enclosing + * scope `scope` with completion `c`. + * + * Invariant: `hasExitScope(scope, last, c) implies scopeLast(scope, last, c)` + */ + abstract predicate hasExitScope(CfgScope scope, ControlFlowElement last, Completion c); + + /** + * Holds if this split is maintained when control passes from `pred` to `succ` with + * completion `c`. + * + * Invariant: `hasSuccessor(pred, succ, c) implies succ(pred, succ, c)` + */ + abstract predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c); + + /** Holds if this split applies to control flow element `cfe`. */ + final predicate appliesTo(ControlFlowElement cfe) { + this.hasEntry(_, cfe, _) + or + this.hasEntryScope(_, cfe) + or + exists(ControlFlowElement pred | this.appliesTo(pred) | this.hasSuccessor(pred, cfe, _)) + } + + /** The `succ` relation restricted to predecessors `pred` that this split applies to. */ + pragma[noinline] + final predicate appliesSucc(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + this.appliesTo(pred) and + succ(pred, succ, c) + } +} + +/** + * A set of control flow node splits. The set is represented by a list of splits, + * ordered by ascending rank. + */ +class Splits extends TSplits { + /** Gets a textual representation of this set of splits. */ + string toString() { result = splitsToString(this) } + + /** Gets a split belonging to this set of splits. */ + SplitImpl getASplit() { + exists(SplitImpl head, Splits tail | this = TSplitsCons(head, tail) | + result = head + or + result = tail.getASplit() + ) + } +} + +private predicate succEntrySplitsFromRank( + CfgScope pred, ControlFlowElement succ, Splits splits, int rnk +) { + splits = TSplitsNil() and + scopeFirst(pred, succ) and + rnk = 0 + or + exists(SplitImpl head, Splits tail | succEntrySplitsCons(pred, succ, head, tail, rnk) | + splits = TSplitsCons(head, tail) + ) +} + +private predicate succEntrySplitsCons( + CfgScope pred, ControlFlowElement succ, SplitImpl head, Splits tail, int rnk +) { + succEntrySplitsFromRank(pred, succ, tail, rnk - 1) and + head.hasEntryScope(pred, succ) and + rnk = head.getKind().getListRank(succ) +} + +/** + * Holds if `succ` with splits `succSplits` is the first element that is executed + * when entering callable `pred`. + */ +pragma[noinline] +private predicate succEntrySplits( + CfgScope pred, ControlFlowElement succ, Splits succSplits, SuccessorType t +) { + exists(int rnk | + scopeFirst(pred, succ) and + successorTypeIsSimple(t) and + succEntrySplitsFromRank(pred, succ, succSplits, rnk) + | + rnk = 0 and + not any(SplitImpl split).hasEntryScope(pred, succ) + or + rnk = max(SplitImpl split | split.hasEntryScope(pred, succ) | split.getKind().getListRank(succ)) + ) +} + +/** + * Holds if `pred` with splits `predSplits` can exit the enclosing callable + * `succ` with type `t`. + */ +private predicate succExitSplits( + ControlFlowElement pred, Splits predSplits, CfgScope succ, SuccessorType t +) { + exists(Reachability::SameSplitsBlock b, Completion c | pred = b.getAnElement() | + b.isReachable(predSplits) and + t = getAMatchingSuccessorType(c) and + scopeLast(succ, pred, c) and + forall(SplitImpl predSplit | predSplit = predSplits.getASplit() | + predSplit.hasExitScope(succ, pred, c) + ) + ) +} + +/** + * Provides a predicate for the successor relation with split information, + * as well as logic used to construct the type `TSplits` representing sets + * of splits. Only sets of splits that can be reached are constructed, hence + * the predicates are mutually recursive. + * + * For the successor relation + * + * ```ql + * succSplits(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, Completion c) + * ``` + * + * the following invariants are maintained: + * + * 1. `pred` is reachable with split set `predSplits`. + * 2. For all `split` in `predSplits`: + * - If `split.hasSuccessor(pred, succ, c)` then `split` in `succSplits`. + * 3. For all `split` in `predSplits`: + * - If `split.hasExit(pred, succ, c)` and not `split.hasEntry(pred, succ, c)` then + * `split` not in `succSplits`. + * 4. For all `split` with kind not in `predSplits`: + * - If `split.hasEntry(pred, succ, c)` then `split` in `succSplits`. + * 5. For all `split` in `succSplits`: + * - `split.hasSuccessor(pred, succ, c)` and `split` in `predSplits`, or + * - `split.hasEntry(pred, succ, c)`. + * + * The algorithm divides into four cases: + * + * 1. The set of splits for the successor is the same as the set of splits + * for the predecessor: + * a) The successor is in the same `SameSplitsBlock` as the predecessor. + * b) The successor is *not* in the same `SameSplitsBlock` as the predecessor. + * 2. The set of splits for the successor is different from the set of splits + * for the predecessor: + * a) The set of splits for the successor is *maybe* non-empty. + * b) The set of splits for the successor is *always* empty. + * + * Only case 2a may introduce new sets of splits, so only predicates from + * this case are used in the definition of `TSplits`. + * + * The predicates in this module are named after the cases above. + */ +private module SuccSplits { + private predicate succInvariant1( + Reachability::SameSplitsBlock b, ControlFlowElement pred, Splits predSplits, + ControlFlowElement succ, Completion c + ) { + pred = b.getAnElement() and + b.isReachable(predSplits) and + succ(pred, succ, c) + } + + private predicate case1b0( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c + ) { + exists(Reachability::SameSplitsBlock b | + // Invariant 1 + succInvariant1(b, pred, predSplits, succ, c) + | + (succ = b.getAnElement() implies succ = b) and + // Invariant 4 + not exists(SplitImpl split | split.hasEntry(pred, succ, c)) + ) + } + + /** + * Case 1b. + * + * Invariants 1 and 4 hold in the base case, and invariants 2, 3, and 5 are + * maintained for all splits in `predSplits` (= `succSplits`), except + * possibly for the splits in `except`. + * + * The predicate is written using explicit recursion, as opposed to a `forall`, + * to avoid negative recursion. + */ + private predicate case1bForall( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, Splits except + ) { + case1b0(pred, predSplits, succ, c) and + except = predSplits + or + exists(SplitImpl split | + case1bForallCons(pred, predSplits, succ, c, split, except) and + split.hasSuccessor(pred, succ, c) + ) + } + + pragma[noinline] + private predicate case1bForallCons( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, + SplitImpl exceptHead, Splits exceptTail + ) { + case1bForall(pred, predSplits, succ, c, TSplitsCons(exceptHead, exceptTail)) + } + + private predicate case1( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c + ) { + // Case 1a + exists(Reachability::SameSplitsBlock b | succInvariant1(b, pred, predSplits, succ, c) | + succ = b.getAnElement() and + not succ = b + ) + or + // Case 1b + case1bForall(pred, predSplits, succ, c, TSplitsNil()) + } + + pragma[noinline] + private SplitImpl succInvariant1GetASplit( + Reachability::SameSplitsBlock b, ControlFlowElement pred, Splits predSplits, + ControlFlowElement succ, Completion c + ) { + succInvariant1(b, pred, predSplits, succ, c) and + result = predSplits.getASplit() + } + + private predicate case2aux( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c + ) { + exists(Reachability::SameSplitsBlock b | + succInvariant1(b, pred, predSplits, succ, c) and + (succ = b.getAnElement() implies succ = b) + | + succInvariant1GetASplit(b, pred, predSplits, succ, c).hasExit(pred, succ, c) + or + any(SplitImpl split).hasEntry(pred, succ, c) + ) + } + + /** + * Holds if `succSplits` should not inherit a split of kind `sk` from + * `predSplits`, except possibly because of a split in `except`. + * + * The predicate is written using explicit recursion, as opposed to a `forall`, + * to avoid negative recursion. + */ + private predicate case2aNoneInheritedOfKindForall( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, SplitKind sk, + Splits except + ) { + case2aux(pred, predSplits, succ, c) and + sk.appliesTo(succ) and + except = predSplits + or + exists(Splits mid, SplitImpl split | + case2aNoneInheritedOfKindForall(pred, predSplits, succ, c, sk, mid) and + mid = TSplitsCons(split, except) + | + split.getKind() = any(SplitKind sk0 | sk0 != sk and sk0.appliesTo(succ)) + or + split.hasExit(pred, succ, c) + ) + } + + pragma[nomagic] + private predicate entryOfKind( + ControlFlowElement pred, ControlFlowElement succ, Completion c, SplitImpl split, SplitKind sk + ) { + split.hasEntry(pred, succ, c) and + sk = split.getKind() + } + + /** Holds if `succSplits` should not have a split of kind `sk`. */ + pragma[nomagic] + private predicate case2aNoneOfKind( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, SplitKind sk + ) { + // None inherited from predecessor + case2aNoneInheritedOfKindForall(pred, predSplits, succ, c, sk, TSplitsNil()) and + // None newly entered into + not entryOfKind(pred, succ, c, _, sk) + } + + /** Holds if `succSplits` should not have a split of kind `sk` at rank `rnk`. */ + pragma[nomagic] + private predicate case2aNoneAtRank( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, int rnk + ) { + exists(SplitKind sk | case2aNoneOfKind(pred, predSplits, succ, c, sk) | + rnk = sk.getListRank(succ) + ) + } + + pragma[nomagic] + private SplitImpl case2auxGetAPredecessorSplit( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c + ) { + case2aux(pred, predSplits, succ, c) and + result = predSplits.getASplit() + } + + /** Gets a split that should be in `succSplits`. */ + pragma[nomagic] + private SplitImpl case2aSome( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, SplitKind sk + ) { + ( + // Inherited from predecessor + result = case2auxGetAPredecessorSplit(pred, predSplits, succ, c) and + result.hasSuccessor(pred, succ, c) + or + // Newly entered into + exists(SplitKind sk0 | + case2aNoneInheritedOfKindForall(pred, predSplits, succ, c, sk0, TSplitsNil()) + | + entryOfKind(pred, succ, c, result, sk0) + ) + ) and + sk = result.getKind() + } + + /** Gets a split that should be in `succSplits` at rank `rnk`. */ + pragma[nomagic] + SplitImpl case2aSomeAtRank( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, int rnk + ) { + exists(SplitKind sk | result = case2aSome(pred, predSplits, succ, c, sk) | + rnk = sk.getListRank(succ) + ) + } + + /** + * Case 2a. + * + * As opposed to the other cases, in this case we need to construct a new set + * of splits `succSplits`. Since this involves constructing the very IPA type, + * we cannot recurse directly over the structure of `succSplits`. Instead, we + * recurse over the ranks of all splits that *might* be in `succSplits`. + * + * - Invariant 1 holds in the base case, + * - invariant 2 holds for all splits with rank at least `rnk`, + * - invariant 3 holds for all splits in `predSplits`, + * - invariant 4 holds for all splits in `succSplits` with rank at least `rnk`, + * and + * - invariant 4 holds for all splits in `succSplits` with rank at least `rnk`. + */ + predicate case2aFromRank( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, + Completion c, int rnk + ) { + case2aux(pred, predSplits, succ, c) and + succSplits = TSplitsNil() and + rnk = max(any(SplitKind sk).getListRank(succ)) + 1 + or + case2aFromRank(pred, predSplits, succ, succSplits, c, rnk + 1) and + case2aNoneAtRank(pred, predSplits, succ, c, rnk) + or + exists(Splits mid, SplitImpl split | split = case2aCons(pred, predSplits, succ, mid, c, rnk) | + succSplits = TSplitsCons(split, mid) + ) + } + + pragma[noinline] + private SplitImpl case2aCons( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, + Completion c, int rnk + ) { + case2aFromRank(pred, predSplits, succ, succSplits, c, rnk + 1) and + result = case2aSomeAtRank(pred, predSplits, succ, c, rnk) + } + + /** + * Case 2b. + * + * Invariants 1, 4, and 5 hold in the base case, and invariants 2 and 3 are + * maintained for all splits in `predSplits`, except possibly for the splits + * in `except`. + * + * The predicate is written using explicit recursion, as opposed to a `forall`, + * to avoid negative recursion. + */ + private predicate case2bForall( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, Splits except + ) { + // Invariant 1 + case2aux(pred, predSplits, succ, c) and + // Invariants 4 and 5 + not any(SplitKind sk).appliesTo(succ) and + except = predSplits + or + exists(SplitImpl split | case2bForallCons(pred, predSplits, succ, c, split, except) | + // Invariants 2 and 3 + split.hasExit(pred, succ, c) + ) + } + + pragma[noinline] + private predicate case2bForallCons( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, + SplitImpl exceptHead, Splits exceptTail + ) { + case2bForall(pred, predSplits, succ, c, TSplitsCons(exceptHead, exceptTail)) + } + + private predicate case2( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, + Completion c + ) { + case2aFromRank(pred, predSplits, succ, succSplits, c, 1) + or + case2bForall(pred, predSplits, succ, c, TSplitsNil()) and + succSplits = TSplitsNil() + } + + /** + * Holds if `succ` with splits `succSplits` is a successor of type `t` for `pred` + * with splits `predSplits`. + */ + predicate succSplits( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, + Completion c + ) { + case1(pred, predSplits, succ, c) and + succSplits = predSplits + or + case2(pred, predSplits, succ, succSplits, c) + } +} + +import SuccSplits + +/** Provides logic for calculating reachable control flow nodes. */ +private module Reachability { + /** + * Holds if `cfe` is a control flow element where the set of possible splits may + * be different from the set of possible splits for one of `cfe`'s predecessors. + * That is, `cfe` starts a new block of elements with the same set of splits. + */ + private predicate startsSplits(ControlFlowElement cfe) { + scopeFirst(_, cfe) + or + exists(SplitImpl s | + s.hasEntry(_, cfe, _) + or + s.hasExit(_, cfe, _) + ) + or + exists(ControlFlowElement pred, SplitImpl split, Completion c | succ(pred, cfe, c) | + split.appliesTo(pred) and + not split.hasSuccessor(pred, cfe, c) + ) + } + + private predicate intraSplitsSucc(ControlFlowElement pred, ControlFlowElement succ) { + succ(pred, succ, _) and + not startsSplits(succ) + } + + private predicate splitsBlockContains(ControlFlowElement start, ControlFlowElement cfe) = + fastTC(intraSplitsSucc/2)(start, cfe) + + /** + * A block of control flow elements where the set of splits is guaranteed + * to remain unchanged, represented by the first element in the block. + */ + class SameSplitsBlock extends ControlFlowElement { + SameSplitsBlock() { startsSplits(this) } + + /** Gets a control flow element in this block. */ + ControlFlowElement getAnElement() { + splitsBlockContains(this, result) + or + result = this + } + + pragma[noinline] + private SameSplitsBlock getASuccessor(Splits predSplits, Splits succSplits) { + exists(ControlFlowElement pred | pred = this.getAnElement() | + succSplits(pred, predSplits, result, succSplits, _) + ) + } + + /** + * Holds if the elements of this block are reachable from a callable entry + * point, with the splits `splits`. + */ + predicate isReachable(Splits splits) { + // Base case + succEntrySplits(_, this, splits, _) + or + // Recursive case + exists(SameSplitsBlock pred, Splits predSplits | pred.isReachable(predSplits) | + this = pred.getASuccessor(predSplits, splits) + ) + } + } +} + +cached +private module Cached { + /** + * If needed, call this predicate from `ControlFlowGraphImplSpecific.qll` in order to + * force a stage-dependency on the `ControlFlowGraphImplShared.qll` stage and therby + * collapsing the two stages. + */ + cached + predicate forceCachingInSameStage() { any() } + + cached + newtype TSplits = + TSplitsNil() or + TSplitsCons(SplitImpl head, Splits tail) { + exists( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, int rnk + | + case2aFromRank(pred, predSplits, succ, tail, c, rnk + 1) and + head = case2aSomeAtRank(pred, predSplits, succ, c, rnk) + ) + or + succEntrySplitsCons(_, _, head, tail, _) + } + + cached + string splitsToString(Splits splits) { + splits = TSplitsNil() and + result = "" + or + exists(SplitImpl head, Splits tail, string headString, string tailString | + splits = TSplitsCons(head, tail) + | + headString = head.toString() and + tailString = tail.toString() and + if tailString = "" + then result = headString + else + if headString = "" + then result = tailString + else result = headString + ", " + tailString + ) + } + + /** + * Internal representation of control flow nodes in the control flow graph. + * The control flow graph is pruned for unreachable nodes. + */ + cached + newtype TNode = + TEntryNode(CfgScope scope) { succEntrySplits(scope, _, _, _) } or + TAnnotatedExitNode(CfgScope scope, boolean normal) { + exists(Reachability::SameSplitsBlock b, SuccessorType t | b.isReachable(_) | + succExitSplits(b.getAnElement(), _, scope, t) and + if isAbnormalExitType(t) then normal = false else normal = true + ) + } or + TExitNode(CfgScope scope) { + exists(Reachability::SameSplitsBlock b | b.isReachable(_) | + succExitSplits(b.getAnElement(), _, scope, _) + ) + } or + TElementNode(ControlFlowElement cfe, Splits splits) { + exists(Reachability::SameSplitsBlock b | b.isReachable(splits) | cfe = b.getAnElement()) + } + + /** Gets a successor node of a given flow type, if any. */ + cached + TNode getASuccessor(TNode pred, SuccessorType t) { + // Callable entry node -> callable body + exists(ControlFlowElement succElement, Splits succSplits, CfgScope scope | + result = TElementNode(succElement, succSplits) and + pred = TEntryNode(scope) and + succEntrySplits(scope, succElement, succSplits, t) + ) + or + exists(ControlFlowElement predElement, Splits predSplits | + pred = TElementNode(predElement, predSplits) + | + // Element node -> callable exit (annotated) + exists(CfgScope scope, boolean normal | + result = TAnnotatedExitNode(scope, normal) and + succExitSplits(predElement, predSplits, scope, t) and + if isAbnormalExitType(t) then normal = false else normal = true + ) + or + // Element node -> element node + exists(ControlFlowElement succElement, Splits succSplits, Completion c | + result = TElementNode(succElement, succSplits) + | + succSplits(predElement, predSplits, succElement, succSplits, c) and + t = getAMatchingSuccessorType(c) + ) + ) + or + // Callable exit (annotated) -> callable exit + exists(CfgScope scope | + pred = TAnnotatedExitNode(scope, _) and + result = TExitNode(scope) and + successorTypeIsSimple(t) + ) + } + + /** + * Gets a first control flow element executed within `cfe`. + */ + cached + ControlFlowElement getAControlFlowEntryNode(ControlFlowElement cfe) { first(cfe, result) } + + /** + * Gets a potential last control flow element executed within `cfe`. + */ + cached + ControlFlowElement getAControlFlowExitNode(ControlFlowElement cfe) { last(cfe, result, _) } +} + +import Cached + +/** + * Import this module into a `.ql` file of `@kind graph` to render a CFG. The + * graph is restricted to nodes from `RelevantNode`. + */ +module TestOutput { + abstract class RelevantNode extends Node { } + + query predicate nodes(RelevantNode n, string attr, string val) { + attr = "semmle.order" and + val = + any(int i | + n = + rank[i](RelevantNode p, Location l | + l = p.getLocation() + | + p + order by + l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(), + l.getStartColumn() + ) + ).toString() + } + + query predicate edges(RelevantNode pred, RelevantNode succ, string attr, string val) { + exists(SuccessorType t | succ = getASuccessor(pred, t) | + attr = "semmle.label" and + if successorTypeIsSimple(t) then val = "" else val = t.toString() + ) + } +} + +/** Provides a set of splitting-related consistency queries. */ +module Consistency { + query predicate nonUniqueSetRepresentation(Splits s1, Splits s2) { + forex(Split s | s = s1.getASplit() | s = s2.getASplit()) and + forex(Split s | s = s2.getASplit() | s = s1.getASplit()) and + s1 != s2 + } + + query predicate breakInvariant2( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, + SplitImpl split, Completion c + ) { + succSplits(pred, predSplits, succ, succSplits, c) and + split = predSplits.getASplit() and + split.hasSuccessor(pred, succ, c) and + not split = succSplits.getASplit() + } + + query predicate breakInvariant3( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, + SplitImpl split, Completion c + ) { + succSplits(pred, predSplits, succ, succSplits, c) and + split = predSplits.getASplit() and + split.hasExit(pred, succ, c) and + not split.hasEntry(pred, succ, c) and + split = succSplits.getASplit() + } + + query predicate breakInvariant4( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, + SplitImpl split, Completion c + ) { + succSplits(pred, predSplits, succ, succSplits, c) and + split.hasEntry(pred, succ, c) and + not split.getKind() = predSplits.getASplit().getKind() and + not split = succSplits.getASplit() + } + + query predicate breakInvariant5( + ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, + SplitImpl split, Completion c + ) { + succSplits(pred, predSplits, succ, succSplits, c) and + split = succSplits.getASplit() and + not (split.hasSuccessor(pred, succ, c) and split = predSplits.getASplit()) and + not split.hasEntry(pred, succ, c) + } + + query predicate multipleSuccessors(Node node, SuccessorType t, Node successor) { + not node instanceof TEntryNode and + strictcount(getASuccessor(node, t)) > 1 and + successor = getASuccessor(node, t) + } +} diff --git a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplSpecific.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplSpecific.qll new file mode 100644 index 00000000000..2d018ff616a --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplSpecific.qll @@ -0,0 +1,74 @@ +private import ruby as rb +private import ControlFlowGraphImpl as Impl +private import Completion as Comp +private import codeql.ruby.ast.internal.Synthesis +private import Splitting as Splitting +private import codeql.ruby.CFG as CFG + +/** The base class for `ControlFlowTree`. */ +class ControlFlowTreeBase extends rb::AstNode { + ControlFlowTreeBase() { not any(Synthesis s).excludeFromControlFlowTree(this) } +} + +class ControlFlowElement = rb::AstNode; + +class Completion = Comp::Completion; + +/** + * Hold if `c` represents normal evaluation of a statement or an + * expression. + */ +predicate completionIsNormal(Completion c) { c instanceof Comp::NormalCompletion } + +/** + * Hold if `c` represents simple (normal) evaluation of a statement or an + * expression. + */ +predicate completionIsSimple(Completion c) { c instanceof Comp::SimpleCompletion } + +/** Holds if `c` is a valid completion for `e`. */ +predicate completionIsValidFor(Completion c, ControlFlowElement e) { c.isValidFor(e) } + +class CfgScope = CFG::CfgScope; + +predicate getCfgScope = Impl::getCfgScope/1; + +/** Holds if `first` is first executed when entering `scope`. */ +predicate scopeFirst(CfgScope scope, ControlFlowElement first) { + scope.(Impl::CfgScope::Range_).entry(first) +} + +/** Holds if `scope` is exited when `last` finishes with completion `c`. */ +predicate scopeLast(CfgScope scope, ControlFlowElement last, Completion c) { + scope.(Impl::CfgScope::Range_).exit(last, c) +} + +/** The maximum number of splits allowed for a given node. */ +int maxSplits() { result = 5 } + +class SplitKindBase = Splitting::TSplitKind; + +class Split = Splitting::Split; + +class SuccessorType = CFG::SuccessorType; + +/** Gets a successor type that matches completion `c`. */ +SuccessorType getAMatchingSuccessorType(Completion c) { result = c.getAMatchingSuccessorType() } + +/** + * Hold if `c` represents simple (normal) evaluation of a statement or an + * expression. + */ +predicate successorTypeIsSimple(SuccessorType t) { + t instanceof CFG::SuccessorTypes::NormalSuccessor +} + +/** Holds if `t` is an abnormal exit type out of a CFG scope. */ +predicate isAbnormalExitType(SuccessorType t) { + t instanceof CFG::SuccessorTypes::RaiseSuccessor or + t instanceof CFG::SuccessorTypes::ExitSuccessor +} + +class Location = rb::Location; + +class Node = CFG::CfgNode; diff --git a/ruby/ql/lib/codeql/ruby/controlflow/internal/NonReturning.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/NonReturning.qll new file mode 100644 index 00000000000..e1927a0b1c9 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/controlflow/internal/NonReturning.qll @@ -0,0 +1,22 @@ +/** Provides a simple analysis for identifying calls that will not return. */ + +private import codeql.ruby.AST +private import Completion + +/** A call that definitely does not return (conservative analysis). */ +abstract class NonReturningCall extends MethodCall { + /** Gets a valid completion for this non-returning call. */ + abstract Completion getACompletion(); +} + +private class RaiseCall extends NonReturningCall { + RaiseCall() { this.getMethodName() = "raise" } + + override RaiseCompletion getACompletion() { not result instanceof NestedCompletion } +} + +private class ExitCall extends NonReturningCall { + ExitCall() { this.getMethodName() in ["abort", "exit"] } + + override ExitCompletion getACompletion() { not result instanceof NestedCompletion } +} diff --git a/ruby/ql/lib/codeql/ruby/controlflow/internal/Splitting.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/Splitting.qll new file mode 100644 index 00000000000..b2068909689 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/controlflow/internal/Splitting.qll @@ -0,0 +1,336 @@ +/** + * Provides classes and predicates relevant for splitting the control flow graph. + */ + +private import codeql.ruby.AST +private import Completion +private import ControlFlowGraphImpl +private import SuccessorTypes +private import codeql.ruby.controlflow.ControlFlowGraph + +cached +private module Cached { + cached + newtype TSplitKind = + TConditionalCompletionSplitKind() { forceCachingInSameStage() } or + TEnsureSplitKind(int nestLevel) { nestLevel = any(Trees::BodyStmtTree t).getNestLevel() } + + cached + newtype TSplit = + TConditionalCompletionSplit(ConditionalCompletion c) or + TEnsureSplit(EnsureSplitting::EnsureSplitType type, int nestLevel) { + nestLevel = any(Trees::BodyStmtTree t).getNestLevel() + } +} + +import Cached + +/** A split for a control flow node. */ +class Split extends TSplit { + /** Gets a textual representation of this split. */ + string toString() { none() } +} + +private module ConditionalCompletionSplitting { + /** + * A split for conditional completions. For example, in + * + * ```rb + * def method x + * if x < 2 and x > 0 + * puts "x is 1" + * end + * end + * ``` + * + * we record whether `x < 2` and `x > 0` evaluate to `true` or `false`, and + * restrict the edges out of `x < 2 and x > 0` accordingly. + */ + class ConditionalCompletionSplit extends Split, TConditionalCompletionSplit { + ConditionalCompletion completion; + + ConditionalCompletionSplit() { this = TConditionalCompletionSplit(completion) } + + override string toString() { result = completion.toString() } + } + + private class ConditionalCompletionSplitKind extends SplitKind, TConditionalCompletionSplitKind { + override int getListOrder() { result = 0 } + + override predicate isEnabled(AstNode n) { this.appliesTo(n) } + + override string toString() { result = "ConditionalCompletion" } + } + + int getNextListOrder() { result = 1 } + + private class ConditionalCompletionSplitImpl extends SplitImpl, ConditionalCompletionSplit { + override ConditionalCompletionSplitKind getKind() { any() } + + override predicate hasEntry(AstNode pred, AstNode succ, Completion c) { + succ(pred, succ, c) and + last(succ, _, completion) and + ( + last(succ.(NotExpr).getOperand(), pred, c) and + completion.(BooleanCompletion).getDual() = c + or + last(succ.(LogicalAndExpr).getAnOperand(), pred, c) and + completion = c + or + last(succ.(LogicalOrExpr).getAnOperand(), pred, c) and + completion = c + or + last(succ.(StmtSequence).getLastStmt(), pred, c) and + completion = c + or + last(succ.(ConditionalExpr).getBranch(_), pred, c) and + completion = c + ) + } + + override predicate hasEntryScope(CfgScope scope, AstNode succ) { none() } + + override predicate hasExit(AstNode pred, AstNode succ, Completion c) { + this.appliesTo(pred) and + succ(pred, succ, c) and + if c instanceof ConditionalCompletion then completion = c else any() + } + + override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) { + this.appliesTo(last) and + succExit(scope, last, c) and + if c instanceof ConditionalCompletion then completion = c else any() + } + + override predicate hasSuccessor(AstNode pred, AstNode succ, Completion c) { none() } + } +} + +module EnsureSplitting { + /** + * The type of a split `ensure` node. + * + * The type represents one of the possible ways of entering an `ensure` + * block. For example, if a block ends with a `return` statement, then + * the `ensure` block must end with a `return` as well (provided that + * the `ensure` block executes normally). + */ + class EnsureSplitType extends SuccessorType { + EnsureSplitType() { not this instanceof ConditionalSuccessor } + + /** Holds if this split type matches entry into an `ensure` block with completion `c`. */ + predicate isSplitForEntryCompletion(Completion c) { + if c instanceof NormalCompletion + then + // If the entry into the `ensure` block completes with any normal completion, + // it simply means normal execution after the `ensure` block + this instanceof NormalSuccessor + else this = c.getAMatchingSuccessorType() + } + } + + /** A node that belongs to an `ensure` block. */ + private class EnsureNode extends AstNode { + private Trees::BodyStmtTree block; + + EnsureNode() { this = block.getAnEnsureDescendant() } + + int getNestLevel() { result = block.getNestLevel() } + + /** Holds if this node is the entry node in the `ensure` block it belongs to. */ + predicate isEntryNode() { first(block.getEnsure(), this) } + } + + /** + * A split for nodes belonging to an `ensure` block, which determines how to + * continue execution after leaving the `ensure` block. For example, in + * + * ```rb + * begin + * if x + * raise "Exception" + * end + * ensure + * puts "Ensure" + * end + * ``` + * + * all control flow nodes in the `ensure` block have two splits: one representing + * normal execution of the body (when `x` evaluates to `true`), and one representing + * exceptional execution of the body (when `x` evaluates to `false`). + */ + class EnsureSplit extends Split, TEnsureSplit { + private EnsureSplitType type; + private int nestLevel; + + EnsureSplit() { this = TEnsureSplit(type, nestLevel) } + + /** + * Gets the type of this `ensure` split, that is, how to continue execution after the + * `ensure` block. + */ + EnsureSplitType getType() { result = type } + + /** Gets the nesting level. */ + int getNestLevel() { result = nestLevel } + + override string toString() { + if type instanceof NormalSuccessor + then result = "" + else + if nestLevel > 0 + then result = "ensure(" + nestLevel + "): " + type.toString() + else result = "ensure: " + type.toString() + } + } + + private int getListOrder(EnsureSplitKind kind) { + result = ConditionalCompletionSplitting::getNextListOrder() + kind.getNestLevel() + } + + int getNextListOrder() { + result = max([getListOrder(_) + 1, ConditionalCompletionSplitting::getNextListOrder()]) + } + + private class EnsureSplitKind extends SplitKind, TEnsureSplitKind { + private int nestLevel; + + EnsureSplitKind() { this = TEnsureSplitKind(nestLevel) } + + /** Gets the nesting level. */ + int getNestLevel() { result = nestLevel } + + override int getListOrder() { result = getListOrder(this) } + + override string toString() { result = "ensure (" + nestLevel + ")" } + } + + pragma[noinline] + private predicate hasEntry0(AstNode pred, EnsureNode succ, int nestLevel, Completion c) { + succ.isEntryNode() and + nestLevel = succ.getNestLevel() and + succ(pred, succ, c) + } + + private class EnsureSplitImpl extends SplitImpl, EnsureSplit { + override EnsureSplitKind getKind() { result.getNestLevel() = this.getNestLevel() } + + override predicate hasEntry(AstNode pred, AstNode succ, Completion c) { + hasEntry0(pred, succ, this.getNestLevel(), c) and + this.getType().isSplitForEntryCompletion(c) + } + + override predicate hasEntryScope(CfgScope scope, AstNode first) { none() } + + /** + * Holds if this split applies to `pred`, where `pred` is a valid predecessor. + */ + private predicate appliesToPredecessor(AstNode pred) { + this.appliesTo(pred) and + (succ(pred, _, _) or succExit(_, pred, _)) + } + + pragma[noinline] + private predicate exit0(AstNode pred, Trees::BodyStmtTree block, int nestLevel, Completion c) { + this.appliesToPredecessor(pred) and + nestLevel = block.getNestLevel() and + block.lastInner(pred, c) + } + + /** + * Holds if `pred` may exit this split with completion `c`. The Boolean + * `inherited` indicates whether `c` is an inherited completion from the + * body. + */ + private predicate exit(Trees::BodyStmtTree block, AstNode pred, Completion c, boolean inherited) { + exists(EnsureSplitType type | + this.exit0(pred, block, this.getNestLevel(), c) and + type = this.getType() + | + if last(block.getEnsure(), pred, c) + then + // `ensure` block can itself exit with completion `c`: either `c` must + // match this split, `c` must be an abnormal completion, or this split + // does not require another completion to be recovered + inherited = false and + ( + type = c.getAMatchingSuccessorType() + or + not c instanceof NormalCompletion + or + type instanceof NormalSuccessor + ) + else ( + // `ensure` block can exit with inherited completion `c`, which must + // match this split + inherited = true and + type = c.getAMatchingSuccessorType() and + not type instanceof NormalSuccessor + ) + ) + or + // If this split is normal, and an outer split can exit based on an inherited + // completion, we need to exit this split as well. For example, in + // + // ```rb + // def m(b1, b2) + // if b1 + // return + // end + // ensure + // begin + // if b2 + // raise "Exception" + // end + // ensure + // puts "inner ensure" + // end + // end + // ``` + // + // if the outer split for `puts "inner ensure"` is `return` and the inner split + // is "normal" (corresponding to `b1 = true` and `b2 = false`), then the inner + // split must be able to exit with a `return` completion. + this.appliesToPredecessor(pred) and + exists(EnsureSplitImpl outer | + outer.getNestLevel() = this.getNestLevel() - 1 and + outer.exit(_, pred, c, inherited) and + this.getType() instanceof NormalSuccessor and + inherited = true + ) + } + + override predicate hasExit(AstNode pred, AstNode succ, Completion c) { + succ(pred, succ, c) and + ( + this.exit(_, pred, c, _) + or + this.exit(_, pred, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion(), _) + ) + } + + override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) { + succExit(scope, last, c) and + ( + this.exit(_, last, c, _) + or + this.exit(_, last, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion(), _) + ) + } + + override predicate hasSuccessor(AstNode pred, AstNode succ, Completion c) { + this.appliesToPredecessor(pred) and + succ(pred, succ, c) and + succ = + any(EnsureNode en | + if en.isEntryNode() + then + // entering a nested `ensure` block + en.getNestLevel() > this.getNestLevel() + else + // staying in the same (possibly nested) `ensure` block as `pred` + en.getNestLevel() >= this.getNestLevel() + ) + } + } +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll new file mode 100644 index 00000000000..0c0ca749eac --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll @@ -0,0 +1,75 @@ +/** Provides commonly used barriers to dataflow. */ + +private import ruby +private import codeql.ruby.DataFlow +private import codeql.ruby.CFG + +/** + * A validation of value by comparing with a constant string value, for example + * in: + * + * ```rb + * dir = params[:order] + * dir = "DESC" unless dir == "ASC" + * User.order("name #{dir}") + * ``` + * + * the equality operation guards against `dir` taking arbitrary values when used + * in the `order` call. + */ +class StringConstCompare extends DataFlow::BarrierGuard, + CfgNodes::ExprNodes::ComparisonOperationCfgNode { + private CfgNode checkedNode; + // The value of the condition that results in the node being validated. + private boolean checkedBranch; + + StringConstCompare() { + exists(CfgNodes::ExprNodes::StringLiteralCfgNode strLitNode | + this.getExpr() instanceof EqExpr and checkedBranch = true + or + this.getExpr() instanceof CaseEqExpr and checkedBranch = true + or + this.getExpr() instanceof NEExpr and checkedBranch = false + | + this.getLeftOperand() = strLitNode and this.getRightOperand() = checkedNode + or + this.getLeftOperand() = checkedNode and this.getRightOperand() = strLitNode + ) + } + + override predicate checks(CfgNode expr, boolean branch) { + expr = checkedNode and branch = checkedBranch + } +} + +/** + * A validation of a value by checking for inclusion in an array of string + * literal values, for example in: + * + * ```rb + * name = params[:user_name] + * if %w(alice bob charlie).include? name + * User.find_by("username = #{name}") + * end + * ``` + * + * the `include?` call guards against `name` taking arbitrary values when used + * in the `find_by` call. + */ +// +class StringConstArrayInclusionCall extends DataFlow::BarrierGuard, + CfgNodes::ExprNodes::MethodCallCfgNode { + private CfgNode checkedNode; + + StringConstArrayInclusionCall() { + exists(ArrayLiteral aLit | + this.getExpr().getMethodName() = "include?" and + this.getExpr().getReceiver() = aLit + | + forall(Expr elem | elem = aLit.getAnElement() | elem instanceof StringLiteral) and + this.getArgument(0) = checkedNode + ) + } + + override predicate checks(CfgNode expr, boolean branch) { expr = checkedNode and branch = true } +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/FlowSummary.qll b/ruby/ql/lib/codeql/ruby/dataflow/FlowSummary.qll new file mode 100644 index 00000000000..f0ccb42c6e0 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/FlowSummary.qll @@ -0,0 +1,127 @@ +/** Provides classes and predicates for defining flow summaries. */ + +import ruby +import codeql.ruby.DataFlow +private import internal.FlowSummaryImpl as Impl +private import internal.DataFlowDispatch + +// import all instances below +private module Summaries { + private import codeql.ruby.Frameworks +} + +class SummaryComponent = Impl::Public::SummaryComponent; + +/** Provides predicates for constructing summary components. */ +module SummaryComponent { + private import Impl::Public::SummaryComponent as SC + + predicate parameter = SC::parameter/1; + + predicate argument = SC::argument/1; + + predicate content = SC::content/1; + + /** Gets a summary component that represents a qualifier. */ + SummaryComponent qualifier() { result = argument(-1) } + + /** Gets a summary component that represents a block argument. */ + SummaryComponent block() { result = argument(-2) } + + /** Gets a summary component that represents the return value of a call. */ + SummaryComponent return() { result = SC::return(any(NormalReturnKind rk)) } +} + +class SummaryComponentStack = Impl::Public::SummaryComponentStack; + +/** Provides predicates for constructing stacks of summary components. */ +module SummaryComponentStack { + private import Impl::Public::SummaryComponentStack as SCS + + predicate singleton = SCS::singleton/1; + + predicate push = SCS::push/2; + + predicate argument = SCS::argument/1; + + /** Gets a singleton stack representing a qualifier. */ + SummaryComponentStack qualifier() { result = singleton(SummaryComponent::qualifier()) } + + /** Gets a singleton stack representing a block argument. */ + SummaryComponentStack block() { result = singleton(SummaryComponent::block()) } + + /** Gets a singleton stack representing the return value of a call. */ + SummaryComponentStack return() { result = singleton(SummaryComponent::return()) } +} + +/** A callable with a flow summary, identified by a unique string. */ +abstract class SummarizedCallable extends LibraryCallable { + bindingset[this] + SummarizedCallable() { any() } + + /** + * Holds if data may flow from `input` to `output` through this callable. + * + * `preservesValue` indicates whether this is a value-preserving step + * or a taint-step. + * + * Input specifications are restricted to stacks that end with + * `SummaryComponent::argument(_)`, preceded by zero or more + * `SummaryComponent::return()` or `SummaryComponent::content(_)` components. + * + * Output specifications are restricted to stacks that end with + * `SummaryComponent::return()` or `SummaryComponent::argument(_)`. + * + * Output stacks ending with `SummaryComponent::return()` can be preceded by zero + * or more `SummaryComponent::content(_)` components. + * + * Output stacks ending with `SummaryComponent::argument(_)` can be preceded by an + * optional `SummaryComponent::parameter(_)` component, which in turn can be preceded + * by zero or more `SummaryComponent::content(_)` components. + */ + pragma[nomagic] + predicate propagatesFlow( + SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue + ) { + none() + } + + /** + * Same as + * + * ```ql + * propagatesFlow( + * SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue + * ) + * ``` + * + * but uses an external (string) representation of the input and output stacks. + */ + pragma[nomagic] + predicate propagatesFlowExt(string input, string output, boolean preservesValue) { none() } + + /** + * Holds if values stored inside `content` are cleared on objects passed as + * the `i`th argument to this callable. + */ + pragma[nomagic] + predicate clearsContent(int i, DataFlow::Content content) { none() } +} + +private class SummarizedCallableAdapter extends Impl::Public::SummarizedCallable { + private SummarizedCallable sc; + + SummarizedCallableAdapter() { this = TLibraryCallable(sc) } + + final override predicate propagatesFlow( + SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue + ) { + sc.propagatesFlow(input, output, preservesValue) + } + + final override predicate clearsContent(int i, DataFlow::Content content) { + sc.clearsContent(i, content) + } +} + +class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack; diff --git a/ruby/ql/lib/codeql/ruby/dataflow/RemoteFlowSources.qll b/ruby/ql/lib/codeql/ruby/dataflow/RemoteFlowSources.qll new file mode 100644 index 00000000000..617bfd8678e --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/RemoteFlowSources.qll @@ -0,0 +1,37 @@ +/** + * Provides an extension point for for modeling user-controlled data. + * Such data is often used as data-flow sources in security queries. + */ + +private import codeql.ruby.dataflow.internal.DataFlowPublic as DataFlow +// Need to import since frameworks can extend `RemoteFlowSource::Range` +private import codeql.ruby.Frameworks + +/** + * A data flow source of remote user input. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `RemoteFlowSource::Range` instead. + */ +class RemoteFlowSource extends DataFlow::Node { + RemoteFlowSource::Range self; + + RemoteFlowSource() { this = self } + + /** Gets a string that describes the type of this remote flow source. */ + string getSourceType() { result = self.getSourceType() } +} + +/** Provides a class for modeling new sources of remote user input. */ +module RemoteFlowSource { + /** + * A data flow source of remote user input. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `RemoteFlowSource` instead. + */ + abstract class Range extends DataFlow::Node { + /** Gets a string that describes the type of this remote flow source. */ + abstract string getSourceType(); + } +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/SSA.qll b/ruby/ql/lib/codeql/ruby/dataflow/SSA.qll new file mode 100644 index 00000000000..a5fb21c1d93 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/SSA.qll @@ -0,0 +1,403 @@ +/** + * Provides the module `Ssa` for working with static single assignment (SSA) form. + */ + +/** + * Provides classes for working with static single assignment (SSA) form. + */ +module Ssa { + private import codeql.Locations + private import codeql.ruby.CFG + private import codeql.ruby.ast.Variable + private import internal.SsaImplCommon as SsaImplCommon + private import internal.SsaImpl as SsaImpl + private import CfgNodes::ExprNodes + + /** A static single assignment (SSA) definition. */ + class Definition extends SsaImplCommon::Definition { + /** + * Gets the control flow node of this SSA definition, if any. Phi nodes are + * examples of SSA definitions without a control flow node, as they are + * modelled at index `-1` in the relevant basic block. + */ + final CfgNode getControlFlowNode() { + exists(BasicBlock bb, int i | this.definesAt(_, bb, i) | result = bb.getNode(i)) + } + + /** + * Gets a control-flow node that reads the value of this SSA definition. + * + * Example: + * + * ```rb + * def m b # defines b_0 + * i = 0 # defines i_0 + * puts i # reads i_0 + * puts i + 1 # reads i_0 + * if b # reads b_0 + * i = 1 # defines i_1 + * puts i # reads i_1 + * puts i + 1 # reads i_1 + * else + * i = 2 # defines i_2 + * puts i # reads i_2 + * puts i + 1 # reads i_2 + * end + * # defines i_3 = phi(i_1, i_2) + * puts i # reads i3 + * end + * ``` + */ + final VariableReadAccessCfgNode getARead() { result = SsaImpl::getARead(this) } + + /** + * Gets a first control-flow node that reads the value of this SSA definition. + * That is, a read that can be reached from this definition without passing + * through other reads. + * + * Example: + * + * ```rb + * def m b # defines b_0 + * i = 0 # defines i_0 + * puts i # first read of i_0 + * puts i + 1 + * if b # first read of b_0 + * i = 1 # defines i_1 + * puts i # first read of i_1 + * puts i + 1 + * else + * i = 2 # defines i_2 + * puts i # first read of i_2 + * puts i + 1 + * end + * # defines i_3 = phi(i_1, i_2) + * puts i # first read of i3 + * end + * ``` + */ + final VariableReadAccessCfgNode getAFirstRead() { SsaImpl::firstRead(this, result) } + + /** + * Gets a last control-flow node that reads the value of this SSA definition. + * That is, a read that can reach the end of the enclosing CFG scope, or another + * SSA definition for the source variable, without passing through any other read. + * + * Example: + * + * ```rb + * def m b # defines b_0 + * i = 0 # defines i_0 + * puts i + * puts i + 1 # last read of i_0 + * if b # last read of b_0 + * i = 1 # defines i_1 + * puts i + * puts i + 1 # last read of i_1 + * else + * i = 2 # defines i_2 + * puts i + * puts i + 1 # last read of i_2 + * end + * # defines i_3 = phi(i_1, i_2) + * puts i # last read of i3 + * end + * ``` + */ + final VariableReadAccessCfgNode getALastRead() { SsaImpl::lastRead(this, result) } + + /** + * Holds if `read1` and `read2` are adjacent reads of this SSA definition. + * That is, `read2` can be reached from `read1` without passing through + * another read. + * + * Example: + * + * ```rb + * def m b + * i = 0 # defines i_0 + * puts i # reads i_0 (read1) + * puts i + 1 # reads i_0 (read2) + * if b + * i = 1 # defines i_1 + * puts i # reads i_1 (read1) + * puts i + 1 # reads i_1 (read2) + * else + * i = 2 # defines i_2 + * puts i # reads i_2 (read1) + * puts i + 1 # reads i_2 (read2) + * end + * puts i + * end + * ``` + */ + final predicate hasAdjacentReads( + VariableReadAccessCfgNode read1, VariableReadAccessCfgNode read2 + ) { + SsaImpl::adjacentReadPair(this, read1, read2) + } + + /** + * Gets an SSA definition whose value can flow to this one in one step. This + * includes inputs to phi nodes and the prior definitions of uncertain writes. + */ + private Definition getAPhiInputOrPriorDefinition() { + result = this.(PhiNode).getAnInput() or + result = this.(CapturedCallDefinition).getPriorDefinition() + } + + /** + * Gets a definition that ultimately defines this SSA definition and is + * not itself a phi node. + * + * Example: + * + * ```rb + * def m b + * i = 0 # defines i_0 + * puts i + * puts i + 1 + * if b + * i = 1 # defines i_1 + * puts i + * puts i + 1 + * else + * i = 2 # defines i_2 + * puts i + * puts i + 1 + * end + * # defines i_3 = phi(i_1, i_2); ultimate definitions are i_1 and i_2 + * puts i + * end + * ``` + */ + final Definition getAnUltimateDefinition() { + result = this.getAPhiInputOrPriorDefinition*() and + not result instanceof PhiNode + } + + override string toString() { result = this.getControlFlowNode().toString() } + + /** Gets the location of this SSA definition. */ + Location getLocation() { result = this.getControlFlowNode().getLocation() } + } + + /** + * An SSA definition that corresponds to a write. For example `x = 10` in + * + * ```rb + * x = 10 + * puts x + * ``` + */ + class WriteDefinition extends Definition, SsaImplCommon::WriteDefinition { + private VariableWriteAccess write; + + WriteDefinition() { + exists(BasicBlock bb, int i, Variable v | + this.definesAt(v, bb, i) and + SsaImpl::variableWriteActual(bb, i, v, write) + ) + } + + /** Gets the underlying write access. */ + final VariableWriteAccess getWriteAccess() { result = write } + + /** + * Holds if this SSA definition represents a direct assignment of `value` + * to the underlying variable. + */ + predicate assigns(CfgNodes::ExprCfgNode value) { + exists(CfgNodes::ExprNodes::AssignExprCfgNode a, BasicBlock bb, int i | + this.definesAt(_, bb, i) and + a = bb.getNode(i) and + value = a.getRhs() + ) + } + + final override string toString() { result = Definition.super.toString() } + + final override Location getLocation() { result = this.getControlFlowNode().getLocation() } + } + + /** + * An SSA definition that corresponds to the value of `self` upon entry to a method, class or module. + */ + class SelfDefinition extends Definition, SsaImplCommon::WriteDefinition { + private SelfVariable v; + + SelfDefinition() { + exists(BasicBlock bb, int i | + this.definesAt(v, bb, i) and + not SsaImpl::capturedEntryWrite(bb, i, v) + ) + } + + final override string toString() { result = "self (" + v.getDeclaringScope() + ")" } + + final override Location getLocation() { result = this.getControlFlowNode().getLocation() } + } + + /** + * An SSA definition inserted at the beginning of a scope to represent an + * uninitialized local variable. For example, in + * + * ```rb + * def m + * x = 10 if b + * puts x + * end + * ``` + * + * since the assignment to `x` is conditional, an unitialized definition for + * `x` is inserted at the start of `m`. + */ + class UninitializedDefinition extends Definition, SsaImplCommon::WriteDefinition { + UninitializedDefinition() { + exists(BasicBlock bb, int i, Variable v | + this.definesAt(v, bb, i) and + SsaImpl::uninitializedWrite(bb, i, v) + ) + } + + final override string toString() { result = "<uninitialized>" } + + final override Location getLocation() { result = this.getBasicBlock().getLocation() } + } + + /** + * An SSA definition inserted at the beginning of a scope to represent a + * captured local variable. For example, in + * + * ```rb + * def m x + * y = 0 + * x.times do |x| + * y += x + * end + * return y + * end + * ``` + * + * an entry definition for `y` is inserted at the start of the `do` block. + */ + class CapturedEntryDefinition extends Definition, SsaImplCommon::WriteDefinition { + CapturedEntryDefinition() { + exists(BasicBlock bb, int i, Variable v | + this.definesAt(v, bb, i) and + SsaImpl::capturedEntryWrite(bb, i, v) + ) + } + + final override string toString() { result = "<captured>" } + + override Location getLocation() { result = this.getBasicBlock().getLocation() } + } + + /** + * An SSA definition inserted at a call that may update the value of a captured + * variable. For example, in + * + * ```rb + * def m x + * y = 0 + * x.times do |x| + * y += x + * end + * return y + * end + * ``` + * + * a definition for `y` is inserted at the call to `times`. + */ + class CapturedCallDefinition extends Definition, SsaImplCommon::UncertainWriteDefinition { + CapturedCallDefinition() { + exists(Variable v, BasicBlock bb, int i | + this.definesAt(v, bb, i) and + SsaImpl::capturedCallWrite(bb, i, v) + ) + } + + /** + * Gets the immediately preceding definition. Since this update is uncertain, + * the value from the preceding definition might still be valid. + */ + final Definition getPriorDefinition() { result = SsaImpl::uncertainWriteDefinitionInput(this) } + + override string toString() { result = this.getControlFlowNode().toString() } + } + + /** + * A phi node. For example, in + * + * ```rb + * if b + * x = 0 + * else + * x = 1 + * end + * puts x + * ``` + * + * a phi node for `x` is inserted just before the call `puts x`. + */ + class PhiNode extends Definition, SsaImplCommon::PhiNode { + /** + * Gets an input of this phi node. + * + * Example: + * + * ```rb + * def m b + * i = 0 # defines i_0 + * puts i + * puts i + 1 + * if b + * i = 1 # defines i_1 + * puts i + * puts i + 1 + * else + * i = 2 # defines i_2 + * puts i + * puts i + 1 + * end + * # defines i_3 = phi(i_1, i_2); inputs are i_1 and i_2 + * puts i + * end + * ``` + */ + final Definition getAnInput() { this.hasInputFromBlock(result, _) } + + /** Holds if `inp` is an input to this phi node along the edge originating in `bb`. */ + predicate hasInputFromBlock(Definition inp, BasicBlock bb) { + inp = SsaImpl::phiHasInputFromBlock(this, bb) + } + + private string getSplitString() { + result = this.getBasicBlock().getFirstNode().(CfgNodes::AstCfgNode).getSplitsString() + } + + override string toString() { + exists(string prefix | + prefix = "[" + this.getSplitString() + "] " + or + not exists(this.getSplitString()) and + prefix = "" + | + result = prefix + "phi" + ) + } + + /* + * The location of a phi node is the same as the location of the first node + * in the basic block in which it is defined. + * + * Strictly speaking, the node is *before* the first node, but such a location + * does not exist in the source program. + */ + + final override Location getLocation() { + result = this.getBasicBlock().getFirstNode().getLocation() + } + } +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll new file mode 100644 index 00000000000..d3cddf8a3a0 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll @@ -0,0 +1,459 @@ +private import ruby +private import codeql.ruby.CFG +private import DataFlowPrivate +private import codeql.ruby.typetracking.TypeTracker +private import codeql.ruby.ast.internal.Module +private import FlowSummaryImpl as FlowSummaryImpl +private import codeql.ruby.dataflow.FlowSummary + +newtype TReturnKind = + TNormalReturnKind() or + TBreakReturnKind() + +/** + * Gets a node that can read the value returned from `call` with return kind + * `kind`. + */ +OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { call = result.getCall(kind) } + +/** + * A return kind. A return kind describes how a value can be returned + * from a callable. + */ +abstract class ReturnKind extends TReturnKind { + /** Gets a textual representation of this position. */ + abstract string toString(); +} + +/** + * A value returned from a callable using a `return` statement or an expression + * body, that is, a "normal" return. + */ +class NormalReturnKind extends ReturnKind, TNormalReturnKind { + override string toString() { result = "return" } +} + +/** + * A value returned from a callable using a `break` statement. + */ +class BreakReturnKind extends ReturnKind, TBreakReturnKind { + override string toString() { result = "break" } +} + +/** A callable defined in library code, identified by a unique string. */ +abstract class LibraryCallable extends string { + bindingset[this] + LibraryCallable() { any() } + + /** Gets a call to this library callable. */ + abstract Call getACall(); +} + +/** + * A callable. This includes callables from source code, as well as callables + * defined in library code. + */ +class DataFlowCallable extends TDataFlowCallable { + /** Gets the underlying source code callable, if any. */ + Callable asCallable() { this = TCfgScope(result) } + + /** Gets the underlying library callable, if any. */ + LibraryCallable asLibraryCallable() { this = TLibraryCallable(result) } + + /** Gets a textual representation of this callable. */ + string toString() { result = [this.asCallable().toString(), this.asLibraryCallable()] } + + /** Gets the location of this callable. */ + Location getLocation() { result = this.asCallable().getLocation() } +} + +/** + * A call. This includes calls from source code, as well as call(back)s + * inside library callables with a flow summary. + */ +class DataFlowCall extends TDataFlowCall { + /** Gets the enclosing callable. */ + DataFlowCallable getEnclosingCallable() { none() } + + /** Gets the underlying source code call, if any. */ + CfgNodes::ExprNodes::CallCfgNode asCall() { none() } + + /** Gets a textual representation of this call. */ + string toString() { none() } + + /** Gets the location of this call. */ + Location getLocation() { none() } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +/** + * A synthesized call inside a callable with a flow summary. + * + * For example, in + * ```rb + * ints.each do |i| + * puts i + * end + * ``` + * + * there is a call to the block argument inside `each`. + */ +class SummaryCall extends DataFlowCall, TSummaryCall { + private FlowSummaryImpl::Public::SummarizedCallable c; + private DataFlow::Node receiver; + + SummaryCall() { this = TSummaryCall(c, receiver) } + + /** Gets the data flow node that this call targets. */ + DataFlow::Node getReceiver() { result = receiver } + + override DataFlowCallable getEnclosingCallable() { result = c } + + override string toString() { result = "[summary] call to " + receiver + " in " + c } + + override Location getLocation() { result = c.getLocation() } +} + +private class NormalCall extends DataFlowCall, TNormalCall { + private CfgNodes::ExprNodes::CallCfgNode c; + + NormalCall() { this = TNormalCall(c) } + + override CfgNodes::ExprNodes::CallCfgNode asCall() { result = c } + + override DataFlowCallable getEnclosingCallable() { result = TCfgScope(c.getScope()) } + + override string toString() { result = c.toString() } + + override Location getLocation() { result = c.getLocation() } +} + +pragma[nomagic] +private predicate methodCall( + CfgNodes::ExprNodes::CallCfgNode call, DataFlow::LocalSourceNode sourceNode, string method +) { + exists(DataFlow::Node nodeTo | + method = call.getExpr().(MethodCall).getMethodName() and + nodeTo.asExpr() = call.getReceiver() and + sourceNode.flowsTo(nodeTo) + ) +} + +private Block yieldCall(CfgNodes::ExprNodes::CallCfgNode call) { + call.getExpr() instanceof YieldCall and + exists(BlockParameterNode node | + node = trackBlock(result) and + node.getMethod() = call.getExpr().getEnclosingMethod() + ) +} + +pragma[nomagic] +private predicate superCall(CfgNodes::ExprNodes::CallCfgNode call, Module superClass, string method) { + call.getExpr() instanceof SuperCall and + exists(Module tp | + tp = call.getExpr().getEnclosingModule().getModule() and + superClass = tp.getSuperClass() and + method = call.getExpr().getEnclosingMethod().getName() + ) +} + +pragma[nomagic] +private predicate instanceMethodCall(CfgNodes::ExprNodes::CallCfgNode call, Module tp, string method) { + exists(DataFlow::LocalSourceNode sourceNode | + methodCall(call, sourceNode, method) and + sourceNode = trackInstance(tp) + ) +} + +cached +private module Cached { + cached + newtype TDataFlowCallable = + TCfgScope(CfgScope scope) or + TLibraryCallable(LibraryCallable callable) + + cached + newtype TDataFlowCall = + TNormalCall(CfgNodes::ExprNodes::CallCfgNode c) or + TSummaryCall(FlowSummaryImpl::Public::SummarizedCallable c, DataFlow::Node receiver) { + FlowSummaryImpl::Private::summaryCallbackRange(c, receiver) + } + + cached + CfgScope getTarget(CfgNodes::ExprNodes::CallCfgNode call) { + // Temporarily disable operation resolution (due to bad performance) + not call.getExpr() instanceof Operation and + ( + exists(string method | + exists(Module tp | + instanceMethodCall(call, tp, method) and + result = lookupMethod(tp, method) and + if result.(Method).isPrivate() + then + exists(Self self | + self = call.getReceiver().getExpr() and + pragma[only_bind_out](self.getEnclosingModule().getModule().getSuperClass*()) = + pragma[only_bind_out](result.getEnclosingModule().getModule()) + ) and + // For now, we restrict the scope of top-level declarations to their file. + // This may remove some plausible targets, but also removes a lot of + // implausible targets + if result.getEnclosingModule() instanceof Toplevel + then result.getFile() = call.getFile() + else any() + else any() + ) + or + exists(DataFlow::LocalSourceNode sourceNode | + methodCall(call, sourceNode, method) and + sourceNode = trackSingletonMethod(result, method) + ) + ) + or + exists(Module superClass, string method | + superCall(call, superClass, method) and + result = lookupMethod(superClass, method) + ) + or + result = yieldCall(call) + ) + } +} + +import Cached + +private DataFlow::LocalSourceNode trackInstance(Module tp, TypeTracker t) { + t.start() and + ( + result.asExpr().getExpr() instanceof NilLiteral and tp = TResolved("NilClass") + or + result.asExpr().getExpr().(BooleanLiteral).isFalse() and tp = TResolved("FalseClass") + or + result.asExpr().getExpr().(BooleanLiteral).isTrue() and tp = TResolved("TrueClass") + or + result.asExpr().getExpr() instanceof IntegerLiteral and tp = TResolved("Integer") + or + result.asExpr().getExpr() instanceof FloatLiteral and tp = TResolved("Float") + or + result.asExpr().getExpr() instanceof RationalLiteral and tp = TResolved("Rational") + or + result.asExpr().getExpr() instanceof ComplexLiteral and tp = TResolved("Complex") + or + result.asExpr().getExpr() instanceof StringlikeLiteral and tp = TResolved("String") + or + exists(ConstantReadAccess array, MethodCall mc | + result.asExpr().getExpr() = mc and + mc.getMethodName() = "[]" and + mc.getReceiver() = array and + array.getName() = "Array" and + array.hasGlobalScope() and + tp = TResolved("Array") + ) + or + result.asExpr().getExpr() instanceof HashLiteral and tp = TResolved("Hash") + or + result.asExpr().getExpr() instanceof MethodBase and tp = TResolved("Symbol") + or + result.asParameter() instanceof BlockParameter and tp = TResolved("Proc") + or + result.asExpr().getExpr() instanceof Lambda and tp = TResolved("Proc") + or + exists(CfgNodes::ExprNodes::CallCfgNode call, DataFlow::Node nodeTo | + call.getExpr().(MethodCall).getMethodName() = "new" and + nodeTo.asExpr() = call.getReceiver() and + trackModule(tp).flowsTo(nodeTo) and + result.asExpr() = call + ) + or + // `self` in method + exists(Self self, Method enclosing | + self = result.asExpr().getExpr() and + enclosing = self.getEnclosingMethod() and + tp = enclosing.getEnclosingModule().getModule() and + not self.getEnclosingModule().getEnclosingMethod() = enclosing + ) + or + // `self` in singleton method + exists(Self self, MethodBase enclosing | + self = result.asExpr().getExpr() and + flowsToSingletonMethodObject(trackInstance(tp), enclosing) and + enclosing = self.getEnclosingMethod() and + not self.getEnclosingModule().getEnclosingMethod() = enclosing + ) + or + // `self` in top-level + exists(Self self, Toplevel enclosing | + self = result.asExpr().getExpr() and + enclosing = self.getEnclosingModule() and + tp = TResolved("Object") and + not self.getEnclosingMethod().getEnclosingModule() = enclosing + ) + or + // a module or class + exists(Module m | + result = trackModule(m) and + if m.isClass() then tp = TResolved("Class") else tp = TResolved("Module") + ) + ) + or + exists(TypeTracker t2, StepSummary summary | + result = trackInstanceRec(tp, t2, summary) and t = t2.append(summary) + ) +} + +pragma[nomagic] +private DataFlow::LocalSourceNode trackInstanceRec(Module tp, TypeTracker t, StepSummary summary) { + StepSummary::step(trackInstance(tp, t), result, summary) +} + +private DataFlow::LocalSourceNode trackInstance(Module tp) { + result = trackInstance(tp, TypeTracker::end()) +} + +private DataFlow::LocalSourceNode trackBlock(Block block, TypeTracker t) { + t.start() and result.asExpr().getExpr() = block + or + exists(TypeTracker t2, StepSummary summary | + result = trackBlockRec(block, t2, summary) and t = t2.append(summary) + ) +} + +pragma[nomagic] +private DataFlow::LocalSourceNode trackBlockRec(Block block, TypeTracker t, StepSummary summary) { + StepSummary::step(trackBlock(block, t), result, summary) +} + +private DataFlow::LocalSourceNode trackBlock(Block block) { + result = trackBlock(block, TypeTracker::end()) +} + +private predicate singletonMethod(MethodBase method, Expr object) { + object = method.(SingletonMethod).getObject() + or + exists(SingletonClass cls | + object = cls.getValue() and method instanceof Method and method = cls.getAMethod() + ) +} + +pragma[nomagic] +private predicate flowsToSingletonMethodObject(DataFlow::LocalSourceNode nodeFrom, MethodBase method) { + exists(DataFlow::LocalSourceNode nodeTo | + nodeFrom.flowsTo(nodeTo) and + singletonMethod(method, nodeTo.asExpr().getExpr()) + ) +} + +pragma[nomagic] +private predicate moduleFlowsToSingletonMethodObject(Module m, MethodBase method) { + flowsToSingletonMethodObject(trackModule(m), method) +} + +pragma[nomagic] +private DataFlow::LocalSourceNode trackSingletonMethod0(MethodBase method, TypeTracker t) { + t.start() and + ( + flowsToSingletonMethodObject(result, method) + or + exists(Module m | result = trackModule(m) and moduleFlowsToSingletonMethodObject(m, method)) + ) + or + exists(TypeTracker t2, StepSummary summary | + result = trackSingletonMethod0Rec(method, t2, summary) and t = t2.append(summary) + ) +} + +pragma[nomagic] +private DataFlow::LocalSourceNode trackSingletonMethod0Rec( + MethodBase method, TypeTracker t, StepSummary summary +) { + StepSummary::step(trackSingletonMethod0(method, t), result, summary) +} + +pragma[nomagic] +private DataFlow::LocalSourceNode trackSingletonMethod(MethodBase m, string name) { + result = trackSingletonMethod0(m, TypeTracker::end()) and + name = m.getName() +} + +private DataFlow::Node selfInModule(Module tp) { + exists(Self self, ModuleBase enclosing | + self = result.asExpr().getExpr() and + enclosing = self.getEnclosingModule() and + tp = enclosing.getModule() and + not self.getEnclosingMethod().getEnclosingModule() = enclosing + ) +} + +private DataFlow::LocalSourceNode trackModule(Module tp, TypeTracker t) { + t.start() and + ( + // ConstantReadAccess to Module + resolveScopeExpr(result.asExpr().getExpr()) = tp + or + // `self` reference to Module + result = selfInModule(tp) + ) + or + exists(TypeTracker t2, StepSummary summary | + result = trackModuleRec(tp, t2, summary) and t = t2.append(summary) + ) +} + +pragma[nomagic] +private DataFlow::LocalSourceNode trackModuleRec(Module tp, TypeTracker t, StepSummary summary) { + StepSummary::step(trackModule(tp, t), result, summary) +} + +private DataFlow::LocalSourceNode trackModule(Module tp) { + result = trackModule(tp, TypeTracker::end()) +} + +/** Gets a viable run-time target for the call `call`. */ +DataFlowCallable viableCallable(DataFlowCall call) { + result = TCfgScope(getTarget(call.asCall())) and + not call.asCall().getExpr() instanceof YieldCall // handled by `lambdaCreation`/`lambdaCall` + or + exists(LibraryCallable callable | + result = TLibraryCallable(callable) and + call.asCall().getExpr() = callable.getACall() + ) +} + +/** + * Holds if the set of viable implementations that can be called by `call` + * might be improved by knowing the call context. This is the case if the + * qualifier accesses a parameter of the enclosing callable `c` (including + * the implicit `self` parameter). + */ +predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c) { none() } + +/** + * Gets a viable dispatch target of `call` in the context `ctx`. This is + * restricted to those `call`s for which a context might make a difference. + */ +DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { none() } + +/** + * Holds if `e` is an `ExprNode` that may be returned by a call to `c`. + */ +predicate exprNodeReturnedFrom(DataFlow::ExprNode e, Callable c) { + exists(ReturningNode r | + r.getEnclosingCallable().asCallable() = c and + ( + r.(ExplicitReturnNode).getReturningNode().getReturnedValueNode() = e.asExpr() or + r.(ExprReturnNode) = e + ) + ) +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll new file mode 100644 index 00000000000..b3d03ea4e26 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll @@ -0,0 +1,4592 @@ +/** + * Provides an implementation of global (interprocedural) data flow. This file + * re-exports the local (intraprocedural) data flow analysis from + * `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed + * through the `Configuration` class. This file exists in several identical + * copies, allowing queries to use multiple `Configuration` classes that depend + * on each other without introducing mutual recursion among those configurations. + */ + +private import DataFlowImplCommon +private import DataFlowImplSpecific::Private +import DataFlowImplSpecific::Public + +/** + * A configuration of interprocedural data flow analysis. This defines + * sources, sinks, and any other configurable aspect of the analysis. Each + * use of the global data flow library must define its own unique extension + * of this abstract class. To create a configuration, extend this class with + * a subclass whose characteristic predicate is a unique singleton string. + * For example, write + * + * ```ql + * class MyAnalysisConfiguration extends DataFlow::Configuration { + * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } + * // Override `isSource` and `isSink`. + * // Optionally override `isBarrier`. + * // Optionally override `isAdditionalFlowStep`. + * } + * ``` + * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and + * the edges are those data-flow steps that preserve the value of the node + * along with any additional edges defined by `isAdditionalFlowStep`. + * Specifying nodes in `isBarrier` will remove those nodes from the graph, and + * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going + * and/or out-going edges from those nodes, respectively. + * + * Then, to query whether there is flow between some `source` and `sink`, + * write + * + * ```ql + * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) + * ``` + * + * Multiple configurations can coexist, but two classes extending + * `DataFlow::Configuration` should never depend on each other. One of them + * should instead depend on a `DataFlow2::Configuration`, a + * `DataFlow3::Configuration`, or a `DataFlow4::Configuration`. + */ +abstract class Configuration extends string { + bindingset[this] + Configuration() { any() } + + /** + * Holds if `source` is a relevant data flow source. + */ + abstract predicate isSource(Node source); + + /** + * Holds if `sink` is a relevant data flow sink. + */ + abstract predicate isSink(Node sink); + + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ + predicate isBarrier(Node node) { none() } + + /** Holds if data flow into `node` is prohibited. */ + predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isBarrierOut(Node node) { none() } + + /** Holds if data flow through nodes guarded by `guard` is prohibited. */ + predicate isBarrierGuard(BarrierGuard guard) { none() } + + /** + * Holds if the additional flow step from `node1` to `node2` must be taken + * into account in the analysis. + */ + predicate isAdditionalFlowStep(Node node1, Node node2) { none() } + + /** + * Holds if an arbitrary number of implicit read steps of content `c` may be + * taken at `node`. + */ + predicate allowImplicitRead(Node node, Content c) { none() } + + /** + * Gets the virtual dispatch branching limit when calculating field flow. + * This can be overridden to a smaller value to improve performance (a + * value of 0 disables field flow), or a larger value to get more results. + */ + int fieldFlowBranchLimit() { result = 2 } + + /** + * Holds if data may flow from `source` to `sink` for this configuration. + */ + predicate hasFlow(Node source, Node sink) { flowsTo(source, sink, this) } + + /** + * Holds if data may flow from `source` to `sink` for this configuration. + * + * The corresponding paths are generated from the end-points and the graph + * included in the module `PathGraph`. + */ + predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + + /** + * Holds if data may flow from some source to `sink` for this configuration. + */ + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } + + /** + * Holds if data may flow from some source to `sink` for this configuration. + */ + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } + + /** + * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` + * measured in approximate number of interprocedural steps. + */ + int explorationLimit() { none() } + + /** + * Holds if there is a partial data flow path from `source` to `node`. The + * approximate distance between `node` and the closest source is `dist` and + * is restricted to be less than or equal to `explorationLimit()`. This + * predicate completely disregards sink definitions. + * + * This predicate is intended for data-flow exploration and debugging and may + * perform poorly if the number of sources is too big and/or the exploration + * limit is set too high without using barriers. + * + * This predicate is disabled (has no results) by default. Override + * `explorationLimit()` with a suitable number to enable this predicate. + * + * To use this in a `path-problem` query, import the module `PartialPathGraph`. + */ + final predicate hasPartialFlow(PartialPathNode source, PartialPathNode node, int dist) { + partialFlow(source, node, this) and + dist = node.getSourceDistance() + } + + /** + * Holds if there is a partial data flow path from `node` to `sink`. The + * approximate distance between `node` and the closest sink is `dist` and + * is restricted to be less than or equal to `explorationLimit()`. This + * predicate completely disregards source definitions. + * + * This predicate is intended for data-flow exploration and debugging and may + * perform poorly if the number of sinks is too big and/or the exploration + * limit is set too high without using barriers. + * + * This predicate is disabled (has no results) by default. Override + * `explorationLimit()` with a suitable number to enable this predicate. + * + * To use this in a `path-problem` query, import the module `PartialPathGraph`. + * + * Note that reverse flow has slightly lower precision than the corresponding + * forward flow, as reverse flow disregards type pruning among other features. + */ + final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) { + revPartialFlow(node, sink, this) and + dist = node.getSinkDistance() + } +} + +/** + * This class exists to prevent mutual recursion between the user-overridden + * member predicates of `Configuration` and the rest of the data-flow library. + * Good performance cannot be guaranteed in the presence of such recursion, so + * it should be replaced by using more than one copy of the data flow library. + */ +abstract private class ConfigurationRecursionPrevention extends Configuration { + bindingset[this] + ConfigurationRecursionPrevention() { any() } + + override predicate hasFlow(Node source, Node sink) { + strictcount(Node n | this.isSource(n)) < 0 + or + strictcount(Node n | this.isSink(n)) < 0 + or + strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 + or + super.hasFlow(source, sink) + } +} + +private newtype TNodeEx = + TNodeNormal(Node n) or + TNodeImplicitRead(Node n, boolean hasRead) { + any(Configuration c).allowImplicitRead(n, _) and hasRead = [false, true] + } + +private class NodeEx extends TNodeEx { + string toString() { + result = this.asNode().toString() + or + exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]") + } + + Node asNode() { this = TNodeNormal(result) } + + predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) } + + Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) } + + pragma[nomagic] + private DataFlowCallable getEnclosingCallable0() { + nodeEnclosingCallable(this.projectToNode(), result) + } + + pragma[inline] + DataFlowCallable getEnclosingCallable() { + pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result) + } + + pragma[nomagic] + private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) } + + pragma[inline] + DataFlowType getDataFlowType() { + pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result) + } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +private class ArgNodeEx extends NodeEx { + ArgNodeEx() { this.asNode() instanceof ArgNode } +} + +private class ParamNodeEx extends NodeEx { + ParamNodeEx() { this.asNode() instanceof ParamNode } + + predicate isParameterOf(DataFlowCallable c, int i) { + this.asNode().(ParamNode).isParameterOf(c, i) + } + + int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } +} + +private class RetNodeEx extends NodeEx { + RetNodeEx() { this.asNode() instanceof ReturnNodeExt } + + ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) } + + ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() } +} + +private predicate inBarrier(NodeEx node, Configuration config) { + exists(Node n | + node.asNode() = n and + config.isBarrierIn(n) and + config.isSource(n) + ) +} + +private predicate outBarrier(NodeEx node, Configuration config) { + exists(Node n | + node.asNode() = n and + config.isBarrierOut(n) and + config.isSink(n) + ) +} + +private predicate fullBarrier(NodeEx node, Configuration config) { + exists(Node n | node.asNode() = n | + config.isBarrier(n) + or + config.isBarrierIn(n) and + not config.isSource(n) + or + config.isBarrierOut(n) and + not config.isSink(n) + or + exists(BarrierGuard g | + config.isBarrierGuard(g) and + n = g.getAGuardedNode() + ) + ) +} + +pragma[nomagic] +private predicate sourceNode(NodeEx node, Configuration config) { config.isSource(node.asNode()) } + +pragma[nomagic] +private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) } + +/** + * Holds if data can flow in one local step from `node1` to `node2`. + */ +private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + simpleLocalFlowStepExt(n1, n2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) + ) + or + exists(Node n | + config.allowImplicitRead(n, _) and + node1.asNode() = n and + node2.isImplicitReadNode(n, false) + ) +} + +/** + * Holds if the additional step from `node1` to `node2` does not jump between callables. + */ +private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + config.isAdditionalFlowStep(n1, n2) and + getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) + ) + or + exists(Node n | + config.allowImplicitRead(n, _) and + node1.isImplicitReadNode(n, true) and + node2.asNode() = n + ) +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ +private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + jumpStepCached(n1, n2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) + ) +} + +/** + * Holds if the additional step from `node1` to `node2` jumps between callables. + */ +private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + config.isAdditionalFlowStep(n1, n2) and + getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) + ) +} + +private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { + read(node1.asNode(), c, node2.asNode()) + or + exists(Node n | + node2.isImplicitReadNode(n, true) and + node1.isImplicitReadNode(n, _) and + config.allowImplicitRead(n, c) + ) +} + +private predicate store( + NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config +) { + store(node1.asNode(), tc, node2.asNode(), contentType) and + read(_, tc.getContent(), _, config) +} + +pragma[nomagic] +private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) { + viableReturnPosOut(call, pos, out.asNode()) +} + +pragma[nomagic] +private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { + viableParamArg(call, p.asNode(), arg.asNode()) +} + +/** + * Holds if field flow should be used for the given configuration. + */ +private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 } + +private module Stage1 { + class ApApprox = Unit; + + class Ap = Unit; + + class ApOption = Unit; + + class Cc = boolean; + + /* Begin: Stage 1 logic. */ + /** + * Holds if `node` is reachable from a source in the configuration `config`. + * + * The Boolean `cc` records whether the node is reached through an + * argument in a call. + */ + predicate fwdFlow(NodeEx node, Cc cc, Configuration config) { + not fullBarrier(node, config) and + ( + sourceNode(node, config) and + cc = false + or + exists(NodeEx mid | + fwdFlow(mid, cc, config) and + localFlowStep(mid, node, config) + ) + or + exists(NodeEx mid | + fwdFlow(mid, cc, config) and + additionalLocalFlowStep(mid, node, config) + ) + or + exists(NodeEx mid | + fwdFlow(mid, _, config) and + jumpStep(mid, node, config) and + cc = false + ) + or + exists(NodeEx mid | + fwdFlow(mid, _, config) and + additionalJumpStep(mid, node, config) and + cc = false + ) + or + // store + exists(NodeEx mid | + useFieldFlow(config) and + fwdFlow(mid, cc, config) and + store(mid, _, node, _, config) and + not outBarrier(mid, config) + ) + or + // read + exists(Content c | + fwdFlowRead(c, node, cc, config) and + fwdFlowConsCand(c, config) and + not inBarrier(node, config) + ) + or + // flow into a callable + exists(NodeEx arg | + fwdFlow(arg, _, config) and + viableParamArgEx(_, node, arg) and + cc = true + ) + or + // flow out of a callable + exists(DataFlowCall call | + fwdFlowOut(call, node, false, config) and + cc = false + or + fwdFlowOutFromArg(call, node, config) and + fwdFlowIsEntered(call, cc, config) + ) + ) + } + + private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) } + + pragma[nomagic] + private predicate fwdFlowRead(Content c, NodeEx node, Cc cc, Configuration config) { + exists(NodeEx mid | + fwdFlow(mid, cc, config) and + read(mid, c, node, config) + ) + } + + /** + * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Content c, Configuration config) { + exists(NodeEx mid, NodeEx node, TypedContent tc | + not fullBarrier(node, config) and + useFieldFlow(config) and + fwdFlow(mid, _, config) and + store(mid, tc, node, _, config) and + c = tc.getContent() + ) + } + + pragma[nomagic] + private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc, Configuration config) { + exists(RetNodeEx ret | + fwdFlow(ret, cc, config) and + ret.getReturnPosition() = pos + ) + } + + pragma[nomagic] + private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) { + exists(ReturnPosition pos | + fwdFlowReturnPosition(pos, cc, config) and + viableReturnPosOutEx(call, pos, out) + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out, Configuration config) { + fwdFlowOut(call, out, true, config) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { + exists(ArgNodeEx arg | + fwdFlow(arg, cc, config) and + viableParamArgEx(call, _, arg) + ) + } + + /** + * Holds if `node` is part of a path from a source to a sink in the + * configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from + * the enclosing callable in order to reach a sink. + */ + pragma[nomagic] + predicate revFlow(NodeEx node, boolean toReturn, Configuration config) { + revFlow0(node, toReturn, config) and + fwdFlow(node, config) + } + + pragma[nomagic] + private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) { + fwdFlow(node, config) and + sinkNode(node, config) and + toReturn = false + or + exists(NodeEx mid | + localFlowStep(node, mid, config) and + revFlow(mid, toReturn, config) + ) + or + exists(NodeEx mid | + additionalLocalFlowStep(node, mid, config) and + revFlow(mid, toReturn, config) + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, _, config) and + toReturn = false + ) + or + exists(NodeEx mid | + additionalJumpStep(node, mid, config) and + revFlow(mid, _, config) and + toReturn = false + ) + or + // store + exists(Content c | + revFlowStore(c, node, toReturn, config) and + revFlowConsCand(c, config) + ) + or + // read + exists(NodeEx mid, Content c | + read(node, c, mid, config) and + fwdFlowConsCand(c, pragma[only_bind_into](config)) and + revFlow(mid, toReturn, pragma[only_bind_into](config)) + ) + or + // flow into a callable + exists(DataFlowCall call | + revFlowIn(call, node, false, config) and + toReturn = false + or + revFlowInToReturn(call, node, config) and + revFlowIsReturned(call, toReturn, config) + ) + or + // flow out of a callable + exists(ReturnPosition pos | + revFlowOut(pos, config) and + node.(RetNodeEx).getReturnPosition() = pos and + toReturn = true + ) + } + + /** + * Holds if `c` is the target of a read in the flow covered by `revFlow`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Content c, Configuration config) { + exists(NodeEx mid, NodeEx node | + fwdFlow(node, pragma[only_bind_into](config)) and + read(node, c, mid, config) and + fwdFlowConsCand(c, pragma[only_bind_into](config)) and + revFlow(pragma[only_bind_into](mid), _, pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate revFlowStore(Content c, NodeEx node, boolean toReturn, Configuration config) { + exists(NodeEx mid, TypedContent tc | + revFlow(mid, toReturn, pragma[only_bind_into](config)) and + fwdFlowConsCand(c, pragma[only_bind_into](config)) and + store(node, tc, mid, _, config) and + c = tc.getContent() + ) + } + + /** + * Holds if `c` is the target of both a read and a store in the flow covered + * by `revFlow`. + */ + private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + revFlowConsCand(c, conf) and + revFlowStore(c, _, _, conf) + } + + pragma[nomagic] + predicate viableReturnPosOutNodeCandFwd1( + DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config + ) { + fwdFlowReturnPosition(pos, _, config) and + viableReturnPosOutEx(call, pos, out) + } + + pragma[nomagic] + private predicate revFlowOut(ReturnPosition pos, Configuration config) { + exists(DataFlowCall call, NodeEx out | + revFlow(out, _, config) and + viableReturnPosOutNodeCandFwd1(call, pos, out, config) + ) + } + + pragma[nomagic] + predicate viableParamArgNodeCandFwd1( + DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config + ) { + viableParamArgEx(call, p, arg) and + fwdFlow(arg, config) + } + + pragma[nomagic] + private predicate revFlowIn( + DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config + ) { + exists(ParamNodeEx p | + revFlow(p, toReturn, config) and + viableParamArgNodeCandFwd1(call, p, arg, config) + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg, Configuration config) { + revFlowIn(call, arg, true, config) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { + exists(NodeEx out | + revFlow(out, toReturn, config) and + fwdFlowOutFromArg(call, out, config) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Content c | + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + revFlow(node2, pragma[only_bind_into](config)) and + store(node1, tc, node2, contentType, config) and + c = tc.getContent() and + exists(ap1) + ) + } + + pragma[nomagic] + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + revFlow(n2, pragma[only_bind_into](config)) and + read(n1, c, n2, pragma[only_bind_into](config)) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } + + predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) { + revFlow(node, toReturn, config) and exists(returnAp) and exists(ap) + } + + private predicate throughFlowNodeCand(NodeEx node, Configuration config) { + revFlow(node, true, config) and + fwdFlow(node, true, config) and + not inBarrier(node, config) and + not outBarrier(node, config) + } + + /** Holds if flow may return from `callable`. */ + pragma[nomagic] + private predicate returnFlowCallableNodeCand( + DataFlowCallable callable, ReturnKindExt kind, Configuration config + ) { + exists(RetNodeEx ret | + throughFlowNodeCand(ret, config) and + callable = ret.getEnclosingCallable() and + kind = ret.getKind() + ) + } + + /** + * Holds if flow may enter through `p` and reach a return node making `p` a + * candidate for the origin of a summary. + */ + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(ReturnKindExt kind | + throughFlowNodeCand(p, config) and + returnFlowCallableNodeCand(c, kind, config) and + p.getEnclosingCallable() = c and + exists(ap) and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(ArgNodeEx arg, boolean toReturn | + revFlow(arg, toReturn, config) and + revFlowInToReturn(call, arg, config) and + revFlowIsReturned(call, toReturn, config) + ) + } + + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, config)) and + fields = count(Content f0 | fwdFlowConsCand(f0, config)) and + conscand = -1 and + tuples = count(NodeEx n, boolean b | fwdFlow(n, b, config)) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, config)) and + fields = count(Content f0 | revFlowConsCand(f0, config)) and + conscand = -1 and + tuples = count(NodeEx n, boolean b | revFlow(n, b, config)) + } + /* End: Stage 1 logic. */ +} + +pragma[noinline] +private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) { + Stage1::revFlow(node2, config) and + localFlowStep(node1, node2, config) +} + +pragma[noinline] +private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) { + Stage1::revFlow(node2, config) and + additionalLocalFlowStep(node1, node2, config) +} + +pragma[nomagic] +private predicate viableReturnPosOutNodeCand1( + DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config +) { + Stage1::revFlow(out, config) and + Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out, config) +} + +/** + * Holds if data can flow out of `call` from `ret` to `out`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. + */ +pragma[nomagic] +private predicate flowOutOfCallNodeCand1( + DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config +) { + viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) +} + +pragma[nomagic] +private predicate viableParamArgNodeCand1( + DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config +) { + Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and + Stage1::revFlow(arg, config) +} + +/** + * Holds if data can flow into `call` and that this step is part of a + * path from a source to a sink. + */ +pragma[nomagic] +private predicate flowIntoCallNodeCand1( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, Configuration config +) { + viableParamArgNodeCand1(call, p, arg, config) and + Stage1::revFlow(p, config) and + not outBarrier(arg, config) and + not inBarrier(p, config) +} + +/** + * Gets the amount of forward branching on the origin of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ +private int branch(NodeEx n1, Configuration conf) { + result = + strictcount(NodeEx n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + ) +} + +/** + * Gets the amount of backward branching on the target of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ +private int join(NodeEx n2, Configuration conf) { + result = + strictcount(NodeEx n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + ) +} + +/** + * Holds if data can flow out of `call` from `ret` to `out`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. The + * `allowsFieldFlow` flag indicates whether the branching is within the limit + * specified by the configuration. + */ +pragma[nomagic] +private predicate flowOutOfCallNodeCand1( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config +) { + flowOutOfCallNodeCand1(call, ret, out, config) and + exists(int b, int j | + b = branch(ret, config) and + j = join(out, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() + then allowsFieldFlow = true + else allowsFieldFlow = false + ) +} + +/** + * Holds if data can flow into `call` and that this step is part of a + * path from a source to a sink. The `allowsFieldFlow` flag indicates whether + * the branching is within the limit specified by the configuration. + */ +pragma[nomagic] +private predicate flowIntoCallNodeCand1( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config +) { + flowIntoCallNodeCand1(call, arg, p, config) and + exists(int b, int j | + b = branch(arg, config) and + j = join(p, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() + then allowsFieldFlow = true + else allowsFieldFlow = false + ) +} + +private module Stage2 { + module PrevStage = Stage1; + + class ApApprox = PrevStage::Ap; + + class Ap = boolean; + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + private ApApprox getApprox(Ap ap) { any() } + + private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + private Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + class Cc = CallContext; + + class CcCall = CallContextCall; + + class CcNoCall = CallContextNoCall; + + Cc ccNone() { result instanceof CallContextAny } + + private class LocalCc = Unit; + + bindingset[call, c, outercc] + private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + + bindingset[call, c, innercc] + private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + checkCallContextReturn(innercc, c, call) and + if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() + } + + bindingset[node, cc, config] + private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() } + + private predicate localStep( + NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc + ) { + ( + preservesValue = true and + localFlowStepNodeCand1(node1, node2, config) + or + preservesValue = false and + additionalLocalFlowStepNodeCand1(node1, node2, config) + ) and + exists(ap) and + exists(lcc) + } + + private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + + private predicate flowIntoCall = flowIntoCallNodeCand1/5; + + bindingset[ap, contentType] + private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } + + /* Begin: Stage 2 logic. */ + private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) { + PrevStage::revFlow(node, _, _, apa, config) + } + + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) + } + + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { + flowCand(node, _, config) and + sourceNode(node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, Ap ap0, LocalCc localCc | + fwdFlow(mid, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc, config) + | + localStep(mid, node, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, node, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and + flowCand(node, _, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and + flowCand(node, _, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } + + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, getApprox(ap1), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } + + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config + ) { + fwdFlow(node1, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) { + revFlow0(node, toReturn, returnAp, ap, config) and + fwdFlow(node, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, _, _, ap, config) and + sinkNode(node, config) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid | + localStep(node, mid, true, _, config, _) and + revFlow(mid, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and + localStep(node, mid, false, _, config, _) and + revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, _, _, ap, config) and + toReturn = true and + if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn, + ApOption returnAp, Configuration config + ) { + revFlow(mid, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, CcCall ccc | + revFlowOut(call, ret, toReturn, returnAp, ap, config) and + fwdFlow(ret, ccc, apSome(_), ap, config) and + ccc.matchesCall(call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + store(node1, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0), + pragma[only_bind_into](config)) and + fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config)) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config)) + } + /* End: Stage 2 logic. */ +} + +pragma[nomagic] +private predicate flowOutOfCallNodeCand2( + DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config +) { + flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + Stage2::revFlow(node2, pragma[only_bind_into](config)) and + Stage2::revFlow(node1, pragma[only_bind_into](config)) +} + +pragma[nomagic] +private predicate flowIntoCallNodeCand2( + DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, + Configuration config +) { + flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + Stage2::revFlow(node2, pragma[only_bind_into](config)) and + Stage2::revFlow(node1, pragma[only_bind_into](config)) +} + +private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends NodeEx { + FlowCheckNode() { + castNode(this.asNode()) or + clearsContentCached(this.asNode(), _) + } + } + + /** + * Holds if `node` can be the first node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + predicate localFlowEntry(NodeEx node, Configuration config) { + Stage2::revFlow(node, config) and + ( + sourceNode(node, config) or + jumpStep(_, node, config) or + additionalJumpStep(_, node, config) or + node instanceof ParamNodeEx or + node.asNode() instanceof OutNodeExt or + store(_, _, node, _, config) or + read(_, _, node, config) or + node instanceof FlowCheckNode + ) + } + + /** + * Holds if `node` can be the last node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + private predicate localFlowExit(NodeEx node, Configuration config) { + exists(NodeEx next | Stage2::revFlow(next, config) | + jumpStep(node, next, config) or + additionalJumpStep(node, next, config) or + flowIntoCallNodeCand1(_, node, next, config) or + flowOutOfCallNodeCand1(_, node, next, config) or + store(node, _, next, _, config) or + read(node, _, next, config) + ) + or + node instanceof FlowCheckNode + or + sinkNode(node, config) + } + + pragma[noinline] + private predicate additionalLocalFlowStepNodeCand2( + NodeEx node1, NodeEx node2, Configuration config + ) { + additionalLocalFlowStepNodeCand1(node1, node2, config) and + Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and + Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config)) + } + + /** + * Holds if the local path from `node1` to `node2` is a prefix of a maximal + * subsequence of local flow steps in a dataflow path. + * + * This is the transitive closure of `[additional]localFlowStep` beginning + * at `localFlowEntry`. + */ + pragma[nomagic] + private predicate localFlowStepPlus( + NodeEx node1, NodeEx node2, boolean preservesValue, DataFlowType t, Configuration config, + LocalCallContext cc + ) { + not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and + ( + localFlowEntry(node1, pragma[only_bind_into](config)) and + ( + localFlowStepNodeCand1(node1, node2, config) and + preservesValue = true and + t = node1.getDataFlowType() // irrelevant dummy value + or + additionalLocalFlowStepNodeCand2(node1, node2, config) and + preservesValue = false and + t = node2.getDataFlowType() + ) and + node1 != node2 and + cc.relevantFor(node1.getEnclosingCallable()) and + not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and + Stage2::revFlow(node2, pragma[only_bind_into](config)) + or + exists(NodeEx mid | + localFlowStepPlus(node1, mid, preservesValue, t, pragma[only_bind_into](config), cc) and + localFlowStepNodeCand1(mid, node2, config) and + not mid instanceof FlowCheckNode and + Stage2::revFlow(node2, pragma[only_bind_into](config)) + ) + or + exists(NodeEx mid | + localFlowStepPlus(node1, mid, _, _, pragma[only_bind_into](config), cc) and + additionalLocalFlowStepNodeCand2(mid, node2, config) and + not mid instanceof FlowCheckNode and + preservesValue = false and + t = node2.getDataFlowType() and + Stage2::revFlow(node2, pragma[only_bind_into](config)) + ) + ) + } + + /** + * Holds if `node1` can step to `node2` in one or more local steps and this + * path can occur as a maximal subsequence of local steps in a dataflow path. + */ + pragma[nomagic] + predicate localFlowBigStep( + NodeEx node1, NodeEx node2, boolean preservesValue, AccessPathFrontNil apf, + Configuration config, LocalCallContext callContext + ) { + localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and + localFlowExit(node2, config) + } +} + +private import LocalFlowBigStep + +private module Stage3 { + module PrevStage = Stage2; + + class ApApprox = PrevStage::Ap; + + class Ap = AccessPathFront; + + class ApNil = AccessPathFrontNil; + + private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + + private ApNil getApNil(NodeEx node) { + PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) + } + + bindingset[tc, tail] + private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + + pragma[noinline] + private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + + class ApOption = AccessPathFrontOption; + + ApOption apNone() { result = TAccessPathFrontNone() } + + ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } + + class Cc = boolean; + + class CcCall extends Cc { + CcCall() { this = true } + + /** Holds if this call context may be `call`. */ + predicate matchesCall(DataFlowCall call) { any() } + } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + private class LocalCc = Unit; + + bindingset[call, c, outercc] + private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } + + bindingset[node, cc, config] + private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() } + + private predicate localStep( + NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc + ) { + localFlowBigStep(node1, node2, preservesValue, ap, config, _) and exists(lcc) + } + + private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + + private predicate flowIntoCall = flowIntoCallNodeCand2/5; + + pragma[nomagic] + private predicate clear(NodeEx node, Ap ap) { ap.isClearedAt(node.asNode()) } + + pragma[nomagic] + private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } + + bindingset[node, ap] + private predicate filter(NodeEx node, Ap ap) { + not clear(node, ap) and + if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + } + + bindingset[ap, contentType] + private predicate typecheckStore(Ap ap, DataFlowType contentType) { + // We need to typecheck stores here, since reverse flow through a getter + // might have a different type here compared to inside the getter. + compatibleTypes(ap.getType(), contentType) + } + + /* Begin: Stage 3 logic. */ + private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) { + PrevStage::revFlow(node, _, _, apa, config) + } + + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + exists(ApApprox apa0 | + apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0) + ) + } + + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) + } + + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { + fwdFlow0(node, cc, argAp, ap, config) and + flowCand(node, unbindApa(getApprox(ap)), config) and + filter(node, ap) + } + + pragma[nomagic] + private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { + flowCand(node, _, config) and + sourceNode(node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, Ap ap0, LocalCc localCc | + fwdFlow(mid, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc, config) + | + localStep(mid, node, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, node, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and + flowCand(node, _, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and + flowCand(node, _, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } + + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } + + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config + ) { + fwdFlow(node1, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) { + revFlow0(node, toReturn, returnAp, ap, config) and + fwdFlow(node, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, _, _, ap, config) and + sinkNode(node, config) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid | + localStep(node, mid, true, _, config, _) and + revFlow(mid, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and + localStep(node, mid, false, _, config, _) and + revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, _, _, ap, config) and + toReturn = true and + if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn, + ApOption returnAp, Configuration config + ) { + revFlow(mid, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, CcCall ccc | + revFlowOut(call, ret, toReturn, returnAp, ap, config) and + fwdFlow(ret, ccc, apSome(_), ap, config) and + ccc.matchesCall(call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + store(node1, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0), + pragma[only_bind_into](config)) and + fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config)) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config)) + } + /* End: Stage 3 logic. */ +} + +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + Stage3::revFlow(node, true, _, apf, config) and + Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and + nodes = + strictcount(NodeEx n | + Stage3::revFlow(n, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() + ) +} + +private newtype TAccessPathApprox = + TNil(DataFlowType t) or + TConsNil(TypedContent tc, DataFlowType t) { + Stage3::consCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + Stage3::consCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) + } + +/** + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPathApprox extends TAccessPathApprox { + abstract string toString(); + + abstract TypedContent getHead(); + + abstract int len(); + + abstract DataFlowType getType(); + + abstract AccessPathFront getFront(); + + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); +} + +private class AccessPathApproxNil extends AccessPathApprox, TNil { + private DataFlowType t; + + AccessPathApproxNil() { this = TNil(t) } + + override string toString() { result = concat(": " + ppReprType(t)) } + + override TypedContent getHead() { none() } + + override int len() { result = 0 } + + override DataFlowType getType() { result = t } + + override AccessPathFront getFront() { result = TFrontNil(t) } + + override AccessPathApprox pop(TypedContent head) { none() } +} + +abstract private class AccessPathApproxCons extends AccessPathApprox { } + +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private TypedContent tc; + private DataFlowType t; + + AccessPathApproxConsNil() { this = TConsNil(tc, t) } + + override string toString() { + // The `concat` becomes "" if `ppReprType` has no result. + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) + } + + override TypedContent getHead() { result = tc } + + override int len() { result = 1 } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } +} + +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private TypedContent tc1; + private TypedContent tc2; + private int len; + + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } + + override string toString() { + if len = 2 + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc1 } + + override int len() { result = len } + + override DataFlowType getType() { result = tc1.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc1) } + + override AccessPathApprox pop(TypedContent head) { + head = tc1 and + ( + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + Stage3::consCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) + ) + } +} + +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } + +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } + +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) + +private class AccessPathApproxOption extends TAccessPathApproxOption { + string toString() { + this = TAccessPathApproxNone() and result = "<none>" + or + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) + } +} + +private module Stage4 { + module PrevStage = Stage3; + + class ApApprox = PrevStage::Ap; + + class Ap = AccessPathApprox; + + class ApNil = AccessPathApproxNil; + + private ApApprox getApprox(Ap ap) { result = ap.getFront() } + + private ApNil getApNil(NodeEx node) { + PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) + } + + bindingset[tc, tail] + private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + + pragma[noinline] + private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + + class ApOption = AccessPathApproxOption; + + ApOption apNone() { result = TAccessPathApproxNone() } + + ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } + + class Cc = CallContext; + + class CcCall = CallContextCall; + + class CcNoCall = CallContextNoCall; + + Cc ccNone() { result instanceof CallContextAny } + + private class LocalCc = LocalCallContext; + + bindingset[call, c, outercc] + private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } + + bindingset[call, c, innercc] + private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + checkCallContextReturn(innercc, c, call) and + if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() + } + + bindingset[node, cc, config] + private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { + localFlowEntry(node, config) and + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + private predicate localStep( + NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc + ) { + localFlowBigStep(node1, node2, preservesValue, ap.getFront(), config, lcc) + } + + pragma[nomagic] + private predicate flowOutOfCall( + DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and + PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate flowIntoCall( + DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, + Configuration config + ) { + flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and + PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config)) + } + + bindingset[node, ap] + private predicate filter(NodeEx node, Ap ap) { any() } + + // Type checking is not necessary here as it has already been done in stage 3. + bindingset[ap, contentType] + private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } + + /* Begin: Stage 4 logic. */ + private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) { + PrevStage::revFlow(node, _, _, apa, config) + } + + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + exists(ApApprox apa0 | + apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0) + ) + } + + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) + } + + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { + fwdFlow0(node, cc, argAp, ap, config) and + flowCand(node, unbindApa(getApprox(ap)), config) and + filter(node, ap) + } + + pragma[nomagic] + private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { + flowCand(node, _, config) and + sourceNode(node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, Ap ap0, LocalCc localCc | + fwdFlow(mid, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc, config) + | + localStep(mid, node, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, node, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and + flowCand(node, _, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and + flowCand(node, _, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } + + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } + + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config + ) { + fwdFlow(node1, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) { + revFlow0(node, toReturn, returnAp, ap, config) and + fwdFlow(node, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, _, _, ap, config) and + sinkNode(node, config) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid | + localStep(node, mid, true, _, config, _) and + revFlow(mid, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and + localStep(node, mid, false, _, config, _) and + revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, _, _, ap, config) and + toReturn = true and + if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn, + ApOption returnAp, Configuration config + ) { + revFlow(mid, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, CcCall ccc | + revFlowOut(call, ret, toReturn, returnAp, ap, config) and + fwdFlow(ret, ccc, apSome(_), ap, config) and + ccc.matchesCall(call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + store(node1, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0), + pragma[only_bind_into](config)) and + fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config)) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config)) + } + /* End: Stage 4 logic. */ +} + +bindingset[conf, result] +private Configuration unbindConf(Configuration conf) { + exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) +} + +private predicate nodeMayUseSummary(NodeEx n, AccessPathApprox apa, Configuration config) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + Stage4::parameterMayFlowThrough(_, c, apa, _) and + Stage4::revFlow(n, true, _, apa0, config) and + Stage4::fwdFlow(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and + n.getEnclosingCallable() = c + ) +} + +private newtype TSummaryCtx = + TSummaryCtxNone() or + TSummaryCtxSome(ParamNodeEx p, AccessPath ap) { + Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) + } + +/** + * A context for generating flow summaries. This represents flow entry through + * a specific parameter with an access path of a specific shape. + * + * Summaries are only created for parameters that may flow through. + */ +abstract private class SummaryCtx extends TSummaryCtx { + abstract string toString(); +} + +/** A summary context from which no flow summary can be generated. */ +private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { + override string toString() { result = "<none>" } +} + +/** A summary context from which a flow summary can be generated. */ +private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { + private ParamNodeEx p; + private AccessPath ap; + + SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } + + int getParameterPos() { p.isParameterOf(_, result) } + + ParamNodeEx getParamNode() { result = p } + + override string toString() { result = p + ": " + ap } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + p.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = + strictcount(NodeEx n | + Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + Stage4::consCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } + +private newtype TPathNode = + TPathNodeMid(NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + // A PathNode is introduced by a source ... + Stage4::revFlow(node, config) and + sourceNode(node, config) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + ap = TAccessPathNil(node.getDataFlowType()) + or + // ... or a step from an existing PathNode to another node. + exists(PathNodeMid mid | + pathStep(mid, node, cc, sc, ap) and + pragma[only_bind_into](config) = mid.getConfiguration() and + Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config)) + ) + } or + TPathNodeSink(NodeEx node, Configuration config) { + sinkNode(node, pragma[only_bind_into](config)) and + Stage4::revFlow(node, pragma[only_bind_into](config)) and + ( + // A sink that is also a source ... + sourceNode(node, config) + or + // ... or a sink that can be reached from a source + exists(PathNodeMid mid | + pathStep(mid, node, _, _, TAccessPathNil(_)) and + pragma[only_bind_into](config) = mid.getConfiguration() + ) + ) + } + +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl(boolean needsSuffix) { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + needsSuffix = false and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + Stage4::consCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } +} + +/** + * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. + * Only those `PathNode`s that are reachable from a source are generated. + */ +class PathNode extends TPathNode { + /** Gets a textual representation of this element. */ + string toString() { none() } + + /** + * Gets a textual representation of this element, including a textual + * representation of the call context. + */ + string toStringWithContext() { none() } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + none() + } + + /** Gets the underlying `Node`. */ + final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result } + + /** Gets the associated configuration. */ + Configuration getConfiguration() { none() } + + private PathNode getASuccessorIfHidden() { + this.(PathNodeImpl).isHidden() and + result = this.(PathNodeImpl).getASuccessorImpl() + } + + /** Gets a successor of this node, if any. */ + final PathNode getASuccessor() { + result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and + not this.(PathNodeImpl).isHidden() and + not result.(PathNodeImpl).isHidden() + } + + /** Holds if this node is a source. */ + predicate isSource() { none() } +} + +abstract private class PathNodeImpl extends PathNode { + abstract PathNode getASuccessorImpl(); + + abstract NodeEx getNodeEx(); + + predicate isHidden() { + hiddenNode(this.getNodeEx().asNode()) and + not this.isSource() and + not this instanceof PathNodeSink + or + this.getNodeEx() instanceof TNodeImplicitRead + } + + private string ppAp() { + this instanceof PathNodeSink and result = "" + or + exists(string s | s = this.(PathNodeMid).getAp().toString() | + if s = "" then result = "" else result = " " + s + ) + } + + private string ppCtx() { + this instanceof PathNodeSink and result = "" + or + result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" + } + + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } + + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +/** Holds if `n` can reach a sink. */ +private predicate directReach(PathNode n) { + n instanceof PathNodeSink or directReach(n.getASuccessor()) +} + +/** Holds if `n` can reach a sink or is used in a subpath. */ +private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) } + +/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */ +private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) } + +private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2) + +/** + * Provides the query predicates needed to include a graph in a path-problem query. + */ +module PathGraph { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) } + + /** Holds if `n` is a node in the graph of data flow path explanations. */ + query predicate nodes(PathNode n, string key, string val) { + reach(n) and key = "semmle.label" and val = n.toString() + } + + query predicate subpaths = Subpaths::subpaths/4; +} + +/** + * An intermediate flow graph node. This is a triple consisting of a `Node`, + * a `CallContext`, and a `Configuration`. + */ +private class PathNodeMid extends PathNodeImpl, TPathNodeMid { + NodeEx node; + CallContext cc; + SummaryCtx sc; + AccessPath ap; + Configuration config; + + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + + override NodeEx getNodeEx() { result = node } + + CallContext getCallContext() { result = cc } + + SummaryCtx getSummaryCtx() { result = sc } + + AccessPath getAp() { result = ap } + + override Configuration getConfiguration() { result = config } + + private PathNodeMid getSuccMid() { + pathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx(), + result.getAp()) and + result.getConfiguration() = unbindConf(this.getConfiguration()) + } + + override PathNodeImpl getASuccessorImpl() { + // an intermediate step to another intermediate node + result = this.getSuccMid() + or + // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges + exists(PathNodeMid mid, PathNodeSink sink | + mid = this.getSuccMid() and + mid.getNodeEx() = sink.getNodeEx() and + mid.getAp() instanceof AccessPathNil and + sink.getConfiguration() = unbindConf(mid.getConfiguration()) and + result = sink + ) + } + + override predicate isSource() { + sourceNode(node, config) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + ap instanceof AccessPathNil + } +} + +/** + * A flow graph node corresponding to a sink. This is disjoint from the + * intermediate nodes in order to uniquely correspond to a given sink by + * excluding the `CallContext`. + */ +private class PathNodeSink extends PathNodeImpl, TPathNodeSink { + NodeEx node; + Configuration config; + + PathNodeSink() { this = TPathNodeSink(node, config) } + + override NodeEx getNodeEx() { result = node } + + override Configuration getConfiguration() { result = config } + + override PathNode getASuccessorImpl() { none() } + + override predicate isSource() { sourceNode(node, config) } +} + +/** + * Holds if data may flow from `mid` to `node`. The last step in or out of + * a callable is recorded by `cc`. + */ +private predicate pathStep( + PathNodeMid mid, NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap +) { + exists(AccessPath ap0, NodeEx midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNodeEx() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + ap = mid.getAp() + or + additionalJumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(node.getDataFlowType()) + or + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() + or + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() + or + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone + or + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() +} + +pragma[nomagic] +private predicate pathReadStep( + PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc +) { + ap0 = mid.getAp() and + tc = ap0.getHead() and + Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and + cc = mid.getCallContext() +} + +pragma[nomagic] +private predicate pathStoreStep( + PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc +) { + ap0 = mid.getAp() and + Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and + cc = mid.getCallContext() +} + +private predicate pathOutOfCallable0( + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config +) { + pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and + innercc = mid.getCallContext() and + innercc instanceof CallContextNoCall and + apa = mid.getAp().getApprox() and + config = mid.getConfiguration() +} + +pragma[nomagic] +private predicate pathOutOfCallable1( + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, + Configuration config +) { + exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | + pathOutOfCallable0(mid, pos, innercc, apa, config) and + c = pos.getCallable() and + kind = pos.getKind() and + resolveReturn(innercc, c, call) + | + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) +} + +pragma[noinline] +private NodeEx getAnOutNodeFlow( + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config +) { + result.asNode() = kind.getAnOutNode(call) and + Stage4::revFlow(result, _, _, apa, config) +} + +/** + * Holds if data may flow from `mid` to `out`. The last step of this path + * is a return from a callable and is recorded by `cc`, if needed. + */ +pragma[noinline] +private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) { + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) + ) +} + +/** + * Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`. + */ +pragma[noinline] +private predicate pathIntoArg( + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config +) { + exists(ArgNode arg | + arg = mid.getNodeEx().asNode() and + cc = mid.getCallContext() and + arg.argumentOf(call, i) and + ap = mid.getAp() and + apa = ap.getApprox() and + config = mid.getConfiguration() + ) +} + +pragma[noinline] +private predicate parameterCand( + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config +) { + exists(ParamNodeEx p | + Stage4::revFlow(p, _, _, apa, config) and + p.isParameterOf(callable, i) + ) +} + +pragma[nomagic] +private predicate pathIntoCallable0( + PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, + AccessPath ap, Configuration config +) { + exists(AccessPathApprox apa | + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and + callable = resolveCall(call, outercc) and + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) + ) +} + +/** + * Holds if data may flow from `mid` to `p` through `call`. The contexts + * before and after entering the callable are `outercc` and `innercc`, + * respectively. + */ +pragma[nomagic] +private predicate pathIntoCallable( + PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + DataFlowCall call, Configuration config +) { + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +/** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ +pragma[nomagic] +private predicate paramFlowsThrough( + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config +) { + exists(PathNodeMid mid, RetNodeEx ret, int pos | + mid.getNodeEx() = ret and + kind = ret.getKind() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + config = mid.getConfiguration() and + ap = mid.getAp() and + apa = ap.getApprox() and + pos = sc.getParameterPos() and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) + ) +} + +pragma[nomagic] +private predicate pathThroughCallable0( + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa, Configuration config +) { + exists(CallContext innercc, SummaryCtx sc | + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) + ) +} + +/** + * Holds if data may flow from `mid` through a callable to the node `out`. + * The context `cc` is restored to its value prior to entering the callable. + */ +pragma[noinline] +private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) + ) +} + +private module Subpaths { + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by + * `kind`, `sc`, `apout`, and `innercc`. + */ + pragma[nomagic] + private predicate subpaths01( + PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, + NodeEx out, AccessPath apout + ) { + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by + * `kind`, `sc`, `apout`, and `innercc`. + */ + pragma[nomagic] + private predicate subpaths02( + PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, + NodeEx out, AccessPath apout + ) { + subpaths01(arg, par, sc, innercc, kind, out, apout) and + out.asNode() = kind.getAnOutNode(_) + } + + pragma[nomagic] + private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple. + */ + pragma[nomagic] + private predicate subpaths03( + PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout + ) { + exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode | + subpaths02(arg, par, sc, innercc, kind, out, apout) and + ret.getNodeEx() = retnode and + kind = retnode.getKind() and + innercc = ret.getCallContext() and + sc = ret.getSummaryCtx() and + ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and + apout = ret.getAp() and + not ret.isHidden() + ) + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through + * a subpath between `par` and `ret` with the connecting edges `arg -> par` and + * `ret -> out` is summarized as the edge `arg -> out`. + */ + predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) { + exists(ParamNodeEx p, NodeEx o, AccessPath apout | + pragma[only_bind_into](arg).getASuccessor() = par and + pragma[only_bind_into](arg).getASuccessor() = out and + subpaths03(arg, p, ret, o, apout) and + par.getNodeEx() = p and + out.getNodeEx() = o and + out.getAp() = apout + ) + } + + /** + * Holds if `n` can reach a return node in a summarized subpath. + */ + predicate retReach(PathNode n) { + subpaths(_, _, n, _) + or + exists(PathNode mid | + retReach(mid) and + n.getASuccessor() = mid and + not subpaths(_, mid, _, _) + ) + } +} + +/** + * Holds if data can flow (inter-procedurally) from `source` to `sink`. + * + * Will only have results if `configuration` has non-empty sources and + * sinks. + */ +private predicate flowsTo( + PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration +) { + flowsource.isSource() and + flowsource.getConfiguration() = configuration and + flowsource.(PathNodeImpl).getNodeEx().asNode() = source and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.getNodeEx().asNode() = sink +} + +/** + * Holds if data can flow (inter-procedurally) from `source` to `sink`. + * + * Will only have results if `configuration` has non-empty sources and + * sinks. + */ +predicate flowsTo(Node source, Node sink, Configuration configuration) { + flowsTo(_, _, source, sink, configuration) +} + +private predicate finalStats(boolean fwd, int nodes, int fields, int conscand, int tuples) { + fwd = true and + nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and + fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and + conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and + tuples = count(PathNode pn) + or + fwd = false and + nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and + fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and + conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and + tuples = count(PathNode pn | reach(pn)) +} + +/** + * INTERNAL: Only for debugging. + * + * Calculates per-stage metrics for data flow. + */ +predicate stageStats( + int n, string stage, int nodes, int fields, int conscand, int tuples, Configuration config +) { + stage = "1 Fwd" and n = 10 and Stage1::stats(true, nodes, fields, conscand, tuples, config) + or + stage = "1 Rev" and n = 15 and Stage1::stats(false, nodes, fields, conscand, tuples, config) + or + stage = "2 Fwd" and n = 20 and Stage2::stats(true, nodes, fields, conscand, tuples, config) + or + stage = "2 Rev" and n = 25 and Stage2::stats(false, nodes, fields, conscand, tuples, config) + or + stage = "3 Fwd" and n = 30 and Stage3::stats(true, nodes, fields, conscand, tuples, config) + or + stage = "3 Rev" and n = 35 and Stage3::stats(false, nodes, fields, conscand, tuples, config) + or + stage = "4 Fwd" and n = 40 and Stage4::stats(true, nodes, fields, conscand, tuples, config) + or + stage = "4 Rev" and n = 45 and Stage4::stats(false, nodes, fields, conscand, tuples, config) + or + stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, tuples) + or + stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, tuples) +} + +private module FlowExploration { + private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2, Configuration config) { + exists(NodeEx node1, NodeEx node2 | + jumpStep(node1, node2, config) + or + additionalJumpStep(node1, node2, config) + or + // flow into callable + viableParamArgEx(_, node2, node1) + or + // flow out of a callable + viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2) + | + c1 = node1.getEnclosingCallable() and + c2 = node2.getEnclosingCallable() and + c1 != c2 + ) + } + + private predicate interestingCallableSrc(DataFlowCallable c, Configuration config) { + exists(Node n | config.isSource(n) and c = getNodeEnclosingCallable(n)) + or + exists(DataFlowCallable mid | + interestingCallableSrc(mid, config) and callableStep(mid, c, config) + ) + } + + private predicate interestingCallableSink(DataFlowCallable c, Configuration config) { + exists(Node n | config.isSink(n) and c = getNodeEnclosingCallable(n)) + or + exists(DataFlowCallable mid | + interestingCallableSink(mid, config) and callableStep(c, mid, config) + ) + } + + private newtype TCallableExt = + TCallable(DataFlowCallable c, Configuration config) { + interestingCallableSrc(c, config) or + interestingCallableSink(c, config) + } or + TCallableSrc() or + TCallableSink() + + private predicate callableExtSrc(TCallableSrc src) { any() } + + private predicate callableExtSink(TCallableSink sink) { any() } + + private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) { + exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config | + callableStep(c1, c2, config) and + ce1 = TCallable(c1, pragma[only_bind_into](config)) and + ce2 = TCallable(c2, pragma[only_bind_into](config)) + ) + or + exists(Node n, Configuration config | + ce1 = TCallableSrc() and + config.isSource(n) and + ce2 = TCallable(getNodeEnclosingCallable(n), config) + ) + or + exists(Node n, Configuration config | + ce2 = TCallableSink() and + config.isSink(n) and + ce1 = TCallable(getNodeEnclosingCallable(n), config) + ) + } + + private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) { + callableExtStepFwd(ce2, ce1) + } + + private int distSrcExt(TCallableExt c) = + shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result) + + private int distSinkExt(TCallableExt c) = + shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result) + + private int distSrc(DataFlowCallable c, Configuration config) { + result = distSrcExt(TCallable(c, config)) - 1 + } + + private int distSink(DataFlowCallable c, Configuration config) { + result = distSinkExt(TCallable(c, config)) - 1 + } + + private newtype TPartialAccessPath = + TPartialNil(DataFlowType t) or + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } + + /** + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first + * element of the list and its length are tracked. If data flows from a source to + * a given node with a given `AccessPath`, this indicates the sequence of + * dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ + private class PartialAccessPath extends TPartialAccessPath { + abstract string toString(); + + TypedContent getHead() { this = TPartialCons(result, _) } + + int len() { + this = TPartialNil(_) and result = 0 + or + this = TPartialCons(_, result) + } + + DataFlowType getType() { + this = TPartialNil(result) + or + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) + } + } + + private class PartialAccessPathNil extends PartialAccessPath, TPartialNil { + override string toString() { + exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t))) + } + } + + private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { + override string toString() { + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + ) + } + } + + private newtype TRevPartialAccessPath = + TRevPartialNil() or + TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] } + + /** + * Conceptually a list of `Content`s, but only the first + * element of the list and its length are tracked. + */ + private class RevPartialAccessPath extends TRevPartialAccessPath { + abstract string toString(); + + Content getHead() { this = TRevPartialCons(result, _) } + + int len() { + this = TRevPartialNil() and result = 0 + or + this = TRevPartialCons(_, result) + } + } + + private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil { + override string toString() { result = "" } + } + + private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons { + override string toString() { + exists(Content c, int len | this = TRevPartialCons(c, len) | + if len = 1 + then result = "[" + c.toString() + "]" + else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" + ) + } + } + + private newtype TSummaryCtx1 = + TSummaryCtx1None() or + TSummaryCtx1Param(ParamNodeEx p) + + private newtype TSummaryCtx2 = + TSummaryCtx2None() or + TSummaryCtx2Some(PartialAccessPath ap) + + private newtype TRevSummaryCtx1 = + TRevSummaryCtx1None() or + TRevSummaryCtx1Some(ReturnPosition pos) + + private newtype TRevSummaryCtx2 = + TRevSummaryCtx2None() or + TRevSummaryCtx2Some(RevPartialAccessPath ap) + + private newtype TPartialPathNode = + TPartialPathNodeFwd( + NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, + Configuration config + ) { + sourceNode(node, config) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + ap = TPartialNil(node.getDataFlowType()) and + not fullBarrier(node, config) and + exists(config.explorationLimit()) + or + partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and + distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit() + } or + TPartialPathNodeRev( + NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap, + Configuration config + ) { + sinkNode(node, config) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + ap = TRevPartialNil() and + not fullBarrier(node, config) and + exists(config.explorationLimit()) + or + exists(PartialPathNodeRev mid | + revPartialPathStep(mid, node, sc1, sc2, ap, config) and + not clearsContentCached(node.asNode(), ap.getHead()) and + not fullBarrier(node, config) and + distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() + ) + } + + pragma[nomagic] + private predicate partialPathNodeMk0( + NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, + Configuration config + ) { + exists(PartialPathNodeFwd mid | + partialPathStep(mid, node, cc, sc1, sc2, ap, config) and + not fullBarrier(node, config) and + not clearsContentCached(node.asNode(), ap.getHead().getContent()) and + if node.asNode() instanceof CastingNode + then compatibleTypes(node.getDataFlowType(), ap.getType()) + else any() + ) + } + + /** + * A `Node` augmented with a call context, an access path, and a configuration. + */ + class PartialPathNode extends TPartialPathNode { + /** Gets a textual representation of this element. */ + string toString() { result = this.getNodeEx().toString() + this.ppAp() } + + /** + * Gets a textual representation of this element, including a textual + * representation of the call context. + */ + string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + + /** Gets the underlying `Node`. */ + final Node getNode() { this.getNodeEx().projectToNode() = result } + + private NodeEx getNodeEx() { + result = this.(PartialPathNodeFwd).getNodeEx() or + result = this.(PartialPathNodeRev).getNodeEx() + } + + /** Gets the associated configuration. */ + Configuration getConfiguration() { none() } + + /** Gets a successor of this node, if any. */ + PartialPathNode getASuccessor() { none() } + + /** + * Gets the approximate distance to the nearest source measured in number + * of interprocedural steps. + */ + int getSourceDistance() { + result = distSrc(this.getNodeEx().getEnclosingCallable(), this.getConfiguration()) + } + + /** + * Gets the approximate distance to the nearest sink measured in number + * of interprocedural steps. + */ + int getSinkDistance() { + result = distSink(this.getNodeEx().getEnclosingCallable(), this.getConfiguration()) + } + + private string ppAp() { + exists(string s | + s = this.(PartialPathNodeFwd).getAp().toString() or + s = this.(PartialPathNodeRev).getAp().toString() + | + if s = "" then result = "" else result = " " + s + ) + } + + private string ppCtx() { + result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">" + } + + /** Holds if this is a source in a forward-flow path. */ + predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() } + + /** Holds if this is a sink in a reverse-flow path. */ + predicate isRevSink() { this.(PartialPathNodeRev).isSink() } + } + + /** + * Provides the query predicates needed to include a graph in a path-problem query. + */ + module PartialPathGraph { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b } + } + + private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd { + NodeEx node; + CallContext cc; + TSummaryCtx1 sc1; + TSummaryCtx2 sc2; + PartialAccessPath ap; + Configuration config; + + PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) } + + NodeEx getNodeEx() { result = node } + + CallContext getCallContext() { result = cc } + + TSummaryCtx1 getSummaryCtx1() { result = sc1 } + + TSummaryCtx2 getSummaryCtx2() { result = sc2 } + + PartialAccessPath getAp() { result = ap } + + override Configuration getConfiguration() { result = config } + + override PartialPathNodeFwd getASuccessor() { + partialPathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx1(), + result.getSummaryCtx2(), result.getAp(), result.getConfiguration()) + } + + predicate isSource() { + sourceNode(node, config) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + ap instanceof TPartialNil + } + } + + private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev { + NodeEx node; + TRevSummaryCtx1 sc1; + TRevSummaryCtx2 sc2; + RevPartialAccessPath ap; + Configuration config; + + PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) } + + NodeEx getNodeEx() { result = node } + + TRevSummaryCtx1 getSummaryCtx1() { result = sc1 } + + TRevSummaryCtx2 getSummaryCtx2() { result = sc2 } + + RevPartialAccessPath getAp() { result = ap } + + override Configuration getConfiguration() { result = config } + + override PartialPathNodeRev getASuccessor() { + revPartialPathStep(result, this.getNodeEx(), this.getSummaryCtx1(), this.getSummaryCtx2(), + this.getAp(), this.getConfiguration()) + } + + predicate isSink() { + sinkNode(node, config) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + ap = TRevPartialNil() + } + } + + private predicate partialPathStep( + PartialPathNodeFwd mid, NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, + PartialAccessPath ap, Configuration config + ) { + not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and + ( + localFlowStep(mid.getNodeEx(), node, config) and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalLocalFlowStep(mid.getNodeEx(), node, config) and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil(node.getDataFlowType()) and + config = mid.getConfiguration() + ) + or + jumpStep(mid.getNodeEx(), node, config) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalJumpStep(mid.getNodeEx(), node, config) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil(node.getDataFlowType()) and + config = mid.getConfiguration() + or + partialPathStoreStep(mid, _, _, node, ap) and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + config = mid.getConfiguration() + or + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + apConsFwd(ap, tc, ap0, config) + ) + or + partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) + or + partialPathOutOfCallable(mid, node, cc, ap, config) and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() + or + partialPathThroughCallable(mid, node, cc, ap, config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() + } + + bindingset[result, i] + private int unbindInt(int i) { i <= result and i >= result } + + pragma[inline] + private predicate partialPathStoreStep( + PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, NodeEx node, + PartialAccessPath ap2 + ) { + exists(NodeEx midNode, DataFlowType contentType | + midNode = mid.getNodeEx() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType, mid.getConfiguration()) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) + } + + pragma[nomagic] + private predicate apConsFwd( + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config + ) { + exists(PartialPathNodeFwd mid | + partialPathStoreStep(mid, ap1, tc, _, ap2) and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate partialPathReadStep( + PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, NodeEx node, CallContext cc, + Configuration config + ) { + exists(NodeEx midNode | + midNode = mid.getNodeEx() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node, pragma[only_bind_into](config)) and + ap.getHead() = tc and + pragma[only_bind_into](config) = mid.getConfiguration() and + cc = mid.getCallContext() + ) + } + + private predicate partialPathOutOfCallable0( + PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap, + Configuration config + ) { + pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and + innercc = mid.getCallContext() and + innercc instanceof CallContextNoCall and + ap = mid.getAp() and + config = mid.getConfiguration() + } + + pragma[nomagic] + private predicate partialPathOutOfCallable1( + PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, + PartialAccessPath ap, Configuration config + ) { + exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | + partialPathOutOfCallable0(mid, pos, innercc, ap, config) and + c = pos.getCallable() and + kind = pos.getKind() and + resolveReturn(innercc, c, call) + | + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) + } + + private predicate partialPathOutOfCallable( + PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config + ) { + exists(ReturnKindExt kind, DataFlowCall call | + partialPathOutOfCallable1(mid, call, kind, cc, ap, config) + | + out.asNode() = kind.getAnOutNode(call) + ) + } + + pragma[noinline] + private predicate partialPathIntoArg( + PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, + Configuration config + ) { + exists(ArgNode arg | + arg = mid.getNodeEx().asNode() and + cc = mid.getCallContext() and + arg.argumentOf(call, i) and + ap = mid.getAp() and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate partialPathIntoCallable0( + PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc, + DataFlowCall call, PartialAccessPath ap, Configuration config + ) { + partialPathIntoArg(mid, i, outercc, call, ap, config) and + callable = resolveCall(call, outercc) + } + + private predicate partialPathIntoCallable( + PartialPathNodeFwd mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, + TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, + Configuration config + ) { + exists(int i, DataFlowCallable callable | + partialPathIntoCallable0(mid, callable, i, outercc, call, ap, config) and + p.isParameterOf(callable, i) and + sc1 = TSummaryCtx1Param(p) and + sc2 = TSummaryCtx2Some(ap) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) + } + + pragma[nomagic] + private predicate paramFlowsThroughInPartialPath( + ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, + PartialAccessPath ap, Configuration config + ) { + exists(PartialPathNodeFwd mid, RetNodeEx ret | + mid.getNodeEx() = ret and + kind = ret.getKind() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + config = mid.getConfiguration() and + ap = mid.getAp() + ) + } + + pragma[noinline] + private predicate partialPathThroughCallable0( + DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, + PartialAccessPath ap, Configuration config + ) { + exists(CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + partialPathIntoCallable(mid, _, cc, innercc, sc1, sc2, call, _, config) and + paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) + ) + } + + private predicate partialPathThroughCallable( + PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config + ) { + exists(DataFlowCall call, ReturnKindExt kind | + partialPathThroughCallable0(call, mid, kind, cc, ap, config) and + out.asNode() = kind.getAnOutNode(call) + ) + } + + private predicate revPartialPathStep( + PartialPathNodeRev mid, NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, + RevPartialAccessPath ap, Configuration config + ) { + localFlowStep(node, mid.getNodeEx(), config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalLocalFlowStep(node, mid.getNodeEx(), config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + mid.getAp() instanceof RevPartialAccessPathNil and + ap = TRevPartialNil() and + config = mid.getConfiguration() + or + jumpStep(node, mid.getNodeEx(), config) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalJumpStep(node, mid.getNodeEx(), config) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + mid.getAp() instanceof RevPartialAccessPathNil and + ap = TRevPartialNil() and + config = mid.getConfiguration() + or + revPartialPathReadStep(mid, _, _, node, ap) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + config = mid.getConfiguration() + or + exists(RevPartialAccessPath ap0, Content c | + revPartialPathStoreStep(mid, ap0, c, node, config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + apConsRev(ap, c, ap0, config) + ) + or + exists(ParamNodeEx p | + mid.getNodeEx() = p and + viableParamArgEx(_, p, node) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + ap = mid.getAp() and + config = mid.getConfiguration() + ) + or + exists(ReturnPosition pos | + revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and + pos = getReturnPosition(node.asNode()) + ) + or + revPartialPathThroughCallable(mid, node, ap, config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() + } + + pragma[inline] + private predicate revPartialPathReadStep( + PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, NodeEx node, + RevPartialAccessPath ap2 + ) { + exists(NodeEx midNode | + midNode = mid.getNodeEx() and + ap1 = mid.getAp() and + read(node, c, midNode, mid.getConfiguration()) and + ap2.getHead() = c and + ap2.len() = unbindInt(ap1.len() + 1) + ) + } + + pragma[nomagic] + private predicate apConsRev( + RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config + ) { + exists(PartialPathNodeRev mid | + revPartialPathReadStep(mid, ap1, c, _, ap2) and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate revPartialPathStoreStep( + PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, NodeEx node, Configuration config + ) { + exists(NodeEx midNode, TypedContent tc | + midNode = mid.getNodeEx() and + ap = mid.getAp() and + store(node, tc, midNode, _, config) and + ap.getHead() = c and + config = mid.getConfiguration() and + tc.getContent() = c + ) + } + + pragma[nomagic] + private predicate revPartialPathIntoReturn( + PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, + DataFlowCall call, RevPartialAccessPath ap, Configuration config + ) { + exists(NodeEx out | + mid.getNodeEx() = out and + viableReturnPosOutEx(call, pos, out) and + sc1 = TRevSummaryCtx1Some(pos) and + sc2 = TRevSummaryCtx2Some(ap) and + ap = mid.getAp() and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate revPartialPathFlowsThrough( + int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, + Configuration config + ) { + exists(PartialPathNodeRev mid, ParamNodeEx p | + mid.getNodeEx() = p and + p.getPosition() = pos and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + ap = mid.getAp() and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate revPartialPathThroughCallable0( + DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap, + Configuration config + ) { + exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 | + revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and + revPartialPathFlowsThrough(pos, sc1, sc2, ap, config) + ) + } + + pragma[nomagic] + private predicate revPartialPathThroughCallable( + PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config + ) { + exists(DataFlowCall call, int pos | + revPartialPathThroughCallable0(call, mid, pos, ap, config) and + node.asNode().(ArgNode).argumentOf(call, pos) + ) + } +} + +import FlowExploration + +private predicate partialFlow( + PartialPathNode source, PartialPathNode node, Configuration configuration +) { + source.getConfiguration() = configuration and + source.isFwdSource() and + node = source.getASuccessor+() +} + +private predicate revPartialFlow( + PartialPathNode node, PartialPathNode sink, Configuration configuration +) { + sink.getConfiguration() = configuration and + sink.isRevSink() and + node.getASuccessor+() = sink +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll new file mode 100644 index 00000000000..b3d03ea4e26 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll @@ -0,0 +1,4592 @@ +/** + * Provides an implementation of global (interprocedural) data flow. This file + * re-exports the local (intraprocedural) data flow analysis from + * `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed + * through the `Configuration` class. This file exists in several identical + * copies, allowing queries to use multiple `Configuration` classes that depend + * on each other without introducing mutual recursion among those configurations. + */ + +private import DataFlowImplCommon +private import DataFlowImplSpecific::Private +import DataFlowImplSpecific::Public + +/** + * A configuration of interprocedural data flow analysis. This defines + * sources, sinks, and any other configurable aspect of the analysis. Each + * use of the global data flow library must define its own unique extension + * of this abstract class. To create a configuration, extend this class with + * a subclass whose characteristic predicate is a unique singleton string. + * For example, write + * + * ```ql + * class MyAnalysisConfiguration extends DataFlow::Configuration { + * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } + * // Override `isSource` and `isSink`. + * // Optionally override `isBarrier`. + * // Optionally override `isAdditionalFlowStep`. + * } + * ``` + * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and + * the edges are those data-flow steps that preserve the value of the node + * along with any additional edges defined by `isAdditionalFlowStep`. + * Specifying nodes in `isBarrier` will remove those nodes from the graph, and + * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going + * and/or out-going edges from those nodes, respectively. + * + * Then, to query whether there is flow between some `source` and `sink`, + * write + * + * ```ql + * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) + * ``` + * + * Multiple configurations can coexist, but two classes extending + * `DataFlow::Configuration` should never depend on each other. One of them + * should instead depend on a `DataFlow2::Configuration`, a + * `DataFlow3::Configuration`, or a `DataFlow4::Configuration`. + */ +abstract class Configuration extends string { + bindingset[this] + Configuration() { any() } + + /** + * Holds if `source` is a relevant data flow source. + */ + abstract predicate isSource(Node source); + + /** + * Holds if `sink` is a relevant data flow sink. + */ + abstract predicate isSink(Node sink); + + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ + predicate isBarrier(Node node) { none() } + + /** Holds if data flow into `node` is prohibited. */ + predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isBarrierOut(Node node) { none() } + + /** Holds if data flow through nodes guarded by `guard` is prohibited. */ + predicate isBarrierGuard(BarrierGuard guard) { none() } + + /** + * Holds if the additional flow step from `node1` to `node2` must be taken + * into account in the analysis. + */ + predicate isAdditionalFlowStep(Node node1, Node node2) { none() } + + /** + * Holds if an arbitrary number of implicit read steps of content `c` may be + * taken at `node`. + */ + predicate allowImplicitRead(Node node, Content c) { none() } + + /** + * Gets the virtual dispatch branching limit when calculating field flow. + * This can be overridden to a smaller value to improve performance (a + * value of 0 disables field flow), or a larger value to get more results. + */ + int fieldFlowBranchLimit() { result = 2 } + + /** + * Holds if data may flow from `source` to `sink` for this configuration. + */ + predicate hasFlow(Node source, Node sink) { flowsTo(source, sink, this) } + + /** + * Holds if data may flow from `source` to `sink` for this configuration. + * + * The corresponding paths are generated from the end-points and the graph + * included in the module `PathGraph`. + */ + predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + + /** + * Holds if data may flow from some source to `sink` for this configuration. + */ + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } + + /** + * Holds if data may flow from some source to `sink` for this configuration. + */ + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } + + /** + * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` + * measured in approximate number of interprocedural steps. + */ + int explorationLimit() { none() } + + /** + * Holds if there is a partial data flow path from `source` to `node`. The + * approximate distance between `node` and the closest source is `dist` and + * is restricted to be less than or equal to `explorationLimit()`. This + * predicate completely disregards sink definitions. + * + * This predicate is intended for data-flow exploration and debugging and may + * perform poorly if the number of sources is too big and/or the exploration + * limit is set too high without using barriers. + * + * This predicate is disabled (has no results) by default. Override + * `explorationLimit()` with a suitable number to enable this predicate. + * + * To use this in a `path-problem` query, import the module `PartialPathGraph`. + */ + final predicate hasPartialFlow(PartialPathNode source, PartialPathNode node, int dist) { + partialFlow(source, node, this) and + dist = node.getSourceDistance() + } + + /** + * Holds if there is a partial data flow path from `node` to `sink`. The + * approximate distance between `node` and the closest sink is `dist` and + * is restricted to be less than or equal to `explorationLimit()`. This + * predicate completely disregards source definitions. + * + * This predicate is intended for data-flow exploration and debugging and may + * perform poorly if the number of sinks is too big and/or the exploration + * limit is set too high without using barriers. + * + * This predicate is disabled (has no results) by default. Override + * `explorationLimit()` with a suitable number to enable this predicate. + * + * To use this in a `path-problem` query, import the module `PartialPathGraph`. + * + * Note that reverse flow has slightly lower precision than the corresponding + * forward flow, as reverse flow disregards type pruning among other features. + */ + final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) { + revPartialFlow(node, sink, this) and + dist = node.getSinkDistance() + } +} + +/** + * This class exists to prevent mutual recursion between the user-overridden + * member predicates of `Configuration` and the rest of the data-flow library. + * Good performance cannot be guaranteed in the presence of such recursion, so + * it should be replaced by using more than one copy of the data flow library. + */ +abstract private class ConfigurationRecursionPrevention extends Configuration { + bindingset[this] + ConfigurationRecursionPrevention() { any() } + + override predicate hasFlow(Node source, Node sink) { + strictcount(Node n | this.isSource(n)) < 0 + or + strictcount(Node n | this.isSink(n)) < 0 + or + strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 + or + super.hasFlow(source, sink) + } +} + +private newtype TNodeEx = + TNodeNormal(Node n) or + TNodeImplicitRead(Node n, boolean hasRead) { + any(Configuration c).allowImplicitRead(n, _) and hasRead = [false, true] + } + +private class NodeEx extends TNodeEx { + string toString() { + result = this.asNode().toString() + or + exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]") + } + + Node asNode() { this = TNodeNormal(result) } + + predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) } + + Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) } + + pragma[nomagic] + private DataFlowCallable getEnclosingCallable0() { + nodeEnclosingCallable(this.projectToNode(), result) + } + + pragma[inline] + DataFlowCallable getEnclosingCallable() { + pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result) + } + + pragma[nomagic] + private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) } + + pragma[inline] + DataFlowType getDataFlowType() { + pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result) + } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +private class ArgNodeEx extends NodeEx { + ArgNodeEx() { this.asNode() instanceof ArgNode } +} + +private class ParamNodeEx extends NodeEx { + ParamNodeEx() { this.asNode() instanceof ParamNode } + + predicate isParameterOf(DataFlowCallable c, int i) { + this.asNode().(ParamNode).isParameterOf(c, i) + } + + int getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } +} + +private class RetNodeEx extends NodeEx { + RetNodeEx() { this.asNode() instanceof ReturnNodeExt } + + ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) } + + ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() } +} + +private predicate inBarrier(NodeEx node, Configuration config) { + exists(Node n | + node.asNode() = n and + config.isBarrierIn(n) and + config.isSource(n) + ) +} + +private predicate outBarrier(NodeEx node, Configuration config) { + exists(Node n | + node.asNode() = n and + config.isBarrierOut(n) and + config.isSink(n) + ) +} + +private predicate fullBarrier(NodeEx node, Configuration config) { + exists(Node n | node.asNode() = n | + config.isBarrier(n) + or + config.isBarrierIn(n) and + not config.isSource(n) + or + config.isBarrierOut(n) and + not config.isSink(n) + or + exists(BarrierGuard g | + config.isBarrierGuard(g) and + n = g.getAGuardedNode() + ) + ) +} + +pragma[nomagic] +private predicate sourceNode(NodeEx node, Configuration config) { config.isSource(node.asNode()) } + +pragma[nomagic] +private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) } + +/** + * Holds if data can flow in one local step from `node1` to `node2`. + */ +private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + simpleLocalFlowStepExt(n1, n2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) + ) + or + exists(Node n | + config.allowImplicitRead(n, _) and + node1.asNode() = n and + node2.isImplicitReadNode(n, false) + ) +} + +/** + * Holds if the additional step from `node1` to `node2` does not jump between callables. + */ +private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + config.isAdditionalFlowStep(n1, n2) and + getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) + ) + or + exists(Node n | + config.allowImplicitRead(n, _) and + node1.isImplicitReadNode(n, true) and + node2.asNode() = n + ) +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ +private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + jumpStepCached(n1, n2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) + ) +} + +/** + * Holds if the additional step from `node1` to `node2` jumps between callables. + */ +private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + config.isAdditionalFlowStep(n1, n2) and + getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) + ) +} + +private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { + read(node1.asNode(), c, node2.asNode()) + or + exists(Node n | + node2.isImplicitReadNode(n, true) and + node1.isImplicitReadNode(n, _) and + config.allowImplicitRead(n, c) + ) +} + +private predicate store( + NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config +) { + store(node1.asNode(), tc, node2.asNode(), contentType) and + read(_, tc.getContent(), _, config) +} + +pragma[nomagic] +private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) { + viableReturnPosOut(call, pos, out.asNode()) +} + +pragma[nomagic] +private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { + viableParamArg(call, p.asNode(), arg.asNode()) +} + +/** + * Holds if field flow should be used for the given configuration. + */ +private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 } + +private module Stage1 { + class ApApprox = Unit; + + class Ap = Unit; + + class ApOption = Unit; + + class Cc = boolean; + + /* Begin: Stage 1 logic. */ + /** + * Holds if `node` is reachable from a source in the configuration `config`. + * + * The Boolean `cc` records whether the node is reached through an + * argument in a call. + */ + predicate fwdFlow(NodeEx node, Cc cc, Configuration config) { + not fullBarrier(node, config) and + ( + sourceNode(node, config) and + cc = false + or + exists(NodeEx mid | + fwdFlow(mid, cc, config) and + localFlowStep(mid, node, config) + ) + or + exists(NodeEx mid | + fwdFlow(mid, cc, config) and + additionalLocalFlowStep(mid, node, config) + ) + or + exists(NodeEx mid | + fwdFlow(mid, _, config) and + jumpStep(mid, node, config) and + cc = false + ) + or + exists(NodeEx mid | + fwdFlow(mid, _, config) and + additionalJumpStep(mid, node, config) and + cc = false + ) + or + // store + exists(NodeEx mid | + useFieldFlow(config) and + fwdFlow(mid, cc, config) and + store(mid, _, node, _, config) and + not outBarrier(mid, config) + ) + or + // read + exists(Content c | + fwdFlowRead(c, node, cc, config) and + fwdFlowConsCand(c, config) and + not inBarrier(node, config) + ) + or + // flow into a callable + exists(NodeEx arg | + fwdFlow(arg, _, config) and + viableParamArgEx(_, node, arg) and + cc = true + ) + or + // flow out of a callable + exists(DataFlowCall call | + fwdFlowOut(call, node, false, config) and + cc = false + or + fwdFlowOutFromArg(call, node, config) and + fwdFlowIsEntered(call, cc, config) + ) + ) + } + + private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) } + + pragma[nomagic] + private predicate fwdFlowRead(Content c, NodeEx node, Cc cc, Configuration config) { + exists(NodeEx mid | + fwdFlow(mid, cc, config) and + read(mid, c, node, config) + ) + } + + /** + * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Content c, Configuration config) { + exists(NodeEx mid, NodeEx node, TypedContent tc | + not fullBarrier(node, config) and + useFieldFlow(config) and + fwdFlow(mid, _, config) and + store(mid, tc, node, _, config) and + c = tc.getContent() + ) + } + + pragma[nomagic] + private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc, Configuration config) { + exists(RetNodeEx ret | + fwdFlow(ret, cc, config) and + ret.getReturnPosition() = pos + ) + } + + pragma[nomagic] + private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) { + exists(ReturnPosition pos | + fwdFlowReturnPosition(pos, cc, config) and + viableReturnPosOutEx(call, pos, out) + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out, Configuration config) { + fwdFlowOut(call, out, true, config) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { + exists(ArgNodeEx arg | + fwdFlow(arg, cc, config) and + viableParamArgEx(call, _, arg) + ) + } + + /** + * Holds if `node` is part of a path from a source to a sink in the + * configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from + * the enclosing callable in order to reach a sink. + */ + pragma[nomagic] + predicate revFlow(NodeEx node, boolean toReturn, Configuration config) { + revFlow0(node, toReturn, config) and + fwdFlow(node, config) + } + + pragma[nomagic] + private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) { + fwdFlow(node, config) and + sinkNode(node, config) and + toReturn = false + or + exists(NodeEx mid | + localFlowStep(node, mid, config) and + revFlow(mid, toReturn, config) + ) + or + exists(NodeEx mid | + additionalLocalFlowStep(node, mid, config) and + revFlow(mid, toReturn, config) + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, _, config) and + toReturn = false + ) + or + exists(NodeEx mid | + additionalJumpStep(node, mid, config) and + revFlow(mid, _, config) and + toReturn = false + ) + or + // store + exists(Content c | + revFlowStore(c, node, toReturn, config) and + revFlowConsCand(c, config) + ) + or + // read + exists(NodeEx mid, Content c | + read(node, c, mid, config) and + fwdFlowConsCand(c, pragma[only_bind_into](config)) and + revFlow(mid, toReturn, pragma[only_bind_into](config)) + ) + or + // flow into a callable + exists(DataFlowCall call | + revFlowIn(call, node, false, config) and + toReturn = false + or + revFlowInToReturn(call, node, config) and + revFlowIsReturned(call, toReturn, config) + ) + or + // flow out of a callable + exists(ReturnPosition pos | + revFlowOut(pos, config) and + node.(RetNodeEx).getReturnPosition() = pos and + toReturn = true + ) + } + + /** + * Holds if `c` is the target of a read in the flow covered by `revFlow`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Content c, Configuration config) { + exists(NodeEx mid, NodeEx node | + fwdFlow(node, pragma[only_bind_into](config)) and + read(node, c, mid, config) and + fwdFlowConsCand(c, pragma[only_bind_into](config)) and + revFlow(pragma[only_bind_into](mid), _, pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate revFlowStore(Content c, NodeEx node, boolean toReturn, Configuration config) { + exists(NodeEx mid, TypedContent tc | + revFlow(mid, toReturn, pragma[only_bind_into](config)) and + fwdFlowConsCand(c, pragma[only_bind_into](config)) and + store(node, tc, mid, _, config) and + c = tc.getContent() + ) + } + + /** + * Holds if `c` is the target of both a read and a store in the flow covered + * by `revFlow`. + */ + private predicate revFlowIsReadAndStored(Content c, Configuration conf) { + revFlowConsCand(c, conf) and + revFlowStore(c, _, _, conf) + } + + pragma[nomagic] + predicate viableReturnPosOutNodeCandFwd1( + DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config + ) { + fwdFlowReturnPosition(pos, _, config) and + viableReturnPosOutEx(call, pos, out) + } + + pragma[nomagic] + private predicate revFlowOut(ReturnPosition pos, Configuration config) { + exists(DataFlowCall call, NodeEx out | + revFlow(out, _, config) and + viableReturnPosOutNodeCandFwd1(call, pos, out, config) + ) + } + + pragma[nomagic] + predicate viableParamArgNodeCandFwd1( + DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config + ) { + viableParamArgEx(call, p, arg) and + fwdFlow(arg, config) + } + + pragma[nomagic] + private predicate revFlowIn( + DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config + ) { + exists(ParamNodeEx p | + revFlow(p, toReturn, config) and + viableParamArgNodeCandFwd1(call, p, arg, config) + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg, Configuration config) { + revFlowIn(call, arg, true, config) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { + exists(NodeEx out | + revFlow(out, toReturn, config) and + fwdFlowOutFromArg(call, out, config) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Content c | + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + revFlow(node2, pragma[only_bind_into](config)) and + store(node1, tc, node2, contentType, config) and + c = tc.getContent() and + exists(ap1) + ) + } + + pragma[nomagic] + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + revFlow(n2, pragma[only_bind_into](config)) and + read(n1, c, n2, pragma[only_bind_into](config)) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } + + predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) { + revFlow(node, toReturn, config) and exists(returnAp) and exists(ap) + } + + private predicate throughFlowNodeCand(NodeEx node, Configuration config) { + revFlow(node, true, config) and + fwdFlow(node, true, config) and + not inBarrier(node, config) and + not outBarrier(node, config) + } + + /** Holds if flow may return from `callable`. */ + pragma[nomagic] + private predicate returnFlowCallableNodeCand( + DataFlowCallable callable, ReturnKindExt kind, Configuration config + ) { + exists(RetNodeEx ret | + throughFlowNodeCand(ret, config) and + callable = ret.getEnclosingCallable() and + kind = ret.getKind() + ) + } + + /** + * Holds if flow may enter through `p` and reach a return node making `p` a + * candidate for the origin of a summary. + */ + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(ReturnKindExt kind | + throughFlowNodeCand(p, config) and + returnFlowCallableNodeCand(c, kind, config) and + p.getEnclosingCallable() = c and + exists(ap) and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(ArgNodeEx arg, boolean toReturn | + revFlow(arg, toReturn, config) and + revFlowInToReturn(call, arg, config) and + revFlowIsReturned(call, toReturn, config) + ) + } + + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, config)) and + fields = count(Content f0 | fwdFlowConsCand(f0, config)) and + conscand = -1 and + tuples = count(NodeEx n, boolean b | fwdFlow(n, b, config)) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, config)) and + fields = count(Content f0 | revFlowConsCand(f0, config)) and + conscand = -1 and + tuples = count(NodeEx n, boolean b | revFlow(n, b, config)) + } + /* End: Stage 1 logic. */ +} + +pragma[noinline] +private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) { + Stage1::revFlow(node2, config) and + localFlowStep(node1, node2, config) +} + +pragma[noinline] +private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) { + Stage1::revFlow(node2, config) and + additionalLocalFlowStep(node1, node2, config) +} + +pragma[nomagic] +private predicate viableReturnPosOutNodeCand1( + DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config +) { + Stage1::revFlow(out, config) and + Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out, config) +} + +/** + * Holds if data can flow out of `call` from `ret` to `out`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. + */ +pragma[nomagic] +private predicate flowOutOfCallNodeCand1( + DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config +) { + viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) +} + +pragma[nomagic] +private predicate viableParamArgNodeCand1( + DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config +) { + Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and + Stage1::revFlow(arg, config) +} + +/** + * Holds if data can flow into `call` and that this step is part of a + * path from a source to a sink. + */ +pragma[nomagic] +private predicate flowIntoCallNodeCand1( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, Configuration config +) { + viableParamArgNodeCand1(call, p, arg, config) and + Stage1::revFlow(p, config) and + not outBarrier(arg, config) and + not inBarrier(p, config) +} + +/** + * Gets the amount of forward branching on the origin of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ +private int branch(NodeEx n1, Configuration conf) { + result = + strictcount(NodeEx n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + ) +} + +/** + * Gets the amount of backward branching on the target of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ +private int join(NodeEx n2, Configuration conf) { + result = + strictcount(NodeEx n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + ) +} + +/** + * Holds if data can flow out of `call` from `ret` to `out`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. The + * `allowsFieldFlow` flag indicates whether the branching is within the limit + * specified by the configuration. + */ +pragma[nomagic] +private predicate flowOutOfCallNodeCand1( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config +) { + flowOutOfCallNodeCand1(call, ret, out, config) and + exists(int b, int j | + b = branch(ret, config) and + j = join(out, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() + then allowsFieldFlow = true + else allowsFieldFlow = false + ) +} + +/** + * Holds if data can flow into `call` and that this step is part of a + * path from a source to a sink. The `allowsFieldFlow` flag indicates whether + * the branching is within the limit specified by the configuration. + */ +pragma[nomagic] +private predicate flowIntoCallNodeCand1( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config +) { + flowIntoCallNodeCand1(call, arg, p, config) and + exists(int b, int j | + b = branch(arg, config) and + j = join(p, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() + then allowsFieldFlow = true + else allowsFieldFlow = false + ) +} + +private module Stage2 { + module PrevStage = Stage1; + + class ApApprox = PrevStage::Ap; + + class Ap = boolean; + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + private ApApprox getApprox(Ap ap) { any() } + + private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + private Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + class Cc = CallContext; + + class CcCall = CallContextCall; + + class CcNoCall = CallContextNoCall; + + Cc ccNone() { result instanceof CallContextAny } + + private class LocalCc = Unit; + + bindingset[call, c, outercc] + private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + + bindingset[call, c, innercc] + private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + checkCallContextReturn(innercc, c, call) and + if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() + } + + bindingset[node, cc, config] + private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() } + + private predicate localStep( + NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc + ) { + ( + preservesValue = true and + localFlowStepNodeCand1(node1, node2, config) + or + preservesValue = false and + additionalLocalFlowStepNodeCand1(node1, node2, config) + ) and + exists(ap) and + exists(lcc) + } + + private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + + private predicate flowIntoCall = flowIntoCallNodeCand1/5; + + bindingset[ap, contentType] + private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } + + /* Begin: Stage 2 logic. */ + private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) { + PrevStage::revFlow(node, _, _, apa, config) + } + + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) + } + + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { + flowCand(node, _, config) and + sourceNode(node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, Ap ap0, LocalCc localCc | + fwdFlow(mid, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc, config) + | + localStep(mid, node, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, node, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and + flowCand(node, _, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and + flowCand(node, _, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } + + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, getApprox(ap1), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } + + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config + ) { + fwdFlow(node1, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) { + revFlow0(node, toReturn, returnAp, ap, config) and + fwdFlow(node, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, _, _, ap, config) and + sinkNode(node, config) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid | + localStep(node, mid, true, _, config, _) and + revFlow(mid, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and + localStep(node, mid, false, _, config, _) and + revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, _, _, ap, config) and + toReturn = true and + if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn, + ApOption returnAp, Configuration config + ) { + revFlow(mid, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, CcCall ccc | + revFlowOut(call, ret, toReturn, returnAp, ap, config) and + fwdFlow(ret, ccc, apSome(_), ap, config) and + ccc.matchesCall(call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + store(node1, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0), + pragma[only_bind_into](config)) and + fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config)) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config)) + } + /* End: Stage 2 logic. */ +} + +pragma[nomagic] +private predicate flowOutOfCallNodeCand2( + DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config +) { + flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + Stage2::revFlow(node2, pragma[only_bind_into](config)) and + Stage2::revFlow(node1, pragma[only_bind_into](config)) +} + +pragma[nomagic] +private predicate flowIntoCallNodeCand2( + DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, + Configuration config +) { + flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + Stage2::revFlow(node2, pragma[only_bind_into](config)) and + Stage2::revFlow(node1, pragma[only_bind_into](config)) +} + +private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends NodeEx { + FlowCheckNode() { + castNode(this.asNode()) or + clearsContentCached(this.asNode(), _) + } + } + + /** + * Holds if `node` can be the first node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + predicate localFlowEntry(NodeEx node, Configuration config) { + Stage2::revFlow(node, config) and + ( + sourceNode(node, config) or + jumpStep(_, node, config) or + additionalJumpStep(_, node, config) or + node instanceof ParamNodeEx or + node.asNode() instanceof OutNodeExt or + store(_, _, node, _, config) or + read(_, _, node, config) or + node instanceof FlowCheckNode + ) + } + + /** + * Holds if `node` can be the last node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + private predicate localFlowExit(NodeEx node, Configuration config) { + exists(NodeEx next | Stage2::revFlow(next, config) | + jumpStep(node, next, config) or + additionalJumpStep(node, next, config) or + flowIntoCallNodeCand1(_, node, next, config) or + flowOutOfCallNodeCand1(_, node, next, config) or + store(node, _, next, _, config) or + read(node, _, next, config) + ) + or + node instanceof FlowCheckNode + or + sinkNode(node, config) + } + + pragma[noinline] + private predicate additionalLocalFlowStepNodeCand2( + NodeEx node1, NodeEx node2, Configuration config + ) { + additionalLocalFlowStepNodeCand1(node1, node2, config) and + Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and + Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config)) + } + + /** + * Holds if the local path from `node1` to `node2` is a prefix of a maximal + * subsequence of local flow steps in a dataflow path. + * + * This is the transitive closure of `[additional]localFlowStep` beginning + * at `localFlowEntry`. + */ + pragma[nomagic] + private predicate localFlowStepPlus( + NodeEx node1, NodeEx node2, boolean preservesValue, DataFlowType t, Configuration config, + LocalCallContext cc + ) { + not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and + ( + localFlowEntry(node1, pragma[only_bind_into](config)) and + ( + localFlowStepNodeCand1(node1, node2, config) and + preservesValue = true and + t = node1.getDataFlowType() // irrelevant dummy value + or + additionalLocalFlowStepNodeCand2(node1, node2, config) and + preservesValue = false and + t = node2.getDataFlowType() + ) and + node1 != node2 and + cc.relevantFor(node1.getEnclosingCallable()) and + not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and + Stage2::revFlow(node2, pragma[only_bind_into](config)) + or + exists(NodeEx mid | + localFlowStepPlus(node1, mid, preservesValue, t, pragma[only_bind_into](config), cc) and + localFlowStepNodeCand1(mid, node2, config) and + not mid instanceof FlowCheckNode and + Stage2::revFlow(node2, pragma[only_bind_into](config)) + ) + or + exists(NodeEx mid | + localFlowStepPlus(node1, mid, _, _, pragma[only_bind_into](config), cc) and + additionalLocalFlowStepNodeCand2(mid, node2, config) and + not mid instanceof FlowCheckNode and + preservesValue = false and + t = node2.getDataFlowType() and + Stage2::revFlow(node2, pragma[only_bind_into](config)) + ) + ) + } + + /** + * Holds if `node1` can step to `node2` in one or more local steps and this + * path can occur as a maximal subsequence of local steps in a dataflow path. + */ + pragma[nomagic] + predicate localFlowBigStep( + NodeEx node1, NodeEx node2, boolean preservesValue, AccessPathFrontNil apf, + Configuration config, LocalCallContext callContext + ) { + localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and + localFlowExit(node2, config) + } +} + +private import LocalFlowBigStep + +private module Stage3 { + module PrevStage = Stage2; + + class ApApprox = PrevStage::Ap; + + class Ap = AccessPathFront; + + class ApNil = AccessPathFrontNil; + + private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + + private ApNil getApNil(NodeEx node) { + PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) + } + + bindingset[tc, tail] + private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + + pragma[noinline] + private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + + class ApOption = AccessPathFrontOption; + + ApOption apNone() { result = TAccessPathFrontNone() } + + ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } + + class Cc = boolean; + + class CcCall extends Cc { + CcCall() { this = true } + + /** Holds if this call context may be `call`. */ + predicate matchesCall(DataFlowCall call) { any() } + } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + private class LocalCc = Unit; + + bindingset[call, c, outercc] + private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } + + bindingset[node, cc, config] + private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() } + + private predicate localStep( + NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc + ) { + localFlowBigStep(node1, node2, preservesValue, ap, config, _) and exists(lcc) + } + + private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + + private predicate flowIntoCall = flowIntoCallNodeCand2/5; + + pragma[nomagic] + private predicate clear(NodeEx node, Ap ap) { ap.isClearedAt(node.asNode()) } + + pragma[nomagic] + private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } + + bindingset[node, ap] + private predicate filter(NodeEx node, Ap ap) { + not clear(node, ap) and + if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() + } + + bindingset[ap, contentType] + private predicate typecheckStore(Ap ap, DataFlowType contentType) { + // We need to typecheck stores here, since reverse flow through a getter + // might have a different type here compared to inside the getter. + compatibleTypes(ap.getType(), contentType) + } + + /* Begin: Stage 3 logic. */ + private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) { + PrevStage::revFlow(node, _, _, apa, config) + } + + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + exists(ApApprox apa0 | + apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0) + ) + } + + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) + } + + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { + fwdFlow0(node, cc, argAp, ap, config) and + flowCand(node, unbindApa(getApprox(ap)), config) and + filter(node, ap) + } + + pragma[nomagic] + private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { + flowCand(node, _, config) and + sourceNode(node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, Ap ap0, LocalCc localCc | + fwdFlow(mid, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc, config) + | + localStep(mid, node, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, node, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and + flowCand(node, _, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and + flowCand(node, _, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } + + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } + + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config + ) { + fwdFlow(node1, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) { + revFlow0(node, toReturn, returnAp, ap, config) and + fwdFlow(node, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, _, _, ap, config) and + sinkNode(node, config) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid | + localStep(node, mid, true, _, config, _) and + revFlow(mid, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and + localStep(node, mid, false, _, config, _) and + revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, _, _, ap, config) and + toReturn = true and + if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn, + ApOption returnAp, Configuration config + ) { + revFlow(mid, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, CcCall ccc | + revFlowOut(call, ret, toReturn, returnAp, ap, config) and + fwdFlow(ret, ccc, apSome(_), ap, config) and + ccc.matchesCall(call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + store(node1, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0), + pragma[only_bind_into](config)) and + fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config)) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config)) + } + /* End: Stage 3 logic. */ +} + +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + Stage3::revFlow(node, true, _, apf, config) and + Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and + nodes = + strictcount(NodeEx n | + Stage3::revFlow(n, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() + ) +} + +private newtype TAccessPathApprox = + TNil(DataFlowType t) or + TConsNil(TypedContent tc, DataFlowType t) { + Stage3::consCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + Stage3::consCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) + } + +/** + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPathApprox extends TAccessPathApprox { + abstract string toString(); + + abstract TypedContent getHead(); + + abstract int len(); + + abstract DataFlowType getType(); + + abstract AccessPathFront getFront(); + + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); +} + +private class AccessPathApproxNil extends AccessPathApprox, TNil { + private DataFlowType t; + + AccessPathApproxNil() { this = TNil(t) } + + override string toString() { result = concat(": " + ppReprType(t)) } + + override TypedContent getHead() { none() } + + override int len() { result = 0 } + + override DataFlowType getType() { result = t } + + override AccessPathFront getFront() { result = TFrontNil(t) } + + override AccessPathApprox pop(TypedContent head) { none() } +} + +abstract private class AccessPathApproxCons extends AccessPathApprox { } + +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private TypedContent tc; + private DataFlowType t; + + AccessPathApproxConsNil() { this = TConsNil(tc, t) } + + override string toString() { + // The `concat` becomes "" if `ppReprType` has no result. + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) + } + + override TypedContent getHead() { result = tc } + + override int len() { result = 1 } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } +} + +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private TypedContent tc1; + private TypedContent tc2; + private int len; + + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } + + override string toString() { + if len = 2 + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc1 } + + override int len() { result = len } + + override DataFlowType getType() { result = tc1.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc1) } + + override AccessPathApprox pop(TypedContent head) { + head = tc1 and + ( + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + Stage3::consCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) + ) + } +} + +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } + +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } + +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) + +private class AccessPathApproxOption extends TAccessPathApproxOption { + string toString() { + this = TAccessPathApproxNone() and result = "<none>" + or + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) + } +} + +private module Stage4 { + module PrevStage = Stage3; + + class ApApprox = PrevStage::Ap; + + class Ap = AccessPathApprox; + + class ApNil = AccessPathApproxNil; + + private ApApprox getApprox(Ap ap) { result = ap.getFront() } + + private ApNil getApNil(NodeEx node) { + PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) + } + + bindingset[tc, tail] + private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + + pragma[noinline] + private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + + class ApOption = AccessPathApproxOption; + + ApOption apNone() { result = TAccessPathApproxNone() } + + ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } + + class Cc = CallContext; + + class CcCall = CallContextCall; + + class CcNoCall = CallContextNoCall; + + Cc ccNone() { result instanceof CallContextAny } + + private class LocalCc = LocalCallContext; + + bindingset[call, c, outercc] + private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } + + bindingset[call, c, innercc] + private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + checkCallContextReturn(innercc, c, call) and + if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() + } + + bindingset[node, cc, config] + private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { + localFlowEntry(node, config) and + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + private predicate localStep( + NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc + ) { + localFlowBigStep(node1, node2, preservesValue, ap.getFront(), config, lcc) + } + + pragma[nomagic] + private predicate flowOutOfCall( + DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and + PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate flowIntoCall( + DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, + Configuration config + ) { + flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and + PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config)) + } + + bindingset[node, ap] + private predicate filter(NodeEx node, Ap ap) { any() } + + // Type checking is not necessary here as it has already been done in stage 3. + bindingset[ap, contentType] + private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } + + /* Begin: Stage 4 logic. */ + private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) { + PrevStage::revFlow(node, _, _, apa, config) + } + + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + exists(ApApprox apa0 | + apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0) + ) + } + + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) + } + + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { + fwdFlow0(node, cc, argAp, ap, config) and + flowCand(node, unbindApa(getApprox(ap)), config) and + filter(node, ap) + } + + pragma[nomagic] + private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) { + flowCand(node, _, config) and + sourceNode(node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, Ap ap0, LocalCc localCc | + fwdFlow(mid, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc, config) + | + localStep(mid, node, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, node, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and + flowCand(node, _, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and + flowCand(node, _, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } + + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } + + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config + ) { + fwdFlow(node1, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) { + revFlow0(node, toReturn, returnAp, ap, config) and + fwdFlow(node, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, _, _, ap, config) and + sinkNode(node, config) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid | + localStep(node, mid, true, _, config, _) and + revFlow(mid, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and + localStep(node, mid, false, _, config, _) and + revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, _, _, ap, config) and + toReturn = true and + if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn, + ApOption returnAp, Configuration config + ) { + revFlow(mid, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, CcCall ccc | + revFlowOut(call, ret, toReturn, returnAp, ap, config) and + fwdFlow(ret, ccc, apSome(_), ap, config) and + ccc.matchesCall(call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + store(node1, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0), + pragma[only_bind_into](config)) and + fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config)) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config)) + } + /* End: Stage 4 logic. */ +} + +bindingset[conf, result] +private Configuration unbindConf(Configuration conf) { + exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) +} + +private predicate nodeMayUseSummary(NodeEx n, AccessPathApprox apa, Configuration config) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + Stage4::parameterMayFlowThrough(_, c, apa, _) and + Stage4::revFlow(n, true, _, apa0, config) and + Stage4::fwdFlow(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and + n.getEnclosingCallable() = c + ) +} + +private newtype TSummaryCtx = + TSummaryCtxNone() or + TSummaryCtxSome(ParamNodeEx p, AccessPath ap) { + Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) + } + +/** + * A context for generating flow summaries. This represents flow entry through + * a specific parameter with an access path of a specific shape. + * + * Summaries are only created for parameters that may flow through. + */ +abstract private class SummaryCtx extends TSummaryCtx { + abstract string toString(); +} + +/** A summary context from which no flow summary can be generated. */ +private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { + override string toString() { result = "<none>" } +} + +/** A summary context from which a flow summary can be generated. */ +private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { + private ParamNodeEx p; + private AccessPath ap; + + SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } + + int getParameterPos() { p.isParameterOf(_, result) } + + ParamNodeEx getParamNode() { result = p } + + override string toString() { result = p + ": " + ap } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + p.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = + strictcount(NodeEx n | + Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + Stage4::consCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } + +private newtype TPathNode = + TPathNodeMid(NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + // A PathNode is introduced by a source ... + Stage4::revFlow(node, config) and + sourceNode(node, config) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + ap = TAccessPathNil(node.getDataFlowType()) + or + // ... or a step from an existing PathNode to another node. + exists(PathNodeMid mid | + pathStep(mid, node, cc, sc, ap) and + pragma[only_bind_into](config) = mid.getConfiguration() and + Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config)) + ) + } or + TPathNodeSink(NodeEx node, Configuration config) { + sinkNode(node, pragma[only_bind_into](config)) and + Stage4::revFlow(node, pragma[only_bind_into](config)) and + ( + // A sink that is also a source ... + sourceNode(node, config) + or + // ... or a sink that can be reached from a source + exists(PathNodeMid mid | + pathStep(mid, node, _, _, TAccessPathNil(_)) and + pragma[only_bind_into](config) = mid.getConfiguration() + ) + ) + } + +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl(boolean needsSuffix) { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + needsSuffix = false and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + Stage4::consCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } +} + +/** + * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. + * Only those `PathNode`s that are reachable from a source are generated. + */ +class PathNode extends TPathNode { + /** Gets a textual representation of this element. */ + string toString() { none() } + + /** + * Gets a textual representation of this element, including a textual + * representation of the call context. + */ + string toStringWithContext() { none() } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + none() + } + + /** Gets the underlying `Node`. */ + final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result } + + /** Gets the associated configuration. */ + Configuration getConfiguration() { none() } + + private PathNode getASuccessorIfHidden() { + this.(PathNodeImpl).isHidden() and + result = this.(PathNodeImpl).getASuccessorImpl() + } + + /** Gets a successor of this node, if any. */ + final PathNode getASuccessor() { + result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and + not this.(PathNodeImpl).isHidden() and + not result.(PathNodeImpl).isHidden() + } + + /** Holds if this node is a source. */ + predicate isSource() { none() } +} + +abstract private class PathNodeImpl extends PathNode { + abstract PathNode getASuccessorImpl(); + + abstract NodeEx getNodeEx(); + + predicate isHidden() { + hiddenNode(this.getNodeEx().asNode()) and + not this.isSource() and + not this instanceof PathNodeSink + or + this.getNodeEx() instanceof TNodeImplicitRead + } + + private string ppAp() { + this instanceof PathNodeSink and result = "" + or + exists(string s | s = this.(PathNodeMid).getAp().toString() | + if s = "" then result = "" else result = " " + s + ) + } + + private string ppCtx() { + this instanceof PathNodeSink and result = "" + or + result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" + } + + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } + + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +/** Holds if `n` can reach a sink. */ +private predicate directReach(PathNode n) { + n instanceof PathNodeSink or directReach(n.getASuccessor()) +} + +/** Holds if `n` can reach a sink or is used in a subpath. */ +private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) } + +/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */ +private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) } + +private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2) + +/** + * Provides the query predicates needed to include a graph in a path-problem query. + */ +module PathGraph { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) } + + /** Holds if `n` is a node in the graph of data flow path explanations. */ + query predicate nodes(PathNode n, string key, string val) { + reach(n) and key = "semmle.label" and val = n.toString() + } + + query predicate subpaths = Subpaths::subpaths/4; +} + +/** + * An intermediate flow graph node. This is a triple consisting of a `Node`, + * a `CallContext`, and a `Configuration`. + */ +private class PathNodeMid extends PathNodeImpl, TPathNodeMid { + NodeEx node; + CallContext cc; + SummaryCtx sc; + AccessPath ap; + Configuration config; + + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + + override NodeEx getNodeEx() { result = node } + + CallContext getCallContext() { result = cc } + + SummaryCtx getSummaryCtx() { result = sc } + + AccessPath getAp() { result = ap } + + override Configuration getConfiguration() { result = config } + + private PathNodeMid getSuccMid() { + pathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx(), + result.getAp()) and + result.getConfiguration() = unbindConf(this.getConfiguration()) + } + + override PathNodeImpl getASuccessorImpl() { + // an intermediate step to another intermediate node + result = this.getSuccMid() + or + // a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges + exists(PathNodeMid mid, PathNodeSink sink | + mid = this.getSuccMid() and + mid.getNodeEx() = sink.getNodeEx() and + mid.getAp() instanceof AccessPathNil and + sink.getConfiguration() = unbindConf(mid.getConfiguration()) and + result = sink + ) + } + + override predicate isSource() { + sourceNode(node, config) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + ap instanceof AccessPathNil + } +} + +/** + * A flow graph node corresponding to a sink. This is disjoint from the + * intermediate nodes in order to uniquely correspond to a given sink by + * excluding the `CallContext`. + */ +private class PathNodeSink extends PathNodeImpl, TPathNodeSink { + NodeEx node; + Configuration config; + + PathNodeSink() { this = TPathNodeSink(node, config) } + + override NodeEx getNodeEx() { result = node } + + override Configuration getConfiguration() { result = config } + + override PathNode getASuccessorImpl() { none() } + + override predicate isSource() { sourceNode(node, config) } +} + +/** + * Holds if data may flow from `mid` to `node`. The last step in or out of + * a callable is recorded by `cc`. + */ +private predicate pathStep( + PathNodeMid mid, NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap +) { + exists(AccessPath ap0, NodeEx midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNodeEx() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + ap = mid.getAp() + or + additionalJumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(node.getDataFlowType()) + or + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() + or + pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp() + or + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone + or + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() +} + +pragma[nomagic] +private predicate pathReadStep( + PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc +) { + ap0 = mid.getAp() and + tc = ap0.getHead() and + Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and + cc = mid.getCallContext() +} + +pragma[nomagic] +private predicate pathStoreStep( + PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc +) { + ap0 = mid.getAp() and + Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and + cc = mid.getCallContext() +} + +private predicate pathOutOfCallable0( + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config +) { + pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and + innercc = mid.getCallContext() and + innercc instanceof CallContextNoCall and + apa = mid.getAp().getApprox() and + config = mid.getConfiguration() +} + +pragma[nomagic] +private predicate pathOutOfCallable1( + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, + Configuration config +) { + exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | + pathOutOfCallable0(mid, pos, innercc, apa, config) and + c = pos.getCallable() and + kind = pos.getKind() and + resolveReturn(innercc, c, call) + | + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) +} + +pragma[noinline] +private NodeEx getAnOutNodeFlow( + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config +) { + result.asNode() = kind.getAnOutNode(call) and + Stage4::revFlow(result, _, _, apa, config) +} + +/** + * Holds if data may flow from `mid` to `out`. The last step of this path + * is a return from a callable and is recorded by `cc`, if needed. + */ +pragma[noinline] +private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) { + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) + ) +} + +/** + * Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`. + */ +pragma[noinline] +private predicate pathIntoArg( + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa, + Configuration config +) { + exists(ArgNode arg | + arg = mid.getNodeEx().asNode() and + cc = mid.getCallContext() and + arg.argumentOf(call, i) and + ap = mid.getAp() and + apa = ap.getApprox() and + config = mid.getConfiguration() + ) +} + +pragma[noinline] +private predicate parameterCand( + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config +) { + exists(ParamNodeEx p | + Stage4::revFlow(p, _, _, apa, config) and + p.isParameterOf(callable, i) + ) +} + +pragma[nomagic] +private predicate pathIntoCallable0( + PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, + AccessPath ap, Configuration config +) { + exists(AccessPathApprox apa | + pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa), + pragma[only_bind_into](config)) and + callable = resolveCall(call, outercc) and + parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) + ) +} + +/** + * Holds if data may flow from `mid` to `p` through `call`. The contexts + * before and after entering the callable are `outercc` and `innercc`, + * respectively. + */ +pragma[nomagic] +private predicate pathIntoCallable( + PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + DataFlowCall call, Configuration config +) { + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +/** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ +pragma[nomagic] +private predicate paramFlowsThrough( + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config +) { + exists(PathNodeMid mid, RetNodeEx ret, int pos | + mid.getNodeEx() = ret and + kind = ret.getKind() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + config = mid.getConfiguration() and + ap = mid.getAp() and + apa = ap.getApprox() and + pos = sc.getParameterPos() and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) + ) +} + +pragma[nomagic] +private predicate pathThroughCallable0( + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa, Configuration config +) { + exists(CallContext innercc, SummaryCtx sc | + pathIntoCallable(mid, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, innercc, sc, ap, apa, config) + ) +} + +/** + * Holds if data may flow from `mid` through a callable to the node `out`. + * The context `cc` is restored to its value prior to entering the callable. + */ +pragma[noinline] +private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) + ) +} + +private module Subpaths { + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by + * `kind`, `sc`, `apout`, and `innercc`. + */ + pragma[nomagic] + private predicate subpaths01( + PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, + NodeEx out, AccessPath apout + ) { + exists(Configuration config | + pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, innercc, sc, _, config) and + paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) + ) + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by + * `kind`, `sc`, `apout`, and `innercc`. + */ + pragma[nomagic] + private predicate subpaths02( + PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, + NodeEx out, AccessPath apout + ) { + subpaths01(arg, par, sc, innercc, kind, out, apout) and + out.asNode() = kind.getAnOutNode(_) + } + + pragma[nomagic] + private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple. + */ + pragma[nomagic] + private predicate subpaths03( + PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout + ) { + exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode | + subpaths02(arg, par, sc, innercc, kind, out, apout) and + ret.getNodeEx() = retnode and + kind = retnode.getKind() and + innercc = ret.getCallContext() and + sc = ret.getSummaryCtx() and + ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and + apout = ret.getAp() and + not ret.isHidden() + ) + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through + * a subpath between `par` and `ret` with the connecting edges `arg -> par` and + * `ret -> out` is summarized as the edge `arg -> out`. + */ + predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) { + exists(ParamNodeEx p, NodeEx o, AccessPath apout | + pragma[only_bind_into](arg).getASuccessor() = par and + pragma[only_bind_into](arg).getASuccessor() = out and + subpaths03(arg, p, ret, o, apout) and + par.getNodeEx() = p and + out.getNodeEx() = o and + out.getAp() = apout + ) + } + + /** + * Holds if `n` can reach a return node in a summarized subpath. + */ + predicate retReach(PathNode n) { + subpaths(_, _, n, _) + or + exists(PathNode mid | + retReach(mid) and + n.getASuccessor() = mid and + not subpaths(_, mid, _, _) + ) + } +} + +/** + * Holds if data can flow (inter-procedurally) from `source` to `sink`. + * + * Will only have results if `configuration` has non-empty sources and + * sinks. + */ +private predicate flowsTo( + PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration +) { + flowsource.isSource() and + flowsource.getConfiguration() = configuration and + flowsource.(PathNodeImpl).getNodeEx().asNode() = source and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.getNodeEx().asNode() = sink +} + +/** + * Holds if data can flow (inter-procedurally) from `source` to `sink`. + * + * Will only have results if `configuration` has non-empty sources and + * sinks. + */ +predicate flowsTo(Node source, Node sink, Configuration configuration) { + flowsTo(_, _, source, sink, configuration) +} + +private predicate finalStats(boolean fwd, int nodes, int fields, int conscand, int tuples) { + fwd = true and + nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and + fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and + conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and + tuples = count(PathNode pn) + or + fwd = false and + nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and + fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and + conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and + tuples = count(PathNode pn | reach(pn)) +} + +/** + * INTERNAL: Only for debugging. + * + * Calculates per-stage metrics for data flow. + */ +predicate stageStats( + int n, string stage, int nodes, int fields, int conscand, int tuples, Configuration config +) { + stage = "1 Fwd" and n = 10 and Stage1::stats(true, nodes, fields, conscand, tuples, config) + or + stage = "1 Rev" and n = 15 and Stage1::stats(false, nodes, fields, conscand, tuples, config) + or + stage = "2 Fwd" and n = 20 and Stage2::stats(true, nodes, fields, conscand, tuples, config) + or + stage = "2 Rev" and n = 25 and Stage2::stats(false, nodes, fields, conscand, tuples, config) + or + stage = "3 Fwd" and n = 30 and Stage3::stats(true, nodes, fields, conscand, tuples, config) + or + stage = "3 Rev" and n = 35 and Stage3::stats(false, nodes, fields, conscand, tuples, config) + or + stage = "4 Fwd" and n = 40 and Stage4::stats(true, nodes, fields, conscand, tuples, config) + or + stage = "4 Rev" and n = 45 and Stage4::stats(false, nodes, fields, conscand, tuples, config) + or + stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, tuples) + or + stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, tuples) +} + +private module FlowExploration { + private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2, Configuration config) { + exists(NodeEx node1, NodeEx node2 | + jumpStep(node1, node2, config) + or + additionalJumpStep(node1, node2, config) + or + // flow into callable + viableParamArgEx(_, node2, node1) + or + // flow out of a callable + viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2) + | + c1 = node1.getEnclosingCallable() and + c2 = node2.getEnclosingCallable() and + c1 != c2 + ) + } + + private predicate interestingCallableSrc(DataFlowCallable c, Configuration config) { + exists(Node n | config.isSource(n) and c = getNodeEnclosingCallable(n)) + or + exists(DataFlowCallable mid | + interestingCallableSrc(mid, config) and callableStep(mid, c, config) + ) + } + + private predicate interestingCallableSink(DataFlowCallable c, Configuration config) { + exists(Node n | config.isSink(n) and c = getNodeEnclosingCallable(n)) + or + exists(DataFlowCallable mid | + interestingCallableSink(mid, config) and callableStep(c, mid, config) + ) + } + + private newtype TCallableExt = + TCallable(DataFlowCallable c, Configuration config) { + interestingCallableSrc(c, config) or + interestingCallableSink(c, config) + } or + TCallableSrc() or + TCallableSink() + + private predicate callableExtSrc(TCallableSrc src) { any() } + + private predicate callableExtSink(TCallableSink sink) { any() } + + private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) { + exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config | + callableStep(c1, c2, config) and + ce1 = TCallable(c1, pragma[only_bind_into](config)) and + ce2 = TCallable(c2, pragma[only_bind_into](config)) + ) + or + exists(Node n, Configuration config | + ce1 = TCallableSrc() and + config.isSource(n) and + ce2 = TCallable(getNodeEnclosingCallable(n), config) + ) + or + exists(Node n, Configuration config | + ce2 = TCallableSink() and + config.isSink(n) and + ce1 = TCallable(getNodeEnclosingCallable(n), config) + ) + } + + private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) { + callableExtStepFwd(ce2, ce1) + } + + private int distSrcExt(TCallableExt c) = + shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result) + + private int distSinkExt(TCallableExt c) = + shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result) + + private int distSrc(DataFlowCallable c, Configuration config) { + result = distSrcExt(TCallable(c, config)) - 1 + } + + private int distSink(DataFlowCallable c, Configuration config) { + result = distSinkExt(TCallable(c, config)) - 1 + } + + private newtype TPartialAccessPath = + TPartialNil(DataFlowType t) or + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } + + /** + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first + * element of the list and its length are tracked. If data flows from a source to + * a given node with a given `AccessPath`, this indicates the sequence of + * dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ + private class PartialAccessPath extends TPartialAccessPath { + abstract string toString(); + + TypedContent getHead() { this = TPartialCons(result, _) } + + int len() { + this = TPartialNil(_) and result = 0 + or + this = TPartialCons(_, result) + } + + DataFlowType getType() { + this = TPartialNil(result) + or + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) + } + } + + private class PartialAccessPathNil extends PartialAccessPath, TPartialNil { + override string toString() { + exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t))) + } + } + + private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { + override string toString() { + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + ) + } + } + + private newtype TRevPartialAccessPath = + TRevPartialNil() or + TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] } + + /** + * Conceptually a list of `Content`s, but only the first + * element of the list and its length are tracked. + */ + private class RevPartialAccessPath extends TRevPartialAccessPath { + abstract string toString(); + + Content getHead() { this = TRevPartialCons(result, _) } + + int len() { + this = TRevPartialNil() and result = 0 + or + this = TRevPartialCons(_, result) + } + } + + private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil { + override string toString() { result = "" } + } + + private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons { + override string toString() { + exists(Content c, int len | this = TRevPartialCons(c, len) | + if len = 1 + then result = "[" + c.toString() + "]" + else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" + ) + } + } + + private newtype TSummaryCtx1 = + TSummaryCtx1None() or + TSummaryCtx1Param(ParamNodeEx p) + + private newtype TSummaryCtx2 = + TSummaryCtx2None() or + TSummaryCtx2Some(PartialAccessPath ap) + + private newtype TRevSummaryCtx1 = + TRevSummaryCtx1None() or + TRevSummaryCtx1Some(ReturnPosition pos) + + private newtype TRevSummaryCtx2 = + TRevSummaryCtx2None() or + TRevSummaryCtx2Some(RevPartialAccessPath ap) + + private newtype TPartialPathNode = + TPartialPathNodeFwd( + NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, + Configuration config + ) { + sourceNode(node, config) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + ap = TPartialNil(node.getDataFlowType()) and + not fullBarrier(node, config) and + exists(config.explorationLimit()) + or + partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and + distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit() + } or + TPartialPathNodeRev( + NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap, + Configuration config + ) { + sinkNode(node, config) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + ap = TRevPartialNil() and + not fullBarrier(node, config) and + exists(config.explorationLimit()) + or + exists(PartialPathNodeRev mid | + revPartialPathStep(mid, node, sc1, sc2, ap, config) and + not clearsContentCached(node.asNode(), ap.getHead()) and + not fullBarrier(node, config) and + distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() + ) + } + + pragma[nomagic] + private predicate partialPathNodeMk0( + NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, + Configuration config + ) { + exists(PartialPathNodeFwd mid | + partialPathStep(mid, node, cc, sc1, sc2, ap, config) and + not fullBarrier(node, config) and + not clearsContentCached(node.asNode(), ap.getHead().getContent()) and + if node.asNode() instanceof CastingNode + then compatibleTypes(node.getDataFlowType(), ap.getType()) + else any() + ) + } + + /** + * A `Node` augmented with a call context, an access path, and a configuration. + */ + class PartialPathNode extends TPartialPathNode { + /** Gets a textual representation of this element. */ + string toString() { result = this.getNodeEx().toString() + this.ppAp() } + + /** + * Gets a textual representation of this element, including a textual + * representation of the call context. + */ + string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + + /** Gets the underlying `Node`. */ + final Node getNode() { this.getNodeEx().projectToNode() = result } + + private NodeEx getNodeEx() { + result = this.(PartialPathNodeFwd).getNodeEx() or + result = this.(PartialPathNodeRev).getNodeEx() + } + + /** Gets the associated configuration. */ + Configuration getConfiguration() { none() } + + /** Gets a successor of this node, if any. */ + PartialPathNode getASuccessor() { none() } + + /** + * Gets the approximate distance to the nearest source measured in number + * of interprocedural steps. + */ + int getSourceDistance() { + result = distSrc(this.getNodeEx().getEnclosingCallable(), this.getConfiguration()) + } + + /** + * Gets the approximate distance to the nearest sink measured in number + * of interprocedural steps. + */ + int getSinkDistance() { + result = distSink(this.getNodeEx().getEnclosingCallable(), this.getConfiguration()) + } + + private string ppAp() { + exists(string s | + s = this.(PartialPathNodeFwd).getAp().toString() or + s = this.(PartialPathNodeRev).getAp().toString() + | + if s = "" then result = "" else result = " " + s + ) + } + + private string ppCtx() { + result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">" + } + + /** Holds if this is a source in a forward-flow path. */ + predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() } + + /** Holds if this is a sink in a reverse-flow path. */ + predicate isRevSink() { this.(PartialPathNodeRev).isSink() } + } + + /** + * Provides the query predicates needed to include a graph in a path-problem query. + */ + module PartialPathGraph { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b } + } + + private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd { + NodeEx node; + CallContext cc; + TSummaryCtx1 sc1; + TSummaryCtx2 sc2; + PartialAccessPath ap; + Configuration config; + + PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) } + + NodeEx getNodeEx() { result = node } + + CallContext getCallContext() { result = cc } + + TSummaryCtx1 getSummaryCtx1() { result = sc1 } + + TSummaryCtx2 getSummaryCtx2() { result = sc2 } + + PartialAccessPath getAp() { result = ap } + + override Configuration getConfiguration() { result = config } + + override PartialPathNodeFwd getASuccessor() { + partialPathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx1(), + result.getSummaryCtx2(), result.getAp(), result.getConfiguration()) + } + + predicate isSource() { + sourceNode(node, config) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + ap instanceof TPartialNil + } + } + + private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev { + NodeEx node; + TRevSummaryCtx1 sc1; + TRevSummaryCtx2 sc2; + RevPartialAccessPath ap; + Configuration config; + + PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) } + + NodeEx getNodeEx() { result = node } + + TRevSummaryCtx1 getSummaryCtx1() { result = sc1 } + + TRevSummaryCtx2 getSummaryCtx2() { result = sc2 } + + RevPartialAccessPath getAp() { result = ap } + + override Configuration getConfiguration() { result = config } + + override PartialPathNodeRev getASuccessor() { + revPartialPathStep(result, this.getNodeEx(), this.getSummaryCtx1(), this.getSummaryCtx2(), + this.getAp(), this.getConfiguration()) + } + + predicate isSink() { + sinkNode(node, config) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + ap = TRevPartialNil() + } + } + + private predicate partialPathStep( + PartialPathNodeFwd mid, NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, + PartialAccessPath ap, Configuration config + ) { + not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and + ( + localFlowStep(mid.getNodeEx(), node, config) and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalLocalFlowStep(mid.getNodeEx(), node, config) and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil(node.getDataFlowType()) and + config = mid.getConfiguration() + ) + or + jumpStep(mid.getNodeEx(), node, config) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalJumpStep(mid.getNodeEx(), node, config) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil(node.getDataFlowType()) and + config = mid.getConfiguration() + or + partialPathStoreStep(mid, _, _, node, ap) and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + config = mid.getConfiguration() + or + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + apConsFwd(ap, tc, ap0, config) + ) + or + partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) + or + partialPathOutOfCallable(mid, node, cc, ap, config) and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() + or + partialPathThroughCallable(mid, node, cc, ap, config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() + } + + bindingset[result, i] + private int unbindInt(int i) { i <= result and i >= result } + + pragma[inline] + private predicate partialPathStoreStep( + PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, NodeEx node, + PartialAccessPath ap2 + ) { + exists(NodeEx midNode, DataFlowType contentType | + midNode = mid.getNodeEx() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType, mid.getConfiguration()) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) + } + + pragma[nomagic] + private predicate apConsFwd( + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config + ) { + exists(PartialPathNodeFwd mid | + partialPathStoreStep(mid, ap1, tc, _, ap2) and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate partialPathReadStep( + PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, NodeEx node, CallContext cc, + Configuration config + ) { + exists(NodeEx midNode | + midNode = mid.getNodeEx() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node, pragma[only_bind_into](config)) and + ap.getHead() = tc and + pragma[only_bind_into](config) = mid.getConfiguration() and + cc = mid.getCallContext() + ) + } + + private predicate partialPathOutOfCallable0( + PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap, + Configuration config + ) { + pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and + innercc = mid.getCallContext() and + innercc instanceof CallContextNoCall and + ap = mid.getAp() and + config = mid.getConfiguration() + } + + pragma[nomagic] + private predicate partialPathOutOfCallable1( + PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, + PartialAccessPath ap, Configuration config + ) { + exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | + partialPathOutOfCallable0(mid, pos, innercc, ap, config) and + c = pos.getCallable() and + kind = pos.getKind() and + resolveReturn(innercc, c, call) + | + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) + } + + private predicate partialPathOutOfCallable( + PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config + ) { + exists(ReturnKindExt kind, DataFlowCall call | + partialPathOutOfCallable1(mid, call, kind, cc, ap, config) + | + out.asNode() = kind.getAnOutNode(call) + ) + } + + pragma[noinline] + private predicate partialPathIntoArg( + PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, + Configuration config + ) { + exists(ArgNode arg | + arg = mid.getNodeEx().asNode() and + cc = mid.getCallContext() and + arg.argumentOf(call, i) and + ap = mid.getAp() and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate partialPathIntoCallable0( + PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc, + DataFlowCall call, PartialAccessPath ap, Configuration config + ) { + partialPathIntoArg(mid, i, outercc, call, ap, config) and + callable = resolveCall(call, outercc) + } + + private predicate partialPathIntoCallable( + PartialPathNodeFwd mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, + TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, + Configuration config + ) { + exists(int i, DataFlowCallable callable | + partialPathIntoCallable0(mid, callable, i, outercc, call, ap, config) and + p.isParameterOf(callable, i) and + sc1 = TSummaryCtx1Param(p) and + sc2 = TSummaryCtx2Some(ap) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) + } + + pragma[nomagic] + private predicate paramFlowsThroughInPartialPath( + ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, + PartialAccessPath ap, Configuration config + ) { + exists(PartialPathNodeFwd mid, RetNodeEx ret | + mid.getNodeEx() = ret and + kind = ret.getKind() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + config = mid.getConfiguration() and + ap = mid.getAp() + ) + } + + pragma[noinline] + private predicate partialPathThroughCallable0( + DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, + PartialAccessPath ap, Configuration config + ) { + exists(CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + partialPathIntoCallable(mid, _, cc, innercc, sc1, sc2, call, _, config) and + paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) + ) + } + + private predicate partialPathThroughCallable( + PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config + ) { + exists(DataFlowCall call, ReturnKindExt kind | + partialPathThroughCallable0(call, mid, kind, cc, ap, config) and + out.asNode() = kind.getAnOutNode(call) + ) + } + + private predicate revPartialPathStep( + PartialPathNodeRev mid, NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, + RevPartialAccessPath ap, Configuration config + ) { + localFlowStep(node, mid.getNodeEx(), config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalLocalFlowStep(node, mid.getNodeEx(), config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + mid.getAp() instanceof RevPartialAccessPathNil and + ap = TRevPartialNil() and + config = mid.getConfiguration() + or + jumpStep(node, mid.getNodeEx(), config) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalJumpStep(node, mid.getNodeEx(), config) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + mid.getAp() instanceof RevPartialAccessPathNil and + ap = TRevPartialNil() and + config = mid.getConfiguration() + or + revPartialPathReadStep(mid, _, _, node, ap) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + config = mid.getConfiguration() + or + exists(RevPartialAccessPath ap0, Content c | + revPartialPathStoreStep(mid, ap0, c, node, config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + apConsRev(ap, c, ap0, config) + ) + or + exists(ParamNodeEx p | + mid.getNodeEx() = p and + viableParamArgEx(_, p, node) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + ap = mid.getAp() and + config = mid.getConfiguration() + ) + or + exists(ReturnPosition pos | + revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and + pos = getReturnPosition(node.asNode()) + ) + or + revPartialPathThroughCallable(mid, node, ap, config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() + } + + pragma[inline] + private predicate revPartialPathReadStep( + PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, NodeEx node, + RevPartialAccessPath ap2 + ) { + exists(NodeEx midNode | + midNode = mid.getNodeEx() and + ap1 = mid.getAp() and + read(node, c, midNode, mid.getConfiguration()) and + ap2.getHead() = c and + ap2.len() = unbindInt(ap1.len() + 1) + ) + } + + pragma[nomagic] + private predicate apConsRev( + RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config + ) { + exists(PartialPathNodeRev mid | + revPartialPathReadStep(mid, ap1, c, _, ap2) and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate revPartialPathStoreStep( + PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, NodeEx node, Configuration config + ) { + exists(NodeEx midNode, TypedContent tc | + midNode = mid.getNodeEx() and + ap = mid.getAp() and + store(node, tc, midNode, _, config) and + ap.getHead() = c and + config = mid.getConfiguration() and + tc.getContent() = c + ) + } + + pragma[nomagic] + private predicate revPartialPathIntoReturn( + PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, + DataFlowCall call, RevPartialAccessPath ap, Configuration config + ) { + exists(NodeEx out | + mid.getNodeEx() = out and + viableReturnPosOutEx(call, pos, out) and + sc1 = TRevSummaryCtx1Some(pos) and + sc2 = TRevSummaryCtx2Some(ap) and + ap = mid.getAp() and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate revPartialPathFlowsThrough( + int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, + Configuration config + ) { + exists(PartialPathNodeRev mid, ParamNodeEx p | + mid.getNodeEx() = p and + p.getPosition() = pos and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + ap = mid.getAp() and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate revPartialPathThroughCallable0( + DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap, + Configuration config + ) { + exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 | + revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and + revPartialPathFlowsThrough(pos, sc1, sc2, ap, config) + ) + } + + pragma[nomagic] + private predicate revPartialPathThroughCallable( + PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config + ) { + exists(DataFlowCall call, int pos | + revPartialPathThroughCallable0(call, mid, pos, ap, config) and + node.asNode().(ArgNode).argumentOf(call, pos) + ) + } +} + +import FlowExploration + +private predicate partialFlow( + PartialPathNode source, PartialPathNode node, Configuration configuration +) { + source.getConfiguration() = configuration and + source.isFwdSource() and + node = source.getASuccessor+() +} + +private predicate revPartialFlow( + PartialPathNode node, PartialPathNode sink, Configuration configuration +) { + sink.getConfiguration() = configuration and + sink.isRevSink() and + node.getASuccessor+() = sink +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll new file mode 100644 index 00000000000..e11244c42b0 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll @@ -0,0 +1,1297 @@ +private import DataFlowImplSpecific::Private +private import DataFlowImplSpecific::Public +import Cached + +/** + * The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathFront` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision during pruning. + */ +predicate accessPathApproxCostLimits(int apLimit, int tupleLimit) { + apLimit = 10 and + tupleLimit = 10000 +} + +/** + * The cost limits for the `AccessPathApprox` to `AccessPath` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathApprox` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision. + */ +predicate accessPathCostLimits(int apLimit, int tupleLimit) { + apLimit = 5 and + tupleLimit = 1000 +} + +/** + * Provides a simple data-flow analysis for resolving lambda calls. The analysis + * currently excludes read-steps, store-steps, and flow-through. + * + * The analysis uses non-linear recursion: When computing a flow path in or out + * of a call, we use the results of the analysis recursively to resolve lambda + * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. + */ +private module LambdaFlow { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) { + p.isParameterOf(viableCallable(call), i) + } + + private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) { + p.isParameterOf(viableCallableLambda(call, _), i) + } + + private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { + exists(int i | + viableParamNonLambda(call, i, p) and + arg.argumentOf(call, i) + ) + } + + private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { + exists(int i | + viableParamLambda(call, i, p) and + arg.argumentOf(call, i) + ) + } + + private newtype TReturnPositionSimple = + TReturnPositionSimple0(DataFlowCallable c, ReturnKind kind) { + exists(ReturnNode ret | + c = getNodeEnclosingCallable(ret) and + kind = ret.getKind() + ) + } + + pragma[noinline] + private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret, ReturnKind kind) { + result = TReturnPositionSimple0(getNodeEnclosingCallable(ret), kind) + } + + pragma[nomagic] + private TReturnPositionSimple viableReturnPosNonLambda(DataFlowCall call, ReturnKind kind) { + result = TReturnPositionSimple0(viableCallable(call), kind) + } + + pragma[nomagic] + private TReturnPositionSimple viableReturnPosLambda( + DataFlowCall call, DataFlowCallOption lastCall, ReturnKind kind + ) { + result = TReturnPositionSimple0(viableCallableLambda(call, lastCall), kind) + } + + private predicate viableReturnPosOutNonLambda( + DataFlowCall call, TReturnPositionSimple pos, OutNode out + ) { + exists(ReturnKind kind | + pos = viableReturnPosNonLambda(call, kind) and + out = getAnOutNode(call, kind) + ) + } + + private predicate viableReturnPosOutLambda( + DataFlowCall call, DataFlowCallOption lastCall, TReturnPositionSimple pos, OutNode out + ) { + exists(ReturnKind kind | + pos = viableReturnPosLambda(call, lastCall, kind) and + out = getAnOutNode(call, kind) + ) + } + + /** + * Holds if data can flow (inter-procedurally) from `node` (of type `t`) to + * the lambda call `lambdaCall`. + * + * The parameter `toReturn` indicates whether the path from `node` to + * `lambdaCall` goes through a return, and `toJump` whether the path goes + * through a jump step. + * + * The call context `lastCall` records the last call on the path from `node` + * to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing + * callable of `lambdaCall`. + */ + pragma[nomagic] + predicate revLambdaFlow( + DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, + boolean toJump, DataFlowCallOption lastCall + ) { + revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and + if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode + then compatibleTypes(t, getNodeDataFlowType(node)) + else any() + } + + pragma[nomagic] + predicate revLambdaFlow0( + DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, + boolean toJump, DataFlowCallOption lastCall + ) { + lambdaCall(lambdaCall, kind, node) and + t = getNodeDataFlowType(node) and + toReturn = false and + toJump = false and + lastCall = TDataFlowCallNone() + or + // local flow + exists(Node mid, DataFlowType t0 | + revLambdaFlow(lambdaCall, kind, mid, t0, toReturn, toJump, lastCall) + | + simpleLocalFlowStep(node, mid) and + t = t0 + or + exists(boolean preservesValue | + additionalLambdaFlowStep(node, mid, preservesValue) and + getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) + | + preservesValue = false and + t = getNodeDataFlowType(node) + or + preservesValue = true and + t = t0 + ) + ) + or + // jump step + exists(Node mid, DataFlowType t0 | + revLambdaFlow(lambdaCall, kind, mid, t0, _, _, _) and + toReturn = false and + toJump = true and + lastCall = TDataFlowCallNone() + | + jumpStepCached(node, mid) and + t = t0 + or + exists(boolean preservesValue | + additionalLambdaFlowStep(node, mid, preservesValue) and + getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) + | + preservesValue = false and + t = getNodeDataFlowType(node) + or + preservesValue = true and + t = t0 + ) + ) + or + // flow into a callable + exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | + revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and + ( + if lastCall0 = TDataFlowCallNone() and toJump = false + then lastCall = TDataFlowCallSome(call) + else lastCall = lastCall0 + ) and + toReturn = false + | + viableParamArgNonLambda(call, p, node) + or + viableParamArgLambda(call, p, node) // non-linear recursion + ) + or + // flow out of a callable + exists(TReturnPositionSimple pos | + revLambdaFlowOut(lambdaCall, kind, pos, t, toJump, lastCall) and + getReturnPositionSimple(node, node.(ReturnNode).getKind()) = pos and + toReturn = true + ) + } + + pragma[nomagic] + predicate revLambdaFlowOutLambdaCall( + DataFlowCall lambdaCall, LambdaCallKind kind, OutNode out, DataFlowType t, boolean toJump, + DataFlowCall call, DataFlowCallOption lastCall + ) { + revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and + exists(ReturnKindExt rk | + out = rk.getAnOutNode(call) and + lambdaCall(call, _, _) + ) + } + + pragma[nomagic] + predicate revLambdaFlowOut( + DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t, + boolean toJump, DataFlowCallOption lastCall + ) { + exists(DataFlowCall call, OutNode out | + revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and + viableReturnPosOutNonLambda(call, pos, out) + or + // non-linear recursion + revLambdaFlowOutLambdaCall(lambdaCall, kind, out, t, toJump, call, lastCall) and + viableReturnPosOutLambda(call, _, pos, out) + ) + } + + pragma[nomagic] + predicate revLambdaFlowIn( + DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, + DataFlowCallOption lastCall + ) { + revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) + } +} + +private DataFlowCallable viableCallableExt(DataFlowCall call) { + result = viableCallable(call) + or + result = viableCallableLambda(call, _) +} + +cached +private module Cached { + /** + * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to + * force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby + * collapsing the two stages. + */ + cached + predicate forceCachingInSameStage() { any() } + + cached + predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() } + + cached + predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { + c = call.getEnclosingCallable() + } + + cached + predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } + + cached + predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } + + cached + predicate clearsContentCached(Node n, Content c) { clearsContent(n, c) } + + cached + predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } + + cached + predicate outNodeExt(Node n) { + n instanceof OutNode + or + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode + } + + cached + predicate hiddenNode(Node n) { nodeIsHidden(n) } + + cached + OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { + result = getAnOutNode(call, k.(ValueReturnKind).getKind()) + or + exists(ArgNode arg | + result.(PostUpdateNode).getPreUpdateNode() = arg and + arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) + ) + } + + cached + predicate returnNodeExt(Node n, ReturnKindExt k) { + k = TValueReturn(n.(ReturnNode).getKind()) + or + exists(ParamNode p, int pos | + parameterValueFlowsToPreUpdate(p, n) and + p.isParameterOf(_, pos) and + k = TParamUpdate(pos) + ) + } + + cached + predicate castNode(Node n) { n instanceof CastNode } + + cached + predicate castingNode(Node n) { + castNode(n) or + n instanceof ParamNode or + n instanceof OutNodeExt or + // For reads, `x.f`, we want to check that the tracked type after the read (which + // is obtained by popping the head of the access path stack) is compatible with + // the type of `x.f`. + read(_, _, n) + } + + cached + predicate parameterNode(Node n, DataFlowCallable c, int i) { + n.(ParameterNode).isParameterOf(c, i) + } + + cached + predicate argumentNode(Node n, DataFlowCall call, int pos) { + n.(ArgumentNode).argumentOf(call, pos) + } + + /** + * Gets a viable target for the lambda call `call`. + * + * `lastCall` records the call required to reach `call` in order for the result + * to be a viable target, if any. + */ + cached + DataFlowCallable viableCallableLambda(DataFlowCall call, DataFlowCallOption lastCall) { + exists(Node creation, LambdaCallKind kind | + LambdaFlow::revLambdaFlow(call, kind, creation, _, _, _, lastCall) and + lambdaCreation(creation, kind, result) + ) + } + + /** + * Holds if `p` is the `i`th parameter of a viable dispatch target of `call`. + * The instance parameter is considered to have index `-1`. + */ + pragma[nomagic] + private predicate viableParam(DataFlowCall call, int i, ParamNode p) { + p.isParameterOf(viableCallableExt(call), i) + } + + /** + * Holds if `arg` is a possible argument to `p` in `call`, taking virtual + * dispatch into account. + */ + cached + predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { + exists(int i | + viableParam(call, i, p) and + arg.argumentOf(call, i) and + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) + ) + } + + pragma[nomagic] + private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) { + viableCallableExt(call) = result.getCallable() and + kind = result.getKind() + } + + /** + * Holds if a value at return position `pos` can be returned to `out` via `call`, + * taking virtual dispatch into account. + */ + cached + predicate viableReturnPosOut(DataFlowCall call, ReturnPosition pos, Node out) { + exists(ReturnKindExt kind | + pos = viableReturnPos(call, kind) and + out = kind.getAnOutNode(call) + ) + } + + /** Provides predicates for calculating flow-through summaries. */ + private module FlowThrough { + /** + * The first flow-through approximation: + * + * - Input access paths are abstracted with a Boolean parameter + * that indicates (non-)emptiness. + */ + private module Cand { + /** + * Holds if `p` can flow to `node` in the same callable using only + * value-preserving steps. + * + * `read` indicates whether it is contents of `p` that can flow to `node`. + */ + pragma[nomagic] + private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { + p = node and + read = false + or + // local flow + exists(Node mid | + parameterValueFlowCand(p, mid, read) and + simpleLocalFlowStep(mid, node) + ) + or + // read + exists(Node mid | + parameterValueFlowCand(p, mid, false) and + read(mid, _, node) and + read = true + ) + or + // flow through: no prior read + exists(ArgNode arg | + parameterValueFlowArgCand(p, arg, false) and + argumentValueFlowsThroughCand(arg, node, read) + ) + or + // flow through: no read inside method + exists(ArgNode arg | + parameterValueFlowArgCand(p, arg, read) and + argumentValueFlowsThroughCand(arg, node, false) + ) + } + + pragma[nomagic] + private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { + parameterValueFlowCand(p, arg, read) + } + + pragma[nomagic] + predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { + parameterValueFlowCand(p, n.getPreUpdateNode(), false) + } + + /** + * Holds if `p` can flow to a return node of kind `kind` in the same + * callable using only value-preserving steps, not taking call contexts + * into account. + * + * `read` indicates whether it is contents of `p` that can flow to the return + * node. + */ + predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { + exists(ReturnNode ret | + parameterValueFlowCand(p, ret, read) and + kind = ret.getKind() + ) + } + + pragma[nomagic] + private predicate argumentValueFlowsThroughCand0( + DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read + ) { + exists(ParamNode param | viableParamArg(call, param, arg) | + parameterValueFlowReturnCand(param, kind, read) + ) + } + + /** + * Holds if `arg` flows to `out` through a call using only value-preserving steps, + * not taking call contexts into account. + * + * `read` indicates whether it is contents of `arg` that can flow to `out`. + */ + predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { + exists(DataFlowCall call, ReturnKind kind | + argumentValueFlowsThroughCand0(call, arg, kind, read) and + out = getAnOutNode(call, kind) + ) + } + + predicate cand(ParamNode p, Node n) { + parameterValueFlowCand(p, n, _) and + ( + parameterValueFlowReturnCand(p, _, _) + or + parameterValueFlowsToPreUpdateCand(p, _) + ) + } + } + + /** + * The final flow-through calculation: + * + * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`) + * or summarized as a single read step with before and after types recorded + * in the `ReadStepTypesOption` parameter. + * - Types are checked using the `compatibleTypes()` relation. + */ + private module Final { + /** + * Holds if `p` can flow to `node` in the same callable using only + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. + * + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. + */ + predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { + parameterValueFlow0(p, node, read) and + if node instanceof CastingNode + then + // normal flow through + read = TReadStepTypesNone() and + compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) + or + // getter + compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) + else any() + } + + pragma[nomagic] + private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { + p = node and + Cand::cand(p, _) and + read = TReadStepTypesNone() + or + // local flow + exists(Node mid | + parameterValueFlow(p, mid, read) and + simpleLocalFlowStep(mid, node) + ) + or + // read + exists(Node mid | + parameterValueFlow(p, mid, TReadStepTypesNone()) and + readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, + read.getContentType()) and + Cand::parameterValueFlowReturnCand(p, _, true) and + compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) + ) + or + parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) + } + + pragma[nomagic] + private predicate parameterValueFlow0_0( + ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read + ) { + // flow through: no prior read + exists(ArgNode arg | + parameterValueFlowArg(p, arg, mustBeNone) and + argumentValueFlowsThrough(arg, read, node) + ) + or + // flow through: no read inside method + exists(ArgNode arg | + parameterValueFlowArg(p, arg, read) and + argumentValueFlowsThrough(arg, mustBeNone, node) + ) + } + + pragma[nomagic] + private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { + parameterValueFlow(p, arg, read) and + Cand::argumentValueFlowsThroughCand(arg, _, _) + } + + pragma[nomagic] + private predicate argumentValueFlowsThrough0( + DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read + ) { + exists(ParamNode param | viableParamArg(call, param, arg) | + parameterValueFlowReturn(param, kind, read) + ) + } + + /** + * Holds if `arg` flows to `out` through a call using only + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. + * + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. + */ + pragma[nomagic] + predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { + exists(DataFlowCall call, ReturnKind kind | + argumentValueFlowsThrough0(call, arg, kind, read) and + out = getAnOutNode(call, kind) + | + // normal flow through + read = TReadStepTypesNone() and + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) + or + // getter + compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) + ) + } + + /** + * Holds if `arg` flows to `out` through a call using only + * value-preserving steps and a single read step, not taking call + * contexts into account, thus representing a getter-step. + */ + predicate getterStep(ArgNode arg, Content c, Node out) { + argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) + } + + /** + * Holds if `p` can flow to a return node of kind `kind` in the same + * callable using only value-preserving steps and possibly a single read + * step. + * + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. + */ + private predicate parameterValueFlowReturn( + ParamNode p, ReturnKind kind, ReadStepTypesOption read + ) { + exists(ReturnNode ret | + parameterValueFlow(p, ret, read) and + kind = ret.getKind() + ) + } + } + + import Final + } + + import FlowThrough + + cached + private module DispatchWithCallContext { + /** + * Holds if the set of viable implementations that can be called by `call` + * might be improved by knowing the call context. + */ + pragma[nomagic] + private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { + mayBenefitFromCallContext(call, callable) + or + callEnclosingCallable(call, callable) and + exists(viableCallableLambda(call, TDataFlowCallSome(_))) + } + + /** + * Gets a viable dispatch target of `call` in the context `ctx`. This is + * restricted to those `call`s for which a context might make a difference. + */ + pragma[nomagic] + private DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContext(call, ctx) + or + result = viableCallableLambda(call, TDataFlowCallSome(ctx)) + or + exists(DataFlowCallable enclosing | + mayBenefitFromCallContextExt(call, enclosing) and + enclosing = viableCallableExt(ctx) and + result = viableCallableLambda(call, TDataFlowCallNone()) + ) + } + + /** + * Holds if the call context `ctx` reduces the set of viable run-time + * dispatch targets of call `call` in `c`. + */ + cached + predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContextExt(call, c) and + c = viableCallableExt(ctx) and + ctxtgts = count(viableImplInCallContextExt(call, ctx)) and + tgts = strictcount(viableCallableExt(call)) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls for which a context + * makes a difference. + */ + cached + DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContextExt(call, ctx) and + reducedViableImplInCallContext(call, _, ctx) + } + + /** + * Holds if flow returning from callable `c` to call `call` might return + * further and if this path restricts the set of call sites that can be + * returned to. + */ + cached + predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContextExt(call, _) and + c = viableCallableExt(call) and + ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and + tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls and results for which + * the return flow from the result to `call` restricts the possible context + * `ctx`. + */ + cached + DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContextExt(call, ctx) and + reducedViableImplInReturn(result, call) + } + } + + import DispatchWithCallContext + + /** + * Holds if `p` can flow to the pre-update node associated with post-update + * node `n`, in the same callable, using only value-preserving steps. + */ + private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { + parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) + } + + private predicate store( + Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType + ) { + storeStep(node1, c, node2) and + contentType = getNodeDataFlowType(node1) and + containerType = getNodeDataFlowType(node2) + or + exists(Node n1, Node n2 | + n1 = node1.(PostUpdateNode).getPreUpdateNode() and + n2 = node2.(PostUpdateNode).getPreUpdateNode() + | + argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) + or + read(n2, c, n1) and + contentType = getNodeDataFlowType(n1) and + containerType = getNodeDataFlowType(n2) + ) + } + + cached + predicate read(Node node1, Content c, Node node2) { readStep(node1, c, node2) } + + /** + * Holds if data can flow from `node1` to `node2` via a direct assignment to + * `f`. + * + * This includes reverse steps through reads when the result of the read has + * been stored into, in order to handle cases like `x.f1.f2 = y`. + */ + cached + predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) { + store(node1, tc.getContent(), node2, contentType, tc.getContainerType()) + } + + /** + * Holds if data can flow from `fromNode` to `toNode` because they are the post-update + * nodes of some function output and input respectively, where the output and input + * are aliases. A typical example is a function returning `this`, implementing a fluent + * interface. + */ + private predicate reverseStepThroughInputOutputAlias( + PostUpdateNode fromNode, PostUpdateNode toNode + ) { + exists(Node fromPre, Node toPre | + fromPre = fromNode.getPreUpdateNode() and + toPre = toNode.getPreUpdateNode() + | + exists(DataFlowCall c | + // Does the language-specific simpleLocalFlowStep already model flow + // from function input to output? + fromPre = getAnOutNode(c, _) and + toPre.(ArgNode).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgNode), fromPre) + ) + or + argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) + ) + } + + cached + predicate simpleLocalFlowStepExt(Node node1, Node node2) { + simpleLocalFlowStep(node1, node2) or + reverseStepThroughInputOutputAlias(node1, node2) + } + + /** + * Holds if the call context `call` improves virtual dispatch in `callable`. + */ + cached + predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) { + reducedViableImplInCallContext(_, callable, call) + } + + /** + * Holds if the call context `call` allows us to prune unreachable nodes in `callable`. + */ + cached + predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) { + exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) + } + + cached + predicate allowParameterReturnInSelfCached(ParamNode p) { allowParameterReturnInSelf(p) } + + cached + newtype TCallContext = + TAnyCallContext() or + TSpecificCall(DataFlowCall call) { recordDataFlowCallSite(call, _) } or + TSomeCall() or + TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) } + + cached + newtype TReturnPosition = + TReturnPosition0(DataFlowCallable c, ReturnKindExt kind) { + exists(ReturnNodeExt ret | + c = returnNodeGetEnclosingCallable(ret) and + kind = ret.getKind() + ) + } + + cached + newtype TLocalFlowCallContext = + TAnyLocalCall() or + TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } + + cached + newtype TReturnKindExt = + TValueReturn(ReturnKind kind) or + TParamUpdate(int pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } + + cached + newtype TBooleanOption = + TBooleanNone() or + TBooleanSome(boolean b) { b = true or b = false } + + cached + newtype TDataFlowCallOption = + TDataFlowCallNone() or + TDataFlowCallSome(DataFlowCall call) + + cached + newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } + + cached + newtype TAccessPathFront = + TFrontNil(DataFlowType t) or + TFrontHead(TypedContent tc) + + cached + newtype TAccessPathFrontOption = + TAccessPathFrontNone() or + TAccessPathFrontSome(AccessPathFront apf) +} + +/** + * Holds if the call context `call` either improves virtual dispatch in + * `callable` or if it allows us to prune unreachable nodes in `callable`. + */ +predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { + recordDataFlowCallSiteDispatch(call, callable) or + recordDataFlowCallSiteUnreachable(call, callable) +} + +/** + * A `Node` at which a cast can occur such that the type should be checked. + */ +class CastingNode extends Node { + CastingNode() { castingNode(this) } +} + +private predicate readStepWithTypes( + Node n1, DataFlowType container, Content c, Node n2, DataFlowType content +) { + read(n1, c, n2) and + container = getNodeDataFlowType(n1) and + content = getNodeDataFlowType(n2) +} + +private newtype TReadStepTypesOption = + TReadStepTypesNone() or + TReadStepTypesSome(DataFlowType container, Content c, DataFlowType content) { + readStepWithTypes(_, container, c, _, content) + } + +private class ReadStepTypesOption extends TReadStepTypesOption { + predicate isSome() { this instanceof TReadStepTypesSome } + + DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) } + + Content getContent() { this = TReadStepTypesSome(_, result, _) } + + DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) } + + string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } +} + +/** + * A call context to restrict the targets of virtual dispatch, prune local flow, + * and match the call sites of flow into a method with flow out of a method. + * + * There are four cases: + * - `TAnyCallContext()` : No restrictions on method flow. + * - `TSpecificCall(DataFlowCall call)` : Flow entered through the + * given `call`. This call improves the set of viable + * dispatch targets for at least one method call in the current callable + * or helps prune unreachable nodes in the current callable. + * - `TSomeCall()` : Flow entered through a parameter. The + * originating call does not improve the set of dispatch targets for any + * method call in the current callable and was therefore not recorded. + * - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and + * this dispatch target of `call` implies a reduced set of dispatch origins + * to which data may flow if it should reach a `return` statement. + */ +abstract class CallContext extends TCallContext { + abstract string toString(); + + /** Holds if this call context is relevant for `callable`. */ + abstract predicate relevantFor(DataFlowCallable callable); +} + +abstract class CallContextNoCall extends CallContext { } + +class CallContextAny extends CallContextNoCall, TAnyCallContext { + override string toString() { result = "CcAny" } + + override predicate relevantFor(DataFlowCallable callable) { any() } +} + +abstract class CallContextCall extends CallContext { + /** Holds if this call context may be `call`. */ + bindingset[call] + abstract predicate matchesCall(DataFlowCall call); +} + +class CallContextSpecificCall extends CallContextCall, TSpecificCall { + override string toString() { + exists(DataFlowCall call | this = TSpecificCall(call) | result = "CcCall(" + call + ")") + } + + override predicate relevantFor(DataFlowCallable callable) { + recordDataFlowCallSite(this.getCall(), callable) + } + + override predicate matchesCall(DataFlowCall call) { call = this.getCall() } + + DataFlowCall getCall() { this = TSpecificCall(result) } +} + +class CallContextSomeCall extends CallContextCall, TSomeCall { + override string toString() { result = "CcSomeCall" } + + override predicate relevantFor(DataFlowCallable callable) { + exists(ParamNode p | getNodeEnclosingCallable(p) = callable) + } + + override predicate matchesCall(DataFlowCall call) { any() } +} + +class CallContextReturn extends CallContextNoCall, TReturn { + override string toString() { + exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")") + } + + override predicate relevantFor(DataFlowCallable callable) { + exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) + } +} + +/** + * A call context that is relevant for pruning local flow. + */ +abstract class LocalCallContext extends TLocalFlowCallContext { + abstract string toString(); + + /** Holds if this call context is relevant for `callable`. */ + abstract predicate relevantFor(DataFlowCallable callable); +} + +class LocalCallContextAny extends LocalCallContext, TAnyLocalCall { + override string toString() { result = "LocalCcAny" } + + override predicate relevantFor(DataFlowCallable callable) { any() } +} + +class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall { + LocalCallContextSpecificCall() { this = TSpecificLocalCall(call) } + + DataFlowCall call; + + DataFlowCall getCall() { result = call } + + override string toString() { result = "LocalCcCall(" + call + ")" } + + override predicate relevantFor(DataFlowCallable callable) { relevantLocalCCtx(call, callable) } +} + +private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { + exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) +} + +/** + * Gets the local call context given the call context and the callable that + * the contexts apply to. + */ +LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) { + ctx.relevantFor(callable) and + if relevantLocalCCtx(ctx.(CallContextSpecificCall).getCall(), callable) + then result.(LocalCallContextSpecificCall).getCall() = ctx.(CallContextSpecificCall).getCall() + else result instanceof LocalCallContextAny +} + +/** + * The value of a parameter at function entry, viewed as a node in a data + * flow graph. + */ +class ParamNode extends Node { + ParamNode() { parameterNode(this, _, _) } + + /** + * Holds if this node is the parameter of callable `c` at the specified + * (zero-based) position. + */ + predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) } +} + +/** A data-flow node that represents a call argument. */ +class ArgNode extends Node { + ArgNode() { argumentNode(this, _, _) } + + /** Holds if this argument occurs at the given position in the given call. */ + final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } +} + +/** + * A node from which flow can return to the caller. This is either a regular + * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. + */ +class ReturnNodeExt extends Node { + ReturnNodeExt() { returnNodeExt(this, _) } + + /** Gets the kind of this returned value. */ + ReturnKindExt getKind() { returnNodeExt(this, result) } +} + +/** + * A node to which data can flow from a call. Either an ordinary out node + * or a post-update node associated with a call argument. + */ +class OutNodeExt extends Node { + OutNodeExt() { outNodeExt(this) } +} + +/** + * An extended return kind. A return kind describes how data can be returned + * from a callable. This can either be through a returned value or an updated + * parameter. + */ +abstract class ReturnKindExt extends TReturnKindExt { + /** Gets a textual representation of this return kind. */ + abstract string toString(); + + /** Gets a node corresponding to data flow out of `call`. */ + final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } +} + +class ValueReturnKind extends ReturnKindExt, TValueReturn { + private ReturnKind kind; + + ValueReturnKind() { this = TValueReturn(kind) } + + ReturnKind getKind() { result = kind } + + override string toString() { result = kind.toString() } +} + +class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { + private int pos; + + ParamUpdateReturnKind() { this = TParamUpdate(pos) } + + int getPosition() { result = pos } + + override string toString() { result = "param update " + pos } +} + +/** A callable tagged with a relevant return kind. */ +class ReturnPosition extends TReturnPosition0 { + private DataFlowCallable c; + private ReturnKindExt kind; + + ReturnPosition() { this = TReturnPosition0(c, kind) } + + /** Gets the callable. */ + DataFlowCallable getCallable() { result = c } + + /** Gets the return kind. */ + ReturnKindExt getKind() { result = kind } + + /** Gets a textual representation of this return position. */ + string toString() { result = "[" + kind + "] " + c } +} + +/** + * Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this + * predicate ensures that joins go from `n` to the result instead of the other + * way around. + */ +pragma[inline] +DataFlowCallable getNodeEnclosingCallable(Node n) { + nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) +} + +/** Gets the type of `n` used for type pruning. */ +pragma[inline] +DataFlowType getNodeDataFlowType(Node n) { + nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) +} + +pragma[noinline] +private DataFlowCallable returnNodeGetEnclosingCallable(ReturnNodeExt ret) { + result = getNodeEnclosingCallable(ret) +} + +pragma[noinline] +private ReturnPosition getReturnPosition0(ReturnNodeExt ret, ReturnKindExt kind) { + result.getCallable() = returnNodeGetEnclosingCallable(ret) and + kind = result.getKind() +} + +pragma[noinline] +ReturnPosition getReturnPosition(ReturnNodeExt ret) { + result = getReturnPosition0(ret, ret.getKind()) +} + +/** + * Checks whether `inner` can return to `call` in the call context `innercc`. + * Assumes a context of `inner = viableCallableExt(call)`. + */ +bindingset[innercc, inner, call] +predicate checkCallContextReturn(CallContext innercc, DataFlowCallable inner, DataFlowCall call) { + innercc instanceof CallContextAny + or + exists(DataFlowCallable c0, DataFlowCall call0 | + callEnclosingCallable(call0, inner) and + innercc = TReturn(c0, call0) and + c0 = prunedViableImplInCallContextReverse(call0, call) + ) +} + +/** + * Checks whether `call` can resolve to `calltarget` in the call context `cc`. + * Assumes a context of `calltarget = viableCallableExt(call)`. + */ +bindingset[cc, call, calltarget] +predicate checkCallContextCall(CallContext cc, DataFlowCall call, DataFlowCallable calltarget) { + exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | + if reducedViableImplInCallContext(call, _, ctx) + then calltarget = prunedViableImplInCallContext(call, ctx) + else any() + ) + or + cc instanceof CallContextSomeCall + or + cc instanceof CallContextAny + or + cc instanceof CallContextReturn +} + +/** + * Resolves a return from `callable` in `cc` to `call`. This is equivalent to + * `callable = viableCallableExt(call) and checkCallContextReturn(cc, callable, call)`. + */ +bindingset[cc, callable] +predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) { + cc instanceof CallContextAny and callable = viableCallableExt(call) + or + exists(DataFlowCallable c0, DataFlowCall call0 | + callEnclosingCallable(call0, callable) and + cc = TReturn(c0, call0) and + c0 = prunedViableImplInCallContextReverse(call0, call) + ) +} + +/** + * Resolves a call from `call` in `cc` to `result`. This is equivalent to + * `result = viableCallableExt(call) and checkCallContextCall(cc, call, result)`. + */ +bindingset[call, cc] +DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { + exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | + if reducedViableImplInCallContext(call, _, ctx) + then result = prunedViableImplInCallContext(call, ctx) + else result = viableCallableExt(call) + ) + or + result = viableCallableExt(call) and cc instanceof CallContextSomeCall + or + result = viableCallableExt(call) and cc instanceof CallContextAny + or + result = viableCallableExt(call) and cc instanceof CallContextReturn +} + +/** An optional Boolean value. */ +class BooleanOption extends TBooleanOption { + string toString() { + this = TBooleanNone() and result = "<none>" + or + this = TBooleanSome(any(boolean b | result = b.toString())) + } +} + +/** An optional `DataFlowCall`. */ +class DataFlowCallOption extends TDataFlowCallOption { + string toString() { + this = TDataFlowCallNone() and + result = "(none)" + or + exists(DataFlowCall call | + this = TDataFlowCallSome(call) and + result = call.toString() + ) + } +} + +/** Content tagged with the type of a containing object. */ +class TypedContent extends MkTypedContent { + private Content c; + private DataFlowType t; + + TypedContent() { this = MkTypedContent(c, t) } + + /** Gets the content. */ + Content getContent() { result = c } + + /** Gets the container type. */ + DataFlowType getContainerType() { result = t } + + /** Gets a textual representation of this content. */ + string toString() { result = c.toString() } + + /** + * Holds if access paths with this `TypedContent` at their head always should + * be tracked at high precision. This disables adaptive access path precision + * for such access paths. + */ + predicate forceHighPrecision() { forceHighPrecision(c) } +} + +/** + * The front of an access path. This is either a head or a nil. + */ +abstract class AccessPathFront extends TAccessPathFront { + abstract string toString(); + + abstract DataFlowType getType(); + + abstract boolean toBoolNonEmpty(); + + TypedContent getHead() { this = TFrontHead(result) } + + predicate isClearedAt(Node n) { clearsContentCached(n, this.getHead().getContent()) } +} + +class AccessPathFrontNil extends AccessPathFront, TFrontNil { + private DataFlowType t; + + AccessPathFrontNil() { this = TFrontNil(t) } + + override string toString() { result = ppReprType(t) } + + override DataFlowType getType() { result = t } + + override boolean toBoolNonEmpty() { result = false } +} + +class AccessPathFrontHead extends AccessPathFront, TFrontHead { + private TypedContent tc; + + AccessPathFrontHead() { this = TFrontHead(tc) } + + override string toString() { result = tc.toString() } + + override DataFlowType getType() { result = tc.getContainerType() } + + override boolean toBoolNonEmpty() { result = true } +} + +/** An optional access path front. */ +class AccessPathFrontOption extends TAccessPathFrontOption { + string toString() { + this = TAccessPathFrontNone() and result = "<none>" + or + this = TAccessPathFrontSome(any(AccessPathFront apf | result = apf.toString())) + } +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll new file mode 100644 index 00000000000..dd64fc70039 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll @@ -0,0 +1,182 @@ +/** + * Provides consistency queries for checking invariants in the language-specific + * data-flow classes and predicates. + */ + +private import DataFlowImplSpecific::Private +private import DataFlowImplSpecific::Public +private import tainttracking1.TaintTrackingParameter::Private +private import tainttracking1.TaintTrackingParameter::Public + +module Consistency { + private class RelevantNode extends Node { + RelevantNode() { + this instanceof ArgumentNode or + this instanceof ParameterNode or + this instanceof ReturnNode or + this = getAnOutNode(_, _) or + simpleLocalFlowStep(this, _) or + simpleLocalFlowStep(_, this) or + jumpStep(this, _) or + jumpStep(_, this) or + storeStep(this, _, _) or + storeStep(_, _, this) or + readStep(this, _, _) or + readStep(_, _, this) or + defaultAdditionalTaintStep(this, _) or + defaultAdditionalTaintStep(_, this) + } + } + + query predicate uniqueEnclosingCallable(Node n, string msg) { + exists(int c | + n instanceof RelevantNode and + c = count(n.getEnclosingCallable()) and + c != 1 and + msg = "Node should have one enclosing callable but has " + c + "." + ) + } + + query predicate uniqueType(Node n, string msg) { + exists(int c | + n instanceof RelevantNode and + c = count(getNodeType(n)) and + c != 1 and + msg = "Node should have one type but has " + c + "." + ) + } + + query predicate uniqueNodeLocation(Node n, string msg) { + exists(int c | + c = + count(string filepath, int startline, int startcolumn, int endline, int endcolumn | + n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + ) and + c != 1 and + msg = "Node should have one location but has " + c + "." + ) + } + + query predicate missingLocation(string msg) { + exists(int c | + c = + strictcount(Node n | + not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | + n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + ) + ) and + msg = "Nodes without location: " + c + ) + } + + query predicate uniqueNodeToString(Node n, string msg) { + exists(int c | + c = count(n.toString()) and + c != 1 and + msg = "Node should have one toString but has " + c + "." + ) + } + + query predicate missingToString(string msg) { + exists(int c | + c = strictcount(Node n | not exists(n.toString())) and + msg = "Nodes without toString: " + c + ) + } + + query predicate parameterCallable(ParameterNode p, string msg) { + exists(DataFlowCallable c | p.isParameterOf(c, _) and c != p.getEnclosingCallable()) and + msg = "Callable mismatch for parameter." + } + + query predicate localFlowIsLocal(Node n1, Node n2, string msg) { + simpleLocalFlowStep(n1, n2) and + n1.getEnclosingCallable() != n2.getEnclosingCallable() and + msg = "Local flow step does not preserve enclosing callable." + } + + private DataFlowType typeRepr() { result = getNodeType(_) } + + query predicate compatibleTypesReflexive(DataFlowType t, string msg) { + t = typeRepr() and + not compatibleTypes(t, t) and + msg = "Type compatibility predicate is not reflexive." + } + + query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) { + isUnreachableInCall(n, call) and + exists(DataFlowCallable c | + c = n.getEnclosingCallable() and + not viableCallable(call) = c + ) and + msg = "Call context for isUnreachableInCall is inconsistent with call graph." + } + + query predicate localCallNodes(DataFlowCall call, Node n, string msg) { + ( + n = getAnOutNode(call, _) and + msg = "OutNode and call does not share enclosing callable." + or + n.(ArgumentNode).argumentOf(call, _) and + msg = "ArgumentNode and call does not share enclosing callable." + ) and + n.getEnclosingCallable() != call.getEnclosingCallable() + } + + // This predicate helps the compiler forget that in some languages + // it is impossible for a result of `getPreUpdateNode` to be an + // instance of `PostUpdateNode`. + private Node getPre(PostUpdateNode n) { + result = n.getPreUpdateNode() + or + none() + } + + query predicate postIsNotPre(PostUpdateNode n, string msg) { + getPre(n) = n and + msg = "PostUpdateNode should not equal its pre-update node." + } + + query predicate postHasUniquePre(PostUpdateNode n, string msg) { + exists(int c | + c = count(n.getPreUpdateNode()) and + c != 1 and + msg = "PostUpdateNode should have one pre-update node but has " + c + "." + ) + } + + query predicate uniquePostUpdate(Node n, string msg) { + 1 < strictcount(PostUpdateNode post | post.getPreUpdateNode() = n) and + msg = "Node has multiple PostUpdateNodes." + } + + query predicate postIsInSameCallable(PostUpdateNode n, string msg) { + n.getEnclosingCallable() != n.getPreUpdateNode().getEnclosingCallable() and + msg = "PostUpdateNode does not share callable with its pre-update node." + } + + private predicate hasPost(Node n) { exists(PostUpdateNode post | post.getPreUpdateNode() = n) } + + query predicate reverseRead(Node n, string msg) { + exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and + msg = "Origin of readStep is missing a PostUpdateNode." + } + + query predicate argHasPostUpdate(ArgumentNode n, string msg) { + not hasPost(n) and + not isImmutableOrUnobservable(n) and + msg = "ArgumentNode is missing PostUpdateNode." + } + + // This predicate helps the compiler forget that in some languages + // it is impossible for a `PostUpdateNode` to be the target of + // `simpleLocalFlowStep`. + private predicate isPostUpdateNode(Node n) { n instanceof PostUpdateNode or none() } + + query predicate postWithInFlow(Node n, string msg) { + isPostUpdateNode(n) and + not clearsContent(n, _) and + simpleLocalFlowStep(_, n) and + msg = "PostUpdateNode should not be the target of local flow." + } +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplSpecific.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplSpecific.qll new file mode 100644 index 00000000000..e78a0814a14 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplSpecific.qll @@ -0,0 +1,11 @@ +/** + * Provides Ruby-specific definitions for use in the data flow library. + */ +module Private { + import DataFlowPrivate + import DataFlowDispatch +} + +module Public { + import DataFlowPublic +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll new file mode 100644 index 00000000000..d3d9c857f96 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll @@ -0,0 +1,814 @@ +private import ruby +private import codeql.ruby.CFG +private import codeql.ruby.dataflow.SSA +private import DataFlowPublic +private import DataFlowDispatch +private import SsaImpl as SsaImpl +private import FlowSummaryImpl as FlowSummaryImpl + +abstract class NodeImpl extends Node { + /** Do not call: use `getEnclosingCallable()` instead. */ + abstract CfgScope getCfgScope(); + + /** Do not call: use `getLocation()` instead. */ + abstract Location getLocationImpl(); + + /** Do not call: use `toString()` instead. */ + abstract string toStringImpl(); +} + +private class ExprNodeImpl extends ExprNode, NodeImpl { + override CfgScope getCfgScope() { result = this.getExprNode().getExpr().getCfgScope() } + + override Location getLocationImpl() { result = this.getExprNode().getLocation() } + + override string toStringImpl() { result = this.getExprNode().toString() } +} + +/** Provides predicates related to local data flow. */ +module LocalFlow { + private import codeql.ruby.dataflow.internal.SsaImpl + + /** + * Holds if `nodeFrom` is a last node referencing SSA definition `def`, which + * can reach `next`. + */ + private predicate localFlowSsaInput(Node nodeFrom, Ssa::Definition def, Ssa::Definition next) { + exists(BasicBlock bb, int i | lastRefBeforeRedef(def, bb, i, next) | + def = nodeFrom.(SsaDefinitionNode).getDefinition() and + def.definesAt(_, bb, i) + or + exists(CfgNodes::ExprCfgNode e | + e = nodeFrom.asExpr() and + e = bb.getNode(i) and + e.getExpr() instanceof VariableReadAccess + ) + ) + } + + /** Gets the SSA definition node corresponding to parameter `p`. */ + SsaDefinitionNode getParameterDefNode(NamedParameter p) { + exists(BasicBlock bb, int i | + bb.getNode(i).getNode() = p.getDefiningAccess() and + result.getDefinition().definesAt(_, bb, i) + ) + } + + /** + * Holds if there is a local flow step from `nodeFrom` to `nodeTo` involving + * SSA definition `def`. + */ + predicate localSsaFlowStep(Ssa::Definition def, Node nodeFrom, Node nodeTo) { + // Flow from assignment into SSA definition + def.(Ssa::WriteDefinition).assigns(nodeFrom.asExpr()) and + nodeTo.(SsaDefinitionNode).getDefinition() = def + or + // Flow from SSA definition to first read + def = nodeFrom.(SsaDefinitionNode).getDefinition() and + nodeTo.asExpr() = def.getAFirstRead() + or + // Flow from read to next read + exists( + CfgNodes::ExprNodes::VariableReadAccessCfgNode read1, + CfgNodes::ExprNodes::VariableReadAccessCfgNode read2 + | + def.hasAdjacentReads(read1, read2) and + nodeTo.asExpr() = read2 + | + nodeFrom.asExpr() = read1 + or + read1 = nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr() + ) + or + // Flow into phi node + exists(Ssa::PhiNode phi | + localFlowSsaInput(nodeFrom, def, phi) and + phi = nodeTo.(SsaDefinitionNode).getDefinition() and + def = phi.getAnInput() + ) + // TODO + // or + // // Flow into uncertain SSA definition + // exists(LocalFlow::UncertainExplicitSsaDefinition uncertain | + // localFlowSsaInput(nodeFrom, def, uncertain) and + // uncertain = nodeTo.(SsaDefinitionNode).getDefinition() and + // def = uncertain.getPriorDefinition() + // ) + } +} + +/** An argument of a call (including qualifier arguments, excluding block arguments). */ +private class Argument extends CfgNodes::ExprCfgNode { + private CfgNodes::ExprNodes::CallCfgNode call; + private int arg; + + Argument() { + this = call.getArgument(arg) and + not this.getExpr() instanceof BlockArgument + or + this = call.getReceiver() and arg = -1 + } + + /** Holds if this expression is the `i`th argument of `c`. */ + predicate isArgumentOf(CfgNodes::ExprNodes::CallCfgNode c, int i) { c = call and i = arg } +} + +/** A collection of cached types and predicates to be evaluated in the same stage. */ +cached +private module Cached { + cached + newtype TNode = + TExprNode(CfgNodes::ExprCfgNode n) or + TReturningNode(CfgNodes::ReturningCfgNode n) or + TSynthReturnNode(CfgScope scope, ReturnKind kind) { + exists(ReturningNode ret | + ret.(NodeImpl).getCfgScope() = scope and + ret.getKind() = kind + ) + } or + TSsaDefinitionNode(Ssa::Definition def) or + TNormalParameterNode(Parameter p) { not p instanceof BlockParameter } or + TSelfParameterNode(MethodBase m) or + TBlockParameterNode(MethodBase m) or + TExprPostUpdateNode(CfgNodes::ExprCfgNode n) { n instanceof Argument } or + TSummaryNode( + FlowSummaryImpl::Public::SummarizedCallable c, + FlowSummaryImpl::Private::SummaryNodeState state + ) { + FlowSummaryImpl::Private::summaryNodeRange(c, state) + } or + TSummaryParameterNode(FlowSummaryImpl::Public::SummarizedCallable c, int i) { + FlowSummaryImpl::Private::summaryParameterNodeRange(c, i) + } + + class TParameterNode = + TNormalParameterNode or TBlockParameterNode or TSelfParameterNode or TSummaryParameterNode; + + private predicate defaultValueFlow(NamedParameter p, ExprNode e) { + p.(OptionalParameter).getDefaultValue() = e.getExprNode().getExpr() + or + p.(KeywordParameter).getDefaultValue() = e.getExprNode().getExpr() + } + + private predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) { + LocalFlow::localSsaFlowStep(_, nodeFrom, nodeTo) + or + nodeFrom.(SelfParameterNode).getMethod() = nodeTo.asExpr().getExpr().getEnclosingCallable() and + nodeTo.asExpr().getExpr() instanceof Self + or + nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::AssignExprCfgNode).getRhs() + or + nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::BlockArgumentCfgNode).getValue() + or + nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::StmtSequenceCfgNode).getLastStmt() + or + nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::ConditionalExprCfgNode).getBranch(_) + or + nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::CaseExprCfgNode).getBranch(_) + or + exists(CfgNodes::ExprCfgNode exprTo, ReturningStatementNode n | + nodeFrom = n and + exprTo = nodeTo.asExpr() and + n.getReturningNode().getNode() instanceof BreakStmt and + exprTo.getNode() instanceof Loop and + nodeTo.asExpr().getAPredecessor(any(SuccessorTypes::BreakSuccessor s)) = n.getReturningNode() + ) + or + nodeFrom.asExpr() = nodeTo.(ReturningStatementNode).getReturningNode().getReturnedValueNode() + or + nodeTo.asExpr() = + any(CfgNodes::ExprNodes::ForExprCfgNode for | + exists(SuccessorType s | + not s instanceof SuccessorTypes::BreakSuccessor and + exists(for.getAPredecessor(s)) + ) and + nodeFrom.asExpr() = for.getValue() + ) + } + + /** + * This is the local flow predicate that is used as a building block in global + * data flow. + */ + cached + predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { + localFlowStepCommon(nodeFrom, nodeTo) + or + defaultValueFlow(nodeTo.(ParameterNode).getParameter(), nodeFrom) + or + nodeTo = LocalFlow::getParameterDefNode(nodeFrom.(ParameterNode).getParameter()) + or + nodeTo.(SynthReturnNode).getAnInput() = nodeFrom + or + FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true) + } + + /** This is the local flow predicate that is exposed. */ + cached + predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) { + localFlowStepCommon(nodeFrom, nodeTo) + or + defaultValueFlow(nodeTo.(ParameterNode).getParameter(), nodeFrom) + or + nodeTo = LocalFlow::getParameterDefNode(nodeFrom.(ParameterNode).getParameter()) + or + // Simple flow through library code is included in the exposed local + // step relation, even though flow is technically inter-procedural + FlowSummaryImpl::Private::Steps::summaryThroughStep(nodeFrom, nodeTo, true) + } + + /** This is the local flow predicate that is used in type tracking. */ + cached + predicate localFlowStepTypeTracker(Node nodeFrom, Node nodeTo) { + localFlowStepCommon(nodeFrom, nodeTo) + or + exists(NamedParameter p | + defaultValueFlow(p, nodeFrom) and + nodeTo = LocalFlow::getParameterDefNode(p) + ) + } + + cached + predicate isLocalSourceNode(Node n) { + n instanceof ParameterNode + or + // This case should not be needed once we have proper use-use flow + // for `self`. At that point, the `self`s returned by `trackInstance` + // in `DataFlowDispatch.qll` should refer to the post-update node, + // and we can remove this case. + n.asExpr().getExpr() instanceof Self + or + not localFlowStepTypeTracker+(any(Node e | + e instanceof ExprNode + or + e instanceof ParameterNode + ), n) + } + + cached + newtype TContent = TTodoContent() // stub +} + +import Cached + +/** Holds if `n` should be hidden from path explanations. */ +predicate nodeIsHidden(Node n) { + exists(Ssa::Definition def | def = n.(SsaDefinitionNode).getDefinition() | + def instanceof Ssa::PhiNode + ) + or + n instanceof SummaryNode + or + n instanceof SummaryParameterNode + or + n instanceof SynthReturnNode +} + +/** An SSA definition, viewed as a node in a data flow graph. */ +class SsaDefinitionNode extends NodeImpl, TSsaDefinitionNode { + Ssa::Definition def; + + SsaDefinitionNode() { this = TSsaDefinitionNode(def) } + + /** Gets the underlying SSA definition. */ + Ssa::Definition getDefinition() { result = def } + + override CfgScope getCfgScope() { result = def.getBasicBlock().getScope() } + + override Location getLocationImpl() { result = def.getLocation() } + + override string toStringImpl() { result = def.toString() } +} + +/** + * A value returning statement, viewed as a node in a data flow graph. + * + * Note that because of control-flow splitting, one `ReturningStmt` may correspond + * to multiple `ReturningStatementNode`s, just like it may correspond to multiple + * `ControlFlow::Node`s. + */ +class ReturningStatementNode extends NodeImpl, TReturningNode { + CfgNodes::ReturningCfgNode n; + + ReturningStatementNode() { this = TReturningNode(n) } + + /** Gets the expression corresponding to this node. */ + CfgNodes::ReturningCfgNode getReturningNode() { result = n } + + override CfgScope getCfgScope() { result = n.getScope() } + + override Location getLocationImpl() { result = n.getLocation() } + + override string toStringImpl() { result = n.toString() } +} + +private module ParameterNodes { + abstract class ParameterNodeImpl extends ParameterNode, NodeImpl { + abstract predicate isSourceParameterOf(Callable c, int i); + + override predicate isParameterOf(DataFlowCallable c, int i) { + this.isSourceParameterOf(c.asCallable(), i) + } + } + + /** + * The value of a normal parameter at function entry, viewed as a node in a data + * flow graph. + */ + class NormalParameterNode extends ParameterNodeImpl, TNormalParameterNode { + private Parameter parameter; + + NormalParameterNode() { this = TNormalParameterNode(parameter) } + + override Parameter getParameter() { result = parameter } + + override predicate isSourceParameterOf(Callable c, int i) { c.getParameter(i) = parameter } + + override CfgScope getCfgScope() { result = parameter.getCallable() } + + override Location getLocationImpl() { result = parameter.getLocation() } + + override string toStringImpl() { result = parameter.toString() } + } + + /** + * The value of the `self` parameter at function entry, viewed as a node in a data + * flow graph. + */ + class SelfParameterNode extends ParameterNodeImpl, TSelfParameterNode { + private MethodBase method; + + SelfParameterNode() { this = TSelfParameterNode(method) } + + final MethodBase getMethod() { result = method } + + override predicate isSourceParameterOf(Callable c, int i) { method = c and i = -1 } + + override CfgScope getCfgScope() { result = method } + + override Location getLocationImpl() { result = method.getLocation() } + + override string toStringImpl() { result = "self in " + method.toString() } + } + + /** + * The value of a block parameter at function entry, viewed as a node in a data + * flow graph. + */ + class BlockParameterNode extends ParameterNodeImpl, TBlockParameterNode { + private MethodBase method; + + BlockParameterNode() { this = TBlockParameterNode(method) } + + final MethodBase getMethod() { result = method } + + override Parameter getParameter() { + result = method.getAParameter() and result instanceof BlockParameter + } + + override predicate isSourceParameterOf(Callable c, int i) { c = method and i = -2 } + + override CfgScope getCfgScope() { result = method } + + override Location getLocationImpl() { + result = this.getParameter().getLocation() + or + not exists(this.getParameter()) and result = method.getLocation() + } + + override string toStringImpl() { + result = this.getParameter().toString() + or + not exists(this.getParameter()) and result = "&block" + } + } + + /** A parameter for a library callable with a flow summary. */ + class SummaryParameterNode extends ParameterNodeImpl, TSummaryParameterNode { + private FlowSummaryImpl::Public::SummarizedCallable sc; + private int pos; + + SummaryParameterNode() { this = TSummaryParameterNode(sc, pos) } + + override predicate isSourceParameterOf(Callable c, int i) { none() } + + override predicate isParameterOf(DataFlowCallable c, int i) { sc = c and i = pos } + + override CfgScope getCfgScope() { none() } + + override DataFlowCallable getEnclosingCallable() { result = sc } + + override EmptyLocation getLocationImpl() { any() } + + override string toStringImpl() { result = "parameter " + pos + " of " + sc } + } +} + +import ParameterNodes + +/** A data-flow node used to model flow summaries. */ +private class SummaryNode extends NodeImpl, TSummaryNode { + private FlowSummaryImpl::Public::SummarizedCallable c; + private FlowSummaryImpl::Private::SummaryNodeState state; + + SummaryNode() { this = TSummaryNode(c, state) } + + override CfgScope getCfgScope() { none() } + + override DataFlowCallable getEnclosingCallable() { result = c } + + override EmptyLocation getLocationImpl() { any() } + + override string toStringImpl() { result = "[summary] " + state + " in " + c } +} + +/** A data-flow node that represents a call argument. */ +abstract class ArgumentNode extends Node { + /** Holds if this argument occurs at the given position in the given call. */ + predicate argumentOf(DataFlowCall call, int pos) { this.sourceArgumentOf(call.asCall(), pos) } + + abstract predicate sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, int pos); + + /** Gets the call in which this node is an argument. */ + final DataFlowCall getCall() { this.argumentOf(result, _) } +} + +private module ArgumentNodes { + /** A data-flow node that represents an explicit call argument. */ + class ExplicitArgumentNode extends ArgumentNode { + Argument arg; + + ExplicitArgumentNode() { this.asExpr() = arg } + + override predicate sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, int pos) { + arg.isArgumentOf(call, pos) + } + } + + /** A data-flow node that represents the `self` argument of a call. */ + class SelfArgumentNode extends ExplicitArgumentNode { + SelfArgumentNode() { arg.isArgumentOf(_, -1) } + } + + /** A data-flow node that represents a block argument. */ + class BlockArgumentNode extends ArgumentNode { + BlockArgumentNode() { + this.asExpr().getExpr() instanceof BlockArgument or + exists(CfgNodes::ExprNodes::CallCfgNode c | c.getBlock() = this.asExpr()) + } + + override predicate sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, int pos) { + pos = -2 and + ( + this.asExpr() = call.getBlock() + or + exists(CfgNodes::ExprCfgNode arg, int n | + arg = call.getArgument(n) and + this.asExpr() = arg and + arg.getExpr() instanceof BlockArgument + ) + ) + } + } + + private class SummaryArgumentNode extends SummaryNode, ArgumentNode { + SummaryArgumentNode() { FlowSummaryImpl::Private::summaryArgumentNode(_, this, _) } + + override predicate sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, int pos) { none() } + + override predicate argumentOf(DataFlowCall call, int pos) { + FlowSummaryImpl::Private::summaryArgumentNode(call, this, pos) + } + } +} + +import ArgumentNodes + +/** A data-flow node that represents a value syntactically returned by a callable. */ +abstract class ReturningNode extends Node { + /** Gets the kind of this return node. */ + abstract ReturnKind getKind(); +} + +/** A data-flow node that represents a value returned by a callable. */ +abstract class ReturnNode extends Node { + /** Gets the kind of this return node. */ + abstract ReturnKind getKind(); +} + +private module ReturnNodes { + private predicate isValid(CfgNodes::ReturningCfgNode node) { + exists(ReturningStmt stmt, Callable scope | + stmt = node.getNode() and + scope = node.getScope() + | + stmt instanceof ReturnStmt and + (scope instanceof Method or scope instanceof SingletonMethod or scope instanceof Lambda) + or + stmt instanceof NextStmt and + (scope instanceof Block or scope instanceof Lambda) + or + stmt instanceof BreakStmt and + (scope instanceof Block or scope instanceof Lambda) + ) + } + + /** + * A data-flow node that represents an expression explicitly returned by + * a callable. + */ + class ExplicitReturnNode extends ReturningNode, ReturningStatementNode { + ExplicitReturnNode() { + isValid(n) and + n.getASuccessor().(CfgNodes::AnnotatedExitNode).isNormal() and + n.getScope() instanceof Callable + } + + override ReturnKind getKind() { + if n.getNode() instanceof BreakStmt + then result instanceof BreakReturnKind + else result instanceof NormalReturnKind + } + } + + pragma[noinline] + private AstNode implicitReturn(Callable c, ExprNode n) { + exists(CfgNodes::ExprCfgNode en | + en = n.getExprNode() and + en.getASuccessor().(CfgNodes::AnnotatedExitNode).isNormal() and + n.(NodeImpl).getCfgScope() = c and + result = en.getExpr() + ) + or + result = implicitReturn(c, n).getParent() + } + + /** + * A data-flow node that represents an expression implicitly returned by + * a callable. An implicit return happens when an expression can be the + * last thing that is evaluated in the body of the callable. + */ + class ExprReturnNode extends ReturningNode, ExprNode { + ExprReturnNode() { exists(Callable c | implicitReturn(c, this) = c.getAStmt()) } + + override ReturnKind getKind() { result instanceof NormalReturnKind } + } + + /** + * A synthetic data-flow node for joining flow from different syntactic + * returns into a single node. + * + * This node only exists to avoid computing the product of a large fan-in + * with a large fan-out. + */ + class SynthReturnNode extends NodeImpl, ReturnNode, TSynthReturnNode { + private CfgScope scope; + private ReturnKind kind; + + SynthReturnNode() { this = TSynthReturnNode(scope, kind) } + + /** Gets a syntactic return node that flows into this synthetic node. */ + ReturningNode getAnInput() { + result.(NodeImpl).getCfgScope() = scope and + result.getKind() = kind + } + + override ReturnKind getKind() { result = kind } + + override CfgScope getCfgScope() { result = scope } + + override Location getLocationImpl() { result = scope.getLocation() } + + override string toStringImpl() { result = "return " + kind + " in " + scope } + } + + private class SummaryReturnNode extends SummaryNode, ReturnNode { + private ReturnKind rk; + + SummaryReturnNode() { FlowSummaryImpl::Private::summaryReturnNode(this, rk) } + + override ReturnKind getKind() { result = rk } + } +} + +import ReturnNodes + +/** A data-flow node that represents the output of a call. */ +abstract class OutNode extends Node { + /** Gets the underlying call, where this node is a corresponding output of kind `kind`. */ + abstract DataFlowCall getCall(ReturnKind kind); +} + +private module OutNodes { + /** + * A data-flow node that reads a value returned directly by a callable, + * either via a call or a `yield` of a block. + */ + class ExprOutNode extends OutNode, ExprNode { + private DataFlowCall call; + + ExprOutNode() { call.asCall() = this.getExprNode() } + + override DataFlowCall getCall(ReturnKind kind) { + result = call and + kind instanceof NormalReturnKind + } + } + + private class SummaryOutNode extends SummaryNode, OutNode { + SummaryOutNode() { FlowSummaryImpl::Private::summaryOutNode(_, this, _) } + + override DataFlowCall getCall(ReturnKind kind) { + FlowSummaryImpl::Private::summaryOutNode(result, this, kind) + } + } +} + +import OutNodes + +predicate jumpStep(Node pred, Node succ) { + SsaImpl::captureFlowIn(pred.(SsaDefinitionNode).getDefinition(), + succ.(SsaDefinitionNode).getDefinition()) + or + SsaImpl::captureFlowOut(pred.(SsaDefinitionNode).getDefinition(), + succ.(SsaDefinitionNode).getDefinition()) + or + exists(Self s, Method m | + s = succ.asExpr().getExpr() and + pred.(SelfParameterNode).getMethod() = m and + m = s.getEnclosingMethod() and + m != s.getEnclosingCallable() + ) + or + succ.asExpr().getExpr().(ConstantReadAccess).getValue() = pred.asExpr().getExpr() +} + +predicate storeStep(Node node1, Content c, Node node2) { + FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, c, node2) +} + +predicate readStep(Node node1, Content c, Node node2) { + FlowSummaryImpl::Private::Steps::summaryReadStep(node1, c, node2) +} + +/** + * Holds if values stored inside content `c` are cleared at node `n`. For example, + * any value stored inside `f` is cleared at the pre-update node associated with `x` + * in `x.f = newValue`. + */ +predicate clearsContent(Node n, Content c) { + storeStep(_, c, n) + or + FlowSummaryImpl::Private::Steps::summaryClearsContent(n, c) +} + +private newtype TDataFlowType = TTodoDataFlowType() + +class DataFlowType extends TDataFlowType { + string toString() { result = "" } +} + +/** Gets the type of `n` used for type pruning. */ +DataFlowType getNodeType(NodeImpl n) { any() } + +/** Gets a string representation of a `DataFlowType`. */ +string ppReprType(DataFlowType t) { result = t.toString() } + +/** + * Holds if `t1` and `t2` are compatible, that is, whether data can flow from + * a node of type `t1` to a node of type `t2`. + */ +pragma[inline] +predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { any() } + +/** + * A node associated with an object after an operation that might have + * changed its state. + * + * This can be either the argument to a callable after the callable returns + * (which might have mutated the argument), or the qualifier of a field after + * an update to the field. + * + * Nodes corresponding to AST elements, for example `ExprNode`, usually refer + * to the value before the update. + */ +abstract class PostUpdateNode extends Node { + /** Gets the node before the state update. */ + abstract Node getPreUpdateNode(); +} + +private module PostUpdateNodes { + class ExprPostUpdateNode extends PostUpdateNode, NodeImpl, TExprPostUpdateNode { + private CfgNodes::ExprCfgNode e; + + ExprPostUpdateNode() { this = TExprPostUpdateNode(e) } + + override ExprNode getPreUpdateNode() { e = result.getExprNode() } + + override CfgScope getCfgScope() { result = e.getExpr().getCfgScope() } + + override Location getLocationImpl() { result = e.getLocation() } + + override string toStringImpl() { result = "[post] " + e.toString() } + } + + private class SummaryPostUpdateNode extends SummaryNode, PostUpdateNode { + private Node pre; + + SummaryPostUpdateNode() { FlowSummaryImpl::Private::summaryPostUpdateNode(this, pre) } + + override Node getPreUpdateNode() { result = pre } + } +} + +private import PostUpdateNodes + +/** A node that performs a type cast. */ +class CastNode extends Node { + CastNode() { this instanceof ReturningNode } +} + +class DataFlowExpr = CfgNodes::ExprCfgNode; + +int accessPathLimit() { result = 5 } + +/** + * Holds if access paths with `c` at their head always should be tracked at high + * precision. This disables adaptive access path precision for such access paths. + */ +predicate forceHighPrecision(Content c) { none() } + +/** The unit type. */ +private newtype TUnit = TMkUnit() + +/** The trivial type with a single element. */ +class Unit extends TUnit { + /** Gets a textual representation of this element. */ + string toString() { result = "unit" } +} + +/** + * Holds if `n` does not require a `PostUpdateNode` as it either cannot be + * modified or its modification cannot be observed, for example if it is a + * freshly created object that is not saved in a variable. + * + * This predicate is only used for consistency checks. + */ +predicate isImmutableOrUnobservable(Node n) { n instanceof BlockArgumentNode } + +/** + * Holds if the node `n` is unreachable when the call context is `call`. + */ +predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } + +newtype LambdaCallKind = + TYieldCallKind() or + TLambdaCallKind() + +/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */ +predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { + kind = TYieldCallKind() and + creation.asExpr().getExpr() = c.asCallable().(Block) + or + kind = TLambdaCallKind() and + ( + creation.asExpr().getExpr() = c.asCallable().(Lambda) + or + creation.asExpr() = + any(CfgNodes::ExprNodes::MethodCallCfgNode mc | + c.asCallable() = mc.getBlock().getExpr() and + mc.getExpr().getMethodName() = "lambda" + ) + ) +} + +/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */ +predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { + kind = TYieldCallKind() and + receiver.(BlockParameterNode).getMethod() = + call.asCall().getExpr().(YieldCall).getEnclosingMethod() + or + kind = TLambdaCallKind() and + call.asCall() = + any(CfgNodes::ExprNodes::MethodCallCfgNode mc | + receiver.asExpr() = mc.getReceiver() and + mc.getExpr().getMethodName() = "call" + ) + or + receiver = call.(SummaryCall).getReceiver() and + if receiver.(ParameterNode).isParameterOf(_, -2) + then kind = TYieldCallKind() + else kind = TLambdaCallKind() +} + +/** Extra data-flow steps needed for lambda flow analysis. */ +predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() } + +/** + * Holds if flow is allowed to pass from parameter `p` and back to itself as a + * side-effect, resulting in a summary from `p` to itself. + * + * One example would be to allow flow like `p.foo = p.bar;`, which is disallowed + * by default as a heuristic. + */ +predicate allowParameterReturnInSelf(ParameterNode p) { none() } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll new file mode 100644 index 00000000000..c24ceac372e --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll @@ -0,0 +1,210 @@ +private import ruby +private import DataFlowDispatch +private import DataFlowPrivate +private import codeql.ruby.CFG +private import codeql.ruby.typetracking.TypeTracker +private import codeql.ruby.dataflow.SSA +private import FlowSummaryImpl as FlowSummaryImpl + +/** + * An element, viewed as a node in a data flow graph. Either an expression + * (`ExprNode`) or a parameter (`ParameterNode`). + */ +class Node extends TNode { + /** Gets the expression corresponding to this node, if any. */ + CfgNodes::ExprCfgNode asExpr() { result = this.(ExprNode).getExprNode() } + + /** Gets the parameter corresponding to this node, if any. */ + Parameter asParameter() { result = this.(ParameterNode).getParameter() } + + /** Gets a textual representation of this node. */ + // TODO: cache + final string toString() { result = this.(NodeImpl).toStringImpl() } + + /** Gets the location of this node. */ + // TODO: cache + final Location getLocation() { result = this.(NodeImpl).getLocationImpl() } + + DataFlowCallable getEnclosingCallable() { result = TCfgScope(this.(NodeImpl).getCfgScope()) } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + + /** + * Gets a local source node from which data may flow to this node in zero or more local data-flow steps. + */ + LocalSourceNode getALocalSource() { result.flowsTo(this) } +} + +/** A data-flow node corresponding to a call in the control-flow graph. */ +class CallNode extends LocalSourceNode { + private CfgNodes::ExprNodes::CallCfgNode node; + + CallNode() { node = this.asExpr() } + + /** Gets the data-flow node corresponding to the receiver of the call corresponding to this data-flow node */ + Node getReceiver() { result.asExpr() = node.getReceiver() } + + /** Gets the data-flow node corresponding to the `n`th argument of the call corresponding to this data-flow node */ + Node getArgument(int n) { result.asExpr() = node.getArgument(n) } + + /** Gets the data-flow node corresponding to the named argument of the call corresponding to this data-flow node */ + Node getKeywordArgument(string name) { result.asExpr() = node.getKeywordArgument(name) } +} + +/** + * An expression, viewed as a node in a data flow graph. + * + * Note that because of control-flow splitting, one `Expr` may correspond + * to multiple `ExprNode`s, just like it may correspond to multiple + * `ControlFlow::Node`s. + */ +class ExprNode extends Node, TExprNode { + private CfgNodes::ExprCfgNode n; + + ExprNode() { this = TExprNode(n) } + + /** Gets the expression corresponding to this node. */ + CfgNodes::ExprCfgNode getExprNode() { result = n } +} + +/** + * The value of a parameter at function entry, viewed as a node in a data + * flow graph. + */ +class ParameterNode extends Node, TParameterNode { + /** Gets the parameter corresponding to this node, if any. */ + Parameter getParameter() { none() } + + /** + * Holds if this node is the parameter of callable `c` at the specified + * (zero-based) position. + */ + predicate isParameterOf(DataFlowCallable c, int i) { none() } +} + +/** + * A data-flow node that is a source of local flow. + */ +class LocalSourceNode extends Node { + LocalSourceNode() { isLocalSourceNode(this) } + + /** Holds if this `LocalSourceNode` can flow to `nodeTo` in one or more local flow steps. */ + pragma[inline] + predicate flowsTo(Node nodeTo) { hasLocalSource(nodeTo, this) } + + /** + * Gets a node that this node may flow to using one heap and/or interprocedural step. + * + * See `TypeTracker` for more details about how to use this. + */ + pragma[inline] + LocalSourceNode track(TypeTracker t2, TypeTracker t) { t = t2.step(this, result) } +} + +predicate hasLocalSource(Node sink, Node source) { + // Declaring `source` to be a `SourceNode` currently causes a redundant check in the + // recursive case, so instead we check it explicitly here. + source = sink and + source instanceof LocalSourceNode + or + exists(Node mid | + hasLocalSource(mid, source) and + localFlowStepTypeTracker(mid, sink) + ) +} + +/** Gets a node corresponding to expression `e`. */ +ExprNode exprNode(CfgNodes::ExprCfgNode e) { result.getExprNode() = e } + +/** + * Gets the node corresponding to the value of parameter `p` at function entry. + */ +ParameterNode parameterNode(Parameter p) { result.getParameter() = p } + +/** + * Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local + * (intra-procedural) step. + */ +predicate localFlowStep = localFlowStepImpl/2; + +/** + * Holds if data flows from `source` to `sink` in zero or more local + * (intra-procedural) steps. + */ +predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) } + +/** + * Holds if data can flow from `e1` to `e2` in zero or more + * local (intra-procedural) steps. + */ +predicate localExprFlow(CfgNodes::ExprCfgNode e1, CfgNodes::ExprCfgNode e2) { + localFlow(exprNode(e1), exprNode(e2)) +} + +/** + * A reference contained in an object. This is either a field, a property, + * or an element in a collection. + */ +class Content extends TContent { + /** Gets a textual representation of this content. */ + string toString() { none() } + + /** Gets the location of this content. */ + Location getLocation() { none() } +} + +/** + * A guard that validates some expression. + * + * To use this in a configuration, extend the class and provide a + * characteristic predicate precisely specifying the guard, and override + * `checks` to specify what is being validated and in which branch. + * + * It is important that all extending classes in scope are disjoint. + */ +abstract class BarrierGuard extends CfgNodes::ExprCfgNode { + private ConditionBlock conditionBlock; + + BarrierGuard() { this = conditionBlock.getLastNode() } + + /** Holds if this guard controls block `b` upon evaluating to `branch`. */ + private predicate controlsBlock(BasicBlock bb, boolean branch) { + exists(SuccessorTypes::BooleanSuccessor s | s.getValue() = branch | + conditionBlock.controls(bb, s) + ) + } + + /** + * Holds if this guard validates `expr` upon evaluating to `branch`. + * For example, the following code validates `foo` when the condition + * `foo == "foo"` is true. + * ```ruby + * if foo == "foo" + * do_something + * else + * do_something_else + * end + * ``` + */ + abstract predicate checks(CfgNode expr, boolean branch); + + final Node getAGuardedNode() { + exists(boolean branch, CfgNodes::ExprCfgNode testedNode, Ssa::Definition def | + def.getARead() = testedNode and + def.getARead() = result.asExpr() and + this.checks(testedNode, branch) and + this.controlsBlock(result.asExpr().getBasicBlock(), branch) + ) + } +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll new file mode 100644 index 00000000000..5955285bd6f --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll @@ -0,0 +1,1020 @@ +/** + * Provides classes and predicates for defining flow summaries. + * + * The definitions in this file are language-independent, and language-specific + * definitions are passed in via the `DataFlowImplSpecific` and + * `FlowSummaryImplSpecific` modules. + */ + +private import FlowSummaryImplSpecific +private import DataFlowImplSpecific::Private +private import DataFlowImplSpecific::Public +private import DataFlowImplCommon + +/** Provides classes and predicates for defining flow summaries. */ +module Public { + private import Private + + /** + * A component used in a flow summary. + * + * Either a parameter or an argument at a given position, a specific + * content type, or a return kind. + */ + class SummaryComponent extends TSummaryComponent { + /** Gets a textual representation of this summary component. */ + string toString() { + exists(Content c | this = TContentSummaryComponent(c) and result = c.toString()) + or + exists(int i | this = TParameterSummaryComponent(i) and result = "parameter " + i) + or + exists(int i | this = TArgumentSummaryComponent(i) and result = "argument " + i) + or + exists(ReturnKind rk | this = TReturnSummaryComponent(rk) and result = "return (" + rk + ")") + } + } + + /** Provides predicates for constructing summary components. */ + module SummaryComponent { + /** Gets a summary component for content `c`. */ + SummaryComponent content(Content c) { result = TContentSummaryComponent(c) } + + /** Gets a summary component for parameter `i`. */ + SummaryComponent parameter(int i) { result = TParameterSummaryComponent(i) } + + /** Gets a summary component for argument `i`. */ + SummaryComponent argument(int i) { result = TArgumentSummaryComponent(i) } + + /** Gets a summary component for a return of kind `rk`. */ + SummaryComponent return(ReturnKind rk) { result = TReturnSummaryComponent(rk) } + } + + /** + * A (non-empty) stack of summary components. + * + * A stack is used to represent where data is read from (input) or where it + * is written to (output). For example, an input stack `[Field f, Argument 0]` + * means that data is read from field `f` from the `0`th argument, while an + * output stack `[Field g, Return]` means that data is written to the field + * `g` of the returned object. + */ + class SummaryComponentStack extends TSummaryComponentStack { + /** Gets the head of this stack. */ + SummaryComponent head() { + this = TSingletonSummaryComponentStack(result) or + this = TConsSummaryComponentStack(result, _) + } + + /** Gets the tail of this stack, if any. */ + SummaryComponentStack tail() { this = TConsSummaryComponentStack(_, result) } + + /** Gets the length of this stack. */ + int length() { + this = TSingletonSummaryComponentStack(_) and result = 1 + or + result = 1 + this.tail().length() + } + + /** Gets the stack obtained by dropping the first `i` elements, if any. */ + SummaryComponentStack drop(int i) { + i = 0 and result = this + or + result = this.tail().drop(i - 1) + } + + /** Holds if this stack contains summary component `c`. */ + predicate contains(SummaryComponent c) { c = this.drop(_).head() } + + /** Gets a textual representation of this stack. */ + string toString() { + exists(SummaryComponent head, SummaryComponentStack tail | + head = this.head() and + tail = this.tail() and + result = head + " of " + tail + ) + or + exists(SummaryComponent c | + this = TSingletonSummaryComponentStack(c) and + result = c.toString() + ) + } + } + + /** Provides predicates for constructing stacks of summary components. */ + module SummaryComponentStack { + /** Gets a singleton stack containing `c`. */ + SummaryComponentStack singleton(SummaryComponent c) { + result = TSingletonSummaryComponentStack(c) + } + + /** + * Gets the stack obtained by pushing `head` onto `tail`. + * + * Make sure to override `RequiredSummaryComponentStack::required()` in order + * to ensure that the constructed stack exists. + */ + SummaryComponentStack push(SummaryComponent head, SummaryComponentStack tail) { + result = TConsSummaryComponentStack(head, tail) + } + + /** Gets a singleton stack for argument `i`. */ + SummaryComponentStack argument(int i) { result = singleton(SummaryComponent::argument(i)) } + + /** Gets a singleton stack representing a return of kind `rk`. */ + SummaryComponentStack return(ReturnKind rk) { result = singleton(SummaryComponent::return(rk)) } + } + + /** + * A class that exists for QL technical reasons only (the IPA type used + * to represent component stacks needs to be bounded). + */ + abstract class RequiredSummaryComponentStack extends SummaryComponentStack { + /** + * Holds if the stack obtained by pushing `head` onto `tail` is required. + */ + abstract predicate required(SummaryComponent c); + } + + /** A callable with a flow summary. */ + abstract class SummarizedCallable extends DataFlowCallable { + /** + * Holds if data may flow from `input` to `output` through this callable. + * + * `preservesValue` indicates whether this is a value-preserving step + * or a taint-step. + * + * Input specifications are restricted to stacks that end with + * `SummaryComponent::argument(_)`, preceded by zero or more + * `SummaryComponent::return(_)` or `SummaryComponent::content(_)` components. + * + * Output specifications are restricted to stacks that end with + * `SummaryComponent::return(_)` or `SummaryComponent::argument(_)`. + * + * Output stacks ending with `SummaryComponent::return(_)` can be preceded by zero + * or more `SummaryComponent::content(_)` components. + * + * Output stacks ending with `SummaryComponent::argument(_)` can be preceded by an + * optional `SummaryComponent::parameter(_)` component, which in turn can be preceded + * by zero or more `SummaryComponent::content(_)` components. + */ + pragma[nomagic] + predicate propagatesFlow( + SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue + ) { + none() + } + + /** + * Holds if values stored inside `content` are cleared on objects passed as + * the `i`th argument to this callable. + */ + pragma[nomagic] + predicate clearsContent(int i, Content content) { none() } + } +} + +/** + * Provides predicates for compiling flow summaries down to atomic local steps, + * read steps, and store steps. + */ +module Private { + private import Public + + newtype TSummaryComponent = + TContentSummaryComponent(Content c) or + TParameterSummaryComponent(int i) { parameterPosition(i) } or + TArgumentSummaryComponent(int i) { parameterPosition(i) } or + TReturnSummaryComponent(ReturnKind rk) + + private TSummaryComponent thisParam() { + result = TParameterSummaryComponent(instanceParameterPosition()) + } + + newtype TSummaryComponentStack = + TSingletonSummaryComponentStack(SummaryComponent c) or + TConsSummaryComponentStack(SummaryComponent head, SummaryComponentStack tail) { + tail.(RequiredSummaryComponentStack).required(head) + or + tail.(RequiredSummaryComponentStack).required(TParameterSummaryComponent(_)) and + head = thisParam() + } + + pragma[nomagic] + private predicate summary( + SummarizedCallable c, SummaryComponentStack input, SummaryComponentStack output, + boolean preservesValue + ) { + c.propagatesFlow(input, output, preservesValue) + or + // observe side effects of callbacks on input arguments + c.propagatesFlow(output, input, preservesValue) and + preservesValue = true and + isCallbackParameter(input) and + isContentOfArgument(output) + or + // flow from the receiver of a callback into the instance-parameter + exists(SummaryComponentStack s, SummaryComponentStack callbackRef | + c.propagatesFlow(s, _, _) or c.propagatesFlow(_, s, _) + | + callbackRef = s.drop(_) and + (isCallbackParameter(callbackRef) or callbackRef.head() = TReturnSummaryComponent(_)) and + input = callbackRef.tail() and + output = TConsSummaryComponentStack(thisParam(), input) and + preservesValue = true + ) + } + + private predicate isCallbackParameter(SummaryComponentStack s) { + s.head() = TParameterSummaryComponent(_) and exists(s.tail()) + } + + private predicate isContentOfArgument(SummaryComponentStack s) { + s.head() = TContentSummaryComponent(_) and isContentOfArgument(s.tail()) + or + s = TSingletonSummaryComponentStack(TArgumentSummaryComponent(_)) + } + + private predicate outputState(SummarizedCallable c, SummaryComponentStack s) { + summary(c, _, s, _) + or + exists(SummaryComponentStack out | + outputState(c, out) and + out.head() = TContentSummaryComponent(_) and + s = out.tail() + ) + or + // Add the argument node corresponding to the requested post-update node + inputState(c, s) and isCallbackParameter(s) + } + + private predicate inputState(SummarizedCallable c, SummaryComponentStack s) { + summary(c, s, _, _) + or + exists(SummaryComponentStack inp | inputState(c, inp) and s = inp.tail()) + or + exists(SummaryComponentStack out | + outputState(c, out) and + out.head() = TParameterSummaryComponent(_) and + s = out.tail() + ) + } + + private newtype TSummaryNodeState = + TSummaryNodeInputState(SummaryComponentStack s) { inputState(_, s) } or + TSummaryNodeOutputState(SummaryComponentStack s) { outputState(_, s) } or + TSummaryNodeClearsContentState(int i, boolean post) { + any(SummarizedCallable sc).clearsContent(i, _) and post in [false, true] + } + + /** + * A state used to break up (complex) flow summaries into atomic flow steps. + * For a flow summary + * + * ```ql + * propagatesFlow( + * SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue + * ) + * ``` + * + * the following states are used: + * + * - `TSummaryNodeInputState(SummaryComponentStack s)`: + * this state represents that the components in `s` _have been read_ from the + * input. + * - `TSummaryNodeOutputState(SummaryComponentStack s)`: + * this state represents that the components in `s` _remain to be written_ to + * the output. + */ + class SummaryNodeState extends TSummaryNodeState { + /** Holds if this state is a valid input state for `c`. */ + pragma[nomagic] + predicate isInputState(SummarizedCallable c, SummaryComponentStack s) { + this = TSummaryNodeInputState(s) and + inputState(c, s) + } + + /** Holds if this state is a valid output state for `c`. */ + pragma[nomagic] + predicate isOutputState(SummarizedCallable c, SummaryComponentStack s) { + this = TSummaryNodeOutputState(s) and + outputState(c, s) + } + + /** Gets a textual representation of this state. */ + string toString() { + exists(SummaryComponentStack s | + this = TSummaryNodeInputState(s) and + result = "read: " + s + ) + or + exists(SummaryComponentStack s | + this = TSummaryNodeOutputState(s) and + result = "to write: " + s + ) + or + exists(int i, boolean post, string postStr | + this = TSummaryNodeClearsContentState(i, post) and + (if post = true then postStr = " (post)" else postStr = "") and + result = "clear: " + i + postStr + ) + } + } + + /** + * Holds if `state` represents having read the `i`th argument for `c`. In this case + * we are not synthesizing a data-flow node, but instead assume that a relevant + * parameter node already exists. + */ + private predicate parameterReadState(SummarizedCallable c, SummaryNodeState state, int i) { + state.isInputState(c, SummaryComponentStack::argument(i)) + } + + /** + * Holds if a synthesized summary node is needed for the state `state` in summarized + * callable `c`. + */ + predicate summaryNodeRange(SummarizedCallable c, SummaryNodeState state) { + state.isInputState(c, _) and + not parameterReadState(c, state, _) + or + state.isOutputState(c, _) + or + exists(int i | + c.clearsContent(i, _) and + state = TSummaryNodeClearsContentState(i, _) + ) + } + + pragma[noinline] + private Node summaryNodeInputState(SummarizedCallable c, SummaryComponentStack s) { + exists(SummaryNodeState state | state.isInputState(c, s) | + result = summaryNode(c, state) + or + exists(int i | + parameterReadState(c, state, i) and + result.(ParamNode).isParameterOf(c, i) + ) + ) + } + + pragma[noinline] + private Node summaryNodeOutputState(SummarizedCallable c, SummaryComponentStack s) { + exists(SummaryNodeState state | + state.isOutputState(c, s) and + result = summaryNode(c, state) + ) + } + + /** + * Holds if a write targets `post`, which is a post-update node for the `i`th + * parameter of `c`. + */ + private predicate isParameterPostUpdate(Node post, SummarizedCallable c, int i) { + post = summaryNodeOutputState(c, SummaryComponentStack::argument(i)) + } + + /** Holds if a parameter node is required for the `i`th parameter of `c`. */ + predicate summaryParameterNodeRange(SummarizedCallable c, int i) { + parameterReadState(c, _, i) + or + isParameterPostUpdate(_, c, i) + or + c.clearsContent(i, _) + } + + private predicate callbackOutput( + SummarizedCallable c, SummaryComponentStack s, Node receiver, ReturnKind rk + ) { + any(SummaryNodeState state).isInputState(c, s) and + s.head() = TReturnSummaryComponent(rk) and + receiver = summaryNodeInputState(c, s.drop(1)) + } + + private predicate callbackInput( + SummarizedCallable c, SummaryComponentStack s, Node receiver, int i + ) { + any(SummaryNodeState state).isOutputState(c, s) and + s.head() = TParameterSummaryComponent(i) and + receiver = summaryNodeInputState(c, s.drop(1)) + } + + /** Holds if a call targeting `receiver` should be synthesized inside `c`. */ + predicate summaryCallbackRange(SummarizedCallable c, Node receiver) { + callbackOutput(c, _, receiver, _) + or + callbackInput(c, _, receiver, _) + } + + /** + * Gets the type of synthesized summary node `n`. + * + * The type is computed based on the language-specific predicates + * `getContentType()`, `getReturnType()`, `getCallbackParameterType()`, and + * `getCallbackReturnType()`. + */ + DataFlowType summaryNodeType(Node n) { + exists(Node pre | + summaryPostUpdateNode(n, pre) and + result = getNodeType(pre) + ) + or + exists(SummarizedCallable c, SummaryComponentStack s, SummaryComponent head | head = s.head() | + n = summaryNodeInputState(c, s) and + ( + exists(Content cont | + head = TContentSummaryComponent(cont) and result = getContentType(cont) + ) + or + exists(ReturnKind rk | + head = TReturnSummaryComponent(rk) and + result = + getCallbackReturnType(getNodeType(summaryNodeInputState(pragma[only_bind_out](c), + s.drop(1))), rk) + ) + ) + or + n = summaryNodeOutputState(c, s) and + ( + exists(Content cont | + head = TContentSummaryComponent(cont) and result = getContentType(cont) + ) + or + s.length() = 1 and + exists(ReturnKind rk | + head = TReturnSummaryComponent(rk) and + result = getReturnType(c, rk) + ) + or + exists(int i | head = TParameterSummaryComponent(i) | + result = + getCallbackParameterType(getNodeType(summaryNodeInputState(pragma[only_bind_out](c), + s.drop(1))), i) + ) + ) + ) + or + exists(SummarizedCallable c, int i, ParamNode p | + n = summaryNode(c, TSummaryNodeClearsContentState(i, false)) and + p.isParameterOf(c, i) and + result = getNodeType(p) + ) + } + + /** Holds if summary node `out` contains output of kind `rk` from call `c`. */ + predicate summaryOutNode(DataFlowCall c, Node out, ReturnKind rk) { + exists(SummarizedCallable callable, SummaryComponentStack s, Node receiver | + callbackOutput(callable, s, receiver, rk) and + out = summaryNodeInputState(callable, s) and + c = summaryDataFlowCall(receiver) + ) + } + + /** Holds if summary node `arg` is the `i`th argument of call `c`. */ + predicate summaryArgumentNode(DataFlowCall c, Node arg, int i) { + exists(SummarizedCallable callable, SummaryComponentStack s, Node receiver | + callbackInput(callable, s, receiver, i) and + arg = summaryNodeOutputState(callable, s) and + c = summaryDataFlowCall(receiver) + ) + } + + /** Holds if summary node `post` is a post-update node with pre-update node `pre`. */ + predicate summaryPostUpdateNode(Node post, Node pre) { + exists(SummarizedCallable c, int i | + isParameterPostUpdate(post, c, i) and + pre.(ParamNode).isParameterOf(c, i) + or + pre = summaryNode(c, TSummaryNodeClearsContentState(i, false)) and + post = summaryNode(c, TSummaryNodeClearsContentState(i, true)) + ) + or + exists(SummarizedCallable callable, SummaryComponentStack s | + callbackInput(callable, s, _, _) and + pre = summaryNodeOutputState(callable, s) and + post = summaryNodeInputState(callable, s) + ) + } + + /** Holds if summary node `ret` is a return node of kind `rk`. */ + predicate summaryReturnNode(Node ret, ReturnKind rk) { + exists(SummarizedCallable callable, SummaryComponentStack s | + ret = summaryNodeOutputState(callable, s) and + s = TSingletonSummaryComponentStack(TReturnSummaryComponent(rk)) + ) + } + + /** + * Holds if flow is allowed to pass from parameter `p`, to a return + * node, and back out to `p`. + */ + predicate summaryAllowParameterReturnInSelf(ParamNode p) { + exists(SummarizedCallable c, int i | + c.clearsContent(i, _) and + p.isParameterOf(c, i) + ) + } + + /** Provides a compilation of flow summaries to atomic data-flow steps. */ + module Steps { + /** + * Holds if there is a local step from `pred` to `succ`, which is synthesized + * from a flow summary. + */ + predicate summaryLocalStep(Node pred, Node succ, boolean preservesValue) { + exists( + SummarizedCallable c, SummaryComponentStack inputContents, + SummaryComponentStack outputContents + | + summary(c, inputContents, outputContents, preservesValue) and + pred = summaryNodeInputState(c, inputContents) and + succ = summaryNodeOutputState(c, outputContents) + | + preservesValue = true + or + preservesValue = false and not summary(c, inputContents, outputContents, true) + ) + or + // If flow through a method updates a parameter from some input A, and that + // parameter also is returned through B, then we'd like a combined flow from A + // to B as well. As an example, this simplifies modeling of fluent methods: + // for `StringBuilder.append(x)` with a specified value flow from qualifier to + // return value and taint flow from argument 0 to the qualifier, then this + // allows us to infer taint flow from argument 0 to the return value. + succ instanceof ParamNode and + summaryPostUpdateNode(pred, succ) and + preservesValue = true + or + // Similarly we would like to chain together summaries where values get passed + // into callbacks along the way. + pred instanceof ArgNode and + summaryPostUpdateNode(succ, pred) and + preservesValue = true + or + exists(SummarizedCallable c, int i | + pred.(ParamNode).isParameterOf(c, i) and + succ = summaryNode(c, TSummaryNodeClearsContentState(i, _)) and + preservesValue = true + ) + } + + /** + * Holds if there is a read step of content `c` from `pred` to `succ`, which + * is synthesized from a flow summary. + */ + predicate summaryReadStep(Node pred, Content c, Node succ) { + exists(SummarizedCallable sc, SummaryComponentStack s | + pred = summaryNodeInputState(sc, s.drop(1)) and + succ = summaryNodeInputState(sc, s) and + SummaryComponent::content(c) = s.head() + ) + } + + /** + * Holds if there is a store step of content `c` from `pred` to `succ`, which + * is synthesized from a flow summary. + */ + predicate summaryStoreStep(Node pred, Content c, Node succ) { + exists(SummarizedCallable sc, SummaryComponentStack s | + pred = summaryNodeOutputState(sc, s) and + succ = summaryNodeOutputState(sc, s.drop(1)) and + SummaryComponent::content(c) = s.head() + ) + } + + /** + * Holds if values stored inside content `c` are cleared at `n`. `n` is a + * synthesized summary node, so in order for values to be cleared at calls + * to the relevant method, it is important that flow does not pass over + * the argument, either via use-use flow or def-use flow. + * + * Example: + * + * ``` + * a.b = taint; + * a.clearB(); // assume we have a flow summary for `clearB` that clears `b` on the qualifier + * sink(a.b); + * ``` + * + * In the above, flow should not pass from `a` on the first line (or the second + * line) to `a` on the third line. Instead, there will be synthesized flow from + * `a` on line 2 to the post-update node for `a` on that line (via an intermediate + * node where field `b` is cleared). + */ + predicate summaryClearsContent(Node n, Content c) { + exists(SummarizedCallable sc, int i | + n = summaryNode(sc, TSummaryNodeClearsContentState(i, true)) and + sc.clearsContent(i, c) + ) + } + + /** + * Holds if values stored inside content `c` are cleared inside a + * callable to which `arg` is an argument. + * + * In such cases, it is important to prevent use-use flow out of + * `arg` (see comment for `summaryClearsContent`). + */ + predicate summaryClearsContentArg(ArgNode arg, Content c) { + exists(DataFlowCall call, int i | + viableCallable(call).(SummarizedCallable).clearsContent(i, c) and + arg.argumentOf(call, i) + ) + } + + pragma[nomagic] + private ParamNode summaryArgParam(ArgNode arg, ReturnKindExt rk, OutNodeExt out) { + exists(DataFlowCall call, int pos, SummarizedCallable callable | + arg.argumentOf(call, pos) and + viableCallable(call) = callable and + result.isParameterOf(callable, pos) and + out = rk.getAnOutNode(call) + ) + } + + /** + * Holds if `arg` flows to `out` using a simple flow summary, that is, a flow + * summary without reads and stores. + * + * NOTE: This step should not be used in global data-flow/taint-tracking, but may + * be useful to include in the exposed local data-flow/taint-tracking relations. + */ + predicate summaryThroughStep(ArgNode arg, Node out, boolean preservesValue) { + exists(ReturnKindExt rk, ReturnNodeExt ret | + summaryLocalStep(summaryArgParam(arg, rk, out), ret, preservesValue) and + ret.getKind() = rk + ) + } + + /** + * Holds if there is a read(+taint) of `c` from `arg` to `out` using a + * flow summary. + * + * NOTE: This step should not be used in global data-flow/taint-tracking, but may + * be useful to include in the exposed local data-flow/taint-tracking relations. + */ + predicate summaryGetterStep(ArgNode arg, Content c, Node out) { + exists(ReturnKindExt rk, Node mid, ReturnNodeExt ret | + summaryReadStep(summaryArgParam(arg, rk, out), c, mid) and + summaryLocalStep(mid, ret, _) and + ret.getKind() = rk + ) + } + + /** + * Holds if there is a (taint+)store of `arg` into content `c` of `out` using a + * flow summary. + * + * NOTE: This step should not be used in global data-flow/taint-tracking, but may + * be useful to include in the exposed local data-flow/taint-tracking relations. + */ + predicate summarySetterStep(ArgNode arg, Content c, Node out) { + exists(ReturnKindExt rk, Node mid, ReturnNodeExt ret | + summaryLocalStep(summaryArgParam(arg, rk, out), mid, _) and + summaryStoreStep(mid, c, ret) and + ret.getKind() = rk + ) + } + } + + /** + * Provides a means of translating externally (e.g., CSV) defined flow + * summaries into a `SummarizedCallable`s. + */ + module External { + /** Holds if `spec` is a relevant external specification. */ + private predicate relevantSpec(string spec) { + summaryElement(_, spec, _, _) or + summaryElement(_, _, spec, _) or + sourceElement(_, spec, _) or + sinkElement(_, spec, _) + } + + /** Holds if the `n`th component of specification `s` is `c`. */ + predicate specSplit(string s, string c, int n) { relevantSpec(s) and s.splitAt(" of ", n) = c } + + /** Holds if specification `s` has length `len`. */ + predicate specLength(string s, int len) { len = 1 + max(int n | specSplit(s, _, n)) } + + /** Gets the last component of specification `s`. */ + string specLast(string s) { + exists(int len | + specLength(s, len) and + specSplit(s, result, len - 1) + ) + } + + /** Holds if specification component `c` parses as parameter `n`. */ + predicate parseParam(string c, int n) { + specSplit(_, c, _) and + ( + c.regexpCapture("Parameter\\[([-0-9]+)\\]", 1).toInt() = n + or + exists(int n1, int n2 | + c.regexpCapture("Parameter\\[([-0-9]+)\\.\\.([0-9]+)\\]", 1).toInt() = n1 and + c.regexpCapture("Parameter\\[([-0-9]+)\\.\\.([0-9]+)\\]", 2).toInt() = n2 and + n = [n1 .. n2] + ) + ) + } + + /** Holds if specification component `c` parses as argument `n`. */ + predicate parseArg(string c, int n) { + specSplit(_, c, _) and + ( + c.regexpCapture("Argument\\[([-0-9]+)\\]", 1).toInt() = n + or + exists(int n1, int n2 | + c.regexpCapture("Argument\\[([-0-9]+)\\.\\.([0-9]+)\\]", 1).toInt() = n1 and + c.regexpCapture("Argument\\[([-0-9]+)\\.\\.([0-9]+)\\]", 2).toInt() = n2 and + n = [n1 .. n2] + ) + ) + } + + private SummaryComponent interpretComponent(string c) { + specSplit(_, c, _) and + ( + exists(int pos | parseArg(c, pos) and result = SummaryComponent::argument(pos)) + or + exists(int pos | parseParam(c, pos) and result = SummaryComponent::parameter(pos)) + or + c = "ReturnValue" and result = SummaryComponent::return(getReturnValueKind()) + or + result = interpretComponentSpecific(c) + ) + } + + /** + * Holds if `spec` specifies summary component stack `stack`. + */ + predicate interpretSpec(string spec, SummaryComponentStack stack) { + interpretSpec(spec, 0, stack) + } + + private predicate interpretSpec(string spec, int idx, SummaryComponentStack stack) { + exists(string c | + relevantSpec(spec) and + specLength(spec, idx + 1) and + specSplit(spec, c, idx) and + stack = SummaryComponentStack::singleton(interpretComponent(c)) + ) + or + exists(SummaryComponent head, SummaryComponentStack tail | + interpretSpec(spec, idx, head, tail) and + stack = SummaryComponentStack::push(head, tail) + ) + } + + private predicate interpretSpec( + string output, int idx, SummaryComponent head, SummaryComponentStack tail + ) { + exists(string c | + interpretSpec(output, idx + 1, tail) and + specSplit(output, c, idx) and + head = interpretComponent(c) + ) + } + + private class MkStack extends RequiredSummaryComponentStack { + MkStack() { interpretSpec(_, _, _, this) } + + override predicate required(SummaryComponent c) { interpretSpec(_, _, c, this) } + } + + private class SummarizedCallableExternal extends SummarizedCallable { + SummarizedCallableExternal() { summaryElement(this, _, _, _) } + + override predicate propagatesFlow( + SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue + ) { + exists(string inSpec, string outSpec, string kind | + summaryElement(this, inSpec, outSpec, kind) and + interpretSpec(inSpec, input) and + interpretSpec(outSpec, output) + | + kind = "value" and preservesValue = true + or + kind = "taint" and preservesValue = false + ) + } + } + + /** Holds if component `c` of specification `spec` cannot be parsed. */ + predicate invalidSpecComponent(string spec, string c) { + specSplit(spec, c, _) and + not exists(interpretComponent(c)) + } + + private predicate inputNeedsReference(string c) { + c = "Argument" or + parseArg(c, _) + } + + private predicate outputNeedsReference(string c) { + c = "Argument" or + parseArg(c, _) or + c = "ReturnValue" + } + + private predicate sourceElementRef(InterpretNode ref, string output, string kind) { + exists(SourceOrSinkElement e | + sourceElement(e, output, kind) and + if outputNeedsReference(specLast(output)) + then e = ref.getCallTarget() + else e = ref.asElement() + ) + } + + private predicate sinkElementRef(InterpretNode ref, string input, string kind) { + exists(SourceOrSinkElement e | + sinkElement(e, input, kind) and + if inputNeedsReference(specLast(input)) + then e = ref.getCallTarget() + else e = ref.asElement() + ) + } + + private predicate interpretOutput(string output, int idx, InterpretNode ref, InterpretNode node) { + sourceElementRef(ref, output, _) and + specLength(output, idx) and + node = ref + or + exists(InterpretNode mid, string c | + interpretOutput(output, idx + 1, ref, mid) and + specSplit(output, c, idx) + | + exists(int pos | + node.asNode().(PostUpdateNode).getPreUpdateNode().(ArgNode).argumentOf(mid.asCall(), pos) + | + c = "Argument" or parseArg(c, pos) + ) + or + exists(int pos | node.asNode().(ParamNode).isParameterOf(mid.asCallable(), pos) | + c = "Parameter" or parseParam(c, pos) + ) + or + c = "ReturnValue" and + node.asNode() = getAnOutNodeExt(mid.asCall(), TValueReturn(getReturnValueKind())) + or + interpretOutputSpecific(c, mid, node) + ) + } + + private predicate interpretInput(string input, int idx, InterpretNode ref, InterpretNode node) { + sinkElementRef(ref, input, _) and + specLength(input, idx) and + node = ref + or + exists(InterpretNode mid, string c | + interpretInput(input, idx + 1, ref, mid) and + specSplit(input, c, idx) + | + exists(int pos | node.asNode().(ArgNode).argumentOf(mid.asCall(), pos) | + c = "Argument" or parseArg(c, pos) + ) + or + exists(ReturnNodeExt ret | + c = "ReturnValue" and + ret = node.asNode() and + ret.getKind().(ValueReturnKind).getKind() = getReturnValueKind() and + mid.asCallable() = getNodeEnclosingCallable(ret) + ) + or + interpretInputSpecific(c, mid, node) + ) + } + + /** + * Holds if `node` is specified as a source with the given kind in a CSV flow + * model. + */ + predicate isSourceNode(InterpretNode node, string kind) { + exists(InterpretNode ref, string output | + sourceElementRef(ref, output, kind) and + interpretOutput(output, 0, ref, node) + ) + } + + /** + * Holds if `node` is specified as a sink with the given kind in a CSV flow + * model. + */ + predicate isSinkNode(InterpretNode node, string kind) { + exists(InterpretNode ref, string input | + sinkElementRef(ref, input, kind) and + interpretInput(input, 0, ref, node) + ) + } + } + + /** Provides a query predicate for outputting a set of relevant flow summaries. */ + module TestOutput { + /** A flow summary to include in the `summary/3` query predicate. */ + abstract class RelevantSummarizedCallable extends SummarizedCallable { + /** Gets the string representation of this callable used by `summary/3`. */ + string getFullString() { result = this.toString() } + } + + /** A query predicate for outputting flow summaries in QL tests. */ + query predicate summary(string callable, string flow, boolean preservesValue) { + exists( + RelevantSummarizedCallable c, SummaryComponentStack input, SummaryComponentStack output + | + callable = c.getFullString() and + c.propagatesFlow(input, output, preservesValue) and + flow = input + " -> " + output + ) + } + } + + /** + * Provides query predicates for rendering the generated data flow graph for + * a summarized callable. + * + * Import this module into a `.ql` file of `@kind graph` to render the graph. + * The graph is restricted to callables from `RelevantSummarizedCallable`. + */ + module RenderSummarizedCallable { + /** A summarized callable to include in the graph. */ + abstract class RelevantSummarizedCallable extends SummarizedCallable { } + + private newtype TNodeOrCall = + MkNode(Node n) { + exists(RelevantSummarizedCallable c | + n = summaryNode(c, _) + or + n.(ParamNode).isParameterOf(c, _) + ) + } or + MkCall(DataFlowCall call) { + call = summaryDataFlowCall(_) and + call.getEnclosingCallable() instanceof RelevantSummarizedCallable + } + + private class NodeOrCall extends TNodeOrCall { + Node asNode() { this = MkNode(result) } + + DataFlowCall asCall() { this = MkCall(result) } + + string toString() { + result = this.asNode().toString() + or + result = this.asCall().toString() + } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.asNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + or + this.asCall().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + } + + query predicate nodes(NodeOrCall n, string key, string val) { + key = "semmle.label" and val = n.toString() + } + + private predicate edgesComponent(NodeOrCall a, NodeOrCall b, string value) { + exists(boolean preservesValue | + Private::Steps::summaryLocalStep(a.asNode(), b.asNode(), preservesValue) and + if preservesValue = true then value = "value" else value = "taint" + ) + or + exists(Content c | + Private::Steps::summaryReadStep(a.asNode(), c, b.asNode()) and + value = "read (" + c + ")" + or + Private::Steps::summaryStoreStep(a.asNode(), c, b.asNode()) and + value = "store (" + c + ")" + or + Private::Steps::summaryClearsContent(a.asNode(), c) and + b = a and + value = "clear (" + c + ")" + ) + or + summaryPostUpdateNode(b.asNode(), a.asNode()) and + value = "post-update" + or + b.asCall() = summaryDataFlowCall(a.asNode()) and + value = "receiver" + or + exists(int i | + summaryArgumentNode(b.asCall(), a.asNode(), i) and + value = "argument (" + i + ")" + ) + } + + query predicate edges(NodeOrCall a, NodeOrCall b, string key, string value) { + key = "semmle.label" and + value = strictconcat(string s | edgesComponent(a, b, s) | s, " / ") + } + } +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImplSpecific.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImplSpecific.qll new file mode 100644 index 00000000000..6127450fb9c --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImplSpecific.qll @@ -0,0 +1,120 @@ +/** + * Provides Ruby specific classes and predicates for defining flow summaries. + */ + +private import ruby +private import DataFlowDispatch +private import DataFlowPrivate +private import DataFlowPublic +private import DataFlowImplCommon +private import FlowSummaryImpl::Private +private import FlowSummaryImpl::Public +private import codeql.ruby.dataflow.FlowSummary as FlowSummary + +/** Holds is `i` is a valid parameter position. */ +predicate parameterPosition(int i) { i in [-2 .. 10] } + +/** Gets the parameter position of the instance parameter. */ +int instanceParameterPosition() { none() } // disables implicit summary flow to `self` for callbacks + +/** Gets the synthesized summary data-flow node for the given values. */ +Node summaryNode(SummarizedCallable c, SummaryNodeState state) { result = TSummaryNode(c, state) } + +/** Gets the synthesized data-flow call for `receiver`. */ +SummaryCall summaryDataFlowCall(Node receiver) { receiver = result.getReceiver() } + +/** Gets the type of content `c`. */ +DataFlowType getContentType(Content c) { any() } + +/** Gets the return type of kind `rk` for callable `c`. */ +bindingset[c, rk] +DataFlowType getReturnType(SummarizedCallable c, ReturnKind rk) { any() } + +/** + * Gets the type of the `i`th parameter in a synthesized call that targets a + * callback of type `t`. + */ +bindingset[t, i] +DataFlowType getCallbackParameterType(DataFlowType t, int i) { any() } + +/** + * Gets the return type of kind `rk` in a synthesized call that targets a + * callback of type `t`. + */ +DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) { any() } + +/** + * Holds if an external flow summary exists for `c` with input specification + * `input`, output specification `output`, and kind `kind`. + */ +predicate summaryElement(DataFlowCallable c, string input, string output, string kind) { + exists(FlowSummary::SummarizedCallable sc, boolean preservesValue | + sc.propagatesFlowExt(input, output, preservesValue) and + c.asLibraryCallable() = sc and + if preservesValue = true then kind = "value" else kind = "taint" + ) +} + +/** + * Gets the summary component for specification component `c`, if any. + * + * This covers all the Ruby-specific components of a flow summary, and + * is currently restricted to `"BlockArgument"`. + */ +SummaryComponent interpretComponentSpecific(string c) { + c = "BlockArgument" and + result = FlowSummary::SummaryComponent::block() + or + c = "Argument[_]" and + result = FlowSummary::SummaryComponent::argument(any(int i | i >= 0)) +} + +/** Gets the return kind corresponding to specification `"ReturnValue"`. */ +NormalReturnKind getReturnValueKind() { any() } + +/** + * All definitions in this module are required by the shared implementation + * (for source/sink interpretation), but they are unused for Ruby, where + * we rely on API graphs instead. + */ +private module UnusedSourceSinkInterpretation { + /** + * Holds if an external source specification exists for `e` with output specification + * `output` and kind `kind`. + */ + predicate sourceElement(AstNode n, string output, string kind) { none() } + + /** + * Holds if an external sink specification exists for `n` with input specification + * `input` and kind `kind`. + */ + predicate sinkElement(AstNode n, string input, string kind) { none() } + + class SourceOrSinkElement = AstNode; + + /** An entity used to interpret a source/sink specification. */ + class InterpretNode extends AstNode { + /** Gets the element that this node corresponds to, if any. */ + SourceOrSinkElement asElement() { none() } + + /** Gets the data-flow node that this node corresponds to, if any. */ + Node asNode() { none() } + + /** Gets the call that this node corresponds to, if any. */ + DataFlowCall asCall() { none() } + + /** Gets the callable that this node corresponds to, if any. */ + DataFlowCallable asCallable() { none() } + + /** Gets the target of this call, if any. */ + Callable getCallTarget() { none() } + } + + /** Provides additional sink specification logic. */ + predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) { none() } + + /** Provides additional source specification logic. */ + predicate interpretInputSpecific(string c, InterpretNode mid, InterpretNode node) { none() } +} + +import UnusedSourceSinkInterpretation diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll new file mode 100644 index 00000000000..54269c5cb59 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll @@ -0,0 +1,289 @@ +private import SsaImplCommon +private import codeql.ruby.AST +private import codeql.ruby.CFG +private import codeql.ruby.ast.Variable +private import CfgNodes::ExprNodes + +/** Holds if `v` is uninitialized at index `i` in entry block `bb`. */ +predicate uninitializedWrite(EntryBasicBlock bb, int i, LocalVariable v) { + v.getDeclaringScope() = bb.getScope() and + i = -1 +} + +/** Holds if `bb` contains a caputured read of variable `v`. */ +pragma[noinline] +private predicate hasCapturedVariableRead(BasicBlock bb, LocalVariable v) { + exists(LocalVariableReadAccess read | + read = bb.getANode().getNode() and + read.isCapturedAccess() and + read.getVariable() = v + ) +} + +/** + * Holds if an entry definition is needed for captured variable `v` at index + * `i` in entry block `bb`. + */ +predicate capturedEntryWrite(EntryBasicBlock bb, int i, LocalVariable v) { + hasCapturedVariableRead(bb.getASuccessor*(), v) and + i = -1 +} + +/** Holds if `bb` contains a caputured write to variable `v`. */ +pragma[noinline] +private predicate writesCapturedVariable(BasicBlock bb, LocalVariable v) { + exists(LocalVariableWriteAccess write | + write = bb.getANode().getNode() and + write.isCapturedAccess() and + write.getVariable() = v + ) +} + +/** + * Holds if a pseudo read of captured variable `v` should be inserted + * at index `i` in exit block `bb`. + */ +private predicate capturedExitRead(AnnotatedExitBasicBlock bb, int i, LocalVariable v) { + bb.isNormal() and + writesCapturedVariable(bb.getAPredecessor*(), v) and + i = bb.length() +} + +private CfgScope getCaptureOuterCfgScope(CfgScope scope) { + result = scope.getOuterCfgScope() and + ( + scope instanceof Block + or + scope instanceof Lambda + ) +} + +/** Holds if captured variable `v` is read inside `scope`. */ +pragma[noinline] +private predicate hasCapturedRead(Variable v, CfgScope scope) { + any(LocalVariableReadAccess read | + read.getVariable() = v and scope = getCaptureOuterCfgScope*(read.getCfgScope()) + ).isCapturedAccess() +} + +pragma[noinline] +private predicate hasVariableWriteWithCapturedRead(BasicBlock bb, LocalVariable v, CfgScope scope) { + hasCapturedRead(v, scope) and + exists(VariableWriteAccess write | + write = bb.getANode().getNode() and + write.getVariable() = v and + bb.getScope() = scope.getOuterCfgScope() + ) +} + +/** + * Holds if the call at index `i` in basic block `bb` may reach a callable + * that reads captured variable `v`. + */ +private predicate capturedCallRead(BasicBlock bb, int i, LocalVariable v) { + exists(CfgScope scope | + hasVariableWriteWithCapturedRead(bb.getAPredecessor*(), v, scope) and + bb.getNode(i).getNode() instanceof Call + | + not scope instanceof Block + or + // If the read happens inside a block, we restrict to the call that + // contains the block + scope = any(MethodCall c | bb.getNode(i) = c.getAControlFlowNode()).getBlock() + ) +} + +/** Holds if captured variable `v` is written inside `scope`. */ +pragma[noinline] +private predicate hasCapturedWrite(Variable v, CfgScope scope) { + any(LocalVariableWriteAccess write | + write.getVariable() = v and scope = getCaptureOuterCfgScope*(write.getCfgScope()) + ).isCapturedAccess() +} + +/** Holds if `v` is read at index `i` in basic block `bb`. */ +private predicate variableReadActual(BasicBlock bb, int i, LocalVariable v) { + exists(VariableReadAccess read | + read.getVariable() = v and + read = bb.getNode(i).getNode() + ) +} + +predicate variableRead(BasicBlock bb, int i, LocalVariable v, boolean certain) { + variableReadActual(bb, i, v) and + certain = true + or + capturedCallRead(bb, i, v) and + certain = false + or + capturedExitRead(bb, i, v) and + certain = false +} + +pragma[noinline] +private predicate hasVariableReadWithCapturedWrite(BasicBlock bb, LocalVariable v, CfgScope scope) { + hasCapturedWrite(v, scope) and + exists(VariableReadAccess read | + read = bb.getANode().getNode() and + read.getVariable() = v and + bb.getScope() = scope.getOuterCfgScope() + ) +} + +cached +private module Cached { + /** + * Holds if the call at index `i` in basic block `bb` may reach a callable + * that writes captured variable `v`. + */ + cached + predicate capturedCallWrite(BasicBlock bb, int i, LocalVariable v) { + exists(CfgScope scope | + hasVariableReadWithCapturedWrite(bb.getASuccessor*(), v, scope) and + bb.getNode(i).getNode() instanceof Call + | + not scope instanceof Block + or + // If the write happens inside a block, we restrict to the call that + // contains the block + scope = any(MethodCall c | bb.getNode(i) = c.getAControlFlowNode()).getBlock() + ) + } + + /** + * Holds if `v` is written at index `i` in basic block `bb`, and the corresponding + * AST write access is `write`. + */ + cached + predicate variableWriteActual(BasicBlock bb, int i, LocalVariable v, VariableWriteAccess write) { + exists(AstNode n | + write.getVariable() = v and + n = bb.getNode(i).getNode() + | + write.isExplicitWrite(n) + or + write.isImplicitWrite() and + n = write + ) + } + + cached + VariableReadAccessCfgNode getARead(Definition def) { + exists(LocalVariable v, BasicBlock bb, int i | + ssaDefReachesRead(v, def, bb, i) and + variableReadActual(bb, i, v) and + result = bb.getNode(i) + ) + } + + /** + * Holds if there is flow for a captured variable from the enclosing scope into a block. + * ```rb + * foo = 0 + * bar { + * puts foo + * } + * ``` + */ + cached + predicate captureFlowIn(Definition def, Definition entry) { + exists(LocalVariable v, BasicBlock bb, int i | + ssaDefReachesRead(v, def, bb, i) and + capturedCallRead(bb, i, v) and + exists(BasicBlock bb2, int i2 | + capturedEntryWrite(bb2, i2, v) and + entry.definesAt(v, bb2, i2) + ) + ) + } + + /** + * Holds if there is outgoing flow for a captured variable that is updated in a block. + * ```rb + * foo = 0 + * bar { + * foo += 10 + * } + * puts foo + * ``` + */ + cached + predicate captureFlowOut(Definition def, Definition exit) { + exists(LocalVariable v, BasicBlock bb, int i | + ssaDefReachesRead(v, def, bb, i) and + capturedExitRead(bb, i, v) and + exists(BasicBlock bb2, int i2 | + capturedCallWrite(bb2, i2, v) and + exit.definesAt(v, bb2, i2) + ) + ) + } + + cached + Definition phiHasInputFromBlock(PhiNode phi, BasicBlock bb) { + phiHasInputFromBlock(phi, result, bb) + } + + /** + * Holds if the value defined at SSA definition `def` can reach a read at `read`, + * without passing through any other non-pseudo read. + */ + cached + predicate firstRead(Definition def, VariableReadAccessCfgNode read) { + exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2 | + def.definesAt(_, bb1, i1) and + adjacentDefNoUncertainReads(def, bb1, i1, bb2, i2) and + read = bb2.getNode(i2) + ) + } + + /** + * Holds if the read at `read2` is a read of the same SSA definition `def` + * as the read at `read1`, and `read2` can be reached from `read1` without + * passing through another non-pseudo read. + */ + cached + predicate adjacentReadPair( + Definition def, VariableReadAccessCfgNode read1, VariableReadAccessCfgNode read2 + ) { + exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2 | + read1 = bb1.getNode(i1) and + variableReadActual(bb1, i1, _) and + adjacentDefNoUncertainReads(def, bb1, i1, bb2, i2) and + read2 = bb2.getNode(i2) + ) + } + + /** + * Holds if the read of `def` at `read` may be a last read. That is, `read` + * can either reach another definition of the underlying source variable or + * the end of the CFG scope, without passing through another non-pseudo read. + */ + cached + predicate lastRead(Definition def, VariableReadAccessCfgNode read) { + exists(BasicBlock bb, int i | + lastRefNoUncertainReads(def, bb, i) and + variableReadActual(bb, i, _) and + read = bb.getNode(i) + ) + } + + /** + * Holds if the reference to `def` at index `i` in basic block `bb` can reach + * another definition `next` of the same underlying source variable, without + * passing through another write or non-pseudo read. + * + * The reference is either a read of `def` or `def` itself. + */ + cached + predicate lastRefBeforeRedef(Definition def, BasicBlock bb, int i, Definition next) { + lastRefRedefNoUncertainReads(def, bb, i, next) + } + + cached + Definition uncertainWriteDefinitionInput(UncertainWriteDefinition def) { + uncertainWriteDefinitionInput(def, result) + } +} + +import Cached diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll new file mode 100644 index 00000000000..395cb5cb171 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll @@ -0,0 +1,638 @@ +/** + * Provides a language-independent implementation of static single assignment + * (SSA) form. + */ + +private import SsaImplSpecific + +private BasicBlock getABasicBlockPredecessor(BasicBlock bb) { getABasicBlockSuccessor(result) = bb } + +/** + * Liveness analysis (based on source variables) to restrict the size of the + * SSA representation. + */ +private module Liveness { + /** + * A classification of variable references into reads (of a given kind) and + * (certain or uncertain) writes. + */ + private newtype TRefKind = + Read(boolean certain) { certain in [false, true] } or + Write(boolean certain) { certain in [false, true] } + + private class RefKind extends TRefKind { + string toString() { + exists(boolean certain | this = Read(certain) and result = "read (" + certain + ")") + or + exists(boolean certain | this = Write(certain) and result = "write (" + certain + ")") + } + + int getOrder() { + this = Read(_) and + result = 0 + or + this = Write(_) and + result = 1 + } + } + + /** + * Holds if the `i`th node of basic block `bb` is a reference to `v` of kind `k`. + */ + private predicate ref(BasicBlock bb, int i, SourceVariable v, RefKind k) { + exists(boolean certain | variableRead(bb, i, v, certain) | k = Read(certain)) + or + exists(boolean certain | variableWrite(bb, i, v, certain) | k = Write(certain)) + } + + private newtype OrderedRefIndex = + MkOrderedRefIndex(int i, int tag) { + exists(RefKind rk | ref(_, i, _, rk) | tag = rk.getOrder()) + } + + private OrderedRefIndex refOrd(BasicBlock bb, int i, SourceVariable v, RefKind k, int ord) { + ref(bb, i, v, k) and + result = MkOrderedRefIndex(i, ord) and + ord = k.getOrder() + } + + /** + * Gets the (1-based) rank of the reference to `v` at the `i`th node of + * basic block `bb`, which has the given reference kind `k`. + * + * Reads are considered before writes when they happen at the same index. + */ + private int refRank(BasicBlock bb, int i, SourceVariable v, RefKind k) { + refOrd(bb, i, v, k, _) = + rank[result](int j, int ord, OrderedRefIndex res | + res = refOrd(bb, j, v, _, ord) + | + res order by j, ord + ) + } + + private int maxRefRank(BasicBlock bb, SourceVariable v) { + result = refRank(bb, _, v, _) and + not result + 1 = refRank(bb, _, v, _) + } + + /** + * Gets the (1-based) rank of the first reference to `v` inside basic block `bb` + * that is either a read or a certain write. + */ + private int firstReadOrCertainWrite(BasicBlock bb, SourceVariable v) { + result = + min(int r, RefKind k | + r = refRank(bb, _, v, k) and + k != Write(false) + | + r + ) + } + + /** + * Holds if source variable `v` is live at the beginning of basic block `bb`. + */ + predicate liveAtEntry(BasicBlock bb, SourceVariable v) { + // The first read or certain write to `v` inside `bb` is a read + refRank(bb, _, v, Read(_)) = firstReadOrCertainWrite(bb, v) + or + // There is no certain write to `v` inside `bb`, but `v` is live at entry + // to a successor basic block of `bb` + not exists(firstReadOrCertainWrite(bb, v)) and + liveAtExit(bb, v) + } + + /** + * Holds if source variable `v` is live at the end of basic block `bb`. + */ + predicate liveAtExit(BasicBlock bb, SourceVariable v) { + liveAtEntry(getABasicBlockSuccessor(bb), v) + } + + /** + * Holds if variable `v` is live in basic block `bb` at index `i`. + * The rank of `i` is `rnk` as defined by `refRank()`. + */ + private predicate liveAtRank(BasicBlock bb, int i, SourceVariable v, int rnk) { + exists(RefKind kind | rnk = refRank(bb, i, v, kind) | + rnk = maxRefRank(bb, v) and + liveAtExit(bb, v) + or + ref(bb, i, v, kind) and + kind = Read(_) + or + exists(RefKind nextKind | + liveAtRank(bb, _, v, rnk + 1) and + rnk + 1 = refRank(bb, _, v, nextKind) and + nextKind != Write(true) + ) + ) + } + + /** + * Holds if variable `v` is live after the (certain or uncertain) write at + * index `i` inside basic block `bb`. + */ + predicate liveAfterWrite(BasicBlock bb, int i, SourceVariable v) { + exists(int rnk | rnk = refRank(bb, i, v, Write(_)) | liveAtRank(bb, i, v, rnk)) + } +} + +private import Liveness + +/** Holds if `bb1` strictly dominates `bb2`. */ +private predicate strictlyDominates(BasicBlock bb1, BasicBlock bb2) { + bb1 = getImmediateBasicBlockDominator+(bb2) +} + +/** Holds if `bb1` dominates a predecessor of `bb2`. */ +private predicate dominatesPredecessor(BasicBlock bb1, BasicBlock bb2) { + exists(BasicBlock pred | pred = getABasicBlockPredecessor(bb2) | + bb1 = pred + or + strictlyDominates(bb1, pred) + ) +} + +/** Holds if `df` is in the dominance frontier of `bb`. */ +pragma[noinline] +private predicate inDominanceFrontier(BasicBlock bb, BasicBlock df) { + dominatesPredecessor(bb, df) and + not strictlyDominates(bb, df) +} + +/** + * Holds if `bb` is in the dominance frontier of a block containing a + * definition of `v`. + */ +pragma[noinline] +private predicate inDefDominanceFrontier(BasicBlock bb, SourceVariable v) { + exists(BasicBlock defbb, Definition def | + def.definesAt(v, defbb, _) and + inDominanceFrontier(defbb, bb) + ) +} + +cached +newtype TDefinition = + TWriteDef(SourceVariable v, BasicBlock bb, int i) { + variableWrite(bb, i, v, _) and + liveAfterWrite(bb, i, v) + } or + TPhiNode(SourceVariable v, BasicBlock bb) { + inDefDominanceFrontier(bb, v) and + liveAtEntry(bb, v) + } + +private module SsaDefReaches { + newtype TSsaRefKind = + SsaRead() or + SsaDef() + + /** + * A classification of SSA variable references into reads and definitions. + */ + class SsaRefKind extends TSsaRefKind { + string toString() { + this = SsaRead() and + result = "SsaRead" + or + this = SsaDef() and + result = "SsaDef" + } + + int getOrder() { + this = SsaRead() and + result = 0 + or + this = SsaDef() and + result = 1 + } + } + + /** + * Holds if the `i`th node of basic block `bb` is a reference to `v`, + * either a read (when `k` is `SsaRead()`) or an SSA definition (when `k` + * is `SsaDef()`). + * + * Unlike `Liveness::ref`, this includes `phi` nodes. + */ + predicate ssaRef(BasicBlock bb, int i, SourceVariable v, SsaRefKind k) { + variableRead(bb, i, v, _) and + k = SsaRead() + or + exists(Definition def | def.definesAt(v, bb, i)) and + k = SsaDef() + } + + private newtype OrderedSsaRefIndex = + MkOrderedSsaRefIndex(int i, SsaRefKind k) { ssaRef(_, i, _, k) } + + private OrderedSsaRefIndex ssaRefOrd(BasicBlock bb, int i, SourceVariable v, SsaRefKind k, int ord) { + ssaRef(bb, i, v, k) and + result = MkOrderedSsaRefIndex(i, k) and + ord = k.getOrder() + } + + /** + * Gets the (1-based) rank of the reference to `v` at the `i`th node of basic + * block `bb`, which has the given reference kind `k`. + * + * For example, if `bb` is a basic block with a phi node for `v` (considered + * to be at index -1), reads `v` at node 2, and defines it at node 5, we have: + * + * ```ql + * ssaRefRank(bb, -1, v, SsaDef()) = 1 // phi node + * ssaRefRank(bb, 2, v, Read()) = 2 // read at node 2 + * ssaRefRank(bb, 5, v, SsaDef()) = 3 // definition at node 5 + * ``` + * + * Reads are considered before writes when they happen at the same index. + */ + int ssaRefRank(BasicBlock bb, int i, SourceVariable v, SsaRefKind k) { + ssaRefOrd(bb, i, v, k, _) = + rank[result](int j, int ord, OrderedSsaRefIndex res | + res = ssaRefOrd(bb, j, v, _, ord) + | + res order by j, ord + ) + } + + int maxSsaRefRank(BasicBlock bb, SourceVariable v) { + result = ssaRefRank(bb, _, v, _) and + not result + 1 = ssaRefRank(bb, _, v, _) + } + + /** + * Holds if the SSA definition `def` reaches rank index `rnk` in its own + * basic block `bb`. + */ + predicate ssaDefReachesRank(BasicBlock bb, Definition def, int rnk, SourceVariable v) { + exists(int i | + rnk = ssaRefRank(bb, i, v, SsaDef()) and + def.definesAt(v, bb, i) + ) + or + ssaDefReachesRank(bb, def, rnk - 1, v) and + rnk = ssaRefRank(bb, _, v, SsaRead()) + } + + /** + * Holds if the SSA definition of `v` at `def` reaches index `i` in the same + * basic block `bb`, without crossing another SSA definition of `v`. + */ + predicate ssaDefReachesReadWithinBlock(SourceVariable v, Definition def, BasicBlock bb, int i) { + exists(int rnk | + ssaDefReachesRank(bb, def, rnk, v) and + rnk = ssaRefRank(bb, i, v, SsaRead()) + ) + } + + /** + * Holds if the SSA definition of `v` at `def` reaches uncertain SSA definition + * `redef` in the same basic block, without crossing another SSA definition of `v`. + */ + predicate ssaDefReachesUncertainDefWithinBlock( + SourceVariable v, Definition def, UncertainWriteDefinition redef + ) { + exists(BasicBlock bb, int rnk, int i | + ssaDefReachesRank(bb, def, rnk, v) and + rnk = ssaRefRank(bb, i, v, SsaDef()) - 1 and + redef.definesAt(v, bb, i) + ) + } + + /** + * Same as `ssaRefRank()`, but restricted to a particular SSA definition `def`. + */ + int ssaDefRank(Definition def, SourceVariable v, BasicBlock bb, int i, SsaRefKind k) { + v = def.getSourceVariable() and + result = ssaRefRank(bb, i, v, k) and + ( + ssaDefReachesRead(_, def, bb, i) + or + def.definesAt(_, bb, i) + ) + } + + /** + * Holds if the reference to `def` at index `i` in basic block `bb` is the + * last reference to `v` inside `bb`. + */ + pragma[noinline] + predicate lastSsaRef(Definition def, SourceVariable v, BasicBlock bb, int i) { + ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) + } + + predicate defOccursInBlock(Definition def, BasicBlock bb, SourceVariable v) { + exists(ssaDefRank(def, v, bb, _, _)) + } + + pragma[noinline] + private predicate ssaDefReachesThroughBlock(Definition def, BasicBlock bb) { + ssaDefReachesEndOfBlock(bb, def, _) and + not defOccursInBlock(_, bb, def.getSourceVariable()) + } + + /** + * Holds if `def` is accessed in basic block `bb1` (either a read or a write), + * `bb2` is a transitive successor of `bb1`, `def` is live at the end of `bb1`, + * and the underlying variable for `def` is neither read nor written in any block + * on the path between `bb1` and `bb2`. + */ + predicate varBlockReaches(Definition def, BasicBlock bb1, BasicBlock bb2) { + defOccursInBlock(def, bb1, _) and + bb2 = getABasicBlockSuccessor(bb1) + or + exists(BasicBlock mid | + varBlockReaches(def, bb1, mid) and + ssaDefReachesThroughBlock(def, mid) and + bb2 = getABasicBlockSuccessor(mid) + ) + } + + /** + * Holds if `def` is accessed in basic block `bb1` (either a read or a write), + * `def` is read at index `i2` in basic block `bb2`, `bb2` is in a transitive + * successor block of `bb1`, and `def` is neither read nor written in any block + * on a path between `bb1` and `bb2`. + */ + predicate defAdjacentRead(Definition def, BasicBlock bb1, BasicBlock bb2, int i2) { + varBlockReaches(def, bb1, bb2) and + ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 + } +} + +private import SsaDefReaches + +pragma[nomagic] +predicate liveThrough(BasicBlock bb, SourceVariable v) { + liveAtExit(bb, v) and + not ssaRef(bb, _, v, SsaDef()) +} + +/** + * NB: If this predicate is exposed, it should be cached. + * + * Holds if the SSA definition of `v` at `def` reaches the end of basic + * block `bb`, at which point it is still live, without crossing another + * SSA definition of `v`. + */ +pragma[nomagic] +predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable v) { + exists(int last | last = maxSsaRefRank(bb, v) | + ssaDefReachesRank(bb, def, last, v) and + liveAtExit(bb, v) + ) + or + // The construction of SSA form ensures that each read of a variable is + // dominated by its definition. An SSA definition therefore reaches a + // control flow node if it is the _closest_ SSA definition that dominates + // the node. If two definitions dominate a node then one must dominate the + // other, so therefore the definition of _closest_ is given by the dominator + // tree. Thus, reaching definitions can be calculated in terms of dominance. + ssaDefReachesEndOfBlock(getImmediateBasicBlockDominator(bb), def, pragma[only_bind_into](v)) and + liveThrough(bb, pragma[only_bind_into](v)) +} + +/** + * NB: If this predicate is exposed, it should be cached. + * + * Holds if `inp` is an input to the phi node `phi` along the edge originating in `bb`. + */ +pragma[nomagic] +predicate phiHasInputFromBlock(PhiNode phi, Definition inp, BasicBlock bb) { + exists(SourceVariable v, BasicBlock bbDef | + phi.definesAt(v, bbDef, _) and + getABasicBlockPredecessor(bbDef) = bb and + ssaDefReachesEndOfBlock(bb, inp, v) + ) +} + +/** + * NB: If this predicate is exposed, it should be cached. + * + * Holds if the SSA definition of `v` at `def` reaches a read at index `i` in + * basic block `bb`, without crossing another SSA definition of `v`. The read + * is of kind `rk`. + */ +pragma[nomagic] +predicate ssaDefReachesRead(SourceVariable v, Definition def, BasicBlock bb, int i) { + ssaDefReachesReadWithinBlock(v, def, bb, i) + or + variableRead(bb, i, v, _) and + ssaDefReachesEndOfBlock(getABasicBlockPredecessor(bb), def, v) and + not ssaDefReachesReadWithinBlock(v, _, bb, i) +} + +/** + * NB: If this predicate is exposed, it should be cached. + * + * Holds if `def` is accessed at index `i1` in basic block `bb1` (either a read + * or a write), `def` is read at index `i2` in basic block `bb2`, and there is a + * path between them without any read of `def`. + */ +pragma[nomagic] +predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2) { + exists(int rnk | + rnk = ssaDefRank(def, _, bb1, i1, _) and + rnk + 1 = ssaDefRank(def, _, bb1, i2, SsaRead()) and + variableRead(bb1, i2, _, _) and + bb2 = bb1 + ) + or + lastSsaRef(def, _, bb1, i1) and + defAdjacentRead(def, bb1, bb2, i2) +} + +pragma[noinline] +private predicate adjacentDefRead( + Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v +) { + adjacentDefRead(def, bb1, i1, bb2, i2) and + v = def.getSourceVariable() +} + +private predicate adjacentDefReachesRead( + Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2 +) { + exists(SourceVariable v | adjacentDefRead(def, bb1, i1, bb2, i2, v) | + ssaRef(bb1, i1, v, SsaDef()) + or + variableRead(bb1, i1, v, true) + ) + or + exists(BasicBlock bb3, int i3 | + adjacentDefReachesRead(def, bb1, i1, bb3, i3) and + variableRead(bb3, i3, _, false) and + adjacentDefRead(def, bb3, i3, bb2, i2) + ) +} + +/** + * NB: If this predicate is exposed, it should be cached. + * + * Same as `adjacentDefRead`, but ignores uncertain reads. + */ +pragma[nomagic] +predicate adjacentDefNoUncertainReads(Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2) { + adjacentDefReachesRead(def, bb1, i1, bb2, i2) and + variableRead(bb2, i2, _, true) +} + +/** + * NB: If this predicate is exposed, it should be cached. + * + * Holds if the node at index `i` in `bb` is a last reference to SSA definition + * `def`. The reference is last because it can reach another write `next`, + * without passing through another read or write. + */ +pragma[nomagic] +predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) { + exists(SourceVariable v | + // Next reference to `v` inside `bb` is a write + exists(int rnk, int j | + rnk = ssaDefRank(def, v, bb, i, _) and + next.definesAt(v, bb, j) and + rnk + 1 = ssaRefRank(bb, j, v, SsaDef()) + ) + or + // Can reach a write using one or more steps + lastSsaRef(def, v, bb, i) and + exists(BasicBlock bb2 | + varBlockReaches(def, bb, bb2) and + 1 = ssaDefRank(next, v, bb2, _, SsaDef()) + ) + ) +} + +/** + * NB: If this predicate is exposed, it should be cached. + * + * Holds if `inp` is an immediately preceding definition of uncertain definition + * `def`. Since `def` is uncertain, the value from the preceding definition might + * still be valid. + */ +pragma[nomagic] +predicate uncertainWriteDefinitionInput(UncertainWriteDefinition def, Definition inp) { + lastRefRedef(inp, _, _, def) +} + +private predicate adjacentDefReachesUncertainRead( + Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2 +) { + adjacentDefReachesRead(def, bb1, i1, bb2, i2) and + variableRead(bb2, i2, _, false) +} + +/** + * NB: If this predicate is exposed, it should be cached. + * + * Same as `lastRefRedef`, but ignores uncertain reads. + */ +pragma[nomagic] +predicate lastRefRedefNoUncertainReads(Definition def, BasicBlock bb, int i, Definition next) { + lastRefRedef(def, bb, i, next) and + not variableRead(bb, i, def.getSourceVariable(), false) + or + exists(BasicBlock bb0, int i0 | + lastRefRedef(def, bb0, i0, next) and + adjacentDefReachesUncertainRead(def, bb, i, bb0, i0) + ) +} + +/** + * NB: If this predicate is exposed, it should be cached. + * + * Holds if the node at index `i` in `bb` is a last reference to SSA + * definition `def`. + * + * That is, the node can reach the end of the enclosing callable, or another + * SSA definition for the underlying source variable, without passing through + * another read. + */ +pragma[nomagic] +predicate lastRef(Definition def, BasicBlock bb, int i) { + lastRefRedef(def, bb, i, _) + or + lastSsaRef(def, _, bb, i) and + ( + // Can reach exit directly + bb instanceof ExitBasicBlock + or + // Can reach a block using one or more steps, where `def` is no longer live + exists(BasicBlock bb2 | varBlockReaches(def, bb, bb2) | + not defOccursInBlock(def, bb2, _) and + not ssaDefReachesEndOfBlock(bb2, def, _) + ) + ) +} + +/** + * NB: If this predicate is exposed, it should be cached. + * + * Same as `lastRefRedef`, but ignores uncertain reads. + */ +pragma[nomagic] +predicate lastRefNoUncertainReads(Definition def, BasicBlock bb, int i) { + lastRef(def, bb, i) and + not variableRead(bb, i, def.getSourceVariable(), false) + or + exists(BasicBlock bb0, int i0 | + lastRef(def, bb0, i0) and + adjacentDefReachesUncertainRead(def, bb, i, bb0, i0) + ) +} + +/** A static single assignment (SSA) definition. */ +class Definition extends TDefinition { + /** Gets the source variable underlying this SSA definition. */ + SourceVariable getSourceVariable() { this.definesAt(result, _, _) } + + /** + * Holds if this SSA definition defines `v` at index `i` in basic block `bb`. + * Phi nodes are considered to be at index `-1`, while normal variable writes + * are at the index of the control flow node they wrap. + */ + final predicate definesAt(SourceVariable v, BasicBlock bb, int i) { + this = TWriteDef(v, bb, i) + or + this = TPhiNode(v, bb) and i = -1 + } + + /** Gets the basic block to which this SSA definition belongs. */ + final BasicBlock getBasicBlock() { this.definesAt(_, result, _) } + + /** Gets a textual representation of this SSA definition. */ + string toString() { none() } +} + +/** An SSA definition that corresponds to a write. */ +class WriteDefinition extends Definition, TWriteDef { + private SourceVariable v; + private BasicBlock bb; + private int i; + + WriteDefinition() { this = TWriteDef(v, bb, i) } + + override string toString() { result = "WriteDef" } +} + +/** A phi node. */ +class PhiNode extends Definition, TPhiNode { + override string toString() { result = "Phi" } +} + +/** + * An SSA definition that represents an uncertain update of the underlying + * source variable. + */ +class UncertainWriteDefinition extends WriteDefinition { + UncertainWriteDefinition() { + exists(SourceVariable v, BasicBlock bb, int i | + this.definesAt(v, bb, i) and + variableWrite(bb, i, v, false) + ) + } +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplSpecific.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplSpecific.qll new file mode 100644 index 00000000000..b363dd526a3 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplSpecific.qll @@ -0,0 +1,47 @@ +/** Provides the Ruby specific parameters for `SsaImplCommon.qll`. */ + +private import SsaImpl as SsaImpl +private import codeql.ruby.AST +private import codeql.ruby.ast.Parameter +private import codeql.ruby.ast.Variable +private import codeql.ruby.controlflow.BasicBlocks as BasicBlocks +private import codeql.ruby.controlflow.ControlFlowGraph + +class BasicBlock = BasicBlocks::BasicBlock; + +BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() } + +BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } + +class ExitBasicBlock = BasicBlocks::ExitBasicBlock; + +class SourceVariable = LocalVariable; + +/** + * Holds if the statement at index `i` of basic block `bb` contains a write to variable `v`. + * `certain` is true if the write definitely occurs. + */ +predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) { + ( + exists(Scope scope | scope = v.(SelfVariable).getDeclaringScope() | + // We consider the `self` variable to have a single write at the entry to a method block... + scope = bb.(BasicBlocks::EntryBasicBlock).getScope() and + i = 0 + or + // ...or a class or module block. + bb.getNode(i).getNode() = scope.(ModuleBase).getAControlFlowEntryNode() + ) + or + SsaImpl::uninitializedWrite(bb, i, v) + or + SsaImpl::capturedEntryWrite(bb, i, v) + or + SsaImpl::variableWriteActual(bb, i, v, _) + ) and + certain = true + or + SsaImpl::capturedCallWrite(bb, i, v) and + certain = false +} + +predicate variableRead = SsaImpl::variableRead/4; diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll new file mode 100755 index 00000000000..86c8ffb7f50 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll @@ -0,0 +1,41 @@ +private import ruby +private import TaintTrackingPublic +private import codeql.ruby.CFG +private import codeql.ruby.DataFlow +private import FlowSummaryImpl as FlowSummaryImpl + +/** + * Holds if `node` should be a sanitizer in all global taint flow configurations + * but not in local taint. + */ +predicate defaultTaintSanitizer(DataFlow::Node node) { none() } + +/** + * Holds if default `TaintTracking::Configuration`s should allow implicit reads + * of `c` at sinks and inputs to additional taint steps. + */ +bindingset[node] +predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::Content c) { none() } + +/** + * Holds if the additional step from `nodeFrom` to `nodeTo` should be included + * in all global taint flow configurations. + */ +cached +predicate defaultAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + // operation involving `nodeFrom` + exists(CfgNodes::ExprNodes::OperationCfgNode op | + op = nodeTo.asExpr() and + op.getAnOperand() = nodeFrom.asExpr() and + not op.getExpr() instanceof AssignExpr + ) + or + // string interpolation of `nodeFrom` into `nodeTo` + nodeFrom.asExpr() = + nodeTo.asExpr().(CfgNodes::ExprNodes::StringlikeLiteralCfgNode).getAComponent() + or + // element reference from nodeFrom + nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::ElementReferenceCfgNode).getReceiver() + or + FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, false) +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPublic.qll new file mode 100755 index 00000000000..3fe5659bdc7 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPublic.qll @@ -0,0 +1,31 @@ +private import ruby +private import TaintTrackingPrivate +private import codeql.ruby.CFG +private import codeql.ruby.DataFlow +private import FlowSummaryImpl as FlowSummaryImpl + +/** + * Holds if taint propagates from `source` to `sink` in zero or more local + * (intra-procedural) steps. + */ +predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) } + +/** + * Holds if taint can flow from `e1` to `e2` in zero or more + * local (intra-procedural) steps. + */ +predicate localExprTaint(CfgNodes::ExprCfgNode e1, CfgNodes::ExprCfgNode e2) { + localTaint(DataFlow::exprNode(e1), DataFlow::exprNode(e2)) +} + +/** + * Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local + * (intra-procedural) step. + */ +predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + defaultAdditionalTaintStep(nodeFrom, nodeTo) + or + // Simple flow through library code is included in the exposed local + // step relation, even though flow is technically inter-procedural + FlowSummaryImpl::Private::Steps::summaryThroughStep(nodeFrom, nodeTo, false) +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll new file mode 100644 index 00000000000..acb029c23d9 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @@ -0,0 +1,122 @@ +/** + * Provides an implementation of global (interprocedural) taint tracking. + * This file re-exports the local (intraprocedural) taint-tracking analysis + * from `TaintTrackingParameter::Public` and adds a global analysis, mainly + * exposed through the `Configuration` class. For some languages, this file + * exists in several identical copies, allowing queries to use multiple + * `Configuration` classes that depend on each other without introducing + * mutual recursion among those configurations. + */ + +import TaintTrackingParameter::Public +private import TaintTrackingParameter::Private + +/** + * A configuration of interprocedural taint tracking analysis. This defines + * sources, sinks, and any other configurable aspect of the analysis. Each + * use of the taint tracking library must define its own unique extension of + * this abstract class. + * + * A taint-tracking configuration is a special data flow configuration + * (`DataFlow::Configuration`) that allows for flow through nodes that do not + * necessarily preserve values but are still relevant from a taint tracking + * perspective. (For example, string concatenation, where one of the operands + * is tainted.) + * + * To create a configuration, extend this class with a subclass whose + * characteristic predicate is a unique singleton string. For example, write + * + * ```ql + * class MyAnalysisConfiguration extends TaintTracking::Configuration { + * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } + * // Override `isSource` and `isSink`. + * // Optionally override `isSanitizer`. + * // Optionally override `isSanitizerIn`. + * // Optionally override `isSanitizerOut`. + * // Optionally override `isSanitizerGuard`. + * // Optionally override `isAdditionalTaintStep`. + * } + * ``` + * + * Then, to query whether there is flow between some `source` and `sink`, + * write + * + * ```ql + * exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink)) + * ``` + * + * Multiple configurations can coexist, but it is unsupported to depend on + * another `TaintTracking::Configuration` or a `DataFlow::Configuration` in the + * overridden predicates that define sources, sinks, or additional steps. + * Instead, the dependency should go to a `TaintTracking2::Configuration` or a + * `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc. + */ +abstract class Configuration extends DataFlow::Configuration { + bindingset[this] + Configuration() { any() } + + /** + * Holds if `source` is a relevant taint source. + * + * The smaller this predicate is, the faster `hasFlow()` will converge. + */ + // overridden to provide taint-tracking specific qldoc + abstract override predicate isSource(DataFlow::Node source); + + /** + * Holds if `sink` is a relevant taint sink. + * + * The smaller this predicate is, the faster `hasFlow()` will converge. + */ + // overridden to provide taint-tracking specific qldoc + abstract override predicate isSink(DataFlow::Node sink); + + /** Holds if the node `node` is a taint sanitizer. */ + predicate isSanitizer(DataFlow::Node node) { none() } + + final override predicate isBarrier(DataFlow::Node node) { + this.isSanitizer(node) or + defaultTaintSanitizer(node) + } + + /** Holds if taint propagation into `node` is prohibited. */ + predicate isSanitizerIn(DataFlow::Node node) { none() } + + final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) } + + /** Holds if taint propagation out of `node` is prohibited. */ + predicate isSanitizerOut(DataFlow::Node node) { none() } + + final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) } + + /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */ + predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } + + final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { + this.isSanitizerGuard(guard) + } + + /** + * Holds if the additional taint propagation step from `node1` to `node2` + * must be taken into account in the analysis. + */ + predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() } + + final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + this.isAdditionalTaintStep(node1, node2) or + defaultAdditionalTaintStep(node1, node2) + } + + override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) { + (this.isSink(node) or this.isAdditionalTaintStep(node, _)) and + defaultImplicitTaintRead(node, c) + } + + /** + * Holds if taint may flow from `source` to `sink` for this configuration. + */ + // overridden to provide taint-tracking specific qldoc + override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) { + super.hasFlow(source, sink) + } +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingParameter.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingParameter.qll new file mode 100644 index 00000000000..ce6f5ed1c48 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingParameter.qll @@ -0,0 +1,6 @@ +import codeql.ruby.dataflow.internal.TaintTrackingPublic as Public + +module Private { + import codeql.ruby.DataFlow::DataFlow as DataFlow + import codeql.ruby.dataflow.internal.TaintTrackingPrivate +} diff --git a/ruby/ql/lib/codeql/ruby/filters/GeneratedCode.qll b/ruby/ql/lib/codeql/ruby/filters/GeneratedCode.qll new file mode 100644 index 00000000000..18d12be3aac --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/filters/GeneratedCode.qll @@ -0,0 +1,43 @@ +/** Provides classes for detecting generated code. */ + +private import ruby +private import codeql.ruby.ast.internal.TreeSitter + +/** A source file that contains generated code. */ +abstract class GeneratedCodeFile extends RubyFile { } + +/** A file contining comments suggesting it contains generated code. */ +class GeneratedCommentFile extends GeneratedCodeFile { + GeneratedCommentFile() { this = any(GeneratedCodeComment c).getLocation().getFile() } +} + +/** A comment line that indicates generated code. */ +abstract class GeneratedCodeComment extends Ruby::Comment { } + +/** + * A generic comment line that suggests that the file is generated. + */ +class GenericGeneratedCodeComment extends GeneratedCodeComment { + GenericGeneratedCodeComment() { + exists(string line, string entity, string was, string automatically | line = getValue() | + entity = "file|class|art[ei]fact|module|script" and + was = "was|is|has been" and + automatically = "automatically |mechanically |auto[- ]?" and + line.regexpMatch("(?i).*\\bThis (" + entity + ") (" + was + ") (" + automatically + + ")?generated\\b.*") + ) + } +} + +/** A comment warning against modifications. */ +class DontModifyMarkerComment extends GeneratedCodeComment { + DontModifyMarkerComment() { + exists(string line | line = getValue() | + line.regexpMatch("(?i).*\\bGenerated by\\b.*\\bDo not edit\\b.*") or + line.regexpMatch("(?i).*\\bAny modifications to this file will be lost\\b.*") + ) + } +} + +/** Holds if `file` looks like it contains generated code. */ +predicate isGeneratedCode(GeneratedCodeFile file) { any() } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll new file mode 100644 index 00000000000..0eec1e15f58 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll @@ -0,0 +1,259 @@ +private import codeql.ruby.AST +private import codeql.ruby.Concepts +private import codeql.ruby.controlflow.CfgNodes +private import codeql.ruby.DataFlow +private import codeql.ruby.dataflow.RemoteFlowSources +private import codeql.ruby.ast.internal.Module +private import ActionView + +private class ActionControllerBaseAccess extends ConstantReadAccess { + ActionControllerBaseAccess() { + this.getName() = "Base" and + this.getScopeExpr().(ConstantAccess).getName() = "ActionController" + } +} + +// ApplicationController extends ActionController::Base, but we +// treat it separately in case the ApplicationController definition +// is not in the database +private class ApplicationControllerAccess extends ConstantReadAccess { + ApplicationControllerAccess() { this.getName() = "ApplicationController" } +} + +/** + * A `ClassDeclaration` for a class that extends `ActionController::Base`. + * For example, + * + * ```rb + * class FooController < ActionController::Base + * def delete_handler + * uid = params[:id] + * User.delete_by("id = ?", uid) + * end + * end + * ``` + */ +class ActionControllerControllerClass extends ClassDeclaration { + ActionControllerControllerClass() { + // class FooController < ActionController::Base + this.getSuperclassExpr() instanceof ActionControllerBaseAccess + or + // class FooController < ApplicationController + this.getSuperclassExpr() instanceof ApplicationControllerAccess + or + // class BarController < FooController + exists(ActionControllerControllerClass other | + other.getModule() = resolveScopeExpr(this.getSuperclassExpr()) + ) + } + + /** + * Gets a `ActionControllerActionMethod` defined in this class. + */ + ActionControllerActionMethod getAnAction() { result = this.getAMethod() } +} + +/** + * An instance method defined within an `ActionController` controller class. + * This may be the target of a route handler, if such a route is defined. + */ +class ActionControllerActionMethod extends Method, HTTP::Server::RequestHandler::Range { + private ActionControllerControllerClass controllerClass; + + ActionControllerActionMethod() { this = controllerClass.getAMethod() } + + /** + * Establishes a mapping between a method within the file + * `<sourcePrefix>app/controllers/<subpath>_controller.rb` and the + * corresponding template file at + * `<sourcePrefix>app/views/<subpath>/<method_name>.html.erb`. + */ + ErbFile getDefaultTemplateFile() { + controllerTemplateFile(this.getControllerClass(), result) and + result.getBaseName() = this.getName() + ".html.erb" + } + + // params come from `params` method rather than a method parameter + override Parameter getARoutedParameter() { none() } + + override string getFramework() { result = "ActionController" } + + /** Gets a call to render from within this method. */ + RenderCall getARenderCall() { result.getParent+() = this } + + // TODO: model the implicit render call when a path through the method does + // not end at an explicit render or redirect + /** Gets the controller class containing this method. */ + ActionControllerControllerClass getControllerClass() { result = controllerClass } +} + +// A method call with a `self` receiver from within a controller class +private class ActionControllerContextCall extends MethodCall { + private ActionControllerControllerClass controllerClass; + + ActionControllerContextCall() { + this.getReceiver() instanceof Self and + this.getEnclosingModule() = controllerClass + } + + ActionControllerControllerClass getControllerClass() { result = controllerClass } +} + +/** + * A call to the `params` method to fetch the request parameters. + */ +abstract class ParamsCall extends MethodCall { + ParamsCall() { this.getMethodName() = "params" } +} + +/** + * A `RemoteFlowSource::Range` to represent accessing the + * ActionController parameters available via the `params` method. + */ +class ParamsSource extends RemoteFlowSource::Range { + ParamsCall call; + + ParamsSource() { this.asExpr().getExpr() = call } + + override string getSourceType() { result = "ActionController::Metal#params" } +} + +// A call to `params` from within a controller. +private class ActionControllerParamsCall extends ActionControllerContextCall, ParamsCall { } + +// A call to `render` from within a controller. +private class ActionControllerRenderCall extends ActionControllerContextCall, RenderCall { } + +// A call to `render_to` from within a controller. +private class ActionControllerRenderToCall extends ActionControllerContextCall, RenderToCall { } + +// A call to `html_safe` from within a controller. +private class ActionControllerHtmlSafeCall extends HtmlSafeCall { + ActionControllerHtmlSafeCall() { + this.getEnclosingModule() instanceof ActionControllerControllerClass + } +} + +// A call to `html_escape` from within a controller. +private class ActionControllerHtmlEscapeCall extends HtmlEscapeCall { + ActionControllerHtmlEscapeCall() { + this.getEnclosingModule() instanceof ActionControllerControllerClass + } +} + +/** + * A call to the `redirect_to` method, used in an action to redirect to a + * specific URL/path or to a different action in this controller. + */ +class RedirectToCall extends ActionControllerContextCall { + RedirectToCall() { this.getMethodName() = "redirect_to" } + + /** Gets the `Expr` representing the URL to redirect to, if any */ + Expr getRedirectUrl() { result = this.getArgument(0) } + + /** Gets the `ActionControllerActionMethod` to redirect to, if any */ + ActionControllerActionMethod getRedirectActionMethod() { + exists(string methodName | + methodName = this.getKeywordArgument("action").(StringlikeLiteral).getValueText() and + methodName = result.getName() and + result.getEnclosingModule() = this.getControllerClass() + ) + } +} + +/** + * A call to the `redirect_to` method, as an `HttpRedirectResponse`. + */ +class ActionControllerRedirectResponse extends HTTP::Server::HttpRedirectResponse::Range { + RedirectToCall redirectToCall; + + ActionControllerRedirectResponse() { this.asExpr().getExpr() = redirectToCall } + + override DataFlow::Node getBody() { none() } + + override DataFlow::Node getMimetypeOrContentTypeArg() { none() } + + override string getMimetypeDefault() { none() } + + override DataFlow::Node getRedirectLocation() { + result.asExpr().getExpr() = redirectToCall.getRedirectUrl() + } +} + +/** + * A method in an `ActionController` class that is accessible from within a + * Rails view as a helper method. For instance, in: + * + * ```rb + * class FooController < ActionController::Base + * helper_method :logged_in? + * def logged_in? + * @current_user != nil + * end + * end + * ``` + * + * the `logged_in?` method is a helper method. + * See also https://api.rubyonrails.org/classes/AbstractController/Helpers/ClassMethods.html#method-i-helper_method + */ +class ActionControllerHelperMethod extends Method { + private ActionControllerControllerClass controllerClass; + + ActionControllerHelperMethod() { + this.getEnclosingModule() = controllerClass and + exists(MethodCall helperMethodMarker | + helperMethodMarker.getMethodName() = "helper_method" and + helperMethodMarker.getAnArgument().(StringlikeLiteral).getValueText() = this.getName() and + helperMethodMarker.getEnclosingModule() = controllerClass + ) + } + + /** Gets the class containing this helper method. */ + ActionControllerControllerClass getControllerClass() { result = controllerClass } +} + +/** + * Gets an `ActionControllerControllerClass` associated with the given `ErbFile` + * according to Rails path conventions. + * For instance, a template file at `app/views/foo/bar/baz.html.erb` will be + * mapped to a controller class in `app/controllers/foo/bar/baz_controller.rb`, + * if such a controller class exists. + */ +ActionControllerControllerClass getAssociatedControllerClass(ErbFile f) { + // There is a direct mapping from template file to controller class + controllerTemplateFile(result, f) + or + // The template `f` is a partial, and it is rendered from within another + // template file, `fp`. In this case, `f` inherits the associated + // controller classes from `fp`. + f.isPartial() and + exists(RenderCall r, ErbFile fp | + r.getLocation().getFile() = fp and + r.getTemplateFile() = f and + result = getAssociatedControllerClass(fp) + ) +} + +// TODO: improve layout support, e.g. for `layout` method +// https://guides.rubyonrails.org/layouts_and_rendering.html +/** + * Holds if `templatesFile` is a viable file "belonging" to the given + * `ActionControllerControllerClass`, according to Rails conventions. + * + * This handles mappings between controllers in `app/controllers/`, and + * templates in `app/views/` and `app/views/layouts/`. + */ +predicate controllerTemplateFile(ActionControllerControllerClass cls, ErbFile templateFile) { + exists(string templatesPath, string sourcePrefix, string subPath, string controllerPath | + controllerPath = cls.getLocation().getFile().getRelativePath() and + templatesPath = templateFile.getParentContainer().getRelativePath() and + // `sourcePrefix` is either a prefix path ending in a slash, or empty if + // the rails app is at the source root + sourcePrefix = [controllerPath.regexpCapture("^(.*/)app/controllers/(?:.*?)/(?:[^/]*)$", 1), ""] and + controllerPath = sourcePrefix + "app/controllers/" + subPath + "_controller.rb" and + ( + templatesPath = sourcePrefix + "app/views/" + subPath or + templateFile.getRelativePath().matches(sourcePrefix + "app/views/layouts/" + subPath + "%") + ) + ) +} diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionView.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionView.qll new file mode 100644 index 00000000000..55638ab6584 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionView.qll @@ -0,0 +1,138 @@ +private import codeql.ruby.AST +private import codeql.ruby.Concepts +private import codeql.ruby.controlflow.CfgNodes +private import codeql.ruby.DataFlow +private import codeql.ruby.dataflow.RemoteFlowSources +private import codeql.ruby.ast.internal.Module +private import ActionController + +predicate inActionViewContext(AstNode n) { + // Within a template + n.getLocation().getFile() instanceof ErbFile +} + +/** + * A method call on a string to mark it as HTML safe for Rails. + * Strings marked as such will not be automatically escaped when inserted into + * HTML. + */ +abstract class HtmlSafeCall extends MethodCall { + HtmlSafeCall() { this.getMethodName() = "html_safe" } +} + +// A call to `html_safe` from within a template. +private class ActionViewHtmlSafeCall extends HtmlSafeCall { + ActionViewHtmlSafeCall() { inActionViewContext(this) } +} + +/** + * A call to a method named "html_escape", "html_escape_once", or "h". + */ +abstract class HtmlEscapeCall extends MethodCall { + // "h" is aliased to "html_escape" in ActiveSupport + HtmlEscapeCall() { this.getMethodName() = ["html_escape", "html_escape_once", "h"] } +} + +class RailsHtmlEscaping extends Escaping::Range, DataFlow::CallNode { + RailsHtmlEscaping() { this.asExpr().getExpr() instanceof HtmlEscapeCall } + + override DataFlow::Node getAnInput() { result = this.getArgument(0) } + + override DataFlow::Node getOutput() { result = this } + + override string getKind() { result = Escaping::getHtmlKind() } +} + +// A call to `html_escape` from within a template. +private class ActionViewHtmlEscapeCall extends HtmlEscapeCall { + ActionViewHtmlEscapeCall() { inActionViewContext(this) } +} + +// A call in a context where some commonly used `ActionView` methods are available. +private class ActionViewContextCall extends MethodCall { + ActionViewContextCall() { + this.getReceiver() instanceof Self and + inActionViewContext(this) + } + + predicate isInErbFile() { this.getLocation().getFile() instanceof ErbFile } +} + +/** A call to the `raw` method to output a value without HTML escaping. */ +class RawCall extends ActionViewContextCall { + RawCall() { this.getMethodName() = "raw" } +} + +// A call to the `params` method within the context of a template. +private class ActionViewParamsCall extends ActionViewContextCall, ParamsCall { } + +/** + * A call to a `render` method that will populate the response body with the + * rendered content. + */ +abstract class RenderCall extends MethodCall { + RenderCall() { this.getMethodName() = "render" } + + private Expr getTemplatePathArgument() { + // TODO: support other ways of specifying paths (e.g. `file`) + result = [this.getKeywordArgument(["partial", "template", "action"]), this.getArgument(0)] + } + + private string getTemplatePathValue() { result = this.getTemplatePathArgument().getValueText() } + + // everything up to and including the final slash, but ignoring any leading slash + private string getSubPath() { + result = this.getTemplatePathValue().regexpCapture("^/?(.*/)?(?:[^/]*?)$", 1) + } + + // everything after the final slash, or the whole string if there is no slash + private string getBaseName() { + result = this.getTemplatePathValue().regexpCapture("^/?(?:.*/)?([^/]*?)$", 1) + } + + /** + * Gets the template file to be rendered by this call, if any. + */ + ErbFile getTemplateFile() { + result.getTemplateName() = this.getBaseName() and + result.getRelativePath().matches("%app/views/" + this.getSubPath() + "%") + } + + /** + * Get the local variables passed as context to the renderer + */ + HashLiteral getLocals() { result = this.getKeywordArgument("locals") } + // TODO: implicit renders in controller actions +} + +// A call to the `render` method within the context of a template. +private class ActionViewRenderCall extends RenderCall, ActionViewContextCall { } + +/** + * A render call that does not automatically set the HTTP response body. + */ +abstract class RenderToCall extends MethodCall { + RenderToCall() { this.getMethodName() = ["render_to_body", "render_to_string"] } +} + +// A call to `render_to` from within a template. +private class ActionViewRenderToCall extends ActionViewContextCall, RenderToCall { } + +/** + * A call to the ActionView `link_to` helper method. + * + * This generates an HTML anchor tag. The method is not designed to expect + * user-input, so provided paths are not automatically HTML escaped. + */ +class LinkToCall extends ActionViewContextCall { + LinkToCall() { this.getMethodName() = "link_to" } + + Expr getPathArgument() { + // When `link_to` is called with a block, it uses the first argument as the + // path, and otherwise the second argument. + exists(this.getBlock()) and result = this.getArgument(0) + or + not exists(this.getBlock()) and result = this.getArgument(1) + } +} +// TODO: model flow in/out of template files properly, diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll new file mode 100644 index 00000000000..2a13b51acfb --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll @@ -0,0 +1,319 @@ +private import codeql.ruby.AST +private import codeql.ruby.Concepts +private import codeql.ruby.controlflow.CfgNodes +private import codeql.ruby.DataFlow +private import codeql.ruby.dataflow.internal.DataFlowDispatch +private import codeql.ruby.ast.internal.Module +private import codeql.ruby.ApiGraphs +private import codeql.ruby.frameworks.StandardLibrary + +private class ActiveRecordBaseAccess extends ConstantReadAccess { + ActiveRecordBaseAccess() { + this.getName() = "Base" and + this.getScopeExpr().(ConstantAccess).getName() = "ActiveRecord" + } +} + +// ApplicationRecord extends ActiveRecord::Base, but we +// treat it separately in case the ApplicationRecord definition +// is not in the database +private class ApplicationRecordAccess extends ConstantReadAccess { + ApplicationRecordAccess() { this.getName() = "ApplicationRecord" } +} + +/// See https://api.rubyonrails.org/classes/ActiveRecord/Persistence.html +private string activeRecordPersistenceInstanceMethodName() { + result = + [ + "becomes", "becomes!", "decrement", "decrement!", "delete", "delete!", "destroy", "destroy!", + "destroyed?", "increment", "increment!", "new_record?", "persisted?", + "previously_new_record?", "reload", "save", "save!", "toggle", "toggle!", "touch", "update", + "update!", "update_attribute", "update_column", "update_columns" + ] +} + +// Methods with these names are defined for all active record model instances, +// so they are unlikely to refer to a database field. +private predicate isBuiltInMethodForActiveRecordModelInstance(string methodName) { + methodName = activeRecordPersistenceInstanceMethodName() or + methodName = basicObjectInstanceMethodName() or + methodName = objectInstanceMethodName() +} + +/** + * A `ClassDeclaration` for a class that extends `ActiveRecord::Base`. For example, + * + * ```rb + * class UserGroup < ActiveRecord::Base + * has_many :users + * end + * ``` + */ +class ActiveRecordModelClass extends ClassDeclaration { + ActiveRecordModelClass() { + // class Foo < ActiveRecord::Base + this.getSuperclassExpr() instanceof ActiveRecordBaseAccess + or + // class Foo < ApplicationRecord + this.getSuperclassExpr() instanceof ApplicationRecordAccess + or + // class Bar < Foo + exists(ActiveRecordModelClass other | + other.getModule() = resolveScopeExpr(this.getSuperclassExpr()) + ) + } + + // Gets the class declaration for this class and all of its super classes + private ModuleBase getAllClassDeclarations() { + result = this.getModule().getSuperClass*().getADeclaration() + } + + /** + * Gets methods defined in this class that may access a field from the database. + */ + Method getAPotentialFieldAccessMethod() { + // It's a method on this class or one of its super classes + result = this.getAllClassDeclarations().getAMethod() and + // There is a value that can be returned by this method which may include field data + exists(DataFlow::Node returned, ActiveRecordInstanceMethodCall cNode, MethodCall c | + exprNodeReturnedFrom(returned, result) and + cNode.flowsTo(returned) and + c = cNode.asExpr().getExpr() + | + // The referenced method is not built-in, and... + not isBuiltInMethodForActiveRecordModelInstance(c.getMethodName()) and + ( + // ...The receiver does not have a matching method definition, or... + not exists( + cNode.getInstance().getClass().getAllClassDeclarations().getMethod(c.getMethodName()) + ) + or + // ...the called method can access a field + c.getATarget() = cNode.getInstance().getClass().getAPotentialFieldAccessMethod() + ) + ) + } +} + +/** A class method call whose receiver is an `ActiveRecordModelClass`. */ +class ActiveRecordModelClassMethodCall extends MethodCall { + private ActiveRecordModelClass recvCls; + + ActiveRecordModelClassMethodCall() { + // e.g. Foo.where(...) + recvCls.getModule() = resolveScopeExpr(this.getReceiver()) + or + // e.g. Foo.joins(:bars).where(...) + recvCls = this.getReceiver().(ActiveRecordModelClassMethodCall).getReceiverClass() + or + // e.g. self.where(...) within an ActiveRecordModelClass + this.getReceiver() instanceof Self and + this.getEnclosingModule() = recvCls + } + + /** The `ActiveRecordModelClass` of the receiver of this method. */ + ActiveRecordModelClass getReceiverClass() { result = recvCls } +} + +private Expr sqlFragmentArgument(MethodCall call) { + exists(string methodName | + methodName = call.getMethodName() and + ( + methodName = + [ + "delete_all", "delete_by", "destroy_all", "destroy_by", "exists?", "find_by", "find_by!", + "find_or_create_by", "find_or_create_by!", "find_or_initialize_by", "find_by_sql", "from", + "group", "having", "joins", "lock", "not", "order", "pluck", "where", "rewhere", "select", + "reselect", "update_all" + ] and + result = call.getArgument(0) + or + methodName = "calculate" and result = call.getArgument(1) + or + methodName in ["average", "count", "maximum", "minimum", "sum"] and + result = call.getArgument(0) + or + // This format was supported until Rails 2.3.8 + methodName = ["all", "find", "first", "last"] and + result = call.getKeywordArgument("conditions") + or + methodName = "reload" and + result = call.getKeywordArgument("lock") + ) + ) +} + +// An expression that, if tainted by unsanitized input, should not be used as +// part of an argument to an SQL executing method +private predicate unsafeSqlExpr(Expr sqlFragmentExpr) { + // Literals containing an interpolated value + exists(StringInterpolationComponent interpolated | + interpolated = sqlFragmentExpr.(StringlikeLiteral).getComponent(_) + ) + or + // String concatenations + sqlFragmentExpr instanceof AddExpr + or + // Variable reads + sqlFragmentExpr instanceof VariableReadAccess + or + // Method call + sqlFragmentExpr instanceof MethodCall +} + +/** + * A method call that may result in executing unintended user-controlled SQL + * queries if the `getSqlFragmentSinkArgument()` expression is tainted by + * unsanitized user-controlled input. For example, supposing that `User` is an + * `ActiveRecord` model class, then + * + * ```rb + * User.where("name = '#{user_name}'") + * ``` + * + * may be unsafe if `user_name` is from unsanitized user input, as a value such + * as `"') OR 1=1 --"` could result in the application looking up all users + * rather than just one with a matching name. + */ +class PotentiallyUnsafeSqlExecutingMethodCall extends ActiveRecordModelClassMethodCall { + // The SQL fragment argument itself + private Expr sqlFragmentExpr; + + PotentiallyUnsafeSqlExecutingMethodCall() { + exists(Expr arg | + arg = sqlFragmentArgument(this) and + unsafeSqlExpr(sqlFragmentExpr) and + ( + sqlFragmentExpr = arg + or + sqlFragmentExpr = arg.(ArrayLiteral).getElement(0) + ) and + // Check that method has not been overridden + not exists(SingletonMethod m | + m.getName() = this.getMethodName() and + m.getOuterScope() = this.getReceiverClass() + ) + ) + } + + Expr getSqlFragmentSinkArgument() { result = sqlFragmentExpr } +} + +/** + * An `SqlExecution::Range` for an argument to a + * `PotentiallyUnsafeSqlExecutingMethodCall` that may be vulnerable to being + * controlled by user input. + */ +class ActiveRecordSqlExecutionRange extends SqlExecution::Range { + ActiveRecordSqlExecutionRange() { + exists(PotentiallyUnsafeSqlExecutingMethodCall mc | + this.asExpr().getNode() = mc.getSqlFragmentSinkArgument() + ) + } + + override DataFlow::Node getSql() { result = this } +} + +// TODO: model `ActiveRecord` sanitizers +// https://api.rubyonrails.org/classes/ActiveRecord/Sanitization/ClassMethods.html +/** + * A node that may evaluate to one or more `ActiveRecordModelClass` instances. + */ +abstract class ActiveRecordModelInstantiation extends OrmInstantiation::Range, + DataFlow::LocalSourceNode { + abstract ActiveRecordModelClass getClass(); + + bindingset[methodName] + override predicate methodCallMayAccessField(string methodName) { + // The method is not a built-in, and... + not isBuiltInMethodForActiveRecordModelInstance(methodName) and + ( + // ...There is no matching method definition in the class, or... + not exists(this.getClass().getMethod(methodName)) + or + // ...the called method can access a field. + exists(Method m | m = this.getClass().getAPotentialFieldAccessMethod() | + m.getName() = methodName + ) + ) + } +} + +// Names of class methods on ActiveRecord models that may return one or more +// instances of that model. This also includes the `initialize` method. +// See https://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html +private string finderMethodName() { + exists(string baseName | + baseName = + [ + "fifth", "find", "find_by", "find_or_initialize_by", "find_or_create_by", "first", + "forty_two", "fourth", "last", "second", "second_to_last", "take", "third", "third_to_last" + ] and + result = baseName + ["", "!"] + ) + or + result = "new" +} + +// Gets the "final" receiver in a chain of method calls. +// For example, in `Foo.bar`, this would give the `Foo` access, and in +// `foo.bar.baz("arg")` it would give the `foo` variable access +private Expr getUltimateReceiver(MethodCall call) { + exists(Expr recv | + recv = call.getReceiver() and + ( + result = getUltimateReceiver(recv) + or + not recv instanceof MethodCall and result = recv + ) + ) +} + +// A call to `find`, `where`, etc. that may return active record model object(s) +private class ActiveRecordModelFinderCall extends ActiveRecordModelInstantiation, DataFlow::CallNode { + private MethodCall call; + private ActiveRecordModelClass cls; + private Expr recv; + + ActiveRecordModelFinderCall() { + call = this.asExpr().getExpr() and + recv = getUltimateReceiver(call) and + resolveConstant(recv) = cls.getQualifiedName() and + call.getMethodName() = finderMethodName() + } + + final override ActiveRecordModelClass getClass() { result = cls } +} + +// A `self` reference that may resolve to an active record model object +private class ActiveRecordModelClassSelfReference extends ActiveRecordModelInstantiation { + private ActiveRecordModelClass cls; + + ActiveRecordModelClassSelfReference() { + exists(Self s | + s.getEnclosingModule() = cls and + s.getEnclosingMethod() = cls.getAMethod() and + s = this.asExpr().getExpr() + ) + } + + final override ActiveRecordModelClass getClass() { result = cls } +} + +// A (locally tracked) active record model object +private class ActiveRecordInstance extends DataFlow::Node { + private ActiveRecordModelInstantiation instantiation; + + ActiveRecordInstance() { this = instantiation or instantiation.flowsTo(this) } + + ActiveRecordModelClass getClass() { result = instantiation.getClass() } +} + +// A call whose receiver may be an active record model object +private class ActiveRecordInstanceMethodCall extends DataFlow::CallNode { + private ActiveRecordInstance instance; + + ActiveRecordInstanceMethodCall() { this.getReceiver() = instance } + + ActiveRecordInstance getInstance() { result = instance } +} diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActiveStorage.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveStorage.qll new file mode 100644 index 00000000000..f25613aa4db --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActiveStorage.qll @@ -0,0 +1,55 @@ +private import codeql.ruby.AST +private import codeql.ruby.ApiGraphs +private import codeql.ruby.Concepts +private import codeql.ruby.DataFlow +private import codeql.ruby.dataflow.FlowSummary + +/** Defines calls to `ActiveStorage::Filename#sanitized` as path sanitizers. */ +class ActiveStorageFilenameSanitizedCall extends Path::PathSanitization::Range, DataFlow::CallNode { + ActiveStorageFilenameSanitizedCall() { + this.getReceiver() = + API::getTopLevelMember("ActiveStorage").getMember("Filename").getAnInstantiation() and + this.asExpr().getExpr().(MethodCall).getMethodName() = "sanitized" + } +} + +/** Taint summary for `ActiveStorage::Filename.new`. */ +class ActiveStorageFilenameNewSummary extends SummarizedCallable { + ActiveStorageFilenameNewSummary() { this = "ActiveStorage::Filename.new" } + + override MethodCall getACall() { + result = + API::getTopLevelMember("ActiveStorage") + .getMember("Filename") + .getAnInstantiation() + .asExpr() + .getExpr() + } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[0]" and + output = "ReturnValue" and + preservesValue = false + } +} + +/** Taint summary for `ActiveStorage::Filename#sanitized`. */ +class ActiveStorageFilenameSanitizedSummary extends SummarizedCallable { + ActiveStorageFilenameSanitizedSummary() { this = "ActiveStorage::Filename#sanitized" } + + override MethodCall getACall() { + result = + API::getTopLevelMember("ActiveStorage") + .getMember("Filename") + .getInstance() + .getAMethodCall("sanitized") + .asExpr() + .getExpr() + } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[-1]" and + output = "ReturnValue" and + preservesValue = false + } +} diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Files.qll b/ruby/ql/lib/codeql/ruby/frameworks/Files.qll new file mode 100644 index 00000000000..440a6e6201d --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/Files.qll @@ -0,0 +1,341 @@ +/** + * Provides classes for working with file system libraries. + */ + +private import ruby +private import codeql.ruby.Concepts +private import codeql.ruby.ApiGraphs +private import codeql.ruby.DataFlow +private import codeql.ruby.frameworks.StandardLibrary +private import codeql.ruby.dataflow.FlowSummary + +private DataFlow::Node ioInstanceInstantiation() { + result = API::getTopLevelMember("IO").getAnInstantiation() or + result = API::getTopLevelMember("IO").getAMethodCall(["for_fd", "open", "try_convert"]) +} + +private DataFlow::Node ioInstance() { + result = ioInstanceInstantiation() + or + exists(DataFlow::Node inst | + inst = ioInstance() and + inst.(DataFlow::LocalSourceNode).flowsTo(result) + ) +} + +// Match some simple cases where a path argument specifies a shell command to +// be executed. For example, the `"|date"` argument in `IO.read("|date")`, which +// will execute a shell command and read its output rather than reading from the +// filesystem. +private predicate pathArgSpawnsSubprocess(Expr arg) { + arg.(StringlikeLiteral).getValueText().charAt(0) = "|" +} + +private DataFlow::Node fileInstanceInstantiation() { + result = API::getTopLevelMember("File").getAnInstantiation() + or + result = API::getTopLevelMember("File").getAMethodCall("open") + or + // Calls to `Kernel.open` can yield `File` instances + result.(KernelMethodCall).getMethodName() = "open" and + // Assume that calls that don't invoke shell commands will instead open + // a file. + not pathArgSpawnsSubprocess(result.(KernelMethodCall).getArgument(0).asExpr().getExpr()) +} + +private DataFlow::Node fileInstance() { + result = fileInstanceInstantiation() + or + exists(DataFlow::Node inst | + inst = fileInstance() and + inst.(DataFlow::LocalSourceNode).flowsTo(result) + ) +} + +private string ioFileReaderClassMethodName() { + result = ["binread", "foreach", "read", "readlines", "try_convert"] +} + +private string ioFileReaderInstanceMethodName() { + result = + [ + "getbyte", "getc", "gets", "pread", "read", "read_nonblock", "readbyte", "readchar", + "readline", "readlines", "readpartial", "sysread" + ] +} + +private string ioFileReaderMethodName(boolean classMethodCall) { + classMethodCall = true and result = ioFileReaderClassMethodName() + or + classMethodCall = false and result = ioFileReaderInstanceMethodName() +} + +/** + * Classes and predicates for modeling the core `IO` module. + */ +module IO { + /** + * An instance of the `IO` class, for example in + * + * ```rb + * rand = IO.new(IO.sysopen("/dev/random", "r"), "r") + * rand_data = rand.read(32) + * ``` + * + * there are 3 `IOInstance`s - the call to `IO.new`, the assignment + * `rand = ...`, and the read access to `rand` on the second line. + */ + class IOInstance extends DataFlow::Node { + IOInstance() { + this = ioInstance() or + this = fileInstance() + } + } + + // "Direct" `IO` instances, i.e. cases where there is no more specific + // subtype such as `File` + private class IOInstanceStrict extends IOInstance { + IOInstanceStrict() { this = ioInstance() } + } + + /** + * A `DataFlow::CallNode` that reads data using the `IO` class. For example, + * the `IO.read call in: + * + * ```rb + * IO.read("|date") + * ``` + * + * returns the output of the `date` shell command, invoked as a subprocess. + * + * This class includes reads both from shell commands and reads from the + * filesystem. For working with filesystem accesses specifically, see + * `IOFileReader` or the `FileSystemReadAccess` concept. + */ + class IOReader extends DataFlow::CallNode { + private boolean classMethodCall; + private string api; + + IOReader() { + // Class methods + api = ["File", "IO"] and + classMethodCall = true and + this = API::getTopLevelMember(api).getAMethodCall(ioFileReaderMethodName(classMethodCall)) + or + // IO instance methods + classMethodCall = false and + api = "IO" and + exists(IOInstanceStrict ii | + this.getReceiver() = ii and + this.asExpr().getExpr().(MethodCall).getMethodName() = + ioFileReaderMethodName(classMethodCall) + ) + or + // File instance methods + classMethodCall = false and + api = "File" and + exists(File::FileInstance fi | + this.getReceiver() = fi and + this.asExpr().getExpr().(MethodCall).getMethodName() = + ioFileReaderMethodName(classMethodCall) + ) + // TODO: enumeration style methods such as `each`, `foreach`, etc. + } + + /** + * Returns the most specific core class used for this read, `IO` or `File` + */ + string getAPI() { result = api } + + predicate isClassMethodCall() { classMethodCall = true } + } + + /** + * A `DataFlow::CallNode` that reads data from the filesystem using the `IO` + * class. For example, the `IO.read call in: + * + * ```rb + * IO.read("foo.txt") + * ``` + * + * reads the file `foo.txt` and returns its contents as a string. + */ + class IOFileReader extends IOReader, FileSystemReadAccess::Range { + IOFileReader() { + this.getAPI() = "File" + or + this.isClassMethodCall() and + // Assume that calls that don't invoke shell commands will instead + // read from a file. + not pathArgSpawnsSubprocess(this.getArgument(0).asExpr().getExpr()) + } + + // TODO: can we infer a path argument for instance method calls? + // e.g. by tracing back to the instantiation of that instance + override DataFlow::Node getAPathArgument() { + result = this.getArgument(0) and this.isClassMethodCall() + } + + // This class represents calls that return data + override DataFlow::Node getADataNode() { result = this } + } +} + +/** + * Classes and predicates for modeling the core `File` module. + * + * Because `File` is a subclass of `IO`, all `FileInstance`s and + * `FileModuleReader`s are also `IOInstance`s and `IOModuleReader`s + * respectively. + */ +module File { + /** + * An instance of the `File` class, for example in + * + * ```rb + * f = File.new("foo.txt") + * puts f.read() + * ``` + * + * there are 3 `FileInstance`s - the call to `File.new`, the assignment + * `f = ...`, and the read access to `f` on the second line. + */ + class FileInstance extends IO::IOInstance { + FileInstance() { this = fileInstance() } + } + + /** + * A read using the `File` module, e.g. the `f.read` call in + * + * ```rb + * f = File.new("foo.txt") + * puts f.read() + * ``` + */ + class FileModuleReader extends IO::IOFileReader { + FileModuleReader() { this.getAPI() = "File" } + } + + /** + * A call to a `File` method that may return one or more filenames. + */ + class FileModuleFilenameSource extends FileNameSource, DataFlow::CallNode { + FileModuleFilenameSource() { + // Class methods + this = + API::getTopLevelMember("File") + .getAMethodCall([ + "absolute_path", "basename", "expand_path", "join", "path", "readlink", + "realdirpath", "realpath" + ]) + or + // Instance methods + exists(FileInstance fi | + this.getReceiver() = fi and + this.asExpr().getExpr().(MethodCall).getMethodName() = ["path", "to_path"] + ) + } + } + + private class FileModulePermissionModification extends FileSystemPermissionModification::Range, + DataFlow::CallNode { + private DataFlow::Node permissionArg; + + FileModulePermissionModification() { + exists(string methodName | this = API::getTopLevelMember("File").getAMethodCall(methodName) | + methodName in ["chmod", "lchmod"] and permissionArg = this.getArgument(0) + or + methodName = "mkfifo" and permissionArg = this.getArgument(1) + or + methodName in ["new", "open"] and permissionArg = this.getArgument(2) + // TODO: defaults for optional args? This may depend on the umask + ) + } + + override DataFlow::Node getAPermissionNode() { result = permissionArg } + } + + /** + * Flow summary for several methods on the `File` class that propagate taint + * from their first argument to the return value. + */ + class FilePathConversionSummary extends SummarizedCallable { + string methodName; + + FilePathConversionSummary() { + methodName = ["absolute_path", "dirname", "expand_path", "path", "realdirpath", "realpath"] and + this = "File." + methodName + } + + override MethodCall getACall() { + result = API::getTopLevelMember("File").getAMethodCall(methodName).asExpr().getExpr() + } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[0]" and + output = "ReturnValue" and + preservesValue = false + } + } + + /** + * Flow summary for `File.join`, which propagates taint from every argument to + * its return value. + */ + class FileJoinSummary extends SummarizedCallable { + FileJoinSummary() { this = "File.join" } + + override MethodCall getACall() { + result = API::getTopLevelMember("File").getAMethodCall("join").asExpr().getExpr() + } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[_]" and + output = "ReturnValue" and + preservesValue = false + } + } +} + +/** + * Classes and predicates for modeling the `FileUtils` module from the standard + * library. + */ +module FileUtils { + /** + * A call to a FileUtils method that may return one or more filenames. + */ + class FileUtilsFilenameSource extends FileNameSource { + FileUtilsFilenameSource() { + // Note that many methods in FileUtils accept a `noop` option that will + // perform a dry run of the command. This means that, for instance, `rm` + // and similar methods may not actually delete/unlink a file when called. + this = + API::getTopLevelMember("FileUtils") + .getAMethodCall([ + "chmod", "chmod_R", "chown", "chown_R", "getwd", "makedirs", "mkdir", "mkdir_p", + "mkpath", "remove", "remove_dir", "remove_entry", "rm", "rm_f", "rm_r", "rm_rf", + "rmdir", "rmtree", "safe_unlink", "touch" + ]) + } + } + + private class FileUtilsPermissionModification extends FileSystemPermissionModification::Range, + DataFlow::CallNode { + private DataFlow::Node permissionArg; + + FileUtilsPermissionModification() { + exists(string methodName | + this = API::getTopLevelMember("FileUtils").getAMethodCall(methodName) + | + methodName in ["chmod", "chmod_R"] and permissionArg = this.getArgument(0) + or + methodName in ["install", "makedirs", "mkdir", "mkdir_p", "mkpath"] and + permissionArg = this.getKeywordArgument("mode") + // TODO: defaults for optional args? This may depend on the umask + ) + } + + override DataFlow::Node getAPermissionNode() { result = permissionArg } + } +} diff --git a/ruby/ql/lib/codeql/ruby/frameworks/HttpClients.qll b/ruby/ql/lib/codeql/ruby/frameworks/HttpClients.qll new file mode 100644 index 00000000000..acb902694fe --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/HttpClients.qll @@ -0,0 +1,12 @@ +/** + * Helper file that imports all HTTP clients. + */ + +private import codeql.ruby.frameworks.http_clients.NetHttp +private import codeql.ruby.frameworks.http_clients.Excon +private import codeql.ruby.frameworks.http_clients.Faraday +private import codeql.ruby.frameworks.http_clients.RestClient +private import codeql.ruby.frameworks.http_clients.Httparty +private import codeql.ruby.frameworks.http_clients.HttpClient +private import codeql.ruby.frameworks.http_clients.OpenURI +private import codeql.ruby.frameworks.http_clients.Typhoeus diff --git a/ruby/ql/lib/codeql/ruby/frameworks/StandardLibrary.qll b/ruby/ql/lib/codeql/ruby/frameworks/StandardLibrary.qll new file mode 100644 index 00000000000..ed2222ff484 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/StandardLibrary.qll @@ -0,0 +1,337 @@ +private import codeql.ruby.AST +private import codeql.ruby.Concepts +private import codeql.ruby.DataFlow +private import codeql.ruby.ApiGraphs + +/** + * The `Kernel` module is included by the `Object` class, so its methods are available + * in every Ruby object. In addition, its module methods can be called by + * providing a specific receiver as in `Kernel.exit`. + */ +class KernelMethodCall extends DataFlow::CallNode { + private MethodCall methodCall; + + KernelMethodCall() { + methodCall = this.asExpr().getExpr() and + ( + this = API::getTopLevelMember("Kernel").getAMethodCall(_) + or + methodCall instanceof UnknownMethodCall and + ( + this.getReceiver().asExpr().getExpr() instanceof Self and + isPrivateKernelMethod(methodCall.getMethodName()) + or + isPublicKernelMethod(methodCall.getMethodName()) + ) + ) + } + + string getMethodName() { result = methodCall.getMethodName() } + + int getNumberOfArguments() { result = methodCall.getNumberOfArguments() } +} + +/** + * Public methods in the `Kernel` module. These can be invoked on any object via the usual dot syntax. + * ```ruby + * arr = [] + * arr.send("push", 5) # => [5] + * ``` + */ +private predicate isPublicKernelMethod(string method) { + method in ["class", "clone", "frozen?", "tap", "then", "yield_self", "send"] +} + +/** + * Private methods in the `Kernel` module. + * These can be be invoked on `self`, on `Kernel`, or using a low-level primitive like `send` or `instance_eval`. + * ```ruby + * puts "hello world" + * Kernel.puts "hello world" + * 5.instance_eval { puts "hello world" } + * 5.send("puts", "hello world") + * ``` + */ +private predicate isPrivateKernelMethod(string method) { + method in [ + "Array", "Complex", "Float", "Hash", "Integer", "Rational", "String", "__callee__", "__dir__", + "__method__", "`", "abort", "at_exit", "autoload", "autoload?", "binding", "block_given?", + "callcc", "caller", "caller_locations", "catch", "chomp", "chop", "eval", "exec", "exit", + "exit!", "fail", "fork", "format", "gets", "global_variables", "gsub", "iterator?", "lambda", + "load", "local_variables", "loop", "open", "p", "pp", "print", "printf", "proc", "putc", + "puts", "raise", "rand", "readline", "readlines", "require", "require_relative", "select", + "set_trace_func", "sleep", "spawn", "sprintf", "srand", "sub", "syscall", "system", "test", + "throw", "trace_var", "trap", "untrace_var", "warn" + ] +} + +string basicObjectInstanceMethodName() { + result in [ + "equal?", "instance_eval", "instance_exec", "method_missing", "singleton_method_added", + "singleton_method_removed", "singleton_method_undefined" + ] +} + +/** + * Instance methods on `BasicObject`, which are available to all classes. + */ +class BasicObjectInstanceMethodCall extends UnknownMethodCall { + BasicObjectInstanceMethodCall() { this.getMethodName() = basicObjectInstanceMethodName() } +} + +string objectInstanceMethodName() { + result in [ + "!~", "<=>", "===", "=~", "callable_methods", "define_singleton_method", "display", + "do_until", "do_while", "dup", "enum_for", "eql?", "extend", "f", "freeze", "h", "hash", + "inspect", "instance_of?", "instance_variable_defined?", "instance_variable_get", + "instance_variable_set", "instance_variables", "is_a?", "itself", "kind_of?", + "matching_methods", "method", "method_missing", "methods", "nil?", "object_id", + "private_methods", "protected_methods", "public_method", "public_methods", "public_send", + "remove_instance_variable", "respond_to?", "respond_to_missing?", "send", + "shortest_abbreviation", "singleton_class", "singleton_method", "singleton_methods", "taint", + "tainted?", "to_enum", "to_s", "trust", "untaint", "untrust", "untrusted?" + ] +} + +/** + * Instance methods on `Object`, which are available to all classes except `BasicObject`. + */ +class ObjectInstanceMethodCall extends UnknownMethodCall { + ObjectInstanceMethodCall() { this.getMethodName() = objectInstanceMethodName() } +} + +/** + * Method calls which have no known target. + * These will typically be calls to methods inherited from a superclass. + */ +class UnknownMethodCall extends MethodCall { + UnknownMethodCall() { not exists(this.(Call).getATarget()) } +} + +/** + * A system command executed via subshell literal syntax. + * E.g. + * ```ruby + * `cat foo.txt` + * %x(cat foo.txt) + * %x[cat foo.txt] + * %x{cat foo.txt} + * %x/cat foo.txt/ + * ``` + */ +class SubshellLiteralExecution extends SystemCommandExecution::Range { + SubshellLiteral literal; + + SubshellLiteralExecution() { this.asExpr().getExpr() = literal } + + override DataFlow::Node getAnArgument() { result.asExpr().getExpr() = literal.getComponent(_) } + + override predicate isShellInterpreted(DataFlow::Node arg) { arg = this.getAnArgument() } +} + +/** + * A system command executed via shell heredoc syntax. + * E.g. + * ```ruby + * <<`EOF` + * cat foo.text + * EOF + * ``` + */ +class SubshellHeredocExecution extends SystemCommandExecution::Range { + HereDoc heredoc; + + SubshellHeredocExecution() { this.asExpr().getExpr() = heredoc and heredoc.isSubShell() } + + override DataFlow::Node getAnArgument() { result.asExpr().getExpr() = heredoc.getComponent(_) } + + override predicate isShellInterpreted(DataFlow::Node arg) { arg = this.getAnArgument() } +} + +/** + * A system command executed via the `Kernel.system` method. + * `Kernel.system` accepts three argument forms: + * - A single string. If it contains no shell meta characters, keywords or + * builtins, it is executed directly in a subprocess. + * Otherwise, it is executed in a subshell. + * ```ruby + * system("cat foo.txt | tail") + * ``` + * - A command and one or more arguments. + * The command is executed in a subprocess. + * ```ruby + * system("cat", "foo.txt") + * ``` + * - An array containing the command name and argv[0], followed by zero or more arguments. + * The command is executed in a subprocess. + * ```ruby + * system(["cat", "cat"], "foo.txt") + * ``` + * In addition, `Kernel.system` accepts an optional environment hash as the + * first argument and an optional options hash as the last argument. + * We don't yet distinguish between these arguments and the command arguments. + * ```ruby + * system({"FOO" => "BAR"}, "cat foo.txt | tail", {unsetenv_others: true}) + * ``` + * Ruby documentation: https://docs.ruby-lang.org/en/3.0.0/Kernel.html#method-i-system + */ +class KernelSystemCall extends SystemCommandExecution::Range, KernelMethodCall { + KernelSystemCall() { this.getMethodName() = "system" } + + override DataFlow::Node getAnArgument() { result = this.getArgument(_) } + + override predicate isShellInterpreted(DataFlow::Node arg) { + // Kernel.system invokes a subshell if you provide a single string as argument + this.getNumberOfArguments() = 1 and arg = this.getAnArgument() + } +} + +/** + * A system command executed via the `Kernel.exec` method. + * `Kernel.exec` takes the same argument forms as `Kernel.system`. See `KernelSystemCall` for details. + * Ruby documentation: https://docs.ruby-lang.org/en/3.0.0/Kernel.html#method-i-exec + */ +class KernelExecCall extends SystemCommandExecution::Range, KernelMethodCall { + KernelExecCall() { this.getMethodName() = "exec" } + + override DataFlow::Node getAnArgument() { result = this.getArgument(_) } + + override predicate isShellInterpreted(DataFlow::Node arg) { + // Kernel.exec invokes a subshell if you provide a single string as argument + this.getNumberOfArguments() = 1 and arg = this.getAnArgument() + } +} + +/** + * A system command executed via the `Kernel.spawn` method. + * `Kernel.spawn` takes the same argument forms as `Kernel.system`. + * See `KernelSystemCall` for details. + * Ruby documentation: https://docs.ruby-lang.org/en/3.0.0/Kernel.html#method-i-spawn + * TODO: document and handle the env and option arguments. + * ``` + * spawn([env,] command... [,options]) -> pid + * ``` + */ +class KernelSpawnCall extends SystemCommandExecution::Range, KernelMethodCall { + KernelSpawnCall() { this.getMethodName() = "spawn" } + + override DataFlow::Node getAnArgument() { result = this.getArgument(_) } + + override predicate isShellInterpreted(DataFlow::Node arg) { + // Kernel.spawn invokes a subshell if you provide a single string as argument + this.getNumberOfArguments() = 1 and arg = this.getAnArgument() + } +} + +/** + * A system command executed via one of the `Open3` methods. + * These methods take the same argument forms as `Kernel.system`. + * See `KernelSystemCall` for details. + */ +class Open3Call extends SystemCommandExecution::Range { + MethodCall methodCall; + + Open3Call() { + this.asExpr().getExpr() = methodCall and + this = + API::getTopLevelMember("Open3") + .getAMethodCall(["popen3", "popen2", "popen2e", "capture3", "capture2", "capture2e"]) + } + + override DataFlow::Node getAnArgument() { result.asExpr().getExpr() = methodCall.getAnArgument() } + + override predicate isShellInterpreted(DataFlow::Node arg) { + // These Open3 methods invoke a subshell if you provide a single string as argument + methodCall.getNumberOfArguments() = 1 and arg.asExpr().getExpr() = methodCall.getAnArgument() + } +} + +/** + * A pipeline of system commands constructed via one of the `Open3` methods. + * These methods accept a variable argument list of commands. + * Commands can be in any form supported by `Kernel.system`. See `KernelSystemCall` for details. + * ```ruby + * Open3.pipeline("cat foo.txt", "tail") + * Open3.pipeline(["cat", "foo.txt"], "tail") + * Open3.pipeline([{}, "cat", "foo.txt"], "tail") + * Open3.pipeline([["cat", "cat"], "foo.txt"], "tail") + */ +class Open3PipelineCall extends SystemCommandExecution::Range { + MethodCall methodCall; + + Open3PipelineCall() { + this.asExpr().getExpr() = methodCall and + this = + API::getTopLevelMember("Open3") + .getAMethodCall(["pipeline_rw", "pipeline_r", "pipeline_w", "pipeline_start", "pipeline"]) + } + + override DataFlow::Node getAnArgument() { result.asExpr().getExpr() = methodCall.getAnArgument() } + + override predicate isShellInterpreted(DataFlow::Node arg) { + // A command in the pipeline is executed in a subshell if it is given as a single string argument. + arg.asExpr().getExpr() instanceof StringlikeLiteral and + arg.asExpr().getExpr() = methodCall.getAnArgument() + } +} + +/** + * A call to `Kernel.eval`, which executes its first argument as Ruby code. + * ```ruby + * a = 1 + * Kernel.eval("a = 2") + * a # => 2 + * ``` + */ +class EvalCallCodeExecution extends CodeExecution::Range, KernelMethodCall { + EvalCallCodeExecution() { this.getMethodName() = "eval" } + + override DataFlow::Node getCode() { result = this.getArgument(0) } +} + +/** + * A call to `Kernel#send`, which executes its first argument as a Ruby method call. + * ```ruby + * arr = [] + * arr.send("push", 1) + * arr # => [1] + * ``` + */ +class SendCallCodeExecution extends CodeExecution::Range, KernelMethodCall { + SendCallCodeExecution() { this.getMethodName() = "send" } + + override DataFlow::Node getCode() { result = this.getArgument(0) } +} + +/** + * A call to `BasicObject#instance_eval`, which executes its first argument as Ruby code. + */ +class InstanceEvalCallCodeExecution extends CodeExecution::Range, DataFlow::CallNode { + InstanceEvalCallCodeExecution() { + this.asExpr().getExpr().(UnknownMethodCall).getMethodName() = "instance_eval" + } + + override DataFlow::Node getCode() { result = this.getArgument(0) } +} + +/** + * A call to `Module#class_eval`, which executes its first argument as Ruby code. + */ +class ClassEvalCallCodeExecution extends CodeExecution::Range, DataFlow::CallNode { + ClassEvalCallCodeExecution() { + this.asExpr().getExpr().(UnknownMethodCall).getMethodName() = "class_eval" + } + + override DataFlow::Node getCode() { result = this.getArgument(0) } +} + +/** + * A call to `Module#module_eval`, which executes its first argument as Ruby code. + */ +class ModuleEvalCallCodeExecution extends CodeExecution::Range, DataFlow::CallNode { + ModuleEvalCallCodeExecution() { + this.asExpr().getExpr().(UnknownMethodCall).getMethodName() = "module_eval" + } + + override DataFlow::Node getCode() { result = this.getArgument(0) } +} diff --git a/ruby/ql/lib/codeql/ruby/frameworks/XmlParsing.qll b/ruby/ql/lib/codeql/ruby/frameworks/XmlParsing.qll new file mode 100644 index 00000000000..00f0e1d7b5d --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/XmlParsing.qll @@ -0,0 +1,182 @@ +private import codeql.ruby.Concepts +private import codeql.ruby.AST +private import codeql.ruby.DataFlow +private import codeql.ruby.typetracking.TypeTracker +private import codeql.ruby.ApiGraphs +private import codeql.ruby.controlflow.CfgNodes as CfgNodes + +private class NokogiriXmlParserCall extends XmlParserCall::Range, DataFlow::CallNode { + NokogiriXmlParserCall() { + this = + [ + API::getTopLevelMember("Nokogiri").getMember("XML"), + API::getTopLevelMember("Nokogiri").getMember("XML").getMember("Document"), + API::getTopLevelMember("Nokogiri") + .getMember("XML") + .getMember("SAX") + .getMember("Parser") + .getInstance() + ].getAMethodCall("parse") + } + + override DataFlow::Node getInput() { result = this.getArgument(0) } + + override predicate externalEntitiesEnabled() { + this.getArgument(3) = + [trackEnableFeature(TNOENT()), trackEnableFeature(TDTDLOAD()), trackDisableFeature(TNONET())] + or + // calls to methods that enable/disable features in a block argument passed to this parser call. + // For example: + // ```ruby + // doc.parse(...) { |options| options.nononet; options.noent } + // ``` + this.asExpr() + .getExpr() + .(MethodCall) + .getBlock() + .getAStmt() + .getAChild*() + .(MethodCall) + .getMethodName() = ["noent", "dtdload", "nononet"] + } +} + +private class LibXmlRubyXmlParserCall extends XmlParserCall::Range, DataFlow::CallNode { + LibXmlRubyXmlParserCall() { + this = + [API::getTopLevelMember("LibXML").getMember("XML"), API::getTopLevelMember("XML")] + .getMember(["Document", "Parser"]) + .getAMethodCall(["file", "io", "string"]) + } + + override DataFlow::Node getInput() { result = this.getArgument(0) } + + override predicate externalEntitiesEnabled() { + exists(Pair pair | + pair = this.getArgument(1).asExpr().getExpr().(HashLiteral).getAKeyValuePair() and + pair.getKey().(Literal).getValueText() = "options" and + pair.getValue() = + [ + trackEnableFeature(TNOENT()), trackEnableFeature(TDTDLOAD()), + trackDisableFeature(TNONET()) + ].asExpr().getExpr() + ) + } +} + +private newtype TFeature = + TNOENT() or + TNONET() or + TDTDLOAD() + +class Feature extends TFeature { + abstract int getValue(); + + string toString() { result = this.getConstantName() } + + abstract string getConstantName(); +} + +private class FeatureNOENT extends Feature, TNOENT { + override int getValue() { result = 2 } + + override string getConstantName() { result = "NOENT" } +} + +private class FeatureNONET extends Feature, TNONET { + override int getValue() { result = 2048 } + + override string getConstantName() { result = "NONET" } +} + +private class FeatureDTDLOAD extends Feature, TDTDLOAD { + override int getValue() { result = 4 } + + override string getConstantName() { result = "DTDLOAD" } +} + +private API::Node parseOptionsModule() { + result = API::getTopLevelMember("Nokogiri").getMember("XML").getMember("ParseOptions") + or + result = + API::getTopLevelMember("LibXML").getMember("XML").getMember("Parser").getMember("Options") + or + result = API::getTopLevelMember("XML").getMember("Parser").getMember("Options") +} + +private predicate bitWiseAndOr(CfgNodes::ExprNodes::OperationCfgNode operation) { + operation.getExpr() instanceof BitwiseAndExpr or + operation.getExpr() instanceof AssignBitwiseAndExpr or + operation.getExpr() instanceof BitwiseOrExpr or + operation.getExpr() instanceof AssignBitwiseOrExpr +} + +private DataFlow::LocalSourceNode trackFeature(Feature f, boolean enable, TypeTracker t) { + t.start() and + ( + // An integer literal with the feature-bit enabled/disabled + exists(int bitValue | + bitValue = result.asExpr().getExpr().(IntegerLiteral).getValue().bitAnd(f.getValue()) + | + if bitValue = 0 then enable = false else enable = true + ) + or + // Use of a constant f + enable = true and + result = parseOptionsModule().getMember(f.getConstantName()).getAUse() + or + // Treat `&`, `&=`, `|` and `|=` operators as if they preserve the on/off states + // of their operands. This is an overapproximation but likely to work well in practice + // because it makes little sense to explicitly set a feature to both `on` and `off` in the + // same code. + exists(CfgNodes::ExprNodes::OperationCfgNode operation | + bitWiseAndOr(operation) and + operation = result.asExpr().(CfgNodes::ExprNodes::OperationCfgNode) and + operation.getAnOperand() = trackFeature(f, enable).asExpr() + ) + or + // The complement operator toggles a feature from enabled to disabled and vice-versa + result.asExpr().getExpr() instanceof ComplementExpr and + result.asExpr().(CfgNodes::ExprNodes::OperationCfgNode).getAnOperand() = + trackFeature(f, enable.booleanNot()).asExpr() + or + // Nokogiri has a ParseOptions class that is a wrapper around the bit-fields and + // provides methods for querying and updating the fields. + result = + API::getTopLevelMember("Nokogiri") + .getMember("XML") + .getMember("ParseOptions") + .getAnInstantiation() and + result.asExpr().(CfgNodes::ExprNodes::CallCfgNode).getArgument(0) = + trackFeature(f, enable).asExpr() + or + // The Nokogiri ParseOptions class has methods for setting/unsetting features. + // The method names are the lowercase variants of the constant names, with a "no" + // prefix for unsetting a feature. + exists(CfgNodes::ExprNodes::CallCfgNode call | + enable = true and + call.getExpr().(MethodCall).getMethodName() = f.getConstantName().toLowerCase() + or + enable = false and + call.getExpr().(MethodCall).getMethodName() = "no" + f.getConstantName().toLowerCase() + | + ( + // these methods update the receiver + result.flowsTo(any(DataFlow::Node n | n.asExpr() = call.getReceiver())) + or + // in addition they return the (updated) receiver to allow chaining calls. + result.asExpr() = call + ) + ) + ) + or + exists(TypeTracker t2 | result = trackFeature(f, enable, t2).track(t2, t)) +} + +private DataFlow::Node trackFeature(Feature f, boolean enable) { + trackFeature(f, enable, TypeTracker::end()).flowsTo(result) +} + +private DataFlow::Node trackEnableFeature(Feature f) { result = trackFeature(f, true) } + +private DataFlow::Node trackDisableFeature(Feature f) { result = trackFeature(f, false) } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Excon.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Excon.qll new file mode 100644 index 00000000000..efb9d7be66c --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Excon.qll @@ -0,0 +1,130 @@ +private import ruby +private import codeql.ruby.Concepts +private import codeql.ruby.ApiGraphs + +/** + * A call that makes an HTTP request using `Excon`. + * ```ruby + * # one-off request + * Excon.get("http://example.com").body + * + * # connection re-use + * connection = Excon.new("http://example.com") + * connection.get(path: "/").body + * connection.request(method: :get, path: "/") + * ``` + * + * TODO: pipelining, streaming responses + * https://github.com/excon/excon/blob/master/README.md + */ +class ExconHttpRequest extends HTTP::Client::Request::Range { + DataFlow::Node requestUse; + API::Node requestNode; + API::Node connectionNode; + + ExconHttpRequest() { + requestUse = requestNode.getAnImmediateUse() and + connectionNode = + [ + // one-off requests + API::getTopLevelMember("Excon"), + // connection re-use + API::getTopLevelMember("Excon").getInstance(), + API::getTopLevelMember("Excon").getMember("Connection").getInstance() + ] and + requestNode = + connectionNode + .getReturn([ + // Excon#request exists but Excon.request doesn't. + // This shouldn't be a problem - in real code the latter would raise NoMethodError anyway. + "get", "head", "delete", "options", "post", "put", "patch", "trace", "request" + ]) and + this = requestUse.asExpr().getExpr() + } + + override DataFlow::Node getResponseBody() { result = requestNode.getAMethodCall("body") } + + override predicate disablesCertificateValidation(DataFlow::Node disablingNode) { + // Check for `ssl_verify_peer: false` in the options hash. + exists(DataFlow::Node arg, int i | + i > 0 and arg = connectionNode.getAUse().(DataFlow::CallNode).getArgument(i) + | + argSetsVerifyPeer(arg, false, disablingNode) + ) + or + // Or we see a call to `Excon.defaults[:ssl_verify_peer] = false` before the + // request, and no `ssl_verify_peer: true` in the explicit options hash for + // the request call. + exists(DataFlow::CallNode disableCall | + setsDefaultVerification(disableCall, false) and + disableCall.asExpr().getASuccessor+() = requestUse.asExpr() and + disablingNode = disableCall and + not exists(DataFlow::Node arg, int i | + i > 0 and arg = connectionNode.getAUse().(DataFlow::CallNode).getArgument(i) + | + argSetsVerifyPeer(arg, true, _) + ) + ) + } + + override string getFramework() { result = "Excon" } +} + +/** + * Holds if `arg` represents an options hash that contains the key + * `:ssl_verify_peer` with `value`, where `kvNode` is the data-flow node for + * this key-value pair. + */ +predicate argSetsVerifyPeer(DataFlow::Node arg, boolean value, DataFlow::Node kvNode) { + // Either passed as an individual key:value argument, e.g.: + // Excon.get(..., ssl_verify_peer: false) + isSslVerifyPeerPair(arg.asExpr().getExpr(), value) and + kvNode = arg + or + // Or as a single hash argument, e.g.: + // Excon.get(..., { ssl_verify_peer: false, ... }) + exists(DataFlow::LocalSourceNode optionsNode, Pair p | + p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and + isSslVerifyPeerPair(p, value) and + optionsNode.flowsTo(arg) and + kvNode.asExpr().getExpr() = p + ) +} + +/** + * Holds if `callNode` sets `Excon.defaults[:ssl_verify_peer]` or + * `Excon.ssl_verify_peer` to `value`. + */ +private predicate setsDefaultVerification(DataFlow::CallNode callNode, boolean value) { + callNode = API::getTopLevelMember("Excon").getReturn("defaults").getAMethodCall("[]=") and + isSslVerifyPeerLiteral(callNode.getArgument(0)) and + hasBooleanValue(callNode.getArgument(1), value) + or + callNode = API::getTopLevelMember("Excon").getAMethodCall("ssl_verify_peer=") and + hasBooleanValue(callNode.getArgument(0), value) +} + +private predicate isSslVerifyPeerLiteral(DataFlow::Node node) { + exists(DataFlow::LocalSourceNode literal | + literal.asExpr().getExpr().(SymbolLiteral).getValueText() = "ssl_verify_peer" and + literal.flowsTo(node) + ) +} + +/** Holds if `node` can contain `value`. */ +private predicate hasBooleanValue(DataFlow::Node node, boolean value) { + exists(DataFlow::LocalSourceNode literal | + literal.asExpr().getExpr().(BooleanLiteral).getValue() = value and + literal.flowsTo(node) + ) +} + +/** Holds if `p` is the pair `ssl_verify_peer: <value>`. */ +private predicate isSslVerifyPeerPair(Pair p, boolean value) { + exists(DataFlow::Node key, DataFlow::Node valueNode | + key.asExpr().getExpr() = p.getKey() and valueNode.asExpr().getExpr() = p.getValue() + | + isSslVerifyPeerLiteral(key) and + hasBooleanValue(valueNode, value) + ) +} diff --git a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Faraday.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Faraday.qll new file mode 100644 index 00000000000..de3f6f5f811 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Faraday.qll @@ -0,0 +1,140 @@ +private import ruby +private import codeql.ruby.Concepts +private import codeql.ruby.ApiGraphs + +/** + * A call that makes an HTTP request using `Faraday`. + * ```ruby + * # one-off request + * Faraday.get("http://example.com").body + * + * # connection re-use + * connection = Faraday.new("http://example.com") + * connection.get("/").body + * ``` + */ +class FaradayHttpRequest extends HTTP::Client::Request::Range { + DataFlow::Node requestUse; + API::Node requestNode; + API::Node connectionNode; + + FaradayHttpRequest() { + connectionNode = + [ + // one-off requests + API::getTopLevelMember("Faraday"), + // connection re-use + API::getTopLevelMember("Faraday").getInstance() + ] and + requestNode = + connectionNode.getReturn(["get", "head", "delete", "post", "put", "patch", "trace"]) and + requestUse = requestNode.getAnImmediateUse() and + this = requestUse.asExpr().getExpr() + } + + override DataFlow::Node getResponseBody() { result = requestNode.getAMethodCall("body") } + + override predicate disablesCertificateValidation(DataFlow::Node disablingNode) { + // `Faraday::new` takes an options hash as its second argument, and we're + // looking for + // `{ ssl: { verify: false } }` + // or + // `{ ssl: { verify_mode: OpenSSL::SSL::VERIFY_NONE } }` + exists(DataFlow::Node arg, int i | + i > 0 and arg = connectionNode.getAUse().(DataFlow::CallNode).getArgument(i) + | + // Either passed as an individual key:value argument, e.g.: + // Faraday.new(..., ssl: {...}) + isSslOptionsPairDisablingValidation(arg.asExpr().getExpr()) and + disablingNode = arg + or + // Or as a single hash argument, e.g.: + // Faraday.new(..., { ssl: {...} }) + exists(DataFlow::LocalSourceNode optionsNode, Pair p | + p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and + isSslOptionsPairDisablingValidation(p) and + optionsNode.flowsTo(arg) and + disablingNode.asExpr().getExpr() = p + ) + ) + } + + override string getFramework() { result = "Faraday" } +} + +/** + * Holds if the pair `p` contains the key `:ssl` for which the value is a hash + * containing either `verify: false` or + * `verify_mode: OpenSSL::SSL::VERIFY_NONE`. + */ +private predicate isSslOptionsPairDisablingValidation(Pair p) { + exists(DataFlow::Node key, DataFlow::Node value | + key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue() + | + isSymbolLiteral(key, "ssl") and + (isHashWithVerifyFalse(value) or isHashWithVerifyModeNone(value)) + ) +} + +/** Holds if `node` represents the symbol literal with the given `valueText`. */ +private predicate isSymbolLiteral(DataFlow::Node node, string valueText) { + exists(DataFlow::LocalSourceNode literal | + literal.asExpr().getExpr().(SymbolLiteral).getValueText() = valueText and + literal.flowsTo(node) + ) +} + +/** + * Holds if `node` represents a hash containing the key-value pair + * `verify: false`. + */ +private predicate isHashWithVerifyFalse(DataFlow::Node node) { + exists(DataFlow::LocalSourceNode hash | + isVerifyFalsePair(hash.asExpr().getExpr().(HashLiteral).getAKeyValuePair()) and + hash.flowsTo(node) + ) +} + +/** + * Holds if `node` represents a hash containing the key-value pair + * `verify_mode: OpenSSL::SSL::VERIFY_NONE`. + */ +private predicate isHashWithVerifyModeNone(DataFlow::Node node) { + exists(DataFlow::LocalSourceNode hash | + isVerifyModeNonePair(hash.asExpr().getExpr().(HashLiteral).getAKeyValuePair()) and + hash.flowsTo(node) + ) +} + +/** + * Holds if the pair `p` has the key `:verify_mode` and the value + * `OpenSSL::SSL::VERIFY_NONE`. + */ +private predicate isVerifyModeNonePair(Pair p) { + exists(DataFlow::Node key, DataFlow::Node value | + key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue() + | + isSymbolLiteral(key, "verify_mode") and + value = API::getTopLevelMember("OpenSSL").getMember("SSL").getMember("VERIFY_NONE").getAUse() + ) +} + +/** + * Holds if the pair `p` has the key `:verify` and the value `false`. + */ +private predicate isVerifyFalsePair(Pair p) { + exists(DataFlow::Node key, DataFlow::Node value | + key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue() + | + isSymbolLiteral(key, "verify") and + isFalse(value) + ) +} + +/** Holds if `node` can contain the Boolean value `false`. */ +private predicate isFalse(DataFlow::Node node) { + exists(DataFlow::LocalSourceNode literal | + literal.asExpr().getExpr().(BooleanLiteral).isFalse() and + literal.flowsTo(node) + ) +} diff --git a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/HttpClient.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/HttpClient.qll new file mode 100644 index 00000000000..3db9c653a5c --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/HttpClient.qll @@ -0,0 +1,55 @@ +private import ruby +private import codeql.ruby.Concepts +private import codeql.ruby.ApiGraphs + +/** + * A call that makes an HTTP request using `HTTPClient`. + * ```ruby + * HTTPClient.get("http://example.com").body + * HTTPClient.get_content("http://example.com") + * ``` + */ +class HttpClientRequest extends HTTP::Client::Request::Range { + API::Node requestNode; + API::Node connectionNode; + DataFlow::Node requestUse; + string method; + + HttpClientRequest() { + connectionNode = + [ + // One-off requests + API::getTopLevelMember("HTTPClient"), + // Conncection re-use + API::getTopLevelMember("HTTPClient").getInstance() + ] and + requestNode = connectionNode.getReturn(method) and + requestUse = requestNode.getAnImmediateUse() and + method in [ + "get", "head", "delete", "options", "post", "put", "trace", "get_content", "post_content" + ] and + this = requestUse.asExpr().getExpr() + } + + override DataFlow::Node getResponseBody() { + // The `get_content` and `post_content` methods return the response body as + // a string. The other methods return a `HTTPClient::Message` object which + // has various methods that return the response body. + method in ["get_content", "post_content"] and result = requestUse + or + not method in ["get_content", "put_content"] and + result = requestNode.getAMethodCall(["body", "http_body", "content", "dump"]) + } + + override predicate disablesCertificateValidation(DataFlow::Node disablingNode) { + // Look for calls to set + // `c.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE` + // on an HTTPClient connection object `c`. + disablingNode = + connectionNode.getReturn("ssl_config").getReturn("verify_mode=").getAnImmediateUse() and + disablingNode.(DataFlow::CallNode).getArgument(0) = + API::getTopLevelMember("OpenSSL").getMember("SSL").getMember("VERIFY_NONE").getAUse() + } + + override string getFramework() { result = "HTTPClient" } +} diff --git a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Httparty.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Httparty.qll new file mode 100644 index 00000000000..b1746692bf7 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Httparty.qll @@ -0,0 +1,95 @@ +private import ruby +private import codeql.ruby.Concepts +private import codeql.ruby.ApiGraphs + +/** + * A call that makes an HTTP request using `HTTParty`. + * ```ruby + * # one-off request - returns the response body + * HTTParty.get("http://example.com") + * + * # TODO: module inclusion + * class MyClass + * include HTTParty + * end + * + * MyClass.new("http://example.com") + * ``` + */ +class HttpartyRequest extends HTTP::Client::Request::Range { + API::Node requestNode; + DataFlow::Node requestUse; + + HttpartyRequest() { + requestUse = requestNode.getAnImmediateUse() and + requestNode = + API::getTopLevelMember("HTTParty") + .getReturn(["get", "head", "delete", "options", "post", "put", "patch"]) and + this = requestUse.asExpr().getExpr() + } + + override DataFlow::Node getResponseBody() { + // If HTTParty can recognise the response type, it will parse and return it + // directly from the request call. Otherwise, it will return a `HTTParty::Response` + // object that has a `#body` method. + // So if there's a call to `#body` on the response, treat that as the response body. + exists(DataFlow::Node r | r = requestNode.getAMethodCall("body") | result = r) + or + // Otherwise, treat the response as the response body. + not exists(DataFlow::Node r | r = requestNode.getAMethodCall("body")) and + result = requestUse + } + + override predicate disablesCertificateValidation(DataFlow::Node disablingNode) { + // The various request methods take an options hash as their second + // argument, and we're looking for `{ verify: false }` or + // `{ verify_peer: false }`. + exists(DataFlow::Node arg, int i | + i > 0 and arg.asExpr().getExpr() = requestUse.asExpr().getExpr().(MethodCall).getArgument(i) + | + // Either passed as an individual key:value argument, e.g.: + // HTTParty.get(..., verify: false) + isVerifyFalsePair(arg.asExpr().getExpr()) and + disablingNode = arg + or + // Or as a single hash argument, e.g.: + // HTTParty.get(..., { verify: false, ... }) + exists(DataFlow::LocalSourceNode optionsNode, Pair p | + p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and + isVerifyFalsePair(p) and + optionsNode.flowsTo(arg) and + disablingNode.asExpr().getExpr() = p + ) + ) + } + + override string getFramework() { result = "HTTParty" } +} + +/** Holds if `node` represents the symbol literal `verify` or `verify_peer`. */ +private predicate isVerifyLiteral(DataFlow::Node node) { + exists(DataFlow::LocalSourceNode literal | + literal.asExpr().getExpr().(SymbolLiteral).getValueText() = ["verify", "verify_peer"] and + literal.flowsTo(node) + ) +} + +/** Holds if `node` can contain the Boolean value `false`. */ +private predicate isFalse(DataFlow::Node node) { + exists(DataFlow::LocalSourceNode literal | + literal.asExpr().getExpr().(BooleanLiteral).isFalse() and + literal.flowsTo(node) + ) +} + +/** + * Holds if `p` is the pair `verify: false` or `verify_peer: false`. + */ +private predicate isVerifyFalsePair(Pair p) { + exists(DataFlow::Node key, DataFlow::Node value | + key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue() + | + isVerifyLiteral(key) and + isFalse(value) + ) +} diff --git a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/NetHttp.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/NetHttp.qll new file mode 100644 index 00000000000..9d9c6f7aff3 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/NetHttp.qll @@ -0,0 +1,69 @@ +private import codeql.ruby.AST +private import codeql.ruby.Concepts +private import codeql.ruby.dataflow.RemoteFlowSources +private import codeql.ruby.ApiGraphs +private import codeql.ruby.dataflow.internal.DataFlowPublic + +/** + * A `Net::HTTP` call which initiates an HTTP request. + * ```ruby + * Net::HTTP.get("http://example.com/") + * Net::HTTP.post("http://example.com/", "some_data") + * req = Net::HTTP.new("example.com") + * response = req.get("/") + * ``` + */ +class NetHttpRequest extends HTTP::Client::Request::Range { + private DataFlow::CallNode request; + private DataFlow::Node responseBody; + + NetHttpRequest() { + exists(API::Node requestNode, string method | + request = requestNode.getAnImmediateUse() and + this = request.asExpr().getExpr() + | + // Net::HTTP.get(...) + method = "get" and + requestNode = API::getTopLevelMember("Net").getMember("HTTP").getReturn(method) and + responseBody = request + or + // Net::HTTP.post(...).body + method in ["post", "post_form"] and + requestNode = API::getTopLevelMember("Net").getMember("HTTP").getReturn(method) and + responseBody = requestNode.getAMethodCall(["body", "read_body", "entity"]) + or + // Net::HTTP.new(..).get(..).body + method in [ + "get", "get2", "request_get", "head", "head2", "request_head", "delete", "put", "patch", + "post", "post2", "request_post", "request" + ] and + requestNode = API::getTopLevelMember("Net").getMember("HTTP").getInstance().getReturn(method) and + responseBody = requestNode.getAMethodCall(["body", "read_body", "entity"]) + ) + } + + /** + * Gets the node representing the URL of the request. + * Currently unused, but may be useful in future, e.g. to filter out certain requests. + */ + DataFlow::Node getURLArgument() { result = request.getArgument(0) } + + override DataFlow::Node getResponseBody() { result = responseBody } + + override predicate disablesCertificateValidation(DataFlow::Node disablingNode) { + // A Net::HTTP request bypasses certificate validation if we see a setter + // call like this: + // foo.verify_mode = OpenSSL::SSL::VERIFY_NONE + // and then the receiver of that call flows to the receiver in the request: + // foo.request(...) + exists(DataFlow::CallNode setter | + disablingNode = + API::getTopLevelMember("OpenSSL").getMember("SSL").getMember("VERIFY_NONE").getAUse() and + setter.asExpr().getExpr().(SetterMethodCall).getMethodName() = "verify_mode=" and + disablingNode = setter.getArgument(0) and + localFlow(setter.getReceiver(), request.getReceiver()) + ) + } + + override string getFramework() { result = "Net::HTTP" } +} diff --git a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/OpenURI.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/OpenURI.qll new file mode 100644 index 00000000000..54a2c180fec --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/OpenURI.qll @@ -0,0 +1,113 @@ +private import ruby +private import codeql.ruby.Concepts +private import codeql.ruby.ApiGraphs +private import codeql.ruby.frameworks.StandardLibrary + +/** + * A call that makes an HTTP request using `OpenURI` via `URI.open` or + * `URI.parse(...).open`. + * + * ```ruby + * URI.open("http://example.com").readlines + * URI.parse("http://example.com").open.read + * ``` + */ +class OpenUriRequest extends HTTP::Client::Request::Range { + API::Node requestNode; + DataFlow::Node requestUse; + + OpenUriRequest() { + requestNode = + [API::getTopLevelMember("URI"), API::getTopLevelMember("URI").getReturn("parse")] + .getReturn("open") and + requestUse = requestNode.getAnImmediateUse() and + this = requestUse.asExpr().getExpr() + } + + override DataFlow::Node getResponseBody() { + result = requestNode.getAMethodCall(["read", "readlines"]) + } + + override predicate disablesCertificateValidation(DataFlow::Node disablingNode) { + exists(DataFlow::Node arg | + arg.asExpr().getExpr() = requestUse.asExpr().getExpr().(MethodCall).getArgument(_) + | + argumentDisablesValidation(arg, disablingNode) + ) + } + + override string getFramework() { result = "OpenURI" } +} + +/** + * A call that makes an HTTP request using `OpenURI` and its `Kernel.open` + * interface. + * + * ```ruby + * Kernel.open("http://example.com").read + * ``` + */ +class OpenUriKernelOpenRequest extends HTTP::Client::Request::Range { + DataFlow::Node requestUse; + + OpenUriKernelOpenRequest() { + requestUse instanceof KernelMethodCall and + this.getMethodName() = "open" and + this = requestUse.asExpr().getExpr() + } + + override DataFlow::CallNode getResponseBody() { + result.asExpr().getExpr().(MethodCall).getMethodName() in ["read", "readlines"] and + requestUse.(DataFlow::LocalSourceNode).flowsTo(result.getReceiver()) + } + + override predicate disablesCertificateValidation(DataFlow::Node disablingNode) { + exists(DataFlow::Node arg, int i | + i > 0 and + arg.asExpr().getExpr() = requestUse.asExpr().getExpr().(MethodCall).getArgument(i) + | + argumentDisablesValidation(arg, disablingNode) + ) + } + + override string getFramework() { result = "OpenURI" } +} + +/** + * Holds if the argument `arg` is an options hash that disables certificate + * validation, and `disablingNode` is the specific node representing the + * `ssl_verify_mode: OpenSSL::SSL_VERIFY_NONE` pair. + */ +private predicate argumentDisablesValidation(DataFlow::Node arg, DataFlow::Node disablingNode) { + // Either passed as an individual key:value argument, e.g.: + // URI.open(..., ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE) + isSslVerifyModeNonePair(arg.asExpr().getExpr()) and + disablingNode = arg + or + // Or as a single hash argument, e.g.: + // URI.open(..., { ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE, ... }) + exists(DataFlow::LocalSourceNode optionsNode, Pair p | + p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and + isSslVerifyModeNonePair(p) and + optionsNode.flowsTo(arg) and + disablingNode.asExpr().getExpr() = p + ) +} + +/** Holds if `p` is the pair `ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE`. */ +private predicate isSslVerifyModeNonePair(Pair p) { + exists(DataFlow::Node key, DataFlow::Node value | + key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue() + | + isSslVerifyModeLiteral(key) and + value = API::getTopLevelMember("OpenSSL").getMember("SSL").getMember("VERIFY_NONE").getAUse() + ) +} + +/** Holds if `node` can represent the symbol literal `:ssl_verify_mode`. */ +private predicate isSslVerifyModeLiteral(DataFlow::Node node) { + exists(DataFlow::LocalSourceNode literal | + literal.asExpr().getExpr().(SymbolLiteral).getValueText() = "ssl_verify_mode" and + literal.flowsTo(node) + ) +} diff --git a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/RestClient.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/RestClient.qll new file mode 100644 index 00000000000..3b6ff318b66 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/RestClient.qll @@ -0,0 +1,71 @@ +private import ruby +private import codeql.ruby.Concepts +private import codeql.ruby.ApiGraphs + +/** + * A call that makes an HTTP request using `RestClient`. + * ```ruby + * RestClient.get("http://example.com").body + * ``` + */ +class RestClientHttpRequest extends HTTP::Client::Request::Range { + DataFlow::Node requestUse; + API::Node requestNode; + API::Node connectionNode; + + RestClientHttpRequest() { + connectionNode = + [ + API::getTopLevelMember("RestClient"), + API::getTopLevelMember("RestClient").getMember("Resource").getInstance() + ] and + requestNode = + connectionNode.getReturn(["get", "head", "delete", "options", "post", "put", "patch"]) and + requestUse = requestNode.getAnImmediateUse() and + this = requestUse.asExpr().getExpr() + } + + override DataFlow::Node getResponseBody() { result = requestNode.getAMethodCall("body") } + + override predicate disablesCertificateValidation(DataFlow::Node disablingNode) { + // `RestClient::Resource::new` takes an options hash argument, and we're + // looking for `{ verify_ssl: OpenSSL::SSL::VERIFY_NONE }`. + exists(DataFlow::Node arg, int i | + i > 0 and arg = connectionNode.getAUse().(DataFlow::CallNode).getArgument(i) + | + // Either passed as an individual key:value argument, e.g.: + // RestClient::Resource.new(..., verify_ssl: OpenSSL::SSL::VERIFY_NONE) + isVerifySslNonePair(arg.asExpr().getExpr()) and + disablingNode = arg + or + // Or as a single hash argument, e.g.: + // RestClient::Resource.new(..., { verify_ssl: OpenSSL::SSL::VERIFY_NONE }) + exists(DataFlow::LocalSourceNode optionsNode, Pair p | + p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and + isVerifySslNonePair(p) and + optionsNode.flowsTo(arg) and + disablingNode.asExpr().getExpr() = p + ) + ) + } + + override string getFramework() { result = "RestClient" } +} + +/** Holds if `p` is the pair `verify_ssl: OpenSSL::SSL::VERIFY_NONE`. */ +private predicate isVerifySslNonePair(Pair p) { + exists(DataFlow::Node key, DataFlow::Node value | + key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue() + | + isSslVerifyModeLiteral(key) and + value = API::getTopLevelMember("OpenSSL").getMember("SSL").getMember("VERIFY_NONE").getAUse() + ) +} + +/** Holds if `node` can represent the symbol literal `:verify_ssl`. */ +private predicate isSslVerifyModeLiteral(DataFlow::Node node) { + exists(DataFlow::LocalSourceNode literal | + literal.asExpr().getExpr().(SymbolLiteral).getValueText() = "verify_ssl" and + literal.flowsTo(node) + ) +} diff --git a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Typhoeus.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Typhoeus.qll new file mode 100644 index 00000000000..38fa5288079 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Typhoeus.qll @@ -0,0 +1,74 @@ +private import ruby +private import codeql.ruby.Concepts +private import codeql.ruby.ApiGraphs + +/** + * A call that makes an HTTP request using `Typhoeus`. + * ```ruby + * Typhoeus.get("http://example.com").body + * ``` + */ +class TyphoeusHttpRequest extends HTTP::Client::Request::Range { + DataFlow::Node requestUse; + API::Node requestNode; + + TyphoeusHttpRequest() { + requestUse = requestNode.getAnImmediateUse() and + requestNode = + API::getTopLevelMember("Typhoeus") + .getReturn(["get", "head", "delete", "options", "post", "put", "patch"]) and + this = requestUse.asExpr().getExpr() + } + + override DataFlow::Node getResponseBody() { result = requestNode.getAMethodCall("body") } + + override predicate disablesCertificateValidation(DataFlow::Node disablingNode) { + // Check for `ssl_verifypeer: false` in the options hash. + exists(DataFlow::Node arg, int i | + i > 0 and arg.asExpr().getExpr() = requestUse.asExpr().getExpr().(MethodCall).getArgument(i) + | + // Either passed as an individual key:value argument, e.g.: + // Typhoeus.get(..., ssl_verifypeer: false) + isSslVerifyPeerFalsePair(arg.asExpr().getExpr()) and + disablingNode = arg + or + // Or as a single hash argument, e.g.: + // Typhoeus.get(..., { ssl_verifypeer: false, ... }) + exists(DataFlow::LocalSourceNode optionsNode, Pair p | + p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and + isSslVerifyPeerFalsePair(p) and + optionsNode.flowsTo(arg) and + disablingNode.asExpr().getExpr() = p + ) + ) + } + + override string getFramework() { result = "Typhoeus" } +} + +/** Holds if `p` is the pair `ssl_verifypeer: false`. */ +private predicate isSslVerifyPeerFalsePair(Pair p) { + exists(DataFlow::Node key, DataFlow::Node value | + key.asExpr().getExpr() = p.getKey() and + value.asExpr().getExpr() = p.getValue() + | + isSslVerifyPeerLiteral(key) and + isFalse(value) + ) +} + +/** Holds if `node` represents the symbol literal `verify` or `verify_peer`. */ +private predicate isSslVerifyPeerLiteral(DataFlow::Node node) { + exists(DataFlow::LocalSourceNode literal | + literal.asExpr().getExpr().(SymbolLiteral).getValueText() = "ssl_verifypeer" and + literal.flowsTo(node) + ) +} + +/** Holds if `node` can contain the Boolean value `false`. */ +private predicate isFalse(DataFlow::Node node) { + exists(DataFlow::LocalSourceNode literal | + literal.asExpr().getExpr().(BooleanLiteral).isFalse() and + literal.flowsTo(node) + ) +} diff --git a/ruby/ql/lib/codeql/ruby/printAst.qll b/ruby/ql/lib/codeql/ruby/printAst.qll new file mode 100644 index 00000000000..225f39f3b75 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/printAst.qll @@ -0,0 +1,203 @@ +/** + * Provides queries to pretty-print a Ruby abstract syntax tree as a graph. + * + * By default, this will print the AST for all nodes in the database. To change + * this behavior, extend `PrintASTConfiguration` and override `shouldPrintNode` + * to hold for only the AST nodes you wish to view. + */ + +private import AST +private import codeql.ruby.regexp.RegExpTreeView as RETV + +/** Holds if `n` appears in the desugaring of some other node. */ +predicate isDesugared(AstNode n) { + n = any(AstNode sugar).getDesugared() + or + isDesugared(n.getParent()) +} + +/** + * 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 isDesugared(n) + or + not n.isSynthesized() + or + n.isSynthesized() and + not n = any(AstNode sugar).getDesugared() and + exists(AstNode parent | + parent = n.getParent() and + not parent.isSynthesized() and + not n = parent.getDesugared() + ) + } + + predicate shouldPrintAstEdge(AstNode parent, string edgeName, AstNode child) { + child = parent.getAChild(edgeName) and + not child = parent.getDesugared() + } +} + +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) +} + +newtype TPrintNode = + TPrintRegularAstNode(AstNode n) { shouldPrintNode(n) } or + TPrintRegExpNode(RETV::RegExpTerm term) { + exists(RegExpLiteral literal | + shouldPrintNode(literal) and + term.getRootTerm() = literal.getParsed() + ) + } + +/** + * A node in the output tree. + */ +class PrintAstNode extends TPrintNode { + /** Gets a textual representation of this node in the PrintAst output tree. */ + string toString() { none() } + + /** + * Gets the child node with name `edgeName`. Typically this is the name of the + * predicate used to access the child. + */ + PrintAstNode getChild(string edgeName) { none() } + + /** Gets a child of this node. */ + final PrintAstNode getAChild() { result = this.getChild(_) } + + /** Gets the parent of this node, if any. */ + final PrintAstNode getParent() { result.getAChild() = this } + + /** + * Holds if this node is at the specified location. The location spans column + * `startcolumn` of line `startline` to column `endcolumn` of line `endline` + * in file `filepath`. For more information, see + * [LGTM locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + none() + } + + /** Gets a value used to order this node amongst its siblings. */ + int getOrder() { none() } + + /** + * Gets the value of the property of this node, where the name of the property + * is `key`. + */ + final string getProperty(string key) { + key = "semmle.label" and + result = this.toString() + or + key = "semmle.order" and result = this.getOrder().toString() + } +} + +/** An `AstNode` in the output tree. */ +class PrintRegularAstNode extends PrintAstNode, TPrintRegularAstNode { + AstNode astNode; + + PrintRegularAstNode() { this = TPrintRegularAstNode(astNode) } + + override string toString() { + result = "[" + concat(astNode.getAPrimaryQlClass(), ", ") + "] " + astNode.toString() + } + + override PrintAstNode getChild(string edgeName) { + exists(AstNode child | shouldPrintAstEdge(astNode, edgeName, child) | + result = TPrintRegularAstNode(child) + ) + or + // If this AST node is a regexp literal, add the parsed regexp tree as a + // child. + exists(RETV::RegExpTerm t | t = astNode.(RegExpLiteral).getParsed() | + result = TPrintRegExpNode(t) and edgeName = "getParsed" + ) + } + + override int getOrder() { + this = + rank[result](PrintRegularAstNode p, Location l, File f | + l = p.getLocation() and + f = l.getFile() + | + p order by f.getBaseName(), f.getAbsolutePath(), l.getStartLine(), l.getStartColumn() + ) + } + + /** Gets the location of this node. */ + Location getLocation() { result = astNode.getLocation() } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + astNode.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +/** A parsed regexp node in the output tree. */ +class PrintRegExpNode extends PrintAstNode, TPrintRegExpNode { + RETV::RegExpTerm regexNode; + + PrintRegExpNode() { this = TPrintRegExpNode(regexNode) } + + override string toString() { + result = "[" + concat(regexNode.getAPrimaryQlClass(), ", ") + "] " + regexNode.toString() + } + + override PrintAstNode getChild(string edgeName) { + // Use the child index as an edge name. + exists(int i | result = TPrintRegExpNode(regexNode.getChild(i)) and edgeName = i.toString()) + } + + override int getOrder() { exists(RETV::RegExpTerm p | p.getChild(result) = regexNode) } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + regexNode.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +/** + * 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) { 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) { + target = source.getChild(_) and + ( + key = "semmle.label" and + value = strictconcat(string name | source.getChild(name) = target | name, "/") + or + key = "semmle.order" 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/ruby/ql/lib/codeql/ruby/regexp/ExponentialBackTracking.qll b/ruby/ql/lib/codeql/ruby/regexp/ExponentialBackTracking.qll new file mode 100644 index 00000000000..a805366bab8 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/regexp/ExponentialBackTracking.qll @@ -0,0 +1,343 @@ +private import ReDoSUtil +private import RegExpTreeView +private import codeql.Locations + +/* + * This query implements the analysis described in the following two papers: + * + * James Kirrage, Asiri Rathnayake, Hayo Thielecke: Static Analysis for + * Regular Expression Denial-of-Service Attacks. NSS 2013. + * (http://www.cs.bham.ac.uk/~hxt/research/reg-exp-sec.pdf) + * Asiri Rathnayake, Hayo Thielecke: Static Analysis for Regular Expression + * Exponential Runtime via Substructural Logics. 2014. + * (https://www.cs.bham.ac.uk/~hxt/research/redos_full.pdf) + * + * The basic idea is to search for overlapping cycles in the NFA, that is, + * states `q` such that there are two distinct paths from `q` to itself + * that consume the same word `w`. + * + * For any such state `q`, an attack string can be constructed as follows: + * concatenate a prefix `v` that takes the NFA to `q` with `n` copies of + * the word `w` that leads back to `q` along two different paths, followed + * by a suffix `x` that is _not_ accepted in state `q`. A backtracking + * implementation will need to explore at least 2^n different ways of going + * from `q` back to itself while trying to match the `n` copies of `w` + * before finally giving up. + * + * Now in order to identify overlapping cycles, all we have to do is find + * pumpable forks, that is, states `q` that can transition to two different + * states `r1` and `r2` on the same input symbol `c`, such that there are + * paths from both `r1` and `r2` to `q` that consume the same word. The latter + * condition is equivalent to saying that `(q, q)` is reachable from `(r1, r2)` + * in the product NFA. + * + * This is what the query does. It makes a simple attempt to construct a + * prefix `v` leading into `q`, but only to improve the alert message. + * And the query tries to prove the existence of a suffix that ensures + * rejection. This check might fail, which can cause false positives. + * + * Finally, sometimes it depends on the translation whether the NFA generated + * for a regular expression has a pumpable fork or not. We implement one + * particular translation, which may result in false positives or negatives + * relative to some particular JavaScript engine. + * + * More precisely, the query constructs an NFA from a regular expression `r` + * as follows: + * + * * Every sub-term `t` gives rise to an NFA state `Match(t,i)`, representing + * the state of the automaton before attempting to match the `i`th character in `t`. + * * There is one accepting state `Accept(r)`. + * * There is a special `AcceptAnySuffix(r)` state, which accepts any suffix string + * by using an epsilon transition to `Accept(r)` and an any transition to itself. + * * Transitions between states may be labelled with epsilon, or an abstract + * input symbol. + * * Each abstract input symbol represents a set of concrete input characters: + * either a single character, a set of characters represented by a + * character class, or the set of all characters. + * * The product automaton is constructed lazily, starting with pair states + * `(q, q)` where `q` is a fork, and proceding along an over-approximate + * step relation. + * * The over-approximate step relation allows transitions along pairs of + * abstract input symbols where the symbols have overlap in the characters they accept. + * * Once a trace of pairs of abstract input symbols that leads from a fork + * back to itself has been identified, we attempt to construct a concrete + * string corresponding to it, which may fail. + * * Lastly we ensure that any state reached by repeating `n` copies of `w` has + * a suffix `x` (possible empty) that is most likely __not__ accepted. + */ + +/** + * Holds if state `s` might be inside a backtracking repetition. + */ +pragma[noinline] +private predicate stateInsideBacktracking(State s) { + s.getRepr().getParent*() instanceof MaybeBacktrackingRepetition +} + +/** + * A infinitely repeating quantifier that might backtrack. + */ +private class MaybeBacktrackingRepetition extends InfiniteRepetitionQuantifier { + MaybeBacktrackingRepetition() { + exists(RegExpTerm child | + child instanceof RegExpAlt or + child instanceof RegExpQuantifier + | + child.getParent+() = this + ) + } +} + +/** + * A state in the product automaton. + * + * We lazily only construct those states that we are actually + * going to need: `(q, q)` for every fork state `q`, and any + * pair of states that can be reached from a pair that we have + * already constructed. To cut down on the number of states, + * we only represent states `(q1, q2)` where `q1` is lexicographically + * no bigger than `q2`. + * + * States are only constructed if both states in the pair are + * inside a repetition that might backtrack. + */ +private newtype TStatePair = + MkStatePair(State q1, State q2) { + isFork(q1, _, _, _, _) and q2 = q1 + or + (step(_, _, _, q1, q2) or step(_, _, _, q2, q1)) and + rankState(q1) <= rankState(q2) + } + +/** + * Gets a unique number for a `state`. + * Is used to create an ordering of states, where states with the same `toString()` will be ordered differently. + */ +private int rankState(State state) { + state = + rank[result](State s, Location l | + l = s.getRepr().getLocation() + | + s order by l.getStartLine(), l.getStartColumn(), s.toString() + ) +} + +/** + * A state in the product automaton. + */ +private class StatePair extends TStatePair { + State q1; + State q2; + + StatePair() { this = MkStatePair(q1, q2) } + + /** Gets a textual representation of this element. */ + string toString() { result = "(" + q1 + ", " + q2 + ")" } + + /** Gets the first component of the state pair. */ + State getLeft() { result = q1 } + + /** Gets the second component of the state pair. */ + State getRight() { result = q2 } +} + +/** + * Holds for all constructed state pairs. + * + * Used in `statePairDist` + */ +private predicate isStatePair(StatePair p) { any() } + +/** + * Holds if there are transitions from the components of `q` to the corresponding + * components of `r`. + * + * Used in `statePairDist` + */ +private predicate delta2(StatePair q, StatePair r) { step(q, _, _, r) } + +/** + * Gets the minimum length of a path from `q` to `r` in the + * product automaton. + */ +private int statePairDist(StatePair q, StatePair r) = + shortestDistances(isStatePair/1, delta2/2)(q, r, result) + +/** + * Holds if there are transitions from `q` to `r1` and from `q` to `r2` + * labelled with `s1` and `s2`, respectively, where `s1` and `s2` do not + * trivially have an empty intersection. + * + * This predicate only holds for states associated with regular expressions + * that have at least one repetition quantifier in them (otherwise the + * expression cannot be vulnerable to ReDoS attacks anyway). + */ +pragma[noopt] +private predicate isFork(State q, InputSymbol s1, InputSymbol s2, State r1, State r2) { + stateInsideBacktracking(q) and + exists(State q1, State q2 | + q1 = epsilonSucc*(q) and + delta(q1, s1, r1) and + q2 = epsilonSucc*(q) and + delta(q2, s2, r2) and + // Use pragma[noopt] to prevent intersect(s1,s2) from being the starting point of the join. + // From (s1,s2) it would find a huge number of intermediate state pairs (q1,q2) originating from different literals, + // and discover at the end that no `q` can reach both `q1` and `q2` by epsilon transitions. + exists(intersect(s1, s2)) + | + s1 != s2 + or + r1 != r2 + or + r1 = r2 and q1 != q2 + or + // If q can reach itself by epsilon transitions, then there are two distinct paths to the q1/q2 state: + // one that uses the loop and one that doesn't. The engine will separately attempt to match with each path, + // despite ending in the same state. The "fork" thus arises from the choice of whether to use the loop or not. + // To avoid every state in the loop becoming a fork state, + // we arbitrarily pick the InfiniteRepetitionQuantifier state as the canonical fork state for the loop + // (every epsilon-loop must contain such a state). + // + // We additionally require that the there exists another InfiniteRepetitionQuantifier `mid` on the path from `q` to itself. + // This is done to avoid flagging regular expressions such as `/(a?)*b/` - that only has polynomial runtime, and is detected by `js/polynomial-redos`. + // The below code is therefore a heuritic, that only flags regular expressions such as `/(a*)*b/`, + // and does not flag regular expressions such as `/(a?b?)c/`, but the latter pattern is not used frequently. + r1 = r2 and + q1 = q2 and + epsilonSucc+(q) = q and + exists(RegExpTerm term | term = q.getRepr() | term instanceof InfiniteRepetitionQuantifier) and + // One of the mid states is an infinite quantifier itself + exists(State mid, RegExpTerm term | + mid = epsilonSucc+(q) and + term = mid.getRepr() and + term instanceof InfiniteRepetitionQuantifier and + q = epsilonSucc+(mid) and + not mid = q + ) + ) and + stateInsideBacktracking(r1) and + stateInsideBacktracking(r2) +} + +/** + * Gets the state pair `(q1, q2)` or `(q2, q1)`; note that only + * one or the other is defined. + */ +private StatePair mkStatePair(State q1, State q2) { + result = MkStatePair(q1, q2) or result = MkStatePair(q2, q1) +} + +/** + * Holds if there are transitions from the components of `q` to the corresponding + * components of `r` labelled with `s1` and `s2`, respectively. + */ +private predicate step(StatePair q, InputSymbol s1, InputSymbol s2, StatePair r) { + exists(State r1, State r2 | step(q, s1, s2, r1, r2) and r = mkStatePair(r1, r2)) +} + +/** + * Holds if there are transitions from the components of `q` to `r1` and `r2` + * labelled with `s1` and `s2`, respectively. + * + * We only consider transitions where the resulting states `(r1, r2)` are both + * inside a repetition that might backtrack. + */ +pragma[noopt] +private predicate step(StatePair q, InputSymbol s1, InputSymbol s2, State r1, State r2) { + exists(State q1, State q2 | q.getLeft() = q1 and q.getRight() = q2 | + deltaClosed(q1, s1, r1) and + deltaClosed(q2, s2, r2) and + // use noopt to force the join on `intersect` to happen last. + exists(intersect(s1, s2)) + ) and + stateInsideBacktracking(r1) and + stateInsideBacktracking(r2) +} + +private newtype TTrace = + Nil() or + Step(InputSymbol s1, InputSymbol s2, TTrace t) { + exists(StatePair p | + isReachableFromFork(_, p, t, _) and + step(p, s1, s2, _) + ) + or + t = Nil() and isFork(_, s1, s2, _, _) + } + +/** + * A list of pairs of input symbols that describe a path in the product automaton + * starting from some fork state. + */ +private class Trace extends TTrace { + /** Gets a textual representation of this element. */ + string toString() { + this = Nil() and result = "Nil()" + or + exists(InputSymbol s1, InputSymbol s2, Trace t | this = Step(s1, s2, t) | + result = "Step(" + s1 + ", " + s2 + ", " + t + ")" + ) + } +} + +/** + * Gets a string corresponding to the trace `t`. + */ +private string concretise(Trace t) { + t = Nil() and result = "" + or + exists(InputSymbol s1, InputSymbol s2, Trace rest | t = Step(s1, s2, rest) | + result = concretise(rest) + intersect(s1, s2) + ) +} + +/** + * Holds if `r` is reachable from `(fork, fork)` under input `w`, and there is + * a path from `r` back to `(fork, fork)` with `rem` steps. + */ +private predicate isReachableFromFork(State fork, StatePair r, Trace w, int rem) { + // base case + exists(InputSymbol s1, InputSymbol s2, State q1, State q2 | + isFork(fork, s1, s2, q1, q2) and + r = MkStatePair(q1, q2) and + w = Step(s1, s2, Nil()) and + rem = statePairDist(r, MkStatePair(fork, fork)) + ) + or + // recursive case + exists(StatePair p, Trace v, InputSymbol s1, InputSymbol s2 | + isReachableFromFork(fork, p, v, rem + 1) and + step(p, s1, s2, r) and + w = Step(s1, s2, v) and + rem >= statePairDist(r, MkStatePair(fork, fork)) + ) +} + +/** + * Gets a state in the product automaton from which `(fork, fork)` is + * reachable in zero or more epsilon transitions. + */ +private StatePair getAForkPair(State fork) { + isFork(fork, _, _, _, _) and + result = MkStatePair(epsilonPred*(fork), epsilonPred*(fork)) +} + +/** + * Holds if `fork` is a pumpable fork with word `w`. + */ +private predicate isPumpable(State fork, string w) { + exists(StatePair q, Trace t | + isReachableFromFork(fork, q, t, _) and + q = getAForkPair(fork) and + w = concretise(t) + ) +} + +/** + * An instantiation of `ReDoSConfiguration` for exponential backtracking. + */ +class ExponentialReDoSConfiguration extends ReDoSConfiguration { + ExponentialReDoSConfiguration() { this = "ExponentialReDoSConfiguration" } + + override predicate isReDoSCandidate(State state, string pump) { isPumpable(state, pump) } +} diff --git a/ruby/ql/lib/codeql/ruby/regexp/ParseRegExp.qll b/ruby/ql/lib/codeql/ruby/regexp/ParseRegExp.qll new file mode 100644 index 00000000000..2af4b67cd07 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/regexp/ParseRegExp.qll @@ -0,0 +1,891 @@ +/** + * Library for parsing for Ruby regular expressions. + * + * N.B. does not yet handle stripping whitespace and comments in regexes with + * the `x` (free-spacing) flag. + */ + +private import codeql.ruby.ast.Literal as AST +private import codeql.Locations + +class RegExp extends AST::RegExpLiteral { + /** + * Helper predicate for `charSetStart(int start, int end)`. + * + * In order to identify left brackets ('[') which actually start a character class, + * we perform a left to right scan of the string. + * + * To avoid negative recursion we return a boolean. See `escaping`, + * the helper for `escapingChar`, for a clean use of this pattern. + * + * result is true for those start chars that actually mark a start of a char set. + */ + boolean charSetStart(int pos) { + exists(int index | + // is opening bracket + this.charSetDelimiter(index, pos) = true and + ( + // if this is the first bracket, `pos` starts a char set + index = 1 and result = true + or + // if the previous char set delimiter was not a closing bracket, `pos` does + // not start a char set. This is needed to handle cases such as `[[]` (a + // char set that matches the `[` char) + index > 1 and + not this.charSetDelimiter(index - 1, _) = false and + result = false + or + // special handling of cases such as `[][]` (the character-set of the characters `]` and `[`). + exists(int prevClosingBracketPos | + // previous bracket is a closing bracket + this.charSetDelimiter(index - 1, prevClosingBracketPos) = false and + if + // check if the character that comes before the previous closing bracket + // is an opening bracket (taking `^` into account) + // check if the character that comes before the previous closing bracket + // is an opening bracket (taking `^` into account) + exists(int posBeforePrevClosingBracket | + if this.getChar(prevClosingBracketPos - 1) = "^" + then posBeforePrevClosingBracket = prevClosingBracketPos - 2 + else posBeforePrevClosingBracket = prevClosingBracketPos - 1 + | + this.charSetDelimiter(index - 2, posBeforePrevClosingBracket) = true + ) + then + // brackets without anything in between is not valid character ranges, so + // the first closing bracket in `[]]` and `[^]]` does not count, + // + // and we should _not_ mark the second opening bracket in `[][]` and `[^][]` + // as starting a new char set. ^ ^ + exists(int posBeforePrevClosingBracket | + this.charSetDelimiter(index - 2, posBeforePrevClosingBracket) = true + | + result = this.charSetStart(posBeforePrevClosingBracket).booleanNot() + ) + else + // if not, `pos` does in fact mark a real start of a character range + result = true + ) + ) + ) + } + + /** + * Helper predicate for chars that could be character-set delimiters. + * Holds if the (non-escaped) char at `pos` in the string, is the (one-based) `index` occurrence of a bracket (`[` or `]`) in the string. + * Result if `true` is the char is `[`, and `false` if the char is `]`. + */ + boolean charSetDelimiter(int index, int pos) { + pos = + rank[index](int p | + (this.nonEscapedCharAt(p) = "[" or this.nonEscapedCharAt(p) = "]") and + // Brackets that art part of POSIX expressions should not count as + // char-set delimiters. + not exists(int x, int y | + this.posixStyleNamedCharacterProperty(x, y, _) and pos >= x and pos < y + ) + ) and + ( + this.nonEscapedCharAt(pos) = "[" and result = true + or + this.nonEscapedCharAt(pos) = "]" and result = false + ) + } + + predicate charSetStart(int start, int end) { + this.charSetStart(start) = true and + ( + this.getChar(start + 1) = "^" and end = start + 2 + or + not this.getChar(start + 1) = "^" and end = start + 1 + ) + } + + /** Whether there is a character class, between start (inclusive) and end (exclusive) */ + predicate charSet(int start, int end) { + exists(int innerStart, int innerEnd | + this.charSetStart(start, innerStart) and + not this.charSetStart(_, start) + | + end = innerEnd + 1 and + innerEnd = + min(int e | + e > innerStart and + this.nonEscapedCharAt(e) = "]" and + not exists(int x, int y | + this.posixStyleNamedCharacterProperty(x, y, _) and e >= x and e < y + ) + | + e + ) + ) + } + + predicate charSetToken(int charsetStart, int index, int tokenStart, int tokenEnd) { + tokenStart = + rank[index](int start, int end | this.charSetToken(charsetStart, start, end) | start) and + this.charSetToken(charsetStart, tokenStart, tokenEnd) + } + + /** Either a char or a - */ + predicate charSetToken(int charsetStart, int start, int end) { + this.charSetStart(charsetStart, start) and + ( + this.escapedCharacter(start, end) + or + this.namedCharacterProperty(start, end, _) + or + exists(this.nonEscapedCharAt(start)) and end = start + 1 + ) + or + this.charSetToken(charsetStart, _, start) and + ( + this.escapedCharacter(start, end) + or + this.namedCharacterProperty(start, end, _) + or + exists(this.nonEscapedCharAt(start)) and + end = start + 1 and + not this.getChar(start) = "]" + ) + } + + predicate charSetChild(int charsetStart, int start, int end) { + this.charSetToken(charsetStart, start, end) and + not exists(int rangeStart, int rangeEnd | + this.charRange(charsetStart, rangeStart, _, _, rangeEnd) and + rangeStart <= start and + rangeEnd >= end + ) + or + this.charRange(charsetStart, start, _, _, end) + } + + predicate charRange(int charsetStart, int start, int lowerEnd, int upperStart, int end) { + exists(int index | + this.charRangeEnd(charsetStart, index) = true and + this.charSetToken(charsetStart, index - 2, start, lowerEnd) and + this.charSetToken(charsetStart, index, upperStart, end) + ) + } + + private boolean charRangeEnd(int charsetStart, int index) { + this.charSetToken(charsetStart, index, _, _) and + ( + index in [1, 2] and result = false + or + index > 2 and + exists(int connectorStart | + this.charSetToken(charsetStart, index - 1, connectorStart, _) and + this.nonEscapedCharAt(connectorStart) = "-" and + result = + this.charRangeEnd(charsetStart, index - 2) + .booleanNot() + .booleanAnd(this.charRangeEnd(charsetStart, index - 1).booleanNot()) + ) + or + not exists(int connectorStart | + this.charSetToken(charsetStart, index - 1, connectorStart, _) and + this.nonEscapedCharAt(connectorStart) = "-" + ) and + result = false + ) + } + + predicate escapingChar(int pos) { this.escaping(pos) = true } + + private boolean escaping(int pos) { + pos = -1 and result = false + or + this.getChar(pos) = "\\" and result = this.escaping(pos - 1).booleanNot() + or + this.getChar(pos) != "\\" and result = false + } + + /** Gets the text of this regex */ + string getText() { result = this.getValueText() } + + string getChar(int i) { result = this.getText().charAt(i) } + + string nonEscapedCharAt(int i) { + result = this.getText().charAt(i) and + not exists(int x, int y | this.escapedCharacter(x, y) and i in [x .. y - 1]) + } + + private predicate isOptionDivider(int i) { this.nonEscapedCharAt(i) = "|" } + + private predicate isGroupEnd(int i) { this.nonEscapedCharAt(i) = ")" and not this.inCharSet(i) } + + private predicate isGroupStart(int i) { this.nonEscapedCharAt(i) = "(" and not this.inCharSet(i) } + + predicate failedToParse(int i) { + exists(this.getChar(i)) and + not exists(int start, int end | + this.topLevel(start, end) and + start <= i and + end > i + ) + } + + /** Matches named character properties such as `\p{Word}` and `[[:digit:]]` */ + predicate namedCharacterProperty(int start, int end, string name) { + this.pStyleNamedCharacterProperty(start, end, name) or + this.posixStyleNamedCharacterProperty(start, end, name) + } + + /** Gets the name of the character property in start,end */ + string getCharacterPropertyName(int start, int end) { + this.namedCharacterProperty(start, end, result) + } + + /** Matches a POSIX bracket expression such as `[:alnum:]` within a character class. */ + private predicate posixStyleNamedCharacterProperty(int start, int end, string name) { + this.getChar(start) = "[" and + this.getChar(start + 1) = ":" and + end = + min(int e | + e > start and + this.getChar(e - 2) = ":" and + this.getChar(e - 1) = "]" + | + e + ) and + exists(int nameStart | + this.getChar(start + 2) = "^" and nameStart = start + 3 + or + not this.getChar(start + 2) = "^" and nameStart = start + 2 + | + name = this.getText().substring(nameStart, end - 2) + ) + } + + /** + * Matches named character properties. For example: + * - `\p{Space}` + * - `\P{Digit}` upper-case P means inverted + * - `\p{^Word}` caret also means inverted + * + * These can occur both inside and outside of character classes. + */ + private predicate pStyleNamedCharacterProperty(int start, int end, string name) { + this.escapingChar(start) and + this.getChar(start + 1) in ["p", "P"] and + this.getChar(start + 2) = "{" and + this.getChar(end - 1) = "}" and + end > start and + not exists(int i | start + 2 < i and i < end - 1 | this.getChar(i) = "}") and + exists(int nameStart | + this.getChar(start + 3) = "^" and nameStart = start + 4 + or + not this.getChar(start + 3) = "^" and nameStart = start + 3 + | + name = this.getText().substring(nameStart, end - 1) + ) + } + + /** + * Holds if the named character property is inverted. Examples for which it holds: + * - `\P{Digit}` upper-case P means inverted + * - `\p{^Word}` caret also means inverted + * - `[[:^digit:]]` + * + * Examples for which it doesn't hold: + * - `\p{Word}` + * - `\P{^Space}` - upper-case P and caret cancel each other out + * - `[[:alnum:]]` + */ + predicate namedCharacterPropertyIsInverted(int start, int end) { + this.pStyleNamedCharacterProperty(start, end, _) and + exists(boolean upperP, boolean caret | + (if this.getChar(start + 1) = "P" then upperP = true else upperP = false) and + (if this.getChar(start + 3) = "^" then caret = true else caret = false) + | + upperP.booleanXor(caret) = true + ) + or + this.posixStyleNamedCharacterProperty(start, end, _) and + this.getChar(start + 3) = "^" + } + + predicate escapedCharacter(int start, int end) { + this.escapingChar(start) and + not this.numberedBackreference(start, _, _) and + not this.namedBackreference(start, _, _) and + not this.pStyleNamedCharacterProperty(start, _, _) and + ( + // hex char \xhh + this.getChar(start + 1) = "x" and end = start + 4 + or + // wide hex char \uhhhh + this.getChar(start + 1) = "u" and end = start + 6 + or + // escape not handled above; update when adding a new case + not this.getChar(start + 1) in ["x", "u"] and + not exists(this.getChar(start + 1).toInt()) and + end = start + 2 + ) + } + + predicate inCharSet(int index) { + exists(int x, int y | this.charSet(x, y) and index in [x + 1 .. y - 2]) + } + + predicate inPosixBracket(int index) { + exists(int x, int y | + this.posixStyleNamedCharacterProperty(x, y, _) and index in [x + 1 .. y - 2] + ) + } + + /** 'Simple' characters are any that don't alter the parsing of the regex. */ + private predicate simpleCharacter(int start, int end) { + end = start + 1 and + not this.charSet(start, _) and + not this.charSet(_, start + 1) and + not exists(int x, int y | + this.posixStyleNamedCharacterProperty(x, y, _) and + start >= x and + end <= y + ) and + exists(string c | c = this.getChar(start) | + exists(int x, int y, int z | + this.charSet(x, z) and + this.charSetStart(x, y) + | + start = y + or + start = z - 2 + or + start > y and start < z - 2 and not this.charRange(_, _, start, end, _) + ) + or + not this.inCharSet(start) and + not c = "(" and + not c = "[" and + not c = ")" and + not c = "|" and + not this.qualifier(start, _, _, _) + ) + } + + predicate character(int start, int end) { + ( + this.simpleCharacter(start, end) and + not exists(int x, int y | this.escapedCharacter(x, y) and x <= start and y >= end) + or + this.escapedCharacter(start, end) + ) and + not exists(int x, int y | this.groupStart(x, y) and x <= start and y >= end) and + not exists(int x, int y | this.backreference(x, y) and x <= start and y >= end) and + not exists(int x, int y | + this.pStyleNamedCharacterProperty(x, y, _) and x <= start and y >= end + ) + } + + predicate normalCharacter(int start, int end) { + this.character(start, end) and + not this.specialCharacter(start, end, _) + } + + predicate specialCharacter(int start, int end, string char) { + this.character(start, end) and + not this.inCharSet(start) and + ( + end = start + 1 and + char = this.getChar(start) and + (char = "$" or char = "^" or char = ".") + or + end = start + 2 and + this.escapingChar(start) and + char = this.getText().substring(start, end) and + char = ["\\A", "\\Z", "\\z"] + ) + } + + /** Whether the text in the range `start,end` is a group */ + predicate group(int start, int end) { + this.groupContents(start, end, _, _) + or + this.emptyGroup(start, end) + } + + /** Gets the number of the group in start,end */ + int getGroupNumber(int start, int end) { + this.group(start, end) and + result = + count(int i | this.group(i, _) and i < start and not this.nonCapturingGroupStart(i, _)) + 1 + } + + /** Gets the name, if it has one, of the group in start,end */ + string getGroupName(int start, int end) { + this.group(start, end) and + exists(int nameEnd | + this.namedGroupStart(start, nameEnd) and + result = this.getText().substring(start + 4, nameEnd - 1) + ) + } + + /** Whether the text in the range start, end is a group and can match the empty string. */ + predicate zeroWidthMatch(int start, int end) { + this.emptyGroup(start, end) + or + this.negativeAssertionGroup(start, end) + or + this.positiveLookaheadAssertionGroup(start, end) + or + this.positiveLookbehindAssertionGroup(start, end) + } + + predicate emptyGroup(int start, int end) { + exists(int endm1 | end = endm1 + 1 | + this.groupStart(start, endm1) and + this.isGroupEnd(endm1) + ) + } + + private predicate emptyMatchAtStartGroup(int start, int end) { + this.emptyGroup(start, end) + or + this.negativeAssertionGroup(start, end) + or + this.positiveLookaheadAssertionGroup(start, end) + } + + private predicate emptyMatchAtEndGroup(int start, int end) { + this.emptyGroup(start, end) + or + this.negativeAssertionGroup(start, end) + or + this.positiveLookbehindAssertionGroup(start, end) + } + + private predicate negativeAssertionGroup(int start, int end) { + exists(int inStart | + this.negativeLookaheadAssertionStart(start, inStart) + or + this.negativeLookbehindAssertionStart(start, inStart) + | + this.groupContents(start, end, inStart, _) + ) + } + + predicate negativeLookaheadAssertionGroup(int start, int end) { + exists(int inStart | this.negativeLookaheadAssertionStart(start, inStart) | + this.groupContents(start, end, inStart, _) + ) + } + + predicate negativeLookbehindAssertionGroup(int start, int end) { + exists(int inStart | this.negativeLookbehindAssertionStart(start, inStart) | + this.groupContents(start, end, inStart, _) + ) + } + + predicate positiveLookaheadAssertionGroup(int start, int end) { + exists(int inStart | this.lookaheadAssertionStart(start, inStart) | + this.groupContents(start, end, inStart, _) + ) + } + + predicate positiveLookbehindAssertionGroup(int start, int end) { + exists(int inStart | this.lookbehindAssertionStart(start, inStart) | + this.groupContents(start, end, inStart, _) + ) + } + + private predicate groupStart(int start, int end) { + this.nonCapturingGroupStart(start, end) + or + this.namedGroupStart(start, end) + or + this.lookaheadAssertionStart(start, end) + or + this.negativeLookaheadAssertionStart(start, end) + or + this.lookbehindAssertionStart(start, end) + or + this.negativeLookbehindAssertionStart(start, end) + or + this.commentGroupStart(start, end) + or + this.simpleGroupStart(start, end) + } + + /** Matches the start of a non-capturing group, e.g. `(?:` */ + private predicate nonCapturingGroupStart(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = ":" and + end = start + 3 + } + + /** Matches the start of a simple group, e.g. `(a+)`. */ + private predicate simpleGroupStart(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) != "?" and + end = start + 1 + } + + /** + * Matches the start of a named group, such as: + * - `(?<name>\w+)` + * - `(?'name'\w+)` + */ + private predicate namedGroupStart(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + ( + this.getChar(start + 2) = "<" and + not this.getChar(start + 3) = "=" and // (?<=foo) is a positive lookbehind assertion + not this.getChar(start + 3) = "!" and // (?<!foo) is a negative lookbehind assertion + exists(int nameEnd | + nameEnd = min(int i | i > start + 3 and this.getChar(i) = ">") and + end = nameEnd + 1 + ) + or + this.getChar(start + 2) = "'" and + exists(int nameEnd | + nameEnd = min(int i | i > start + 2 and this.getChar(i) = "'") and end = nameEnd + 1 + ) + ) + } + + /** Matches the start of a positive lookahead assertion, i.e. `(?=`. */ + private predicate lookaheadAssertionStart(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "=" and + end = start + 3 + } + + /** Matches the start of a negative lookahead assertion, i.e. `(?!`. */ + private predicate negativeLookaheadAssertionStart(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "!" and + end = start + 3 + } + + /** Matches the start of a positive lookbehind assertion, i.e. `(?<=`. */ + private predicate lookbehindAssertionStart(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "<" and + this.getChar(start + 3) = "=" and + end = start + 4 + } + + /** Matches the start of a negative lookbehind assertion, i.e. `(?<!`. */ + private predicate negativeLookbehindAssertionStart(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "<" and + this.getChar(start + 3) = "!" and + end = start + 4 + } + + /** Matches the start of a comment group, i.e. `(?#`. */ + private predicate commentGroupStart(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "#" and + end = start + 3 + } + + predicate groupContents(int start, int end, int inStart, int inEnd) { + this.groupStart(start, inStart) and + end = inEnd + 1 and + this.topLevel(inStart, inEnd) and + this.isGroupEnd(inEnd) + } + + /** Matches a named backreference, e.g. `\k<foo>`. */ + predicate namedBackreference(int start, int end, string name) { + this.escapingChar(start) and + this.getChar(start + 1) = "k" and + this.getChar(start + 2) = "<" and + exists(int nameEnd | nameEnd = min(int i | i > start + 3 and this.getChar(i) = ">") | + end = nameEnd + 1 and + name = this.getText().substring(start + 3, nameEnd) + ) + } + + /** Matches a numbered backreference, e.g. `\1`. */ + predicate numberedBackreference(int start, int end, int value) { + this.escapingChar(start) and + not this.getChar(start + 1) = "0" and + exists(string text, string svalue, int len | + end = start + len and + text = this.getText() and + len in [2 .. 3] + | + svalue = text.substring(start + 1, start + len) and + value = svalue.toInt() and + not exists(text.substring(start + 1, start + len + 1).toInt()) and + value > 0 + ) + } + + /** Whether the text in the range `start,end` is a back reference */ + predicate backreference(int start, int end) { + this.numberedBackreference(start, end, _) + or + this.namedBackreference(start, end, _) + } + + /** Gets the number of the back reference in start,end */ + int getBackRefNumber(int start, int end) { this.numberedBackreference(start, end, result) } + + /** Gets the name, if it has one, of the back reference in start,end */ + string getBackRefName(int start, int end) { this.namedBackreference(start, end, result) } + + private predicate baseItem(int start, int end) { + this.character(start, end) and + not exists(int x, int y | this.charSet(x, y) and x <= start and y >= end) + or + this.group(start, end) + or + this.charSet(start, end) + or + this.backreference(start, end) + or + this.pStyleNamedCharacterProperty(start, end, _) + } + + private predicate qualifier(int start, int end, boolean maybeEmpty, boolean mayRepeatForever) { + this.shortQualifier(start, end, maybeEmpty, mayRepeatForever) and + not this.getChar(end) = "?" + or + exists(int shortEnd | this.shortQualifier(start, shortEnd, maybeEmpty, mayRepeatForever) | + if this.getChar(shortEnd) = "?" then end = shortEnd + 1 else end = shortEnd + ) + } + + private predicate shortQualifier(int start, int end, boolean maybeEmpty, boolean mayRepeatForever) { + ( + this.getChar(start) = "+" and maybeEmpty = false and mayRepeatForever = true + or + this.getChar(start) = "*" and maybeEmpty = true and mayRepeatForever = true + or + this.getChar(start) = "?" and maybeEmpty = true and mayRepeatForever = false + ) and + end = start + 1 + or + exists(string lower, string upper | + this.multiples(start, end, lower, upper) and + (if lower = "" or lower.toInt() = 0 then maybeEmpty = true else maybeEmpty = false) and + if upper = "" then mayRepeatForever = true else mayRepeatForever = false + ) + } + + predicate multiples(int start, int end, string lower, string upper) { + exists(string text, string match, string inner | + text = this.getText() and + end = start + match.length() and + inner = match.substring(1, match.length() - 1) + | + match = text.regexpFind("\\{[0-9]+\\}", _, start) and + lower = inner and + upper = lower + or + match = text.regexpFind("\\{[0-9]*,[0-9]*\\}", _, start) and + exists(int commaIndex | + commaIndex = inner.indexOf(",") and + lower = inner.prefix(commaIndex) and + upper = inner.suffix(commaIndex + 1) + ) + ) + } + + /** + * Whether the text in the range start,end is a qualified item, where item is a character, + * a character set or a group. + */ + predicate qualifiedItem(int start, int end, boolean maybeEmpty, boolean mayRepeatForever) { + this.qualifiedPart(start, _, end, maybeEmpty, mayRepeatForever) + } + + predicate qualifiedPart( + int start, int partEnd, int end, boolean maybeEmpty, boolean mayRepeatForever + ) { + this.baseItem(start, partEnd) and + this.qualifier(partEnd, end, maybeEmpty, mayRepeatForever) + } + + predicate item(int start, int end) { + this.qualifiedItem(start, end, _, _) + or + this.baseItem(start, end) and not this.qualifier(end, _, _, _) + } + + private predicate subsequence(int start, int end) { + ( + start = 0 or + this.groupStart(_, start) or + this.isOptionDivider(start - 1) + ) and + this.item(start, end) + or + exists(int mid | + this.subsequence(start, mid) and + this.item(mid, end) + ) + } + + /** + * Whether the text in the range start,end is a sequence of 1 or more items, where an item is a character, + * a character set or a group. + */ + predicate sequence(int start, int end) { + this.sequenceOrQualified(start, end) and + not this.qualifiedItem(start, end, _, _) + } + + private predicate sequenceOrQualified(int start, int end) { + this.subsequence(start, end) and + not this.itemStart(end) + } + + private predicate itemStart(int start) { + this.character(start, _) or + this.isGroupStart(start) or + this.charSet(start, _) or + this.backreference(start, _) or + this.namedCharacterProperty(start, _, _) + } + + private predicate itemEnd(int end) { + this.character(_, end) + or + exists(int endm1 | this.isGroupEnd(endm1) and end = endm1 + 1) + or + this.charSet(_, end) + or + this.qualifier(_, end, _, _) + } + + private predicate topLevel(int start, int end) { + this.subalternation(start, end, _) and + not this.isOptionDivider(end) + } + + private predicate subalternation(int start, int end, int itemStart) { + this.sequenceOrQualified(start, end) and + not this.isOptionDivider(start - 1) and + itemStart = start + or + start = end and + not this.itemEnd(start) and + this.isOptionDivider(end) and + itemStart = start + or + exists(int mid | + this.subalternation(start, mid, _) and + this.isOptionDivider(mid) and + itemStart = mid + 1 + | + this.sequenceOrQualified(itemStart, end) + or + not this.itemStart(end) and end = itemStart + ) + } + + /** + * Whether the text in the range start,end is an alternation + */ + predicate alternation(int start, int end) { + this.topLevel(start, end) and + exists(int less | this.subalternation(start, less, _) and less < end) + } + + /** + * Whether the text in the range start,end is an alternation and the text in partStart, partEnd is one of the + * options in that alternation. + */ + predicate alternationOption(int start, int end, int partStart, int partEnd) { + this.alternation(start, end) and + this.subalternation(start, partEnd, partStart) + } + + /** A part of the regex that may match the start of the string. */ + private predicate firstPart(int start, int end) { + start = 0 and end = this.getText().length() + or + exists(int x | this.firstPart(x, end) | + this.emptyMatchAtStartGroup(x, start) + or + this.qualifiedItem(x, start, true, _) + or + // ^ and \A match the start of the string + this.specialCharacter(x, start, ["^", "\\A"]) + ) + or + exists(int y | this.firstPart(start, y) | + this.item(start, end) + or + this.qualifiedPart(start, end, y, _, _) + ) + or + exists(int x, int y | this.firstPart(x, y) | + this.groupContents(x, y, start, end) + or + this.alternationOption(x, y, start, end) + ) + } + + /** A part of the regex that may match the end of the string. */ + private predicate lastPart(int start, int end) { + start = 0 and end = this.getText().length() + or + exists(int y | this.lastPart(start, y) | + this.emptyMatchAtEndGroup(end, y) + or + this.qualifiedItem(end, y, true, _) + or + // $, \Z, and \z match the end of the string. + this.specialCharacter(end, y, ["$", "\\Z", "\\z"]) + ) + or + exists(int x | + this.lastPart(x, end) and + this.item(start, end) + ) + or + exists(int y | this.lastPart(start, y) | this.qualifiedPart(start, end, y, _, _)) + or + exists(int x, int y | this.lastPart(x, y) | + this.groupContents(x, y, start, end) + or + this.alternationOption(x, y, start, end) + ) + } + + /** + * Whether the item at [start, end) is one of the first items + * to be matched. + */ + predicate firstItem(int start, int end) { + ( + this.character(start, end) + or + this.qualifiedItem(start, end, _, _) + or + this.charSet(start, end) + ) and + this.firstPart(start, end) + } + + /** + * Whether the item at [start, end) is one of the last items + * to be matched. + */ + predicate lastItem(int start, int end) { + ( + this.character(start, end) + or + this.qualifiedItem(start, end, _, _) + or + this.charSet(start, end) + ) and + this.lastPart(start, end) + } +} diff --git a/ruby/ql/lib/codeql/ruby/regexp/PolynomialReDoSCustomizations.qll b/ruby/ql/lib/codeql/ruby/regexp/PolynomialReDoSCustomizations.qll new file mode 100644 index 00000000000..3d3655ad3a9 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/regexp/PolynomialReDoSCustomizations.qll @@ -0,0 +1,131 @@ +/** + * Provides default sources, sinks and sanitizers for reasoning about + * polynomial regular expression denial-of-service attacks, as well + * as extension points for adding your own. + */ + +private import codeql.ruby.AST as AST +private import codeql.ruby.CFG +private import codeql.ruby.DataFlow +private import codeql.ruby.dataflow.RemoteFlowSources +private import codeql.ruby.regexp.ParseRegExp as RegExp +private import codeql.ruby.regexp.RegExpTreeView +private import codeql.ruby.regexp.SuperlinearBackTracking + +module PolynomialReDoS { + /** + * A data flow source node for polynomial regular expression denial-of-service vulnerabilities. + */ + abstract class Source extends DataFlow::Node { } + + /** + * A data flow sink node for polynomial regular expression denial-of-service vulnerabilities. + */ + abstract class Sink extends DataFlow::Node { + /** Gets the regex that is being executed by this node. */ + abstract RegExpTerm getRegExp(); + + /** Gets the node to highlight in the alert message. */ + DataFlow::Node getHighlight() { result = this } + } + + /** + * A sanitizer for polynomial regular expression denial-of-service vulnerabilities. + */ + abstract class Sanitizer extends DataFlow::Node { } + + /** + * A sanitizer guard for polynomial regular expression denial of service + * vulnerabilities. + */ + abstract class SanitizerGuard extends DataFlow::BarrierGuard { } + + /** + * A source of remote user input, considered as a flow source. + */ + class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + + /** + * Gets the AST of a regular expression object that can flow to `node`. + */ + RegExpTerm getRegExpObjectFromNode(DataFlow::Node node) { + exists(DataFlow::LocalSourceNode regexp | + regexp.flowsTo(node) and + result = regexp.asExpr().(CfgNodes::ExprNodes::RegExpLiteralCfgNode).getExpr().getParsed() + ) + } + + /** + * A regexp match against a superlinear backtracking term, seen as a sink for + * polynomial regular expression denial-of-service vulnerabilities. + */ + class PolynomialBackTrackingTermMatch extends Sink { + PolynomialBackTrackingTerm term; + DataFlow::ExprNode matchNode; + + PolynomialBackTrackingTermMatch() { + exists(DataFlow::Node regexp | + term.getRootTerm() = getRegExpObjectFromNode(regexp) and + ( + // `=~` or `!~` + exists(CfgNodes::ExprNodes::BinaryOperationCfgNode op | + matchNode.asExpr() = op and + ( + op.getExpr() instanceof AST::RegExpMatchExpr or + op.getExpr() instanceof AST::NoRegExpMatchExpr + ) and + ( + this.asExpr() = op.getLeftOperand() and regexp.asExpr() = op.getRightOperand() + or + this.asExpr() = op.getRightOperand() and regexp.asExpr() = op.getLeftOperand() + ) + ) + or + // Any of the methods on `String` that take a regexp. + exists(CfgNodes::ExprNodes::MethodCallCfgNode call | + matchNode.asExpr() = call and + call.getExpr().getMethodName() = + [ + "[]", "gsub", "gsub!", "index", "match", "match?", "partition", "rindex", + "rpartition", "scan", "slice!", "split", "sub", "sub!" + ] and + this.asExpr() = call.getReceiver() and + regexp.asExpr() = call.getArgument(0) + ) + or + // A call to `match` or `match?` where the regexp is the receiver. + exists(CfgNodes::ExprNodes::MethodCallCfgNode call | + matchNode.asExpr() = call and + call.getExpr().getMethodName() = ["match", "match?"] and + regexp.asExpr() = call.getReceiver() and + this.asExpr() = call.getArgument(0) + ) + ) + ) + } + + override RegExpTerm getRegExp() { result = term } + + override DataFlow::Node getHighlight() { result = matchNode } + } + + /** + * A check on the length of a string, seen as a sanitizer guard. + */ + class LengthGuard extends SanitizerGuard, CfgNodes::ExprNodes::RelationalOperationCfgNode { + private DataFlow::Node input; + + LengthGuard() { + exists(DataFlow::CallNode length, DataFlow::ExprNode operand | + length.asExpr().getExpr().(AST::MethodCall).getMethodName() = "length" and + length.getReceiver() = input and + length.flowsTo(operand) and + operand.getExprNode() = this.getAnOperand() + ) + } + + override predicate checks(CfgNode node, boolean branch) { + node = input.asExpr() and branch = true + } + } +} diff --git a/ruby/ql/lib/codeql/ruby/regexp/PolynomialReDoSQuery.qll b/ruby/ql/lib/codeql/ruby/regexp/PolynomialReDoSQuery.qll new file mode 100644 index 00000000000..db7269d7fdb --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/regexp/PolynomialReDoSQuery.qll @@ -0,0 +1,37 @@ +/** + * Provides a taint tracking configuration for reasoning about polynomial + * regular expression denial-of-service attacks. + * + * Note, for performance reasons: only import this file if `Configuration` is + * needed. Otherwise, `PolynomialReDoSCustomizations` should be imported + * instead. + */ + +private import codeql.ruby.DataFlow +private import codeql.ruby.TaintTracking + +/** + * Provides a taint-tracking configuration for detecting polynomial regular + * expression denial of service vulnerabilities. + */ +module PolynomialReDoS { + import PolynomialReDoSCustomizations::PolynomialReDoS + + /** + * A taint-tracking configuration for detecting polynomial regular expression + * denial of service vulnerabilities. + */ + class Configuration extends TaintTracking::Configuration { + Configuration() { this = "PolynomialReDoS" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + + override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer } + + override predicate isSanitizerGuard(DataFlow::BarrierGuard node) { + node instanceof SanitizerGuard + } + } +} diff --git a/ruby/ql/lib/codeql/ruby/regexp/ReDoSUtil.qll b/ruby/ql/lib/codeql/ruby/regexp/ReDoSUtil.qll new file mode 100644 index 00000000000..884030b99ee --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/regexp/ReDoSUtil.qll @@ -0,0 +1,1186 @@ +/** + * Provides classes for working with regular expressions that can + * perform backtracking in superlinear/exponential time. + * + * This module contains a number of utility predicates for compiling a regular expression into a NFA and reasoning about this NFA. + * + * The `ReDoSConfiguration` contains a `isReDoSCandidate` predicate that is used to + * to determine which states the prefix/suffix search should happen on. + * There is only meant to exist one `ReDoSConfiguration` at a time. + * + * The predicate `hasReDoSResult` outputs a de-duplicated set of + * states that will cause backtracking (a rejecting suffix exists). + */ + +import RegExpTreeView +private import codeql.Locations + +/** + * A configuration for which parts of a regular expression should be considered relevant for + * the different predicates in `ReDoS.qll`. + * Used to adjust the computations for either superlinear or exponential backtracking. + */ +abstract class ReDoSConfiguration extends string { + bindingset[this] + ReDoSConfiguration() { any() } + + /** + * Holds if `state` with the pump string `pump` is a candidate for a + * ReDoS vulnerable state. + * This is used to determine which states are considered for the prefix/suffix construction. + */ + abstract predicate isReDoSCandidate(State state, string pump); +} + +/** + * Holds if repeating `pump' starting at `state` is a candidate for causing backtracking. + * No check whether a rejected suffix exists has been made. + */ +private predicate isReDoSCandidate(State state, string pump) { + any(ReDoSConfiguration conf).isReDoSCandidate(state, pump) and + ( + not any(ReDoSConfiguration conf).isReDoSCandidate(epsilonSucc+(state), _) + or + epsilonSucc+(state) = state and + state = + max(State s, Location l | + s = epsilonSucc+(state) and + l = s.getRepr().getLocation() and + any(ReDoSConfiguration conf).isReDoSCandidate(s, _) and + s.getRepr() instanceof InfiniteRepetitionQuantifier + | + s order by l.getStartLine(), l.getStartColumn(), l.getEndColumn(), l.getEndLine() + ) + ) +} + +/** + * Gets the char after `c` (from a simplified ASCII table). + */ +private string nextChar(string c) { exists(int code | code = ascii(c) | code + 1 = ascii(result)) } + +/** + * Gets an approximation for the ASCII code for `char`. + * Only the easily printable chars are included (so no newline, tab, null, etc). + */ +private int ascii(string char) { + char = + rank[result](string c | + c = + "! \"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" + .charAt(_) + ) +} + +/** + * Holds if `t` matches at least an epsilon symbol. + * + * That is, this term does not restrict the language of the enclosing regular expression. + * + * This is implemented as an under-approximation, and this predicate does not hold for sub-patterns in particular. + */ +predicate matchesEpsilon(RegExpTerm t) { + t instanceof RegExpStar + or + t instanceof RegExpOpt + or + t.(RegExpRange).getLowerBound() = 0 + or + exists(RegExpTerm child | + child = t.getAChild() and + matchesEpsilon(child) + | + t instanceof RegExpAlt or + t instanceof RegExpGroup or + t instanceof RegExpPlus or + t instanceof RegExpRange + ) + or + matchesEpsilon(t.(RegExpBackRef).getGroup()) + or + forex(RegExpTerm child | child = t.(RegExpSequence).getAChild() | matchesEpsilon(child)) +} + +/** + * A lookahead/lookbehind that matches the empty string. + */ +class EmptyPositiveSubPatttern extends RegExpSubPattern { + EmptyPositiveSubPatttern() { + ( + this instanceof RegExpPositiveLookahead + or + this instanceof RegExpPositiveLookbehind + ) and + matchesEpsilon(this.getOperand()) + } +} + +/** + * A branch in a disjunction that is the root node in a literal, or a literal + * whose root node is not a disjunction. + */ +class RegExpRoot extends RegExpTerm { + RegExpParent parent; + + RegExpRoot() { + exists(RegExpAlt alt | + alt.isRootTerm() and + this = alt.getAChild() and + parent = alt.getParent() + ) + or + this.isRootTerm() and + not this instanceof RegExpAlt and + parent = this.getParent() + } + + /** + * Holds if this root term is relevant to the ReDoS analysis. + */ + predicate isRelevant() { + // there is at least one repetition + getRoot(any(InfiniteRepetitionQuantifier q)) = this and + // there are no lookbehinds + not exists(RegExpLookbehind lbh | getRoot(lbh) = this) and + // is actually used as a RegExp + this.isUsedAsRegExp() //and + // // pragmatic performance optimization: ignore minified files. + // not getRootTerm().getParent().(Expr).getTopLevel().isMinified() + } +} + +/** + * A constant in a regular expression that represents valid Unicode character(s). + */ +private class RegexpCharacterConstant extends RegExpConstant { + RegexpCharacterConstant() { this.isCharacter() } +} + +/** + * Holds if `term` is the chosen canonical representative for all terms with string representation `str`. + * + * Using canonical representatives gives a huge performance boost when working with tuples containing multiple `InputSymbol`s. + * The number of `InputSymbol`s is decreased by 3 orders of magnitude or more in some larger benchmarks. + */ +private predicate isCanonicalTerm(RegExpTerm term, string str) { + term = + rank[1](RegExpTerm t, Location loc, File file | + loc = t.getLocation() and + file = t.getFile() and + str = t.getRawValue() + | + t order by t.getFile().getRelativePath(), loc.getStartLine(), loc.getStartColumn() + ) +} + +/** + * An abstract input symbol, representing a set of concrete characters. + */ +private newtype TInputSymbol = + /** An input symbol corresponding to character `c`. */ + Char(string c) { + c = any(RegexpCharacterConstant cc | getRoot(cc).isRelevant()).getValue().charAt(_) + } or + /** + * An input symbol representing all characters matched by + * a (non-universal) character class that has string representation `charClassString`. + */ + CharClass(string charClassString) { + exists(RegExpTerm term | term.getRawValue() = charClassString | getRoot(term).isRelevant()) and + exists(RegExpTerm recc | isCanonicalTerm(recc, charClassString) | + recc instanceof RegExpCharacterClass and + not recc.(RegExpCharacterClass).isUniversalClass() + or + recc instanceof RegExpCharacterClassEscape + or + recc instanceof RegExpNamedCharacterProperty + ) + } or + /** An input symbol representing all characters matched by `.`. */ + Dot() or + /** An input symbol representing all characters. */ + Any() or + /** An epsilon transition in the automaton. */ + Epsilon() + +/** + * Gets the canonical CharClass for `term`. + */ +CharClass getCanonicalCharClass(RegExpTerm term) { + exists(string str | isCanonicalTerm(term, str) | result = CharClass(str)) +} + +/** + * Holds if `a` and `b` are input symbols from the same regexp. + */ +private predicate sharesRoot(TInputSymbol a, TInputSymbol b) { + exists(RegExpRoot root | + belongsTo(a, root) and + belongsTo(b, root) + ) +} + +/** + * Holds if the `a` is an input symbol from a regexp that has root `root`. + */ +private predicate belongsTo(TInputSymbol a, RegExpRoot root) { + exists(State s | getRoot(s.getRepr()) = root | + delta(s, a, _) + or + delta(_, a, s) + ) +} + +/** + * An abstract input symbol, representing a set of concrete characters. + */ +class InputSymbol extends TInputSymbol { + InputSymbol() { not this instanceof Epsilon } + + /** + * Gets a string representation of this input symbol. + */ + string toString() { + this = Char(result) + or + this = CharClass(result) + or + this = Dot() and result = "." + or + this = Any() and result = "[^]" + } +} + +/** + * An abstract input symbol that represents a character class. + */ +abstract private class CharacterClass extends InputSymbol { + /** + * Gets a character that is relevant for intersection-tests involving this + * character class. + * + * Specifically, this is any of the characters mentioned explicitly in the + * character class, offset by one if it is inverted. For character class escapes, + * the result is as if the class had been written out as a series of intervals. + * + * This set is large enough to ensure that for any two intersecting character + * classes, one contains a relevant character from the other. + */ + abstract string getARelevantChar(); + + /** + * Holds if this character class matches `char`. + */ + bindingset[char] + abstract predicate matches(string char); + + /** + * Gets a character matched by this character class. + */ + string choose() { result = this.getARelevantChar() and this.matches(result) } +} + +/** + * Provides implementations for `CharacterClass`. + */ +private module CharacterClasses { + /** + * Holds if the character class `cc` has a child (constant or range) that matches `char`. + */ + pragma[noinline] + predicate hasChildThatMatches(RegExpCharacterClass cc, string char) { + exists(getCanonicalCharClass(cc)) and + exists(RegExpTerm child | child = cc.getAChild() | + char = child.(RegexpCharacterConstant).getValue() + or + rangeMatchesOnLetterOrDigits(child, char) + or + not rangeMatchesOnLetterOrDigits(child, _) and + char = getARelevantChar() and + exists(string lo, string hi | child.(RegExpCharacterRange).isRange(lo, hi) | + lo <= char and + char <= hi + ) + or + exists(RegExpCharacterClassEscape escape | escape = child | + escape.getValue() = escape.getValue().toLowerCase() and + classEscapeMatches(escape.getValue(), char) + or + char = getARelevantChar() and + escape.getValue() = escape.getValue().toUpperCase() and + not classEscapeMatches(escape.getValue().toLowerCase(), char) + ) + or + exists(RegExpNamedCharacterProperty charProp | charProp = child | + not charProp.isInverted() and + namedCharacterPropertyMatches(charProp.getName(), char) + or + char = getARelevantChar() and + charProp.isInverted() and + not namedCharacterPropertyMatches(charProp.getName(), char) + ) + ) + } + + /** + * Holds if `range` is a range on lower-case, upper-case, or digits, and matches `char`. + * This predicate is used to restrict the searchspace for ranges by only joining `getAnyPossiblyMatchedChar` + * on a few ranges. + */ + private predicate rangeMatchesOnLetterOrDigits(RegExpCharacterRange range, string char) { + exists(string lo, string hi | + range.isRange(lo, hi) and lo = lowercaseLetter() and hi = lowercaseLetter() + | + lo <= char and + char <= hi and + char = lowercaseLetter() + ) + or + exists(string lo, string hi | + range.isRange(lo, hi) and lo = upperCaseLetter() and hi = upperCaseLetter() + | + lo <= char and + char <= hi and + char = upperCaseLetter() + ) + or + exists(string lo, string hi | range.isRange(lo, hi) and lo = digit() and hi = digit() | + lo <= char and + char <= hi and + char = digit() + ) + } + + private string lowercaseLetter() { result = "abdcefghijklmnopqrstuvwxyz".charAt(_) } + + private string upperCaseLetter() { result = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".charAt(_) } + + private string digit() { result = [0 .. 9].toString() } + + /** + * Gets a char that could be matched by a regular expression. + * Includes all printable ascii chars, all constants mentioned in a regexp, and all chars matches by the regexp `/\s|\d|\w/`. + */ + string getARelevantChar() { + exists(ascii(result)) + or + exists(RegexpCharacterConstant c | result = c.getValue().charAt(_)) + or + classEscapeMatches(_, result) + } + + /** + * Gets a char that is mentioned in the character class `c`. + */ + private string getAMentionedChar(RegExpCharacterClass c) { + exists(RegExpTerm child | child = c.getAChild() | + result = child.(RegexpCharacterConstant).getValue() + or + child.(RegExpCharacterRange).isRange(result, _) + or + child.(RegExpCharacterRange).isRange(_, result) + or + exists(RegExpCharacterClassEscape escape | child = escape | + result = min(string s | classEscapeMatches(escape.getValue().toLowerCase(), s)) + or + result = max(string s | classEscapeMatches(escape.getValue().toLowerCase(), s)) + ) + or + exists(RegExpNamedCharacterProperty charProp | child = charProp | + result = min(string s | namedCharacterPropertyMatches(charProp.getName(), s)) + or + result = max(string s | namedCharacterPropertyMatches(charProp.getName(), s)) + ) + ) + } + + /** + * An implementation of `CharacterClass` for positive (non inverted) character classes. + */ + private class PositiveCharacterClass extends CharacterClass { + RegExpCharacterClass cc; + + PositiveCharacterClass() { this = getCanonicalCharClass(cc) and not cc.isInverted() } + + override string getARelevantChar() { result = getAMentionedChar(cc) } + + override predicate matches(string char) { hasChildThatMatches(cc, char) } + } + + /** + * An implementation of `CharacterClass` for inverted character classes. + */ + private class InvertedCharacterClass extends CharacterClass { + RegExpCharacterClass cc; + + InvertedCharacterClass() { this = getCanonicalCharClass(cc) and cc.isInverted() } + + override string getARelevantChar() { + result = nextChar(getAMentionedChar(cc)) or + nextChar(result) = getAMentionedChar(cc) + } + + bindingset[char] + override predicate matches(string char) { not hasChildThatMatches(cc, char) } + } + + /** + * Holds if the character class escape `clazz` (\d, \s, or \w) matches `char`. + */ + pragma[noinline] + private predicate classEscapeMatches(string clazz, string char) { + clazz = "d" and + char = "0123456789".charAt(_) + or + clazz = "s" and + char = [" ", "\t", "\r", "\n", 11.toUnicode(), 12.toUnicode()] // 11.toUnicode() = \v, 12.toUnicode() = \f' + or + clazz = "w" and + char = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_".charAt(_) + } + + /** + * Holds if the named character property (e.g. from a POSIX bracket + * expression) `propName` matches `char`. For example, it holds when `name` is + * `"word"` and `char` is `"a"`. + * + * TODO: expand to cover more properties. + */ + private predicate namedCharacterPropertyMatches(string propName, string char) { + propName = ["digit", "Digit"] and + char = "0123456789".charAt(_) + or + propName = ["space", "Space"] and + ( + char = [" ", "\t", "\r", "\n"] + or + char = getARelevantChar() and + char.regexpMatch("\\u000b|\\u000c") // \v|\f (vertical tab | form feed) + ) + or + propName = ["word", "Word"] and + char = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_".charAt(_) + } + + /** + * An implementation of `CharacterClass` for \d, \s, and \w. + */ + private class PositiveCharacterClassEscape extends CharacterClass { + RegExpCharacterClassEscape cc; + + PositiveCharacterClassEscape() { + this = getCanonicalCharClass(cc) and cc.getValue() = ["d", "s", "w"] + } + + override string getARelevantChar() { + cc.getValue() = "d" and + result = ["0", "9"] + or + cc.getValue() = "s" and + result = [" "] + or + cc.getValue() = "w" and + result = ["a", "Z", "_", "0", "9"] + } + + override predicate matches(string char) { classEscapeMatches(cc.getValue(), char) } + + override string choose() { + cc.getValue() = "d" and + result = "9" + or + cc.getValue() = "s" and + result = [" "] + or + cc.getValue() = "w" and + result = "a" + } + } + + /** + * An implementation of `CharacterClass` for \D, \S, and \W. + */ + private class NegativeCharacterClassEscape extends CharacterClass { + RegExpCharacterClassEscape cc; + + NegativeCharacterClassEscape() { + this = getCanonicalCharClass(cc) and cc.getValue() = ["D", "S", "W"] + } + + override string getARelevantChar() { + cc.getValue() = "D" and + result = ["a", "Z", "!"] + or + cc.getValue() = "S" and + result = ["a", "9", "!"] + or + cc.getValue() = "W" and + result = [" ", "!"] + } + + bindingset[char] + override predicate matches(string char) { + not classEscapeMatches(cc.getValue().toLowerCase(), char) + } + } + + /** + * An implementation of `NamedCharacterProperty` for positive (non-inverted) + * character properties. + */ + private class PositiveNamedCharacterProperty extends CharacterClass { + RegExpNamedCharacterProperty cp; + + PositiveNamedCharacterProperty() { this = getCanonicalCharClass(cp) and not cp.isInverted() } + + override string getARelevantChar() { + exists(string lowerName | lowerName = cp.getName().toLowerCase() | + lowerName = "digit" and + result = ["0", "9"] + or + lowerName = "space" and + result = [" "] + or + lowerName = "word" and + result = ["a", "Z", "_", "0", "9"] + ) + } + + override predicate matches(string char) { namedCharacterPropertyMatches(cp.getName(), char) } + + override string choose() { + exists(string lowerName | lowerName = cp.getName().toLowerCase() | + lowerName = "digit" and + result = "9" + or + lowerName = "space" and + result = " " + or + lowerName = "word" and + result = "a" + ) + } + } + + private class InvertedNamedCharacterProperty extends CharacterClass { + RegExpNamedCharacterProperty cp; + + InvertedNamedCharacterProperty() { this = getCanonicalCharClass(cp) and cp.isInverted() } + + override string getARelevantChar() { + exists(string lowerName | lowerName = cp.getName().toLowerCase() | + lowerName = "digit" and + result = ["a", "Z", "!"] + or + lowerName = "space" and + result = ["a", "9", "!"] + or + lowerName = "word" and + result = [" ", "!"] + ) + } + + bindingset[char] + override predicate matches(string char) { + not namedCharacterPropertyMatches(cp.getName(), char) + } + } +} + +private class EdgeLabel extends TInputSymbol { + string toString() { + this = Epsilon() and result = "" + or + exists(InputSymbol s | this = s and result = s.toString()) + } +} + +/** + * Gets the state before matching `t`. + */ +pragma[inline] +private State before(RegExpTerm t) { result = Match(t, 0) } + +/** + * Gets a state the NFA may be in after matching `t`. + */ +private State after(RegExpTerm t) { + exists(RegExpAlt alt | t = alt.getAChild() | result = after(alt)) + or + exists(RegExpSequence seq, int i | t = seq.getChild(i) | + result = before(seq.getChild(i + 1)) + or + i + 1 = seq.getNumChild() and result = after(seq) + ) + or + exists(RegExpGroup grp | t = grp.getAChild() | result = after(grp)) + or + exists(RegExpStar star | t = star.getAChild() | result = before(star)) + or + exists(RegExpPlus plus | t = plus.getAChild() | + result = before(plus) or + result = after(plus) + ) + or + exists(RegExpOpt opt | t = opt.getAChild() | result = after(opt)) + or + exists(RegExpRoot root | t = root | result = AcceptAnySuffix(root)) +} + +/** + * Holds if the NFA has a transition from `q1` to `q2` labelled with `lbl`. + */ +predicate delta(State q1, EdgeLabel lbl, State q2) { + exists(RegexpCharacterConstant s, int i | + q1 = Match(s, i) and + lbl = Char(s.getValue().charAt(i)) and + ( + q2 = Match(s, i + 1) + or + s.getValue().length() = i + 1 and + q2 = after(s) + ) + ) + or + exists(RegExpDot dot | q1 = before(dot) and q2 = after(dot) | + if dot.getLiteral().isDotAll() then lbl = Any() else lbl = Dot() + ) + or + exists(RegExpCharacterClass cc | + cc.isUniversalClass() and q1 = before(cc) and lbl = Any() and q2 = after(cc) + or + q1 = before(cc) and + lbl = CharClass(cc.getRawValue()) and + q2 = after(cc) + ) + or + exists(RegExpCharacterClassEscape cc | + q1 = before(cc) and + lbl = CharClass(cc.getRawValue()) and + q2 = after(cc) + ) + or + exists(RegExpNamedCharacterProperty cp | + q1 = before(cp) and + lbl = CharClass(cp.getRawValue()) and + q2 = after(cp) + ) + or + exists(RegExpAlt alt | lbl = Epsilon() | q1 = before(alt) and q2 = before(alt.getAChild())) + or + exists(RegExpSequence seq | lbl = Epsilon() | q1 = before(seq) and q2 = before(seq.getChild(0))) + or + exists(RegExpGroup grp | lbl = Epsilon() | q1 = before(grp) and q2 = before(grp.getChild(0))) + or + exists(RegExpStar star | lbl = Epsilon() | + q1 = before(star) and q2 = before(star.getChild(0)) + or + q1 = before(star) and q2 = after(star) + ) + or + exists(RegExpPlus plus | lbl = Epsilon() | q1 = before(plus) and q2 = before(plus.getChild(0))) + or + exists(RegExpOpt opt | lbl = Epsilon() | + q1 = before(opt) and q2 = before(opt.getChild(0)) + or + q1 = before(opt) and q2 = after(opt) + ) + or + exists(RegExpRoot root | q1 = AcceptAnySuffix(root) | + lbl = Any() and q2 = q1 + or + lbl = Epsilon() and q2 = Accept(root) + ) + or + exists(RegExpRoot root | q1 = Match(root, 0) | lbl = Any() and q2 = q1) + or + exists(RegExpDollar dollar | q1 = before(dollar) | + lbl = Epsilon() and q2 = Accept(getRoot(dollar)) + ) + or + exists(EmptyPositiveSubPatttern empty | q1 = before(empty) | + lbl = Epsilon() and q2 = after(empty) + ) +} + +/** + * Gets a state that `q` has an epsilon transition to. + */ +State epsilonSucc(State q) { delta(q, Epsilon(), result) } + +/** + * Gets a state that has an epsilon transition to `q`. + */ +State epsilonPred(State q) { q = epsilonSucc(result) } + +/** + * Holds if there is a state `q` that can be reached from `q1` + * along epsilon edges, such that there is a transition from + * `q` to `q2` that consumes symbol `s`. + */ +predicate deltaClosed(State q1, InputSymbol s, State q2) { delta(epsilonSucc*(q1), s, q2) } + +/** + * Gets the root containing the given term, that is, the root of the literal, + * or a branch of the root disjunction. + */ +RegExpRoot getRoot(RegExpTerm term) { + result = term or + result = getRoot(term.getParent()) +} + +private newtype TState = + Match(RegExpTerm t, int i) { + getRoot(t).isRelevant() and + ( + i = 0 + or + exists(t.(RegexpCharacterConstant).getValue().charAt(i)) + ) + } or + Accept(RegExpRoot l) { l.isRelevant() } or + AcceptAnySuffix(RegExpRoot l) { l.isRelevant() } + +/** + * Gets a state that is about to match the regular expression `t`. + */ +State mkMatch(RegExpTerm t) { result = Match(t, 0) } + +/** + * A state in the NFA corresponding to a regular expression. + * + * Each regular expression literal `l` has one accepting state + * `Accept(l)`, one state that accepts all suffixes `AcceptAnySuffix(l)`, + * and a state `Match(t, i)` for every subterm `t`, + * which represents the state of the NFA before starting to + * match `t`, or the `i`th character in `t` if `t` is a constant. + */ +class State extends TState { + RegExpTerm repr; + + State() { + this = Match(repr, _) or + this = Accept(repr) or + this = AcceptAnySuffix(repr) + } + + /** + * Gets a string representation for this state in a regular expression. + */ + string toString() { + exists(int i | this = Match(repr, i) | result = "Match(" + repr + "," + i + ")") + or + this instanceof Accept and + result = "Accept(" + repr + ")" + or + this instanceof AcceptAnySuffix and + result = "AcceptAny(" + repr + ")" + } + + /** + * Gets the location for this state. + */ + Location getLocation() { result = repr.getLocation() } + + /** + * Gets the term represented by this state. + */ + RegExpTerm getRepr() { result = repr } +} + +/** + * Gets the minimum char that is matched by both the character classes `c` and `d`. + */ +private string getMinOverlapBetweenCharacterClasses(CharacterClass c, CharacterClass d) { + result = min(getAOverlapBetweenCharacterClasses(c, d)) +} + +/** + * Gets a char that is matched by both the character classes `c` and `d`. + * And `c` and `d` is not the same character class. + */ +private string getAOverlapBetweenCharacterClasses(CharacterClass c, CharacterClass d) { + sharesRoot(c, d) and + result = [c.getARelevantChar(), d.getARelevantChar()] and + c.matches(result) and + d.matches(result) and + not c = d +} + +/** + * Gets a character that is represented by both `c` and `d`. + */ +string intersect(InputSymbol c, InputSymbol d) { + (sharesRoot(c, d) or [c, d] = Any()) and + ( + c = Char(result) and + d = getAnInputSymbolMatching(result) + or + result = getMinOverlapBetweenCharacterClasses(c, d) + or + result = c.(CharacterClass).choose() and + ( + d = c + or + d = Dot() and + not (result = "\n" or result = "\r") + or + d = Any() + ) + or + (c = Dot() or c = Any()) and + (d = Dot() or d = Any()) and + result = "a" + ) + or + result = intersect(d, c) +} + +/** + * Gets a symbol that matches `char`. + */ +bindingset[char] +InputSymbol getAnInputSymbolMatching(string char) { + result = Char(char) + or + result.(CharacterClass).matches(char) + or + result = Dot() and + not (char = "\n" or char = "\r") + or + result = Any() +} + +/** + * Predicates for constructing a prefix string that leads to a given state. + */ +private module PrefixConstruction { + /** + * Holds if `state` starts the string matched by the regular expression. + */ + private predicate isStartState(State state) { + state instanceof StateInPumpableRegexp and + ( + state = Match(any(RegExpRoot r), _) + or + exists(RegExpCaret car | state = after(car)) + ) + } + + /** + * Holds if `state` is the textually last start state for the regular expression. + */ + private predicate lastStartState(State state) { + exists(RegExpRoot root | + state = + max(State s, Location l | + isStartState(s) and getRoot(s.getRepr()) = root and l = s.getRepr().getLocation() + | + s + order by + l.getStartLine(), l.getStartColumn(), s.getRepr().toString(), l.getEndColumn(), + l.getEndLine() + ) + ) + } + + /** + * Holds if there exists any transition (Epsilon() or other) from `a` to `b`. + */ + private predicate existsTransition(State a, State b) { delta(a, _, b) } + + /** + * Gets the minimum number of transitions it takes to reach `state` from the `start` state. + */ + int prefixLength(State start, State state) = + shortestDistances(lastStartState/1, existsTransition/2)(start, state, result) + + /** + * Gets the minimum number of transitions it takes to reach `state` from the start state. + */ + private int lengthFromStart(State state) { result = prefixLength(_, state) } + + /** + * Gets a string for which the regular expression will reach `state`. + * + * Has at most one result for any given `state`. + * This predicate will not always have a result even if there is a ReDoS issue in + * the regular expression. + */ + string prefix(State state) { + lastStartState(state) and + result = "" + or + // the search stops past the last redos candidate state. + lengthFromStart(state) <= max(lengthFromStart(any(State s | isReDoSCandidate(s, _)))) and + exists(State prev | + // select a unique predecessor (by an arbitrary measure) + prev = + min(State s, Location loc | + lengthFromStart(s) = lengthFromStart(state) - 1 and + loc = s.getRepr().getLocation() and + delta(s, _, state) + | + s + order by + loc.getStartLine(), loc.getStartColumn(), loc.getEndLine(), loc.getEndColumn(), + s.getRepr().toString() + ) + | + // greedy search for the shortest prefix + result = prefix(prev) and delta(prev, Epsilon(), state) + or + not delta(prev, Epsilon(), state) and + result = prefix(prev) + getCanonicalEdgeChar(prev, state) + ) + } + + /** + * Gets a canonical char for which there exists a transition from `prev` to `next` in the NFA. + */ + private string getCanonicalEdgeChar(State prev, State next) { + result = + min(string c | delta(prev, any(InputSymbol symbol | c = intersect(Any(), symbol)), next)) + } + + /** + * A state within a regular expression that has a pumpable state. + */ + class StateInPumpableRegexp extends State { + pragma[noinline] + StateInPumpableRegexp() { + exists(State s | isReDoSCandidate(s, _) | getRoot(s.getRepr()) = getRoot(this.getRepr())) + } + } +} + +/** + * Predicates for testing the presence of a rejecting suffix. + * + * These predicates are used to ensure that the all states reached from the fork + * by repeating `w` have a rejecting suffix. + * + * For example, a regexp like `/^(a+)+/` will accept any string as long the prefix is + * some number of `"a"`s, and it is therefore not possible to construct a rejecting suffix. + * + * A regexp like `/(a+)+$/` or `/(a+)+b/` trivially has a rejecting suffix, + * as the suffix "X" will cause both the regular expressions to be rejected. + * + * The string `w` is repeated any number of times because it needs to be + * infinitely repeatedable for the attack to work. + * For the regular expression `/((ab)+)*abab/` the accepting state is not reachable from the fork + * using epsilon transitions. But any attempt at repeating `w` will end in a state that accepts all suffixes. + */ +private module SuffixConstruction { + import PrefixConstruction + + /** + * Holds if all states reachable from `fork` by repeating `w` + * are likely rejectable by appending some suffix. + */ + predicate reachesOnlyRejectableSuffixes(State fork, string w) { + isReDoSCandidate(fork, w) and + forex(State next | next = process(fork, w, w.length() - 1) | isLikelyRejectable(next)) + } + + /** + * Holds if there likely exists a suffix starting from `s` that leads to the regular expression being rejected. + * This predicate might find impossible suffixes when searching for suffixes of length > 1, which can cause FPs. + */ + pragma[noinline] + private predicate isLikelyRejectable(StateInPumpableRegexp s) { + // exists a reject edge with some char. + hasRejectEdge(s) + or + hasEdgeToLikelyRejectable(s) + or + // stopping here is rejection + isRejectState(s) + } + + /** + * Holds if `s` is not an accept state, and there is no epsilon transition to an accept state. + */ + predicate isRejectState(StateInPumpableRegexp s) { not epsilonSucc*(s) = Accept(_) } + + /** + * Holds if there is likely a non-empty suffix leading to rejection starting in `s`. + */ + pragma[noopt] + predicate hasEdgeToLikelyRejectable(StateInPumpableRegexp s) { + // all edges (at least one) with some char leads to another state that is rejectable. + // the `next` states might not share a common suffix, which can cause FPs. + exists(string char | char = hasEdgeToLikelyRejectableHelper(s) | + // noopt to force `hasEdgeToLikelyRejectableHelper` to be first in the join-order. + exists(State next | deltaClosedChar(s, char, next) | isLikelyRejectable(next)) and + forall(State next | deltaClosedChar(s, char, next) | isLikelyRejectable(next)) + ) + } + + /** + * Gets a char for there exists a transition away from `s`, + * and `s` has not been found to be rejectable by `hasRejectEdge` or `isRejectState`. + */ + pragma[noinline] + private string hasEdgeToLikelyRejectableHelper(StateInPumpableRegexp s) { + not hasRejectEdge(s) and + not isRejectState(s) and + deltaClosedChar(s, result, _) + } + + /** + * Holds if there is a state `next` that can be reached from `prev` + * along epsilon edges, such that there is a transition from + * `prev` to `next` that the character symbol `char`. + */ + predicate deltaClosedChar(StateInPumpableRegexp prev, string char, StateInPumpableRegexp next) { + deltaClosed(prev, getAnInputSymbolMatchingRelevant(char), next) + } + + pragma[noinline] + InputSymbol getAnInputSymbolMatchingRelevant(string char) { + char = relevant(_) and + result = getAnInputSymbolMatching(char) + } + + /** + * Gets a char used for finding possible suffixes inside `root`. + */ + pragma[noinline] + private string relevant(RegExpRoot root) { + exists(ascii(result)) + or + exists(InputSymbol s | belongsTo(s, root) | result = intersect(s, _)) + or + // The characters from `hasSimpleRejectEdge`. Only `\n` is really needed (as `\n` is not in the `ascii` relation). + // The three chars must be kept in sync with `hasSimpleRejectEdge`. + result = ["|", "\n", "Z"] + } + + /** + * Holds if there exists a `char` such that there is no edge from `s` labeled `char` in our NFA. + * The NFA does not model reject states, so the above is the same as saying there is a reject edge. + */ + private predicate hasRejectEdge(State s) { + hasSimpleRejectEdge(s) + or + not hasSimpleRejectEdge(s) and + exists(string char | char = relevant(getRoot(s.getRepr())) | not deltaClosedChar(s, char, _)) + } + + /** + * Holds if there is no edge from `s` labeled with "|", "\n", or "Z" in our NFA. + * This predicate is used as a cheap pre-processing to speed up `hasRejectEdge`. + */ + private predicate hasSimpleRejectEdge(State s) { + // The three chars were chosen arbitrarily. The three chars must be kept in sync with `relevant`. + exists(string char | char = ["|", "\n", "Z"] | not deltaClosedChar(s, char, _)) + } + + /** + * Gets a state that can be reached from pumpable `fork` consuming all + * chars in `w` any number of times followed by the first `i+1` characters of `w`. + */ + pragma[noopt] + private State process(State fork, string w, int i) { + exists(State prev | prev = getProcessPrevious(fork, i, w) | + exists(string char, InputSymbol sym | + char = w.charAt(i) and + deltaClosed(prev, sym, result) and + // noopt to prevent joining `prev` with all possible `chars` that could transition away from `prev`. + // Instead only join with the set of `chars` where a relevant `InputSymbol` has already been found. + sym = getAProcessInputSymbol(char) + ) + ) + } + + /** + * Gets a state that can be reached from pumpable `fork` consuming all + * chars in `w` any number of times followed by the first `i` characters of `w`. + */ + private State getProcessPrevious(State fork, int i, string w) { + isReDoSCandidate(fork, w) and + ( + i = 0 and result = fork + or + result = process(fork, w, i - 1) + or + // repeat until fixpoint + i = 0 and + result = process(fork, w, w.length() - 1) + ) + } + + /** + * Gets an InputSymbol that matches `char`. + * The predicate is specialized to only have a result for the `char`s that are relevant for the `process` predicate. + */ + private InputSymbol getAProcessInputSymbol(string char) { + char = getAProcessChar() and + result = getAnInputSymbolMatching(char) + } + + /** + * Gets a `char` that occurs in a `pump` string. + */ + private string getAProcessChar() { result = any(string s | isReDoSCandidate(_, s)).charAt(_) } +} + +/** + * Gets the result of backslash-escaping newlines, carriage-returns and + * backslashes in `s`. + */ +bindingset[s] +private string escape(string s) { + result = + s.replaceAll("\\", "\\\\") + .replaceAll("\n", "\\n") + .replaceAll("\r", "\\r") + .replaceAll("\t", "\\t") +} + +/** + * Gets `str` with the last `i` characters moved to the front. + * + * We use this to adjust the pump string to match with the beginning of + * a RegExpTerm, so it doesn't start in the middle of a constant. + */ +bindingset[str, i] +private string rotate(string str, int i) { + result = str.suffix(str.length() - i) + str.prefix(str.length() - i) +} + +/** + * Holds if `term` may cause superlinear backtracking on strings containing many repetitions of `pump`. + * Gets the shortest string that causes superlinear backtracking. + */ +private predicate isReDoSAttackable(RegExpTerm term, string pump, State s) { + exists(int i, string c | s = Match(term, i) | + c = + min(string w | + any(ReDoSConfiguration conf).isReDoSCandidate(s, w) and + SuffixConstruction::reachesOnlyRejectableSuffixes(s, w) + | + w order by w.length(), w + ) and + pump = escape(rotate(c, i)) + ) +} + +/** + * Holds if the state `s` (represented by the term `t`) can have backtracking with repetitions of `pump`. + * + * `prefixMsg` contains a friendly message for a prefix that reaches `s` (or `prefixMsg` is the empty string if the prefix is empty or if no prefix could be found). + */ +predicate hasReDoSResult(RegExpTerm t, string pump, State s, string prefixMsg) { + not t.getRegExp().hasFreeSpacingFlag() and // exclude free-spacing mode regexes + isReDoSAttackable(t, pump, s) and + ( + prefixMsg = "starting with '" + escape(PrefixConstruction::prefix(s)) + "' and " and + not PrefixConstruction::prefix(s) = "" + or + PrefixConstruction::prefix(s) = "" and prefixMsg = "" + or + not exists(PrefixConstruction::prefix(s)) and prefixMsg = "" + ) +} diff --git a/ruby/ql/lib/codeql/ruby/regexp/RegExpTreeView.qll b/ruby/ql/lib/codeql/ruby/regexp/RegExpTreeView.qll new file mode 100644 index 00000000000..c56756f804a --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/regexp/RegExpTreeView.qll @@ -0,0 +1,724 @@ +private import codeql.ruby.ast.Literal as AST +private import codeql.Locations +private import ParseRegExp + +/** + * An element containing a regular expression term, that is, either + * a string literal (parsed as a regular expression) + * or another regular expression term. + */ +class RegExpParent extends TRegExpParent { + string toString() { result = "RegExpParent" } + + RegExpTerm getChild(int i) { none() } + + RegExpTerm getAChild() { result = this.getChild(_) } + + int getNumChild() { result = count(this.getAChild()) } + + /** + * Gets the name of a primary CodeQL class to which this regular + * expression term belongs. + */ + string getAPrimaryQlClass() { result = "RegExpParent" } + + /** + * Gets a comma-separated list of the names of the primary CodeQL classes to + * which this regular expression term belongs. + */ + final string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") } +} + +class RegExpLiteral extends TRegExpLiteral, RegExpParent { + RegExp re; + + RegExpLiteral() { this = TRegExpLiteral(re) } + + override RegExpTerm getChild(int i) { i = 0 and result.getRegExp() = re and result.isRootTerm() } + + predicate isDotAll() { re.hasMultilineFlag() } + + override string getAPrimaryQlClass() { result = "RegExpLiteral" } +} + +class RegExpTerm extends RegExpParent { + RegExp re; + int start; + int end; + + RegExpTerm() { + this = TRegExpAlt(re, start, end) + or + this = TRegExpBackRef(re, start, end) + or + this = TRegExpCharacterClass(re, start, end) + or + this = TRegExpCharacterRange(re, start, end) + or + this = TRegExpNormalChar(re, start, end) + or + this = TRegExpGroup(re, start, end) + or + this = TRegExpQuantifier(re, start, end) + or + this = TRegExpSequence(re, start, end) and + exists(seqChild(re, start, end, 1)) // if a sequence does not have more than one element, it should be treated as that element instead. + or + this = TRegExpSpecialChar(re, start, end) + or + this = TRegExpNamedCharacterProperty(re, start, end) + } + + RegExpTerm getRootTerm() { + this.isRootTerm() and result = this + or + result = this.getParent().(RegExpTerm).getRootTerm() + } + + predicate isUsedAsRegExp() { any() } + + predicate isRootTerm() { start = 0 and end = re.getText().length() } + + override RegExpTerm getChild(int i) { + result = this.(RegExpAlt).getChild(i) + or + result = this.(RegExpBackRef).getChild(i) + or + result = this.(RegExpCharacterClass).getChild(i) + or + result = this.(RegExpCharacterRange).getChild(i) + or + result = this.(RegExpNormalChar).getChild(i) + or + result = this.(RegExpGroup).getChild(i) + or + result = this.(RegExpQuantifier).getChild(i) + or + result = this.(RegExpSequence).getChild(i) + or + result = this.(RegExpSpecialChar).getChild(i) + or + result = this.(RegExpNamedCharacterProperty).getChild(i) + } + + RegExpParent getParent() { result.getAChild() = this } + + RegExp getRegExp() { result = re } + + int getStart() { result = start } + + int getEnd() { result = end } + + override string toString() { result = re.getText().substring(start, end) } + + override string getAPrimaryQlClass() { result = "RegExpTerm" } + + Location getLocation() { result = re.getLocation() } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + exists(int re_start, int re_end | + re.getComponent(0).getLocation().hasLocationInfo(filepath, startline, re_start, _, _) and + re.getComponent(re.getNumberOfComponents() - 1) + .getLocation() + .hasLocationInfo(filepath, _, _, endline, re_end) + | + startcolumn = re_start + start and + endcolumn = re_start + end - 1 + ) + } + + File getFile() { result = this.getLocation().getFile() } + + string getRawValue() { result = this.toString() } + + RegExpLiteral getLiteral() { result = TRegExpLiteral(re) } + + /** Gets the regular expression term that is matched (textually) before this one, if any. */ + RegExpTerm getPredecessor() { + exists(RegExpTerm parent | parent = this.getParent() | + result = parent.(RegExpSequence).previousElement(this) + or + not exists(parent.(RegExpSequence).previousElement(this)) and + not parent instanceof RegExpSubPattern and + result = parent.getPredecessor() + ) + } + + /** Gets the regular expression term that is matched (textually) after this one, if any. */ + RegExpTerm getSuccessor() { + exists(RegExpTerm parent | parent = this.getParent() | + result = parent.(RegExpSequence).nextElement(this) + or + not exists(parent.(RegExpSequence).nextElement(this)) and + not parent instanceof RegExpSubPattern and + result = parent.getSuccessor() + ) + } +} + +newtype TRegExpParent = + TRegExpLiteral(RegExp re) or + TRegExpQuantifier(RegExp re, int start, int end) { re.qualifiedItem(start, end, _, _) } or + TRegExpSequence(RegExp re, int start, int end) { re.sequence(start, end) } or + TRegExpAlt(RegExp re, int start, int end) { re.alternation(start, end) } or + TRegExpCharacterClass(RegExp re, int start, int end) { re.charSet(start, end) } or + TRegExpCharacterRange(RegExp re, int start, int end) { re.charRange(_, start, _, _, end) } or + TRegExpGroup(RegExp re, int start, int end) { re.group(start, end) } or + TRegExpSpecialChar(RegExp re, int start, int end) { re.specialCharacter(start, end, _) } or + TRegExpNormalChar(RegExp re, int start, int end) { re.normalCharacter(start, end) } or + TRegExpBackRef(RegExp re, int start, int end) { re.backreference(start, end) } or + TRegExpNamedCharacterProperty(RegExp re, int start, int end) { + re.namedCharacterProperty(start, end, _) + } + +class RegExpQuantifier extends RegExpTerm, TRegExpQuantifier { + int part_end; + boolean maybe_empty; + boolean may_repeat_forever; + + RegExpQuantifier() { + this = TRegExpQuantifier(re, start, end) and + re.qualifiedPart(start, part_end, end, maybe_empty, may_repeat_forever) + } + + override RegExpTerm getChild(int i) { + i = 0 and + result.getRegExp() = re and + result.getStart() = start and + result.getEnd() = part_end + } + + predicate mayRepeatForever() { may_repeat_forever = true } + + string getQualifier() { result = re.getText().substring(part_end, end) } + + override string getAPrimaryQlClass() { result = "RegExpQuantifier" } +} + +class InfiniteRepetitionQuantifier extends RegExpQuantifier { + InfiniteRepetitionQuantifier() { this.mayRepeatForever() } + + override string getAPrimaryQlClass() { result = "InfiniteRepetitionQuantifier" } +} + +class RegExpStar extends InfiniteRepetitionQuantifier { + RegExpStar() { this.getQualifier().charAt(0) = "*" } + + override string getAPrimaryQlClass() { result = "RegExpStar" } +} + +class RegExpPlus extends InfiniteRepetitionQuantifier { + RegExpPlus() { this.getQualifier().charAt(0) = "+" } + + override string getAPrimaryQlClass() { result = "RegExpPlus" } +} + +class RegExpOpt extends RegExpQuantifier { + RegExpOpt() { this.getQualifier().charAt(0) = "?" } + + override string getAPrimaryQlClass() { result = "RegExpOpt" } +} + +class RegExpRange extends RegExpQuantifier { + string upper; + string lower; + + RegExpRange() { re.multiples(part_end, end, lower, upper) } + + string getUpper() { result = upper } + + string getLower() { result = lower } + + /** + * Gets the upper bound of the range, if any. + * + * If there is no upper bound, any number of repetitions is allowed. + * For a term of the form `r{lo}`, both the lower and the upper bound + * are `lo`. + */ + int getUpperBound() { result = this.getUpper().toInt() } + + /** Gets the lower bound of the range. */ + int getLowerBound() { result = this.getLower().toInt() } + + override string getAPrimaryQlClass() { result = "RegExpRange" } +} + +class RegExpSequence extends RegExpTerm, TRegExpSequence { + RegExpSequence() { + this = TRegExpSequence(re, start, end) and + exists(seqChild(re, start, end, 1)) // if a sequence does not have more than one element, it should be treated as that element instead. + } + + override RegExpTerm getChild(int i) { result = seqChild(re, start, end, i) } + + /** Gets the element preceding `element` in this sequence. */ + RegExpTerm previousElement(RegExpTerm element) { element = this.nextElement(result) } + + /** Gets the element following `element` in this sequence. */ + RegExpTerm nextElement(RegExpTerm element) { + exists(int i | + element = this.getChild(i) and + result = this.getChild(i + 1) + ) + } + + override string getAPrimaryQlClass() { result = "RegExpSequence" } +} + +pragma[nomagic] +private int seqChildEnd(RegExp re, int start, int end, int i) { + result = seqChild(re, start, end, i).getEnd() +} + +// moved out so we can use it in the charpred +private RegExpTerm seqChild(RegExp re, int start, int end, int i) { + re.sequence(start, end) and + ( + i = 0 and + result.getRegExp() = re and + result.getStart() = start and + exists(int itemEnd | + re.item(start, itemEnd) and + result.getEnd() = itemEnd + ) + or + i > 0 and + result.getRegExp() = re and + exists(int itemStart | itemStart = seqChildEnd(re, start, end, i - 1) | + result.getStart() = itemStart and + re.item(itemStart, result.getEnd()) + ) + ) +} + +class RegExpAlt extends RegExpTerm, TRegExpAlt { + RegExpAlt() { this = TRegExpAlt(re, start, end) } + + override RegExpTerm getChild(int i) { + i = 0 and + result.getRegExp() = re and + result.getStart() = start and + exists(int part_end | + re.alternationOption(start, end, start, part_end) and + result.getEnd() = part_end + ) + or + i > 0 and + result.getRegExp() = re and + exists(int part_start | + part_start = this.getChild(i - 1).getEnd() + 1 // allow for the | + | + result.getStart() = part_start and + re.alternationOption(start, end, part_start, result.getEnd()) + ) + } + + override string getAPrimaryQlClass() { result = "RegExpAlt" } +} + +class RegExpEscape extends RegExpNormalChar { + RegExpEscape() { re.escapedCharacter(start, end) } + + /** + * Gets the name of the escaped; for example, `w` for `\w`. + * TODO: Handle named escapes. + */ + override string getValue() { + this.isIdentityEscape() and result = this.getUnescaped() + or + this.getUnescaped() = "n" and result = "\n" + or + this.getUnescaped() = "r" and result = "\r" + or + this.getUnescaped() = "t" and result = "\t" + or + this.isUnicode() and + result = this.getUnicode() + } + + predicate isIdentityEscape() { not this.getUnescaped() in ["n", "r", "t"] } + + /** + * Gets the text for this escape. That is e.g. "\w". + */ + private string getText() { result = re.getText().substring(start, end) } + + /** + * Holds if this is a unicode escape. + */ + private predicate isUnicode() { this.getText().prefix(2) = ["\\u", "\\U"] } + + /** + * Gets the unicode char for this escape. + * E.g. for `\u0061` this returns "a". + */ + private string getUnicode() { + exists(int codepoint | codepoint = sum(this.getHexValueFromUnicode(_)) | + result = codepoint.toUnicode() + ) + } + + /** + * Gets int value for the `index`th char in the hex number of the unicode escape. + * E.g. for `\u0061` and `index = 2` this returns 96 (the number `6` interpreted as hex). + */ + private int getHexValueFromUnicode(int index) { + this.isUnicode() and + exists(string hex, string char | hex = this.getText().suffix(2) | + char = hex.charAt(index) and + result = 16.pow(hex.length() - index - 1) * toHex(char) + ) + } + + string getUnescaped() { result = this.getText().suffix(1) } + + override string getAPrimaryQlClass() { result = "RegExpEscape" } +} + +/** + * Gets the hex number for the `hex` char. + */ +private int toHex(string hex) { + hex = [0 .. 9].toString() and + result = hex.toInt() + or + result = 10 and hex = ["a", "A"] + or + result = 11 and hex = ["b", "B"] + or + result = 12 and hex = ["c", "C"] + or + result = 13 and hex = ["d", "D"] + or + result = 14 and hex = ["e", "E"] + or + result = 15 and hex = ["f", "F"] +} + +/** + * A character class escape in a regular expression. + * That is, an escaped character that denotes multiple characters. + * + * Examples: + * + * ``` + * \w + * \S + * ``` + */ +class RegExpCharacterClassEscape extends RegExpEscape { + RegExpCharacterClassEscape() { this.getValue() in ["d", "D", "s", "S", "w", "W", "h", "H"] } + + /** Gets the name of the character class; for example, `w` for `\w`. */ + // override string getValue() { result = value } + override RegExpTerm getChild(int i) { none() } + + override string getAPrimaryQlClass() { result = "RegExpCharacterClassEscape" } +} + +/** + * A character class. + * + * Examples: + * + * ```rb + * /[a-fA-F0-9]/ + * /[^abc]/ + * ``` + */ +class RegExpCharacterClass extends RegExpTerm, TRegExpCharacterClass { + RegExpCharacterClass() { this = TRegExpCharacterClass(re, start, end) } + + predicate isInverted() { re.getChar(start + 1) = "^" } + + predicate isUniversalClass() { + // [^] + this.isInverted() and not exists(this.getAChild()) + or + // [\w\W] and similar + not this.isInverted() and + exists(string cce1, string cce2 | + cce1 = this.getAChild().(RegExpCharacterClassEscape).getValue() and + cce2 = this.getAChild().(RegExpCharacterClassEscape).getValue() + | + cce1 != cce2 and cce1.toLowerCase() = cce2.toLowerCase() + ) + } + + override RegExpTerm getChild(int i) { + i = 0 and + result.getRegExp() = re and + exists(int itemStart, int itemEnd | + result.getStart() = itemStart and + re.charSetStart(start, itemStart) and + re.charSetChild(start, itemStart, itemEnd) and + result.getEnd() = itemEnd + ) + or + i > 0 and + result.getRegExp() = re and + exists(int itemStart | itemStart = this.getChild(i - 1).getEnd() | + result.getStart() = itemStart and + re.charSetChild(start, itemStart, result.getEnd()) + ) + } + + override string getAPrimaryQlClass() { result = "RegExpCharacterClass" } +} + +class RegExpCharacterRange extends RegExpTerm, TRegExpCharacterRange { + int lower_end; + int upper_start; + + RegExpCharacterRange() { + this = TRegExpCharacterRange(re, start, end) and + re.charRange(_, start, lower_end, upper_start, end) + } + + predicate isRange(string lo, string hi) { + lo = re.getText().substring(start, lower_end) and + hi = re.getText().substring(upper_start, end) + } + + override RegExpTerm getChild(int i) { + i = 0 and + result.getRegExp() = re and + result.getStart() = start and + result.getEnd() = lower_end + or + i = 1 and + result.getRegExp() = re and + result.getStart() = upper_start and + result.getEnd() = end + } + + override string getAPrimaryQlClass() { result = "RegExpCharacterRange" } +} + +class RegExpNormalChar extends RegExpTerm, TRegExpNormalChar { + RegExpNormalChar() { this = TRegExpNormalChar(re, start, end) } + + predicate isCharacter() { any() } + + string getValue() { result = re.getText().substring(start, end) } + + override RegExpTerm getChild(int i) { none() } + + override string getAPrimaryQlClass() { result = "RegExpNormalChar" } +} + +class RegExpConstant extends RegExpTerm { + string value; + + RegExpConstant() { + this = TRegExpNormalChar(re, start, end) and + not this instanceof RegExpCharacterClassEscape and + // exclude chars in qualifiers + // TODO: push this into regex library + not exists(int qstart, int qend | re.qualifiedPart(_, qstart, qend, _, _) | + qstart <= start and end <= qend + ) and + value = this.(RegExpNormalChar).getValue() + or + this = TRegExpSpecialChar(re, start, end) and + re.inCharSet(start) and + value = this.(RegExpSpecialChar).getChar() + } + + predicate isCharacter() { any() } + + string getValue() { result = value } + + override RegExpTerm getChild(int i) { none() } + + override string getAPrimaryQlClass() { result = "RegExpConstant" } +} + +class RegExpGroup extends RegExpTerm, TRegExpGroup { + RegExpGroup() { this = TRegExpGroup(re, start, end) } + + /** + * Gets the index of this capture group within the enclosing regular + * expression literal. + * + * For example, in the regular expression `/((a?).)(?:b)/`, the + * group `((a?).)` has index 1, the group `(a?)` nested inside it + * has index 2, and the group `(?:b)` has no index, since it is + * not a capture group. + */ + int getNumber() { result = re.getGroupNumber(start, end) } + + /** Holds if this is a named capture group. */ + predicate isNamed() { exists(this.getName()) } + + /** Gets the name of this capture group, if any. */ + string getName() { result = re.getGroupName(start, end) } + + predicate isCharacter() { any() } + + string getValue() { result = re.getText().substring(start, end) } + + override RegExpTerm getChild(int i) { + result.getRegExp() = re and + i = 0 and + re.groupContents(start, end, result.getStart(), result.getEnd()) + } + + override string getAPrimaryQlClass() { result = "RegExpGroup" } +} + +class RegExpSpecialChar extends RegExpTerm, TRegExpSpecialChar { + string char; + + RegExpSpecialChar() { + this = TRegExpSpecialChar(re, start, end) and + re.specialCharacter(start, end, char) + } + + predicate isCharacter() { any() } + + string getChar() { result = char } + + override RegExpTerm getChild(int i) { none() } + + override string getAPrimaryQlClass() { result = "RegExpSpecialChar" } +} + +class RegExpDot extends RegExpSpecialChar { + RegExpDot() { this.getChar() = "." } + + override string getAPrimaryQlClass() { result = "RegExpDot" } +} + +class RegExpDollar extends RegExpSpecialChar { + RegExpDollar() { this.getChar() = ["$", "\\Z", "\\z"] } + + override string getAPrimaryQlClass() { result = "RegExpDollar" } +} + +class RegExpCaret extends RegExpSpecialChar { + RegExpCaret() { this.getChar() = ["^", "\\A"] } + + override string getAPrimaryQlClass() { result = "RegExpCaret" } +} + +class RegExpZeroWidthMatch extends RegExpGroup { + RegExpZeroWidthMatch() { re.zeroWidthMatch(start, end) } + + override predicate isCharacter() { any() } + + override RegExpTerm getChild(int i) { none() } + + override string getAPrimaryQlClass() { result = "RegExpZeroWidthMatch" } +} + +/** + * A zero-width lookahead or lookbehind assertion. + * + * Examples: + * + * ``` + * (?=\w) + * (?!\n) + * (?<=\.) + * (?<!\\) + * ``` + */ +class RegExpSubPattern extends RegExpZeroWidthMatch { + RegExpSubPattern() { not re.emptyGroup(start, end) } + + /** Gets the lookahead term. */ + RegExpTerm getOperand() { + exists(int in_start, int in_end | re.groupContents(start, end, in_start, in_end) | + result.getRegExp() = re and + result.getStart() = in_start and + result.getEnd() = in_end + ) + } +} + +abstract class RegExpLookahead extends RegExpSubPattern { } + +class RegExpPositiveLookahead extends RegExpLookahead { + RegExpPositiveLookahead() { re.positiveLookaheadAssertionGroup(start, end) } + + override string getAPrimaryQlClass() { result = "RegExpPositiveLookahead" } +} + +class RegExpNegativeLookahead extends RegExpLookahead { + RegExpNegativeLookahead() { re.negativeLookaheadAssertionGroup(start, end) } + + override string getAPrimaryQlClass() { result = "RegExpNegativeLookahead" } +} + +abstract class RegExpLookbehind extends RegExpSubPattern { } + +class RegExpPositiveLookbehind extends RegExpLookbehind { + RegExpPositiveLookbehind() { re.positiveLookbehindAssertionGroup(start, end) } + + override string getAPrimaryQlClass() { result = "RegExpPositiveLookbehind" } +} + +class RegExpNegativeLookbehind extends RegExpLookbehind { + RegExpNegativeLookbehind() { re.negativeLookbehindAssertionGroup(start, end) } + + override string getAPrimaryQlClass() { result = "RegExpNegativeLookbehind" } +} + +class RegExpBackRef extends RegExpTerm, TRegExpBackRef { + RegExpBackRef() { this = TRegExpBackRef(re, start, end) } + + /** + * Gets the number of the capture group this back reference refers to, if any. + */ + int getNumber() { result = re.getBackRefNumber(start, end) } + + /** + * Gets the name of the capture group this back reference refers to, if any. + */ + string getName() { result = re.getBackRefName(start, end) } + + /** Gets the capture group this back reference refers to. */ + RegExpGroup getGroup() { + result.getLiteral() = this.getLiteral() and + ( + result.getNumber() = this.getNumber() or + result.getName() = this.getName() + ) + } + + override RegExpTerm getChild(int i) { none() } + + override string getAPrimaryQlClass() { result = "RegExpBackRef" } +} + +/** + * A named character property. For example, the POSIX bracket expression + * `[[:digit:]]`. + */ +class RegExpNamedCharacterProperty extends RegExpTerm, TRegExpNamedCharacterProperty { + RegExpNamedCharacterProperty() { this = TRegExpNamedCharacterProperty(re, start, end) } + + override RegExpTerm getChild(int i) { none() } + + override string getAPrimaryQlClass() { result = "RegExpNamedCharacterProperty" } + + /** + * Gets the property name. For example, in `\p{Space}`, the result is + * `"Space"`. + */ + string getName() { result = re.getCharacterPropertyName(start, end) } + + /** + * Holds if the property is inverted. For example, it holds for `\p{^Digit}`, + * which matches non-digits. + */ + predicate isInverted() { re.namedCharacterPropertyIsInverted(start, end) } +} + +RegExpTerm getParsedRegExp(AST::RegExpLiteral re) { + result.getRegExp() = re and result.isRootTerm() +} diff --git a/ruby/ql/lib/codeql/ruby/regexp/SuperlinearBackTracking.qll b/ruby/ql/lib/codeql/ruby/regexp/SuperlinearBackTracking.qll new file mode 100644 index 00000000000..2b42165ff7e --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/regexp/SuperlinearBackTracking.qll @@ -0,0 +1,420 @@ +/** + * Provides classes for working with regular expressions that can + * perform backtracking in superlinear time. + */ + +import ReDoSUtil + +/* + * This module implements the analysis described in the paper: + * Valentin Wustholz, Oswaldo Olivo, Marijn J. H. Heule, and Isil Dillig: + * Static Detection of DoS Vulnerabilities in + * Programs that use Regular Expressions + * (Extended Version). + * (https://arxiv.org/pdf/1701.04045.pdf) + * + * Theorem 3 from the paper describes the basic idea. + * + * The following explains the idea using variables and predicate names that are used in the implementation: + * We consider a pair of repetitions, which we will call `pivot` and `succ`. + * + * We create a product automaton of 3-tuples of states (see `StateTuple`). + * There exists a transition `(a,b,c) -> (d,e,f)` in the product automaton + * iff there exists three transitions in the NFA `a->d, b->e, c->f` where those three + * transitions all match a shared character `char`. (see `getAThreewayIntersect`) + * + * We start a search in the product automaton at `(pivot, pivot, succ)`, + * and search for a series of transitions (a `Trace`), such that we end + * at `(pivot, succ, succ)` (see `isReachableFromStartTuple`). + * + * For example, consider the regular expression `/^\d*5\w*$/`. + * The search will start at the tuple `(\d*, \d*, \w*)` and search + * for a path to `(\d*, \w*, \w*)`. + * This path exists, and consists of a single transition in the product automaton, + * where the three corresponding NFA edges all match the character `"5"`. + * + * The start-state in the NFA has an any-transition to itself, this allows us to + * flag regular expressions such as `/a*$/` - which does not have a start anchor - + * and can thus start matching anywhere. + * + * The implementation is not perfect. + * It has the same suffix detection issue as the `js/redos` query, which can cause false positives. + * It also doesn't find all transitions in the product automaton, which can cause false negatives. + */ + +/** + * An instantiaion of `ReDoSConfiguration` for superlinear ReDoS. + */ +class SuperLinearReDoSConfiguration extends ReDoSConfiguration { + SuperLinearReDoSConfiguration() { this = "SuperLinearReDoSConfiguration" } + + override predicate isReDoSCandidate(State state, string pump) { isPumpable(_, state, pump) } +} + +/** + * Gets any root (start) state of a regular expression. + */ +private State getRootState() { result = mkMatch(any(RegExpRoot r)) } + +private newtype TStateTuple = + MkStateTuple(State q1, State q2, State q3) { + // starts at (pivot, pivot, succ) + isStartLoops(q1, q3) and q1 = q2 + or + step(_, _, _, _, q1, q2, q3) and FeasibleTuple::isFeasibleTuple(q1, q2, q3) + } + +/** + * A state in the product automaton. + * The product automaton contains 3-tuples of states. + * + * We lazily only construct those states that we are actually + * going to need. + * Either a start state `(pivot, pivot, succ)`, or a state + * where there exists a transition from an already existing state. + * + * The exponential variant of this query (`js/redos`) uses an optimization + * trick where `q1 <= q2`. This trick cannot be used here as the order + * of the elements matter. + */ +class StateTuple extends TStateTuple { + State q1; + State q2; + State q3; + + StateTuple() { this = MkStateTuple(q1, q2, q3) } + + /** + * Gest a string repesentation of this tuple. + */ + string toString() { result = "(" + q1 + ", " + q2 + ", " + q3 + ")" } + + /** + * Holds if this tuple is `(r1, r2, r3)`. + */ + pragma[noinline] + predicate isTuple(State r1, State r2, State r3) { r1 = q1 and r2 = q2 and r3 = q3 } +} + +/** + * A module for determining feasible tuples for the product automaton. + * + * The implementation is split into many predicates for performance reasons. + */ +private module FeasibleTuple { + /** + * Holds if the tuple `(r1, r2, r3)` might be on path from a start-state to an end-state in the product automaton. + */ + pragma[inline] + predicate isFeasibleTuple(State r1, State r2, State r3) { + // The first element is either inside a repetition (or the start state itself) + isRepetitionOrStart(r1) and + // The last element is inside a repetition + stateInsideRepetition(r3) and + // The states are reachable in the NFA in the order r1 -> r2 -> r3 + delta+(r1) = r2 and + delta+(r2) = r3 and + // The first element can reach a beginning (the "pivot" state in a `(pivot, succ)` pair). + canReachABeginning(r1) and + // The last element can reach a target (the "succ" state in a `(pivot, succ)` pair). + canReachATarget(r3) + } + + /** + * Holds if `s` is either inside a repetition, or is the start state (which is a repetition). + */ + pragma[noinline] + private predicate isRepetitionOrStart(State s) { stateInsideRepetition(s) or s = getRootState() } + + /** + * Holds if state `s` might be inside a backtracking repetition. + */ + pragma[noinline] + private predicate stateInsideRepetition(State s) { + s.getRepr().getParent*() instanceof InfiniteRepetitionQuantifier + } + + /** + * Holds if there exists a path in the NFA from `s` to a "pivot" state + * (from a `(pivot, succ)` pair that starts the search). + */ + pragma[noinline] + private predicate canReachABeginning(State s) { + delta+(s) = any(State pivot | isStartLoops(pivot, _)) + } + + /** + * Holds if there exists a path in the NFA from `s` to a "succ" state + * (from a `(pivot, succ)` pair that starts the search). + */ + pragma[noinline] + private predicate canReachATarget(State s) { delta+(s) = any(State succ | isStartLoops(_, succ)) } +} + +/** + * Holds if `pivot` and `succ` are a pair of loops that could be the beginning of a quadratic blowup. + * + * There is a slight implementation difference compared to the paper: this predicate requires that `pivot != succ`. + * The case where `pivot = succ` causes exponential backtracking and is handled by the `js/redos` query. + */ +predicate isStartLoops(State pivot, State succ) { + pivot != succ and + succ.getRepr() instanceof InfiniteRepetitionQuantifier and + delta+(pivot) = succ and + ( + pivot.getRepr() instanceof InfiniteRepetitionQuantifier + or + pivot = mkMatch(any(RegExpRoot root)) + ) +} + +/** + * Gets a state for which there exists a transition in the NFA from `s'. + */ +State delta(State s) { delta(s, _, result) } + +/** + * Holds if there are transitions from the components of `q` to the corresponding + * components of `r` labelled with `s1`, `s2`, and `s3`, respectively. + */ +pragma[noinline] +predicate step(StateTuple q, InputSymbol s1, InputSymbol s2, InputSymbol s3, StateTuple r) { + exists(State r1, State r2, State r3 | + step(q, s1, s2, s3, r1, r2, r3) and r = MkStateTuple(r1, r2, r3) + ) +} + +/** + * Holds if there are transitions from the components of `q` to `r1`, `r2`, and `r3 + * labelled with `s1`, `s2`, and `s3`, respectively. + */ +pragma[noopt] +predicate step( + StateTuple q, InputSymbol s1, InputSymbol s2, InputSymbol s3, State r1, State r2, State r3 +) { + exists(State q1, State q2, State q3 | q.isTuple(q1, q2, q3) | + deltaClosed(q1, s1, r1) and + deltaClosed(q2, s2, r2) and + deltaClosed(q3, s3, r3) and + // use noopt to force the join on `getAThreewayIntersect` to happen last. + exists(getAThreewayIntersect(s1, s2, s3)) + ) +} + +/** + * Gets a char that is matched by all the edges `s1`, `s2`, and `s3`. + * + * The result is not complete, and might miss some combination of edges that share some character. + */ +pragma[noinline] +string getAThreewayIntersect(InputSymbol s1, InputSymbol s2, InputSymbol s3) { + result = minAndMaxIntersect(s1, s2) and result = [intersect(s2, s3), intersect(s1, s3)] + or + result = minAndMaxIntersect(s1, s3) and result = [intersect(s2, s3), intersect(s1, s2)] + or + result = minAndMaxIntersect(s2, s3) and result = [intersect(s1, s2), intersect(s1, s3)] +} + +/** + * Gets the minimum and maximum characters that intersect between `a` and `b`. + * This predicate is used to limit the size of `getAThreewayIntersect`. + */ +pragma[noinline] +string minAndMaxIntersect(InputSymbol a, InputSymbol b) { + result = [min(intersect(a, b)), max(intersect(a, b))] +} + +private newtype TTrace = + Nil() or + Step(InputSymbol s1, InputSymbol s2, InputSymbol s3, TTrace t) { + exists(StateTuple p | + isReachableFromStartTuple(_, _, p, t, _) and + step(p, s1, s2, s3, _) + ) + or + exists(State pivot, State succ | isStartLoops(pivot, succ) | + t = Nil() and step(MkStateTuple(pivot, pivot, succ), s1, s2, s3, _) + ) + } + +/** + * A list of tuples of input symbols that describe a path in the product automaton + * starting from some start state. + */ +class Trace extends TTrace { + /** + * Gets a string representation of this Trace that can be used for debug purposes. + */ + string toString() { + this = Nil() and result = "Nil()" + or + exists(InputSymbol s1, InputSymbol s2, InputSymbol s3, Trace t | this = Step(s1, s2, s3, t) | + result = "Step(" + s1 + ", " + s2 + ", " + s3 + ", " + t + ")" + ) + } +} + +/** + * Gets a string corresponding to the trace `t`. + */ +string concretise(Trace t) { + t = Nil() and result = "" + or + exists(InputSymbol s1, InputSymbol s2, InputSymbol s3, Trace rest | t = Step(s1, s2, s3, rest) | + result = concretise(rest) + getAThreewayIntersect(s1, s2, s3) + ) +} + +/** + * Holds if there exists a transition from `r` to `q` in the product automaton. + * Notice that the arguments are flipped, and thus the direction is backwards. + */ +pragma[noinline] +predicate tupleDeltaBackwards(StateTuple q, StateTuple r) { step(r, _, _, _, q) } + +/** + * Holds if `tuple` is an end state in our search. + * That means there exists a pair of loops `(pivot, succ)` such that `tuple = (pivot, succ, succ)`. + */ +predicate isEndTuple(StateTuple tuple) { tuple = getAnEndTuple(_, _) } + +/** + * Gets the minimum length of a path from `r` to some an end state `end`. + * + * The implementation searches backwards from the end-tuple. + * This approach was chosen because it is way more efficient if the first predicate given to `shortestDistances` is small. + * The `end` argument must always be an end state. + */ +int distBackFromEnd(StateTuple r, StateTuple end) = + shortestDistances(isEndTuple/1, tupleDeltaBackwards/2)(end, r, result) + +/** + * Holds if there exists a pair of repetitions `(pivot, succ)` in the regular expression such that: + * `tuple` is reachable from `(pivot, pivot, succ)` in the product automaton, + * and there is a distance of `dist` from `tuple` to the nearest end-tuple `(pivot, succ, succ)`, + * and a path from a start-state to `tuple` follows the transitions in `trace`. + */ +predicate isReachableFromStartTuple(State pivot, State succ, StateTuple tuple, Trace trace, int dist) { + // base case. The first step is inlined to start the search after all possible 1-steps, and not just the ones with the shortest path. + exists(InputSymbol s1, InputSymbol s2, InputSymbol s3, State q1, State q2, State q3 | + isStartLoops(pivot, succ) and + step(MkStateTuple(pivot, pivot, succ), s1, s2, s3, tuple) and + tuple = MkStateTuple(q1, q2, q3) and + trace = Step(s1, s2, s3, Nil()) and + dist = distBackFromEnd(tuple, MkStateTuple(pivot, succ, succ)) + ) + or + // recursive case + exists(StateTuple p, Trace v, InputSymbol s1, InputSymbol s2, InputSymbol s3 | + isReachableFromStartTuple(pivot, succ, p, v, dist + 1) and + dist = isReachableFromStartTupleHelper(pivot, succ, tuple, p, s1, s2, s3) and + trace = Step(s1, s2, s3, v) + ) +} + +/** + * Helper predicate for the recursive case in `isReachableFromStartTuple`. + */ +pragma[noinline] +private int isReachableFromStartTupleHelper( + State pivot, State succ, StateTuple r, StateTuple p, InputSymbol s1, InputSymbol s2, + InputSymbol s3 +) { + result = distBackFromEnd(r, MkStateTuple(pivot, succ, succ)) and + step(p, s1, s2, s3, r) +} + +/** + * Gets the tuple `(pivot, succ, succ)` from the product automaton. + */ +StateTuple getAnEndTuple(State pivot, State succ) { + isStartLoops(pivot, succ) and + result = MkStateTuple(pivot, succ, succ) +} + +/** + * Holds if matching repetitions of `pump` can: + * 1) Transition from `pivot` back to `pivot`. + * 2) Transition from `pivot` to `succ`. + * 3) Transition from `succ` to `succ`. + * + * From theorem 3 in the paper linked in the top of this file we can therefore conclude that + * the regular expression has polynomial backtracking - if a rejecting suffix exists. + * + * This predicate is used by `SuperLinearReDoSConfiguration`, and the final results are + * available in the `hasReDoSResult` predicate. + */ +predicate isPumpable(State pivot, State succ, string pump) { + exists(StateTuple q, Trace t | + isReachableFromStartTuple(pivot, succ, q, t, _) and + q = getAnEndTuple(pivot, succ) and + pump = concretise(t) + ) +} + +/** + * Holds if repetitions of `pump` at `t` will cause polynomial backtracking. + */ +predicate polynimalReDoS(RegExpTerm t, string pump, string prefixMsg, RegExpTerm prev) { + exists(State s, State pivot | + hasReDoSResult(t, pump, s, prefixMsg) and + isPumpable(pivot, s, _) and + prev = pivot.getRepr() + ) +} + +/** + * Gets a message for why `term` can cause polynomial backtracking. + */ +string getReasonString(RegExpTerm term, string pump, string prefixMsg, RegExpTerm prev) { + polynimalReDoS(term, pump, prefixMsg, prev) and + result = + "Strings " + prefixMsg + "with many repetitions of '" + pump + + "' can start matching anywhere after the start of the preceeding " + prev +} + +/** + * A term that may cause a regular expression engine to perform a + * polynomial number of match attempts, relative to the input length. + */ +class PolynomialBackTrackingTerm extends InfiniteRepetitionQuantifier { + string reason; + string pump; + string prefixMsg; + RegExpTerm prev; + + PolynomialBackTrackingTerm() { + reason = getReasonString(this, pump, prefixMsg, prev) and + // there might be many reasons for this term to have polynomial backtracking - we pick the shortest one. + reason = min(string msg | msg = getReasonString(this, _, _, _) | msg order by msg.length(), msg) + } + + /** + * Holds if all non-empty successors to the polynomial backtracking term matches the end of the line. + */ + predicate isAtEndLine() { + forall(RegExpTerm succ | this.getSuccessor+() = succ and not matchesEpsilon(succ) | + succ instanceof RegExpDollar + ) + } + + /** + * Gets the string that should be repeated to cause this regular expression to perform polynomially. + */ + string getPumpString() { result = pump } + + /** + * Gets a message for which prefix a matching string must start with for this term to cause polynomial backtracking. + */ + string getPrefixMessage() { result = prefixMsg } + + /** + * Gets a predecessor to `this`, which also loops on the pump string, and thereby causes polynomial backtracking. + */ + RegExpTerm getPreviousLoop() { result = prev } + + /** + * Gets the reason for the number of match attempts. + */ + string getReason() { result = reason } +} diff --git a/ruby/ql/lib/codeql/ruby/security/CodeInjectionCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/CodeInjectionCustomizations.qll new file mode 100644 index 00000000000..4baceba42db --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/security/CodeInjectionCustomizations.qll @@ -0,0 +1,40 @@ +private import ruby +private import codeql.ruby.DataFlow +private import codeql.ruby.Concepts +private import codeql.ruby.Frameworks +private import codeql.ruby.dataflow.RemoteFlowSources +private import codeql.ruby.dataflow.BarrierGuards + +/** + * Provides default sources, sinks and sanitizers for detecting + * "Code injection" vulnerabilities, as well as extension points for + * adding your own. + */ +module CodeInjection { + /** + * A data flow source for "Code injection" vulnerabilities. + */ + abstract class Source extends DataFlow::Node { } + + /** + * A data flow sink for "Code injection" vulnerabilities. + */ + abstract class Sink extends DataFlow::Node { } + + /** + * A sanitizer guard for "Code injection" vulnerabilities. + */ + abstract class SanitizerGuard extends DataFlow::BarrierGuard { } + + /** + * A source of remote user input, considered as a flow source. + */ + class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + + /** + * A call that evaluates its arguments as Ruby code, considered as a flow sink. + */ + class CodeExecutionAsSink extends Sink { + CodeExecutionAsSink() { this = any(CodeExecution c).getCode() } + } +} diff --git a/ruby/ql/lib/codeql/ruby/security/CodeInjectionQuery.qll b/ruby/ql/lib/codeql/ruby/security/CodeInjectionQuery.qll new file mode 100644 index 00000000000..95e08a82dc3 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/security/CodeInjectionQuery.qll @@ -0,0 +1,29 @@ +/** + * Provides a taint-tracking configuration for detecting "Code injection" vulnerabilities. + * + * Note, for performance reasons: only import this file if `Configuration` is needed, + * otherwise `CodeInjectionCustomizations` should be imported instead. + */ + +import codeql.ruby.DataFlow::DataFlow::PathGraph +import codeql.ruby.DataFlow +import codeql.ruby.TaintTracking +import CodeInjectionCustomizations::CodeInjection +import codeql.ruby.dataflow.BarrierGuards + +/** + * A taint-tracking configuration for detecting "Code injection" vulnerabilities. + */ +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "CodeInjection" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + + override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { + guard instanceof SanitizerGuard or + guard instanceof StringConstCompare or + guard instanceof StringConstArrayInclusionCall + } +} diff --git a/ruby/ql/lib/codeql/ruby/security/CommandInjectionCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/CommandInjectionCustomizations.qll new file mode 100644 index 00000000000..b39455195be --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/security/CommandInjectionCustomizations.qll @@ -0,0 +1,54 @@ +/** + * Provides default sources, sinks and sanitizers for reasoning about + * command-injection vulnerabilities, as well as extension points for + * adding your own. + */ + +private import codeql.ruby.DataFlow +private import codeql.ruby.dataflow.RemoteFlowSources +private import codeql.ruby.Concepts +private import codeql.ruby.Frameworks +private import codeql.ruby.ApiGraphs + +module CommandInjection { + /** + * A data flow source for command-injection vulnerabilities. + */ + abstract class Source extends DataFlow::Node { + /** Gets a string that describes the type of this remote flow source. */ + abstract string getSourceType(); + } + + /** + * A data flow sink for command-injection vulnerabilities. + */ + abstract class Sink extends DataFlow::Node { } + + /** + * A sanitizer for command-injection vulnerabilities. + */ + abstract class Sanitizer extends DataFlow::Node { } + + /** A source of remote user input, considered as a flow source for command injection. */ + class RemoteFlowSourceAsSource extends Source { + RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource } + + override string getSourceType() { result = "a user-provided value" } + } + + /** + * A command argument to a function that initiates an operating system command. + */ + class SystemCommandExecutionSink extends Sink { + SystemCommandExecutionSink() { exists(SystemCommandExecution c | c.isShellInterpreted(this)) } + } + + /** + * A call to `Shellwords.escape` or `Shellwords.shellescape` sanitizes its input. + */ + class ShellwordsEscapeAsSanitizer extends Sanitizer { + ShellwordsEscapeAsSanitizer() { + this = API::getTopLevelMember("Shellwords").getAMethodCall(["escape", "shellescape"]) + } + } +} diff --git a/ruby/ql/lib/codeql/ruby/security/CommandInjectionQuery.qll b/ruby/ql/lib/codeql/ruby/security/CommandInjectionQuery.qll new file mode 100644 index 00000000000..25460ad65df --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/security/CommandInjectionQuery.qll @@ -0,0 +1,32 @@ +/** + * Provides a taint tracking configuration for reasoning about + * command-injection vulnerabilities (CWE-078). + * + * Note, for performance reasons: only import this file if + * `CommandInjection::Configuration` is needed, otherwise + * `CommandInjectionCustomizations` should be imported instead. + */ + +import ruby +import codeql.ruby.TaintTracking +import CommandInjectionCustomizations::CommandInjection +import codeql.ruby.DataFlow +import codeql.ruby.dataflow.BarrierGuards + +/** + * A taint-tracking configuration for reasoning about command-injection vulnerabilities. + */ +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "CommandInjection" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + + override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer } + + override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { + guard instanceof StringConstCompare or + guard instanceof StringConstArrayInclusionCall + } +} diff --git a/ruby/ql/lib/codeql/ruby/security/PathInjectionCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/PathInjectionCustomizations.qll new file mode 100644 index 00000000000..a2c3d907c5f --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/security/PathInjectionCustomizations.qll @@ -0,0 +1,54 @@ +/** + * Provides default sources, sinks and sanitizers for reasoning about + * path injection vulnerabilities, as well as extension points for + * adding your own. + */ + +private import ruby +private import codeql.ruby.ApiGraphs +private import codeql.ruby.CFG +private import codeql.ruby.Concepts +private import codeql.ruby.DataFlow +private import codeql.ruby.dataflow.BarrierGuards +private import codeql.ruby.dataflow.RemoteFlowSources + +module PathInjection { + /** + * A data flow source for path injection vulnerabilities. + */ + abstract class Source extends DataFlow::Node { } + + /** + * A data flow sink for path injection vulnerabilities. + */ + abstract class Sink extends DataFlow::Node { } + + /** + * A sanitizer guard for path injection vulnerabilities. + */ + abstract class SanitizerGuard extends DataFlow::BarrierGuard { } + + /** + * A source of remote user input, considered as a flow source. + */ + class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + + /** + * A file system access, considered as a flow sink. + */ + class FileSystemAccessAsSink extends Sink { + FileSystemAccessAsSink() { this = any(FileSystemAccess e).getAPathArgument() } + } + + /** + * A comparison with a constant string, considered as a sanitizer-guard. + */ + class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { } + + /** + * An inclusion check against an array of constant strings, considered as a + * sanitizer-guard. + */ + class StringConstArrayInclusionCallAsSanitizerGuard extends SanitizerGuard, + StringConstArrayInclusionCall { } +} diff --git a/ruby/ql/lib/codeql/ruby/security/PathInjectionQuery.qll b/ruby/ql/lib/codeql/ruby/security/PathInjectionQuery.qll new file mode 100644 index 00000000000..1901486c498 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/security/PathInjectionQuery.qll @@ -0,0 +1,31 @@ +/** + * Provides a taint tracking configuration for reasoning about + * path injection vulnerabilities. + * + * Note, for performance reasons: only import this file if + * `PathInjection::Configuration` is needed, otherwise + * `PathInjectionCustomizations` should be imported instead. + */ + +import PathInjectionCustomizations +private import codeql.ruby.Concepts +private import codeql.ruby.DataFlow +private import codeql.ruby.TaintTracking + +/** + * A taint-tracking configuration for reasoning about path injection + * vulnerabilities. + */ +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "PathInjection" } + + override predicate isSource(DataFlow::Node source) { source instanceof PathInjection::Source } + + override predicate isSink(DataFlow::Node sink) { sink instanceof PathInjection::Sink } + + override predicate isSanitizer(DataFlow::Node node) { node instanceof Path::PathSanitization } + + override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { + guard instanceof PathInjection::SanitizerGuard + } +} diff --git a/ruby/ql/lib/codeql/ruby/security/ReflectedXSSQuery.qll b/ruby/ql/lib/codeql/ruby/security/ReflectedXSSQuery.qll new file mode 100644 index 00000000000..60e152a06fc --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/security/ReflectedXSSQuery.qll @@ -0,0 +1,39 @@ +/** + * Provides a taint-tracking configuration for detecting "reflected server-side cross-site scripting" vulnerabilities. + * + * Note, for performance reasons: only import this file if + * `ReflectedXSS::Configuration` is needed, otherwise + * `XSS::ReflectedXSS` should be imported instead. + */ + +private import ruby +import codeql.ruby.DataFlow +import codeql.ruby.TaintTracking + +/** + * Provides a taint-tracking configuration for detecting "reflected server-side cross-site scripting" vulnerabilities. + */ +module ReflectedXSS { + import XSS::ReflectedXSS + + /** + * A taint-tracking configuration for detecting "reflected server-side cross-site scripting" vulnerabilities. + */ + class Configuration extends TaintTracking::Configuration { + Configuration() { this = "ReflectedXSS" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + + override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer } + + override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { + guard instanceof SanitizerGuard + } + + override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + isAdditionalXSSTaintStep(node1, node2) + } + } +} diff --git a/ruby/ql/lib/codeql/ruby/security/StoredXSSQuery.qll b/ruby/ql/lib/codeql/ruby/security/StoredXSSQuery.qll new file mode 100644 index 00000000000..2a089050e5a --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/security/StoredXSSQuery.qll @@ -0,0 +1,40 @@ +/** + * Provides a taint-tracking configuration for reasoning about stored + * cross-site scripting vulnerabilities. + * + * Note, for performance reasons: only import this file if + * `StoredXSS::Configuration` is needed, otherwise + * `XSS::StoredXSS` should be imported instead. + */ + +import ruby +import codeql.ruby.DataFlow +import codeql.ruby.TaintTracking + +module StoredXSS { + import XSS::StoredXSS + + /** + * A taint-tracking configuration for reasoning about Stored XSS. + */ + class Configuration extends TaintTracking::Configuration { + Configuration() { this = "StoredXss" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + + override predicate isSanitizer(DataFlow::Node node) { + super.isSanitizer(node) or + node instanceof Sanitizer + } + + override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { + guard instanceof SanitizerGuard + } + + override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + isAdditionalXSSTaintStep(node1, node2) + } + } +} diff --git a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll new file mode 100644 index 00000000000..0e39e053b2a --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll @@ -0,0 +1,190 @@ +/** + * Provides default sources, sinks and sanitizers for reasoning about unsafe + * deserialization, as well as extension points for adding your own. + */ + +private import ruby +private import codeql.ruby.ApiGraphs +private import codeql.ruby.CFG +private import codeql.ruby.DataFlow +private import codeql.ruby.dataflow.RemoteFlowSources + +module UnsafeDeserialization { + /** + * A data flow source for unsafe deserialization vulnerabilities. + */ + abstract class Source extends DataFlow::Node { } + + /** + * A data flow sink for unsafe deserialization vulnerabilities. + */ + abstract class Sink extends DataFlow::Node { } + + /** + * A sanitizer for unsafe deserialization vulnerabilities. + */ + abstract class Sanitizer extends DataFlow::Node { } + + /** + * Additional taint steps for "unsafe deserialization" vulnerabilities. + */ + predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) { + base64DecodeTaintStep(fromNode, toNode) + } + + /** A source of remote user input, considered as a flow source for unsafe deserialization. */ + class RemoteFlowSourceAsSource extends Source { + RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource } + } + + /** + * An argument in a call to `Marshal.load` or `Marshal.restore`, considered a + * sink for unsafe deserialization. + */ + class MarshalLoadOrRestoreArgument extends Sink { + MarshalLoadOrRestoreArgument() { + this = API::getTopLevelMember("Marshal").getAMethodCall(["load", "restore"]).getArgument(0) + } + } + + /** + * An argument in a call to `YAML.load`, considered a sink for unsafe + * deserialization. + */ + class YamlLoadArgument extends Sink { + YamlLoadArgument() { + this = API::getTopLevelMember("YAML").getAMethodCall("load").getArgument(0) + } + } + + /** + * An argument in a call to `JSON.load` or `JSON.restore`, considered a sink + * for unsafe deserialization. + */ + class JsonLoadArgument extends Sink { + JsonLoadArgument() { + this = API::getTopLevelMember("JSON").getAMethodCall(["load", "restore"]).getArgument(0) + } + } + + private string getAKnownOjModeName(boolean isSafe) { + result = ["compat", "custom", "json", "null", "rails", "strict", "wab"] and isSafe = true + or + result = "object" and isSafe = false + } + + private predicate isOjModePair(Pair p, string modeValue) { + p.getKey().getValueText() = "mode" and + exists(DataFlow::LocalSourceNode symbolLiteral, DataFlow::Node value | + symbolLiteral.asExpr().getExpr().(SymbolLiteral).getValueText() = modeValue and + symbolLiteral.flowsTo(value) and + value.asExpr().getExpr() = p.getValue() + ) + } + + /** + * A node representing a hash that contains the key `:mode`. + */ + private class OjOptionsHashWithModeKey extends DataFlow::Node { + private string modeValue; + + OjOptionsHashWithModeKey() { + exists(DataFlow::LocalSourceNode options | + options.flowsTo(this) and + isOjModePair(options.asExpr().getExpr().(HashLiteral).getAKeyValuePair(), modeValue) + ) + } + + /** + * Holds if this hash node contains a `:mode` key whose value is one known + * to be `isSafe` with untrusted data. + */ + predicate hasKnownMode(boolean isSafe) { modeValue = getAKnownOjModeName(isSafe) } + + /** + * Holds if this hash node contains a `:mode` key whose value is one of the + * `Oj` modes known to be safe to use with untrusted data. + */ + predicate hasSafeMode() { this.hasKnownMode(true) } + } + + /** + * A call node that sets `Oj.default_options`. + * + * ```rb + * Oj.default_options = { allow_blank: true, mode: :compat } + * ``` + */ + private class SetOjDefaultOptionsCall extends DataFlow::CallNode { + SetOjDefaultOptionsCall() { + this = API::getTopLevelMember("Oj").getAMethodCall("default_options=") + } + + /** + * Gets the value being assigned to `Oj.default_options`. + */ + DataFlow::Node getValue() { + result.asExpr() = + this.getArgument(0).asExpr().(CfgNodes::ExprNodes::AssignExprCfgNode).getRhs() + } + } + + /** + * A call to `Oj.load`. + */ + private class OjLoadCall extends DataFlow::CallNode { + OjLoadCall() { this = API::getTopLevelMember("Oj").getAMethodCall("load") } + + /** + * Holds if this call to `Oj.load` includes an explicit options hash + * argument that sets the mode to one that is known to be `isSafe`. + */ + predicate hasExplicitKnownMode(boolean isSafe) { + exists(DataFlow::Node arg, int i | i >= 1 and arg = this.getArgument(i) | + arg.(OjOptionsHashWithModeKey).hasKnownMode(isSafe) + or + isOjModePair(arg.asExpr().getExpr(), getAKnownOjModeName(isSafe)) + ) + } + } + + /** + * An argument in a call to `Oj.load` where the mode is `:object` (which is + * the default), considered a sink for unsafe deserialization. + */ + class UnsafeOjLoadArgument extends Sink { + UnsafeOjLoadArgument() { + exists(OjLoadCall ojLoad | + this = ojLoad.getArgument(0) and + // Exclude calls that explicitly pass a safe mode option. + not ojLoad.hasExplicitKnownMode(true) and + ( + // Sinks to include: + // - Calls with an explicit, unsafe mode option. + ojLoad.hasExplicitKnownMode(false) + or + // - Calls with no explicit mode option, unless there exists a call + // anywhere to set the default options to a known safe mode. + not ojLoad.hasExplicitKnownMode(_) and + not exists(SetOjDefaultOptionsCall setOpts | + setOpts.getValue().(OjOptionsHashWithModeKey).hasSafeMode() + ) + ) + ) + } + } + + /** + * `Base64.decode64` propagates taint from its argument to its return value. + */ + predicate base64DecodeTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) { + exists(DataFlow::CallNode callNode | + callNode = + API::getTopLevelMember("Base64") + .getAMethodCall(["decode64", "strict_decode64", "urlsafe_decode64"]) + | + fromNode = callNode.getArgument(0) and + toNode = callNode + ) + } +} diff --git a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll new file mode 100644 index 00000000000..d08b73da936 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll @@ -0,0 +1,34 @@ +/** + * Provides a taint-tracking configuration for reasoning about unsafe deserialization. + * + * Note, for performance reasons: only import this file if + * `UnsafeDeserialization::Configuration` is needed, otherwise + * `UnsafeDeserializationCustomizations` should be imported instead. + */ + +private import ruby +private import codeql.ruby.DataFlow +private import codeql.ruby.TaintTracking +import UnsafeDeserializationCustomizations + +/** + * A taint-tracking configuration for reasoning about unsafe deserialization. + */ +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "UnsafeDeserialization" } + + override predicate isSource(DataFlow::Node source) { + source instanceof UnsafeDeserialization::Source + } + + override predicate isSink(DataFlow::Node sink) { sink instanceof UnsafeDeserialization::Sink } + + override predicate isSanitizer(DataFlow::Node node) { + super.isSanitizer(node) or + node instanceof UnsafeDeserialization::Sanitizer + } + + override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) { + UnsafeDeserialization::isAdditionalTaintStep(fromNode, toNode) + } +} diff --git a/ruby/ql/lib/codeql/ruby/security/UrlRedirectCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/UrlRedirectCustomizations.qll new file mode 100644 index 00000000000..caaf2264018 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/security/UrlRedirectCustomizations.qll @@ -0,0 +1,127 @@ +/** + * Provides default sources, sinks and sanitizers for detecting "URL + * redirection" vulnerabilities, as well as extension points for adding your + * own. + */ + +private import ruby +private import codeql.ruby.DataFlow +private import codeql.ruby.Concepts +private import codeql.ruby.dataflow.RemoteFlowSources +private import codeql.ruby.dataflow.BarrierGuards + +/** + * Provides default sources, sinks and sanitizers for detecting + * "URL redirection" vulnerabilities, as well as extension points for + * adding your own. + */ +module UrlRedirect { + /** + * A data flow source for "URL redirection" vulnerabilities. + */ + abstract class Source extends DataFlow::Node { } + + /** + * A data flow sink for "URL redirection" vulnerabilities. + */ + abstract class Sink extends DataFlow::Node { } + + /** + * A sanitizer for "URL redirection" vulnerabilities. + */ + abstract class Sanitizer extends DataFlow::Node { } + + /** + * A sanitizer guard for "URL redirection" vulnerabilities. + */ + abstract class SanitizerGuard extends DataFlow::BarrierGuard { } + + /** + * Additional taint steps for "URL redirection" vulnerabilities. + */ + predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + taintStepViaMethodCallReturnValue(node1, node2) + } + + /** + * A source of remote user input, considered as a flow source. + */ + class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + + /** + * A HTTP redirect response, considered as a flow sink. + */ + class RedirectLocationAsSink extends Sink { + RedirectLocationAsSink() { + exists(HTTP::Server::HttpRedirectResponse e | + this = e.getRedirectLocation() and + // As a rough heuristic, assume that methods with these names are handlers for POST/PUT/PATCH/DELETE requests, + // which are not as vulnerable to URL redirection because browsers will not initiate them from clicking a link. + not this.getEnclosingCallable() + .asCallable() + .(Method) + .getName() + .regexpMatch(".*(create|update|destroy).*") + ) + } + } + + /** + * A comparison with a constant string, considered as a sanitizer-guard. + */ + class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { } + + /** + * Some methods will propagate taint to their return values. + * Here we cover a few common ones related to `ActionController::Parameters`. + * TODO: use ApiGraphs or something to restrict these method calls to the correct receiver, rather + * than matching on method name alone. + */ + predicate taintStepViaMethodCallReturnValue(DataFlow::Node node1, DataFlow::Node node2) { + exists(MethodCall m | m = node2.asExpr().getExpr() | + m.getReceiver() = node1.asExpr().getExpr() and + (actionControllerTaintedMethod(m) or hashTaintedMethod(m)) + ) + } + + /** + * String interpolation is considered safe, provided the string is prefixed by a non-tainted value. + * In most cases this will prevent the tainted value from controlling e.g. the host of the URL. + * + * For example: + * + * ```ruby + * redirect_to "/users/#{params[:key]}" # safe + * redirect_to "#{params[:key]}/users" # unsafe + * ``` + * + * There are prefixed interpolations that are not safe, e.g. + * + * ```ruby + * redirect_to "foo#{params[:key]}/users" # => "foo-malicious-site.com/users" + * ``` + * + * We currently don't catch these cases. + */ + class StringInterpolationAsSanitizer extends Sanitizer { + StringInterpolationAsSanitizer() { + exists(StringlikeLiteral str, int n | str.getComponent(n) = this.asExpr().getExpr() and n > 0) + } + } + + /** + * These methods return a new `ActionController::Parameters` or a `Hash` containing a subset of + * the original values. This may still contain user input, so the results are tainted. + * TODO: flesh this out to cover the whole API. + */ + predicate actionControllerTaintedMethod(MethodCall m) { + m.getMethodName() in ["to_unsafe_hash", "to_unsafe_h", "permit", "require"] + } + + /** + * These `Hash` methods preserve taint because they return a new hash which may still contain keys + * with user input. + * TODO: flesh this out to cover the whole API. + */ + predicate hashTaintedMethod(MethodCall m) { m.getMethodName() in ["merge", "fetch"] } +} diff --git a/ruby/ql/lib/codeql/ruby/security/UrlRedirectQuery.qll b/ruby/ql/lib/codeql/ruby/security/UrlRedirectQuery.qll new file mode 100644 index 00000000000..5a984d1fd6e --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/security/UrlRedirectQuery.qll @@ -0,0 +1,34 @@ +/** + * Provides a taint-tracking configuration for detecting "URL redirection" vulnerabilities. + * + * Note, for performance reasons: only import this file if `Configuration` is needed, + * otherwise `UrlRedirectCustomizations` should be imported instead. + */ + +private import ruby +import codeql.ruby.DataFlow::DataFlow::PathGraph +import codeql.ruby.DataFlow +import codeql.ruby.TaintTracking +import UrlRedirectCustomizations +import UrlRedirectCustomizations::UrlRedirect + +/** + * A taint-tracking configuration for detecting "URL redirection" vulnerabilities. + */ +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "UrlRedirect" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + + override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer } + + override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { + guard instanceof SanitizerGuard + } + + override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + UrlRedirect::isAdditionalTaintStep(node1, node2) + } +} diff --git a/ruby/ql/lib/codeql/ruby/security/XSS.qll b/ruby/ql/lib/codeql/ruby/security/XSS.qll new file mode 100644 index 00000000000..8f8f15b630a --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/security/XSS.qll @@ -0,0 +1,369 @@ +/** + * Provides classes and predicates used by the XSS queries. + */ + +private import ruby +private import codeql.ruby.DataFlow +private import codeql.ruby.DataFlow2 +private import codeql.ruby.CFG +private import codeql.ruby.Concepts +private import codeql.ruby.Frameworks +private import codeql.ruby.frameworks.ActionController +private import codeql.ruby.frameworks.ActionView +private import codeql.ruby.dataflow.RemoteFlowSources +private import codeql.ruby.dataflow.BarrierGuards +private import codeql.ruby.dataflow.internal.DataFlowDispatch + +/** + * Provides default sources, sinks and sanitizers for detecting + * "server-side cross-site scripting" vulnerabilities, as well as + * extension points for adding your own. + */ +private module Shared { + /** + * A data flow source for "server-side cross-site scripting" vulnerabilities. + */ + abstract class Source extends DataFlow::Node { } + + /** + * A data flow sink for "server-side cross-site scripting" vulnerabilities. + */ + abstract class Sink extends DataFlow::Node { } + + /** + * A sanitizer for "server-side cross-site scripting" vulnerabilities. + */ + abstract class Sanitizer extends DataFlow::Node { } + + /** + * A sanitizer guard for "server-side cross-site scripting" vulnerabilities. + */ + abstract class SanitizerGuard extends DataFlow::BarrierGuard { } + + private class ErbOutputMethodCallArgumentNode extends DataFlow::Node { + private MethodCall call; + + ErbOutputMethodCallArgumentNode() { + exists(ErbOutputDirective d | + call = d.getTerminalStmt() and + this.asExpr().getExpr() = call.getAnArgument() + ) + } + + MethodCall getCall() { result = call } + } + + /** + * An `html_safe` call marking the output as not requiring HTML escaping, + * considered as a flow sink. + */ + class HtmlSafeCallAsSink extends Sink { + HtmlSafeCallAsSink() { + exists(HtmlSafeCall c, ErbOutputDirective d | + this.asExpr().getExpr() = c.getReceiver() and + c = d.getTerminalStmt() + ) + } + } + + /** + * An argument to a call to the `raw` method, considered as a flow sink. + */ + class RawCallArgumentAsSink extends Sink, ErbOutputMethodCallArgumentNode { + RawCallArgumentAsSink() { this.getCall() instanceof RawCall } + } + + /** + * A argument to a call to the `link_to` method, which does not expect + * unsanitized user-input, considered as a flow sink. + */ + class LinkToCallArgumentAsSink extends Sink, ErbOutputMethodCallArgumentNode { + LinkToCallArgumentAsSink() { + this.asExpr().getExpr() = this.getCall().(LinkToCall).getPathArgument() + } + } + + /** + * An HTML escaping, considered as a sanitizer. + */ + class HtmlEscapingAsSanitizer extends Sanitizer { + HtmlEscapingAsSanitizer() { this = any(HtmlEscaping esc).getOutput() } + } + + /** + * A comparison with a constant string, considered as a sanitizer-guard. + */ + class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { } + + /** + * An inclusion check against an array of constant strings, considered as a sanitizer-guard. + */ + class StringConstArrayInclusionCallAsSanitizerGuard extends SanitizerGuard, + StringConstArrayInclusionCall { } + + /** + * A `VariableWriteAccessCfgNode` that is not succeeded (locally) by another + * write to that variable. + */ + private class FinalInstanceVarWrite extends CfgNodes::ExprNodes::InstanceVariableWriteAccessCfgNode { + private InstanceVariable var; + + FinalInstanceVarWrite() { + var = this.getExpr().getVariable() and + not exists(CfgNodes::ExprNodes::InstanceVariableWriteAccessCfgNode succWrite | + succWrite.getExpr().getVariable() = var + | + succWrite = this.getASuccessor+() + ) + } + + InstanceVariable getVariable() { result = var } + + AssignExpr getAnAssignExpr() { result.getLeftOperand() = this.getExpr() } + } + + /** + * Holds if `call` is a method call in ERB file `erb`, targeting a method + * named `name`. + */ + pragma[noinline] + private predicate isMethodCall(MethodCall call, string name, ErbFile erb) { + name = call.getMethodName() and + erb = call.getLocation().getFile() + } + + /** + * Holds if some render call passes `value` for `hashKey` in the `locals` + * argument, in ERB file `erb`. + */ + pragma[noinline] + private predicate renderCallLocals(string hashKey, Expr value, ErbFile erb) { + exists(RenderCall call, Pair kvPair | + call.getLocals().getAKeyValuePair() = kvPair and + kvPair.getValue() = value and + kvPair.getKey().getValueText() = hashKey and + call.getTemplateFile() = erb + ) + } + + pragma[noinline] + private predicate isFlowFromLocals0( + CfgNodes::ExprNodes::ElementReferenceCfgNode refNode, string hashKey, ErbFile erb + ) { + exists(DataFlow::Node argNode, CfgNodes::ExprNodes::StringlikeLiteralCfgNode strNode | + argNode.asExpr() = refNode.getArgument(0) and + refNode.getReceiver().getExpr().(MethodCall).getMethodName() = "local_assigns" and + argNode.getALocalSource() = DataFlow::exprNode(strNode) and + strNode.getExpr().getValueText() = hashKey and + erb = refNode.getFile() + ) + } + + private predicate isFlowFromLocals(DataFlow::Node node1, DataFlow::Node node2) { + exists(string hashKey, ErbFile erb | + // node1 is a `locals` argument to a render call... + renderCallLocals(hashKey, node1.asExpr().getExpr(), erb) + | + // node2 is an element reference against `local_assigns` + isFlowFromLocals0(node2.asExpr(), hashKey, erb) + or + // ...node2 is a "method call" to a "method" with `hashKey` as its name + // TODO: This may be a variable read in reality that we interpret as a method call + isMethodCall(node2.asExpr().getExpr(), hashKey, erb) + ) + } + + /** + * Holds if `action` contains an assignment of `value` to an instance + * variable named `name`, in ERB file `erb`. + */ + pragma[noinline] + private predicate actionAssigns( + ActionControllerActionMethod action, string name, Expr value, ErbFile erb + ) { + exists(AssignExpr ae, FinalInstanceVarWrite controllerVarWrite | + action.getDefaultTemplateFile() = erb and + ae.getParent+() = action and + ae = controllerVarWrite.getAnAssignExpr() and + name = controllerVarWrite.getVariable().getName() and + value = ae.getRightOperand() + ) + } + + pragma[noinline] + private predicate isVariableReadAccess(VariableReadAccess viewVarRead, string name, ErbFile erb) { + erb = viewVarRead.getLocation().getFile() and + viewVarRead.getVariable().getName() = name + } + + private predicate isFlowFromControllerInstanceVariable(DataFlow::Node node1, DataFlow::Node node2) { + // instance variables in the controller + exists(ActionControllerActionMethod action, string name, ErbFile template | + // match read to write on variable name + actionAssigns(action, name, node1.asExpr().getExpr(), template) and + // propagate taint from assignment RHS expr to variable read access in view + isVariableReadAccess(node2.asExpr().getExpr(), name, template) + ) + } + + /** + * Holds if `helperMethod` is a helper method named `name` that is associated + * with ERB file `erb`. + */ + pragma[noinline] + private predicate isHelperMethod( + ActionControllerHelperMethod helperMethod, string name, ErbFile erb + ) { + helperMethod.getName() = name and + helperMethod.getControllerClass() = getAssociatedControllerClass(erb) + } + + private predicate isFlowIntoHelperMethod(DataFlow::Node node1, DataFlow::Node node2) { + // flow from template into controller helper method + exists( + ErbFile template, ActionControllerHelperMethod helperMethod, string name, + CfgNodes::ExprNodes::MethodCallCfgNode helperMethodCall, int argIdx + | + isHelperMethod(helperMethod, name, template) and + isMethodCall(helperMethodCall.getExpr(), name, template) and + helperMethodCall.getArgument(pragma[only_bind_into](argIdx)) = node1.asExpr() and + helperMethod.getParameter(pragma[only_bind_into](argIdx)) = node2.asExpr().getExpr() + ) + } + + private predicate isFlowFromHelperMethod(DataFlow::Node node1, DataFlow::Node node2) { + // flow out of controller helper method into template + exists(ErbFile template, ActionControllerHelperMethod helperMethod, string name | + // `node1` is an expr node that may be returned by the helper method + exprNodeReturnedFrom(node1, helperMethod) and + // `node2` is a call to the helper method + isHelperMethod(helperMethod, name, template) and + isMethodCall(node2.asExpr().getExpr(), name, template) + ) + } + + /** + * An additional step that is preserves dataflow in the context of XSS. + */ + predicate isAdditionalXSSFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + isFlowFromLocals(node1, node2) + or + isFlowFromControllerInstanceVariable(node1, node2) + or + isFlowIntoHelperMethod(node1, node2) + or + isFlowFromHelperMethod(node1, node2) + } +} + +/** + * Provides default sources, sinks and sanitizers for detecting + * "reflected cross-site scripting" vulnerabilities, as well as + * extension points for adding your own. + */ +module ReflectedXSS { + /** A data flow source for stored XSS vulnerabilities. */ + abstract class Source extends Shared::Source { } + + /** A data flow sink for stored XSS vulnerabilities. */ + abstract class Sink extends Shared::Sink { } + + /** A sanitizer for stored XSS vulnerabilities. */ + abstract class Sanitizer extends Shared::Sanitizer { } + + /** A sanitizer guard for stored XSS vulnerabilities. */ + abstract class SanitizerGuard extends Shared::SanitizerGuard { } + + // Consider all arbitrary XSS sinks to be reflected XSS sinks + private class AnySink extends Sink instanceof Shared::Sink { } + + // Consider all arbitrary XSS sanitizers to be reflected XSS sanitizers + private class AnySanitizer extends Sanitizer instanceof Shared::Sanitizer { } + + // Consider all arbitrary XSS sanitizer guards to be reflected XSS sanitizer guards + private class AnySanitizerGuard extends SanitizerGuard instanceof Shared::SanitizerGuard { + override predicate checks(CfgNode expr, boolean branch) { + Shared::SanitizerGuard.super.checks(expr, branch) + } + } + + /** + * An additional step that is preserves dataflow in the context of reflected XSS. + */ + predicate isAdditionalXSSTaintStep = Shared::isAdditionalXSSFlowStep/2; + + /** + * A source of remote user input, considered as a flow source. + */ + class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } +} + +private module OrmTracking { + /** + * A data flow configuration to track flow from finder calls to field accesses. + */ + class Configuration extends DataFlow2::Configuration { + Configuration() { this = "OrmTracking" } + + override predicate isSource(DataFlow2::Node source) { source instanceof OrmInstantiation } + + // Select any call node and narrow down later + override predicate isSink(DataFlow2::Node sink) { sink instanceof DataFlow2::CallNode } + + override predicate isAdditionalFlowStep(DataFlow2::Node node1, DataFlow2::Node node2) { + Shared::isAdditionalXSSFlowStep(node1, node2) + or + // Propagate flow through arbitrary method calls + node2.(DataFlow2::CallNode).getReceiver() = node1 + or + // Propagate flow through "or" expressions `or`/`||` + node2.asExpr().getExpr().(LogicalOrExpr).getAnOperand() = node1.asExpr().getExpr() + } + } +} + +module StoredXSS { + /** A data flow source for stored XSS vulnerabilities. */ + abstract class Source extends Shared::Source { } + + /** A data flow sink for stored XSS vulnerabilities. */ + abstract class Sink extends Shared::Sink { } + + /** A sanitizer for stored XSS vulnerabilities. */ + abstract class Sanitizer extends Shared::Sanitizer { } + + /** A sanitizer guard for stored XSS vulnerabilities. */ + abstract class SanitizerGuard extends Shared::SanitizerGuard { } + + // Consider all arbitrary XSS sinks to be stored XSS sinks + private class AnySink extends Sink instanceof Shared::Sink { } + + // Consider all arbitrary XSS sanitizers to be stored XSS sanitizers + private class AnySanitizer extends Sanitizer instanceof Shared::Sanitizer { } + + // Consider all arbitrary XSS sanitizer guards to be stored XSS sanitizer guards + private class AnySanitizerGuard extends SanitizerGuard instanceof Shared::SanitizerGuard { + override predicate checks(CfgNode expr, boolean branch) { + Shared::SanitizerGuard.super.checks(expr, branch) + } + } + + /** + * An additional step that preserves dataflow in the context of stored XSS. + */ + predicate isAdditionalXSSTaintStep = Shared::isAdditionalXSSFlowStep/2; + + private class OrmFieldAsSource extends Source instanceof DataFlow2::CallNode { + OrmFieldAsSource() { + exists(OrmTracking::Configuration subConfig, DataFlow2::CallNode subSrc, MethodCall call | + subConfig.hasFlow(subSrc, this) and + call = this.asExpr().getExpr() and + subSrc.(OrmInstantiation).methodCallMayAccessField(call.getMethodName()) + ) + } + } + + /** A file read, considered as a flow source for stored XSS. */ + private class FileSystemReadAccessAsSource extends Source instanceof FileSystemReadAccess { } + // TODO: Consider `FileNameSource` flowing to script tag `src` attributes and similar +} diff --git a/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll b/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll new file mode 100644 index 00000000000..6ced6a8206e --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll @@ -0,0 +1,470 @@ +/** Step Summaries and Type Tracking */ + +private import TypeTrackerSpecific + +/** + * Any string that may appear as the name of a piece of content. This will usually include things like: + * - Attribute names (in Python) + * - Property names (in JavaScript) + * + * In general, this can also be used to model things like stores to specific list indices. To ensure + * correctness, it is important that + * + * - different types of content do not have overlapping names, and + * - the empty string `""` is not a valid piece of content, as it is used to indicate the absence of + * content instead. + */ +class ContentName extends string { + ContentName() { this = getPossibleContentName() } +} + +/** Either a content name, or the empty string (representing no content). */ +class OptionalContentName extends string { + OptionalContentName() { this instanceof ContentName or this = "" } +} + +cached +private module Cached { + /** + * A description of a step on an inter-procedural data flow path. + */ + cached + newtype TStepSummary = + LevelStep() or + CallStep() or + ReturnStep() or + StoreStep(ContentName content) or + LoadStep(ContentName content) + + /** Gets the summary resulting from appending `step` to type-tracking summary `tt`. */ + cached + TypeTracker append(TypeTracker tt, StepSummary step) { + exists(Boolean hasCall, OptionalContentName content | tt = MkTypeTracker(hasCall, content) | + step = LevelStep() and result = tt + or + step = CallStep() and result = MkTypeTracker(true, content) + or + step = ReturnStep() and hasCall = false and result = tt + or + step = LoadStep(content) and result = MkTypeTracker(hasCall, "") + or + exists(string p | step = StoreStep(p) and content = "" and result = MkTypeTracker(hasCall, p)) + ) + } + + /** + * Gets the summary that corresponds to having taken a forwards + * heap and/or intra-procedural step from `nodeFrom` to `nodeTo`. + * + * Steps contained in this predicate should _not_ depend on the call graph. + */ + cached + predicate stepNoCall(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) { + exists(Node mid | nodeFrom.flowsTo(mid) and smallstepNoCall(mid, nodeTo, summary)) + } + + /** + * Gets the summary that corresponds to having taken a forwards + * inter-procedural step from `nodeFrom` to `nodeTo`. + */ + cached + predicate stepCall(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) { + exists(Node mid | nodeFrom.flowsTo(mid) and smallstepCall(mid, nodeTo, summary)) + } +} + +private import Cached + +/** + * INTERNAL: Use `TypeTracker` or `TypeBackTracker` instead. + * + * A description of a step on an inter-procedural data flow path. + */ +class StepSummary extends TStepSummary { + /** Gets a textual representation of this step summary. */ + string toString() { + this instanceof LevelStep and result = "level" + or + this instanceof CallStep and result = "call" + or + this instanceof ReturnStep and result = "return" + or + exists(string content | this = StoreStep(content) | result = "store " + content) + or + exists(string content | this = LoadStep(content) | result = "load " + content) + } +} + +pragma[noinline] +private predicate smallstepNoCall(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) { + jumpStep(nodeFrom, nodeTo) and + summary = LevelStep() + or + exists(string content | + StepSummary::localSourceStoreStep(nodeFrom, nodeTo, content) and + summary = StoreStep(content) + or + basicLoadStep(nodeFrom, nodeTo, content) and summary = LoadStep(content) + ) +} + +pragma[noinline] +private predicate smallstepCall(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) { + callStep(nodeFrom, nodeTo) and summary = CallStep() + or + returnStep(nodeFrom, nodeTo) and + summary = ReturnStep() +} + +/** Provides predicates for updating step summaries (`StepSummary`s). */ +module StepSummary { + /** + * Gets the summary that corresponds to having taken a forwards + * heap and/or inter-procedural step from `nodeFrom` to `nodeTo`. + * + * This predicate is inlined, which enables better join-orders when + * the call graph construction and type tracking are mutually recursive. + * In such cases, non-linear recursion involving `step` will be limited + * to non-linear recursion for the parts of `step` that involve the + * call graph. + */ + pragma[inline] + predicate step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) { + stepNoCall(nodeFrom, nodeTo, summary) + or + stepCall(nodeFrom, nodeTo, summary) + } + + /** + * Gets the summary that corresponds to having taken a forwards + * local, heap and/or inter-procedural step from `nodeFrom` to `nodeTo`. + * + * Unlike `StepSummary::step`, this predicate does not compress + * type-preserving steps. + */ + pragma[inline] + predicate smallstep(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) { + smallstepNoCall(nodeFrom, nodeTo, summary) + or + smallstepCall(nodeFrom, nodeTo, summary) + } + + /** + * Holds if `nodeFrom` is being written to the `content` content of the object in `nodeTo`. + * + * Note that `nodeTo` will always be a local source node that flows to the place where the content + * is written in `basicStoreStep`. This may lead to the flow of information going "back in time" + * from the point of view of the execution of the program. + * + * For instance, if we interpret attribute writes in Python as writing to content with the same + * name as the attribute and consider the following snippet + * + * ```python + * def foo(y): + * x = Foo() + * bar(x) + * x.attr = y + * baz(x) + * + * def bar(x): + * z = x.attr + * ``` + * for the attribute write `x.attr = y`, we will have `content` being the literal string `"attr"`, + * `nodeFrom` will be `y`, and `nodeTo` will be the object `Foo()` created on the first line of the + * function. This means we will track the fact that `x.attr` can have the type of `y` into the + * assignment to `z` inside `bar`, even though this attribute write happens _after_ `bar` is called. + */ + predicate localSourceStoreStep(Node nodeFrom, TypeTrackingNode nodeTo, string content) { + exists(Node obj | nodeTo.flowsTo(obj) and basicStoreStep(nodeFrom, obj, content)) + } +} + +private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalContentName content) + +/** + * Summary of the steps needed to track a value to a given dataflow node. + * + * This can be used to track objects that implement a certain API in order to + * recognize calls to that API. Note that type-tracking does not by itself provide a + * source/sink relation, that is, it may determine that a node has a given type, + * but it won't determine where that type came from. + * + * It is recommended that all uses of this type are written in the following form, + * for tracking some type `myType`: + * ```ql + * DataFlow::TypeTrackingNode myType(DataFlow::TypeTracker t) { + * t.start() and + * result = < source of myType > + * or + * exists (DataFlow::TypeTracker t2 | + * result = myType(t2).track(t2, t) + * ) + * } + * + * DataFlow::Node myType() { myType(DataFlow::TypeTracker::end()).flowsTo(result) } + * ``` + * + * Instead of `result = myType(t2).track(t2, t)`, you can also use the equivalent + * `t = t2.step(myType(t2), result)`. If you additionally want to track individual + * intra-procedural steps, use `t = t2.smallstep(myCallback(t2), result)`. + */ +class TypeTracker extends TTypeTracker { + Boolean hasCall; + OptionalContentName content; + + TypeTracker() { this = MkTypeTracker(hasCall, content) } + + /** Gets the summary resulting from appending `step` to this type-tracking summary. */ + TypeTracker append(StepSummary step) { result = append(this, step) } + + /** Gets a textual representation of this summary. */ + string toString() { + exists(string withCall, string withContent | + (if hasCall = true then withCall = "with" else withCall = "without") and + (if content != "" then withContent = " with content " + content else withContent = "") and + result = "type tracker " + withCall + " call steps" + withContent + ) + } + + /** + * Holds if this is the starting point of type tracking. + */ + predicate start() { hasCall = false and content = "" } + + /** + * Holds if this is the starting point of type tracking, and the value starts in the content named `contentName`. + * The type tracking only ends after the content has been loaded. + */ + predicate startInContent(ContentName contentName) { hasCall = false and content = contentName } + + /** + * Holds if this is the starting point of type tracking + * when tracking a parameter into a call, but not out of it. + */ + predicate call() { hasCall = true and content = "" } + + /** + * Holds if this is the end point of type tracking. + */ + predicate end() { content = "" } + + /** + * INTERNAL. DO NOT USE. + * + * Holds if this type has been tracked into a call. + */ + boolean hasCall() { result = hasCall } + + /** + * INTERNAL. DO NOT USE. + * + * Gets the content associated with this type tracker. + */ + string getContent() { result = content } + + /** + * Gets a type tracker that starts where this one has left off to allow continued + * tracking. + * + * This predicate is only defined if the type is not associated to a piece of content. + */ + TypeTracker continue() { content = "" and result = this } + + /** + * Gets the summary that corresponds to having taken a forwards + * heap and/or inter-procedural step from `nodeFrom` to `nodeTo`. + */ + pragma[inline] + TypeTracker step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) { + exists(StepSummary summary | + StepSummary::step(nodeFrom, pragma[only_bind_out](nodeTo), pragma[only_bind_into](summary)) and + result = this.append(pragma[only_bind_into](summary)) + ) + } + + /** + * Gets the summary that corresponds to having taken a forwards + * local, heap and/or inter-procedural step from `nodeFrom` to `nodeTo`. + * + * Unlike `TypeTracker::step`, this predicate exposes all edges + * in the flow graph, and not just the edges between `Node`s. + * It may therefore be less performant. + * + * Type tracking predicates using small steps typically take the following form: + * ```ql + * DataFlow::Node myType(DataFlow::TypeTracker t) { + * t.start() and + * result = < source of myType > + * or + * exists (DataFlow::TypeTracker t2 | + * t = t2.smallstep(myType(t2), result) + * ) + * } + * + * DataFlow::Node myType() { + * result = myType(DataFlow::TypeTracker::end()) + * } + * ``` + */ + pragma[inline] + TypeTracker smallstep(Node nodeFrom, Node nodeTo) { + exists(StepSummary summary | + StepSummary::smallstep(nodeFrom, nodeTo, summary) and + result = this.append(summary) + ) + or + simpleLocalFlowStep(nodeFrom, nodeTo) and + result = this + } +} + +/** Provides predicates for implementing custom `TypeTracker`s. */ +module TypeTracker { + /** + * Gets a valid end point of type tracking. + */ + TypeTracker end() { result.end() } +} + +private newtype TTypeBackTracker = MkTypeBackTracker(Boolean hasReturn, OptionalContentName content) + +/** + * Summary of the steps needed to back-track a use of a value to a given dataflow node. + * + * This can for example be used to track callbacks that are passed to a certain API, + * so we can model specific parameters of that callback as having a certain type. + * + * Note that type back-tracking does not provide a source/sink relation, that is, + * it may determine that a node will be used in an API call somewhere, but it won't + * determine exactly where that use was, or the path that led to the use. + * + * It is recommended that all uses of this type are written in the following form, + * for back-tracking some callback type `myCallback`: + * + * ```ql + * DataFlow::TypeTrackingNode myCallback(DataFlow::TypeBackTracker t) { + * t.start() and + * result = (< some API call >).getArgument(< n >).getALocalSource() + * or + * exists (DataFlow::TypeBackTracker t2 | + * result = myCallback(t2).backtrack(t2, t) + * ) + * } + * + * DataFlow::TypeTrackingNode myCallback() { result = myCallback(DataFlow::TypeBackTracker::end()) } + * ``` + * + * Instead of `result = myCallback(t2).backtrack(t2, t)`, you can also use the equivalent + * `t2 = t.step(result, myCallback(t2))`. If you additionally want to track individual + * intra-procedural steps, use `t2 = t.smallstep(result, myCallback(t2))`. + */ +class TypeBackTracker extends TTypeBackTracker { + Boolean hasReturn; + string content; + + TypeBackTracker() { this = MkTypeBackTracker(hasReturn, content) } + + /** Gets the summary resulting from prepending `step` to this type-tracking summary. */ + TypeBackTracker prepend(StepSummary step) { + step = LevelStep() and result = this + or + step = CallStep() and hasReturn = false and result = this + or + step = ReturnStep() and result = MkTypeBackTracker(true, content) + or + exists(string p | + step = LoadStep(p) and content = "" and result = MkTypeBackTracker(hasReturn, p) + ) + or + step = StoreStep(content) and result = MkTypeBackTracker(hasReturn, "") + } + + /** Gets a textual representation of this summary. */ + string toString() { + exists(string withReturn, string withContent | + (if hasReturn = true then withReturn = "with" else withReturn = "without") and + (if content != "" then withContent = " with content " + content else withContent = "") and + result = "type back-tracker " + withReturn + " return steps" + withContent + ) + } + + /** + * Holds if this is the starting point of type tracking. + */ + predicate start() { hasReturn = false and content = "" } + + /** + * Holds if this is the end point of type tracking. + */ + predicate end() { content = "" } + + /** + * INTERNAL. DO NOT USE. + * + * Holds if this type has been back-tracked into a call through return edge. + */ + boolean hasReturn() { result = hasReturn } + + /** + * Gets a type tracker that starts where this one has left off to allow continued + * tracking. + * + * This predicate is only defined if the type has not been tracked into a piece of content. + */ + TypeBackTracker continue() { content = "" and result = this } + + /** + * Gets the summary that corresponds to having taken a backwards + * heap and/or inter-procedural step from `nodeTo` to `nodeFrom`. + */ + pragma[inline] + TypeBackTracker step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) { + exists(StepSummary summary | + StepSummary::step(pragma[only_bind_out](nodeFrom), nodeTo, pragma[only_bind_into](summary)) and + this = result.prepend(pragma[only_bind_into](summary)) + ) + } + + /** + * Gets the summary that corresponds to having taken a backwards + * local, heap and/or inter-procedural step from `nodeTo` to `nodeFrom`. + * + * Unlike `TypeBackTracker::step`, this predicate exposes all edges + * in the flowgraph, and not just the edges between + * `TypeTrackingNode`s. It may therefore be less performant. + * + * Type tracking predicates using small steps typically take the following form: + * ```ql + * DataFlow::Node myType(DataFlow::TypeBackTracker t) { + * t.start() and + * result = < some API call >.getArgument(< n >) + * or + * exists (DataFlow::TypeBackTracker t2 | + * t = t2.smallstep(result, myType(t2)) + * ) + * } + * + * DataFlow::Node myType() { + * result = myType(DataFlow::TypeBackTracker::end()) + * } + * ``` + */ + pragma[inline] + TypeBackTracker smallstep(Node nodeFrom, Node nodeTo) { + exists(StepSummary summary | + StepSummary::smallstep(nodeFrom, nodeTo, summary) and + this = result.prepend(summary) + ) + or + simpleLocalFlowStep(nodeFrom, nodeTo) and + this = result + } +} + +/** Provides predicates for implementing custom `TypeBackTracker`s. */ +module TypeBackTracker { + /** + * Gets a valid end point of type back-tracking. + */ + TypeBackTracker end() { result.end() } +} diff --git a/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll b/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll new file mode 100644 index 00000000000..40beb734d37 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll @@ -0,0 +1,146 @@ +private import codeql.ruby.AST as AST +private import codeql.ruby.CFG as CFG +private import CFG::CfgNodes +private import codeql.ruby.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon +private import codeql.ruby.dataflow.internal.DataFlowPublic as DataFlowPublic +private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate +private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch +private import codeql.ruby.dataflow.internal.SsaImpl as SsaImpl + +class Node = DataFlowPublic::Node; + +class TypeTrackingNode = DataFlowPublic::LocalSourceNode; + +predicate simpleLocalFlowStep = DataFlowPrivate::localFlowStepTypeTracker/2; + +predicate jumpStep = DataFlowPrivate::jumpStep/2; + +/** + * Gets the name of a possible piece of content. This will usually include things like + * + * - Attribute names (in Python) + * - Property names (in JavaScript) + */ +string getPossibleContentName() { result = getSetterCallAttributeName(_) } + +/** + * Holds if `nodeFrom` steps to `nodeTo` by being passed as a parameter in a call. + * + * Flow into summarized library methods is not included, as that will lead to negative + * recursion (or, at best, terrible performance), since identifying calls to library + * methods is done using API graphs (which uses type tracking). + */ +predicate callStep(Node nodeFrom, Node nodeTo) { + exists(ExprNodes::CallCfgNode call, CFG::CfgScope callable, int i | + DataFlowDispatch::getTarget(call) = callable and + nodeFrom.(DataFlowPrivate::ArgumentNode).sourceArgumentOf(call, i) and + nodeTo.(DataFlowPrivate::ParameterNodeImpl).isSourceParameterOf(callable, i) + ) + or + // In normal data-flow, this will be a local flow step. But for type tracking + // we model it as a call step, in order to avoid computing a potential + // self-cross product of all calls to a function that returns one of its parameters + // (only to later filter that flow out using `TypeTracker::append`). + nodeTo = + DataFlowPrivate::LocalFlow::getParameterDefNode(nodeFrom + .(DataFlowPublic::ParameterNode) + .getParameter()) +} + +/** + * Holds if `nodeFrom` steps to `nodeTo` by being returned from a call. + * + * Flow out of summarized library methods is not included, as that will lead to negative + * recursion (or, at best, terrible performance), since identifying calls to library + * methods is done using API graphs (which uses type tracking). + */ +predicate returnStep(Node nodeFrom, Node nodeTo) { + exists(ExprNodes::CallCfgNode call | + nodeFrom instanceof DataFlowPrivate::ReturnNode and + nodeFrom.(DataFlowPrivate::NodeImpl).getCfgScope() = DataFlowDispatch::getTarget(call) and + nodeTo.asExpr().getNode() = call.getNode() + ) + or + // In normal data-flow, this will be a local flow step. But for type tracking + // we model it as a returning flow step, in order to avoid computing a potential + // self-cross product of all calls to a function that returns one of its parameters + // (only to later filter that flow out using `TypeTracker::append`). + nodeTo.(DataFlowPrivate::SynthReturnNode).getAnInput() = nodeFrom +} + +/** + * Holds if `nodeFrom` is being written to the `content` content of the object + * in `nodeTo`. + * + * Note that the choice of `nodeTo` does not have to make sense + * "chronologically". All we care about is whether the `content` content of + * `nodeTo` can have a specific type, and the assumption is that if a specific + * type appears here, then any access of that particular content can yield + * something of that particular type. + * + * Thus, in an example such as + * + * ```rb + * def foo(y) + * x = Foo.new + * bar(x) + * x.content = y + * baz(x) + * end + * + * def bar(x) + * z = x.content + * end + * ``` + * for the content write `x.content = y`, we will have `content` being the + * literal string `"content"`, `nodeFrom` will be `y`, and `nodeTo` will be the + * `Foo` object created on the first line of the function. This means we will + * track the fact that `x.content` can have the type of `y` into the assignment + * to `z` inside `bar`, even though this content write happens _after_ `bar` is + * called. + */ +predicate basicStoreStep(Node nodeFrom, DataFlowPublic::LocalSourceNode nodeTo, string content) { + // TODO: support SetterMethodCall inside TuplePattern + exists(ExprNodes::MethodCallCfgNode call | + content = getSetterCallAttributeName(call.getExpr()) and + nodeTo.(DataFlowPublic::ExprNode).getExprNode() = call.getReceiver() and + call.getExpr() instanceof AST::SetterMethodCall and + call.getArgument(call.getNumberOfArguments() - 1) = + nodeFrom.(DataFlowPublic::ExprNode).getExprNode() + ) +} + +/** + * Returns the name of the attribute being set by the setter method call, i.e. + * the name of the setter method without the trailing `=`. In the following + * example, the result is `"bar"`. + * + * ```rb + * foo.bar = 1 + * ``` + */ +private string getSetterCallAttributeName(AST::SetterMethodCall call) { + // TODO: this should be exposed in `SetterMethodCall` + exists(string setterName | + setterName = call.getMethodName() and result = setterName.prefix(setterName.length() - 1) + ) +} + +/** + * Holds if `nodeTo` is the result of accessing the `content` content of `nodeFrom`. + */ +predicate basicLoadStep(Node nodeFrom, Node nodeTo, string content) { + exists(ExprNodes::MethodCallCfgNode call | + call.getExpr().getNumberOfArguments() = 0 and + content = call.getExpr().(AST::MethodCall).getMethodName() and + nodeFrom.asExpr() = call.getReceiver() and + nodeTo.asExpr() = call + ) +} + +/** + * A utility class that is equivalent to `boolean` but does not require type joining. + */ +class Boolean extends boolean { + Boolean() { this = true or this = false } +} diff --git a/ruby/ql/lib/qlpack.lock.yml b/ruby/ql/lib/qlpack.lock.yml new file mode 100644 index 00000000000..06dd07fc7dc --- /dev/null +++ b/ruby/ql/lib/qlpack.lock.yml @@ -0,0 +1,4 @@ +--- +dependencies: {} +compiled: false +lockVersion: 1.0.0 diff --git a/ruby/ql/lib/qlpack.yml b/ruby/ql/lib/qlpack.yml new file mode 100644 index 00000000000..91f40532fc9 --- /dev/null +++ b/ruby/ql/lib/qlpack.yml @@ -0,0 +1,6 @@ +name: codeql/ruby-all +version: 0.0.2 +extractor: ruby +dbscheme: ruby.dbscheme +upgrades: upgrades +library: true diff --git a/ruby/ql/lib/ruby.dbscheme b/ruby/ql/lib/ruby.dbscheme new file mode 100644 index 00000000000..f36dd8a35ce --- /dev/null +++ b/ruby/ql/lib/ruby.dbscheme @@ -0,0 +1,1318 @@ +// CodeQL database schema for Ruby +// Automatically generated from the tree-sitter grammar; do not edit + +@location = @location_default + +locations_default( + unique int id: @location_default, + int file: @file ref, + int start_line: int ref, + int start_column: int ref, + int end_line: int ref, + int end_column: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +sourceLocationPrefix( + string prefix: string ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +case @diagnostic.severity of + 10 = @diagnostic_debug +| 20 = @diagnostic_info +| 30 = @diagnostic_warning +| 40 = @diagnostic_error +; + + +@ruby_underscore_arg = @ruby_assignment | @ruby_binary | @ruby_conditional | @ruby_operator_assignment | @ruby_range | @ruby_unary | @ruby_underscore_primary + +@ruby_underscore_lhs = @ruby_call | @ruby_element_reference | @ruby_scope_resolution | @ruby_token_false | @ruby_token_nil | @ruby_token_true | @ruby_underscore_variable + +@ruby_underscore_method_name = @ruby_delimited_symbol | @ruby_setter | @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_operator | @ruby_token_simple_symbol + +@ruby_underscore_primary = @ruby_array | @ruby_begin | @ruby_break | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_delimited_symbol | @ruby_for | @ruby_hash | @ruby_if | @ruby_lambda | @ruby_method | @ruby_module | @ruby_next | @ruby_parenthesized_statements | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_retry | @ruby_return | @ruby_singleton_class | @ruby_singleton_method | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_symbol_array | @ruby_token_character | @ruby_token_complex | @ruby_token_float | @ruby_token_heredoc_beginning | @ruby_token_integer | @ruby_token_simple_symbol | @ruby_unary | @ruby_underscore_lhs | @ruby_unless | @ruby_until | @ruby_while | @ruby_yield + +@ruby_underscore_statement = @ruby_alias | @ruby_assignment | @ruby_begin_block | @ruby_binary | @ruby_break | @ruby_call | @ruby_end_block | @ruby_if_modifier | @ruby_next | @ruby_operator_assignment | @ruby_rescue_modifier | @ruby_return | @ruby_unary | @ruby_undef | @ruby_underscore_arg | @ruby_unless_modifier | @ruby_until_modifier | @ruby_while_modifier | @ruby_yield + +@ruby_underscore_variable = @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_self | @ruby_token_super + +ruby_alias_def( + unique int id: @ruby_alias, + int alias: @ruby_underscore_method_name ref, + int name: @ruby_underscore_method_name ref, + int loc: @location ref +); + +@ruby_argument_list_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_argument_list, index] +ruby_argument_list_child( + int ruby_argument_list: @ruby_argument_list ref, + int index: int ref, + unique int child: @ruby_argument_list_child_type ref +); + +ruby_argument_list_def( + unique int id: @ruby_argument_list, + int loc: @location ref +); + +@ruby_array_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_array, index] +ruby_array_child( + int ruby_array: @ruby_array ref, + int index: int ref, + unique int child: @ruby_array_child_type ref +); + +ruby_array_def( + unique int id: @ruby_array, + int loc: @location ref +); + +@ruby_assignment_left_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +@ruby_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_right_assignment_list | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +ruby_assignment_def( + unique int id: @ruby_assignment, + int left: @ruby_assignment_left_type ref, + int right: @ruby_assignment_right_type ref, + int loc: @location ref +); + +@ruby_bare_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_string, index] +ruby_bare_string_child( + int ruby_bare_string: @ruby_bare_string ref, + int index: int ref, + unique int child: @ruby_bare_string_child_type ref +); + +ruby_bare_string_def( + unique int id: @ruby_bare_string, + int loc: @location ref +); + +@ruby_bare_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_symbol, index] +ruby_bare_symbol_child( + int ruby_bare_symbol: @ruby_bare_symbol ref, + int index: int ref, + unique int child: @ruby_bare_symbol_child_type ref +); + +ruby_bare_symbol_def( + unique int id: @ruby_bare_symbol, + int loc: @location ref +); + +@ruby_begin_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin, index] +ruby_begin_child( + int ruby_begin: @ruby_begin ref, + int index: int ref, + unique int child: @ruby_begin_child_type ref +); + +ruby_begin_def( + unique int id: @ruby_begin, + int loc: @location ref +); + +@ruby_begin_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin_block, index] +ruby_begin_block_child( + int ruby_begin_block: @ruby_begin_block ref, + int index: int ref, + unique int child: @ruby_begin_block_child_type ref +); + +ruby_begin_block_def( + unique int id: @ruby_begin_block, + int loc: @location ref +); + +@ruby_binary_left_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +case @ruby_binary.operator of + 0 = @ruby_binary_bangequal +| 1 = @ruby_binary_bangtilde +| 2 = @ruby_binary_percent +| 3 = @ruby_binary_ampersand +| 4 = @ruby_binary_ampersandampersand +| 5 = @ruby_binary_star +| 6 = @ruby_binary_starstar +| 7 = @ruby_binary_plus +| 8 = @ruby_binary_minus +| 9 = @ruby_binary_slash +| 10 = @ruby_binary_langle +| 11 = @ruby_binary_langlelangle +| 12 = @ruby_binary_langleequal +| 13 = @ruby_binary_langleequalrangle +| 14 = @ruby_binary_equalequal +| 15 = @ruby_binary_equalequalequal +| 16 = @ruby_binary_equaltilde +| 17 = @ruby_binary_rangle +| 18 = @ruby_binary_rangleequal +| 19 = @ruby_binary_ranglerangle +| 20 = @ruby_binary_caret +| 21 = @ruby_binary_and +| 22 = @ruby_binary_or +| 23 = @ruby_binary_pipe +| 24 = @ruby_binary_pipepipe +; + + +@ruby_binary_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_binary_def( + unique int id: @ruby_binary, + int left: @ruby_binary_left_type ref, + int operator: int ref, + int right: @ruby_binary_right_type ref, + int loc: @location ref +); + +ruby_block_parameters( + unique int ruby_block: @ruby_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_block, index] +ruby_block_child( + int ruby_block: @ruby_block ref, + int index: int ref, + unique int child: @ruby_block_child_type ref +); + +ruby_block_def( + unique int id: @ruby_block, + int loc: @location ref +); + +ruby_block_argument_def( + unique int id: @ruby_block_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_block_parameter_def( + unique int id: @ruby_block_parameter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_block_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_identifier + +#keyset[ruby_block_parameters, index] +ruby_block_parameters_child( + int ruby_block_parameters: @ruby_block_parameters ref, + int index: int ref, + unique int child: @ruby_block_parameters_child_type ref +); + +ruby_block_parameters_def( + unique int id: @ruby_block_parameters, + int loc: @location ref +); + +ruby_break_child( + unique int ruby_break: @ruby_break ref, + unique int child: @ruby_argument_list ref +); + +ruby_break_def( + unique int id: @ruby_break, + int loc: @location ref +); + +ruby_call_arguments( + unique int ruby_call: @ruby_call ref, + unique int arguments: @ruby_argument_list ref +); + +@ruby_call_block_type = @ruby_block | @ruby_do_block + +ruby_call_block( + unique int ruby_call: @ruby_call ref, + unique int block: @ruby_call_block_type ref +); + +@ruby_call_method_type = @ruby_argument_list | @ruby_scope_resolution | @ruby_token_operator | @ruby_underscore_variable + +@ruby_call_receiver_type = @ruby_call | @ruby_underscore_primary + +ruby_call_receiver( + unique int ruby_call: @ruby_call ref, + unique int receiver: @ruby_call_receiver_type ref +); + +ruby_call_def( + unique int id: @ruby_call, + int method: @ruby_call_method_type ref, + int loc: @location ref +); + +ruby_case_value( + unique int ruby_case__: @ruby_case__ ref, + unique int value: @ruby_underscore_statement ref +); + +@ruby_case_child_type = @ruby_else | @ruby_when + +#keyset[ruby_case__, index] +ruby_case_child( + int ruby_case__: @ruby_case__ ref, + int index: int ref, + unique int child: @ruby_case_child_type ref +); + +ruby_case_def( + unique int id: @ruby_case__, + int loc: @location ref +); + +#keyset[ruby_chained_string, index] +ruby_chained_string_child( + int ruby_chained_string: @ruby_chained_string ref, + int index: int ref, + unique int child: @ruby_string__ ref +); + +ruby_chained_string_def( + unique int id: @ruby_chained_string, + int loc: @location ref +); + +@ruby_class_name_type = @ruby_scope_resolution | @ruby_token_constant + +ruby_class_superclass( + unique int ruby_class: @ruby_class ref, + unique int superclass: @ruby_superclass ref +); + +@ruby_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_class, index] +ruby_class_child( + int ruby_class: @ruby_class ref, + int index: int ref, + unique int child: @ruby_class_child_type ref +); + +ruby_class_def( + unique int id: @ruby_class, + int name: @ruby_class_name_type ref, + int loc: @location ref +); + +ruby_conditional_def( + unique int id: @ruby_conditional, + int alternative: @ruby_underscore_arg ref, + int condition: @ruby_underscore_arg ref, + int consequence: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_delimited_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_delimited_symbol, index] +ruby_delimited_symbol_child( + int ruby_delimited_symbol: @ruby_delimited_symbol ref, + int index: int ref, + unique int child: @ruby_delimited_symbol_child_type ref +); + +ruby_delimited_symbol_def( + unique int id: @ruby_delimited_symbol, + int loc: @location ref +); + +@ruby_destructured_left_assignment_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_destructured_left_assignment, index] +ruby_destructured_left_assignment_child( + int ruby_destructured_left_assignment: @ruby_destructured_left_assignment ref, + int index: int ref, + unique int child: @ruby_destructured_left_assignment_child_type ref +); + +ruby_destructured_left_assignment_def( + unique int id: @ruby_destructured_left_assignment, + int loc: @location ref +); + +@ruby_destructured_parameter_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_identifier + +#keyset[ruby_destructured_parameter, index] +ruby_destructured_parameter_child( + int ruby_destructured_parameter: @ruby_destructured_parameter ref, + int index: int ref, + unique int child: @ruby_destructured_parameter_child_type ref +); + +ruby_destructured_parameter_def( + unique int id: @ruby_destructured_parameter, + int loc: @location ref +); + +@ruby_do_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do, index] +ruby_do_child( + int ruby_do: @ruby_do ref, + int index: int ref, + unique int child: @ruby_do_child_type ref +); + +ruby_do_def( + unique int id: @ruby_do, + int loc: @location ref +); + +ruby_do_block_parameters( + unique int ruby_do_block: @ruby_do_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_do_block_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do_block, index] +ruby_do_block_child( + int ruby_do_block: @ruby_do_block ref, + int index: int ref, + unique int child: @ruby_do_block_child_type ref +); + +ruby_do_block_def( + unique int id: @ruby_do_block, + int loc: @location ref +); + +@ruby_element_reference_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_element_reference, index] +ruby_element_reference_child( + int ruby_element_reference: @ruby_element_reference ref, + int index: int ref, + unique int child: @ruby_element_reference_child_type ref +); + +ruby_element_reference_def( + unique int id: @ruby_element_reference, + int object: @ruby_underscore_primary ref, + int loc: @location ref +); + +@ruby_else_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_else, index] +ruby_else_child( + int ruby_else: @ruby_else ref, + int index: int ref, + unique int child: @ruby_else_child_type ref +); + +ruby_else_def( + unique int id: @ruby_else, + int loc: @location ref +); + +@ruby_elsif_alternative_type = @ruby_else | @ruby_elsif + +ruby_elsif_alternative( + unique int ruby_elsif: @ruby_elsif ref, + unique int alternative: @ruby_elsif_alternative_type ref +); + +ruby_elsif_consequence( + unique int ruby_elsif: @ruby_elsif ref, + unique int consequence: @ruby_then ref +); + +ruby_elsif_def( + unique int id: @ruby_elsif, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_end_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_end_block, index] +ruby_end_block_child( + int ruby_end_block: @ruby_end_block ref, + int index: int ref, + unique int child: @ruby_end_block_child_type ref +); + +ruby_end_block_def( + unique int id: @ruby_end_block, + int loc: @location ref +); + +@ruby_ensure_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_ensure, index] +ruby_ensure_child( + int ruby_ensure: @ruby_ensure ref, + int index: int ref, + unique int child: @ruby_ensure_child_type ref +); + +ruby_ensure_def( + unique int id: @ruby_ensure, + int loc: @location ref +); + +ruby_exception_variable_def( + unique int id: @ruby_exception_variable, + int child: @ruby_underscore_lhs ref, + int loc: @location ref +); + +@ruby_exceptions_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_exceptions, index] +ruby_exceptions_child( + int ruby_exceptions: @ruby_exceptions ref, + int index: int ref, + unique int child: @ruby_exceptions_child_type ref +); + +ruby_exceptions_def( + unique int id: @ruby_exceptions, + int loc: @location ref +); + +@ruby_for_pattern_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +ruby_for_def( + unique int id: @ruby_for, + int body: @ruby_do ref, + int pattern: @ruby_for_pattern_type ref, + int value: @ruby_in ref, + int loc: @location ref +); + +@ruby_hash_child_type = @ruby_hash_splat_argument | @ruby_pair + +#keyset[ruby_hash, index] +ruby_hash_child( + int ruby_hash: @ruby_hash ref, + int index: int ref, + unique int child: @ruby_hash_child_type ref +); + +ruby_hash_def( + unique int id: @ruby_hash, + int loc: @location ref +); + +ruby_hash_splat_argument_def( + unique int id: @ruby_hash_splat_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_hash_splat_parameter_name( + unique int ruby_hash_splat_parameter: @ruby_hash_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_hash_splat_parameter_def( + unique int id: @ruby_hash_splat_parameter, + int loc: @location ref +); + +@ruby_heredoc_body_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_heredoc_content | @ruby_token_heredoc_end + +#keyset[ruby_heredoc_body, index] +ruby_heredoc_body_child( + int ruby_heredoc_body: @ruby_heredoc_body ref, + int index: int ref, + unique int child: @ruby_heredoc_body_child_type ref +); + +ruby_heredoc_body_def( + unique int id: @ruby_heredoc_body, + int loc: @location ref +); + +@ruby_if_alternative_type = @ruby_else | @ruby_elsif + +ruby_if_alternative( + unique int ruby_if: @ruby_if ref, + unique int alternative: @ruby_if_alternative_type ref +); + +ruby_if_consequence( + unique int ruby_if: @ruby_if ref, + unique int consequence: @ruby_then ref +); + +ruby_if_def( + unique int id: @ruby_if, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_if_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_if_modifier_def( + unique int id: @ruby_if_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_if_modifier_condition_type ref, + int loc: @location ref +); + +ruby_in_def( + unique int id: @ruby_in, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_interpolation_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_interpolation, index] +ruby_interpolation_child( + int ruby_interpolation: @ruby_interpolation ref, + int index: int ref, + unique int child: @ruby_interpolation_child_type ref +); + +ruby_interpolation_def( + unique int id: @ruby_interpolation, + int loc: @location ref +); + +ruby_keyword_parameter_value( + unique int ruby_keyword_parameter: @ruby_keyword_parameter ref, + unique int value: @ruby_underscore_arg ref +); + +ruby_keyword_parameter_def( + unique int id: @ruby_keyword_parameter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_lambda_body_type = @ruby_block | @ruby_do_block + +ruby_lambda_parameters( + unique int ruby_lambda: @ruby_lambda ref, + unique int parameters: @ruby_lambda_parameters ref +); + +ruby_lambda_def( + unique int id: @ruby_lambda, + int body: @ruby_lambda_body_type ref, + int loc: @location ref +); + +@ruby_lambda_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_identifier + +#keyset[ruby_lambda_parameters, index] +ruby_lambda_parameters_child( + int ruby_lambda_parameters: @ruby_lambda_parameters ref, + int index: int ref, + unique int child: @ruby_lambda_parameters_child_type ref +); + +ruby_lambda_parameters_def( + unique int id: @ruby_lambda_parameters, + int loc: @location ref +); + +@ruby_left_assignment_list_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_left_assignment_list, index] +ruby_left_assignment_list_child( + int ruby_left_assignment_list: @ruby_left_assignment_list ref, + int index: int ref, + unique int child: @ruby_left_assignment_list_child_type ref +); + +ruby_left_assignment_list_def( + unique int id: @ruby_left_assignment_list, + int loc: @location ref +); + +ruby_method_parameters( + unique int ruby_method: @ruby_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_method, index] +ruby_method_child( + int ruby_method: @ruby_method ref, + int index: int ref, + unique int child: @ruby_method_child_type ref +); + +ruby_method_def( + unique int id: @ruby_method, + int name: @ruby_underscore_method_name ref, + int loc: @location ref +); + +@ruby_method_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_identifier + +#keyset[ruby_method_parameters, index] +ruby_method_parameters_child( + int ruby_method_parameters: @ruby_method_parameters ref, + int index: int ref, + unique int child: @ruby_method_parameters_child_type ref +); + +ruby_method_parameters_def( + unique int id: @ruby_method_parameters, + int loc: @location ref +); + +@ruby_module_name_type = @ruby_scope_resolution | @ruby_token_constant + +@ruby_module_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_module, index] +ruby_module_child( + int ruby_module: @ruby_module ref, + int index: int ref, + unique int child: @ruby_module_child_type ref +); + +ruby_module_def( + unique int id: @ruby_module, + int name: @ruby_module_name_type ref, + int loc: @location ref +); + +ruby_next_child( + unique int ruby_next: @ruby_next ref, + unique int child: @ruby_argument_list ref +); + +ruby_next_def( + unique int id: @ruby_next, + int loc: @location ref +); + +case @ruby_operator_assignment.operator of + 0 = @ruby_operator_assignment_percentequal +| 1 = @ruby_operator_assignment_ampersandampersandequal +| 2 = @ruby_operator_assignment_ampersandequal +| 3 = @ruby_operator_assignment_starstarequal +| 4 = @ruby_operator_assignment_starequal +| 5 = @ruby_operator_assignment_plusequal +| 6 = @ruby_operator_assignment_minusequal +| 7 = @ruby_operator_assignment_slashequal +| 8 = @ruby_operator_assignment_langlelangleequal +| 9 = @ruby_operator_assignment_ranglerangleequal +| 10 = @ruby_operator_assignment_caretequal +| 11 = @ruby_operator_assignment_pipeequal +| 12 = @ruby_operator_assignment_pipepipeequal +; + + +@ruby_operator_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_operator_assignment_def( + unique int id: @ruby_operator_assignment, + int left: @ruby_underscore_lhs ref, + int operator: int ref, + int right: @ruby_operator_assignment_right_type ref, + int loc: @location ref +); + +ruby_optional_parameter_def( + unique int id: @ruby_optional_parameter, + int name: @ruby_token_identifier ref, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_pair_key_type = @ruby_string__ | @ruby_token_hash_key_symbol | @ruby_underscore_arg + +ruby_pair_def( + unique int id: @ruby_pair, + int key__: @ruby_pair_key_type ref, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_parenthesized_statements_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_parenthesized_statements, index] +ruby_parenthesized_statements_child( + int ruby_parenthesized_statements: @ruby_parenthesized_statements ref, + int index: int ref, + unique int child: @ruby_parenthesized_statements_child_type ref +); + +ruby_parenthesized_statements_def( + unique int id: @ruby_parenthesized_statements, + int loc: @location ref +); + +@ruby_pattern_child_type = @ruby_splat_argument | @ruby_underscore_arg + +ruby_pattern_def( + unique int id: @ruby_pattern, + int child: @ruby_pattern_child_type ref, + int loc: @location ref +); + +@ruby_program_child_type = @ruby_token_empty_statement | @ruby_token_uninterpreted | @ruby_underscore_statement + +#keyset[ruby_program, index] +ruby_program_child( + int ruby_program: @ruby_program ref, + int index: int ref, + unique int child: @ruby_program_child_type ref +); + +ruby_program_def( + unique int id: @ruby_program, + int loc: @location ref +); + +ruby_range_begin( + unique int ruby_range: @ruby_range ref, + unique int begin: @ruby_underscore_arg ref +); + +ruby_range_end( + unique int ruby_range: @ruby_range ref, + unique int end: @ruby_underscore_arg ref +); + +case @ruby_range.operator of + 0 = @ruby_range_dotdot +| 1 = @ruby_range_dotdotdot +; + + +ruby_range_def( + unique int id: @ruby_range, + int operator: int ref, + int loc: @location ref +); + +@ruby_rational_child_type = @ruby_token_float | @ruby_token_integer + +ruby_rational_def( + unique int id: @ruby_rational, + int child: @ruby_rational_child_type ref, + int loc: @location ref +); + +ruby_redo_child( + unique int ruby_redo: @ruby_redo ref, + unique int child: @ruby_argument_list ref +); + +ruby_redo_def( + unique int id: @ruby_redo, + int loc: @location ref +); + +@ruby_regex_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_regex, index] +ruby_regex_child( + int ruby_regex: @ruby_regex ref, + int index: int ref, + unique int child: @ruby_regex_child_type ref +); + +ruby_regex_def( + unique int id: @ruby_regex, + int loc: @location ref +); + +ruby_rescue_body( + unique int ruby_rescue: @ruby_rescue ref, + unique int body: @ruby_then ref +); + +ruby_rescue_exceptions( + unique int ruby_rescue: @ruby_rescue ref, + unique int exceptions: @ruby_exceptions ref +); + +ruby_rescue_variable( + unique int ruby_rescue: @ruby_rescue ref, + unique int variable: @ruby_exception_variable ref +); + +ruby_rescue_def( + unique int id: @ruby_rescue, + int loc: @location ref +); + +@ruby_rescue_modifier_handler_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_rescue_modifier_def( + unique int id: @ruby_rescue_modifier, + int body: @ruby_underscore_statement ref, + int handler: @ruby_rescue_modifier_handler_type ref, + int loc: @location ref +); + +ruby_rest_assignment_child( + unique int ruby_rest_assignment: @ruby_rest_assignment ref, + unique int child: @ruby_underscore_lhs ref +); + +ruby_rest_assignment_def( + unique int id: @ruby_rest_assignment, + int loc: @location ref +); + +ruby_retry_child( + unique int ruby_retry: @ruby_retry ref, + unique int child: @ruby_argument_list ref +); + +ruby_retry_def( + unique int id: @ruby_retry, + int loc: @location ref +); + +ruby_return_child( + unique int ruby_return: @ruby_return ref, + unique int child: @ruby_argument_list ref +); + +ruby_return_def( + unique int id: @ruby_return, + int loc: @location ref +); + +@ruby_right_assignment_list_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_right_assignment_list, index] +ruby_right_assignment_list_child( + int ruby_right_assignment_list: @ruby_right_assignment_list ref, + int index: int ref, + unique int child: @ruby_right_assignment_list_child_type ref +); + +ruby_right_assignment_list_def( + unique int id: @ruby_right_assignment_list, + int loc: @location ref +); + +@ruby_scope_resolution_name_type = @ruby_token_constant | @ruby_token_identifier + +ruby_scope_resolution_scope( + unique int ruby_scope_resolution: @ruby_scope_resolution ref, + unique int scope: @ruby_underscore_primary ref +); + +ruby_scope_resolution_def( + unique int id: @ruby_scope_resolution, + int name: @ruby_scope_resolution_name_type ref, + int loc: @location ref +); + +ruby_setter_def( + unique int id: @ruby_setter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_singleton_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_class, index] +ruby_singleton_class_child( + int ruby_singleton_class: @ruby_singleton_class ref, + int index: int ref, + unique int child: @ruby_singleton_class_child_type ref +); + +ruby_singleton_class_def( + unique int id: @ruby_singleton_class, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_singleton_method_object_type = @ruby_underscore_arg | @ruby_underscore_variable + +ruby_singleton_method_parameters( + unique int ruby_singleton_method: @ruby_singleton_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_singleton_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_method, index] +ruby_singleton_method_child( + int ruby_singleton_method: @ruby_singleton_method ref, + int index: int ref, + unique int child: @ruby_singleton_method_child_type ref +); + +ruby_singleton_method_def( + unique int id: @ruby_singleton_method, + int name: @ruby_underscore_method_name ref, + int object: @ruby_singleton_method_object_type ref, + int loc: @location ref +); + +ruby_splat_argument_def( + unique int id: @ruby_splat_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_splat_parameter_name( + unique int ruby_splat_parameter: @ruby_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_splat_parameter_def( + unique int id: @ruby_splat_parameter, + int loc: @location ref +); + +@ruby_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_string__, index] +ruby_string_child( + int ruby_string__: @ruby_string__ ref, + int index: int ref, + unique int child: @ruby_string_child_type ref +); + +ruby_string_def( + unique int id: @ruby_string__, + int loc: @location ref +); + +#keyset[ruby_string_array, index] +ruby_string_array_child( + int ruby_string_array: @ruby_string_array ref, + int index: int ref, + unique int child: @ruby_bare_string ref +); + +ruby_string_array_def( + unique int id: @ruby_string_array, + int loc: @location ref +); + +@ruby_subshell_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_subshell, index] +ruby_subshell_child( + int ruby_subshell: @ruby_subshell ref, + int index: int ref, + unique int child: @ruby_subshell_child_type ref +); + +ruby_subshell_def( + unique int id: @ruby_subshell, + int loc: @location ref +); + +@ruby_superclass_child_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_superclass_def( + unique int id: @ruby_superclass, + int child: @ruby_superclass_child_type ref, + int loc: @location ref +); + +#keyset[ruby_symbol_array, index] +ruby_symbol_array_child( + int ruby_symbol_array: @ruby_symbol_array ref, + int index: int ref, + unique int child: @ruby_bare_symbol ref +); + +ruby_symbol_array_def( + unique int id: @ruby_symbol_array, + int loc: @location ref +); + +@ruby_then_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_then, index] +ruby_then_child( + int ruby_then: @ruby_then ref, + int index: int ref, + unique int child: @ruby_then_child_type ref +); + +ruby_then_def( + unique int id: @ruby_then, + int loc: @location ref +); + +@ruby_unary_operand_type = @ruby_break | @ruby_call | @ruby_next | @ruby_parenthesized_statements | @ruby_return | @ruby_token_float | @ruby_token_integer | @ruby_underscore_arg | @ruby_yield + +case @ruby_unary.operator of + 0 = @ruby_unary_bang +| 1 = @ruby_unary_plus +| 2 = @ruby_unary_minus +| 3 = @ruby_unary_definedquestion +| 4 = @ruby_unary_not +| 5 = @ruby_unary_tilde +; + + +ruby_unary_def( + unique int id: @ruby_unary, + int operand: @ruby_unary_operand_type ref, + int operator: int ref, + int loc: @location ref +); + +#keyset[ruby_undef, index] +ruby_undef_child( + int ruby_undef: @ruby_undef ref, + int index: int ref, + unique int child: @ruby_underscore_method_name ref +); + +ruby_undef_def( + unique int id: @ruby_undef, + int loc: @location ref +); + +@ruby_unless_alternative_type = @ruby_else | @ruby_elsif + +ruby_unless_alternative( + unique int ruby_unless: @ruby_unless ref, + unique int alternative: @ruby_unless_alternative_type ref +); + +ruby_unless_consequence( + unique int ruby_unless: @ruby_unless ref, + unique int consequence: @ruby_then ref +); + +ruby_unless_def( + unique int id: @ruby_unless, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_unless_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_unless_modifier_def( + unique int id: @ruby_unless_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_unless_modifier_condition_type ref, + int loc: @location ref +); + +ruby_until_def( + unique int id: @ruby_until, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_until_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_until_modifier_def( + unique int id: @ruby_until_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_until_modifier_condition_type ref, + int loc: @location ref +); + +ruby_when_body( + unique int ruby_when: @ruby_when ref, + unique int body: @ruby_then ref +); + +#keyset[ruby_when, index] +ruby_when_pattern( + int ruby_when: @ruby_when ref, + int index: int ref, + unique int pattern: @ruby_pattern ref +); + +ruby_when_def( + unique int id: @ruby_when, + int loc: @location ref +); + +ruby_while_def( + unique int id: @ruby_while, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_while_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_while_modifier_def( + unique int id: @ruby_while_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_while_modifier_condition_type ref, + int loc: @location ref +); + +ruby_yield_child( + unique int ruby_yield: @ruby_yield ref, + unique int child: @ruby_argument_list ref +); + +ruby_yield_def( + unique int id: @ruby_yield, + int loc: @location ref +); + +ruby_tokeninfo( + unique int id: @ruby_token, + int kind: int ref, + string value: string ref, + int loc: @location ref +); + +case @ruby_token.kind of + 0 = @ruby_reserved_word +| 1 = @ruby_token_character +| 2 = @ruby_token_class_variable +| 3 = @ruby_token_comment +| 4 = @ruby_token_complex +| 5 = @ruby_token_constant +| 6 = @ruby_token_empty_statement +| 7 = @ruby_token_escape_sequence +| 8 = @ruby_token_false +| 9 = @ruby_token_float +| 10 = @ruby_token_forward_argument +| 11 = @ruby_token_forward_parameter +| 12 = @ruby_token_global_variable +| 13 = @ruby_token_hash_key_symbol +| 14 = @ruby_token_heredoc_beginning +| 15 = @ruby_token_heredoc_content +| 16 = @ruby_token_heredoc_end +| 17 = @ruby_token_identifier +| 18 = @ruby_token_instance_variable +| 19 = @ruby_token_integer +| 20 = @ruby_token_nil +| 21 = @ruby_token_operator +| 22 = @ruby_token_self +| 23 = @ruby_token_simple_symbol +| 24 = @ruby_token_string_content +| 25 = @ruby_token_super +| 26 = @ruby_token_true +| 27 = @ruby_token_uninterpreted +; + + +@ruby_ast_node = @ruby_alias | @ruby_argument_list | @ruby_array | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_parameter | @ruby_block_parameters | @ruby_break | @ruby_call | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_for | @ruby_hash | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_modifier | @ruby_in | @ruby_interpolation | @ruby_keyword_parameter | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield + +@ruby_ast_node_parent = @file | @ruby_ast_node + +#keyset[parent, parent_index] +ruby_ast_node_parent( + int child: @ruby_ast_node ref, + int parent: @ruby_ast_node_parent ref, + int parent_index: int ref +); + +erb_comment_directive_def( + unique int id: @erb_comment_directive, + int child: @erb_token_comment ref, + int loc: @location ref +); + +erb_directive_def( + unique int id: @erb_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +erb_graphql_directive_def( + unique int id: @erb_graphql_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +erb_output_directive_def( + unique int id: @erb_output_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +@erb_template_child_type = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_token_content + +#keyset[erb_template, index] +erb_template_child( + int erb_template: @erb_template ref, + int index: int ref, + unique int child: @erb_template_child_type ref +); + +erb_template_def( + unique int id: @erb_template, + int loc: @location ref +); + +erb_tokeninfo( + unique int id: @erb_token, + int kind: int ref, + string value: string ref, + int loc: @location ref +); + +case @erb_token.kind of + 0 = @erb_reserved_word +| 1 = @erb_token_code +| 2 = @erb_token_comment +| 3 = @erb_token_content +; + + +@erb_ast_node = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_template | @erb_token + +@erb_ast_node_parent = @erb_ast_node | @file + +#keyset[parent, parent_index] +erb_ast_node_parent( + int child: @erb_ast_node ref, + int parent: @erb_ast_node_parent ref, + int parent_index: int ref +); + diff --git a/ruby/ql/lib/ruby.dbscheme.stats b/ruby/ql/lib/ruby.dbscheme.stats new file mode 100644 index 00000000000..19858c66f58 --- /dev/null +++ b/ruby/ql/lib/ruby.dbscheme.stats @@ -0,0 +1,25270 @@ +<dbstats> + <typesizes><e> + <k>@diagnostic_debug</k> + <v>0</v> + </e> + <e> + <k>@diagnostic_error</k> + <v>43</v> + </e> + <e> + <k>@diagnostic_info</k> + <v>0</v> + </e> + <e> + <k>@diagnostic_warning</k> + <v>0</v> + </e> + <e> + <k>@erb_comment_directive</k> + <v>13</v> + </e> + <e> + <k>@erb_directive</k> + <v>479</v> + </e> + <e> + <k>@erb_graphql_directive</k> + <v>0</v> + </e> + <e> + <k>@erb_output_directive</k> + <v>1294</v> + </e> + <e> + <k>@erb_reserved_word</k> + <v>3546</v> + </e> + <e> + <k>@erb_template</k> + <v>508</v> + </e> + <e> + <k>@erb_token_code</k> + <v>1773</v> + </e> + <e> + <k>@erb_token_comment</k> + <v>13</v> + </e> + <e> + <k>@erb_token_content</k> + <v>1268</v> + </e> + <e> + <k>@file</k> + <v>5104</v> + </e> + <e> + <k>@folder</k> + <v>1445</v> + </e> + <e> + <k>@location_default</k> + <v>2732781</v> + </e> + <e> + <k>@ruby_alias</k> + <v>410</v> + </e> + <e> + <k>@ruby_argument_list</k> + <v>222600</v> + </e> + <e> + <k>@ruby_array</k> + <v>10913</v> + </e> + <e> + <k>@ruby_assignment</k> + <v>41784</v> + </e> + <e> + <k>@ruby_bare_string</k> + <v>3119</v> + </e> + <e> + <k>@ruby_bare_symbol</k> + <v>685</v> + </e> + <e> + <k>@ruby_begin</k> + <v>641</v> + </e> + <e> + <k>@ruby_begin_block</k> + <v>0</v> + </e> + <e> + <k>@ruby_binary_ampersand</k> + <v>44</v> + </e> + <e> + <k>@ruby_binary_ampersandampersand</k> + <v>2870</v> + </e> + <e> + <k>@ruby_binary_and</k> + <v>57</v> + </e> + <e> + <k>@ruby_binary_bangequal</k> + <v>516</v> + </e> + <e> + <k>@ruby_binary_bangtilde</k> + <v>38</v> + </e> + <e> + <k>@ruby_binary_caret</k> + <v>29</v> + </e> + <e> + <k>@ruby_binary_equalequal</k> + <v>2581</v> + </e> + <e> + <k>@ruby_binary_equalequalequal</k> + <v>180</v> + </e> + <e> + <k>@ruby_binary_equaltilde</k> + <v>260</v> + </e> + <e> + <k>@ruby_binary_langle</k> + <v>439</v> + </e> + <e> + <k>@ruby_binary_langleequal</k> + <v>93</v> + </e> + <e> + <k>@ruby_binary_langleequalrangle</k> + <v>85</v> + </e> + <e> + <k>@ruby_binary_langlelangle</k> + <v>3396</v> + </e> + <e> + <k>@ruby_binary_minus</k> + <v>663</v> + </e> + <e> + <k>@ruby_binary_or</k> + <v>4</v> + </e> + <e> + <k>@ruby_binary_percent</k> + <v>141</v> + </e> + <e> + <k>@ruby_binary_pipe</k> + <v>43</v> + </e> + <e> + <k>@ruby_binary_pipepipe</k> + <v>2626</v> + </e> + <e> + <k>@ruby_binary_plus</k> + <v>1538</v> + </e> + <e> + <k>@ruby_binary_rangle</k> + <v>796</v> + </e> + <e> + <k>@ruby_binary_rangleequal</k> + <v>138</v> + </e> + <e> + <k>@ruby_binary_ranglerangle</k> + <v>6</v> + </e> + <e> + <k>@ruby_binary_slash</k> + <v>158</v> + </e> + <e> + <k>@ruby_binary_star</k> + <v>412</v> + </e> + <e> + <k>@ruby_binary_starstar</k> + <v>39</v> + </e> + <e> + <k>@ruby_block</k> + <v>28681</v> + </e> + <e> + <k>@ruby_block_argument</k> + <v>1945</v> + </e> + <e> + <k>@ruby_block_parameter</k> + <v>775</v> + </e> + <e> + <k>@ruby_block_parameters</k> + <v>7513</v> + </e> + <e> + <k>@ruby_break</k> + <v>220</v> + </e> + <e> + <k>@ruby_call</k> + <v>319372</v> + </e> + <e> + <k>@ruby_case__</k> + <v>389</v> + </e> + <e> + <k>@ruby_chained_string</k> + <v>288</v> + </e> + <e> + <k>@ruby_class</k> + <v>5404</v> + </e> + <e> + <k>@ruby_conditional</k> + <v>1153</v> + </e> + <e> + <k>@ruby_delimited_symbol</k> + <v>394</v> + </e> + <e> + <k>@ruby_destructured_left_assignment</k> + <v>1</v> + </e> + <e> + <k>@ruby_destructured_parameter</k> + <v>64</v> + </e> + <e> + <k>@ruby_do</k> + <v>120</v> + </e> + <e> + <k>@ruby_do_block</k> + <v>44478</v> + </e> + <e> + <k>@ruby_element_reference</k> + <v>26646</v> + </e> + <e> + <k>@ruby_else</k> + <v>2228</v> + </e> + <e> + <k>@ruby_elsif</k> + <v>506</v> + </e> + <e> + <k>@ruby_end_block</k> + <v>0</v> + </e> + <e> + <k>@ruby_ensure</k> + <v>1216</v> + </e> + <e> + <k>@ruby_exception_variable</k> + <v>328</v> + </e> + <e> + <k>@ruby_exceptions</k> + <v>454</v> + </e> + <e> + <k>@ruby_for</k> + <v>1</v> + </e> + <e> + <k>@ruby_hash</k> + <v>8337</v> + </e> + <e> + <k>@ruby_hash_splat_argument</k> + <v>427</v> + </e> + <e> + <k>@ruby_hash_splat_parameter</k> + <v>432</v> + </e> + <e> + <k>@ruby_heredoc_body</k> + <v>1702</v> + </e> + <e> + <k>@ruby_if</k> + <v>5991</v> + </e> + <e> + <k>@ruby_if_modifier</k> + <v>4440</v> + </e> + <e> + <k>@ruby_in</k> + <v>1</v> + </e> + <e> + <k>@ruby_interpolation</k> + <v>12427</v> + </e> + <e> + <k>@ruby_keyword_parameter</k> + <v>1152</v> + </e> + <e> + <k>@ruby_lambda</k> + <v>852</v> + </e> + <e> + <k>@ruby_lambda_parameters</k> + <v>201</v> + </e> + <e> + <k>@ruby_left_assignment_list</k> + <v>817</v> + </e> + <e> + <k>@ruby_method</k> + <v>31713</v> + </e> + <e> + <k>@ruby_method_parameters</k> + <v>9313</v> + </e> + <e> + <k>@ruby_module</k> + <v>6369</v> + </e> + <e> + <k>@ruby_next</k> + <v>664</v> + </e> + <e> + <k>@ruby_operator_assignment_ampersandampersandequal</k> + <v>5</v> + </e> + <e> + <k>@ruby_operator_assignment_ampersandequal</k> + <v>5</v> + </e> + <e> + <k>@ruby_operator_assignment_caretequal</k> + <v>0</v> + </e> + <e> + <k>@ruby_operator_assignment_langlelangleequal</k> + <v>0</v> + </e> + <e> + <k>@ruby_operator_assignment_minusequal</k> + <v>64</v> + </e> + <e> + <k>@ruby_operator_assignment_percentequal</k> + <v>8</v> + </e> + <e> + <k>@ruby_operator_assignment_pipeequal</k> + <v>45</v> + </e> + <e> + <k>@ruby_operator_assignment_pipepipeequal</k> + <v>1510</v> + </e> + <e> + <k>@ruby_operator_assignment_plusequal</k> + <v>518</v> + </e> + <e> + <k>@ruby_operator_assignment_ranglerangleequal</k> + <v>0</v> + </e> + <e> + <k>@ruby_operator_assignment_slashequal</k> + <v>4</v> + </e> + <e> + <k>@ruby_operator_assignment_starequal</k> + <v>2</v> + </e> + <e> + <k>@ruby_operator_assignment_starstarequal</k> + <v>0</v> + </e> + <e> + <k>@ruby_optional_parameter</k> + <v>2105</v> + </e> + <e> + <k>@ruby_pair</k> + <v>65283</v> + </e> + <e> + <k>@ruby_parenthesized_statements</k> + <v>1771</v> + </e> + <e> + <k>@ruby_pattern</k> + <v>1234</v> + </e> + <e> + <k>@ruby_program</k> + <v>5100</v> + </e> + <e> + <k>@ruby_range_dotdot</k> + <v>449</v> + </e> + <e> + <k>@ruby_range_dotdotdot</k> + <v>128</v> + </e> + <e> + <k>@ruby_rational</k> + <v>4</v> + </e> + <e> + <k>@ruby_redo</k> + <v>0</v> + </e> + <e> + <k>@ruby_regex</k> + <v>4134</v> + </e> + <e> + <k>@ruby_rescue</k> + <v>669</v> + </e> + <e> + <k>@ruby_rescue_modifier</k> + <v>184</v> + </e> + <e> + <k>@ruby_reserved_word</k> + <v>1062894</v> + </e> + <e> + <k>@ruby_rest_assignment</k> + <v>18</v> + </e> + <e> + <k>@ruby_retry</k> + <v>12</v> + </e> + <e> + <k>@ruby_return</k> + <v>2746</v> + </e> + <e> + <k>@ruby_right_assignment_list</k> + <v>440</v> + </e> + <e> + <k>@ruby_scope_resolution</k> + <v>24908</v> + </e> + <e> + <k>@ruby_setter</k> + <v>195</v> + </e> + <e> + <k>@ruby_singleton_class</k> + <v>198</v> + </e> + <e> + <k>@ruby_singleton_method</k> + <v>2139</v> + </e> + <e> + <k>@ruby_splat_argument</k> + <v>693</v> + </e> + <e> + <k>@ruby_splat_parameter</k> + <v>946</v> + </e> + <e> + <k>@ruby_string__</k> + <v>95938</v> + </e> + <e> + <k>@ruby_string_array</k> + <v>977</v> + </e> + <e> + <k>@ruby_subshell</k> + <v>134</v> + </e> + <e> + <k>@ruby_superclass</k> + <v>4276</v> + </e> + <e> + <k>@ruby_symbol_array</k> + <v>140</v> + </e> + <e> + <k>@ruby_then</k> + <v>7974</v> + </e> + <e> + <k>@ruby_token_character</k> + <v>10</v> + </e> + <e> + <k>@ruby_token_class_variable</k> + <v>364</v> + </e> + <e> + <k>@ruby_token_comment</k> + <v>57899</v> + </e> + <e> + <k>@ruby_token_complex</k> + <v>0</v> + </e> + <e> + <k>@ruby_token_constant</k> + <v>90944</v> + </e> + <e> + <k>@ruby_token_empty_statement</k> + <v>0</v> + </e> + <e> + <k>@ruby_token_escape_sequence</k> + <v>20910</v> + </e> + <e> + <k>@ruby_token_false</k> + <v>5412</v> + </e> + <e> + <k>@ruby_token_float</k> + <v>2512</v> + </e> + <e> + <k>@ruby_token_forward_argument</k> + <v>16</v> + </e> + <e> + <k>@ruby_token_forward_parameter</k> + <v>18</v> + </e> + <e> + <k>@ruby_token_global_variable</k> + <v>758</v> + </e> + <e> + <k>@ruby_token_hash_key_symbol</k> + <v>63614</v> + </e> + <e> + <k>@ruby_token_heredoc_beginning</k> + <v>1702</v> + </e> + <e> + <k>@ruby_token_heredoc_content</k> + <v>4009</v> + </e> + <e> + <k>@ruby_token_heredoc_end</k> + <v>1702</v> + </e> + <e> + <k>@ruby_token_identifier</k> + <v>488470</v> + </e> + <e> + <k>@ruby_token_instance_variable</k> + <v>26229</v> + </e> + <e> + <k>@ruby_token_integer</k> + <v>34151</v> + </e> + <e> + <k>@ruby_token_nil</k> + <v>4230</v> + </e> + <e> + <k>@ruby_token_operator</k> + <v>199</v> + </e> + <e> + <k>@ruby_token_self</k> + <v>4215</v> + </e> + <e> + <k>@ruby_token_simple_symbol</k> + <v>79859</v> + </e> + <e> + <k>@ruby_token_string_content</k> + <v>121039</v> + </e> + <e> + <k>@ruby_token_super</k> + <v>1611</v> + </e> + <e> + <k>@ruby_token_true</k> + <v>7426</v> + </e> + <e> + <k>@ruby_token_uninterpreted</k> + <v>0</v> + </e> + <e> + <k>@ruby_unary_bang</k> + <v>1819</v> + </e> + <e> + <k>@ruby_unary_definedquestion</k> + <v>246</v> + </e> + <e> + <k>@ruby_unary_minus</k> + <v>674</v> + </e> + <e> + <k>@ruby_unary_not</k> + <v>10</v> + </e> + <e> + <k>@ruby_unary_plus</k> + <v>450</v> + </e> + <e> + <k>@ruby_unary_tilde</k> + <v>4</v> + </e> + <e> + <k>@ruby_undef</k> + <v>13</v> + </e> + <e> + <k>@ruby_unless</k> + <v>471</v> + </e> + <e> + <k>@ruby_unless_modifier</k> + <v>1435</v> + </e> + <e> + <k>@ruby_until</k> + <v>16</v> + </e> + <e> + <k>@ruby_until_modifier</k> + <v>13</v> + </e> + <e> + <k>@ruby_when</k> + <v>1025</v> + </e> + <e> + <k>@ruby_while</k> + <v>109</v> + </e> + <e> + <k>@ruby_while_modifier</k> + <v>9</v> + </e> + <e> + <k>@ruby_yield</k> + <v>774</v> + </e> + </typesizes> + <stats><relation> + <name>containerparent</name> + <cardinality>6541</cardinality> + <columnsizes> + <e> + <k>parent</k> + <v>1445</v> + </e> + <e> + <k>child</k> + <v>6541</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>parent</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>645</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>259</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>96</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>131</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>123</v> + </b> + <b> + <a>7</a> + <b>13</b> + <v>114</v> + </b> + <b> + <a>13</a> + <b>116</b> + <v>74</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>parent</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6541</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>diagnostics</name> + <cardinality>43</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>43</v> + </e> + <e> + <k>severity</k> + <v>4</v> + </e> + <e> + <k>error_tag</k> + <v>4</v> + </e> + <e> + <k>error_message</k> + <v>8</v> + </e> + <e> + <k>full_error_message</k> + <v>35</v> + </e> + <e> + <k>location</k> + <v>43</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>severity</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>43</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>error_tag</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>43</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>error_message</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>43</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>full_error_message</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>43</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>location</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>43</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>severity</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>10</a> + <b>11</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>severity</src> + <trg>error_tag</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>severity</src> + <trg>error_message</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>severity</src> + <trg>full_error_message</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>8</a> + <b>9</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>severity</src> + <trg>location</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>10</a> + <b>11</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>error_tag</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>10</a> + <b>11</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>error_tag</src> + <trg>severity</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>error_tag</src> + <trg>error_message</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>error_tag</src> + <trg>full_error_message</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>8</a> + <b>9</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>error_tag</src> + <trg>location</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>10</a> + <b>11</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>error_message</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>error_message</src> + <trg>severity</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>error_message</src> + <trg>error_tag</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>error_message</src> + <trg>full_error_message</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>error_message</src> + <trg>location</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>full_error_message</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>26</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>8</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>full_error_message</src> + <trg>severity</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>35</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>full_error_message</src> + <trg>error_tag</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>35</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>full_error_message</src> + <trg>error_message</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>35</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>full_error_message</src> + <trg>location</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>26</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>8</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>location</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>43</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>location</src> + <trg>severity</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>43</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>location</src> + <trg>error_tag</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>43</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>location</src> + <trg>error_message</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>43</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>location</src> + <trg>full_error_message</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>43</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>erb_ast_node_parent</name> + <cardinality>8501</cardinality> + <columnsizes> + <e> + <k>child</k> + <v>8501</v> + </e> + <e> + <k>parent</k> + <v>2055</v> + </e> + <e> + <k>parent_index</k> + <v>249</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>child</src> + <trg>parent</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8501</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>parent_index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8501</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parent</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>3</b> + <v>149</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>1820</v> + </b> + <b> + <a>4</a> + <b>250</b> + <v>86</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parent</src> + <trg>parent_index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>3</b> + <v>149</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>1820</v> + </b> + <b> + <a>4</a> + <b>250</b> + <v>86</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parent_index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>53</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>38</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>38</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>24</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>20</v> + </b> + <b> + <a>8</a> + <b>13</b> + <v>20</v> + </b> + <b> + <a>14</a> + <b>24</b> + <v>20</v> + </b> + <b> + <a>24</a> + <b>47</b> + <v>19</v> + </b> + <b> + <a>49</a> + <b>2056</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parent_index</src> + <trg>parent</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>53</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>38</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>38</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>24</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>20</v> + </b> + <b> + <a>8</a> + <b>13</b> + <v>20</v> + </b> + <b> + <a>14</a> + <b>24</b> + <v>20</v> + </b> + <b> + <a>24</a> + <b>47</b> + <v>19</v> + </b> + <b> + <a>49</a> + <b>2056</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>erb_comment_directive_def</name> + <cardinality>13</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>13</v> + </e> + <e> + <k>child</k> + <v>13</v> + </e> + <e> + <k>loc</k> + <v>13</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>erb_directive_def</name> + <cardinality>479</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>479</v> + </e> + <e> + <k>child</k> + <v>479</v> + </e> + <e> + <k>loc</k> + <v>479</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>479</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>479</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>479</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>479</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>479</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>479</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>erb_graphql_directive_def</name> + <cardinality>0</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>0</v> + </e> + <e> + <k>child</k> + <v>0</v> + </e> + <e> + <k>loc</k> + <v>0</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>erb_output_directive_def</name> + <cardinality>1294</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1294</v> + </e> + <e> + <k>child</k> + <v>1294</v> + </e> + <e> + <k>loc</k> + <v>1294</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1294</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1294</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1294</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1294</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1294</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1294</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>erb_template_child</name> + <cardinality>3041</cardinality> + <columnsizes> + <e> + <k>erb_template</k> + <v>141</v> + </e> + <e> + <k>index</k> + <v>249</v> + </e> + <e> + <k>child</k> + <v>3041</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>erb_template</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>3</b> + <v>8</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>47</v> + </b> + <b> + <a>4</a> + <b>7</b> + <v>9</v> + </b> + <b> + <a>7</a> + <b>9</b> + <v>10</v> + </b> + <b> + <a>9</a> + <b>13</b> + <v>12</v> + </b> + <b> + <a>13</a> + <b>20</b> + <v>11</v> + </b> + <b> + <a>20</a> + <b>30</b> + <v>11</v> + </b> + <b> + <a>30</a> + <b>42</b> + <v>11</v> + </b> + <b> + <a>42</a> + <b>62</b> + <v>11</v> + </b> + <b> + <a>65</a> + <b>250</b> + <v>11</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>erb_template</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>3</b> + <v>8</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>47</v> + </b> + <b> + <a>4</a> + <b>7</b> + <v>9</v> + </b> + <b> + <a>7</a> + <b>9</b> + <v>10</v> + </b> + <b> + <a>9</a> + <b>13</b> + <v>12</v> + </b> + <b> + <a>13</a> + <b>20</b> + <v>11</v> + </b> + <b> + <a>20</a> + <b>30</b> + <v>11</v> + </b> + <b> + <a>30</a> + <b>42</b> + <v>11</v> + </b> + <b> + <a>42</a> + <b>62</b> + <v>11</v> + </b> + <b> + <a>65</a> + <b>250</b> + <v>11</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>erb_template</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>53</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>38</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>38</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>24</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>20</v> + </b> + <b> + <a>8</a> + <b>13</b> + <v>20</v> + </b> + <b> + <a>14</a> + <b>24</b> + <v>20</v> + </b> + <b> + <a>24</a> + <b>47</b> + <v>19</v> + </b> + <b> + <a>49</a> + <b>142</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>53</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>38</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>38</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>24</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>20</v> + </b> + <b> + <a>8</a> + <b>13</b> + <v>20</v> + </b> + <b> + <a>14</a> + <b>24</b> + <v>20</v> + </b> + <b> + <a>24</a> + <b>47</b> + <v>19</v> + </b> + <b> + <a>49</a> + <b>142</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>erb_template</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3041</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3041</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>erb_template_def</name> + <cardinality>508</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>508</v> + </e> + <e> + <k>loc</k> + <v>508</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>508</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>508</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>erb_tokeninfo</name> + <cardinality>6587</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>6587</v> + </e> + <e> + <k>kind</k> + <v>3</v> + </e> + <e> + <k>value</k> + <v>1945</v> + </e> + <e> + <k>loc</k> + <v>6587</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>kind</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6587</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6587</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6587</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>kind</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1268</a> + <b>1269</b> + <v>1</v> + </b> + <b> + <a>1773</a> + <b>1774</b> + <v>1</v> + </b> + <b> + <a>3546</a> + <b>3547</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>kind</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>53</a> + <b>54</b> + <v>1</v> + </b> + <b> + <a>869</a> + <b>870</b> + <v>1</v> + </b> + <b> + <a>1023</a> + <b>1024</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>kind</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1268</a> + <b>1269</b> + <v>1</v> + </b> + <b> + <a>1773</a> + <b>1774</b> + <v>1</v> + </b> + <b> + <a>3546</a> + <b>3547</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1540</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>245</v> + </b> + <b> + <a>3</a> + <b>27</b> + <v>146</v> + </b> + <b> + <a>32</a> + <b>1704</b> + <v>14</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>kind</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1945</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1540</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>245</v> + </b> + <b> + <a>3</a> + <b>27</b> + <v>146</v> + </b> + <b> + <a>32</a> + <b>1704</b> + <v>14</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6587</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>kind</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6587</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6587</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>files</name> + <cardinality>5104</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>5104</v> + </e> + <e> + <k>name</k> + <v>5104</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5104</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5104</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>folders</name> + <cardinality>1445</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1445</v> + </e> + <e> + <k>name</k> + <v>1445</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1445</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1445</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>locations_default</name> + <cardinality>2732781</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>2732781</v> + </e> + <e> + <k>file</k> + <v>5104</v> + </e> + <e> + <k>start_line</k> + <v>6510</v> + </e> + <e> + <k>start_column</k> + <v>1287</v> + </e> + <e> + <k>end_line</k> + <v>6510</v> + </e> + <e> + <k>end_column</k> + <v>1287</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>file</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2732781</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>start_line</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2732781</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>start_column</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2732781</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>end_line</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2732781</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>end_column</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2732781</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>file</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>34</b> + <v>386</v> + </b> + <b> + <a>34</a> + <b>53</b> + <v>390</v> + </b> + <b> + <a>53</a> + <b>80</b> + <v>390</v> + </b> + <b> + <a>80</a> + <b>98</b> + <v>390</v> + </b> + <b> + <a>98</a> + <b>136</b> + <v>395</v> + </b> + <b> + <a>136</a> + <b>182</b> + <v>386</v> + </b> + <b> + <a>182</a> + <b>231</b> + <v>386</v> + </b> + <b> + <a>231</a> + <b>293</b> + <v>390</v> + </b> + <b> + <a>294</a> + <b>381</b> + <v>386</v> + </b> + <b> + <a>381</a> + <b>505</b> + <v>395</v> + </b> + <b> + <a>506</a> + <b>774</b> + <v>386</v> + </b> + <b> + <a>775</a> + <b>1367</b> + <v>386</v> + </b> + <b> + <a>1380</a> + <b>5613</b> + <v>386</v> + </b> + <b> + <a>6171</a> + <b>15687</b> + <v>43</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>file</src> + <trg>start_line</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>7</b> + <v>325</v> + </b> + <b> + <a>7</a> + <b>9</b> + <v>395</v> + </b> + <b> + <a>9</a> + <b>13</b> + <v>465</v> + </b> + <b> + <a>13</a> + <b>16</b> + <v>355</v> + </b> + <b> + <a>16</a> + <b>20</b> + <v>399</v> + </b> + <b> + <a>20</a> + <b>25</b> + <v>408</v> + </b> + <b> + <a>25</a> + <b>31</b> + <v>421</v> + </b> + <b> + <a>31</a> + <b>39</b> + <v>461</v> + </b> + <b> + <a>39</a> + <b>49</b> + <v>390</v> + </b> + <b> + <a>49</a> + <b>69</b> + <v>412</v> + </b> + <b> + <a>69</a> + <b>105</b> + <v>386</v> + </b> + <b> + <a>105</a> + <b>196</b> + <v>386</v> + </b> + <b> + <a>196</a> + <b>1242</b> + <v>294</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>file</src> + <trg>start_column</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>17</b> + <v>439</v> + </b> + <b> + <a>17</a> + <b>28</b> + <v>465</v> + </b> + <b> + <a>28</a> + <b>37</b> + <v>386</v> + </b> + <b> + <a>37</a> + <b>44</b> + <v>461</v> + </b> + <b> + <a>44</a> + <b>51</b> + <v>426</v> + </b> + <b> + <a>51</a> + <b>59</b> + <v>421</v> + </b> + <b> + <a>59</a> + <b>65</b> + <v>390</v> + </b> + <b> + <a>65</a> + <b>71</b> + <v>386</v> + </b> + <b> + <a>71</a> + <b>79</b> + <v>404</v> + </b> + <b> + <a>79</a> + <b>88</b> + <v>390</v> + </b> + <b> + <a>88</a> + <b>102</b> + <v>395</v> + </b> + <b> + <a>102</a> + <b>129</b> + <v>395</v> + </b> + <b> + <a>129</a> + <b>209</b> + <v>140</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>file</src> + <trg>end_line</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>7</b> + <v>325</v> + </b> + <b> + <a>7</a> + <b>10</b> + <v>439</v> + </b> + <b> + <a>10</a> + <b>13</b> + <v>417</v> + </b> + <b> + <a>13</a> + <b>16</b> + <v>347</v> + </b> + <b> + <a>16</a> + <b>20</b> + <v>412</v> + </b> + <b> + <a>20</a> + <b>25</b> + <v>408</v> + </b> + <b> + <a>25</a> + <b>31</b> + <v>421</v> + </b> + <b> + <a>31</a> + <b>39</b> + <v>461</v> + </b> + <b> + <a>39</a> + <b>49</b> + <v>390</v> + </b> + <b> + <a>49</a> + <b>69</b> + <v>412</v> + </b> + <b> + <a>69</a> + <b>105</b> + <v>386</v> + </b> + <b> + <a>105</a> + <b>196</b> + <v>386</v> + </b> + <b> + <a>196</a> + <b>1242</b> + <v>294</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>file</src> + <trg>end_column</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>21</b> + <v>430</v> + </b> + <b> + <a>21</a> + <b>31</b> + <v>390</v> + </b> + <b> + <a>31</a> + <b>40</b> + <v>426</v> + </b> + <b> + <a>40</a> + <b>47</b> + <v>386</v> + </b> + <b> + <a>47</a> + <b>53</b> + <v>399</v> + </b> + <b> + <a>53</a> + <b>61</b> + <v>430</v> + </b> + <b> + <a>61</a> + <b>68</b> + <v>390</v> + </b> + <b> + <a>68</a> + <b>75</b> + <v>439</v> + </b> + <b> + <a>75</a> + <b>82</b> + <v>399</v> + </b> + <b> + <a>82</a> + <b>91</b> + <v>399</v> + </b> + <b> + <a>91</a> + <b>102</b> + <v>386</v> + </b> + <b> + <a>102</a> + <b>123</b> + <v>399</v> + </b> + <b> + <a>123</a> + <b>212</b> + <v>224</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>start_line</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>8</b> + <v>483</v> + </b> + <b> + <a>8</a> + <b>11</b> + <v>496</v> + </b> + <b> + <a>11</a> + <b>20</b> + <v>500</v> + </b> + <b> + <a>20</a> + <b>29</b> + <v>518</v> + </b> + <b> + <a>29</a> + <b>52</b> + <v>496</v> + </b> + <b> + <a>52</a> + <b>83</b> + <v>496</v> + </b> + <b> + <a>83</a> + <b>112</b> + <v>492</v> + </b> + <b> + <a>112</a> + <b>154</b> + <v>492</v> + </b> + <b> + <a>154</a> + <b>205</b> + <v>500</v> + </b> + <b> + <a>205</a> + <b>289</b> + <v>496</v> + </b> + <b> + <a>289</a> + <b>534</b> + <v>492</v> + </b> + <b> + <a>539</a> + <b>1169</b> + <v>496</v> + </b> + <b> + <a>1177</a> + <b>6299</b> + <v>492</v> + </b> + <b> + <a>6403</a> + <b>9021</b> + <v>57</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>start_line</src> + <trg>file</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1423</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>663</v> + </b> + <b> + <a>3</a> + <b>6</b> + <v>588</v> + </b> + <b> + <a>6</a> + <b>10</b> + <v>584</v> + </b> + <b> + <a>10</a> + <b>14</b> + <v>509</v> + </b> + <b> + <a>14</a> + <b>19</b> + <v>557</v> + </b> + <b> + <a>19</a> + <b>27</b> + <v>500</v> + </b> + <b> + <a>27</a> + <b>45</b> + <v>509</v> + </b> + <b> + <a>45</a> + <b>104</b> + <v>492</v> + </b> + <b> + <a>104</a> + <b>377</b> + <v>492</v> + </b> + <b> + <a>408</a> + <b>1162</b> + <v>188</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>start_line</src> + <trg>start_column</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>5</b> + <v>470</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>527</v> + </b> + <b> + <a>8</a> + <b>14</b> + <v>474</v> + </b> + <b> + <a>14</a> + <b>19</b> + <v>509</v> + </b> + <b> + <a>19</a> + <b>29</b> + <v>505</v> + </b> + <b> + <a>29</a> + <b>40</b> + <v>513</v> + </b> + <b> + <a>40</a> + <b>49</b> + <v>509</v> + </b> + <b> + <a>49</a> + <b>59</b> + <v>505</v> + </b> + <b> + <a>59</a> + <b>68</b> + <v>527</v> + </b> + <b> + <a>68</a> + <b>79</b> + <v>505</v> + </b> + <b> + <a>79</a> + <b>95</b> + <v>500</v> + </b> + <b> + <a>95</a> + <b>114</b> + <v>513</v> + </b> + <b> + <a>114</a> + <b>190</b> + <v>448</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>start_line</src> + <trg>end_line</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1853</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1194</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>601</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>500</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>355</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>588</v> + </b> + <b> + <a>8</a> + <b>11</b> + <v>496</v> + </b> + <b> + <a>11</a> + <b>18</b> + <v>549</v> + </b> + <b> + <a>18</a> + <b>243</b> + <v>369</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>start_line</src> + <trg>end_column</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>6</b> + <v>487</v> + </b> + <b> + <a>6</a> + <b>9</b> + <v>571</v> + </b> + <b> + <a>9</a> + <b>15</b> + <v>562</v> + </b> + <b> + <a>15</a> + <b>23</b> + <v>513</v> + </b> + <b> + <a>23</a> + <b>34</b> + <v>522</v> + </b> + <b> + <a>34</a> + <b>46</b> + <v>544</v> + </b> + <b> + <a>46</a> + <b>56</b> + <v>522</v> + </b> + <b> + <a>56</a> + <b>65</b> + <v>500</v> + </b> + <b> + <a>65</a> + <b>75</b> + <v>553</v> + </b> + <b> + <a>75</a> + <b>89</b> + <v>527</v> + </b> + <b> + <a>89</a> + <b>108</b> + <v>518</v> + </b> + <b> + <a>108</a> + <b>132</b> + <v>496</v> + </b> + <b> + <a>132</a> + <b>194</b> + <v>188</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>start_column</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>180</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>79</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>118</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>118</v> + </b> + <b> + <a>8</a> + <b>21</b> + <v>96</v> + </b> + <b> + <a>21</a> + <b>66</b> + <v>96</v> + </b> + <b> + <a>67</a> + <b>243</b> + <v>96</v> + </b> + <b> + <a>260</a> + <b>688</b> + <v>96</v> + </b> + <b> + <a>740</a> + <b>1935</b> + <v>96</v> + </b> + <b> + <a>2001</a> + <b>5191</b> + <v>96</v> + </b> + <b> + <a>5235</a> + <b>8634</b> + <v>96</v> + </b> + <b> + <a>8826</a> + <b>18208</b> + <v>96</v> + </b> + <b> + <a>23101</a> + <b>34182</b> + <v>17</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>start_column</src> + <trg>file</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>263</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>131</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>96</v> + </b> + <b> + <a>5</a> + <b>13</b> + <v>96</v> + </b> + <b> + <a>13</a> + <b>39</b> + <v>96</v> + </b> + <b> + <a>39</a> + <b>100</b> + <v>96</v> + </b> + <b> + <a>101</a> + <b>255</b> + <v>96</v> + </b> + <b> + <a>255</a> + <b>437</b> + <v>96</v> + </b> + <b> + <a>449</a> + <b>700</b> + <v>101</v> + </b> + <b> + <a>709</a> + <b>867</b> + <v>101</v> + </b> + <b> + <a>867</a> + <b>1049</b> + <v>96</v> + </b> + <b> + <a>1085</a> + <b>1162</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>start_column</src> + <trg>start_line</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>228</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>92</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>114</v> + </b> + <b> + <a>5</a> + <b>10</b> + <v>101</v> + </b> + <b> + <a>10</a> + <b>34</b> + <v>96</v> + </b> + <b> + <a>37</a> + <b>94</b> + <v>96</v> + </b> + <b> + <a>96</a> + <b>197</b> + <v>96</v> + </b> + <b> + <a>199</a> + <b>363</b> + <v>96</v> + </b> + <b> + <a>365</a> + <b>572</b> + <v>96</v> + </b> + <b> + <a>587</a> + <b>802</b> + <v>96</v> + </b> + <b> + <a>804</a> + <b>885</b> + <v>96</v> + </b> + <b> + <a>888</a> + <b>1254</b> + <v>74</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>start_column</src> + <trg>end_line</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>228</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>92</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>114</v> + </b> + <b> + <a>5</a> + <b>10</b> + <v>101</v> + </b> + <b> + <a>10</a> + <b>34</b> + <v>96</v> + </b> + <b> + <a>37</a> + <b>94</b> + <v>96</v> + </b> + <b> + <a>95</a> + <b>196</b> + <v>96</v> + </b> + <b> + <a>199</a> + <b>367</b> + <v>101</v> + </b> + <b> + <a>379</a> + <b>597</b> + <v>96</v> + </b> + <b> + <a>609</a> + <b>818</b> + <v>96</v> + </b> + <b> + <a>819</a> + <b>898</b> + <v>96</v> + </b> + <b> + <a>904</a> + <b>1254</b> + <v>70</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>start_column</src> + <trg>end_column</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>224</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>158</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>101</v> + </b> + <b> + <a>4</a> + <b>6</b> + <v>96</v> + </b> + <b> + <a>6</a> + <b>13</b> + <v>96</v> + </b> + <b> + <a>13</a> + <b>29</b> + <v>105</v> + </b> + <b> + <a>29</a> + <b>48</b> + <v>96</v> + </b> + <b> + <a>48</a> + <b>65</b> + <v>96</v> + </b> + <b> + <a>67</a> + <b>97</b> + <v>96</v> + </b> + <b> + <a>97</a> + <b>116</b> + <v>101</v> + </b> + <b> + <a>117</a> + <b>148</b> + <v>96</v> + </b> + <b> + <a>156</a> + <b>169</b> + <v>17</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end_line</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>6</b> + <v>448</v> + </b> + <b> + <a>6</a> + <b>12</b> + <v>549</v> + </b> + <b> + <a>12</a> + <b>21</b> + <v>597</v> + </b> + <b> + <a>21</a> + <b>34</b> + <v>509</v> + </b> + <b> + <a>34</a> + <b>59</b> + <v>500</v> + </b> + <b> + <a>59</a> + <b>90</b> + <v>500</v> + </b> + <b> + <a>90</a> + <b>120</b> + <v>492</v> + </b> + <b> + <a>121</a> + <b>166</b> + <v>496</v> + </b> + <b> + <a>166</a> + <b>218</b> + <v>500</v> + </b> + <b> + <a>220</a> + <b>324</b> + <v>492</v> + </b> + <b> + <a>328</a> + <b>639</b> + <v>492</v> + </b> + <b> + <a>642</a> + <b>1550</b> + <v>492</v> + </b> + <b> + <a>1554</a> + <b>8937</b> + <v>439</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end_line</src> + <trg>file</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1423</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>663</v> + </b> + <b> + <a>3</a> + <b>6</b> + <v>588</v> + </b> + <b> + <a>6</a> + <b>10</b> + <v>584</v> + </b> + <b> + <a>10</a> + <b>14</b> + <v>509</v> + </b> + <b> + <a>14</a> + <b>19</b> + <v>557</v> + </b> + <b> + <a>19</a> + <b>27</b> + <v>500</v> + </b> + <b> + <a>27</a> + <b>45</b> + <v>509</v> + </b> + <b> + <a>45</a> + <b>104</b> + <v>492</v> + </b> + <b> + <a>104</a> + <b>377</b> + <v>492</v> + </b> + <b> + <a>408</a> + <b>1146</b> + <v>188</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end_line</src> + <trg>start_line</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1823</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1124</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>588</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>500</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>351</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>369</v> + </b> + <b> + <a>7</a> + <b>10</b> + <v>566</v> + </b> + <b> + <a>10</a> + <b>16</b> + <v>527</v> + </b> + <b> + <a>16</a> + <b>26</b> + <v>505</v> + </b> + <b> + <a>26</a> + <b>34</b> + <v>153</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end_line</src> + <trg>start_column</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>5</b> + <v>461</v> + </b> + <b> + <a>5</a> + <b>9</b> + <v>584</v> + </b> + <b> + <a>9</a> + <b>15</b> + <v>584</v> + </b> + <b> + <a>15</a> + <b>23</b> + <v>540</v> + </b> + <b> + <a>23</a> + <b>34</b> + <v>518</v> + </b> + <b> + <a>34</a> + <b>45</b> + <v>522</v> + </b> + <b> + <a>45</a> + <b>54</b> + <v>492</v> + </b> + <b> + <a>54</a> + <b>63</b> + <v>492</v> + </b> + <b> + <a>63</a> + <b>72</b> + <v>509</v> + </b> + <b> + <a>72</a> + <b>85</b> + <v>562</v> + </b> + <b> + <a>85</a> + <b>103</b> + <v>505</v> + </b> + <b> + <a>103</a> + <b>124</b> + <v>496</v> + </b> + <b> + <a>124</a> + <b>189</b> + <v>241</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end_line</src> + <trg>end_column</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>5</b> + <v>470</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>518</v> + </b> + <b> + <a>8</a> + <b>14</b> + <v>474</v> + </b> + <b> + <a>14</a> + <b>19</b> + <v>522</v> + </b> + <b> + <a>19</a> + <b>30</b> + <v>527</v> + </b> + <b> + <a>30</a> + <b>42</b> + <v>500</v> + </b> + <b> + <a>42</a> + <b>52</b> + <v>535</v> + </b> + <b> + <a>52</a> + <b>62</b> + <v>496</v> + </b> + <b> + <a>62</a> + <b>71</b> + <v>496</v> + </b> + <b> + <a>71</a> + <b>81</b> + <v>496</v> + </b> + <b> + <a>81</a> + <b>98</b> + <v>500</v> + </b> + <b> + <a>98</a> + <b>117</b> + <v>496</v> + </b> + <b> + <a>117</a> + <b>193</b> + <v>474</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end_column</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>127</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>96</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>87</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>105</v> + </b> + <b> + <a>8</a> + <b>18</b> + <v>101</v> + </b> + <b> + <a>24</a> + <b>69</b> + <v>96</v> + </b> + <b> + <a>70</a> + <b>158</b> + <v>96</v> + </b> + <b> + <a>202</a> + <b>599</b> + <v>96</v> + </b> + <b> + <a>729</a> + <b>1549</b> + <v>96</v> + </b> + <b> + <a>1680</a> + <b>3571</b> + <v>96</v> + </b> + <b> + <a>3709</a> + <b>6978</b> + <v>96</v> + </b> + <b> + <a>7000</a> + <b>9296</b> + <v>96</v> + </b> + <b> + <a>9306</a> + <b>17045</b> + <v>92</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end_column</src> + <trg>file</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>232</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>140</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>105</v> + </b> + <b> + <a>5</a> + <b>13</b> + <v>101</v> + </b> + <b> + <a>13</a> + <b>42</b> + <v>96</v> + </b> + <b> + <a>42</a> + <b>107</b> + <v>96</v> + </b> + <b> + <a>108</a> + <b>251</b> + <v>96</v> + </b> + <b> + <a>263</a> + <b>467</b> + <v>96</v> + </b> + <b> + <a>479</a> + <b>738</b> + <v>96</v> + </b> + <b> + <a>750</a> + <b>893</b> + <v>96</v> + </b> + <b> + <a>894</a> + <b>1012</b> + <v>101</v> + </b> + <b> + <a>1051</a> + <b>1133</b> + <v>26</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end_column</src> + <trg>start_line</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>210</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>87</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>114</v> + </b> + <b> + <a>5</a> + <b>9</b> + <v>105</v> + </b> + <b> + <a>9</a> + <b>33</b> + <v>96</v> + </b> + <b> + <a>33</a> + <b>76</b> + <v>96</v> + </b> + <b> + <a>80</a> + <b>208</b> + <v>96</v> + </b> + <b> + <a>221</a> + <b>338</b> + <v>96</v> + </b> + <b> + <a>339</a> + <b>563</b> + <v>96</v> + </b> + <b> + <a>563</a> + <b>794</b> + <v>96</v> + </b> + <b> + <a>818</a> + <b>888</b> + <v>96</v> + </b> + <b> + <a>888</a> + <b>1247</b> + <v>92</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end_column</src> + <trg>start_column</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>153</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>114</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>105</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>96</v> + </b> + <b> + <a>7</a> + <b>20</b> + <v>96</v> + </b> + <b> + <a>20</a> + <b>34</b> + <v>114</v> + </b> + <b> + <a>34</a> + <b>43</b> + <v>96</v> + </b> + <b> + <a>43</a> + <b>55</b> + <v>105</v> + </b> + <b> + <a>55</a> + <b>69</b> + <v>105</v> + </b> + <b> + <a>69</a> + <b>82</b> + <v>101</v> + </b> + <b> + <a>82</a> + <b>93</b> + <v>96</v> + </b> + <b> + <a>93</a> + <b>106</b> + <v>96</v> + </b> + <b> + <a>108</a> + <b>109</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end_column</src> + <trg>end_line</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>210</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>87</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>114</v> + </b> + <b> + <a>5</a> + <b>9</b> + <v>105</v> + </b> + <b> + <a>9</a> + <b>33</b> + <v>101</v> + </b> + <b> + <a>35</a> + <b>78</b> + <v>96</v> + </b> + <b> + <a>83</a> + <b>222</b> + <v>96</v> + </b> + <b> + <a>226</a> + <b>336</b> + <v>96</v> + </b> + <b> + <a>369</a> + <b>563</b> + <v>101</v> + </b> + <b> + <a>567</a> + <b>838</b> + <v>96</v> + </b> + <b> + <a>839</a> + <b>888</b> + <v>96</v> + </b> + <b> + <a>890</a> + <b>1202</b> + <v>83</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_alias_def</name> + <cardinality>410</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>410</v> + </e> + <e> + <k>alias</k> + <v>410</v> + </e> + <e> + <k>name</k> + <v>410</v> + </e> + <e> + <k>loc</k> + <v>410</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>alias</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>410</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>410</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>410</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>alias</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>410</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>alias</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>410</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>alias</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>410</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>410</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>alias</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>410</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>410</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>410</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>alias</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>410</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>410</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_argument_list_child</name> + <cardinality>276475</cardinality> + <columnsizes> + <e> + <k>ruby_argument_list</k> + <v>222512</v> + </e> + <e> + <k>index</k> + <v>140</v> + </e> + <e> + <k>child</k> + <v>276475</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_argument_list</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>187851</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>22346</v> + </b> + <b> + <a>3</a> + <b>33</b> + <v>12313</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_argument_list</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>187851</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>22346</v> + </b> + <b> + <a>3</a> + <b>33</b> + <v>12313</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_argument_list</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>48</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>13</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>4</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>8</v> + </b> + <b> + <a>8</a> + <b>10</b> + <v>8</v> + </b> + <b> + <a>11</a> + <b>16</b> + <v>8</v> + </b> + <b> + <a>19</a> + <b>23</b> + <v>8</v> + </b> + <b> + <a>30</a> + <b>43</b> + <v>8</v> + </b> + <b> + <a>55</a> + <b>101</b> + <v>8</v> + </b> + <b> + <a>356</a> + <b>890</b> + <v>8</v> + </b> + <b> + <a>2803</a> + <b>7891</b> + <v>8</v> + </b> + <b> + <a>50652</a> + <b>50653</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>48</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>13</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>4</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>8</v> + </b> + <b> + <a>8</a> + <b>10</b> + <v>8</v> + </b> + <b> + <a>11</a> + <b>16</b> + <v>8</v> + </b> + <b> + <a>19</a> + <b>23</b> + <v>8</v> + </b> + <b> + <a>30</a> + <b>43</b> + <v>8</v> + </b> + <b> + <a>55</a> + <b>101</b> + <v>8</v> + </b> + <b> + <a>356</a> + <b>890</b> + <v>8</v> + </b> + <b> + <a>2803</a> + <b>7891</b> + <v>8</v> + </b> + <b> + <a>50652</a> + <b>50653</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_argument_list</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>276475</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>276475</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_argument_list_def</name> + <cardinality>222600</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>222600</v> + </e> + <e> + <k>loc</k> + <v>222600</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>222600</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>222600</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_array_child</name> + <cardinality>20697</cardinality> + <columnsizes> + <e> + <k>ruby_array</k> + <v>9235</v> + </e> + <e> + <k>index</k> + <v>96</v> + </e> + <e> + <k>child</k> + <v>20697</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_array</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3040</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>4012</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>1342</v> + </b> + <b> + <a>4</a> + <b>10</b> + <v>723</v> + </b> + <b> + <a>10</a> + <b>95</b> + <v>117</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_array</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3040</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>4012</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>1342</v> + </b> + <b> + <a>4</a> + <b>10</b> + <v>723</v> + </b> + <b> + <a>10</a> + <b>95</b> + <v>117</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_array</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>10</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>27</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>13</v> + </b> + <b> + <a>4</a> + <b>6</b> + <v>8</v> + </b> + <b> + <a>6</a> + <b>14</b> + <v>8</v> + </b> + <b> + <a>14</a> + <b>25</b> + <v>8</v> + </b> + <b> + <a>25</a> + <b>46</b> + <v>8</v> + </b> + <b> + <a>58</a> + <b>450</b> + <v>8</v> + </b> + <b> + <a>821</a> + <b>9015</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>10</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>27</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>13</v> + </b> + <b> + <a>4</a> + <b>6</b> + <v>8</v> + </b> + <b> + <a>6</a> + <b>14</b> + <v>8</v> + </b> + <b> + <a>14</a> + <b>25</b> + <v>8</v> + </b> + <b> + <a>25</a> + <b>46</b> + <v>8</v> + </b> + <b> + <a>58</a> + <b>450</b> + <v>8</v> + </b> + <b> + <a>821</a> + <b>9015</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_array</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>20697</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>20697</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_array_def</name> + <cardinality>10913</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>10913</v> + </e> + <e> + <k>loc</k> + <v>10913</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>10913</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>10913</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_assignment_def</name> + <cardinality>41784</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>41784</v> + </e> + <e> + <k>left</k> + <v>41784</v> + </e> + <e> + <k>right</k> + <v>41784</v> + </e> + <e> + <k>loc</k> + <v>41784</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>41784</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>41784</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>41784</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>41784</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>41784</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>41784</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>41784</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>41784</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>41784</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>41784</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>41784</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>41784</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_ast_node_parent</name> + <cardinality>2822121</cardinality> + <columnsizes> + <e> + <k>child</k> + <v>2822121</v> + </e> + <e> + <k>parent</k> + <v>926369</v> + </e> + <e> + <k>parent_index</k> + <v>773</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>child</src> + <trg>parent</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2822121</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>parent_index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2822121</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parent</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>102553</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>130189</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>508318</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>118399</v> + </b> + <b> + <a>5</a> + <b>177</b> + <v>66909</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parent</src> + <trg>parent_index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>102553</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>130189</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>508318</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>118399</v> + </b> + <b> + <a>5</a> + <b>177</b> + <v>66909</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parent_index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>136</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>43</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>83</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>74</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>70</v> + </b> + <b> + <a>8</a> + <b>15</b> + <v>61</v> + </b> + <b> + <a>15</a> + <b>25</b> + <v>61</v> + </b> + <b> + <a>26</a> + <b>50</b> + <v>61</v> + </b> + <b> + <a>54</a> + <b>125</b> + <v>61</v> + </b> + <b> + <a>139</a> + <b>618</b> + <v>61</v> + </b> + <b> + <a>764</a> + <b>210877</b> + <v>57</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parent_index</src> + <trg>parent</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>136</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>43</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>83</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>74</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>70</v> + </b> + <b> + <a>8</a> + <b>15</b> + <v>61</v> + </b> + <b> + <a>15</a> + <b>25</b> + <v>61</v> + </b> + <b> + <a>26</a> + <b>50</b> + <v>61</v> + </b> + <b> + <a>54</a> + <b>125</b> + <v>61</v> + </b> + <b> + <a>139</a> + <b>618</b> + <v>61</v> + </b> + <b> + <a>764</a> + <b>210877</b> + <v>57</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_bare_string_child</name> + <cardinality>3132</cardinality> + <columnsizes> + <e> + <k>ruby_bare_string</k> + <v>3119</v> + </e> + <e> + <k>index</k> + <v>2</v> + </e> + <e> + <k>child</k> + <v>3132</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_bare_string</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3107</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>12</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_bare_string</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3107</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>12</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_bare_string</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>12</a> + <b>13</b> + <v>1</v> + </b> + <b> + <a>3045</a> + <b>3046</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>12</a> + <b>13</b> + <v>1</v> + </b> + <b> + <a>3045</a> + <b>3046</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_bare_string</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3132</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3132</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_bare_string_def</name> + <cardinality>3119</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>3119</v> + </e> + <e> + <k>loc</k> + <v>3119</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3119</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3119</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_bare_symbol_child</name> + <cardinality>685</cardinality> + <columnsizes> + <e> + <k>ruby_bare_symbol</k> + <v>685</v> + </e> + <e> + <k>index</k> + <v>1</v> + </e> + <e> + <k>child</k> + <v>685</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_bare_symbol</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>685</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_bare_symbol</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>685</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_bare_symbol</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>685</a> + <b>686</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>685</a> + <b>686</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_bare_symbol</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>685</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>685</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_bare_symbol_def</name> + <cardinality>685</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>685</v> + </e> + <e> + <k>loc</k> + <v>685</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>685</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>685</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_begin_block_child</name> + <cardinality>0</cardinality> + <columnsizes> + <e> + <k>ruby_begin_block</k> + <v>0</v> + </e> + <e> + <k>index</k> + <v>0</v> + </e> + <e> + <k>child</k> + <v>0</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_begin_block</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>ruby_begin_block</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_begin_block</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_begin_block</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_begin_block_def</name> + <cardinality>0</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>0</v> + </e> + <e> + <k>loc</k> + <v>0</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_begin_child</name> + <cardinality>2205</cardinality> + <columnsizes> + <e> + <k>ruby_begin</k> + <v>641</v> + </e> + <e> + <k>index</k> + <v>34</v> + </e> + <e> + <k>child</k> + <v>2205</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_begin</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>35</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>293</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>128</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>71</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>57</v> + </b> + <b> + <a>7</a> + <b>14</b> + <v>50</v> + </b> + <b> + <a>14</a> + <b>35</b> + <v>7</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_begin</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>35</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>293</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>128</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>71</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>57</v> + </b> + <b> + <a>7</a> + <b>14</b> + <v>50</v> + </b> + <b> + <a>14</a> + <b>35</b> + <v>7</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_begin</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>8</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>5</v> + </b> + <b> + <a>6</a> + <b>13</b> + <v>3</v> + </b> + <b> + <a>13</a> + <b>23</b> + <v>3</v> + </b> + <b> + <a>30</a> + <b>58</b> + <v>3</v> + </b> + <b> + <a>78</a> + <b>186</b> + <v>3</v> + </b> + <b> + <a>313</a> + <b>642</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>8</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>5</v> + </b> + <b> + <a>6</a> + <b>13</b> + <v>3</v> + </b> + <b> + <a>13</a> + <b>23</b> + <v>3</v> + </b> + <b> + <a>30</a> + <b>58</b> + <v>3</v> + </b> + <b> + <a>78</a> + <b>186</b> + <v>3</v> + </b> + <b> + <a>313</a> + <b>642</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_begin</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2205</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2205</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_begin_def</name> + <cardinality>641</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>641</v> + </e> + <e> + <k>loc</k> + <v>641</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>641</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>641</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_binary_def</name> + <cardinality>14456</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>14456</v> + </e> + <e> + <k>left</k> + <v>14456</v> + </e> + <e> + <k>operator</k> + <v>23</v> + </e> + <e> + <k>right</k> + <v>14456</v> + </e> + <e> + <k>loc</k> + <v>14456</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14456</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14456</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14456</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14456</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14456</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14456</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14456</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14456</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + <b> + <a>9</a> + <b>17</b> + <v>2</v> + </b> + <b> + <a>22</a> + <b>39</b> + <v>2</v> + </b> + <b> + <a>44</a> + <b>94</b> + <v>2</v> + </b> + <b> + <a>115</a> + <b>125</b> + <v>2</v> + </b> + <b> + <a>125</a> + <b>139</b> + <v>2</v> + </b> + <b> + <a>260</a> + <b>321</b> + <v>2</v> + </b> + <b> + <a>439</a> + <b>517</b> + <v>2</v> + </b> + <b> + <a>663</a> + <b>797</b> + <v>2</v> + </b> + <b> + <a>1241</a> + <b>1419</b> + <v>2</v> + </b> + <b> + <a>2581</a> + <b>2627</b> + <v>2</v> + </b> + <b> + <a>2870</a> + <b>2871</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + <b> + <a>9</a> + <b>17</b> + <v>2</v> + </b> + <b> + <a>22</a> + <b>39</b> + <v>2</v> + </b> + <b> + <a>44</a> + <b>94</b> + <v>2</v> + </b> + <b> + <a>115</a> + <b>125</b> + <v>2</v> + </b> + <b> + <a>125</a> + <b>139</b> + <v>2</v> + </b> + <b> + <a>260</a> + <b>321</b> + <v>2</v> + </b> + <b> + <a>439</a> + <b>517</b> + <v>2</v> + </b> + <b> + <a>663</a> + <b>797</b> + <v>2</v> + </b> + <b> + <a>1241</a> + <b>1419</b> + <v>2</v> + </b> + <b> + <a>2581</a> + <b>2627</b> + <v>2</v> + </b> + <b> + <a>2870</a> + <b>2871</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + <b> + <a>9</a> + <b>17</b> + <v>2</v> + </b> + <b> + <a>22</a> + <b>39</b> + <v>2</v> + </b> + <b> + <a>44</a> + <b>94</b> + <v>2</v> + </b> + <b> + <a>115</a> + <b>125</b> + <v>2</v> + </b> + <b> + <a>125</a> + <b>139</b> + <v>2</v> + </b> + <b> + <a>260</a> + <b>321</b> + <v>2</v> + </b> + <b> + <a>439</a> + <b>517</b> + <v>2</v> + </b> + <b> + <a>663</a> + <b>797</b> + <v>2</v> + </b> + <b> + <a>1241</a> + <b>1419</b> + <v>2</v> + </b> + <b> + <a>2581</a> + <b>2627</b> + <v>2</v> + </b> + <b> + <a>2870</a> + <b>2871</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + <b> + <a>9</a> + <b>17</b> + <v>2</v> + </b> + <b> + <a>22</a> + <b>39</b> + <v>2</v> + </b> + <b> + <a>44</a> + <b>94</b> + <v>2</v> + </b> + <b> + <a>115</a> + <b>125</b> + <v>2</v> + </b> + <b> + <a>125</a> + <b>139</b> + <v>2</v> + </b> + <b> + <a>260</a> + <b>321</b> + <v>2</v> + </b> + <b> + <a>439</a> + <b>517</b> + <v>2</v> + </b> + <b> + <a>663</a> + <b>797</b> + <v>2</v> + </b> + <b> + <a>1241</a> + <b>1419</b> + <v>2</v> + </b> + <b> + <a>2581</a> + <b>2627</b> + <v>2</v> + </b> + <b> + <a>2870</a> + <b>2871</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14456</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14456</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14456</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14456</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14456</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14456</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14456</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14456</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_block_argument_def</name> + <cardinality>1945</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1945</v> + </e> + <e> + <k>child</k> + <v>1945</v> + </e> + <e> + <k>loc</k> + <v>1945</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1945</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1945</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1945</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1945</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1945</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1945</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_block_child</name> + <cardinality>28655</cardinality> + <columnsizes> + <e> + <k>ruby_block</k> + <v>28620</v> + </e> + <e> + <k>index</k> + <v>17</v> + </e> + <e> + <k>child</k> + <v>28655</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_block</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>28593</v> + </b> + <b> + <a>2</a> + <b>5</b> + <v>26</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_block</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>28593</v> + </b> + <b> + <a>2</a> + <b>5</b> + <v>26</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_block</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>4</v> + </b> + <b> + <a>6515</a> + <b>6516</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>4</v> + </b> + <b> + <a>6515</a> + <b>6516</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_block</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>28655</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>28655</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_block_def</name> + <cardinality>28681</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>28681</v> + </e> + <e> + <k>loc</k> + <v>28681</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>28681</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>28681</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_block_parameter_def</name> + <cardinality>775</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>775</v> + </e> + <e> + <k>name</k> + <v>775</v> + </e> + <e> + <k>loc</k> + <v>775</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>775</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>775</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>775</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>775</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>775</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>775</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_block_parameters</name> + <cardinality>2716</cardinality> + <columnsizes> + <e> + <k>ruby_block</k> + <v>2716</v> + </e> + <e> + <k>parameters</k> + <v>2716</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_block</src> + <trg>parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2716</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parameters</src> + <trg>ruby_block</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2716</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_block_parameters_child</name> + <cardinality>8752</cardinality> + <columnsizes> + <e> + <k>ruby_block_parameters</k> + <v>7513</v> + </e> + <e> + <k>index</k> + <v>5</v> + </e> + <e> + <k>child</k> + <v>8752</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_block_parameters</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6420</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>990</v> + </b> + <b> + <a>3</a> + <b>6</b> + <v>102</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_block_parameters</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6420</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>990</v> + </b> + <b> + <a>3</a> + <b>6</b> + <v>102</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_block_parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>33</a> + <b>34</b> + <v>1</v> + </b> + <b> + <a>100</a> + <b>101</b> + <v>1</v> + </b> + <b> + <a>1067</a> + <b>1068</b> + <v>1</v> + </b> + <b> + <a>7333</a> + <b>7334</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>33</a> + <b>34</b> + <v>1</v> + </b> + <b> + <a>100</a> + <b>101</b> + <v>1</v> + </b> + <b> + <a>1067</a> + <b>1068</b> + <v>1</v> + </b> + <b> + <a>7333</a> + <b>7334</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_block_parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8752</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8752</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_block_parameters_def</name> + <cardinality>7513</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>7513</v> + </e> + <e> + <k>loc</k> + <v>7513</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>7513</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>7513</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_break_child</name> + <cardinality>13</cardinality> + <columnsizes> + <e> + <k>ruby_break</k> + <v>13</v> + </e> + <e> + <k>child</k> + <v>13</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_break</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_break</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_break_def</name> + <cardinality>220</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>220</v> + </e> + <e> + <k>loc</k> + <v>220</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>220</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>220</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_call_arguments</name> + <cardinality>221646</cardinality> + <columnsizes> + <e> + <k>ruby_call</k> + <v>221646</v> + </e> + <e> + <k>arguments</k> + <v>221646</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_call</src> + <trg>arguments</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>221646</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>arguments</src> + <trg>ruby_call</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>221646</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_call_block</name> + <cardinality>72308</cardinality> + <columnsizes> + <e> + <k>ruby_call</k> + <v>72308</v> + </e> + <e> + <k>block</k> + <v>72308</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_call</src> + <trg>block</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>72308</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>block</src> + <trg>ruby_call</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>72308</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_call_def</name> + <cardinality>319372</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>319372</v> + </e> + <e> + <k>method</k> + <v>319372</v> + </e> + <e> + <k>loc</k> + <v>319372</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>method</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>319372</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>319372</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>method</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>319372</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>method</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>319372</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>319372</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>method</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>319372</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_call_receiver</name> + <cardinality>173933</cardinality> + <columnsizes> + <e> + <k>ruby_call</k> + <v>173933</v> + </e> + <e> + <k>receiver</k> + <v>173933</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_call</src> + <trg>receiver</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>173933</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>receiver</src> + <trg>ruby_call</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>173933</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_case_child</name> + <cardinality>1311</cardinality> + <columnsizes> + <e> + <k>ruby_case__</k> + <v>389</v> + </e> + <e> + <k>index</k> + <v>22</v> + </e> + <e> + <k>child</k> + <v>1311</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_case__</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>12</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>102</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>159</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>61</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>32</v> + </b> + <b> + <a>7</a> + <b>23</b> + <v>20</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_case__</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>12</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>102</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>159</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>61</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>32</v> + </b> + <b> + <a>7</a> + <b>23</b> + <v>20</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_case__</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>2</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>2</v> + </b> + <b> + <a>8</a> + <b>11</b> + <v>2</v> + </b> + <b> + <a>13</a> + <b>21</b> + <v>2</v> + </b> + <b> + <a>30</a> + <b>53</b> + <v>2</v> + </b> + <b> + <a>112</a> + <b>269</b> + <v>2</v> + </b> + <b> + <a>368</a> + <b>381</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>2</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>2</v> + </b> + <b> + <a>8</a> + <b>11</b> + <v>2</v> + </b> + <b> + <a>13</a> + <b>21</b> + <v>2</v> + </b> + <b> + <a>30</a> + <b>53</b> + <v>2</v> + </b> + <b> + <a>112</a> + <b>269</b> + <v>2</v> + </b> + <b> + <a>368</a> + <b>381</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_case__</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1311</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1311</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_case_def</name> + <cardinality>389</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>389</v> + </e> + <e> + <k>loc</k> + <v>389</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>389</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>389</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_case_value</name> + <cardinality>377</cardinality> + <columnsizes> + <e> + <k>ruby_case__</k> + <v>377</v> + </e> + <e> + <k>value</k> + <v>377</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_case__</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>377</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>ruby_case__</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>377</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_chained_string_child</name> + <cardinality>1095</cardinality> + <columnsizes> + <e> + <k>ruby_chained_string</k> + <v>288</v> + </e> + <e> + <k>index</k> + <v>12</v> + </e> + <e> + <k>child</k> + <v>1095</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_chained_string</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>98</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>63</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>44</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>40</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>21</v> + </b> + <b> + <a>8</a> + <b>13</b> + <v>20</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_chained_string</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>98</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>63</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>44</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>40</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>21</v> + </b> + <b> + <a>8</a> + <b>13</b> + <v>20</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_chained_string</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>1</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>1</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>1</v> + </b> + <b> + <a>20</a> + <b>21</b> + <v>1</v> + </b> + <b> + <a>32</a> + <b>33</b> + <v>1</v> + </b> + <b> + <a>41</a> + <b>42</b> + <v>1</v> + </b> + <b> + <a>81</a> + <b>82</b> + <v>1</v> + </b> + <b> + <a>124</a> + <b>125</b> + <v>1</v> + </b> + <b> + <a>186</a> + <b>187</b> + <v>1</v> + </b> + <b> + <a>282</a> + <b>283</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>1</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>1</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>1</v> + </b> + <b> + <a>20</a> + <b>21</b> + <v>1</v> + </b> + <b> + <a>32</a> + <b>33</b> + <v>1</v> + </b> + <b> + <a>41</a> + <b>42</b> + <v>1</v> + </b> + <b> + <a>81</a> + <b>82</b> + <v>1</v> + </b> + <b> + <a>124</a> + <b>125</b> + <v>1</v> + </b> + <b> + <a>186</a> + <b>187</b> + <v>1</v> + </b> + <b> + <a>282</a> + <b>283</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_chained_string</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1095</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1095</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_chained_string_def</name> + <cardinality>288</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>288</v> + </e> + <e> + <k>loc</k> + <v>288</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>288</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>288</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_class_child</name> + <cardinality>42347</cardinality> + <columnsizes> + <e> + <k>ruby_class</k> + <v>4867</v> + </e> + <e> + <k>index</k> + <v>318</v> + </e> + <e> + <k>child</k> + <v>42347</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_class</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1053</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>750</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>502</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>402</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>307</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>267</v> + </b> + <b> + <a>7</a> + <b>9</b> + <v>362</v> + </b> + <b> + <a>9</a> + <b>13</b> + <v>413</v> + </b> + <b> + <a>13</a> + <b>21</b> + <v>383</v> + </b> + <b> + <a>21</a> + <b>78</b> + <v>367</v> + </b> + <b> + <a>80</a> + <b>312</b> + <v>57</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_class</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1053</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>750</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>502</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>402</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>307</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>267</v> + </b> + <b> + <a>7</a> + <b>9</b> + <v>362</v> + </b> + <b> + <a>9</a> + <b>13</b> + <v>413</v> + </b> + <b> + <a>13</a> + <b>21</b> + <v>383</v> + </b> + <b> + <a>21</a> + <b>78</b> + <v>367</v> + </b> + <b> + <a>80</a> + <b>312</b> + <v>57</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_class</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>10</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>33</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>28</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>24</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>24</v> + </b> + <b> + <a>8</a> + <b>11</b> + <v>25</v> + </b> + <b> + <a>11</a> + <b>15</b> + <v>25</v> + </b> + <b> + <a>16</a> + <b>27</b> + <v>27</v> + </b> + <b> + <a>27</a> + <b>42</b> + <v>24</v> + </b> + <b> + <a>44</a> + <b>72</b> + <v>25</v> + </b> + <b> + <a>74</a> + <b>153</b> + <v>24</v> + </b> + <b> + <a>158</a> + <b>467</b> + <v>24</v> + </b> + <b> + <a>491</a> + <b>4752</b> + <v>18</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>10</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>33</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>28</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>24</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>24</v> + </b> + <b> + <a>8</a> + <b>11</b> + <v>25</v> + </b> + <b> + <a>11</a> + <b>15</b> + <v>25</v> + </b> + <b> + <a>16</a> + <b>27</b> + <v>27</v> + </b> + <b> + <a>27</a> + <b>42</b> + <v>24</v> + </b> + <b> + <a>44</a> + <b>72</b> + <v>25</v> + </b> + <b> + <a>74</a> + <b>153</b> + <v>24</v> + </b> + <b> + <a>158</a> + <b>467</b> + <v>24</v> + </b> + <b> + <a>491</a> + <b>4752</b> + <v>18</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_class</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>42347</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>42347</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_class_def</name> + <cardinality>5404</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>5404</v> + </e> + <e> + <k>name</k> + <v>5404</v> + </e> + <e> + <k>loc</k> + <v>5404</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5404</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5404</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5404</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5404</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5404</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5404</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_class_superclass</name> + <cardinality>4276</cardinality> + <columnsizes> + <e> + <k>ruby_class</k> + <v>4276</v> + </e> + <e> + <k>superclass</k> + <v>4276</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_class</src> + <trg>superclass</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4276</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>superclass</src> + <trg>ruby_class</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4276</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_conditional_def</name> + <cardinality>1153</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1153</v> + </e> + <e> + <k>alternative</k> + <v>1153</v> + </e> + <e> + <k>condition</k> + <v>1153</v> + </e> + <e> + <k>consequence</k> + <v>1153</v> + </e> + <e> + <k>loc</k> + <v>1153</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>alternative</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1153</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1153</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>consequence</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1153</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1153</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>alternative</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1153</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>alternative</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1153</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>alternative</src> + <trg>consequence</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1153</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>alternative</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1153</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1153</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>alternative</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1153</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>consequence</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1153</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1153</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>consequence</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1153</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>consequence</src> + <trg>alternative</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1153</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>consequence</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1153</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>consequence</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1153</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1153</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>alternative</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1153</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1153</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>consequence</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1153</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_delimited_symbol_child</name> + <cardinality>543</cardinality> + <columnsizes> + <e> + <k>ruby_delimited_symbol</k> + <v>394</v> + </e> + <e> + <k>index</k> + <v>8</v> + </e> + <e> + <k>child</k> + <v>543</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_delimited_symbol</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>298</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>73</v> + </b> + <b> + <a>3</a> + <b>9</b> + <v>22</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_delimited_symbol</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>298</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>73</v> + </b> + <b> + <a>3</a> + <b>9</b> + <v>22</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_delimited_symbol</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>1</v> + </b> + <b> + <a>12</a> + <b>13</b> + <v>1</v> + </b> + <b> + <a>22</a> + <b>23</b> + <v>1</v> + </b> + <b> + <a>94</a> + <b>95</b> + <v>1</v> + </b> + <b> + <a>385</a> + <b>386</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>1</v> + </b> + <b> + <a>12</a> + <b>13</b> + <v>1</v> + </b> + <b> + <a>22</a> + <b>23</b> + <v>1</v> + </b> + <b> + <a>94</a> + <b>95</b> + <v>1</v> + </b> + <b> + <a>385</a> + <b>386</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_delimited_symbol</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>543</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>543</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_delimited_symbol_def</name> + <cardinality>394</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>394</v> + </e> + <e> + <k>loc</k> + <v>394</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>394</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>394</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_destructured_left_assignment_child</name> + <cardinality>2</cardinality> + <columnsizes> + <e> + <k>ruby_destructured_left_assignment</k> + <v>1</v> + </e> + <e> + <k>index</k> + <v>2</v> + </e> + <e> + <k>child</k> + <v>2</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_destructured_left_assignment</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_destructured_left_assignment</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_destructured_left_assignment</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_destructured_left_assignment</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_destructured_left_assignment_def</name> + <cardinality>1</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1</v> + </e> + <e> + <k>loc</k> + <v>1</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_destructured_parameter_child</name> + <cardinality>132</cardinality> + <columnsizes> + <e> + <k>ruby_destructured_parameter</k> + <v>64</v> + </e> + <e> + <k>index</k> + <v>4</v> + </e> + <e> + <k>child</k> + <v>132</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_destructured_parameter</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>56</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>5</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_destructured_parameter</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>56</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>5</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_destructured_parameter</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>60</a> + <b>61</b> + <v>1</v> + </b> + <b> + <a>63</a> + <b>64</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>60</a> + <b>61</b> + <v>1</v> + </b> + <b> + <a>63</a> + <b>64</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_destructured_parameter</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>132</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>132</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_destructured_parameter_def</name> + <cardinality>64</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>64</v> + </e> + <e> + <k>loc</k> + <v>64</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>64</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>64</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_do_block_child</name> + <cardinality>130365</cardinality> + <columnsizes> + <e> + <k>ruby_do_block</k> + <v>44447</v> + </e> + <e> + <k>index</k> + <v>316</v> + </e> + <e> + <k>child</k> + <v>130365</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_do_block</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14610</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>12124</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>7077</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>3861</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>3571</v> + </b> + <b> + <a>7</a> + <b>73</b> + <v>3202</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_do_block</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14610</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>12124</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>7077</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>3861</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>3571</v> + </b> + <b> + <a>7</a> + <b>73</b> + <v>3202</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_do_block</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>30</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>70</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>4</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>26</v> + </b> + <b> + <a>6</a> + <b>10</b> + <v>26</v> + </b> + <b> + <a>10</a> + <b>15</b> + <v>26</v> + </b> + <b> + <a>15</a> + <b>22</b> + <v>26</v> + </b> + <b> + <a>30</a> + <b>65</b> + <v>26</v> + </b> + <b> + <a>75</a> + <b>164</b> + <v>26</v> + </b> + <b> + <a>206</a> + <b>730</b> + <v>26</v> + </b> + <b> + <a>1031</a> + <b>10119</b> + <v>26</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>30</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>70</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>4</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>26</v> + </b> + <b> + <a>6</a> + <b>10</b> + <v>26</v> + </b> + <b> + <a>10</a> + <b>15</b> + <v>26</v> + </b> + <b> + <a>15</a> + <b>22</b> + <v>26</v> + </b> + <b> + <a>30</a> + <b>65</b> + <v>26</v> + </b> + <b> + <a>75</a> + <b>164</b> + <v>26</v> + </b> + <b> + <a>206</a> + <b>730</b> + <v>26</v> + </b> + <b> + <a>1031</a> + <b>10119</b> + <v>26</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_do_block</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>130365</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>130365</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_do_block_def</name> + <cardinality>44478</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>44478</v> + </e> + <e> + <k>loc</k> + <v>44478</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>44478</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>44478</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_do_block_parameters</name> + <cardinality>4797</cardinality> + <columnsizes> + <e> + <k>ruby_do_block</k> + <v>4797</v> + </e> + <e> + <k>parameters</k> + <v>4797</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_do_block</src> + <trg>parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4797</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parameters</src> + <trg>ruby_do_block</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4797</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_do_child</name> + <cardinality>276</cardinality> + <columnsizes> + <e> + <k>ruby_do</k> + <v>117</v> + </e> + <e> + <k>index</k> + <v>18</v> + </e> + <e> + <k>child</k> + <v>276</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_do</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>36</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>48</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>17</v> + </b> + <b> + <a>4</a> + <b>6</b> + <v>10</v> + </b> + <b> + <a>6</a> + <b>19</b> + <v>6</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_do</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>36</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>48</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>17</v> + </b> + <b> + <a>4</a> + <b>6</b> + <v>10</v> + </b> + <b> + <a>6</a> + <b>19</b> + <v>6</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_do</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>3</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>1</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>1</v> + </b> + <b> + <a>16</a> + <b>17</b> + <v>1</v> + </b> + <b> + <a>33</a> + <b>34</b> + <v>1</v> + </b> + <b> + <a>81</a> + <b>82</b> + <v>1</v> + </b> + <b> + <a>117</a> + <b>118</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>3</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>1</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>1</v> + </b> + <b> + <a>16</a> + <b>17</b> + <v>1</v> + </b> + <b> + <a>33</a> + <b>34</b> + <v>1</v> + </b> + <b> + <a>81</a> + <b>82</b> + <v>1</v> + </b> + <b> + <a>117</a> + <b>118</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_do</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>276</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>276</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_do_def</name> + <cardinality>120</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>120</v> + </e> + <e> + <k>loc</k> + <v>120</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>120</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>120</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_element_reference_child</name> + <cardinality>26696</cardinality> + <columnsizes> + <e> + <k>ruby_element_reference</k> + <v>26644</v> + </e> + <e> + <k>index</k> + <v>2</v> + </e> + <e> + <k>child</k> + <v>26696</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_element_reference</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>26592</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>52</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_element_reference</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>26592</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>52</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_element_reference</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>52</a> + <b>53</b> + <v>1</v> + </b> + <b> + <a>26644</a> + <b>26645</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>52</a> + <b>53</b> + <v>1</v> + </b> + <b> + <a>26644</a> + <b>26645</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_element_reference</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>26696</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>26696</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_element_reference_def</name> + <cardinality>26646</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>26646</v> + </e> + <e> + <k>object</k> + <v>26646</v> + </e> + <e> + <k>loc</k> + <v>26646</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>object</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>26646</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>26646</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>object</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>26646</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>object</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>26646</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>26646</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>object</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>26646</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_else_child</name> + <cardinality>2834</cardinality> + <columnsizes> + <e> + <k>ruby_else</k> + <v>2225</v> + </e> + <e> + <k>index</k> + <v>11</v> + </e> + <e> + <k>child</k> + <v>2834</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_else</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1871</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>222</v> + </b> + <b> + <a>3</a> + <b>12</b> + <v>131</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_else</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1871</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>222</v> + </b> + <b> + <a>3</a> + <b>12</b> + <v>131</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_else</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>1</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>1</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>1</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>16</a> + <b>17</b> + <v>1</v> + </b> + <b> + <a>28</a> + <b>29</b> + <v>1</v> + </b> + <b> + <a>54</a> + <b>55</b> + <v>1</v> + </b> + <b> + <a>128</a> + <b>129</b> + <v>1</v> + </b> + <b> + <a>345</a> + <b>346</b> + <v>1</v> + </b> + <b> + <a>2172</a> + <b>2173</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>1</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>1</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>1</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>16</a> + <b>17</b> + <v>1</v> + </b> + <b> + <a>28</a> + <b>29</b> + <v>1</v> + </b> + <b> + <a>54</a> + <b>55</b> + <v>1</v> + </b> + <b> + <a>128</a> + <b>129</b> + <v>1</v> + </b> + <b> + <a>345</a> + <b>346</b> + <v>1</v> + </b> + <b> + <a>2172</a> + <b>2173</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_else</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2834</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2834</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_else_def</name> + <cardinality>2228</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>2228</v> + </e> + <e> + <k>loc</k> + <v>2228</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2228</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2228</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_elsif_alternative</name> + <cardinality>280</cardinality> + <columnsizes> + <e> + <k>ruby_elsif</k> + <v>280</v> + </e> + <e> + <k>alternative</k> + <v>280</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_elsif</src> + <trg>alternative</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>280</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>alternative</src> + <trg>ruby_elsif</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>280</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_elsif_consequence</name> + <cardinality>505</cardinality> + <columnsizes> + <e> + <k>ruby_elsif</k> + <v>505</v> + </e> + <e> + <k>consequence</k> + <v>505</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_elsif</src> + <trg>consequence</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>505</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>consequence</src> + <trg>ruby_elsif</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>505</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_elsif_def</name> + <cardinality>506</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>506</v> + </e> + <e> + <k>condition</k> + <v>506</v> + </e> + <e> + <k>loc</k> + <v>506</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>506</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>506</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>506</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>506</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>506</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>506</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_end_block_child</name> + <cardinality>0</cardinality> + <columnsizes> + <e> + <k>ruby_end_block</k> + <v>0</v> + </e> + <e> + <k>index</k> + <v>0</v> + </e> + <e> + <k>child</k> + <v>0</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_end_block</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>ruby_end_block</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_end_block</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_end_block</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_end_block_def</name> + <cardinality>0</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>0</v> + </e> + <e> + <k>loc</k> + <v>0</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_ensure_child</name> + <cardinality>1636</cardinality> + <columnsizes> + <e> + <k>ruby_ensure</k> + <v>1216</v> + </e> + <e> + <k>index</k> + <v>16</v> + </e> + <e> + <k>child</k> + <v>1636</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_ensure</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>945</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>176</v> + </b> + <b> + <a>3</a> + <b>9</b> + <v>92</v> + </b> + <b> + <a>16</a> + <b>17</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_ensure</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>945</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>176</v> + </b> + <b> + <a>3</a> + <b>9</b> + <v>92</v> + </b> + <b> + <a>16</a> + <b>17</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_ensure</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>8</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>2</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>2</v> + </b> + <b> + <a>16</a> + <b>17</b> + <v>1</v> + </b> + <b> + <a>92</a> + <b>93</b> + <v>1</v> + </b> + <b> + <a>264</a> + <b>265</b> + <v>1</v> + </b> + <b> + <a>1187</a> + <b>1188</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>8</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>2</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>2</v> + </b> + <b> + <a>16</a> + <b>17</b> + <v>1</v> + </b> + <b> + <a>92</a> + <b>93</b> + <v>1</v> + </b> + <b> + <a>264</a> + <b>265</b> + <v>1</v> + </b> + <b> + <a>1187</a> + <b>1188</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_ensure</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1636</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1636</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_ensure_def</name> + <cardinality>1216</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1216</v> + </e> + <e> + <k>loc</k> + <v>1216</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1216</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1216</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_exception_variable_def</name> + <cardinality>328</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>328</v> + </e> + <e> + <k>child</k> + <v>328</v> + </e> + <e> + <k>loc</k> + <v>328</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>328</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>328</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>328</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>328</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>328</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>328</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_exceptions_child</name> + <cardinality>531</cardinality> + <columnsizes> + <e> + <k>ruby_exceptions</k> + <v>454</v> + </e> + <e> + <k>index</k> + <v>8</v> + </e> + <e> + <k>child</k> + <v>531</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_exceptions</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>406</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>33</v> + </b> + <b> + <a>3</a> + <b>9</b> + <v>15</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_exceptions</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>406</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>33</v> + </b> + <b> + <a>3</a> + <b>9</b> + <v>15</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_exceptions</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>3</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>15</a> + <b>16</b> + <v>1</v> + </b> + <b> + <a>48</a> + <b>49</b> + <v>1</v> + </b> + <b> + <a>454</a> + <b>455</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>3</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>15</a> + <b>16</b> + <v>1</v> + </b> + <b> + <a>48</a> + <b>49</b> + <v>1</v> + </b> + <b> + <a>454</a> + <b>455</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_exceptions</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>531</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>531</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_exceptions_def</name> + <cardinality>454</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>454</v> + </e> + <e> + <k>loc</k> + <v>454</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>454</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>454</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_for_def</name> + <cardinality>1</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1</v> + </e> + <e> + <k>body</k> + <v>1</v> + </e> + <e> + <k>pattern</k> + <v>1</v> + </e> + <e> + <k>value</k> + <v>1</v> + </e> + <e> + <k>loc</k> + <v>1</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>pattern</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>pattern</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>pattern</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>pattern</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>pattern</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>pattern</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>pattern</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>pattern</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_hash_child</name> + <cardinality>15313</cardinality> + <columnsizes> + <e> + <k>ruby_hash</k> + <v>7063</v> + </e> + <e> + <k>index</k> + <v>149</v> + </e> + <e> + <k>child</k> + <v>15313</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_hash</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3760</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1853</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>557</v> + </b> + <b> + <a>4</a> + <b>7</b> + <v>593</v> + </b> + <b> + <a>7</a> + <b>35</b> + <v>298</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_hash</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3760</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1853</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>557</v> + </b> + <b> + <a>4</a> + <b>7</b> + <v>593</v> + </b> + <b> + <a>7</a> + <b>35</b> + <v>298</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_hash</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>35</v> + </b> + <b> + <a>2</a> + <b>5</b> + <v>13</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>13</v> + </b> + <b> + <a>8</a> + <b>10</b> + <v>13</v> + </b> + <b> + <a>14</a> + <b>15</b> + <v>13</v> + </b> + <b> + <a>17</a> + <b>24</b> + <v>13</v> + </b> + <b> + <a>29</a> + <b>39</b> + <v>13</v> + </b> + <b> + <a>61</a> + <b>87</b> + <v>13</v> + </b> + <b> + <a>121</a> + <b>331</b> + <v>13</v> + </b> + <b> + <a>752</a> + <b>1609</b> + <v>8</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>35</v> + </b> + <b> + <a>2</a> + <b>5</b> + <v>13</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>13</v> + </b> + <b> + <a>8</a> + <b>10</b> + <v>13</v> + </b> + <b> + <a>14</a> + <b>15</b> + <v>13</v> + </b> + <b> + <a>17</a> + <b>24</b> + <v>13</v> + </b> + <b> + <a>29</a> + <b>39</b> + <v>13</v> + </b> + <b> + <a>61</a> + <b>87</b> + <v>13</v> + </b> + <b> + <a>121</a> + <b>331</b> + <v>13</v> + </b> + <b> + <a>752</a> + <b>1609</b> + <v>8</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_hash</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>15313</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>15313</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_hash_def</name> + <cardinality>8337</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>8337</v> + </e> + <e> + <k>loc</k> + <v>8337</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8337</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8337</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_hash_splat_argument_def</name> + <cardinality>427</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>427</v> + </e> + <e> + <k>child</k> + <v>427</v> + </e> + <e> + <k>loc</k> + <v>427</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>427</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>427</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>427</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>427</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>427</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>427</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_hash_splat_parameter_def</name> + <cardinality>432</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>432</v> + </e> + <e> + <k>loc</k> + <v>432</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>432</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>432</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_hash_splat_parameter_name</name> + <cardinality>361</cardinality> + <columnsizes> + <e> + <k>ruby_hash_splat_parameter</k> + <v>361</v> + </e> + <e> + <k>name</k> + <v>361</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_hash_splat_parameter</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>361</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>ruby_hash_splat_parameter</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>361</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_heredoc_body_child</name> + <cardinality>8097</cardinality> + <columnsizes> + <e> + <k>ruby_heredoc_body</k> + <v>1702</v> + </e> + <e> + <k>index</k> + <v>72</v> + </e> + <e> + <k>child</k> + <v>8097</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_heredoc_body</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>927</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>209</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>256</v> + </b> + <b> + <a>7</a> + <b>9</b> + <v>109</v> + </b> + <b> + <a>10</a> + <b>15</b> + <v>138</v> + </b> + <b> + <a>16</a> + <b>73</b> + <v>62</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_heredoc_body</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>927</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>209</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>256</v> + </b> + <b> + <a>7</a> + <b>9</b> + <v>109</v> + </b> + <b> + <a>10</a> + <b>15</b> + <v>138</v> + </b> + <b> + <a>16</a> + <b>73</b> + <v>62</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_heredoc_body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>19</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>7</v> + </b> + <b> + <a>3</a> + <b>6</b> + <v>6</v> + </b> + <b> + <a>6</a> + <b>9</b> + <v>5</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>3</v> + </b> + <b> + <a>11</a> + <b>14</b> + <v>6</v> + </b> + <b> + <a>15</a> + <b>25</b> + <v>6</v> + </b> + <b> + <a>30</a> + <b>63</b> + <v>6</v> + </b> + <b> + <a>90</a> + <b>201</b> + <v>6</v> + </b> + <b> + <a>304</a> + <b>776</b> + <v>6</v> + </b> + <b> + <a>1702</a> + <b>1703</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>19</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>7</v> + </b> + <b> + <a>3</a> + <b>6</b> + <v>6</v> + </b> + <b> + <a>6</a> + <b>9</b> + <v>5</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>3</v> + </b> + <b> + <a>11</a> + <b>14</b> + <v>6</v> + </b> + <b> + <a>15</a> + <b>25</b> + <v>6</v> + </b> + <b> + <a>30</a> + <b>63</b> + <v>6</v> + </b> + <b> + <a>90</a> + <b>201</b> + <v>6</v> + </b> + <b> + <a>304</a> + <b>776</b> + <v>6</v> + </b> + <b> + <a>1702</a> + <b>1703</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_heredoc_body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8097</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8097</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_heredoc_body_def</name> + <cardinality>1702</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1702</v> + </e> + <e> + <k>loc</k> + <v>1702</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1702</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1702</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_if_alternative</name> + <cardinality>2071</cardinality> + <columnsizes> + <e> + <k>ruby_if</k> + <v>2071</v> + </e> + <e> + <k>alternative</k> + <v>2071</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_if</src> + <trg>alternative</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2071</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>alternative</src> + <trg>ruby_if</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2071</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_if_consequence</name> + <cardinality>5971</cardinality> + <columnsizes> + <e> + <k>ruby_if</k> + <v>5971</v> + </e> + <e> + <k>consequence</k> + <v>5971</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_if</src> + <trg>consequence</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5971</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>consequence</src> + <trg>ruby_if</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5971</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_if_def</name> + <cardinality>5991</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>5991</v> + </e> + <e> + <k>condition</k> + <v>5991</v> + </e> + <e> + <k>loc</k> + <v>5991</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5991</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5991</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5991</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5991</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5991</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5991</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_if_modifier_def</name> + <cardinality>4440</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>4440</v> + </e> + <e> + <k>body</k> + <v>4440</v> + </e> + <e> + <k>condition</k> + <v>4440</v> + </e> + <e> + <k>loc</k> + <v>4440</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4440</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4440</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4440</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4440</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4440</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4440</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4440</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4440</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4440</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4440</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4440</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4440</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_in_def</name> + <cardinality>1</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1</v> + </e> + <e> + <k>child</k> + <v>1</v> + </e> + <e> + <k>loc</k> + <v>1</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_interpolation_child</name> + <cardinality>12427</cardinality> + <columnsizes> + <e> + <k>ruby_interpolation</k> + <v>12427</v> + </e> + <e> + <k>index</k> + <v>1</v> + </e> + <e> + <k>child</k> + <v>12427</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_interpolation</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>12427</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_interpolation</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>12427</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_interpolation</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>12427</a> + <b>12428</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>12427</a> + <b>12428</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_interpolation</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>12427</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>12427</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_interpolation_def</name> + <cardinality>12427</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>12427</v> + </e> + <e> + <k>loc</k> + <v>12427</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>12427</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>12427</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_keyword_parameter_def</name> + <cardinality>1152</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1152</v> + </e> + <e> + <k>name</k> + <v>1152</v> + </e> + <e> + <k>loc</k> + <v>1152</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1152</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1152</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1152</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1152</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1152</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1152</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_keyword_parameter_value</name> + <cardinality>856</cardinality> + <columnsizes> + <e> + <k>ruby_keyword_parameter</k> + <v>856</v> + </e> + <e> + <k>value</k> + <v>856</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_keyword_parameter</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>856</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>ruby_keyword_parameter</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>856</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_lambda_def</name> + <cardinality>852</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>852</v> + </e> + <e> + <k>body</k> + <v>852</v> + </e> + <e> + <k>loc</k> + <v>852</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>852</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>852</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>852</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>852</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>852</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>852</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_lambda_parameters</name> + <cardinality>201</cardinality> + <columnsizes> + <e> + <k>ruby_lambda</k> + <v>201</v> + </e> + <e> + <k>parameters</k> + <v>201</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_lambda</src> + <trg>parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>201</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parameters</src> + <trg>ruby_lambda</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>201</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_lambda_parameters_child</name> + <cardinality>259</cardinality> + <columnsizes> + <e> + <k>ruby_lambda_parameters</k> + <v>196</v> + </e> + <e> + <k>index</k> + <v>4</v> + </e> + <e> + <k>child</k> + <v>259</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_lambda_parameters</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>156</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>23</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_lambda_parameters</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>156</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>23</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_lambda_parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>6</a> + <b>7</b> + <v>1</v> + </b> + <b> + <a>16</a> + <b>17</b> + <v>1</v> + </b> + <b> + <a>39</a> + <b>40</b> + <v>1</v> + </b> + <b> + <a>192</a> + <b>193</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>6</a> + <b>7</b> + <v>1</v> + </b> + <b> + <a>16</a> + <b>17</b> + <v>1</v> + </b> + <b> + <a>39</a> + <b>40</b> + <v>1</v> + </b> + <b> + <a>192</a> + <b>193</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_lambda_parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>259</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>259</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_lambda_parameters_def</name> + <cardinality>201</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>201</v> + </e> + <e> + <k>loc</k> + <v>201</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>201</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>201</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_left_assignment_list_child</name> + <cardinality>1820</cardinality> + <columnsizes> + <e> + <k>ruby_left_assignment_list</k> + <v>817</v> + </e> + <e> + <k>index</k> + <v>9</v> + </e> + <e> + <k>child</k> + <v>1820</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_left_assignment_list</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>667</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>128</v> + </b> + <b> + <a>4</a> + <b>10</b> + <v>20</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_left_assignment_list</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>667</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>128</v> + </b> + <b> + <a>4</a> + <b>10</b> + <v>20</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_left_assignment_list</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>2</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>20</a> + <b>21</b> + <v>1</v> + </b> + <b> + <a>145</a> + <b>146</b> + <v>1</v> + </b> + <b> + <a>796</a> + <b>797</b> + <v>1</v> + </b> + <b> + <a>798</a> + <b>799</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>2</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>20</a> + <b>21</b> + <v>1</v> + </b> + <b> + <a>145</a> + <b>146</b> + <v>1</v> + </b> + <b> + <a>796</a> + <b>797</b> + <v>1</v> + </b> + <b> + <a>798</a> + <b>799</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_left_assignment_list</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1820</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1820</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_left_assignment_list_def</name> + <cardinality>817</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>817</v> + </e> + <e> + <k>loc</k> + <v>817</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>817</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>817</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_method_child</name> + <cardinality>85186</cardinality> + <columnsizes> + <e> + <k>ruby_method</k> + <v>31408</v> + </e> + <e> + <k>index</k> + <v>77</v> + </e> + <e> + <k>child</k> + <v>85186</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_method</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14155</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>5782</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>4078</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>2523</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>2625</v> + </b> + <b> + <a>7</a> + <b>77</b> + <v>2241</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_method</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14155</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>5782</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>4078</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>2523</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>2625</v> + </b> + <b> + <a>7</a> + <b>77</b> + <v>2241</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_method</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>7</v> + </b> + <b> + <a>2</a> + <b>4</b> + <v>2</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>9</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>9</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>9</v> + </b> + <b> + <a>9</a> + <b>12</b> + <v>4</v> + </b> + <b> + <a>13</a> + <b>19</b> + <v>6</v> + </b> + <b> + <a>20</a> + <b>38</b> + <v>6</v> + </b> + <b> + <a>45</a> + <b>114</b> + <v>6</v> + </b> + <b> + <a>149</a> + <b>399</b> + <v>6</v> + </b> + <b> + <a>510</a> + <b>2189</b> + <v>6</v> + </b> + <b> + <a>3195</a> + <b>30655</b> + <v>6</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>7</v> + </b> + <b> + <a>2</a> + <b>4</b> + <v>2</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>9</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>9</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>9</v> + </b> + <b> + <a>9</a> + <b>12</b> + <v>4</v> + </b> + <b> + <a>13</a> + <b>19</b> + <v>6</v> + </b> + <b> + <a>20</a> + <b>38</b> + <v>6</v> + </b> + <b> + <a>45</a> + <b>114</b> + <v>6</v> + </b> + <b> + <a>149</a> + <b>399</b> + <v>6</v> + </b> + <b> + <a>510</a> + <b>2189</b> + <v>6</v> + </b> + <b> + <a>3195</a> + <b>30655</b> + <v>6</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_method</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>85186</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>85186</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_method_def</name> + <cardinality>31713</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>31713</v> + </e> + <e> + <k>name</k> + <v>31713</v> + </e> + <e> + <k>loc</k> + <v>31713</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>31713</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>31713</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>31713</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>31713</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>31713</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>31713</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_method_parameters</name> + <cardinality>8765</cardinality> + <columnsizes> + <e> + <k>ruby_method</k> + <v>8765</v> + </e> + <e> + <k>parameters</k> + <v>8765</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_method</src> + <trg>parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8765</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parameters</src> + <trg>ruby_method</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8765</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_method_parameters_child</name> + <cardinality>15286</cardinality> + <columnsizes> + <e> + <k>ruby_method_parameters</k> + <v>9244</v> + </e> + <e> + <k>index</k> + <v>11</v> + </e> + <e> + <k>child</k> + <v>15286</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_method_parameters</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5566</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>2262</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>889</v> + </b> + <b> + <a>4</a> + <b>12</b> + <v>525</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_method_parameters</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5566</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>2262</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>889</v> + </b> + <b> + <a>4</a> + <b>12</b> + <v>525</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_method_parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>3</a> + <b>4</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>26</a> + <b>27</b> + <v>1</v> + </b> + <b> + <a>45</a> + <b>46</b> + <v>1</v> + </b> + <b> + <a>103</a> + <b>104</b> + <v>1</v> + </b> + <b> + <a>223</a> + <b>224</b> + <v>1</v> + </b> + <b> + <a>513</a> + <b>514</b> + <v>1</v> + </b> + <b> + <a>1381</a> + <b>1382</b> + <v>1</v> + </b> + <b> + <a>3589</a> + <b>3590</b> + <v>1</v> + </b> + <b> + <a>9022</a> + <b>9023</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>3</a> + <b>4</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>26</a> + <b>27</b> + <v>1</v> + </b> + <b> + <a>45</a> + <b>46</b> + <v>1</v> + </b> + <b> + <a>103</a> + <b>104</b> + <v>1</v> + </b> + <b> + <a>223</a> + <b>224</b> + <v>1</v> + </b> + <b> + <a>513</a> + <b>514</b> + <v>1</v> + </b> + <b> + <a>1381</a> + <b>1382</b> + <v>1</v> + </b> + <b> + <a>3589</a> + <b>3590</b> + <v>1</v> + </b> + <b> + <a>9022</a> + <b>9023</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_method_parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>15286</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>15286</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_method_parameters_def</name> + <cardinality>9313</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>9313</v> + </e> + <e> + <k>loc</k> + <v>9313</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9313</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9313</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_module_child</name> + <cardinality>10035</cardinality> + <columnsizes> + <e> + <k>ruby_module</k> + <v>3397</v> + </e> + <e> + <k>index</k> + <v>127</v> + </e> + <e> + <k>child</k> + <v>10035</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_module</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2407</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>288</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>243</v> + </b> + <b> + <a>5</a> + <b>11</b> + <v>270</v> + </b> + <b> + <a>11</a> + <b>125</b> + <v>186</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_module</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2407</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>288</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>243</v> + </b> + <b> + <a>5</a> + <b>11</b> + <v>270</v> + </b> + <b> + <a>11</a> + <b>125</b> + <v>186</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_module</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>11</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>11</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>2</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>23</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>10</v> + </b> + <b> + <a>8</a> + <b>10</b> + <v>10</v> + </b> + <b> + <a>10</a> + <b>16</b> + <v>11</v> + </b> + <b> + <a>16</a> + <b>23</b> + <v>11</v> + </b> + <b> + <a>25</a> + <b>45</b> + <v>10</v> + </b> + <b> + <a>50</a> + <b>107</b> + <v>10</v> + </b> + <b> + <a>123</a> + <b>374</b> + <v>10</v> + </b> + <b> + <a>446</a> + <b>3317</b> + <v>5</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>11</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>11</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>2</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>23</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>10</v> + </b> + <b> + <a>8</a> + <b>10</b> + <v>10</v> + </b> + <b> + <a>10</a> + <b>16</b> + <v>11</v> + </b> + <b> + <a>16</a> + <b>23</b> + <v>11</v> + </b> + <b> + <a>25</a> + <b>45</b> + <v>10</v> + </b> + <b> + <a>50</a> + <b>107</b> + <v>10</v> + </b> + <b> + <a>123</a> + <b>374</b> + <v>10</v> + </b> + <b> + <a>446</a> + <b>3317</b> + <v>5</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_module</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>10035</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>10035</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_module_def</name> + <cardinality>6369</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>6369</v> + </e> + <e> + <k>name</k> + <v>6369</v> + </e> + <e> + <k>loc</k> + <v>6369</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6369</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6369</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6369</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6369</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6369</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6369</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_next_child</name> + <cardinality>15</cardinality> + <columnsizes> + <e> + <k>ruby_next</k> + <v>15</v> + </e> + <e> + <k>child</k> + <v>15</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_next</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>15</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_next</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>15</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_next_def</name> + <cardinality>664</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>664</v> + </e> + <e> + <k>loc</k> + <v>664</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>664</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>664</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_operator_assignment_def</name> + <cardinality>2107</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>2107</v> + </e> + <e> + <k>left</k> + <v>2107</v> + </e> + <e> + <k>operator</k> + <v>6</v> + </e> + <e> + <k>right</k> + <v>2107</v> + </e> + <e> + <k>loc</k> + <v>2107</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2107</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2107</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2107</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2107</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2107</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2107</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2107</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2107</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>64</a> + <b>65</b> + <v>1</v> + </b> + <b> + <a>518</a> + <b>519</b> + <v>1</v> + </b> + <b> + <a>1510</a> + <b>1511</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>64</a> + <b>65</b> + <v>1</v> + </b> + <b> + <a>518</a> + <b>519</b> + <v>1</v> + </b> + <b> + <a>1510</a> + <b>1511</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>64</a> + <b>65</b> + <v>1</v> + </b> + <b> + <a>518</a> + <b>519</b> + <v>1</v> + </b> + <b> + <a>1510</a> + <b>1511</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>64</a> + <b>65</b> + <v>1</v> + </b> + <b> + <a>518</a> + <b>519</b> + <v>1</v> + </b> + <b> + <a>1510</a> + <b>1511</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2107</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2107</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2107</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2107</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2107</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2107</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2107</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2107</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_optional_parameter_def</name> + <cardinality>2105</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>2105</v> + </e> + <e> + <k>name</k> + <v>2105</v> + </e> + <e> + <k>value</k> + <v>2105</v> + </e> + <e> + <k>loc</k> + <v>2105</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2105</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2105</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2105</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2105</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2105</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2105</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2105</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2105</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2105</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2105</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2105</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2105</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_pair_def</name> + <cardinality>65283</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>65283</v> + </e> + <e> + <k>key__</k> + <v>65283</v> + </e> + <e> + <k>value</k> + <v>65283</v> + </e> + <e> + <k>loc</k> + <v>65283</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>key__</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>65283</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>65283</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>65283</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>key__</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>65283</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>key__</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>65283</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>key__</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>65283</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>65283</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>key__</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>65283</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>65283</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>65283</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>key__</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>65283</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>65283</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_parenthesized_statements_child</name> + <cardinality>1772</cardinality> + <columnsizes> + <e> + <k>ruby_parenthesized_statements</k> + <v>1771</v> + </e> + <e> + <k>index</k> + <v>2</v> + </e> + <e> + <k>child</k> + <v>1772</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_parenthesized_statements</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1770</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_parenthesized_statements</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1770</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_parenthesized_statements</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>1771</a> + <b>1772</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>1771</a> + <b>1772</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_parenthesized_statements</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1772</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1772</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_parenthesized_statements_def</name> + <cardinality>1771</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1771</v> + </e> + <e> + <k>loc</k> + <v>1771</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1771</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1771</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_pattern_def</name> + <cardinality>1234</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1234</v> + </e> + <e> + <k>child</k> + <v>1234</v> + </e> + <e> + <k>loc</k> + <v>1234</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1234</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1234</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1234</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1234</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1234</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1234</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_program_child</name> + <cardinality>10668</cardinality> + <columnsizes> + <e> + <k>ruby_program</k> + <v>3364</v> + </e> + <e> + <k>index</k> + <v>77</v> + </e> + <e> + <k>child</k> + <v>10668</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_program</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1239</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>813</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>530</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>251</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>300</v> + </b> + <b> + <a>8</a> + <b>77</b> + <v>229</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_program</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1239</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>813</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>530</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>251</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>300</v> + </b> + <b> + <a>8</a> + <b>77</b> + <v>229</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_program</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>20</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>10</v> + </b> + <b> + <a>3</a> + <b>9</b> + <v>6</v> + </b> + <b> + <a>9</a> + <b>15</b> + <v>6</v> + </b> + <b> + <a>15</a> + <b>21</b> + <v>6</v> + </b> + <b> + <a>23</a> + <b>35</b> + <v>6</v> + </b> + <b> + <a>37</a> + <b>62</b> + <v>6</v> + </b> + <b> + <a>66</a> + <b>137</b> + <v>6</v> + </b> + <b> + <a>159</a> + <b>518</b> + <v>6</v> + </b> + <b> + <a>762</a> + <b>3285</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>20</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>10</v> + </b> + <b> + <a>3</a> + <b>9</b> + <v>6</v> + </b> + <b> + <a>9</a> + <b>15</b> + <v>6</v> + </b> + <b> + <a>15</a> + <b>21</b> + <v>6</v> + </b> + <b> + <a>23</a> + <b>35</b> + <v>6</v> + </b> + <b> + <a>37</a> + <b>62</b> + <v>6</v> + </b> + <b> + <a>66</a> + <b>137</b> + <v>6</v> + </b> + <b> + <a>159</a> + <b>518</b> + <v>6</v> + </b> + <b> + <a>762</a> + <b>3285</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_program</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>10668</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>10668</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_program_def</name> + <cardinality>5100</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>5100</v> + </e> + <e> + <k>loc</k> + <v>5100</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5100</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5100</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_range_begin</name> + <cardinality>570</cardinality> + <columnsizes> + <e> + <k>ruby_range</k> + <v>570</v> + </e> + <e> + <k>begin</k> + <v>570</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_range</src> + <trg>begin</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>570</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>begin</src> + <trg>ruby_range</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>570</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_range_def</name> + <cardinality>577</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>577</v> + </e> + <e> + <k>operator</k> + <v>2</v> + </e> + <e> + <k>loc</k> + <v>577</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>577</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>577</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>125</a> + <b>126</b> + <v>1</v> + </b> + <b> + <a>439</a> + <b>440</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>125</a> + <b>126</b> + <v>1</v> + </b> + <b> + <a>439</a> + <b>440</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>577</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>577</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_range_end</name> + <cardinality>484</cardinality> + <columnsizes> + <e> + <k>ruby_range</k> + <v>484</v> + </e> + <e> + <k>end</k> + <v>484</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_range</src> + <trg>end</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>484</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end</src> + <trg>ruby_range</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>484</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_rational_def</name> + <cardinality>4</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>4</v> + </e> + <e> + <k>child</k> + <v>4</v> + </e> + <e> + <k>loc</k> + <v>4</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_redo_child</name> + <cardinality>0</cardinality> + <columnsizes> + <e> + <k>ruby_redo</k> + <v>0</v> + </e> + <e> + <k>child</k> + <v>0</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_redo</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_redo</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_redo_def</name> + <cardinality>0</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>0</v> + </e> + <e> + <k>loc</k> + <v>0</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_regex_child</name> + <cardinality>14013</cardinality> + <columnsizes> + <e> + <k>ruby_regex</k> + <v>4129</v> + </e> + <e> + <k>index</k> + <v>44</v> + </e> + <e> + <k>child</k> + <v>14013</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_regex</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2110</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>229</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>539</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>156</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>363</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>315</v> + </b> + <b> + <a>8</a> + <b>15</b> + <v>313</v> + </b> + <b> + <a>15</a> + <b>44</b> + <v>99</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_regex</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2110</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>229</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>539</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>156</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>363</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>315</v> + </b> + <b> + <a>8</a> + <b>15</b> + <v>313</v> + </b> + <b> + <a>15</a> + <b>44</b> + <v>99</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_regex</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>4</v> + </b> + <b> + <a>4</a> + <b>7</b> + <v>3</v> + </b> + <b> + <a>7</a> + <b>11</b> + <v>3</v> + </b> + <b> + <a>12</a> + <b>17</b> + <v>3</v> + </b> + <b> + <a>17</a> + <b>18</b> + <v>2</v> + </b> + <b> + <a>20</a> + <b>22</b> + <v>3</v> + </b> + <b> + <a>23</a> + <b>25</b> + <v>2</v> + </b> + <b> + <a>25</a> + <b>31</b> + <v>3</v> + </b> + <b> + <a>32</a> + <b>40</b> + <v>3</v> + </b> + <b> + <a>57</a> + <b>98</b> + <v>3</v> + </b> + <b> + <a>108</a> + <b>169</b> + <v>3</v> + </b> + <b> + <a>231</a> + <b>343</b> + <v>3</v> + </b> + <b> + <a>403</a> + <b>712</b> + <v>3</v> + </b> + <b> + <a>1066</a> + <b>1747</b> + <v>3</v> + </b> + <b> + <a>1970</a> + <b>4031</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>4</v> + </b> + <b> + <a>4</a> + <b>7</b> + <v>3</v> + </b> + <b> + <a>7</a> + <b>11</b> + <v>3</v> + </b> + <b> + <a>12</a> + <b>17</b> + <v>3</v> + </b> + <b> + <a>17</a> + <b>18</b> + <v>2</v> + </b> + <b> + <a>20</a> + <b>22</b> + <v>3</v> + </b> + <b> + <a>23</a> + <b>25</b> + <v>2</v> + </b> + <b> + <a>25</a> + <b>31</b> + <v>3</v> + </b> + <b> + <a>32</a> + <b>40</b> + <v>3</v> + </b> + <b> + <a>57</a> + <b>98</b> + <v>3</v> + </b> + <b> + <a>108</a> + <b>169</b> + <v>3</v> + </b> + <b> + <a>231</a> + <b>343</b> + <v>3</v> + </b> + <b> + <a>403</a> + <b>712</b> + <v>3</v> + </b> + <b> + <a>1066</a> + <b>1747</b> + <v>3</v> + </b> + <b> + <a>1970</a> + <b>4031</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_regex</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14013</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14013</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_regex_def</name> + <cardinality>4134</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>4134</v> + </e> + <e> + <k>loc</k> + <v>4134</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4134</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4134</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_rescue_body</name> + <cardinality>574</cardinality> + <columnsizes> + <e> + <k>ruby_rescue</k> + <v>574</v> + </e> + <e> + <k>body</k> + <v>574</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_rescue</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>574</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>ruby_rescue</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>574</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_rescue_def</name> + <cardinality>669</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>669</v> + </e> + <e> + <k>loc</k> + <v>669</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>669</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>669</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_rescue_exceptions</name> + <cardinality>454</cardinality> + <columnsizes> + <e> + <k>ruby_rescue</k> + <v>454</v> + </e> + <e> + <k>exceptions</k> + <v>454</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_rescue</src> + <trg>exceptions</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>454</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>exceptions</src> + <trg>ruby_rescue</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>454</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_rescue_modifier_def</name> + <cardinality>184</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>184</v> + </e> + <e> + <k>body</k> + <v>184</v> + </e> + <e> + <k>handler</k> + <v>184</v> + </e> + <e> + <k>loc</k> + <v>184</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>184</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>handler</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>184</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>184</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>184</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>handler</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>184</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>184</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>handler</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>184</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>handler</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>184</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>handler</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>184</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>184</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>184</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>handler</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>184</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_rescue_variable</name> + <cardinality>328</cardinality> + <columnsizes> + <e> + <k>ruby_rescue</k> + <v>328</v> + </e> + <e> + <k>variable</k> + <v>328</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_rescue</src> + <trg>variable</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>328</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>variable</src> + <trg>ruby_rescue</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>328</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_rest_assignment_child</name> + <cardinality>7</cardinality> + <columnsizes> + <e> + <k>ruby_rest_assignment</k> + <v>7</v> + </e> + <e> + <k>child</k> + <v>7</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_rest_assignment</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>7</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_rest_assignment</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>7</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_rest_assignment_def</name> + <cardinality>18</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>18</v> + </e> + <e> + <k>loc</k> + <v>18</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>18</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>18</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_retry_child</name> + <cardinality>0</cardinality> + <columnsizes> + <e> + <k>ruby_retry</k> + <v>0</v> + </e> + <e> + <k>child</k> + <v>0</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_retry</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_retry</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_retry_def</name> + <cardinality>12</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>12</v> + </e> + <e> + <k>loc</k> + <v>12</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>12</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>12</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_return_child</name> + <cardinality>1731</cardinality> + <columnsizes> + <e> + <k>ruby_return</k> + <v>1731</v> + </e> + <e> + <k>child</k> + <v>1731</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_return</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1731</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_return</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1731</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_return_def</name> + <cardinality>2746</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>2746</v> + </e> + <e> + <k>loc</k> + <v>2746</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2746</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2746</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_right_assignment_list_child</name> + <cardinality>938</cardinality> + <columnsizes> + <e> + <k>ruby_right_assignment_list</k> + <v>440</v> + </e> + <e> + <k>index</k> + <v>5</v> + </e> + <e> + <k>child</k> + <v>938</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_right_assignment_list</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>394</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>36</v> + </b> + <b> + <a>4</a> + <b>6</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_right_assignment_list</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>394</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>36</v> + </b> + <b> + <a>4</a> + <b>6</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_right_assignment_list</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>45</a> + <b>46</b> + <v>1</v> + </b> + <b> + <a>430</a> + <b>431</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>45</a> + <b>46</b> + <v>1</v> + </b> + <b> + <a>430</a> + <b>431</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_right_assignment_list</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>938</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>938</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_right_assignment_list_def</name> + <cardinality>440</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>440</v> + </e> + <e> + <k>loc</k> + <v>440</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>440</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>440</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_scope_resolution_def</name> + <cardinality>24908</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>24908</v> + </e> + <e> + <k>name</k> + <v>24908</v> + </e> + <e> + <k>loc</k> + <v>24908</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>24908</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>24908</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>24908</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>24908</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>24908</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>24908</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_scope_resolution_scope</name> + <cardinality>24336</cardinality> + <columnsizes> + <e> + <k>ruby_scope_resolution</k> + <v>24336</v> + </e> + <e> + <k>scope</k> + <v>24336</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_scope_resolution</src> + <trg>scope</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>24336</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>scope</src> + <trg>ruby_scope_resolution</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>24336</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_setter_def</name> + <cardinality>195</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>195</v> + </e> + <e> + <k>name</k> + <v>195</v> + </e> + <e> + <k>loc</k> + <v>195</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>195</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>195</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>195</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>195</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>195</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>195</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_singleton_class_child</name> + <cardinality>761</cardinality> + <columnsizes> + <e> + <k>ruby_singleton_class</k> + <v>198</v> + </e> + <e> + <k>index</k> + <v>24</v> + </e> + <e> + <k>child</k> + <v>761</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_singleton_class</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>93</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>23</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>11</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>15</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>10</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>17</v> + </b> + <b> + <a>8</a> + <b>14</b> + <v>17</v> + </b> + <b> + <a>14</a> + <b>25</b> + <v>10</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_singleton_class</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>93</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>23</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>11</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>15</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>10</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>17</v> + </b> + <b> + <a>8</a> + <b>14</b> + <v>17</v> + </b> + <b> + <a>14</a> + <b>25</b> + <v>10</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_singleton_class</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>3</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>2</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>1</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>2</v> + </b> + <b> + <a>10</a> + <b>14</b> + <v>2</v> + </b> + <b> + <a>17</a> + <b>19</b> + <v>2</v> + </b> + <b> + <a>20</a> + <b>24</b> + <v>2</v> + </b> + <b> + <a>27</a> + <b>35</b> + <v>2</v> + </b> + <b> + <a>44</a> + <b>55</b> + <v>2</v> + </b> + <b> + <a>69</a> + <b>81</b> + <v>2</v> + </b> + <b> + <a>103</a> + <b>195</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>3</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>2</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>1</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>2</v> + </b> + <b> + <a>10</a> + <b>14</b> + <v>2</v> + </b> + <b> + <a>17</a> + <b>19</b> + <v>2</v> + </b> + <b> + <a>20</a> + <b>24</b> + <v>2</v> + </b> + <b> + <a>27</a> + <b>35</b> + <v>2</v> + </b> + <b> + <a>44</a> + <b>55</b> + <v>2</v> + </b> + <b> + <a>69</a> + <b>81</b> + <v>2</v> + </b> + <b> + <a>103</a> + <b>195</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_singleton_class</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>761</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>761</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_singleton_class_def</name> + <cardinality>198</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>198</v> + </e> + <e> + <k>value</k> + <v>198</v> + </e> + <e> + <k>loc</k> + <v>198</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>198</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>198</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>198</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>198</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>198</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>198</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_singleton_method_child</name> + <cardinality>5233</cardinality> + <columnsizes> + <e> + <k>ruby_singleton_method</k> + <v>2139</v> + </e> + <e> + <k>index</k> + <v>28</v> + </e> + <e> + <k>child</k> + <v>5233</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_singleton_method</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1212</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>325</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>187</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>131</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>166</v> + </b> + <b> + <a>8</a> + <b>29</b> + <v>118</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_singleton_method</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1212</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>325</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>187</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>131</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>166</v> + </b> + <b> + <a>8</a> + <b>29</b> + <v>118</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_singleton_method</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>2</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>2</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>4</v> + </b> + <b> + <a>7</a> + <b>9</b> + <v>2</v> + </b> + <b> + <a>11</a> + <b>16</b> + <v>2</v> + </b> + <b> + <a>22</a> + <b>27</b> + <v>2</v> + </b> + <b> + <a>31</a> + <b>39</b> + <v>2</v> + </b> + <b> + <a>49</a> + <b>64</b> + <v>2</v> + </b> + <b> + <a>86</a> + <b>119</b> + <v>2</v> + </b> + <b> + <a>151</a> + <b>203</b> + <v>2</v> + </b> + <b> + <a>284</a> + <b>416</b> + <v>2</v> + </b> + <b> + <a>602</a> + <b>928</b> + <v>2</v> + </b> + <b> + <a>2139</a> + <b>2140</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>2</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>2</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>4</v> + </b> + <b> + <a>7</a> + <b>9</b> + <v>2</v> + </b> + <b> + <a>11</a> + <b>16</b> + <v>2</v> + </b> + <b> + <a>22</a> + <b>27</b> + <v>2</v> + </b> + <b> + <a>31</a> + <b>39</b> + <v>2</v> + </b> + <b> + <a>49</a> + <b>64</b> + <v>2</v> + </b> + <b> + <a>86</a> + <b>119</b> + <v>2</v> + </b> + <b> + <a>151</a> + <b>203</b> + <v>2</v> + </b> + <b> + <a>284</a> + <b>416</b> + <v>2</v> + </b> + <b> + <a>602</a> + <b>928</b> + <v>2</v> + </b> + <b> + <a>2139</a> + <b>2140</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_singleton_method</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5233</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5233</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_singleton_method_def</name> + <cardinality>2139</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>2139</v> + </e> + <e> + <k>name</k> + <v>2139</v> + </e> + <e> + <k>object</k> + <v>2139</v> + </e> + <e> + <k>loc</k> + <v>2139</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2139</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>object</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2139</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2139</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2139</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>object</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2139</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2139</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>object</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2139</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>object</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2139</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>object</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2139</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2139</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2139</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>object</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2139</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_singleton_method_parameters</name> + <cardinality>1342</cardinality> + <columnsizes> + <e> + <k>ruby_singleton_method</k> + <v>1342</v> + </e> + <e> + <k>parameters</k> + <v>1342</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_singleton_method</src> + <trg>parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1342</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parameters</src> + <trg>ruby_singleton_method</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1342</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_splat_argument_def</name> + <cardinality>693</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>693</v> + </e> + <e> + <k>child</k> + <v>693</v> + </e> + <e> + <k>loc</k> + <v>693</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>693</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>693</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>693</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>693</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>693</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>693</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_splat_parameter_def</name> + <cardinality>946</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>946</v> + </e> + <e> + <k>loc</k> + <v>946</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>946</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>946</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_splat_parameter_name</name> + <cardinality>768</cardinality> + <columnsizes> + <e> + <k>ruby_splat_parameter</k> + <v>768</v> + </e> + <e> + <k>name</k> + <v>768</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_splat_parameter</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>768</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>ruby_splat_parameter</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>768</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_string_array_child</name> + <cardinality>3119</cardinality> + <columnsizes> + <e> + <k>ruby_string_array</k> + <v>971</v> + </e> + <e> + <k>index</k> + <v>76</v> + </e> + <e> + <k>child</k> + <v>3119</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_string_array</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>207</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>320</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>245</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>69</v> + </b> + <b> + <a>5</a> + <b>9</b> + <v>79</v> + </b> + <b> + <a>9</a> + <b>76</b> + <v>47</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_string_array</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>207</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>320</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>245</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>69</v> + </b> + <b> + <a>5</a> + <b>9</b> + <v>79</v> + </b> + <b> + <a>9</a> + <b>76</b> + <v>47</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_string_array</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>30</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>6</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>13</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>4</v> + </b> + <b> + <a>7</a> + <b>13</b> + <v>5</v> + </b> + <b> + <a>13</a> + <b>36</b> + <v>6</v> + </b> + <b> + <a>41</a> + <b>125</b> + <v>6</v> + </b> + <b> + <a>192</a> + <b>949</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>30</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>6</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>13</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>4</v> + </b> + <b> + <a>7</a> + <b>13</b> + <v>5</v> + </b> + <b> + <a>13</a> + <b>36</b> + <v>6</v> + </b> + <b> + <a>41</a> + <b>125</b> + <v>6</v> + </b> + <b> + <a>192</a> + <b>949</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_string_array</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3119</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3119</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_string_array_def</name> + <cardinality>977</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>977</v> + </e> + <e> + <k>loc</k> + <v>977</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>977</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>977</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_string_child</name> + <cardinality>130566</cardinality> + <columnsizes> + <e> + <k>ruby_string__</k> + <v>94788</v> + </e> + <e> + <k>index</k> + <v>127</v> + </e> + <e> + <k>child</k> + <v>130566</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_string__</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>87623</v> + </b> + <b> + <a>2</a> + <b>121</b> + <v>7146</v> + </b> + <b> + <a>122</a> + <b>125</b> + <v>18</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_string__</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>87623</v> + </b> + <b> + <a>2</a> + <b>121</b> + <v>7146</v> + </b> + <b> + <a>122</a> + <b>125</b> + <v>18</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_string__</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>19</b> + <v>4</v> + </b> + <b> + <a>61</a> + <b>62</b> + <v>13</v> + </b> + <b> + <a>62</a> + <b>63</b> + <v>37</v> + </b> + <b> + <a>64</a> + <b>82</b> + <v>8</v> + </b> + <b> + <a>142</a> + <b>146</b> + <v>10</v> + </b> + <b> + <a>146</a> + <b>195</b> + <v>10</v> + </b> + <b> + <a>195</a> + <b>218</b> + <v>10</v> + </b> + <b> + <a>220</a> + <b>366</b> + <v>10</v> + </b> + <b> + <a>404</a> + <b>480</b> + <v>10</v> + </b> + <b> + <a>485</a> + <b>3529</b> + <v>10</v> + </b> + <b> + <a>6993</a> + <b>92513</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>19</b> + <v>4</v> + </b> + <b> + <a>61</a> + <b>62</b> + <v>13</v> + </b> + <b> + <a>62</a> + <b>63</b> + <v>37</v> + </b> + <b> + <a>64</a> + <b>82</b> + <v>8</v> + </b> + <b> + <a>142</a> + <b>146</b> + <v>10</v> + </b> + <b> + <a>146</a> + <b>195</b> + <v>10</v> + </b> + <b> + <a>195</a> + <b>218</b> + <v>10</v> + </b> + <b> + <a>220</a> + <b>366</b> + <v>10</v> + </b> + <b> + <a>404</a> + <b>480</b> + <v>10</v> + </b> + <b> + <a>485</a> + <b>3529</b> + <v>10</v> + </b> + <b> + <a>6993</a> + <b>92513</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_string__</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>130566</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>130566</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_string_def</name> + <cardinality>95938</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>95938</v> + </e> + <e> + <k>loc</k> + <v>95938</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>95938</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>95938</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_subshell_child</name> + <cardinality>211</cardinality> + <columnsizes> + <e> + <k>ruby_subshell</k> + <v>134</v> + </e> + <e> + <k>index</k> + <v>11</v> + </e> + <e> + <k>child</k> + <v>211</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_subshell</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>101</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>17</v> + </b> + <b> + <a>3</a> + <b>6</b> + <v>9</v> + </b> + <b> + <a>6</a> + <b>12</b> + <v>6</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_subshell</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>101</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>17</v> + </b> + <b> + <a>3</a> + <b>6</b> + <v>9</v> + </b> + <b> + <a>6</a> + <b>12</b> + <v>6</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_subshell</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>1</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>1</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>15</a> + <b>16</b> + <v>1</v> + </b> + <b> + <a>32</a> + <b>33</b> + <v>1</v> + </b> + <b> + <a>131</a> + <b>132</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>1</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>1</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>15</a> + <b>16</b> + <v>1</v> + </b> + <b> + <a>32</a> + <b>33</b> + <v>1</v> + </b> + <b> + <a>131</a> + <b>132</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_subshell</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>211</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>211</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_subshell_def</name> + <cardinality>134</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>134</v> + </e> + <e> + <k>loc</k> + <v>134</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>134</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>134</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_superclass_def</name> + <cardinality>4276</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>4276</v> + </e> + <e> + <k>child</k> + <v>4276</v> + </e> + <e> + <k>loc</k> + <v>4276</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4276</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4276</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4276</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4276</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4276</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4276</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_symbol_array_child</name> + <cardinality>685</cardinality> + <columnsizes> + <e> + <k>ruby_symbol_array</k> + <v>140</v> + </e> + <e> + <k>index</k> + <v>32</v> + </e> + <e> + <k>child</k> + <v>685</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_symbol_array</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>53</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>25</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>13</v> + </b> + <b> + <a>4</a> + <b>6</b> + <v>7</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>8</v> + </b> + <b> + <a>7</a> + <b>10</b> + <v>12</v> + </b> + <b> + <a>10</a> + <b>16</b> + <v>12</v> + </b> + <b> + <a>17</a> + <b>33</b> + <v>10</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_symbol_array</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>53</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>25</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>13</v> + </b> + <b> + <a>4</a> + <b>6</b> + <v>7</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>8</v> + </b> + <b> + <a>7</a> + <b>10</b> + <v>12</v> + </b> + <b> + <a>10</a> + <b>16</b> + <v>12</v> + </b> + <b> + <a>17</a> + <b>33</b> + <v>10</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_symbol_array</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>4</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>3</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>2</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>2</v> + </b> + <b> + <a>10</a> + <b>11</b> + <v>2</v> + </b> + <b> + <a>13</a> + <b>17</b> + <v>2</v> + </b> + <b> + <a>17</a> + <b>20</b> + <v>2</v> + </b> + <b> + <a>21</a> + <b>23</b> + <v>2</v> + </b> + <b> + <a>27</a> + <b>30</b> + <v>2</v> + </b> + <b> + <a>34</a> + <b>43</b> + <v>2</v> + </b> + <b> + <a>43</a> + <b>50</b> + <v>2</v> + </b> + <b> + <a>62</a> + <b>88</b> + <v>2</v> + </b> + <b> + <a>140</a> + <b>141</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>4</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>3</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>2</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>2</v> + </b> + <b> + <a>10</a> + <b>11</b> + <v>2</v> + </b> + <b> + <a>13</a> + <b>17</b> + <v>2</v> + </b> + <b> + <a>17</a> + <b>20</b> + <v>2</v> + </b> + <b> + <a>21</a> + <b>23</b> + <v>2</v> + </b> + <b> + <a>27</a> + <b>30</b> + <v>2</v> + </b> + <b> + <a>34</a> + <b>43</b> + <v>2</v> + </b> + <b> + <a>43</a> + <b>50</b> + <v>2</v> + </b> + <b> + <a>62</a> + <b>88</b> + <v>2</v> + </b> + <b> + <a>140</a> + <b>141</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_symbol_array</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>685</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>685</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_symbol_array_def</name> + <cardinality>140</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>140</v> + </e> + <e> + <k>loc</k> + <v>140</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>140</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>140</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_then_child</name> + <cardinality>13507</cardinality> + <columnsizes> + <e> + <k>ruby_then</k> + <v>7974</v> + </e> + <e> + <k>index</k> + <v>35</v> + </e> + <e> + <k>child</k> + <v>13507</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_then</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4962</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1805</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>662</v> + </b> + <b> + <a>4</a> + <b>36</b> + <v>545</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_then</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4962</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1805</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>662</v> + </b> + <b> + <a>4</a> + <b>36</b> + <v>545</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_then</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>3</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>3</v> + </b> + <b> + <a>7</a> + <b>11</b> + <v>3</v> + </b> + <b> + <a>12</a> + <b>28</b> + <v>3</v> + </b> + <b> + <a>43</a> + <b>92</b> + <v>3</v> + </b> + <b> + <a>157</a> + <b>546</b> + <v>3</v> + </b> + <b> + <a>1207</a> + <b>7975</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>3</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>3</v> + </b> + <b> + <a>7</a> + <b>11</b> + <v>3</v> + </b> + <b> + <a>12</a> + <b>28</b> + <v>3</v> + </b> + <b> + <a>43</a> + <b>92</b> + <v>3</v> + </b> + <b> + <a>157</a> + <b>546</b> + <v>3</v> + </b> + <b> + <a>1207</a> + <b>7975</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_then</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13507</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13507</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_then_def</name> + <cardinality>7974</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>7974</v> + </e> + <e> + <k>loc</k> + <v>7974</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>7974</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>7974</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_tokeninfo</name> + <cardinality>1900891</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1900891</v> + </e> + <e> + <k>kind</k> + <v>23</v> + </e> + <e> + <k>value</k> + <v>85529</v> + </e> + <e> + <k>loc</k> + <v>1900857</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>kind</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1900891</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1900891</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1900891</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>kind</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>35</b> + <v>2</v> + </b> + <b> + <a>139</a> + <b>215</b> + <v>2</v> + </b> + <b> + <a>448</a> + <b>1559</b> + <v>2</v> + </b> + <b> + <a>1702</a> + <b>1703</b> + <v>2</v> + </b> + <b> + <a>3878</a> + <b>4010</b> + <v>2</v> + </b> + <b> + <a>4065</a> + <b>5413</b> + <v>2</v> + </b> + <b> + <a>7426</a> + <b>9322</b> + <v>2</v> + </b> + <b> + <a>13268</a> + <b>16529</b> + <v>2</v> + </b> + <b> + <a>23105</a> + <b>52596</b> + <v>2</v> + </b> + <b> + <a>52780</a> + <b>76069</b> + <v>2</v> + </b> + <b> + <a>90090</a> + <b>477694</b> + <v>2</v> + </b> + <b> + <a>1058855</a> + <b>1058856</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>kind</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6</v> + </b> + <b> + <a>5</a> + <b>33</b> + <v>2</v> + </b> + <b> + <a>44</a> + <b>55</b> + <v>2</v> + </b> + <b> + <a>61</a> + <b>121</b> + <v>2</v> + </b> + <b> + <a>123</a> + <b>137</b> + <v>2</v> + </b> + <b> + <a>558</a> + <b>1709</b> + <v>2</v> + </b> + <b> + <a>2944</a> + <b>3623</b> + <v>2</v> + </b> + <b> + <a>4420</a> + <b>7408</b> + <v>2</v> + </b> + <b> + <a>9662</a> + <b>18033</b> + <v>2</v> + </b> + <b> + <a>42031</a> + <b>42032</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>kind</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>35</b> + <v>2</v> + </b> + <b> + <a>139</a> + <b>215</b> + <v>2</v> + </b> + <b> + <a>448</a> + <b>1559</b> + <v>2</v> + </b> + <b> + <a>1702</a> + <b>1703</b> + <v>2</v> + </b> + <b> + <a>3878</a> + <b>4010</b> + <v>2</v> + </b> + <b> + <a>4065</a> + <b>5413</b> + <v>2</v> + </b> + <b> + <a>7426</a> + <b>9322</b> + <v>2</v> + </b> + <b> + <a>13268</a> + <b>16529</b> + <v>2</v> + </b> + <b> + <a>23105</a> + <b>52596</b> + <v>2</v> + </b> + <b> + <a>52780</a> + <b>76069</b> + <v>2</v> + </b> + <b> + <a>90090</a> + <b>477694</b> + <v>2</v> + </b> + <b> + <a>1058855</a> + <b>1058856</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>50563</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>12534</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>6042</v> + </b> + <b> + <a>4</a> + <b>7</b> + <v>7155</v> + </b> + <b> + <a>7</a> + <b>26</b> + <v>6458</v> + </b> + <b> + <a>26</a> + <b>175170</b> + <v>2777</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>kind</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>81175</v> + </b> + <b> + <a>2</a> + <b>5</b> + <v>4354</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>50564</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>12533</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>6042</v> + </b> + <b> + <a>4</a> + <b>7</b> + <v>7155</v> + </b> + <b> + <a>7</a> + <b>26</b> + <v>6458</v> + </b> + <b> + <a>26</a> + <b>175170</b> + <v>2777</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1900823</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>34</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>kind</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1900823</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>34</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1900857</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_unary_def</name> + <cardinality>2603</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>2603</v> + </e> + <e> + <k>operand</k> + <v>2603</v> + </e> + <e> + <k>operator</k> + <v>5</v> + </e> + <e> + <k>loc</k> + <v>2603</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>operand</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2603</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2603</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2603</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operand</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2603</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operand</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2603</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operand</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2603</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>10</a> + <b>11</b> + <v>1</v> + </b> + <b> + <a>82</a> + <b>83</b> + <v>1</v> + </b> + <b> + <a>138</a> + <b>139</b> + <v>1</v> + </b> + <b> + <a>554</a> + <b>555</b> + <v>1</v> + </b> + <b> + <a>1819</a> + <b>1820</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>operand</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>10</a> + <b>11</b> + <v>1</v> + </b> + <b> + <a>82</a> + <b>83</b> + <v>1</v> + </b> + <b> + <a>138</a> + <b>139</b> + <v>1</v> + </b> + <b> + <a>554</a> + <b>555</b> + <v>1</v> + </b> + <b> + <a>1819</a> + <b>1820</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>10</a> + <b>11</b> + <v>1</v> + </b> + <b> + <a>82</a> + <b>83</b> + <v>1</v> + </b> + <b> + <a>138</a> + <b>139</b> + <v>1</v> + </b> + <b> + <a>554</a> + <b>555</b> + <v>1</v> + </b> + <b> + <a>1819</a> + <b>1820</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2603</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>operand</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2603</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2603</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_undef_child</name> + <cardinality>13</cardinality> + <columnsizes> + <e> + <k>ruby_undef</k> + <v>13</v> + </e> + <e> + <k>index</k> + <v>1</v> + </e> + <e> + <k>child</k> + <v>13</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_undef</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_undef</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_undef</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>13</a> + <b>14</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>13</a> + <b>14</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_undef</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_undef_def</name> + <cardinality>13</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>13</v> + </e> + <e> + <k>loc</k> + <v>13</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_unless_alternative</name> + <cardinality>14</cardinality> + <columnsizes> + <e> + <k>ruby_unless</k> + <v>14</v> + </e> + <e> + <k>alternative</k> + <v>14</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_unless</src> + <trg>alternative</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>alternative</src> + <trg>ruby_unless</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_unless_consequence</name> + <cardinality>471</cardinality> + <columnsizes> + <e> + <k>ruby_unless</k> + <v>471</v> + </e> + <e> + <k>consequence</k> + <v>471</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_unless</src> + <trg>consequence</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>471</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>consequence</src> + <trg>ruby_unless</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>471</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_unless_def</name> + <cardinality>471</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>471</v> + </e> + <e> + <k>condition</k> + <v>471</v> + </e> + <e> + <k>loc</k> + <v>471</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>471</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>471</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>471</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>471</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>471</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>471</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_unless_modifier_def</name> + <cardinality>1435</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1435</v> + </e> + <e> + <k>body</k> + <v>1435</v> + </e> + <e> + <k>condition</k> + <v>1435</v> + </e> + <e> + <k>loc</k> + <v>1435</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1435</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1435</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1435</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1435</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1435</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1435</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1435</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1435</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1435</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1435</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1435</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1435</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_until_def</name> + <cardinality>16</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>16</v> + </e> + <e> + <k>body</k> + <v>16</v> + </e> + <e> + <k>condition</k> + <v>16</v> + </e> + <e> + <k>loc</k> + <v>16</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_until_modifier_def</name> + <cardinality>13</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>13</v> + </e> + <e> + <k>body</k> + <v>13</v> + </e> + <e> + <k>condition</k> + <v>13</v> + </e> + <e> + <k>loc</k> + <v>13</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_when_body</name> + <cardinality>1015</cardinality> + <columnsizes> + <e> + <k>ruby_when</k> + <v>1015</v> + </e> + <e> + <k>body</k> + <v>1015</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_when</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1015</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>ruby_when</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1015</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_when_def</name> + <cardinality>1025</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1025</v> + </e> + <e> + <k>loc</k> + <v>1025</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1025</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1025</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_when_pattern</name> + <cardinality>1234</cardinality> + <columnsizes> + <e> + <k>ruby_when</k> + <v>1025</v> + </e> + <e> + <k>index</k> + <v>14</v> + </e> + <e> + <k>pattern</k> + <v>1234</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_when</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>895</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>97</v> + </b> + <b> + <a>3</a> + <b>15</b> + <v>32</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ruby_when</src> + <trg>pattern</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>895</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>97</v> + </b> + <b> + <a>3</a> + <b>15</b> + <v>32</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ruby_when</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>4</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>4</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>1</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>1</v> + </b> + <b> + <a>12</a> + <b>13</b> + <v>1</v> + </b> + <b> + <a>32</a> + <b>33</b> + <v>1</v> + </b> + <b> + <a>127</a> + <b>128</b> + <v>1</v> + </b> + <b> + <a>1001</a> + <b>1002</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>pattern</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>4</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>4</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>1</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>1</v> + </b> + <b> + <a>12</a> + <b>13</b> + <v>1</v> + </b> + <b> + <a>32</a> + <b>33</b> + <v>1</v> + </b> + <b> + <a>127</a> + <b>128</b> + <v>1</v> + </b> + <b> + <a>1001</a> + <b>1002</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>pattern</src> + <trg>ruby_when</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1234</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>pattern</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1234</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_while_def</name> + <cardinality>109</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>109</v> + </e> + <e> + <k>body</k> + <v>109</v> + </e> + <e> + <k>condition</k> + <v>109</v> + </e> + <e> + <k>loc</k> + <v>109</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>109</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>109</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>109</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>109</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>109</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>109</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>109</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>109</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>109</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>109</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>109</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>109</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_while_modifier_def</name> + <cardinality>9</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>9</v> + </e> + <e> + <k>body</k> + <v>9</v> + </e> + <e> + <k>condition</k> + <v>9</v> + </e> + <e> + <k>loc</k> + <v>9</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_yield_child</name> + <cardinality>360</cardinality> + <columnsizes> + <e> + <k>ruby_yield</k> + <v>360</v> + </e> + <e> + <k>child</k> + <v>360</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ruby_yield</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>360</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ruby_yield</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>360</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ruby_yield_def</name> + <cardinality>774</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>774</v> + </e> + <e> + <k>loc</k> + <v>774</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>774</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>774</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>sourceLocationPrefix</name> + <cardinality>4</cardinality> + <columnsizes> + <e> + <k>prefix</k> + <v>4</v> + </e> + </columnsizes> + <dependencies/> + </relation> + </stats> +</dbstats> diff --git a/ruby/ql/lib/ruby.qll b/ruby/ql/lib/ruby.qll new file mode 100644 index 00000000000..18468c9f8cf --- /dev/null +++ b/ruby/ql/lib/ruby.qll @@ -0,0 +1 @@ +import codeql.ruby.AST diff --git a/ruby/ql/lib/tutorial.qll b/ruby/ql/lib/tutorial.qll new file mode 100644 index 00000000000..8cb1797a532 --- /dev/null +++ b/ruby/ql/lib/tutorial.qll @@ -0,0 +1,1207 @@ +/** + * This library is used in the QL detective tutorials. + * + * Note: Data is usually stored in a separate database and the QL libraries only contain predicates, + * but for this tutorial both the data and the predicates are stored in the library. + */ +class Person extends string { + Person() { + this = "Ronil" or + this = "Dina" or + this = "Ravi" or + this = "Bruce" or + this = "Jo" or + this = "Aida" or + this = "Esme" or + this = "Charlie" or + this = "Fred" or + this = "Meera" or + this = "Maya" or + this = "Chad" or + this = "Tiana" or + this = "Laura" or + this = "George" or + this = "Will" or + this = "Mary" or + this = "Almira" or + this = "Susannah" or + this = "Rhoda" or + this = "Cynthia" or + this = "Eunice" or + this = "Olive" or + this = "Virginia" or + this = "Angeline" or + this = "Helen" or + this = "Cornelia" or + this = "Harriet" or + this = "Mahala" or + this = "Abby" or + this = "Margaret" or + this = "Deb" or + this = "Minerva" or + this = "Severus" or + this = "Lavina" or + this = "Adeline" or + this = "Cath" or + this = "Elisa" or + this = "Lucretia" or + this = "Anne" or + this = "Eleanor" or + this = "Joanna" or + this = "Adam" or + this = "Agnes" or + this = "Rosanna" or + this = "Clara" or + this = "Melissa" or + this = "Amy" or + this = "Isabel" or + this = "Jemima" or + this = "Cordelia" or + this = "Melinda" or + this = "Delila" or + this = "Jeremiah" or + this = "Elijah" or + this = "Hester" or + this = "Walter" or + this = "Oliver" or + this = "Hugh" or + this = "Aaron" or + this = "Reuben" or + this = "Eli" or + this = "Amos" or + this = "Augustus" or + this = "Theodore" or + this = "Ira" or + this = "Timothy" or + this = "Cyrus" or + this = "Horace" or + this = "Simon" or + this = "Asa" or + this = "Frank" or + this = "Nelson" or + this = "Leonard" or + this = "Harrison" or + this = "Anthony" or + this = "Louis" or + this = "Milton" or + this = "Noah" or + this = "Cornelius" or + this = "Abdul" or + this = "Warren" or + this = "Harvey" or + this = "Dennis" or + this = "Wesley" or + this = "Sylvester" or + this = "Gilbert" or + this = "Sullivan" or + this = "Edmund" or + this = "Wilson" or + this = "Perry" or + this = "Matthew" or + this = "Simba" or + this = "Nala" or + this = "Rafiki" or + this = "Shenzi" or + this = "Ernest" or + this = "Gertrude" or + this = "Oscar" or + this = "Lilian" or + this = "Raymond" or + this = "Elgar" or + this = "Elmer" or + this = "Herbert" or + this = "Maude" or + this = "Mae" or + this = "Otto" or + this = "Edwin" or + this = "Ophelia" or + this = "Parsley" or + this = "Sage" or + this = "Rosemary" or + this = "Thyme" or + this = "Garfunkel" or + this = "King Basil" or + this = "Stephen" + } + + /** Gets the hair color of the person. If the person is bald, there is no result. */ + string getHairColor() { + this = "Ronil" and result = "black" + or + this = "Dina" and result = "black" + or + this = "Ravi" and result = "black" + or + this = "Bruce" and result = "brown" + or + this = "Jo" and result = "red" + or + this = "Aida" and result = "blond" + or + this = "Esme" and result = "blond" + or + this = "Fred" and result = "gray" + or + this = "Meera" and result = "brown" + or + this = "Maya" and result = "brown" + or + this = "Chad" and result = "brown" + or + this = "Tiana" and result = "black" + or + this = "Laura" and result = "blond" + or + this = "George" and result = "blond" + or + this = "Will" and result = "blond" + or + this = "Mary" and result = "blond" + or + this = "Almira" and result = "black" + or + this = "Susannah" and result = "blond" + or + this = "Rhoda" and result = "blond" + or + this = "Cynthia" and result = "gray" + or + this = "Eunice" and result = "white" + or + this = "Olive" and result = "brown" + or + this = "Virginia" and result = "brown" + or + this = "Angeline" and result = "red" + or + this = "Helen" and result = "white" + or + this = "Cornelia" and result = "gray" + or + this = "Harriet" and result = "white" + or + this = "Mahala" and result = "black" + or + this = "Abby" and result = "red" + or + this = "Margaret" and result = "brown" + or + this = "Deb" and result = "brown" + or + this = "Minerva" and result = "brown" + or + this = "Severus" and result = "black" + or + this = "Lavina" and result = "brown" + or + this = "Adeline" and result = "brown" + or + this = "Cath" and result = "brown" + or + this = "Elisa" and result = "brown" + or + this = "Lucretia" and result = "gray" + or + this = "Anne" and result = "black" + or + this = "Eleanor" and result = "brown" + or + this = "Joanna" and result = "brown" + or + this = "Adam" and result = "black" + or + this = "Agnes" and result = "black" + or + this = "Rosanna" and result = "gray" + or + this = "Clara" and result = "blond" + or + this = "Melissa" and result = "brown" + or + this = "Amy" and result = "brown" + or + this = "Isabel" and result = "black" + or + this = "Jemima" and result = "red" + or + this = "Cordelia" and result = "red" + or + this = "Melinda" and result = "gray" + or + this = "Delila" and result = "white" + or + this = "Jeremiah" and result = "gray" + or + this = "Hester" and result = "black" + or + this = "Walter" and result = "black" + or + this = "Aaron" and result = "gray" + or + this = "Reuben" and result = "gray" + or + this = "Eli" and result = "gray" + or + this = "Amos" and result = "white" + or + this = "Augustus" and result = "white" + or + this = "Theodore" and result = "white" + or + this = "Timothy" and result = "brown" + or + this = "Cyrus" and result = "brown" + or + this = "Horace" and result = "brown" + or + this = "Simon" and result = "brown" + or + this = "Asa" and result = "brown" + or + this = "Frank" and result = "brown" + or + this = "Nelson" and result = "black" + or + this = "Leonard" and result = "black" + or + this = "Harrison" and result = "black" + or + this = "Anthony" and result = "black" + or + this = "Louis" and result = "black" + or + this = "Milton" and result = "blond" + or + this = "Noah" and result = "blond" + or + this = "Cornelius" and result = "red" + or + this = "Abdul" and result = "brown" + or + this = "Warren" and result = "red" + or + this = "Harvey" and result = "blond" + or + this = "Dennis" and result = "blond" + or + this = "Wesley" and result = "brown" + or + this = "Sylvester" and result = "brown" + or + this = "Gilbert" and result = "brown" + or + this = "Sullivan" and result = "brown" + or + this = "Edmund" and result = "brown" + or + this = "Wilson" and result = "blond" + or + this = "Perry" and result = "black" + or + this = "Simba" and result = "brown" + or + this = "Nala" and result = "brown" + or + this = "Rafiki" and result = "red" + or + this = "Shenzi" and result = "gray" + or + this = "Ernest" and result = "blond" + or + this = "Gertrude" and result = "brown" + or + this = "Oscar" and result = "blond" + or + this = "Lilian" and result = "brown" + or + this = "Raymond" and result = "brown" + or + this = "Elgar" and result = "brown" + or + this = "Elmer" and result = "brown" + or + this = "Herbert" and result = "brown" + or + this = "Maude" and result = "brown" + or + this = "Mae" and result = "brown" + or + this = "Otto" and result = "black" + or + this = "Edwin" and result = "black" + or + this = "Ophelia" and result = "brown" + or + this = "Parsley" and result = "brown" + or + this = "Sage" and result = "brown" + or + this = "Rosemary" and result = "brown" + or + this = "Thyme" and result = "brown" + or + this = "Garfunkel" and result = "brown" + or + this = "King Basil" and result = "brown" + or + this = "Stephen" and result = "black" + or + this = "Stephen" and result = "gray" + } + + /** Gets the age of the person (in years). If the person is deceased, there is no result. */ + int getAge() { + this = "Ronil" and result = 21 + or + this = "Dina" and result = 53 + or + this = "Ravi" and result = 16 + or + this = "Bruce" and result = 35 + or + this = "Jo" and result = 47 + or + this = "Aida" and result = 26 + or + this = "Esme" and result = 25 + or + this = "Charlie" and result = 31 + or + this = "Fred" and result = 68 + or + this = "Meera" and result = 62 + or + this = "Maya" and result = 29 + or + this = "Chad" and result = 49 + or + this = "Tiana" and result = 18 + or + this = "Laura" and result = 2 + or + this = "George" and result = 3 + or + this = "Will" and result = 41 + or + this = "Mary" and result = 51 + or + this = "Almira" and result = 1 + or + this = "Susannah" and result = 97 + or + this = "Rhoda" and result = 39 + or + this = "Cynthia" and result = 89 + or + this = "Eunice" and result = 83 + or + this = "Olive" and result = 25 + or + this = "Virginia" and result = 52 + or + this = "Angeline" and result = 22 + or + this = "Helen" and result = 79 + or + this = "Cornelia" and result = 59 + or + this = "Harriet" and result = 57 + or + this = "Mahala" and result = 61 + or + this = "Abby" and result = 24 + or + this = "Margaret" and result = 59 + or + this = "Deb" and result = 31 + or + this = "Minerva" and result = 72 + or + this = "Severus" and result = 61 + or + this = "Lavina" and result = 33 + or + this = "Adeline" and result = 17 + or + this = "Cath" and result = 22 + or + this = "Elisa" and result = 9 + or + this = "Lucretia" and result = 56 + or + this = "Anne" and result = 11 + or + this = "Eleanor" and result = 80 + or + this = "Joanna" and result = 43 + or + this = "Adam" and result = 37 + or + this = "Agnes" and result = 47 + or + this = "Rosanna" and result = 61 + or + this = "Clara" and result = 31 + or + this = "Melissa" and result = 37 + or + this = "Amy" and result = 12 + or + this = "Isabel" and result = 6 + or + this = "Jemima" and result = 16 + or + this = "Cordelia" and result = 21 + or + this = "Melinda" and result = 55 + or + this = "Delila" and result = 66 + or + this = "Jeremiah" and result = 54 + or + this = "Elijah" and result = 42 + or + this = "Hester" and result = 68 + or + this = "Walter" and result = 66 + or + this = "Oliver" and result = 33 + or + this = "Hugh" and result = 51 + or + this = "Aaron" and result = 49 + or + this = "Reuben" and result = 58 + or + this = "Eli" and result = 70 + or + this = "Amos" and result = 65 + or + this = "Augustus" and result = 56 + or + this = "Theodore" and result = 69 + or + this = "Ira" and result = 1 + or + this = "Timothy" and result = 54 + or + this = "Cyrus" and result = 78 + or + this = "Horace" and result = 34 + or + this = "Simon" and result = 23 + or + this = "Asa" and result = 28 + or + this = "Frank" and result = 59 + or + this = "Nelson" and result = 38 + or + this = "Leonard" and result = 58 + or + this = "Harrison" and result = 7 + or + this = "Anthony" and result = 2 + or + this = "Louis" and result = 34 + or + this = "Milton" and result = 36 + or + this = "Noah" and result = 48 + or + this = "Cornelius" and result = 41 + or + this = "Abdul" and result = 67 + or + this = "Warren" and result = 47 + or + this = "Harvey" and result = 31 + or + this = "Dennis" and result = 39 + or + this = "Wesley" and result = 13 + or + this = "Sylvester" and result = 19 + or + this = "Gilbert" and result = 16 + or + this = "Sullivan" and result = 17 + or + this = "Edmund" and result = 29 + or + this = "Wilson" and result = 27 + or + this = "Perry" and result = 31 + or + this = "Matthew" and result = 55 + or + this = "Simba" and result = 8 + or + this = "Nala" and result = 7 + or + this = "Rafiki" and result = 76 + or + this = "Shenzi" and result = 67 + } + + /** Gets the height of the person (in cm). If the person is deceased, there is no result. */ + float getHeight() { + this = "Ronil" and result = 183.0 + or + this = "Dina" and result = 155.1 + or + this = "Ravi" and result = 175.2 + or + this = "Bruce" and result = 191.3 + or + this = "Jo" and result = 163.4 + or + this = "Aida" and result = 182.6 + or + this = "Esme" and result = 176.9 + or + this = "Charlie" and result = 189.7 + or + this = "Fred" and result = 179.4 + or + this = "Meera" and result = 160.1 + or + this = "Maya" and result = 153.0 + or + this = "Chad" and result = 168.5 + or + this = "Tiana" and result = 149.7 + or + this = "Laura" and result = 87.5 + or + this = "George" and result = 96.4 + or + this = "Will" and result = 167.1 + or + this = "Mary" and result = 159.8 + or + this = "Almira" and result = 62.1 + or + this = "Susannah" and result = 145.8 + or + this = "Rhoda" and result = 180.1 + or + this = "Cynthia" and result = 161.8 + or + this = "Eunice" and result = 153.2 + or + this = "Olive" and result = 179.9 + or + this = "Virginia" and result = 165.1 + or + this = "Angeline" and result = 172.3 + or + this = "Helen" and result = 163.1 + or + this = "Cornelia" and result = 160.8 + or + this = "Harriet" and result = 163.2 + or + this = "Mahala" and result = 157.7 + or + this = "Abby" and result = 174.5 + or + this = "Margaret" and result = 165.6 + or + this = "Deb" and result = 171.6 + or + this = "Minerva" and result = 168.7 + or + this = "Severus" and result = 188.8 + or + this = "Lavina" and result = 155.1 + or + this = "Adeline" and result = 165.5 + or + this = "Cath" and result = 147.8 + or + this = "Elisa" and result = 129.4 + or + this = "Lucretia" and result = 153.6 + or + this = "Anne" and result = 140.4 + or + this = "Eleanor" and result = 151.1 + or + this = "Joanna" and result = 167.2 + or + this = "Adam" and result = 155.5 + or + this = "Agnes" and result = 156.8 + or + this = "Rosanna" and result = 162.4 + or + this = "Clara" and result = 158.6 + or + this = "Melissa" and result = 182.3 + or + this = "Amy" and result = 147.1 + or + this = "Isabel" and result = 121.4 + or + this = "Jemima" and result = 149.8 + or + this = "Cordelia" and result = 151.7 + or + this = "Melinda" and result = 154.4 + or + this = "Delila" and result = 163.4 + or + this = "Jeremiah" and result = 167.5 + or + this = "Elijah" and result = 184.5 + or + this = "Hester" and result = 152.7 + or + this = "Walter" and result = 159.6 + or + this = "Oliver" and result = 192.4 + or + this = "Hugh" and result = 173.1 + or + this = "Aaron" and result = 176.6 + or + this = "Reuben" and result = 169.9 + or + this = "Eli" and result = 180.4 + or + this = "Amos" and result = 167.4 + or + this = "Augustus" and result = 156.5 + or + this = "Theodore" and result = 176.6 + or + this = "Ira" and result = 54.1 + or + this = "Timothy" and result = 172.2 + or + this = "Cyrus" and result = 157.9 + or + this = "Horace" and result = 169.3 + or + this = "Simon" and result = 157.1 + or + this = "Asa" and result = 149.4 + or + this = "Frank" and result = 167.2 + or + this = "Nelson" and result = 173.0 + or + this = "Leonard" and result = 172.0 + or + this = "Harrison" and result = 126.0 + or + this = "Anthony" and result = 98.4 + or + this = "Louis" and result = 186.8 + or + this = "Milton" and result = 157.8 + or + this = "Noah" and result = 190.5 + or + this = "Cornelius" and result = 183.1 + or + this = "Abdul" and result = 182.0 + or + this = "Warren" and result = 175.0 + or + this = "Harvey" and result = 169.3 + or + this = "Dennis" and result = 160.4 + or + this = "Wesley" and result = 139.8 + or + this = "Sylvester" and result = 188.2 + or + this = "Gilbert" and result = 177.6 + or + this = "Sullivan" and result = 168.3 + or + this = "Edmund" and result = 159.2 + or + this = "Wilson" and result = 167.6 + or + this = "Perry" and result = 189.1 + or + this = "Matthew" and result = 167.2 + or + this = "Simba" and result = 140.1 + or + this = "Nala" and result = 138.0 + or + this = "Rafiki" and result = 139.3 + or + this = "Shenzi" and result = 171.1 + } + + /** Gets the location of the person's home ("north", "south", "east", or "west"). If the person is deceased, there is no result. */ + string getLocation() { + this = "Ronil" and result = "north" + or + this = "Dina" and result = "north" + or + this = "Ravi" and result = "north" + or + this = "Bruce" and result = "south" + or + this = "Jo" and result = "west" + or + this = "Aida" and result = "east" + or + this = "Esme" and result = "east" + or + this = "Charlie" and result = "south" + or + this = "Fred" and result = "west" + or + this = "Meera" and result = "south" + or + this = "Maya" and result = "south" + or + this = "Chad" and result = "south" + or + this = "Tiana" and result = "west" + or + this = "Laura" and result = "south" + or + this = "George" and result = "south" + or + this = "Will" and result = "south" + or + this = "Mary" and result = "south" + or + this = "Almira" and result = "south" + or + this = "Susannah" and result = "north" + or + this = "Rhoda" and result = "north" + or + this = "Cynthia" and result = "north" + or + this = "Eunice" and result = "north" + or + this = "Olive" and result = "west" + or + this = "Virginia" and result = "west" + or + this = "Angeline" and result = "west" + or + this = "Helen" and result = "west" + or + this = "Cornelia" and result = "east" + or + this = "Harriet" and result = "east" + or + this = "Mahala" and result = "east" + or + this = "Abby" and result = "east" + or + this = "Margaret" and result = "east" + or + this = "Deb" and result = "east" + or + this = "Minerva" and result = "south" + or + this = "Severus" and result = "north" + or + this = "Lavina" and result = "east" + or + this = "Adeline" and result = "west" + or + this = "Cath" and result = "east" + or + this = "Elisa" and result = "east" + or + this = "Lucretia" and result = "north" + or + this = "Anne" and result = "north" + or + this = "Eleanor" and result = "south" + or + this = "Joanna" and result = "south" + or + this = "Adam" and result = "east" + or + this = "Agnes" and result = "east" + or + this = "Rosanna" and result = "east" + or + this = "Clara" and result = "east" + or + this = "Melissa" and result = "west" + or + this = "Amy" and result = "west" + or + this = "Isabel" and result = "west" + or + this = "Jemima" and result = "west" + or + this = "Cordelia" and result = "west" + or + this = "Melinda" and result = "west" + or + this = "Delila" and result = "south" + or + this = "Jeremiah" and result = "north" + or + this = "Elijah" and result = "north" + or + this = "Hester" and result = "east" + or + this = "Walter" and result = "east" + or + this = "Oliver" and result = "east" + or + this = "Hugh" and result = "south" + or + this = "Aaron" and result = "south" + or + this = "Reuben" and result = "west" + or + this = "Eli" and result = "west" + or + this = "Amos" and result = "east" + or + this = "Augustus" and result = "south" + or + this = "Theodore" and result = "west" + or + this = "Ira" and result = "south" + or + this = "Timothy" and result = "north" + or + this = "Cyrus" and result = "north" + or + this = "Horace" and result = "east" + or + this = "Simon" and result = "east" + or + this = "Asa" and result = "east" + or + this = "Frank" and result = "west" + or + this = "Nelson" and result = "west" + or + this = "Leonard" and result = "west" + or + this = "Harrison" and result = "north" + or + this = "Anthony" and result = "north" + or + this = "Louis" and result = "north" + or + this = "Milton" and result = "south" + or + this = "Noah" and result = "south" + or + this = "Cornelius" and result = "east" + or + this = "Abdul" and result = "east" + or + this = "Warren" and result = "west" + or + this = "Harvey" and result = "west" + or + this = "Dennis" and result = "west" + or + this = "Wesley" and result = "west" + or + this = "Sylvester" and result = "south" + or + this = "Gilbert" and result = "east" + or + this = "Sullivan" and result = "east" + or + this = "Edmund" and result = "north" + or + this = "Wilson" and result = "north" + or + this = "Perry" and result = "west" + or + this = "Matthew" and result = "east" + or + this = "Simba" and result = "south" + or + this = "Nala" and result = "south" + or + this = "Rafiki" and result = "north" + or + this = "Shenzi" and result = "west" + } + + /** Holds if the person is deceased. */ + predicate isDeceased() { + this = "Ernest" or + this = "Gertrude" or + this = "Oscar" or + this = "Lilian" or + this = "Edwin" or + this = "Raymond" or + this = "Elgar" or + this = "Elmer" or + this = "Herbert" or + this = "Maude" or + this = "Mae" or + this = "Otto" or + this = "Ophelia" or + this = "Parsley" or + this = "Sage" or + this = "Rosemary" or + this = "Thyme" or + this = "Garfunkel" or + this = "King Basil" + } + + /** Gets a parent of the person (alive or deceased). */ + Person getAParent() { + this = "Stephen" and result = "Edmund" + or + this = "Edmund" and result = "Augustus" + or + this = "Augustus" and result = "Stephen" + or + this = "Abby" and result = "Cornelia" + or + this = "Abby" and result = "Amos" + or + this = "Abdul" and result = "Susannah" + or + this = "Adam" and result = "Amos" + or + this = "Adeline" and result = "Melinda" + or + this = "Adeline" and result = "Frank" + or + this = "Agnes" and result = "Abdul" + or + this = "Aida" and result = "Agnes" + or + this = "Almira" and result = "Sylvester" + or + this = "Amos" and result = "Eunice" + or + this = "Amy" and result = "Noah" + or + this = "Amy" and result = "Chad" + or + this = "Angeline" and result = "Reuben" + or + this = "Angeline" and result = "Lucretia" + or + this = "Anne" and result = "Rhoda" + or + this = "Anne" and result = "Louis" + or + this = "Anthony" and result = "Lavina" + or + this = "Anthony" and result = "Asa" + or + this = "Asa" and result = "Cornelia" + or + this = "Cath" and result = "Harriet" + or + this = "Charlie" and result = "Matthew" + or + this = "Clara" and result = "Ernest" + or + this = "Cornelia" and result = "Cynthia" + or + this = "Cornelius" and result = "Eli" + or + this = "Deb" and result = "Margaret" + or + this = "Dennis" and result = "Fred" + or + this = "Eli" and result = "Susannah" + or + this = "Elijah" and result = "Delila" + or + this = "Elisa" and result = "Deb" + or + this = "Elisa" and result = "Horace" + or + this = "Esme" and result = "Margaret" + or + this = "Frank" and result = "Eleanor" + or + this = "Frank" and result = "Cyrus" + or + this = "George" and result = "Maya" + or + this = "George" and result = "Wilson" + or + this = "Gilbert" and result = "Cornelius" + or + this = "Harriet" and result = "Cynthia" + or + this = "Harrison" and result = "Louis" + or + this = "Harvey" and result = "Fred" + or + this = "Helen" and result = "Susannah" + or + this = "Hester" and result = "Edwin" + or + this = "Hugh" and result = "Cyrus" + or + this = "Hugh" and result = "Helen" + or + this = "Ira" and result = "Maya" + or + this = "Ira" and result = "Wilson" + or + this = "Isabel" and result = "Perry" + or + this = "Isabel" and result = "Harvey" + or + this = "Jemima" and result = "Melinda" + or + this = "Jemima" and result = "Frank" + or + this = "Ernest" and result = "Lilian" + or + this = "Ernest" and result = "Oscar" + or + this = "Gertrude" and result = "Ophelia" + or + this = "Gertrude" and result = "Raymond" + or + this = "Lilian" and result = "Elgar" + or + this = "Lilian" and result = "Mae" + or + this = "Raymond" and result = "Elgar" + or + this = "Raymond" and result = "Mae" + or + this = "Elmer" and result = "Ophelia" + or + this = "Elmer" and result = "Raymond" + or + this = "Herbert" and result = "Ophelia" + or + this = "Herbert" and result = "Raymond" + or + this = "Maude" and result = "Ophelia" + or + this = "Maude" and result = "Raymond" + or + this = "Otto" and result = "Elgar" + or + this = "Otto" and result = "Mae" + or + this = "Edwin" and result = "Otto" + or + this = "Parsley" and result = "Simon" + or + this = "Parsley" and result = "Garfunkel" + or + this = "Sage" and result = "Simon" + or + this = "Sage" and result = "Garfunkel" + or + this = "Rosemary" and result = "Simon" + or + this = "Rosemary" and result = "Garfunkel" + or + this = "Thyme" and result = "Simon" + or + this = "Thyme" and result = "Garfunkel" + or + this = "King Basil" and result = "Ophelia" + or + this = "King Basil" and result = "Raymond" + or + this = "Jo" and result = "Theodore" + or + this = "Joanna" and result = "Shenzi" + or + this = "Laura" and result = "Maya" + or + this = "Laura" and result = "Wilson" + or + this = "Lavina" and result = "Mahala" + or + this = "Lavina" and result = "Walter" + or + this = "Leonard" and result = "Cyrus" + or + this = "Leonard" and result = "Helen" + or + this = "Lucretia" and result = "Eleanor" + or + this = "Lucretia" and result = "Cyrus" + or + this = "Mahala" and result = "Eunice" + or + this = "Margaret" and result = "Cynthia" + or + this = "Matthew" and result = "Cyrus" + or + this = "Matthew" and result = "Helen" + or + this = "Maya" and result = "Meera" + or + this = "Melinda" and result = "Rafiki" + or + this = "Melissa" and result = "Mahala" + or + this = "Melissa" and result = "Walter" + or + this = "Nala" and result = "Bruce" + or + this = "Nelson" and result = "Mahala" + or + this = "Nelson" and result = "Walter" + or + this = "Noah" and result = "Eli" + or + this = "Olive" and result = "Reuben" + or + this = "Olive" and result = "Lucretia" + or + this = "Oliver" and result = "Matthew" + or + this = "Perry" and result = "Leonard" + or + this = "Ravi" and result = "Dina" + or + this = "Simba" and result = "Will" + or + this = "Simon" and result = "Margaret" + or + this = "Sullivan" and result = "Cornelius" + or + this = "Sylvester" and result = "Timothy" + or + this = "Theodore" and result = "Susannah" + or + this = "Tiana" and result = "Jo" + or + this = "Virginia" and result = "Helen" + or + this = "Warren" and result = "Shenzi" + or + this = "Wesley" and result = "Warren" + or + this = "Wesley" and result = "Jo" + or + this = "Will" and result = "Eli" + } + + /** Holds if the person is allowed in the region. Initially, all villagers are allowed in every region. */ + predicate isAllowedIn(string region) { + region = "north" or + region = "south" or + region = "east" or + region = "west" + } +} + +/** Returns a parent of the person. */ +Person parentOf(Person p) { result = p.getAParent() } diff --git a/ruby/ql/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/old.dbscheme b/ruby/ql/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/old.dbscheme new file mode 100644 index 00000000000..09a494ce67d --- /dev/null +++ b/ruby/ql/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/old.dbscheme @@ -0,0 +1,1333 @@ +// CodeQL database schema for Ruby +// Automatically generated from the tree-sitter grammar; do not edit + +@location = @location_default + +locations_default( + unique int id: @location_default, + int file: @file ref, + int start_line: int ref, + int start_column: int ref, + int end_line: int ref, + int end_column: int ref +); + +@sourceline = @file + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref +); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +sourceLocationPrefix( + string prefix: string ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +case @diagnostic.severity of + 10 = @diagnostic_debug +| 20 = @diagnostic_info +| 30 = @diagnostic_warning +| 40 = @diagnostic_error +; + + +@ruby_underscore_arg = @ruby_assignment | @ruby_binary | @ruby_conditional | @ruby_operator_assignment | @ruby_range | @ruby_unary | @ruby_underscore_primary + +@ruby_underscore_lhs = @ruby_call | @ruby_element_reference | @ruby_scope_resolution | @ruby_token_false | @ruby_token_nil | @ruby_token_true | @ruby_underscore_variable + +@ruby_underscore_method_name = @ruby_delimited_symbol | @ruby_setter | @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_operator | @ruby_token_simple_symbol + +@ruby_underscore_primary = @ruby_array | @ruby_begin | @ruby_break | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_delimited_symbol | @ruby_for | @ruby_hash | @ruby_if | @ruby_lambda | @ruby_method | @ruby_module | @ruby_next | @ruby_parenthesized_statements | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_retry | @ruby_return | @ruby_singleton_class | @ruby_singleton_method | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_symbol_array | @ruby_token_character | @ruby_token_complex | @ruby_token_float | @ruby_token_heredoc_beginning | @ruby_token_integer | @ruby_token_simple_symbol | @ruby_unary | @ruby_underscore_lhs | @ruby_unless | @ruby_until | @ruby_while | @ruby_yield + +@ruby_underscore_statement = @ruby_alias | @ruby_assignment | @ruby_begin_block | @ruby_binary | @ruby_break | @ruby_call | @ruby_end_block | @ruby_if_modifier | @ruby_next | @ruby_operator_assignment | @ruby_rescue_modifier | @ruby_return | @ruby_unary | @ruby_undef | @ruby_underscore_arg | @ruby_unless_modifier | @ruby_until_modifier | @ruby_while_modifier | @ruby_yield + +@ruby_underscore_variable = @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_self | @ruby_token_super + +ruby_alias_def( + unique int id: @ruby_alias, + int alias: @ruby_underscore_method_name ref, + int name: @ruby_underscore_method_name ref, + int loc: @location ref +); + +@ruby_argument_list_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_argument_list, index] +ruby_argument_list_child( + int ruby_argument_list: @ruby_argument_list ref, + int index: int ref, + unique int child: @ruby_argument_list_child_type ref +); + +ruby_argument_list_def( + unique int id: @ruby_argument_list, + int loc: @location ref +); + +@ruby_array_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_array, index] +ruby_array_child( + int ruby_array: @ruby_array ref, + int index: int ref, + unique int child: @ruby_array_child_type ref +); + +ruby_array_def( + unique int id: @ruby_array, + int loc: @location ref +); + +@ruby_assignment_left_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +@ruby_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_right_assignment_list | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +ruby_assignment_def( + unique int id: @ruby_assignment, + int left: @ruby_assignment_left_type ref, + int right: @ruby_assignment_right_type ref, + int loc: @location ref +); + +@ruby_bare_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_string, index] +ruby_bare_string_child( + int ruby_bare_string: @ruby_bare_string ref, + int index: int ref, + unique int child: @ruby_bare_string_child_type ref +); + +ruby_bare_string_def( + unique int id: @ruby_bare_string, + int loc: @location ref +); + +@ruby_bare_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_symbol, index] +ruby_bare_symbol_child( + int ruby_bare_symbol: @ruby_bare_symbol ref, + int index: int ref, + unique int child: @ruby_bare_symbol_child_type ref +); + +ruby_bare_symbol_def( + unique int id: @ruby_bare_symbol, + int loc: @location ref +); + +@ruby_begin_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin, index] +ruby_begin_child( + int ruby_begin: @ruby_begin ref, + int index: int ref, + unique int child: @ruby_begin_child_type ref +); + +ruby_begin_def( + unique int id: @ruby_begin, + int loc: @location ref +); + +@ruby_begin_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin_block, index] +ruby_begin_block_child( + int ruby_begin_block: @ruby_begin_block ref, + int index: int ref, + unique int child: @ruby_begin_block_child_type ref +); + +ruby_begin_block_def( + unique int id: @ruby_begin_block, + int loc: @location ref +); + +@ruby_binary_left_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +case @ruby_binary.operator of + 0 = @ruby_binary_bangequal +| 1 = @ruby_binary_bangtilde +| 2 = @ruby_binary_percent +| 3 = @ruby_binary_ampersand +| 4 = @ruby_binary_ampersandampersand +| 5 = @ruby_binary_star +| 6 = @ruby_binary_starstar +| 7 = @ruby_binary_plus +| 8 = @ruby_binary_minus +| 9 = @ruby_binary_slash +| 10 = @ruby_binary_langle +| 11 = @ruby_binary_langlelangle +| 12 = @ruby_binary_langleequal +| 13 = @ruby_binary_langleequalrangle +| 14 = @ruby_binary_equalequal +| 15 = @ruby_binary_equalequalequal +| 16 = @ruby_binary_equaltilde +| 17 = @ruby_binary_rangle +| 18 = @ruby_binary_rangleequal +| 19 = @ruby_binary_ranglerangle +| 20 = @ruby_binary_caret +| 21 = @ruby_binary_and +| 22 = @ruby_binary_or +| 23 = @ruby_binary_pipe +| 24 = @ruby_binary_pipepipe +; + + +@ruby_binary_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_binary_def( + unique int id: @ruby_binary, + int left: @ruby_binary_left_type ref, + int operator: int ref, + int right: @ruby_binary_right_type ref, + int loc: @location ref +); + +ruby_block_parameters( + unique int ruby_block: @ruby_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_block, index] +ruby_block_child( + int ruby_block: @ruby_block ref, + int index: int ref, + unique int child: @ruby_block_child_type ref +); + +ruby_block_def( + unique int id: @ruby_block, + int loc: @location ref +); + +ruby_block_argument_def( + unique int id: @ruby_block_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_block_parameter_def( + unique int id: @ruby_block_parameter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_block_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_block_parameters, index] +ruby_block_parameters_child( + int ruby_block_parameters: @ruby_block_parameters ref, + int index: int ref, + unique int child: @ruby_block_parameters_child_type ref +); + +ruby_block_parameters_def( + unique int id: @ruby_block_parameters, + int loc: @location ref +); + +ruby_break_child( + unique int ruby_break: @ruby_break ref, + unique int child: @ruby_argument_list ref +); + +ruby_break_def( + unique int id: @ruby_break, + int loc: @location ref +); + +ruby_call_arguments( + unique int ruby_call: @ruby_call ref, + unique int arguments: @ruby_argument_list ref +); + +@ruby_call_block_type = @ruby_block | @ruby_do_block + +ruby_call_block( + unique int ruby_call: @ruby_call ref, + unique int block: @ruby_call_block_type ref +); + +@ruby_call_method_type = @ruby_argument_list | @ruby_scope_resolution | @ruby_token_operator | @ruby_underscore_variable + +@ruby_call_receiver_type = @ruby_call | @ruby_underscore_primary + +ruby_call_receiver( + unique int ruby_call: @ruby_call ref, + unique int receiver: @ruby_call_receiver_type ref +); + +ruby_call_def( + unique int id: @ruby_call, + int method: @ruby_call_method_type ref, + int loc: @location ref +); + +ruby_case_value( + unique int ruby_case__: @ruby_case__ ref, + unique int value: @ruby_underscore_statement ref +); + +@ruby_case_child_type = @ruby_else | @ruby_when + +#keyset[ruby_case__, index] +ruby_case_child( + int ruby_case__: @ruby_case__ ref, + int index: int ref, + unique int child: @ruby_case_child_type ref +); + +ruby_case_def( + unique int id: @ruby_case__, + int loc: @location ref +); + +#keyset[ruby_chained_string, index] +ruby_chained_string_child( + int ruby_chained_string: @ruby_chained_string ref, + int index: int ref, + unique int child: @ruby_string__ ref +); + +ruby_chained_string_def( + unique int id: @ruby_chained_string, + int loc: @location ref +); + +@ruby_class_name_type = @ruby_scope_resolution | @ruby_token_constant + +ruby_class_superclass( + unique int ruby_class: @ruby_class ref, + unique int superclass: @ruby_superclass ref +); + +@ruby_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_class, index] +ruby_class_child( + int ruby_class: @ruby_class ref, + int index: int ref, + unique int child: @ruby_class_child_type ref +); + +ruby_class_def( + unique int id: @ruby_class, + int name: @ruby_class_name_type ref, + int loc: @location ref +); + +ruby_conditional_def( + unique int id: @ruby_conditional, + int alternative: @ruby_underscore_arg ref, + int condition: @ruby_underscore_arg ref, + int consequence: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_delimited_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_delimited_symbol, index] +ruby_delimited_symbol_child( + int ruby_delimited_symbol: @ruby_delimited_symbol ref, + int index: int ref, + unique int child: @ruby_delimited_symbol_child_type ref +); + +ruby_delimited_symbol_def( + unique int id: @ruby_delimited_symbol, + int loc: @location ref +); + +@ruby_destructured_left_assignment_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_destructured_left_assignment, index] +ruby_destructured_left_assignment_child( + int ruby_destructured_left_assignment: @ruby_destructured_left_assignment ref, + int index: int ref, + unique int child: @ruby_destructured_left_assignment_child_type ref +); + +ruby_destructured_left_assignment_def( + unique int id: @ruby_destructured_left_assignment, + int loc: @location ref +); + +@ruby_destructured_parameter_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_destructured_parameter, index] +ruby_destructured_parameter_child( + int ruby_destructured_parameter: @ruby_destructured_parameter ref, + int index: int ref, + unique int child: @ruby_destructured_parameter_child_type ref +); + +ruby_destructured_parameter_def( + unique int id: @ruby_destructured_parameter, + int loc: @location ref +); + +@ruby_do_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do, index] +ruby_do_child( + int ruby_do: @ruby_do ref, + int index: int ref, + unique int child: @ruby_do_child_type ref +); + +ruby_do_def( + unique int id: @ruby_do, + int loc: @location ref +); + +ruby_do_block_parameters( + unique int ruby_do_block: @ruby_do_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_do_block_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do_block, index] +ruby_do_block_child( + int ruby_do_block: @ruby_do_block ref, + int index: int ref, + unique int child: @ruby_do_block_child_type ref +); + +ruby_do_block_def( + unique int id: @ruby_do_block, + int loc: @location ref +); + +@ruby_element_reference_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_element_reference, index] +ruby_element_reference_child( + int ruby_element_reference: @ruby_element_reference ref, + int index: int ref, + unique int child: @ruby_element_reference_child_type ref +); + +ruby_element_reference_def( + unique int id: @ruby_element_reference, + int object: @ruby_underscore_primary ref, + int loc: @location ref +); + +@ruby_else_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_else, index] +ruby_else_child( + int ruby_else: @ruby_else ref, + int index: int ref, + unique int child: @ruby_else_child_type ref +); + +ruby_else_def( + unique int id: @ruby_else, + int loc: @location ref +); + +@ruby_elsif_alternative_type = @ruby_else | @ruby_elsif + +ruby_elsif_alternative( + unique int ruby_elsif: @ruby_elsif ref, + unique int alternative: @ruby_elsif_alternative_type ref +); + +ruby_elsif_consequence( + unique int ruby_elsif: @ruby_elsif ref, + unique int consequence: @ruby_then ref +); + +ruby_elsif_def( + unique int id: @ruby_elsif, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_end_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_end_block, index] +ruby_end_block_child( + int ruby_end_block: @ruby_end_block ref, + int index: int ref, + unique int child: @ruby_end_block_child_type ref +); + +ruby_end_block_def( + unique int id: @ruby_end_block, + int loc: @location ref +); + +@ruby_ensure_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_ensure, index] +ruby_ensure_child( + int ruby_ensure: @ruby_ensure ref, + int index: int ref, + unique int child: @ruby_ensure_child_type ref +); + +ruby_ensure_def( + unique int id: @ruby_ensure, + int loc: @location ref +); + +ruby_exception_variable_def( + unique int id: @ruby_exception_variable, + int child: @ruby_underscore_lhs ref, + int loc: @location ref +); + +@ruby_exceptions_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_exceptions, index] +ruby_exceptions_child( + int ruby_exceptions: @ruby_exceptions ref, + int index: int ref, + unique int child: @ruby_exceptions_child_type ref +); + +ruby_exceptions_def( + unique int id: @ruby_exceptions, + int loc: @location ref +); + +@ruby_for_pattern_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +ruby_for_def( + unique int id: @ruby_for, + int body: @ruby_do ref, + int pattern: @ruby_for_pattern_type ref, + int value: @ruby_in ref, + int loc: @location ref +); + +@ruby_hash_child_type = @ruby_hash_splat_argument | @ruby_pair + +#keyset[ruby_hash, index] +ruby_hash_child( + int ruby_hash: @ruby_hash ref, + int index: int ref, + unique int child: @ruby_hash_child_type ref +); + +ruby_hash_def( + unique int id: @ruby_hash, + int loc: @location ref +); + +ruby_hash_splat_argument_def( + unique int id: @ruby_hash_splat_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_hash_splat_parameter_name( + unique int ruby_hash_splat_parameter: @ruby_hash_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_hash_splat_parameter_def( + unique int id: @ruby_hash_splat_parameter, + int loc: @location ref +); + +@ruby_heredoc_body_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_heredoc_content | @ruby_token_heredoc_end + +#keyset[ruby_heredoc_body, index] +ruby_heredoc_body_child( + int ruby_heredoc_body: @ruby_heredoc_body ref, + int index: int ref, + unique int child: @ruby_heredoc_body_child_type ref +); + +ruby_heredoc_body_def( + unique int id: @ruby_heredoc_body, + int loc: @location ref +); + +@ruby_if_alternative_type = @ruby_else | @ruby_elsif + +ruby_if_alternative( + unique int ruby_if: @ruby_if ref, + unique int alternative: @ruby_if_alternative_type ref +); + +ruby_if_consequence( + unique int ruby_if: @ruby_if ref, + unique int consequence: @ruby_then ref +); + +ruby_if_def( + unique int id: @ruby_if, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_if_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_if_modifier_def( + unique int id: @ruby_if_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_if_modifier_condition_type ref, + int loc: @location ref +); + +ruby_in_def( + unique int id: @ruby_in, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_interpolation_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_interpolation, index] +ruby_interpolation_child( + int ruby_interpolation: @ruby_interpolation ref, + int index: int ref, + unique int child: @ruby_interpolation_child_type ref +); + +ruby_interpolation_def( + unique int id: @ruby_interpolation, + int loc: @location ref +); + +ruby_keyword_parameter_value( + unique int ruby_keyword_parameter: @ruby_keyword_parameter ref, + unique int value: @ruby_underscore_arg ref +); + +ruby_keyword_parameter_def( + unique int id: @ruby_keyword_parameter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_lambda_body_type = @ruby_block | @ruby_do_block + +ruby_lambda_parameters( + unique int ruby_lambda: @ruby_lambda ref, + unique int parameters: @ruby_lambda_parameters ref +); + +ruby_lambda_def( + unique int id: @ruby_lambda, + int body: @ruby_lambda_body_type ref, + int loc: @location ref +); + +@ruby_lambda_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_lambda_parameters, index] +ruby_lambda_parameters_child( + int ruby_lambda_parameters: @ruby_lambda_parameters ref, + int index: int ref, + unique int child: @ruby_lambda_parameters_child_type ref +); + +ruby_lambda_parameters_def( + unique int id: @ruby_lambda_parameters, + int loc: @location ref +); + +@ruby_left_assignment_list_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_left_assignment_list, index] +ruby_left_assignment_list_child( + int ruby_left_assignment_list: @ruby_left_assignment_list ref, + int index: int ref, + unique int child: @ruby_left_assignment_list_child_type ref +); + +ruby_left_assignment_list_def( + unique int id: @ruby_left_assignment_list, + int loc: @location ref +); + +ruby_method_parameters( + unique int ruby_method: @ruby_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_method, index] +ruby_method_child( + int ruby_method: @ruby_method ref, + int index: int ref, + unique int child: @ruby_method_child_type ref +); + +ruby_method_def( + unique int id: @ruby_method, + int name: @ruby_underscore_method_name ref, + int loc: @location ref +); + +@ruby_method_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_method_parameters, index] +ruby_method_parameters_child( + int ruby_method_parameters: @ruby_method_parameters ref, + int index: int ref, + unique int child: @ruby_method_parameters_child_type ref +); + +ruby_method_parameters_def( + unique int id: @ruby_method_parameters, + int loc: @location ref +); + +@ruby_module_name_type = @ruby_scope_resolution | @ruby_token_constant + +@ruby_module_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_module, index] +ruby_module_child( + int ruby_module: @ruby_module ref, + int index: int ref, + unique int child: @ruby_module_child_type ref +); + +ruby_module_def( + unique int id: @ruby_module, + int name: @ruby_module_name_type ref, + int loc: @location ref +); + +ruby_next_child( + unique int ruby_next: @ruby_next ref, + unique int child: @ruby_argument_list ref +); + +ruby_next_def( + unique int id: @ruby_next, + int loc: @location ref +); + +case @ruby_operator_assignment.operator of + 0 = @ruby_operator_assignment_percentequal +| 1 = @ruby_operator_assignment_ampersandampersandequal +| 2 = @ruby_operator_assignment_ampersandequal +| 3 = @ruby_operator_assignment_starstarequal +| 4 = @ruby_operator_assignment_starequal +| 5 = @ruby_operator_assignment_plusequal +| 6 = @ruby_operator_assignment_minusequal +| 7 = @ruby_operator_assignment_slashequal +| 8 = @ruby_operator_assignment_langlelangleequal +| 9 = @ruby_operator_assignment_ranglerangleequal +| 10 = @ruby_operator_assignment_caretequal +| 11 = @ruby_operator_assignment_pipeequal +| 12 = @ruby_operator_assignment_pipepipeequal +; + + +@ruby_operator_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_operator_assignment_def( + unique int id: @ruby_operator_assignment, + int left: @ruby_underscore_lhs ref, + int operator: int ref, + int right: @ruby_operator_assignment_right_type ref, + int loc: @location ref +); + +ruby_optional_parameter_def( + unique int id: @ruby_optional_parameter, + int name: @ruby_token_identifier ref, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_pair_key_type = @ruby_string__ | @ruby_token_hash_key_symbol | @ruby_underscore_arg + +ruby_pair_def( + unique int id: @ruby_pair, + int key__: @ruby_pair_key_type ref, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_parenthesized_statements_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_parenthesized_statements, index] +ruby_parenthesized_statements_child( + int ruby_parenthesized_statements: @ruby_parenthesized_statements ref, + int index: int ref, + unique int child: @ruby_parenthesized_statements_child_type ref +); + +ruby_parenthesized_statements_def( + unique int id: @ruby_parenthesized_statements, + int loc: @location ref +); + +@ruby_pattern_child_type = @ruby_splat_argument | @ruby_underscore_arg + +ruby_pattern_def( + unique int id: @ruby_pattern, + int child: @ruby_pattern_child_type ref, + int loc: @location ref +); + +@ruby_program_child_type = @ruby_token_empty_statement | @ruby_token_uninterpreted | @ruby_underscore_statement + +#keyset[ruby_program, index] +ruby_program_child( + int ruby_program: @ruby_program ref, + int index: int ref, + unique int child: @ruby_program_child_type ref +); + +ruby_program_def( + unique int id: @ruby_program, + int loc: @location ref +); + +ruby_range_begin( + unique int ruby_range: @ruby_range ref, + unique int begin: @ruby_underscore_arg ref +); + +ruby_range_end( + unique int ruby_range: @ruby_range ref, + unique int end: @ruby_underscore_arg ref +); + +case @ruby_range.operator of + 0 = @ruby_range_dotdot +| 1 = @ruby_range_dotdotdot +; + + +ruby_range_def( + unique int id: @ruby_range, + int operator: int ref, + int loc: @location ref +); + +@ruby_rational_child_type = @ruby_token_float | @ruby_token_integer + +ruby_rational_def( + unique int id: @ruby_rational, + int child: @ruby_rational_child_type ref, + int loc: @location ref +); + +ruby_redo_child( + unique int ruby_redo: @ruby_redo ref, + unique int child: @ruby_argument_list ref +); + +ruby_redo_def( + unique int id: @ruby_redo, + int loc: @location ref +); + +@ruby_regex_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_regex, index] +ruby_regex_child( + int ruby_regex: @ruby_regex ref, + int index: int ref, + unique int child: @ruby_regex_child_type ref +); + +ruby_regex_def( + unique int id: @ruby_regex, + int loc: @location ref +); + +ruby_rescue_body( + unique int ruby_rescue: @ruby_rescue ref, + unique int body: @ruby_then ref +); + +ruby_rescue_exceptions( + unique int ruby_rescue: @ruby_rescue ref, + unique int exceptions: @ruby_exceptions ref +); + +ruby_rescue_variable( + unique int ruby_rescue: @ruby_rescue ref, + unique int variable: @ruby_exception_variable ref +); + +ruby_rescue_def( + unique int id: @ruby_rescue, + int loc: @location ref +); + +@ruby_rescue_modifier_handler_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_rescue_modifier_def( + unique int id: @ruby_rescue_modifier, + int body: @ruby_underscore_statement ref, + int handler: @ruby_rescue_modifier_handler_type ref, + int loc: @location ref +); + +ruby_rest_assignment_child( + unique int ruby_rest_assignment: @ruby_rest_assignment ref, + unique int child: @ruby_underscore_lhs ref +); + +ruby_rest_assignment_def( + unique int id: @ruby_rest_assignment, + int loc: @location ref +); + +ruby_retry_child( + unique int ruby_retry: @ruby_retry ref, + unique int child: @ruby_argument_list ref +); + +ruby_retry_def( + unique int id: @ruby_retry, + int loc: @location ref +); + +ruby_return_child( + unique int ruby_return: @ruby_return ref, + unique int child: @ruby_argument_list ref +); + +ruby_return_def( + unique int id: @ruby_return, + int loc: @location ref +); + +@ruby_right_assignment_list_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_right_assignment_list, index] +ruby_right_assignment_list_child( + int ruby_right_assignment_list: @ruby_right_assignment_list ref, + int index: int ref, + unique int child: @ruby_right_assignment_list_child_type ref +); + +ruby_right_assignment_list_def( + unique int id: @ruby_right_assignment_list, + int loc: @location ref +); + +@ruby_scope_resolution_name_type = @ruby_token_constant | @ruby_token_identifier + +ruby_scope_resolution_scope( + unique int ruby_scope_resolution: @ruby_scope_resolution ref, + unique int scope: @ruby_underscore_primary ref +); + +ruby_scope_resolution_def( + unique int id: @ruby_scope_resolution, + int name: @ruby_scope_resolution_name_type ref, + int loc: @location ref +); + +ruby_setter_def( + unique int id: @ruby_setter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_singleton_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_class, index] +ruby_singleton_class_child( + int ruby_singleton_class: @ruby_singleton_class ref, + int index: int ref, + unique int child: @ruby_singleton_class_child_type ref +); + +ruby_singleton_class_def( + unique int id: @ruby_singleton_class, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_singleton_method_object_type = @ruby_underscore_arg | @ruby_underscore_variable + +ruby_singleton_method_parameters( + unique int ruby_singleton_method: @ruby_singleton_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_singleton_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_method, index] +ruby_singleton_method_child( + int ruby_singleton_method: @ruby_singleton_method ref, + int index: int ref, + unique int child: @ruby_singleton_method_child_type ref +); + +ruby_singleton_method_def( + unique int id: @ruby_singleton_method, + int name: @ruby_underscore_method_name ref, + int object: @ruby_singleton_method_object_type ref, + int loc: @location ref +); + +ruby_splat_argument_def( + unique int id: @ruby_splat_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_splat_parameter_name( + unique int ruby_splat_parameter: @ruby_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_splat_parameter_def( + unique int id: @ruby_splat_parameter, + int loc: @location ref +); + +@ruby_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_string__, index] +ruby_string_child( + int ruby_string__: @ruby_string__ ref, + int index: int ref, + unique int child: @ruby_string_child_type ref +); + +ruby_string_def( + unique int id: @ruby_string__, + int loc: @location ref +); + +#keyset[ruby_string_array, index] +ruby_string_array_child( + int ruby_string_array: @ruby_string_array ref, + int index: int ref, + unique int child: @ruby_bare_string ref +); + +ruby_string_array_def( + unique int id: @ruby_string_array, + int loc: @location ref +); + +@ruby_subshell_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_subshell, index] +ruby_subshell_child( + int ruby_subshell: @ruby_subshell ref, + int index: int ref, + unique int child: @ruby_subshell_child_type ref +); + +ruby_subshell_def( + unique int id: @ruby_subshell, + int loc: @location ref +); + +@ruby_superclass_child_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_superclass_def( + unique int id: @ruby_superclass, + int child: @ruby_superclass_child_type ref, + int loc: @location ref +); + +#keyset[ruby_symbol_array, index] +ruby_symbol_array_child( + int ruby_symbol_array: @ruby_symbol_array ref, + int index: int ref, + unique int child: @ruby_bare_symbol ref +); + +ruby_symbol_array_def( + unique int id: @ruby_symbol_array, + int loc: @location ref +); + +@ruby_then_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_then, index] +ruby_then_child( + int ruby_then: @ruby_then ref, + int index: int ref, + unique int child: @ruby_then_child_type ref +); + +ruby_then_def( + unique int id: @ruby_then, + int loc: @location ref +); + +@ruby_unary_operand_type = @ruby_break | @ruby_call | @ruby_next | @ruby_parenthesized_statements | @ruby_return | @ruby_token_float | @ruby_token_integer | @ruby_underscore_arg | @ruby_yield + +case @ruby_unary.operator of + 0 = @ruby_unary_bang +| 1 = @ruby_unary_plus +| 2 = @ruby_unary_minus +| 3 = @ruby_unary_definedquestion +| 4 = @ruby_unary_not +| 5 = @ruby_unary_tilde +; + + +ruby_unary_def( + unique int id: @ruby_unary, + int operand: @ruby_unary_operand_type ref, + int operator: int ref, + int loc: @location ref +); + +#keyset[ruby_undef, index] +ruby_undef_child( + int ruby_undef: @ruby_undef ref, + int index: int ref, + unique int child: @ruby_underscore_method_name ref +); + +ruby_undef_def( + unique int id: @ruby_undef, + int loc: @location ref +); + +@ruby_unless_alternative_type = @ruby_else | @ruby_elsif + +ruby_unless_alternative( + unique int ruby_unless: @ruby_unless ref, + unique int alternative: @ruby_unless_alternative_type ref +); + +ruby_unless_consequence( + unique int ruby_unless: @ruby_unless ref, + unique int consequence: @ruby_then ref +); + +ruby_unless_def( + unique int id: @ruby_unless, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_unless_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_unless_modifier_def( + unique int id: @ruby_unless_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_unless_modifier_condition_type ref, + int loc: @location ref +); + +ruby_until_def( + unique int id: @ruby_until, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_until_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_until_modifier_def( + unique int id: @ruby_until_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_until_modifier_condition_type ref, + int loc: @location ref +); + +ruby_when_body( + unique int ruby_when: @ruby_when ref, + unique int body: @ruby_then ref +); + +#keyset[ruby_when, index] +ruby_when_pattern( + int ruby_when: @ruby_when ref, + int index: int ref, + unique int pattern: @ruby_pattern ref +); + +ruby_when_def( + unique int id: @ruby_when, + int loc: @location ref +); + +ruby_while_def( + unique int id: @ruby_while, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_while_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_while_modifier_def( + unique int id: @ruby_while_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_while_modifier_condition_type ref, + int loc: @location ref +); + +ruby_yield_child( + unique int ruby_yield: @ruby_yield ref, + unique int child: @ruby_argument_list ref +); + +ruby_yield_def( + unique int id: @ruby_yield, + int loc: @location ref +); + +ruby_tokeninfo( + unique int id: @ruby_token, + int kind: int ref, + int file: @file ref, + int idx: int ref, + string value: string ref, + int loc: @location ref +); + +case @ruby_token.kind of + 0 = @ruby_reserved_word +| 1 = @ruby_token_character +| 2 = @ruby_token_class_variable +| 3 = @ruby_token_comment +| 4 = @ruby_token_complex +| 5 = @ruby_token_constant +| 6 = @ruby_token_empty_statement +| 7 = @ruby_token_escape_sequence +| 8 = @ruby_token_false +| 9 = @ruby_token_float +| 10 = @ruby_token_global_variable +| 11 = @ruby_token_hash_key_symbol +| 12 = @ruby_token_heredoc_beginning +| 13 = @ruby_token_heredoc_content +| 14 = @ruby_token_heredoc_end +| 15 = @ruby_token_identifier +| 16 = @ruby_token_instance_variable +| 17 = @ruby_token_integer +| 18 = @ruby_token_nil +| 19 = @ruby_token_operator +| 20 = @ruby_token_self +| 21 = @ruby_token_simple_symbol +| 22 = @ruby_token_string_content +| 23 = @ruby_token_super +| 24 = @ruby_token_true +| 25 = @ruby_token_uninterpreted +; + + +@ruby_ast_node = @ruby_alias | @ruby_argument_list | @ruby_array | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_parameter | @ruby_block_parameters | @ruby_break | @ruby_call | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_for | @ruby_hash | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_modifier | @ruby_in | @ruby_interpolation | @ruby_keyword_parameter | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield + +@ruby_ast_node_parent = @file | @ruby_ast_node + +#keyset[parent, parent_index] +ruby_ast_node_parent( + int child: @ruby_ast_node ref, + int parent: @ruby_ast_node_parent ref, + int parent_index: int ref +); + +erb_comment_directive_def( + unique int id: @erb_comment_directive, + int child: @erb_token_comment ref, + int loc: @location ref +); + +erb_directive_def( + unique int id: @erb_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +erb_graphql_directive_def( + unique int id: @erb_graphql_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +erb_output_directive_def( + unique int id: @erb_output_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +@erb_template_child_type = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_token_content + +#keyset[erb_template, index] +erb_template_child( + int erb_template: @erb_template ref, + int index: int ref, + unique int child: @erb_template_child_type ref +); + +erb_template_def( + unique int id: @erb_template, + int loc: @location ref +); + +erb_tokeninfo( + unique int id: @erb_token, + int kind: int ref, + int file: @file ref, + int idx: int ref, + string value: string ref, + int loc: @location ref +); + +case @erb_token.kind of + 0 = @erb_reserved_word +| 1 = @erb_token_code +| 2 = @erb_token_comment +| 3 = @erb_token_content +; + + +@erb_ast_node = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_template | @erb_token + +@erb_ast_node_parent = @erb_ast_node | @file + +#keyset[parent, parent_index] +erb_ast_node_parent( + int child: @erb_ast_node ref, + int parent: @erb_ast_node_parent ref, + int parent_index: int ref +); + diff --git a/ruby/ql/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/ruby.dbscheme b/ruby/ql/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/ruby.dbscheme new file mode 100644 index 00000000000..30e1075bbdc --- /dev/null +++ b/ruby/ql/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/ruby.dbscheme @@ -0,0 +1,1329 @@ +// CodeQL database schema for Ruby +// Automatically generated from the tree-sitter grammar; do not edit + +@location = @location_default + +locations_default( + unique int id: @location_default, + int file: @file ref, + int start_line: int ref, + int start_column: int ref, + int end_line: int ref, + int end_column: int ref +); + +@sourceline = @file + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +sourceLocationPrefix( + string prefix: string ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +case @diagnostic.severity of + 10 = @diagnostic_debug +| 20 = @diagnostic_info +| 30 = @diagnostic_warning +| 40 = @diagnostic_error +; + + +@ruby_underscore_arg = @ruby_assignment | @ruby_binary | @ruby_conditional | @ruby_operator_assignment | @ruby_range | @ruby_unary | @ruby_underscore_primary + +@ruby_underscore_lhs = @ruby_call | @ruby_element_reference | @ruby_scope_resolution | @ruby_token_false | @ruby_token_nil | @ruby_token_true | @ruby_underscore_variable + +@ruby_underscore_method_name = @ruby_delimited_symbol | @ruby_setter | @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_operator | @ruby_token_simple_symbol + +@ruby_underscore_primary = @ruby_array | @ruby_begin | @ruby_break | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_delimited_symbol | @ruby_for | @ruby_hash | @ruby_if | @ruby_lambda | @ruby_method | @ruby_module | @ruby_next | @ruby_parenthesized_statements | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_retry | @ruby_return | @ruby_singleton_class | @ruby_singleton_method | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_symbol_array | @ruby_token_character | @ruby_token_complex | @ruby_token_float | @ruby_token_heredoc_beginning | @ruby_token_integer | @ruby_token_simple_symbol | @ruby_unary | @ruby_underscore_lhs | @ruby_unless | @ruby_until | @ruby_while | @ruby_yield + +@ruby_underscore_statement = @ruby_alias | @ruby_assignment | @ruby_begin_block | @ruby_binary | @ruby_break | @ruby_call | @ruby_end_block | @ruby_if_modifier | @ruby_next | @ruby_operator_assignment | @ruby_rescue_modifier | @ruby_return | @ruby_unary | @ruby_undef | @ruby_underscore_arg | @ruby_unless_modifier | @ruby_until_modifier | @ruby_while_modifier | @ruby_yield + +@ruby_underscore_variable = @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_self | @ruby_token_super + +ruby_alias_def( + unique int id: @ruby_alias, + int alias: @ruby_underscore_method_name ref, + int name: @ruby_underscore_method_name ref, + int loc: @location ref +); + +@ruby_argument_list_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_argument_list, index] +ruby_argument_list_child( + int ruby_argument_list: @ruby_argument_list ref, + int index: int ref, + unique int child: @ruby_argument_list_child_type ref +); + +ruby_argument_list_def( + unique int id: @ruby_argument_list, + int loc: @location ref +); + +@ruby_array_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_array, index] +ruby_array_child( + int ruby_array: @ruby_array ref, + int index: int ref, + unique int child: @ruby_array_child_type ref +); + +ruby_array_def( + unique int id: @ruby_array, + int loc: @location ref +); + +@ruby_assignment_left_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +@ruby_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_right_assignment_list | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +ruby_assignment_def( + unique int id: @ruby_assignment, + int left: @ruby_assignment_left_type ref, + int right: @ruby_assignment_right_type ref, + int loc: @location ref +); + +@ruby_bare_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_string, index] +ruby_bare_string_child( + int ruby_bare_string: @ruby_bare_string ref, + int index: int ref, + unique int child: @ruby_bare_string_child_type ref +); + +ruby_bare_string_def( + unique int id: @ruby_bare_string, + int loc: @location ref +); + +@ruby_bare_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_symbol, index] +ruby_bare_symbol_child( + int ruby_bare_symbol: @ruby_bare_symbol ref, + int index: int ref, + unique int child: @ruby_bare_symbol_child_type ref +); + +ruby_bare_symbol_def( + unique int id: @ruby_bare_symbol, + int loc: @location ref +); + +@ruby_begin_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin, index] +ruby_begin_child( + int ruby_begin: @ruby_begin ref, + int index: int ref, + unique int child: @ruby_begin_child_type ref +); + +ruby_begin_def( + unique int id: @ruby_begin, + int loc: @location ref +); + +@ruby_begin_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin_block, index] +ruby_begin_block_child( + int ruby_begin_block: @ruby_begin_block ref, + int index: int ref, + unique int child: @ruby_begin_block_child_type ref +); + +ruby_begin_block_def( + unique int id: @ruby_begin_block, + int loc: @location ref +); + +@ruby_binary_left_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +case @ruby_binary.operator of + 0 = @ruby_binary_bangequal +| 1 = @ruby_binary_bangtilde +| 2 = @ruby_binary_percent +| 3 = @ruby_binary_ampersand +| 4 = @ruby_binary_ampersandampersand +| 5 = @ruby_binary_star +| 6 = @ruby_binary_starstar +| 7 = @ruby_binary_plus +| 8 = @ruby_binary_minus +| 9 = @ruby_binary_slash +| 10 = @ruby_binary_langle +| 11 = @ruby_binary_langlelangle +| 12 = @ruby_binary_langleequal +| 13 = @ruby_binary_langleequalrangle +| 14 = @ruby_binary_equalequal +| 15 = @ruby_binary_equalequalequal +| 16 = @ruby_binary_equaltilde +| 17 = @ruby_binary_rangle +| 18 = @ruby_binary_rangleequal +| 19 = @ruby_binary_ranglerangle +| 20 = @ruby_binary_caret +| 21 = @ruby_binary_and +| 22 = @ruby_binary_or +| 23 = @ruby_binary_pipe +| 24 = @ruby_binary_pipepipe +; + + +@ruby_binary_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_binary_def( + unique int id: @ruby_binary, + int left: @ruby_binary_left_type ref, + int operator: int ref, + int right: @ruby_binary_right_type ref, + int loc: @location ref +); + +ruby_block_parameters( + unique int ruby_block: @ruby_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_block, index] +ruby_block_child( + int ruby_block: @ruby_block ref, + int index: int ref, + unique int child: @ruby_block_child_type ref +); + +ruby_block_def( + unique int id: @ruby_block, + int loc: @location ref +); + +ruby_block_argument_def( + unique int id: @ruby_block_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_block_parameter_def( + unique int id: @ruby_block_parameter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_block_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_block_parameters, index] +ruby_block_parameters_child( + int ruby_block_parameters: @ruby_block_parameters ref, + int index: int ref, + unique int child: @ruby_block_parameters_child_type ref +); + +ruby_block_parameters_def( + unique int id: @ruby_block_parameters, + int loc: @location ref +); + +ruby_break_child( + unique int ruby_break: @ruby_break ref, + unique int child: @ruby_argument_list ref +); + +ruby_break_def( + unique int id: @ruby_break, + int loc: @location ref +); + +ruby_call_arguments( + unique int ruby_call: @ruby_call ref, + unique int arguments: @ruby_argument_list ref +); + +@ruby_call_block_type = @ruby_block | @ruby_do_block + +ruby_call_block( + unique int ruby_call: @ruby_call ref, + unique int block: @ruby_call_block_type ref +); + +@ruby_call_method_type = @ruby_argument_list | @ruby_scope_resolution | @ruby_token_operator | @ruby_underscore_variable + +@ruby_call_receiver_type = @ruby_call | @ruby_underscore_primary + +ruby_call_receiver( + unique int ruby_call: @ruby_call ref, + unique int receiver: @ruby_call_receiver_type ref +); + +ruby_call_def( + unique int id: @ruby_call, + int method: @ruby_call_method_type ref, + int loc: @location ref +); + +ruby_case_value( + unique int ruby_case__: @ruby_case__ ref, + unique int value: @ruby_underscore_statement ref +); + +@ruby_case_child_type = @ruby_else | @ruby_when + +#keyset[ruby_case__, index] +ruby_case_child( + int ruby_case__: @ruby_case__ ref, + int index: int ref, + unique int child: @ruby_case_child_type ref +); + +ruby_case_def( + unique int id: @ruby_case__, + int loc: @location ref +); + +#keyset[ruby_chained_string, index] +ruby_chained_string_child( + int ruby_chained_string: @ruby_chained_string ref, + int index: int ref, + unique int child: @ruby_string__ ref +); + +ruby_chained_string_def( + unique int id: @ruby_chained_string, + int loc: @location ref +); + +@ruby_class_name_type = @ruby_scope_resolution | @ruby_token_constant + +ruby_class_superclass( + unique int ruby_class: @ruby_class ref, + unique int superclass: @ruby_superclass ref +); + +@ruby_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_class, index] +ruby_class_child( + int ruby_class: @ruby_class ref, + int index: int ref, + unique int child: @ruby_class_child_type ref +); + +ruby_class_def( + unique int id: @ruby_class, + int name: @ruby_class_name_type ref, + int loc: @location ref +); + +ruby_conditional_def( + unique int id: @ruby_conditional, + int alternative: @ruby_underscore_arg ref, + int condition: @ruby_underscore_arg ref, + int consequence: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_delimited_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_delimited_symbol, index] +ruby_delimited_symbol_child( + int ruby_delimited_symbol: @ruby_delimited_symbol ref, + int index: int ref, + unique int child: @ruby_delimited_symbol_child_type ref +); + +ruby_delimited_symbol_def( + unique int id: @ruby_delimited_symbol, + int loc: @location ref +); + +@ruby_destructured_left_assignment_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_destructured_left_assignment, index] +ruby_destructured_left_assignment_child( + int ruby_destructured_left_assignment: @ruby_destructured_left_assignment ref, + int index: int ref, + unique int child: @ruby_destructured_left_assignment_child_type ref +); + +ruby_destructured_left_assignment_def( + unique int id: @ruby_destructured_left_assignment, + int loc: @location ref +); + +@ruby_destructured_parameter_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_destructured_parameter, index] +ruby_destructured_parameter_child( + int ruby_destructured_parameter: @ruby_destructured_parameter ref, + int index: int ref, + unique int child: @ruby_destructured_parameter_child_type ref +); + +ruby_destructured_parameter_def( + unique int id: @ruby_destructured_parameter, + int loc: @location ref +); + +@ruby_do_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do, index] +ruby_do_child( + int ruby_do: @ruby_do ref, + int index: int ref, + unique int child: @ruby_do_child_type ref +); + +ruby_do_def( + unique int id: @ruby_do, + int loc: @location ref +); + +ruby_do_block_parameters( + unique int ruby_do_block: @ruby_do_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_do_block_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do_block, index] +ruby_do_block_child( + int ruby_do_block: @ruby_do_block ref, + int index: int ref, + unique int child: @ruby_do_block_child_type ref +); + +ruby_do_block_def( + unique int id: @ruby_do_block, + int loc: @location ref +); + +@ruby_element_reference_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_element_reference, index] +ruby_element_reference_child( + int ruby_element_reference: @ruby_element_reference ref, + int index: int ref, + unique int child: @ruby_element_reference_child_type ref +); + +ruby_element_reference_def( + unique int id: @ruby_element_reference, + int object: @ruby_underscore_primary ref, + int loc: @location ref +); + +@ruby_else_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_else, index] +ruby_else_child( + int ruby_else: @ruby_else ref, + int index: int ref, + unique int child: @ruby_else_child_type ref +); + +ruby_else_def( + unique int id: @ruby_else, + int loc: @location ref +); + +@ruby_elsif_alternative_type = @ruby_else | @ruby_elsif + +ruby_elsif_alternative( + unique int ruby_elsif: @ruby_elsif ref, + unique int alternative: @ruby_elsif_alternative_type ref +); + +ruby_elsif_consequence( + unique int ruby_elsif: @ruby_elsif ref, + unique int consequence: @ruby_then ref +); + +ruby_elsif_def( + unique int id: @ruby_elsif, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_end_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_end_block, index] +ruby_end_block_child( + int ruby_end_block: @ruby_end_block ref, + int index: int ref, + unique int child: @ruby_end_block_child_type ref +); + +ruby_end_block_def( + unique int id: @ruby_end_block, + int loc: @location ref +); + +@ruby_ensure_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_ensure, index] +ruby_ensure_child( + int ruby_ensure: @ruby_ensure ref, + int index: int ref, + unique int child: @ruby_ensure_child_type ref +); + +ruby_ensure_def( + unique int id: @ruby_ensure, + int loc: @location ref +); + +ruby_exception_variable_def( + unique int id: @ruby_exception_variable, + int child: @ruby_underscore_lhs ref, + int loc: @location ref +); + +@ruby_exceptions_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_exceptions, index] +ruby_exceptions_child( + int ruby_exceptions: @ruby_exceptions ref, + int index: int ref, + unique int child: @ruby_exceptions_child_type ref +); + +ruby_exceptions_def( + unique int id: @ruby_exceptions, + int loc: @location ref +); + +@ruby_for_pattern_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +ruby_for_def( + unique int id: @ruby_for, + int body: @ruby_do ref, + int pattern: @ruby_for_pattern_type ref, + int value: @ruby_in ref, + int loc: @location ref +); + +@ruby_hash_child_type = @ruby_hash_splat_argument | @ruby_pair + +#keyset[ruby_hash, index] +ruby_hash_child( + int ruby_hash: @ruby_hash ref, + int index: int ref, + unique int child: @ruby_hash_child_type ref +); + +ruby_hash_def( + unique int id: @ruby_hash, + int loc: @location ref +); + +ruby_hash_splat_argument_def( + unique int id: @ruby_hash_splat_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_hash_splat_parameter_name( + unique int ruby_hash_splat_parameter: @ruby_hash_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_hash_splat_parameter_def( + unique int id: @ruby_hash_splat_parameter, + int loc: @location ref +); + +@ruby_heredoc_body_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_heredoc_content | @ruby_token_heredoc_end + +#keyset[ruby_heredoc_body, index] +ruby_heredoc_body_child( + int ruby_heredoc_body: @ruby_heredoc_body ref, + int index: int ref, + unique int child: @ruby_heredoc_body_child_type ref +); + +ruby_heredoc_body_def( + unique int id: @ruby_heredoc_body, + int loc: @location ref +); + +@ruby_if_alternative_type = @ruby_else | @ruby_elsif + +ruby_if_alternative( + unique int ruby_if: @ruby_if ref, + unique int alternative: @ruby_if_alternative_type ref +); + +ruby_if_consequence( + unique int ruby_if: @ruby_if ref, + unique int consequence: @ruby_then ref +); + +ruby_if_def( + unique int id: @ruby_if, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_if_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_if_modifier_def( + unique int id: @ruby_if_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_if_modifier_condition_type ref, + int loc: @location ref +); + +ruby_in_def( + unique int id: @ruby_in, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_interpolation_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_interpolation, index] +ruby_interpolation_child( + int ruby_interpolation: @ruby_interpolation ref, + int index: int ref, + unique int child: @ruby_interpolation_child_type ref +); + +ruby_interpolation_def( + unique int id: @ruby_interpolation, + int loc: @location ref +); + +ruby_keyword_parameter_value( + unique int ruby_keyword_parameter: @ruby_keyword_parameter ref, + unique int value: @ruby_underscore_arg ref +); + +ruby_keyword_parameter_def( + unique int id: @ruby_keyword_parameter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_lambda_body_type = @ruby_block | @ruby_do_block + +ruby_lambda_parameters( + unique int ruby_lambda: @ruby_lambda ref, + unique int parameters: @ruby_lambda_parameters ref +); + +ruby_lambda_def( + unique int id: @ruby_lambda, + int body: @ruby_lambda_body_type ref, + int loc: @location ref +); + +@ruby_lambda_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_lambda_parameters, index] +ruby_lambda_parameters_child( + int ruby_lambda_parameters: @ruby_lambda_parameters ref, + int index: int ref, + unique int child: @ruby_lambda_parameters_child_type ref +); + +ruby_lambda_parameters_def( + unique int id: @ruby_lambda_parameters, + int loc: @location ref +); + +@ruby_left_assignment_list_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_left_assignment_list, index] +ruby_left_assignment_list_child( + int ruby_left_assignment_list: @ruby_left_assignment_list ref, + int index: int ref, + unique int child: @ruby_left_assignment_list_child_type ref +); + +ruby_left_assignment_list_def( + unique int id: @ruby_left_assignment_list, + int loc: @location ref +); + +ruby_method_parameters( + unique int ruby_method: @ruby_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_method, index] +ruby_method_child( + int ruby_method: @ruby_method ref, + int index: int ref, + unique int child: @ruby_method_child_type ref +); + +ruby_method_def( + unique int id: @ruby_method, + int name: @ruby_underscore_method_name ref, + int loc: @location ref +); + +@ruby_method_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_method_parameters, index] +ruby_method_parameters_child( + int ruby_method_parameters: @ruby_method_parameters ref, + int index: int ref, + unique int child: @ruby_method_parameters_child_type ref +); + +ruby_method_parameters_def( + unique int id: @ruby_method_parameters, + int loc: @location ref +); + +@ruby_module_name_type = @ruby_scope_resolution | @ruby_token_constant + +@ruby_module_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_module, index] +ruby_module_child( + int ruby_module: @ruby_module ref, + int index: int ref, + unique int child: @ruby_module_child_type ref +); + +ruby_module_def( + unique int id: @ruby_module, + int name: @ruby_module_name_type ref, + int loc: @location ref +); + +ruby_next_child( + unique int ruby_next: @ruby_next ref, + unique int child: @ruby_argument_list ref +); + +ruby_next_def( + unique int id: @ruby_next, + int loc: @location ref +); + +case @ruby_operator_assignment.operator of + 0 = @ruby_operator_assignment_percentequal +| 1 = @ruby_operator_assignment_ampersandampersandequal +| 2 = @ruby_operator_assignment_ampersandequal +| 3 = @ruby_operator_assignment_starstarequal +| 4 = @ruby_operator_assignment_starequal +| 5 = @ruby_operator_assignment_plusequal +| 6 = @ruby_operator_assignment_minusequal +| 7 = @ruby_operator_assignment_slashequal +| 8 = @ruby_operator_assignment_langlelangleequal +| 9 = @ruby_operator_assignment_ranglerangleequal +| 10 = @ruby_operator_assignment_caretequal +| 11 = @ruby_operator_assignment_pipeequal +| 12 = @ruby_operator_assignment_pipepipeequal +; + + +@ruby_operator_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_operator_assignment_def( + unique int id: @ruby_operator_assignment, + int left: @ruby_underscore_lhs ref, + int operator: int ref, + int right: @ruby_operator_assignment_right_type ref, + int loc: @location ref +); + +ruby_optional_parameter_def( + unique int id: @ruby_optional_parameter, + int name: @ruby_token_identifier ref, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_pair_key_type = @ruby_string__ | @ruby_token_hash_key_symbol | @ruby_underscore_arg + +ruby_pair_def( + unique int id: @ruby_pair, + int key__: @ruby_pair_key_type ref, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_parenthesized_statements_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_parenthesized_statements, index] +ruby_parenthesized_statements_child( + int ruby_parenthesized_statements: @ruby_parenthesized_statements ref, + int index: int ref, + unique int child: @ruby_parenthesized_statements_child_type ref +); + +ruby_parenthesized_statements_def( + unique int id: @ruby_parenthesized_statements, + int loc: @location ref +); + +@ruby_pattern_child_type = @ruby_splat_argument | @ruby_underscore_arg + +ruby_pattern_def( + unique int id: @ruby_pattern, + int child: @ruby_pattern_child_type ref, + int loc: @location ref +); + +@ruby_program_child_type = @ruby_token_empty_statement | @ruby_token_uninterpreted | @ruby_underscore_statement + +#keyset[ruby_program, index] +ruby_program_child( + int ruby_program: @ruby_program ref, + int index: int ref, + unique int child: @ruby_program_child_type ref +); + +ruby_program_def( + unique int id: @ruby_program, + int loc: @location ref +); + +ruby_range_begin( + unique int ruby_range: @ruby_range ref, + unique int begin: @ruby_underscore_arg ref +); + +ruby_range_end( + unique int ruby_range: @ruby_range ref, + unique int end: @ruby_underscore_arg ref +); + +case @ruby_range.operator of + 0 = @ruby_range_dotdot +| 1 = @ruby_range_dotdotdot +; + + +ruby_range_def( + unique int id: @ruby_range, + int operator: int ref, + int loc: @location ref +); + +@ruby_rational_child_type = @ruby_token_float | @ruby_token_integer + +ruby_rational_def( + unique int id: @ruby_rational, + int child: @ruby_rational_child_type ref, + int loc: @location ref +); + +ruby_redo_child( + unique int ruby_redo: @ruby_redo ref, + unique int child: @ruby_argument_list ref +); + +ruby_redo_def( + unique int id: @ruby_redo, + int loc: @location ref +); + +@ruby_regex_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_regex, index] +ruby_regex_child( + int ruby_regex: @ruby_regex ref, + int index: int ref, + unique int child: @ruby_regex_child_type ref +); + +ruby_regex_def( + unique int id: @ruby_regex, + int loc: @location ref +); + +ruby_rescue_body( + unique int ruby_rescue: @ruby_rescue ref, + unique int body: @ruby_then ref +); + +ruby_rescue_exceptions( + unique int ruby_rescue: @ruby_rescue ref, + unique int exceptions: @ruby_exceptions ref +); + +ruby_rescue_variable( + unique int ruby_rescue: @ruby_rescue ref, + unique int variable: @ruby_exception_variable ref +); + +ruby_rescue_def( + unique int id: @ruby_rescue, + int loc: @location ref +); + +@ruby_rescue_modifier_handler_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_rescue_modifier_def( + unique int id: @ruby_rescue_modifier, + int body: @ruby_underscore_statement ref, + int handler: @ruby_rescue_modifier_handler_type ref, + int loc: @location ref +); + +ruby_rest_assignment_child( + unique int ruby_rest_assignment: @ruby_rest_assignment ref, + unique int child: @ruby_underscore_lhs ref +); + +ruby_rest_assignment_def( + unique int id: @ruby_rest_assignment, + int loc: @location ref +); + +ruby_retry_child( + unique int ruby_retry: @ruby_retry ref, + unique int child: @ruby_argument_list ref +); + +ruby_retry_def( + unique int id: @ruby_retry, + int loc: @location ref +); + +ruby_return_child( + unique int ruby_return: @ruby_return ref, + unique int child: @ruby_argument_list ref +); + +ruby_return_def( + unique int id: @ruby_return, + int loc: @location ref +); + +@ruby_right_assignment_list_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_right_assignment_list, index] +ruby_right_assignment_list_child( + int ruby_right_assignment_list: @ruby_right_assignment_list ref, + int index: int ref, + unique int child: @ruby_right_assignment_list_child_type ref +); + +ruby_right_assignment_list_def( + unique int id: @ruby_right_assignment_list, + int loc: @location ref +); + +@ruby_scope_resolution_name_type = @ruby_token_constant | @ruby_token_identifier + +ruby_scope_resolution_scope( + unique int ruby_scope_resolution: @ruby_scope_resolution ref, + unique int scope: @ruby_underscore_primary ref +); + +ruby_scope_resolution_def( + unique int id: @ruby_scope_resolution, + int name: @ruby_scope_resolution_name_type ref, + int loc: @location ref +); + +ruby_setter_def( + unique int id: @ruby_setter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_singleton_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_class, index] +ruby_singleton_class_child( + int ruby_singleton_class: @ruby_singleton_class ref, + int index: int ref, + unique int child: @ruby_singleton_class_child_type ref +); + +ruby_singleton_class_def( + unique int id: @ruby_singleton_class, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_singleton_method_object_type = @ruby_underscore_arg | @ruby_underscore_variable + +ruby_singleton_method_parameters( + unique int ruby_singleton_method: @ruby_singleton_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_singleton_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_method, index] +ruby_singleton_method_child( + int ruby_singleton_method: @ruby_singleton_method ref, + int index: int ref, + unique int child: @ruby_singleton_method_child_type ref +); + +ruby_singleton_method_def( + unique int id: @ruby_singleton_method, + int name: @ruby_underscore_method_name ref, + int object: @ruby_singleton_method_object_type ref, + int loc: @location ref +); + +ruby_splat_argument_def( + unique int id: @ruby_splat_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_splat_parameter_name( + unique int ruby_splat_parameter: @ruby_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_splat_parameter_def( + unique int id: @ruby_splat_parameter, + int loc: @location ref +); + +@ruby_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_string__, index] +ruby_string_child( + int ruby_string__: @ruby_string__ ref, + int index: int ref, + unique int child: @ruby_string_child_type ref +); + +ruby_string_def( + unique int id: @ruby_string__, + int loc: @location ref +); + +#keyset[ruby_string_array, index] +ruby_string_array_child( + int ruby_string_array: @ruby_string_array ref, + int index: int ref, + unique int child: @ruby_bare_string ref +); + +ruby_string_array_def( + unique int id: @ruby_string_array, + int loc: @location ref +); + +@ruby_subshell_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_subshell, index] +ruby_subshell_child( + int ruby_subshell: @ruby_subshell ref, + int index: int ref, + unique int child: @ruby_subshell_child_type ref +); + +ruby_subshell_def( + unique int id: @ruby_subshell, + int loc: @location ref +); + +@ruby_superclass_child_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_superclass_def( + unique int id: @ruby_superclass, + int child: @ruby_superclass_child_type ref, + int loc: @location ref +); + +#keyset[ruby_symbol_array, index] +ruby_symbol_array_child( + int ruby_symbol_array: @ruby_symbol_array ref, + int index: int ref, + unique int child: @ruby_bare_symbol ref +); + +ruby_symbol_array_def( + unique int id: @ruby_symbol_array, + int loc: @location ref +); + +@ruby_then_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_then, index] +ruby_then_child( + int ruby_then: @ruby_then ref, + int index: int ref, + unique int child: @ruby_then_child_type ref +); + +ruby_then_def( + unique int id: @ruby_then, + int loc: @location ref +); + +@ruby_unary_operand_type = @ruby_break | @ruby_call | @ruby_next | @ruby_parenthesized_statements | @ruby_return | @ruby_token_float | @ruby_token_integer | @ruby_underscore_arg | @ruby_yield + +case @ruby_unary.operator of + 0 = @ruby_unary_bang +| 1 = @ruby_unary_plus +| 2 = @ruby_unary_minus +| 3 = @ruby_unary_definedquestion +| 4 = @ruby_unary_not +| 5 = @ruby_unary_tilde +; + + +ruby_unary_def( + unique int id: @ruby_unary, + int operand: @ruby_unary_operand_type ref, + int operator: int ref, + int loc: @location ref +); + +#keyset[ruby_undef, index] +ruby_undef_child( + int ruby_undef: @ruby_undef ref, + int index: int ref, + unique int child: @ruby_underscore_method_name ref +); + +ruby_undef_def( + unique int id: @ruby_undef, + int loc: @location ref +); + +@ruby_unless_alternative_type = @ruby_else | @ruby_elsif + +ruby_unless_alternative( + unique int ruby_unless: @ruby_unless ref, + unique int alternative: @ruby_unless_alternative_type ref +); + +ruby_unless_consequence( + unique int ruby_unless: @ruby_unless ref, + unique int consequence: @ruby_then ref +); + +ruby_unless_def( + unique int id: @ruby_unless, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_unless_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_unless_modifier_def( + unique int id: @ruby_unless_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_unless_modifier_condition_type ref, + int loc: @location ref +); + +ruby_until_def( + unique int id: @ruby_until, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_until_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_until_modifier_def( + unique int id: @ruby_until_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_until_modifier_condition_type ref, + int loc: @location ref +); + +ruby_when_body( + unique int ruby_when: @ruby_when ref, + unique int body: @ruby_then ref +); + +#keyset[ruby_when, index] +ruby_when_pattern( + int ruby_when: @ruby_when ref, + int index: int ref, + unique int pattern: @ruby_pattern ref +); + +ruby_when_def( + unique int id: @ruby_when, + int loc: @location ref +); + +ruby_while_def( + unique int id: @ruby_while, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_while_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_while_modifier_def( + unique int id: @ruby_while_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_while_modifier_condition_type ref, + int loc: @location ref +); + +ruby_yield_child( + unique int ruby_yield: @ruby_yield ref, + unique int child: @ruby_argument_list ref +); + +ruby_yield_def( + unique int id: @ruby_yield, + int loc: @location ref +); + +ruby_tokeninfo( + unique int id: @ruby_token, + int kind: int ref, + int file: @file ref, + int idx: int ref, + string value: string ref, + int loc: @location ref +); + +case @ruby_token.kind of + 0 = @ruby_reserved_word +| 1 = @ruby_token_character +| 2 = @ruby_token_class_variable +| 3 = @ruby_token_comment +| 4 = @ruby_token_complex +| 5 = @ruby_token_constant +| 6 = @ruby_token_empty_statement +| 7 = @ruby_token_escape_sequence +| 8 = @ruby_token_false +| 9 = @ruby_token_float +| 10 = @ruby_token_global_variable +| 11 = @ruby_token_hash_key_symbol +| 12 = @ruby_token_heredoc_beginning +| 13 = @ruby_token_heredoc_content +| 14 = @ruby_token_heredoc_end +| 15 = @ruby_token_identifier +| 16 = @ruby_token_instance_variable +| 17 = @ruby_token_integer +| 18 = @ruby_token_nil +| 19 = @ruby_token_operator +| 20 = @ruby_token_self +| 21 = @ruby_token_simple_symbol +| 22 = @ruby_token_string_content +| 23 = @ruby_token_super +| 24 = @ruby_token_true +| 25 = @ruby_token_uninterpreted +; + + +@ruby_ast_node = @ruby_alias | @ruby_argument_list | @ruby_array | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_parameter | @ruby_block_parameters | @ruby_break | @ruby_call | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_for | @ruby_hash | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_modifier | @ruby_in | @ruby_interpolation | @ruby_keyword_parameter | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield + +@ruby_ast_node_parent = @file | @ruby_ast_node + +#keyset[parent, parent_index] +ruby_ast_node_parent( + int child: @ruby_ast_node ref, + int parent: @ruby_ast_node_parent ref, + int parent_index: int ref +); + +erb_comment_directive_def( + unique int id: @erb_comment_directive, + int child: @erb_token_comment ref, + int loc: @location ref +); + +erb_directive_def( + unique int id: @erb_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +erb_graphql_directive_def( + unique int id: @erb_graphql_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +erb_output_directive_def( + unique int id: @erb_output_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +@erb_template_child_type = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_token_content + +#keyset[erb_template, index] +erb_template_child( + int erb_template: @erb_template ref, + int index: int ref, + unique int child: @erb_template_child_type ref +); + +erb_template_def( + unique int id: @erb_template, + int loc: @location ref +); + +erb_tokeninfo( + unique int id: @erb_token, + int kind: int ref, + int file: @file ref, + int idx: int ref, + string value: string ref, + int loc: @location ref +); + +case @erb_token.kind of + 0 = @erb_reserved_word +| 1 = @erb_token_code +| 2 = @erb_token_comment +| 3 = @erb_token_content +; + + +@erb_ast_node = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_template | @erb_token + +@erb_ast_node_parent = @erb_ast_node | @file + +#keyset[parent, parent_index] +erb_ast_node_parent( + int child: @erb_ast_node ref, + int parent: @erb_ast_node_parent ref, + int parent_index: int ref +); + diff --git a/ruby/ql/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/upgrade.properties b/ruby/ql/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/upgrade.properties new file mode 100644 index 00000000000..71f3ee178e5 --- /dev/null +++ b/ruby/ql/lib/upgrades/09a494ce67d8141f28d6411f89b9ff7bdad440f3/upgrade.properties @@ -0,0 +1,4 @@ +description: Removed unused column from the `folders` and `files` relations +compatibility: full +files.rel: reorder files.rel (int id, string name, string simple, string ext, int fromSource) id name +folders.rel: reorder folders.rel (int id, string name, string simple) id name diff --git a/ruby/ql/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/old.dbscheme b/ruby/ql/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/old.dbscheme new file mode 100644 index 00000000000..30e1075bbdc --- /dev/null +++ b/ruby/ql/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/old.dbscheme @@ -0,0 +1,1329 @@ +// CodeQL database schema for Ruby +// Automatically generated from the tree-sitter grammar; do not edit + +@location = @location_default + +locations_default( + unique int id: @location_default, + int file: @file ref, + int start_line: int ref, + int start_column: int ref, + int end_line: int ref, + int end_column: int ref +); + +@sourceline = @file + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +sourceLocationPrefix( + string prefix: string ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +case @diagnostic.severity of + 10 = @diagnostic_debug +| 20 = @diagnostic_info +| 30 = @diagnostic_warning +| 40 = @diagnostic_error +; + + +@ruby_underscore_arg = @ruby_assignment | @ruby_binary | @ruby_conditional | @ruby_operator_assignment | @ruby_range | @ruby_unary | @ruby_underscore_primary + +@ruby_underscore_lhs = @ruby_call | @ruby_element_reference | @ruby_scope_resolution | @ruby_token_false | @ruby_token_nil | @ruby_token_true | @ruby_underscore_variable + +@ruby_underscore_method_name = @ruby_delimited_symbol | @ruby_setter | @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_operator | @ruby_token_simple_symbol + +@ruby_underscore_primary = @ruby_array | @ruby_begin | @ruby_break | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_delimited_symbol | @ruby_for | @ruby_hash | @ruby_if | @ruby_lambda | @ruby_method | @ruby_module | @ruby_next | @ruby_parenthesized_statements | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_retry | @ruby_return | @ruby_singleton_class | @ruby_singleton_method | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_symbol_array | @ruby_token_character | @ruby_token_complex | @ruby_token_float | @ruby_token_heredoc_beginning | @ruby_token_integer | @ruby_token_simple_symbol | @ruby_unary | @ruby_underscore_lhs | @ruby_unless | @ruby_until | @ruby_while | @ruby_yield + +@ruby_underscore_statement = @ruby_alias | @ruby_assignment | @ruby_begin_block | @ruby_binary | @ruby_break | @ruby_call | @ruby_end_block | @ruby_if_modifier | @ruby_next | @ruby_operator_assignment | @ruby_rescue_modifier | @ruby_return | @ruby_unary | @ruby_undef | @ruby_underscore_arg | @ruby_unless_modifier | @ruby_until_modifier | @ruby_while_modifier | @ruby_yield + +@ruby_underscore_variable = @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_self | @ruby_token_super + +ruby_alias_def( + unique int id: @ruby_alias, + int alias: @ruby_underscore_method_name ref, + int name: @ruby_underscore_method_name ref, + int loc: @location ref +); + +@ruby_argument_list_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_argument_list, index] +ruby_argument_list_child( + int ruby_argument_list: @ruby_argument_list ref, + int index: int ref, + unique int child: @ruby_argument_list_child_type ref +); + +ruby_argument_list_def( + unique int id: @ruby_argument_list, + int loc: @location ref +); + +@ruby_array_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_array, index] +ruby_array_child( + int ruby_array: @ruby_array ref, + int index: int ref, + unique int child: @ruby_array_child_type ref +); + +ruby_array_def( + unique int id: @ruby_array, + int loc: @location ref +); + +@ruby_assignment_left_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +@ruby_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_right_assignment_list | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +ruby_assignment_def( + unique int id: @ruby_assignment, + int left: @ruby_assignment_left_type ref, + int right: @ruby_assignment_right_type ref, + int loc: @location ref +); + +@ruby_bare_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_string, index] +ruby_bare_string_child( + int ruby_bare_string: @ruby_bare_string ref, + int index: int ref, + unique int child: @ruby_bare_string_child_type ref +); + +ruby_bare_string_def( + unique int id: @ruby_bare_string, + int loc: @location ref +); + +@ruby_bare_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_symbol, index] +ruby_bare_symbol_child( + int ruby_bare_symbol: @ruby_bare_symbol ref, + int index: int ref, + unique int child: @ruby_bare_symbol_child_type ref +); + +ruby_bare_symbol_def( + unique int id: @ruby_bare_symbol, + int loc: @location ref +); + +@ruby_begin_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin, index] +ruby_begin_child( + int ruby_begin: @ruby_begin ref, + int index: int ref, + unique int child: @ruby_begin_child_type ref +); + +ruby_begin_def( + unique int id: @ruby_begin, + int loc: @location ref +); + +@ruby_begin_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin_block, index] +ruby_begin_block_child( + int ruby_begin_block: @ruby_begin_block ref, + int index: int ref, + unique int child: @ruby_begin_block_child_type ref +); + +ruby_begin_block_def( + unique int id: @ruby_begin_block, + int loc: @location ref +); + +@ruby_binary_left_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +case @ruby_binary.operator of + 0 = @ruby_binary_bangequal +| 1 = @ruby_binary_bangtilde +| 2 = @ruby_binary_percent +| 3 = @ruby_binary_ampersand +| 4 = @ruby_binary_ampersandampersand +| 5 = @ruby_binary_star +| 6 = @ruby_binary_starstar +| 7 = @ruby_binary_plus +| 8 = @ruby_binary_minus +| 9 = @ruby_binary_slash +| 10 = @ruby_binary_langle +| 11 = @ruby_binary_langlelangle +| 12 = @ruby_binary_langleequal +| 13 = @ruby_binary_langleequalrangle +| 14 = @ruby_binary_equalequal +| 15 = @ruby_binary_equalequalequal +| 16 = @ruby_binary_equaltilde +| 17 = @ruby_binary_rangle +| 18 = @ruby_binary_rangleequal +| 19 = @ruby_binary_ranglerangle +| 20 = @ruby_binary_caret +| 21 = @ruby_binary_and +| 22 = @ruby_binary_or +| 23 = @ruby_binary_pipe +| 24 = @ruby_binary_pipepipe +; + + +@ruby_binary_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_binary_def( + unique int id: @ruby_binary, + int left: @ruby_binary_left_type ref, + int operator: int ref, + int right: @ruby_binary_right_type ref, + int loc: @location ref +); + +ruby_block_parameters( + unique int ruby_block: @ruby_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_block, index] +ruby_block_child( + int ruby_block: @ruby_block ref, + int index: int ref, + unique int child: @ruby_block_child_type ref +); + +ruby_block_def( + unique int id: @ruby_block, + int loc: @location ref +); + +ruby_block_argument_def( + unique int id: @ruby_block_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_block_parameter_def( + unique int id: @ruby_block_parameter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_block_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_block_parameters, index] +ruby_block_parameters_child( + int ruby_block_parameters: @ruby_block_parameters ref, + int index: int ref, + unique int child: @ruby_block_parameters_child_type ref +); + +ruby_block_parameters_def( + unique int id: @ruby_block_parameters, + int loc: @location ref +); + +ruby_break_child( + unique int ruby_break: @ruby_break ref, + unique int child: @ruby_argument_list ref +); + +ruby_break_def( + unique int id: @ruby_break, + int loc: @location ref +); + +ruby_call_arguments( + unique int ruby_call: @ruby_call ref, + unique int arguments: @ruby_argument_list ref +); + +@ruby_call_block_type = @ruby_block | @ruby_do_block + +ruby_call_block( + unique int ruby_call: @ruby_call ref, + unique int block: @ruby_call_block_type ref +); + +@ruby_call_method_type = @ruby_argument_list | @ruby_scope_resolution | @ruby_token_operator | @ruby_underscore_variable + +@ruby_call_receiver_type = @ruby_call | @ruby_underscore_primary + +ruby_call_receiver( + unique int ruby_call: @ruby_call ref, + unique int receiver: @ruby_call_receiver_type ref +); + +ruby_call_def( + unique int id: @ruby_call, + int method: @ruby_call_method_type ref, + int loc: @location ref +); + +ruby_case_value( + unique int ruby_case__: @ruby_case__ ref, + unique int value: @ruby_underscore_statement ref +); + +@ruby_case_child_type = @ruby_else | @ruby_when + +#keyset[ruby_case__, index] +ruby_case_child( + int ruby_case__: @ruby_case__ ref, + int index: int ref, + unique int child: @ruby_case_child_type ref +); + +ruby_case_def( + unique int id: @ruby_case__, + int loc: @location ref +); + +#keyset[ruby_chained_string, index] +ruby_chained_string_child( + int ruby_chained_string: @ruby_chained_string ref, + int index: int ref, + unique int child: @ruby_string__ ref +); + +ruby_chained_string_def( + unique int id: @ruby_chained_string, + int loc: @location ref +); + +@ruby_class_name_type = @ruby_scope_resolution | @ruby_token_constant + +ruby_class_superclass( + unique int ruby_class: @ruby_class ref, + unique int superclass: @ruby_superclass ref +); + +@ruby_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_class, index] +ruby_class_child( + int ruby_class: @ruby_class ref, + int index: int ref, + unique int child: @ruby_class_child_type ref +); + +ruby_class_def( + unique int id: @ruby_class, + int name: @ruby_class_name_type ref, + int loc: @location ref +); + +ruby_conditional_def( + unique int id: @ruby_conditional, + int alternative: @ruby_underscore_arg ref, + int condition: @ruby_underscore_arg ref, + int consequence: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_delimited_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_delimited_symbol, index] +ruby_delimited_symbol_child( + int ruby_delimited_symbol: @ruby_delimited_symbol ref, + int index: int ref, + unique int child: @ruby_delimited_symbol_child_type ref +); + +ruby_delimited_symbol_def( + unique int id: @ruby_delimited_symbol, + int loc: @location ref +); + +@ruby_destructured_left_assignment_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_destructured_left_assignment, index] +ruby_destructured_left_assignment_child( + int ruby_destructured_left_assignment: @ruby_destructured_left_assignment ref, + int index: int ref, + unique int child: @ruby_destructured_left_assignment_child_type ref +); + +ruby_destructured_left_assignment_def( + unique int id: @ruby_destructured_left_assignment, + int loc: @location ref +); + +@ruby_destructured_parameter_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_destructured_parameter, index] +ruby_destructured_parameter_child( + int ruby_destructured_parameter: @ruby_destructured_parameter ref, + int index: int ref, + unique int child: @ruby_destructured_parameter_child_type ref +); + +ruby_destructured_parameter_def( + unique int id: @ruby_destructured_parameter, + int loc: @location ref +); + +@ruby_do_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do, index] +ruby_do_child( + int ruby_do: @ruby_do ref, + int index: int ref, + unique int child: @ruby_do_child_type ref +); + +ruby_do_def( + unique int id: @ruby_do, + int loc: @location ref +); + +ruby_do_block_parameters( + unique int ruby_do_block: @ruby_do_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_do_block_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do_block, index] +ruby_do_block_child( + int ruby_do_block: @ruby_do_block ref, + int index: int ref, + unique int child: @ruby_do_block_child_type ref +); + +ruby_do_block_def( + unique int id: @ruby_do_block, + int loc: @location ref +); + +@ruby_element_reference_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_element_reference, index] +ruby_element_reference_child( + int ruby_element_reference: @ruby_element_reference ref, + int index: int ref, + unique int child: @ruby_element_reference_child_type ref +); + +ruby_element_reference_def( + unique int id: @ruby_element_reference, + int object: @ruby_underscore_primary ref, + int loc: @location ref +); + +@ruby_else_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_else, index] +ruby_else_child( + int ruby_else: @ruby_else ref, + int index: int ref, + unique int child: @ruby_else_child_type ref +); + +ruby_else_def( + unique int id: @ruby_else, + int loc: @location ref +); + +@ruby_elsif_alternative_type = @ruby_else | @ruby_elsif + +ruby_elsif_alternative( + unique int ruby_elsif: @ruby_elsif ref, + unique int alternative: @ruby_elsif_alternative_type ref +); + +ruby_elsif_consequence( + unique int ruby_elsif: @ruby_elsif ref, + unique int consequence: @ruby_then ref +); + +ruby_elsif_def( + unique int id: @ruby_elsif, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_end_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_end_block, index] +ruby_end_block_child( + int ruby_end_block: @ruby_end_block ref, + int index: int ref, + unique int child: @ruby_end_block_child_type ref +); + +ruby_end_block_def( + unique int id: @ruby_end_block, + int loc: @location ref +); + +@ruby_ensure_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_ensure, index] +ruby_ensure_child( + int ruby_ensure: @ruby_ensure ref, + int index: int ref, + unique int child: @ruby_ensure_child_type ref +); + +ruby_ensure_def( + unique int id: @ruby_ensure, + int loc: @location ref +); + +ruby_exception_variable_def( + unique int id: @ruby_exception_variable, + int child: @ruby_underscore_lhs ref, + int loc: @location ref +); + +@ruby_exceptions_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_exceptions, index] +ruby_exceptions_child( + int ruby_exceptions: @ruby_exceptions ref, + int index: int ref, + unique int child: @ruby_exceptions_child_type ref +); + +ruby_exceptions_def( + unique int id: @ruby_exceptions, + int loc: @location ref +); + +@ruby_for_pattern_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +ruby_for_def( + unique int id: @ruby_for, + int body: @ruby_do ref, + int pattern: @ruby_for_pattern_type ref, + int value: @ruby_in ref, + int loc: @location ref +); + +@ruby_hash_child_type = @ruby_hash_splat_argument | @ruby_pair + +#keyset[ruby_hash, index] +ruby_hash_child( + int ruby_hash: @ruby_hash ref, + int index: int ref, + unique int child: @ruby_hash_child_type ref +); + +ruby_hash_def( + unique int id: @ruby_hash, + int loc: @location ref +); + +ruby_hash_splat_argument_def( + unique int id: @ruby_hash_splat_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_hash_splat_parameter_name( + unique int ruby_hash_splat_parameter: @ruby_hash_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_hash_splat_parameter_def( + unique int id: @ruby_hash_splat_parameter, + int loc: @location ref +); + +@ruby_heredoc_body_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_heredoc_content | @ruby_token_heredoc_end + +#keyset[ruby_heredoc_body, index] +ruby_heredoc_body_child( + int ruby_heredoc_body: @ruby_heredoc_body ref, + int index: int ref, + unique int child: @ruby_heredoc_body_child_type ref +); + +ruby_heredoc_body_def( + unique int id: @ruby_heredoc_body, + int loc: @location ref +); + +@ruby_if_alternative_type = @ruby_else | @ruby_elsif + +ruby_if_alternative( + unique int ruby_if: @ruby_if ref, + unique int alternative: @ruby_if_alternative_type ref +); + +ruby_if_consequence( + unique int ruby_if: @ruby_if ref, + unique int consequence: @ruby_then ref +); + +ruby_if_def( + unique int id: @ruby_if, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_if_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_if_modifier_def( + unique int id: @ruby_if_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_if_modifier_condition_type ref, + int loc: @location ref +); + +ruby_in_def( + unique int id: @ruby_in, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_interpolation_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_interpolation, index] +ruby_interpolation_child( + int ruby_interpolation: @ruby_interpolation ref, + int index: int ref, + unique int child: @ruby_interpolation_child_type ref +); + +ruby_interpolation_def( + unique int id: @ruby_interpolation, + int loc: @location ref +); + +ruby_keyword_parameter_value( + unique int ruby_keyword_parameter: @ruby_keyword_parameter ref, + unique int value: @ruby_underscore_arg ref +); + +ruby_keyword_parameter_def( + unique int id: @ruby_keyword_parameter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_lambda_body_type = @ruby_block | @ruby_do_block + +ruby_lambda_parameters( + unique int ruby_lambda: @ruby_lambda ref, + unique int parameters: @ruby_lambda_parameters ref +); + +ruby_lambda_def( + unique int id: @ruby_lambda, + int body: @ruby_lambda_body_type ref, + int loc: @location ref +); + +@ruby_lambda_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_lambda_parameters, index] +ruby_lambda_parameters_child( + int ruby_lambda_parameters: @ruby_lambda_parameters ref, + int index: int ref, + unique int child: @ruby_lambda_parameters_child_type ref +); + +ruby_lambda_parameters_def( + unique int id: @ruby_lambda_parameters, + int loc: @location ref +); + +@ruby_left_assignment_list_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_left_assignment_list, index] +ruby_left_assignment_list_child( + int ruby_left_assignment_list: @ruby_left_assignment_list ref, + int index: int ref, + unique int child: @ruby_left_assignment_list_child_type ref +); + +ruby_left_assignment_list_def( + unique int id: @ruby_left_assignment_list, + int loc: @location ref +); + +ruby_method_parameters( + unique int ruby_method: @ruby_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_method, index] +ruby_method_child( + int ruby_method: @ruby_method ref, + int index: int ref, + unique int child: @ruby_method_child_type ref +); + +ruby_method_def( + unique int id: @ruby_method, + int name: @ruby_underscore_method_name ref, + int loc: @location ref +); + +@ruby_method_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_method_parameters, index] +ruby_method_parameters_child( + int ruby_method_parameters: @ruby_method_parameters ref, + int index: int ref, + unique int child: @ruby_method_parameters_child_type ref +); + +ruby_method_parameters_def( + unique int id: @ruby_method_parameters, + int loc: @location ref +); + +@ruby_module_name_type = @ruby_scope_resolution | @ruby_token_constant + +@ruby_module_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_module, index] +ruby_module_child( + int ruby_module: @ruby_module ref, + int index: int ref, + unique int child: @ruby_module_child_type ref +); + +ruby_module_def( + unique int id: @ruby_module, + int name: @ruby_module_name_type ref, + int loc: @location ref +); + +ruby_next_child( + unique int ruby_next: @ruby_next ref, + unique int child: @ruby_argument_list ref +); + +ruby_next_def( + unique int id: @ruby_next, + int loc: @location ref +); + +case @ruby_operator_assignment.operator of + 0 = @ruby_operator_assignment_percentequal +| 1 = @ruby_operator_assignment_ampersandampersandequal +| 2 = @ruby_operator_assignment_ampersandequal +| 3 = @ruby_operator_assignment_starstarequal +| 4 = @ruby_operator_assignment_starequal +| 5 = @ruby_operator_assignment_plusequal +| 6 = @ruby_operator_assignment_minusequal +| 7 = @ruby_operator_assignment_slashequal +| 8 = @ruby_operator_assignment_langlelangleequal +| 9 = @ruby_operator_assignment_ranglerangleequal +| 10 = @ruby_operator_assignment_caretequal +| 11 = @ruby_operator_assignment_pipeequal +| 12 = @ruby_operator_assignment_pipepipeequal +; + + +@ruby_operator_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_operator_assignment_def( + unique int id: @ruby_operator_assignment, + int left: @ruby_underscore_lhs ref, + int operator: int ref, + int right: @ruby_operator_assignment_right_type ref, + int loc: @location ref +); + +ruby_optional_parameter_def( + unique int id: @ruby_optional_parameter, + int name: @ruby_token_identifier ref, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_pair_key_type = @ruby_string__ | @ruby_token_hash_key_symbol | @ruby_underscore_arg + +ruby_pair_def( + unique int id: @ruby_pair, + int key__: @ruby_pair_key_type ref, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_parenthesized_statements_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_parenthesized_statements, index] +ruby_parenthesized_statements_child( + int ruby_parenthesized_statements: @ruby_parenthesized_statements ref, + int index: int ref, + unique int child: @ruby_parenthesized_statements_child_type ref +); + +ruby_parenthesized_statements_def( + unique int id: @ruby_parenthesized_statements, + int loc: @location ref +); + +@ruby_pattern_child_type = @ruby_splat_argument | @ruby_underscore_arg + +ruby_pattern_def( + unique int id: @ruby_pattern, + int child: @ruby_pattern_child_type ref, + int loc: @location ref +); + +@ruby_program_child_type = @ruby_token_empty_statement | @ruby_token_uninterpreted | @ruby_underscore_statement + +#keyset[ruby_program, index] +ruby_program_child( + int ruby_program: @ruby_program ref, + int index: int ref, + unique int child: @ruby_program_child_type ref +); + +ruby_program_def( + unique int id: @ruby_program, + int loc: @location ref +); + +ruby_range_begin( + unique int ruby_range: @ruby_range ref, + unique int begin: @ruby_underscore_arg ref +); + +ruby_range_end( + unique int ruby_range: @ruby_range ref, + unique int end: @ruby_underscore_arg ref +); + +case @ruby_range.operator of + 0 = @ruby_range_dotdot +| 1 = @ruby_range_dotdotdot +; + + +ruby_range_def( + unique int id: @ruby_range, + int operator: int ref, + int loc: @location ref +); + +@ruby_rational_child_type = @ruby_token_float | @ruby_token_integer + +ruby_rational_def( + unique int id: @ruby_rational, + int child: @ruby_rational_child_type ref, + int loc: @location ref +); + +ruby_redo_child( + unique int ruby_redo: @ruby_redo ref, + unique int child: @ruby_argument_list ref +); + +ruby_redo_def( + unique int id: @ruby_redo, + int loc: @location ref +); + +@ruby_regex_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_regex, index] +ruby_regex_child( + int ruby_regex: @ruby_regex ref, + int index: int ref, + unique int child: @ruby_regex_child_type ref +); + +ruby_regex_def( + unique int id: @ruby_regex, + int loc: @location ref +); + +ruby_rescue_body( + unique int ruby_rescue: @ruby_rescue ref, + unique int body: @ruby_then ref +); + +ruby_rescue_exceptions( + unique int ruby_rescue: @ruby_rescue ref, + unique int exceptions: @ruby_exceptions ref +); + +ruby_rescue_variable( + unique int ruby_rescue: @ruby_rescue ref, + unique int variable: @ruby_exception_variable ref +); + +ruby_rescue_def( + unique int id: @ruby_rescue, + int loc: @location ref +); + +@ruby_rescue_modifier_handler_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_rescue_modifier_def( + unique int id: @ruby_rescue_modifier, + int body: @ruby_underscore_statement ref, + int handler: @ruby_rescue_modifier_handler_type ref, + int loc: @location ref +); + +ruby_rest_assignment_child( + unique int ruby_rest_assignment: @ruby_rest_assignment ref, + unique int child: @ruby_underscore_lhs ref +); + +ruby_rest_assignment_def( + unique int id: @ruby_rest_assignment, + int loc: @location ref +); + +ruby_retry_child( + unique int ruby_retry: @ruby_retry ref, + unique int child: @ruby_argument_list ref +); + +ruby_retry_def( + unique int id: @ruby_retry, + int loc: @location ref +); + +ruby_return_child( + unique int ruby_return: @ruby_return ref, + unique int child: @ruby_argument_list ref +); + +ruby_return_def( + unique int id: @ruby_return, + int loc: @location ref +); + +@ruby_right_assignment_list_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_right_assignment_list, index] +ruby_right_assignment_list_child( + int ruby_right_assignment_list: @ruby_right_assignment_list ref, + int index: int ref, + unique int child: @ruby_right_assignment_list_child_type ref +); + +ruby_right_assignment_list_def( + unique int id: @ruby_right_assignment_list, + int loc: @location ref +); + +@ruby_scope_resolution_name_type = @ruby_token_constant | @ruby_token_identifier + +ruby_scope_resolution_scope( + unique int ruby_scope_resolution: @ruby_scope_resolution ref, + unique int scope: @ruby_underscore_primary ref +); + +ruby_scope_resolution_def( + unique int id: @ruby_scope_resolution, + int name: @ruby_scope_resolution_name_type ref, + int loc: @location ref +); + +ruby_setter_def( + unique int id: @ruby_setter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_singleton_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_class, index] +ruby_singleton_class_child( + int ruby_singleton_class: @ruby_singleton_class ref, + int index: int ref, + unique int child: @ruby_singleton_class_child_type ref +); + +ruby_singleton_class_def( + unique int id: @ruby_singleton_class, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_singleton_method_object_type = @ruby_underscore_arg | @ruby_underscore_variable + +ruby_singleton_method_parameters( + unique int ruby_singleton_method: @ruby_singleton_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_singleton_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_method, index] +ruby_singleton_method_child( + int ruby_singleton_method: @ruby_singleton_method ref, + int index: int ref, + unique int child: @ruby_singleton_method_child_type ref +); + +ruby_singleton_method_def( + unique int id: @ruby_singleton_method, + int name: @ruby_underscore_method_name ref, + int object: @ruby_singleton_method_object_type ref, + int loc: @location ref +); + +ruby_splat_argument_def( + unique int id: @ruby_splat_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_splat_parameter_name( + unique int ruby_splat_parameter: @ruby_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_splat_parameter_def( + unique int id: @ruby_splat_parameter, + int loc: @location ref +); + +@ruby_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_string__, index] +ruby_string_child( + int ruby_string__: @ruby_string__ ref, + int index: int ref, + unique int child: @ruby_string_child_type ref +); + +ruby_string_def( + unique int id: @ruby_string__, + int loc: @location ref +); + +#keyset[ruby_string_array, index] +ruby_string_array_child( + int ruby_string_array: @ruby_string_array ref, + int index: int ref, + unique int child: @ruby_bare_string ref +); + +ruby_string_array_def( + unique int id: @ruby_string_array, + int loc: @location ref +); + +@ruby_subshell_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_subshell, index] +ruby_subshell_child( + int ruby_subshell: @ruby_subshell ref, + int index: int ref, + unique int child: @ruby_subshell_child_type ref +); + +ruby_subshell_def( + unique int id: @ruby_subshell, + int loc: @location ref +); + +@ruby_superclass_child_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_superclass_def( + unique int id: @ruby_superclass, + int child: @ruby_superclass_child_type ref, + int loc: @location ref +); + +#keyset[ruby_symbol_array, index] +ruby_symbol_array_child( + int ruby_symbol_array: @ruby_symbol_array ref, + int index: int ref, + unique int child: @ruby_bare_symbol ref +); + +ruby_symbol_array_def( + unique int id: @ruby_symbol_array, + int loc: @location ref +); + +@ruby_then_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_then, index] +ruby_then_child( + int ruby_then: @ruby_then ref, + int index: int ref, + unique int child: @ruby_then_child_type ref +); + +ruby_then_def( + unique int id: @ruby_then, + int loc: @location ref +); + +@ruby_unary_operand_type = @ruby_break | @ruby_call | @ruby_next | @ruby_parenthesized_statements | @ruby_return | @ruby_token_float | @ruby_token_integer | @ruby_underscore_arg | @ruby_yield + +case @ruby_unary.operator of + 0 = @ruby_unary_bang +| 1 = @ruby_unary_plus +| 2 = @ruby_unary_minus +| 3 = @ruby_unary_definedquestion +| 4 = @ruby_unary_not +| 5 = @ruby_unary_tilde +; + + +ruby_unary_def( + unique int id: @ruby_unary, + int operand: @ruby_unary_operand_type ref, + int operator: int ref, + int loc: @location ref +); + +#keyset[ruby_undef, index] +ruby_undef_child( + int ruby_undef: @ruby_undef ref, + int index: int ref, + unique int child: @ruby_underscore_method_name ref +); + +ruby_undef_def( + unique int id: @ruby_undef, + int loc: @location ref +); + +@ruby_unless_alternative_type = @ruby_else | @ruby_elsif + +ruby_unless_alternative( + unique int ruby_unless: @ruby_unless ref, + unique int alternative: @ruby_unless_alternative_type ref +); + +ruby_unless_consequence( + unique int ruby_unless: @ruby_unless ref, + unique int consequence: @ruby_then ref +); + +ruby_unless_def( + unique int id: @ruby_unless, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_unless_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_unless_modifier_def( + unique int id: @ruby_unless_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_unless_modifier_condition_type ref, + int loc: @location ref +); + +ruby_until_def( + unique int id: @ruby_until, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_until_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_until_modifier_def( + unique int id: @ruby_until_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_until_modifier_condition_type ref, + int loc: @location ref +); + +ruby_when_body( + unique int ruby_when: @ruby_when ref, + unique int body: @ruby_then ref +); + +#keyset[ruby_when, index] +ruby_when_pattern( + int ruby_when: @ruby_when ref, + int index: int ref, + unique int pattern: @ruby_pattern ref +); + +ruby_when_def( + unique int id: @ruby_when, + int loc: @location ref +); + +ruby_while_def( + unique int id: @ruby_while, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_while_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_while_modifier_def( + unique int id: @ruby_while_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_while_modifier_condition_type ref, + int loc: @location ref +); + +ruby_yield_child( + unique int ruby_yield: @ruby_yield ref, + unique int child: @ruby_argument_list ref +); + +ruby_yield_def( + unique int id: @ruby_yield, + int loc: @location ref +); + +ruby_tokeninfo( + unique int id: @ruby_token, + int kind: int ref, + int file: @file ref, + int idx: int ref, + string value: string ref, + int loc: @location ref +); + +case @ruby_token.kind of + 0 = @ruby_reserved_word +| 1 = @ruby_token_character +| 2 = @ruby_token_class_variable +| 3 = @ruby_token_comment +| 4 = @ruby_token_complex +| 5 = @ruby_token_constant +| 6 = @ruby_token_empty_statement +| 7 = @ruby_token_escape_sequence +| 8 = @ruby_token_false +| 9 = @ruby_token_float +| 10 = @ruby_token_global_variable +| 11 = @ruby_token_hash_key_symbol +| 12 = @ruby_token_heredoc_beginning +| 13 = @ruby_token_heredoc_content +| 14 = @ruby_token_heredoc_end +| 15 = @ruby_token_identifier +| 16 = @ruby_token_instance_variable +| 17 = @ruby_token_integer +| 18 = @ruby_token_nil +| 19 = @ruby_token_operator +| 20 = @ruby_token_self +| 21 = @ruby_token_simple_symbol +| 22 = @ruby_token_string_content +| 23 = @ruby_token_super +| 24 = @ruby_token_true +| 25 = @ruby_token_uninterpreted +; + + +@ruby_ast_node = @ruby_alias | @ruby_argument_list | @ruby_array | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_parameter | @ruby_block_parameters | @ruby_break | @ruby_call | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_for | @ruby_hash | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_modifier | @ruby_in | @ruby_interpolation | @ruby_keyword_parameter | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield + +@ruby_ast_node_parent = @file | @ruby_ast_node + +#keyset[parent, parent_index] +ruby_ast_node_parent( + int child: @ruby_ast_node ref, + int parent: @ruby_ast_node_parent ref, + int parent_index: int ref +); + +erb_comment_directive_def( + unique int id: @erb_comment_directive, + int child: @erb_token_comment ref, + int loc: @location ref +); + +erb_directive_def( + unique int id: @erb_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +erb_graphql_directive_def( + unique int id: @erb_graphql_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +erb_output_directive_def( + unique int id: @erb_output_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +@erb_template_child_type = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_token_content + +#keyset[erb_template, index] +erb_template_child( + int erb_template: @erb_template ref, + int index: int ref, + unique int child: @erb_template_child_type ref +); + +erb_template_def( + unique int id: @erb_template, + int loc: @location ref +); + +erb_tokeninfo( + unique int id: @erb_token, + int kind: int ref, + int file: @file ref, + int idx: int ref, + string value: string ref, + int loc: @location ref +); + +case @erb_token.kind of + 0 = @erb_reserved_word +| 1 = @erb_token_code +| 2 = @erb_token_comment +| 3 = @erb_token_content +; + + +@erb_ast_node = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_template | @erb_token + +@erb_ast_node_parent = @erb_ast_node | @file + +#keyset[parent, parent_index] +erb_ast_node_parent( + int child: @erb_ast_node ref, + int parent: @erb_ast_node_parent ref, + int parent_index: int ref +); + diff --git a/ruby/ql/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/ruby.dbscheme b/ruby/ql/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/ruby.dbscheme new file mode 100644 index 00000000000..b5aef9c93ae --- /dev/null +++ b/ruby/ql/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/ruby.dbscheme @@ -0,0 +1,1320 @@ +// CodeQL database schema for Ruby +// Automatically generated from the tree-sitter grammar; do not edit + +@location = @location_default + +locations_default( + unique int id: @location_default, + int file: @file ref, + int start_line: int ref, + int start_column: int ref, + int end_line: int ref, + int end_column: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +sourceLocationPrefix( + string prefix: string ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +case @diagnostic.severity of + 10 = @diagnostic_debug +| 20 = @diagnostic_info +| 30 = @diagnostic_warning +| 40 = @diagnostic_error +; + + +@ruby_underscore_arg = @ruby_assignment | @ruby_binary | @ruby_conditional | @ruby_operator_assignment | @ruby_range | @ruby_unary | @ruby_underscore_primary + +@ruby_underscore_lhs = @ruby_call | @ruby_element_reference | @ruby_scope_resolution | @ruby_token_false | @ruby_token_nil | @ruby_token_true | @ruby_underscore_variable + +@ruby_underscore_method_name = @ruby_delimited_symbol | @ruby_setter | @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_operator | @ruby_token_simple_symbol + +@ruby_underscore_primary = @ruby_array | @ruby_begin | @ruby_break | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_delimited_symbol | @ruby_for | @ruby_hash | @ruby_if | @ruby_lambda | @ruby_method | @ruby_module | @ruby_next | @ruby_parenthesized_statements | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_retry | @ruby_return | @ruby_singleton_class | @ruby_singleton_method | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_symbol_array | @ruby_token_character | @ruby_token_complex | @ruby_token_float | @ruby_token_heredoc_beginning | @ruby_token_integer | @ruby_token_simple_symbol | @ruby_unary | @ruby_underscore_lhs | @ruby_unless | @ruby_until | @ruby_while | @ruby_yield + +@ruby_underscore_statement = @ruby_alias | @ruby_assignment | @ruby_begin_block | @ruby_binary | @ruby_break | @ruby_call | @ruby_end_block | @ruby_if_modifier | @ruby_next | @ruby_operator_assignment | @ruby_rescue_modifier | @ruby_return | @ruby_unary | @ruby_undef | @ruby_underscore_arg | @ruby_unless_modifier | @ruby_until_modifier | @ruby_while_modifier | @ruby_yield + +@ruby_underscore_variable = @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_self | @ruby_token_super + +ruby_alias_def( + unique int id: @ruby_alias, + int alias: @ruby_underscore_method_name ref, + int name: @ruby_underscore_method_name ref, + int loc: @location ref +); + +@ruby_argument_list_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_argument_list, index] +ruby_argument_list_child( + int ruby_argument_list: @ruby_argument_list ref, + int index: int ref, + unique int child: @ruby_argument_list_child_type ref +); + +ruby_argument_list_def( + unique int id: @ruby_argument_list, + int loc: @location ref +); + +@ruby_array_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_array, index] +ruby_array_child( + int ruby_array: @ruby_array ref, + int index: int ref, + unique int child: @ruby_array_child_type ref +); + +ruby_array_def( + unique int id: @ruby_array, + int loc: @location ref +); + +@ruby_assignment_left_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +@ruby_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_right_assignment_list | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +ruby_assignment_def( + unique int id: @ruby_assignment, + int left: @ruby_assignment_left_type ref, + int right: @ruby_assignment_right_type ref, + int loc: @location ref +); + +@ruby_bare_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_string, index] +ruby_bare_string_child( + int ruby_bare_string: @ruby_bare_string ref, + int index: int ref, + unique int child: @ruby_bare_string_child_type ref +); + +ruby_bare_string_def( + unique int id: @ruby_bare_string, + int loc: @location ref +); + +@ruby_bare_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_symbol, index] +ruby_bare_symbol_child( + int ruby_bare_symbol: @ruby_bare_symbol ref, + int index: int ref, + unique int child: @ruby_bare_symbol_child_type ref +); + +ruby_bare_symbol_def( + unique int id: @ruby_bare_symbol, + int loc: @location ref +); + +@ruby_begin_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin, index] +ruby_begin_child( + int ruby_begin: @ruby_begin ref, + int index: int ref, + unique int child: @ruby_begin_child_type ref +); + +ruby_begin_def( + unique int id: @ruby_begin, + int loc: @location ref +); + +@ruby_begin_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin_block, index] +ruby_begin_block_child( + int ruby_begin_block: @ruby_begin_block ref, + int index: int ref, + unique int child: @ruby_begin_block_child_type ref +); + +ruby_begin_block_def( + unique int id: @ruby_begin_block, + int loc: @location ref +); + +@ruby_binary_left_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +case @ruby_binary.operator of + 0 = @ruby_binary_bangequal +| 1 = @ruby_binary_bangtilde +| 2 = @ruby_binary_percent +| 3 = @ruby_binary_ampersand +| 4 = @ruby_binary_ampersandampersand +| 5 = @ruby_binary_star +| 6 = @ruby_binary_starstar +| 7 = @ruby_binary_plus +| 8 = @ruby_binary_minus +| 9 = @ruby_binary_slash +| 10 = @ruby_binary_langle +| 11 = @ruby_binary_langlelangle +| 12 = @ruby_binary_langleequal +| 13 = @ruby_binary_langleequalrangle +| 14 = @ruby_binary_equalequal +| 15 = @ruby_binary_equalequalequal +| 16 = @ruby_binary_equaltilde +| 17 = @ruby_binary_rangle +| 18 = @ruby_binary_rangleequal +| 19 = @ruby_binary_ranglerangle +| 20 = @ruby_binary_caret +| 21 = @ruby_binary_and +| 22 = @ruby_binary_or +| 23 = @ruby_binary_pipe +| 24 = @ruby_binary_pipepipe +; + + +@ruby_binary_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_binary_def( + unique int id: @ruby_binary, + int left: @ruby_binary_left_type ref, + int operator: int ref, + int right: @ruby_binary_right_type ref, + int loc: @location ref +); + +ruby_block_parameters( + unique int ruby_block: @ruby_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_block, index] +ruby_block_child( + int ruby_block: @ruby_block ref, + int index: int ref, + unique int child: @ruby_block_child_type ref +); + +ruby_block_def( + unique int id: @ruby_block, + int loc: @location ref +); + +ruby_block_argument_def( + unique int id: @ruby_block_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_block_parameter_def( + unique int id: @ruby_block_parameter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_block_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_block_parameters, index] +ruby_block_parameters_child( + int ruby_block_parameters: @ruby_block_parameters ref, + int index: int ref, + unique int child: @ruby_block_parameters_child_type ref +); + +ruby_block_parameters_def( + unique int id: @ruby_block_parameters, + int loc: @location ref +); + +ruby_break_child( + unique int ruby_break: @ruby_break ref, + unique int child: @ruby_argument_list ref +); + +ruby_break_def( + unique int id: @ruby_break, + int loc: @location ref +); + +ruby_call_arguments( + unique int ruby_call: @ruby_call ref, + unique int arguments: @ruby_argument_list ref +); + +@ruby_call_block_type = @ruby_block | @ruby_do_block + +ruby_call_block( + unique int ruby_call: @ruby_call ref, + unique int block: @ruby_call_block_type ref +); + +@ruby_call_method_type = @ruby_argument_list | @ruby_scope_resolution | @ruby_token_operator | @ruby_underscore_variable + +@ruby_call_receiver_type = @ruby_call | @ruby_underscore_primary + +ruby_call_receiver( + unique int ruby_call: @ruby_call ref, + unique int receiver: @ruby_call_receiver_type ref +); + +ruby_call_def( + unique int id: @ruby_call, + int method: @ruby_call_method_type ref, + int loc: @location ref +); + +ruby_case_value( + unique int ruby_case__: @ruby_case__ ref, + unique int value: @ruby_underscore_statement ref +); + +@ruby_case_child_type = @ruby_else | @ruby_when + +#keyset[ruby_case__, index] +ruby_case_child( + int ruby_case__: @ruby_case__ ref, + int index: int ref, + unique int child: @ruby_case_child_type ref +); + +ruby_case_def( + unique int id: @ruby_case__, + int loc: @location ref +); + +#keyset[ruby_chained_string, index] +ruby_chained_string_child( + int ruby_chained_string: @ruby_chained_string ref, + int index: int ref, + unique int child: @ruby_string__ ref +); + +ruby_chained_string_def( + unique int id: @ruby_chained_string, + int loc: @location ref +); + +@ruby_class_name_type = @ruby_scope_resolution | @ruby_token_constant + +ruby_class_superclass( + unique int ruby_class: @ruby_class ref, + unique int superclass: @ruby_superclass ref +); + +@ruby_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_class, index] +ruby_class_child( + int ruby_class: @ruby_class ref, + int index: int ref, + unique int child: @ruby_class_child_type ref +); + +ruby_class_def( + unique int id: @ruby_class, + int name: @ruby_class_name_type ref, + int loc: @location ref +); + +ruby_conditional_def( + unique int id: @ruby_conditional, + int alternative: @ruby_underscore_arg ref, + int condition: @ruby_underscore_arg ref, + int consequence: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_delimited_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_delimited_symbol, index] +ruby_delimited_symbol_child( + int ruby_delimited_symbol: @ruby_delimited_symbol ref, + int index: int ref, + unique int child: @ruby_delimited_symbol_child_type ref +); + +ruby_delimited_symbol_def( + unique int id: @ruby_delimited_symbol, + int loc: @location ref +); + +@ruby_destructured_left_assignment_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_destructured_left_assignment, index] +ruby_destructured_left_assignment_child( + int ruby_destructured_left_assignment: @ruby_destructured_left_assignment ref, + int index: int ref, + unique int child: @ruby_destructured_left_assignment_child_type ref +); + +ruby_destructured_left_assignment_def( + unique int id: @ruby_destructured_left_assignment, + int loc: @location ref +); + +@ruby_destructured_parameter_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_destructured_parameter, index] +ruby_destructured_parameter_child( + int ruby_destructured_parameter: @ruby_destructured_parameter ref, + int index: int ref, + unique int child: @ruby_destructured_parameter_child_type ref +); + +ruby_destructured_parameter_def( + unique int id: @ruby_destructured_parameter, + int loc: @location ref +); + +@ruby_do_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do, index] +ruby_do_child( + int ruby_do: @ruby_do ref, + int index: int ref, + unique int child: @ruby_do_child_type ref +); + +ruby_do_def( + unique int id: @ruby_do, + int loc: @location ref +); + +ruby_do_block_parameters( + unique int ruby_do_block: @ruby_do_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_do_block_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do_block, index] +ruby_do_block_child( + int ruby_do_block: @ruby_do_block ref, + int index: int ref, + unique int child: @ruby_do_block_child_type ref +); + +ruby_do_block_def( + unique int id: @ruby_do_block, + int loc: @location ref +); + +@ruby_element_reference_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_element_reference, index] +ruby_element_reference_child( + int ruby_element_reference: @ruby_element_reference ref, + int index: int ref, + unique int child: @ruby_element_reference_child_type ref +); + +ruby_element_reference_def( + unique int id: @ruby_element_reference, + int object: @ruby_underscore_primary ref, + int loc: @location ref +); + +@ruby_else_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_else, index] +ruby_else_child( + int ruby_else: @ruby_else ref, + int index: int ref, + unique int child: @ruby_else_child_type ref +); + +ruby_else_def( + unique int id: @ruby_else, + int loc: @location ref +); + +@ruby_elsif_alternative_type = @ruby_else | @ruby_elsif + +ruby_elsif_alternative( + unique int ruby_elsif: @ruby_elsif ref, + unique int alternative: @ruby_elsif_alternative_type ref +); + +ruby_elsif_consequence( + unique int ruby_elsif: @ruby_elsif ref, + unique int consequence: @ruby_then ref +); + +ruby_elsif_def( + unique int id: @ruby_elsif, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_end_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_end_block, index] +ruby_end_block_child( + int ruby_end_block: @ruby_end_block ref, + int index: int ref, + unique int child: @ruby_end_block_child_type ref +); + +ruby_end_block_def( + unique int id: @ruby_end_block, + int loc: @location ref +); + +@ruby_ensure_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_ensure, index] +ruby_ensure_child( + int ruby_ensure: @ruby_ensure ref, + int index: int ref, + unique int child: @ruby_ensure_child_type ref +); + +ruby_ensure_def( + unique int id: @ruby_ensure, + int loc: @location ref +); + +ruby_exception_variable_def( + unique int id: @ruby_exception_variable, + int child: @ruby_underscore_lhs ref, + int loc: @location ref +); + +@ruby_exceptions_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_exceptions, index] +ruby_exceptions_child( + int ruby_exceptions: @ruby_exceptions ref, + int index: int ref, + unique int child: @ruby_exceptions_child_type ref +); + +ruby_exceptions_def( + unique int id: @ruby_exceptions, + int loc: @location ref +); + +@ruby_for_pattern_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +ruby_for_def( + unique int id: @ruby_for, + int body: @ruby_do ref, + int pattern: @ruby_for_pattern_type ref, + int value: @ruby_in ref, + int loc: @location ref +); + +@ruby_hash_child_type = @ruby_hash_splat_argument | @ruby_pair + +#keyset[ruby_hash, index] +ruby_hash_child( + int ruby_hash: @ruby_hash ref, + int index: int ref, + unique int child: @ruby_hash_child_type ref +); + +ruby_hash_def( + unique int id: @ruby_hash, + int loc: @location ref +); + +ruby_hash_splat_argument_def( + unique int id: @ruby_hash_splat_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_hash_splat_parameter_name( + unique int ruby_hash_splat_parameter: @ruby_hash_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_hash_splat_parameter_def( + unique int id: @ruby_hash_splat_parameter, + int loc: @location ref +); + +@ruby_heredoc_body_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_heredoc_content | @ruby_token_heredoc_end + +#keyset[ruby_heredoc_body, index] +ruby_heredoc_body_child( + int ruby_heredoc_body: @ruby_heredoc_body ref, + int index: int ref, + unique int child: @ruby_heredoc_body_child_type ref +); + +ruby_heredoc_body_def( + unique int id: @ruby_heredoc_body, + int loc: @location ref +); + +@ruby_if_alternative_type = @ruby_else | @ruby_elsif + +ruby_if_alternative( + unique int ruby_if: @ruby_if ref, + unique int alternative: @ruby_if_alternative_type ref +); + +ruby_if_consequence( + unique int ruby_if: @ruby_if ref, + unique int consequence: @ruby_then ref +); + +ruby_if_def( + unique int id: @ruby_if, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_if_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_if_modifier_def( + unique int id: @ruby_if_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_if_modifier_condition_type ref, + int loc: @location ref +); + +ruby_in_def( + unique int id: @ruby_in, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_interpolation_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_interpolation, index] +ruby_interpolation_child( + int ruby_interpolation: @ruby_interpolation ref, + int index: int ref, + unique int child: @ruby_interpolation_child_type ref +); + +ruby_interpolation_def( + unique int id: @ruby_interpolation, + int loc: @location ref +); + +ruby_keyword_parameter_value( + unique int ruby_keyword_parameter: @ruby_keyword_parameter ref, + unique int value: @ruby_underscore_arg ref +); + +ruby_keyword_parameter_def( + unique int id: @ruby_keyword_parameter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_lambda_body_type = @ruby_block | @ruby_do_block + +ruby_lambda_parameters( + unique int ruby_lambda: @ruby_lambda ref, + unique int parameters: @ruby_lambda_parameters ref +); + +ruby_lambda_def( + unique int id: @ruby_lambda, + int body: @ruby_lambda_body_type ref, + int loc: @location ref +); + +@ruby_lambda_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_lambda_parameters, index] +ruby_lambda_parameters_child( + int ruby_lambda_parameters: @ruby_lambda_parameters ref, + int index: int ref, + unique int child: @ruby_lambda_parameters_child_type ref +); + +ruby_lambda_parameters_def( + unique int id: @ruby_lambda_parameters, + int loc: @location ref +); + +@ruby_left_assignment_list_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_left_assignment_list, index] +ruby_left_assignment_list_child( + int ruby_left_assignment_list: @ruby_left_assignment_list ref, + int index: int ref, + unique int child: @ruby_left_assignment_list_child_type ref +); + +ruby_left_assignment_list_def( + unique int id: @ruby_left_assignment_list, + int loc: @location ref +); + +ruby_method_parameters( + unique int ruby_method: @ruby_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_method, index] +ruby_method_child( + int ruby_method: @ruby_method ref, + int index: int ref, + unique int child: @ruby_method_child_type ref +); + +ruby_method_def( + unique int id: @ruby_method, + int name: @ruby_underscore_method_name ref, + int loc: @location ref +); + +@ruby_method_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_method_parameters, index] +ruby_method_parameters_child( + int ruby_method_parameters: @ruby_method_parameters ref, + int index: int ref, + unique int child: @ruby_method_parameters_child_type ref +); + +ruby_method_parameters_def( + unique int id: @ruby_method_parameters, + int loc: @location ref +); + +@ruby_module_name_type = @ruby_scope_resolution | @ruby_token_constant + +@ruby_module_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_module, index] +ruby_module_child( + int ruby_module: @ruby_module ref, + int index: int ref, + unique int child: @ruby_module_child_type ref +); + +ruby_module_def( + unique int id: @ruby_module, + int name: @ruby_module_name_type ref, + int loc: @location ref +); + +ruby_next_child( + unique int ruby_next: @ruby_next ref, + unique int child: @ruby_argument_list ref +); + +ruby_next_def( + unique int id: @ruby_next, + int loc: @location ref +); + +case @ruby_operator_assignment.operator of + 0 = @ruby_operator_assignment_percentequal +| 1 = @ruby_operator_assignment_ampersandampersandequal +| 2 = @ruby_operator_assignment_ampersandequal +| 3 = @ruby_operator_assignment_starstarequal +| 4 = @ruby_operator_assignment_starequal +| 5 = @ruby_operator_assignment_plusequal +| 6 = @ruby_operator_assignment_minusequal +| 7 = @ruby_operator_assignment_slashequal +| 8 = @ruby_operator_assignment_langlelangleequal +| 9 = @ruby_operator_assignment_ranglerangleequal +| 10 = @ruby_operator_assignment_caretequal +| 11 = @ruby_operator_assignment_pipeequal +| 12 = @ruby_operator_assignment_pipepipeequal +; + + +@ruby_operator_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_operator_assignment_def( + unique int id: @ruby_operator_assignment, + int left: @ruby_underscore_lhs ref, + int operator: int ref, + int right: @ruby_operator_assignment_right_type ref, + int loc: @location ref +); + +ruby_optional_parameter_def( + unique int id: @ruby_optional_parameter, + int name: @ruby_token_identifier ref, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_pair_key_type = @ruby_string__ | @ruby_token_hash_key_symbol | @ruby_underscore_arg + +ruby_pair_def( + unique int id: @ruby_pair, + int key__: @ruby_pair_key_type ref, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_parenthesized_statements_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_parenthesized_statements, index] +ruby_parenthesized_statements_child( + int ruby_parenthesized_statements: @ruby_parenthesized_statements ref, + int index: int ref, + unique int child: @ruby_parenthesized_statements_child_type ref +); + +ruby_parenthesized_statements_def( + unique int id: @ruby_parenthesized_statements, + int loc: @location ref +); + +@ruby_pattern_child_type = @ruby_splat_argument | @ruby_underscore_arg + +ruby_pattern_def( + unique int id: @ruby_pattern, + int child: @ruby_pattern_child_type ref, + int loc: @location ref +); + +@ruby_program_child_type = @ruby_token_empty_statement | @ruby_token_uninterpreted | @ruby_underscore_statement + +#keyset[ruby_program, index] +ruby_program_child( + int ruby_program: @ruby_program ref, + int index: int ref, + unique int child: @ruby_program_child_type ref +); + +ruby_program_def( + unique int id: @ruby_program, + int loc: @location ref +); + +ruby_range_begin( + unique int ruby_range: @ruby_range ref, + unique int begin: @ruby_underscore_arg ref +); + +ruby_range_end( + unique int ruby_range: @ruby_range ref, + unique int end: @ruby_underscore_arg ref +); + +case @ruby_range.operator of + 0 = @ruby_range_dotdot +| 1 = @ruby_range_dotdotdot +; + + +ruby_range_def( + unique int id: @ruby_range, + int operator: int ref, + int loc: @location ref +); + +@ruby_rational_child_type = @ruby_token_float | @ruby_token_integer + +ruby_rational_def( + unique int id: @ruby_rational, + int child: @ruby_rational_child_type ref, + int loc: @location ref +); + +ruby_redo_child( + unique int ruby_redo: @ruby_redo ref, + unique int child: @ruby_argument_list ref +); + +ruby_redo_def( + unique int id: @ruby_redo, + int loc: @location ref +); + +@ruby_regex_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_regex, index] +ruby_regex_child( + int ruby_regex: @ruby_regex ref, + int index: int ref, + unique int child: @ruby_regex_child_type ref +); + +ruby_regex_def( + unique int id: @ruby_regex, + int loc: @location ref +); + +ruby_rescue_body( + unique int ruby_rescue: @ruby_rescue ref, + unique int body: @ruby_then ref +); + +ruby_rescue_exceptions( + unique int ruby_rescue: @ruby_rescue ref, + unique int exceptions: @ruby_exceptions ref +); + +ruby_rescue_variable( + unique int ruby_rescue: @ruby_rescue ref, + unique int variable: @ruby_exception_variable ref +); + +ruby_rescue_def( + unique int id: @ruby_rescue, + int loc: @location ref +); + +@ruby_rescue_modifier_handler_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_rescue_modifier_def( + unique int id: @ruby_rescue_modifier, + int body: @ruby_underscore_statement ref, + int handler: @ruby_rescue_modifier_handler_type ref, + int loc: @location ref +); + +ruby_rest_assignment_child( + unique int ruby_rest_assignment: @ruby_rest_assignment ref, + unique int child: @ruby_underscore_lhs ref +); + +ruby_rest_assignment_def( + unique int id: @ruby_rest_assignment, + int loc: @location ref +); + +ruby_retry_child( + unique int ruby_retry: @ruby_retry ref, + unique int child: @ruby_argument_list ref +); + +ruby_retry_def( + unique int id: @ruby_retry, + int loc: @location ref +); + +ruby_return_child( + unique int ruby_return: @ruby_return ref, + unique int child: @ruby_argument_list ref +); + +ruby_return_def( + unique int id: @ruby_return, + int loc: @location ref +); + +@ruby_right_assignment_list_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_right_assignment_list, index] +ruby_right_assignment_list_child( + int ruby_right_assignment_list: @ruby_right_assignment_list ref, + int index: int ref, + unique int child: @ruby_right_assignment_list_child_type ref +); + +ruby_right_assignment_list_def( + unique int id: @ruby_right_assignment_list, + int loc: @location ref +); + +@ruby_scope_resolution_name_type = @ruby_token_constant | @ruby_token_identifier + +ruby_scope_resolution_scope( + unique int ruby_scope_resolution: @ruby_scope_resolution ref, + unique int scope: @ruby_underscore_primary ref +); + +ruby_scope_resolution_def( + unique int id: @ruby_scope_resolution, + int name: @ruby_scope_resolution_name_type ref, + int loc: @location ref +); + +ruby_setter_def( + unique int id: @ruby_setter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_singleton_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_class, index] +ruby_singleton_class_child( + int ruby_singleton_class: @ruby_singleton_class ref, + int index: int ref, + unique int child: @ruby_singleton_class_child_type ref +); + +ruby_singleton_class_def( + unique int id: @ruby_singleton_class, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_singleton_method_object_type = @ruby_underscore_arg | @ruby_underscore_variable + +ruby_singleton_method_parameters( + unique int ruby_singleton_method: @ruby_singleton_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_singleton_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_method, index] +ruby_singleton_method_child( + int ruby_singleton_method: @ruby_singleton_method ref, + int index: int ref, + unique int child: @ruby_singleton_method_child_type ref +); + +ruby_singleton_method_def( + unique int id: @ruby_singleton_method, + int name: @ruby_underscore_method_name ref, + int object: @ruby_singleton_method_object_type ref, + int loc: @location ref +); + +ruby_splat_argument_def( + unique int id: @ruby_splat_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_splat_parameter_name( + unique int ruby_splat_parameter: @ruby_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_splat_parameter_def( + unique int id: @ruby_splat_parameter, + int loc: @location ref +); + +@ruby_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_string__, index] +ruby_string_child( + int ruby_string__: @ruby_string__ ref, + int index: int ref, + unique int child: @ruby_string_child_type ref +); + +ruby_string_def( + unique int id: @ruby_string__, + int loc: @location ref +); + +#keyset[ruby_string_array, index] +ruby_string_array_child( + int ruby_string_array: @ruby_string_array ref, + int index: int ref, + unique int child: @ruby_bare_string ref +); + +ruby_string_array_def( + unique int id: @ruby_string_array, + int loc: @location ref +); + +@ruby_subshell_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_subshell, index] +ruby_subshell_child( + int ruby_subshell: @ruby_subshell ref, + int index: int ref, + unique int child: @ruby_subshell_child_type ref +); + +ruby_subshell_def( + unique int id: @ruby_subshell, + int loc: @location ref +); + +@ruby_superclass_child_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_superclass_def( + unique int id: @ruby_superclass, + int child: @ruby_superclass_child_type ref, + int loc: @location ref +); + +#keyset[ruby_symbol_array, index] +ruby_symbol_array_child( + int ruby_symbol_array: @ruby_symbol_array ref, + int index: int ref, + unique int child: @ruby_bare_symbol ref +); + +ruby_symbol_array_def( + unique int id: @ruby_symbol_array, + int loc: @location ref +); + +@ruby_then_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_then, index] +ruby_then_child( + int ruby_then: @ruby_then ref, + int index: int ref, + unique int child: @ruby_then_child_type ref +); + +ruby_then_def( + unique int id: @ruby_then, + int loc: @location ref +); + +@ruby_unary_operand_type = @ruby_break | @ruby_call | @ruby_next | @ruby_parenthesized_statements | @ruby_return | @ruby_token_float | @ruby_token_integer | @ruby_underscore_arg | @ruby_yield + +case @ruby_unary.operator of + 0 = @ruby_unary_bang +| 1 = @ruby_unary_plus +| 2 = @ruby_unary_minus +| 3 = @ruby_unary_definedquestion +| 4 = @ruby_unary_not +| 5 = @ruby_unary_tilde +; + + +ruby_unary_def( + unique int id: @ruby_unary, + int operand: @ruby_unary_operand_type ref, + int operator: int ref, + int loc: @location ref +); + +#keyset[ruby_undef, index] +ruby_undef_child( + int ruby_undef: @ruby_undef ref, + int index: int ref, + unique int child: @ruby_underscore_method_name ref +); + +ruby_undef_def( + unique int id: @ruby_undef, + int loc: @location ref +); + +@ruby_unless_alternative_type = @ruby_else | @ruby_elsif + +ruby_unless_alternative( + unique int ruby_unless: @ruby_unless ref, + unique int alternative: @ruby_unless_alternative_type ref +); + +ruby_unless_consequence( + unique int ruby_unless: @ruby_unless ref, + unique int consequence: @ruby_then ref +); + +ruby_unless_def( + unique int id: @ruby_unless, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_unless_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_unless_modifier_def( + unique int id: @ruby_unless_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_unless_modifier_condition_type ref, + int loc: @location ref +); + +ruby_until_def( + unique int id: @ruby_until, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_until_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_until_modifier_def( + unique int id: @ruby_until_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_until_modifier_condition_type ref, + int loc: @location ref +); + +ruby_when_body( + unique int ruby_when: @ruby_when ref, + unique int body: @ruby_then ref +); + +#keyset[ruby_when, index] +ruby_when_pattern( + int ruby_when: @ruby_when ref, + int index: int ref, + unique int pattern: @ruby_pattern ref +); + +ruby_when_def( + unique int id: @ruby_when, + int loc: @location ref +); + +ruby_while_def( + unique int id: @ruby_while, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_while_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_while_modifier_def( + unique int id: @ruby_while_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_while_modifier_condition_type ref, + int loc: @location ref +); + +ruby_yield_child( + unique int ruby_yield: @ruby_yield ref, + unique int child: @ruby_argument_list ref +); + +ruby_yield_def( + unique int id: @ruby_yield, + int loc: @location ref +); + +ruby_tokeninfo( + unique int id: @ruby_token, + int kind: int ref, + int file: @file ref, + int idx: int ref, + string value: string ref, + int loc: @location ref +); + +case @ruby_token.kind of + 0 = @ruby_reserved_word +| 1 = @ruby_token_character +| 2 = @ruby_token_class_variable +| 3 = @ruby_token_comment +| 4 = @ruby_token_complex +| 5 = @ruby_token_constant +| 6 = @ruby_token_empty_statement +| 7 = @ruby_token_escape_sequence +| 8 = @ruby_token_false +| 9 = @ruby_token_float +| 10 = @ruby_token_global_variable +| 11 = @ruby_token_hash_key_symbol +| 12 = @ruby_token_heredoc_beginning +| 13 = @ruby_token_heredoc_content +| 14 = @ruby_token_heredoc_end +| 15 = @ruby_token_identifier +| 16 = @ruby_token_instance_variable +| 17 = @ruby_token_integer +| 18 = @ruby_token_nil +| 19 = @ruby_token_operator +| 20 = @ruby_token_self +| 21 = @ruby_token_simple_symbol +| 22 = @ruby_token_string_content +| 23 = @ruby_token_super +| 24 = @ruby_token_true +| 25 = @ruby_token_uninterpreted +; + + +@ruby_ast_node = @ruby_alias | @ruby_argument_list | @ruby_array | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_parameter | @ruby_block_parameters | @ruby_break | @ruby_call | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_for | @ruby_hash | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_modifier | @ruby_in | @ruby_interpolation | @ruby_keyword_parameter | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield + +@ruby_ast_node_parent = @file | @ruby_ast_node + +#keyset[parent, parent_index] +ruby_ast_node_parent( + int child: @ruby_ast_node ref, + int parent: @ruby_ast_node_parent ref, + int parent_index: int ref +); + +erb_comment_directive_def( + unique int id: @erb_comment_directive, + int child: @erb_token_comment ref, + int loc: @location ref +); + +erb_directive_def( + unique int id: @erb_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +erb_graphql_directive_def( + unique int id: @erb_graphql_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +erb_output_directive_def( + unique int id: @erb_output_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +@erb_template_child_type = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_token_content + +#keyset[erb_template, index] +erb_template_child( + int erb_template: @erb_template ref, + int index: int ref, + unique int child: @erb_template_child_type ref +); + +erb_template_def( + unique int id: @erb_template, + int loc: @location ref +); + +erb_tokeninfo( + unique int id: @erb_token, + int kind: int ref, + int file: @file ref, + int idx: int ref, + string value: string ref, + int loc: @location ref +); + +case @erb_token.kind of + 0 = @erb_reserved_word +| 1 = @erb_token_code +| 2 = @erb_token_comment +| 3 = @erb_token_content +; + + +@erb_ast_node = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_template | @erb_token + +@erb_ast_node_parent = @erb_ast_node | @file + +#keyset[parent, parent_index] +erb_ast_node_parent( + int child: @erb_ast_node ref, + int parent: @erb_ast_node_parent ref, + int parent_index: int ref +); + diff --git a/ruby/ql/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/upgrade.properties b/ruby/ql/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/upgrade.properties new file mode 100644 index 00000000000..c10884c8b1e --- /dev/null +++ b/ruby/ql/lib/upgrades/30e1075bbdc9ce935dbe28dc7175489fe8e69a4c/upgrade.properties @@ -0,0 +1,2 @@ +description: Removed unused `numlines` relation +compatibility: full diff --git a/ruby/ql/lib/upgrades/31a238d080f3dd563d7225fc0458254617d1e5ba/old.dbscheme b/ruby/ql/lib/upgrades/31a238d080f3dd563d7225fc0458254617d1e5ba/old.dbscheme new file mode 100644 index 00000000000..31a238d080f --- /dev/null +++ b/ruby/ql/lib/upgrades/31a238d080f3dd563d7225fc0458254617d1e5ba/old.dbscheme @@ -0,0 +1,1316 @@ +// CodeQL database schema for Ruby +// Automatically generated from the tree-sitter grammar; do not edit + +@location = @location_default + +locations_default( + unique int id: @location_default, + int file: @file ref, + int start_line: int ref, + int start_column: int ref, + int end_line: int ref, + int end_column: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +sourceLocationPrefix( + string prefix: string ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +case @diagnostic.severity of + 10 = @diagnostic_debug +| 20 = @diagnostic_info +| 30 = @diagnostic_warning +| 40 = @diagnostic_error +; + + +@ruby_underscore_arg = @ruby_assignment | @ruby_binary | @ruby_conditional | @ruby_operator_assignment | @ruby_range | @ruby_unary | @ruby_underscore_primary + +@ruby_underscore_lhs = @ruby_call | @ruby_element_reference | @ruby_scope_resolution | @ruby_token_false | @ruby_token_nil | @ruby_token_true | @ruby_underscore_variable + +@ruby_underscore_method_name = @ruby_delimited_symbol | @ruby_setter | @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_operator | @ruby_token_simple_symbol + +@ruby_underscore_primary = @ruby_array | @ruby_begin | @ruby_break | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_delimited_symbol | @ruby_for | @ruby_hash | @ruby_if | @ruby_lambda | @ruby_method | @ruby_module | @ruby_next | @ruby_parenthesized_statements | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_retry | @ruby_return | @ruby_singleton_class | @ruby_singleton_method | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_symbol_array | @ruby_token_character | @ruby_token_complex | @ruby_token_float | @ruby_token_heredoc_beginning | @ruby_token_integer | @ruby_token_simple_symbol | @ruby_unary | @ruby_underscore_lhs | @ruby_unless | @ruby_until | @ruby_while | @ruby_yield + +@ruby_underscore_statement = @ruby_alias | @ruby_assignment | @ruby_begin_block | @ruby_binary | @ruby_break | @ruby_call | @ruby_end_block | @ruby_if_modifier | @ruby_next | @ruby_operator_assignment | @ruby_rescue_modifier | @ruby_return | @ruby_unary | @ruby_undef | @ruby_underscore_arg | @ruby_unless_modifier | @ruby_until_modifier | @ruby_while_modifier | @ruby_yield + +@ruby_underscore_variable = @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_self | @ruby_token_super + +ruby_alias_def( + unique int id: @ruby_alias, + int alias: @ruby_underscore_method_name ref, + int name: @ruby_underscore_method_name ref, + int loc: @location ref +); + +@ruby_argument_list_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_argument_list, index] +ruby_argument_list_child( + int ruby_argument_list: @ruby_argument_list ref, + int index: int ref, + unique int child: @ruby_argument_list_child_type ref +); + +ruby_argument_list_def( + unique int id: @ruby_argument_list, + int loc: @location ref +); + +@ruby_array_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_array, index] +ruby_array_child( + int ruby_array: @ruby_array ref, + int index: int ref, + unique int child: @ruby_array_child_type ref +); + +ruby_array_def( + unique int id: @ruby_array, + int loc: @location ref +); + +@ruby_assignment_left_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +@ruby_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_right_assignment_list | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +ruby_assignment_def( + unique int id: @ruby_assignment, + int left: @ruby_assignment_left_type ref, + int right: @ruby_assignment_right_type ref, + int loc: @location ref +); + +@ruby_bare_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_string, index] +ruby_bare_string_child( + int ruby_bare_string: @ruby_bare_string ref, + int index: int ref, + unique int child: @ruby_bare_string_child_type ref +); + +ruby_bare_string_def( + unique int id: @ruby_bare_string, + int loc: @location ref +); + +@ruby_bare_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_symbol, index] +ruby_bare_symbol_child( + int ruby_bare_symbol: @ruby_bare_symbol ref, + int index: int ref, + unique int child: @ruby_bare_symbol_child_type ref +); + +ruby_bare_symbol_def( + unique int id: @ruby_bare_symbol, + int loc: @location ref +); + +@ruby_begin_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin, index] +ruby_begin_child( + int ruby_begin: @ruby_begin ref, + int index: int ref, + unique int child: @ruby_begin_child_type ref +); + +ruby_begin_def( + unique int id: @ruby_begin, + int loc: @location ref +); + +@ruby_begin_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin_block, index] +ruby_begin_block_child( + int ruby_begin_block: @ruby_begin_block ref, + int index: int ref, + unique int child: @ruby_begin_block_child_type ref +); + +ruby_begin_block_def( + unique int id: @ruby_begin_block, + int loc: @location ref +); + +@ruby_binary_left_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +case @ruby_binary.operator of + 0 = @ruby_binary_bangequal +| 1 = @ruby_binary_bangtilde +| 2 = @ruby_binary_percent +| 3 = @ruby_binary_ampersand +| 4 = @ruby_binary_ampersandampersand +| 5 = @ruby_binary_star +| 6 = @ruby_binary_starstar +| 7 = @ruby_binary_plus +| 8 = @ruby_binary_minus +| 9 = @ruby_binary_slash +| 10 = @ruby_binary_langle +| 11 = @ruby_binary_langlelangle +| 12 = @ruby_binary_langleequal +| 13 = @ruby_binary_langleequalrangle +| 14 = @ruby_binary_equalequal +| 15 = @ruby_binary_equalequalequal +| 16 = @ruby_binary_equaltilde +| 17 = @ruby_binary_rangle +| 18 = @ruby_binary_rangleequal +| 19 = @ruby_binary_ranglerangle +| 20 = @ruby_binary_caret +| 21 = @ruby_binary_and +| 22 = @ruby_binary_or +| 23 = @ruby_binary_pipe +| 24 = @ruby_binary_pipepipe +; + + +@ruby_binary_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_binary_def( + unique int id: @ruby_binary, + int left: @ruby_binary_left_type ref, + int operator: int ref, + int right: @ruby_binary_right_type ref, + int loc: @location ref +); + +ruby_block_parameters( + unique int ruby_block: @ruby_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_block, index] +ruby_block_child( + int ruby_block: @ruby_block ref, + int index: int ref, + unique int child: @ruby_block_child_type ref +); + +ruby_block_def( + unique int id: @ruby_block, + int loc: @location ref +); + +ruby_block_argument_def( + unique int id: @ruby_block_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_block_parameter_def( + unique int id: @ruby_block_parameter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_block_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_block_parameters, index] +ruby_block_parameters_child( + int ruby_block_parameters: @ruby_block_parameters ref, + int index: int ref, + unique int child: @ruby_block_parameters_child_type ref +); + +ruby_block_parameters_def( + unique int id: @ruby_block_parameters, + int loc: @location ref +); + +ruby_break_child( + unique int ruby_break: @ruby_break ref, + unique int child: @ruby_argument_list ref +); + +ruby_break_def( + unique int id: @ruby_break, + int loc: @location ref +); + +ruby_call_arguments( + unique int ruby_call: @ruby_call ref, + unique int arguments: @ruby_argument_list ref +); + +@ruby_call_block_type = @ruby_block | @ruby_do_block + +ruby_call_block( + unique int ruby_call: @ruby_call ref, + unique int block: @ruby_call_block_type ref +); + +@ruby_call_method_type = @ruby_argument_list | @ruby_scope_resolution | @ruby_token_operator | @ruby_underscore_variable + +@ruby_call_receiver_type = @ruby_call | @ruby_underscore_primary + +ruby_call_receiver( + unique int ruby_call: @ruby_call ref, + unique int receiver: @ruby_call_receiver_type ref +); + +ruby_call_def( + unique int id: @ruby_call, + int method: @ruby_call_method_type ref, + int loc: @location ref +); + +ruby_case_value( + unique int ruby_case__: @ruby_case__ ref, + unique int value: @ruby_underscore_statement ref +); + +@ruby_case_child_type = @ruby_else | @ruby_when + +#keyset[ruby_case__, index] +ruby_case_child( + int ruby_case__: @ruby_case__ ref, + int index: int ref, + unique int child: @ruby_case_child_type ref +); + +ruby_case_def( + unique int id: @ruby_case__, + int loc: @location ref +); + +#keyset[ruby_chained_string, index] +ruby_chained_string_child( + int ruby_chained_string: @ruby_chained_string ref, + int index: int ref, + unique int child: @ruby_string__ ref +); + +ruby_chained_string_def( + unique int id: @ruby_chained_string, + int loc: @location ref +); + +@ruby_class_name_type = @ruby_scope_resolution | @ruby_token_constant + +ruby_class_superclass( + unique int ruby_class: @ruby_class ref, + unique int superclass: @ruby_superclass ref +); + +@ruby_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_class, index] +ruby_class_child( + int ruby_class: @ruby_class ref, + int index: int ref, + unique int child: @ruby_class_child_type ref +); + +ruby_class_def( + unique int id: @ruby_class, + int name: @ruby_class_name_type ref, + int loc: @location ref +); + +ruby_conditional_def( + unique int id: @ruby_conditional, + int alternative: @ruby_underscore_arg ref, + int condition: @ruby_underscore_arg ref, + int consequence: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_delimited_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_delimited_symbol, index] +ruby_delimited_symbol_child( + int ruby_delimited_symbol: @ruby_delimited_symbol ref, + int index: int ref, + unique int child: @ruby_delimited_symbol_child_type ref +); + +ruby_delimited_symbol_def( + unique int id: @ruby_delimited_symbol, + int loc: @location ref +); + +@ruby_destructured_left_assignment_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_destructured_left_assignment, index] +ruby_destructured_left_assignment_child( + int ruby_destructured_left_assignment: @ruby_destructured_left_assignment ref, + int index: int ref, + unique int child: @ruby_destructured_left_assignment_child_type ref +); + +ruby_destructured_left_assignment_def( + unique int id: @ruby_destructured_left_assignment, + int loc: @location ref +); + +@ruby_destructured_parameter_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_destructured_parameter, index] +ruby_destructured_parameter_child( + int ruby_destructured_parameter: @ruby_destructured_parameter ref, + int index: int ref, + unique int child: @ruby_destructured_parameter_child_type ref +); + +ruby_destructured_parameter_def( + unique int id: @ruby_destructured_parameter, + int loc: @location ref +); + +@ruby_do_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do, index] +ruby_do_child( + int ruby_do: @ruby_do ref, + int index: int ref, + unique int child: @ruby_do_child_type ref +); + +ruby_do_def( + unique int id: @ruby_do, + int loc: @location ref +); + +ruby_do_block_parameters( + unique int ruby_do_block: @ruby_do_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_do_block_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do_block, index] +ruby_do_block_child( + int ruby_do_block: @ruby_do_block ref, + int index: int ref, + unique int child: @ruby_do_block_child_type ref +); + +ruby_do_block_def( + unique int id: @ruby_do_block, + int loc: @location ref +); + +@ruby_element_reference_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_element_reference, index] +ruby_element_reference_child( + int ruby_element_reference: @ruby_element_reference ref, + int index: int ref, + unique int child: @ruby_element_reference_child_type ref +); + +ruby_element_reference_def( + unique int id: @ruby_element_reference, + int object: @ruby_underscore_primary ref, + int loc: @location ref +); + +@ruby_else_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_else, index] +ruby_else_child( + int ruby_else: @ruby_else ref, + int index: int ref, + unique int child: @ruby_else_child_type ref +); + +ruby_else_def( + unique int id: @ruby_else, + int loc: @location ref +); + +@ruby_elsif_alternative_type = @ruby_else | @ruby_elsif + +ruby_elsif_alternative( + unique int ruby_elsif: @ruby_elsif ref, + unique int alternative: @ruby_elsif_alternative_type ref +); + +ruby_elsif_consequence( + unique int ruby_elsif: @ruby_elsif ref, + unique int consequence: @ruby_then ref +); + +ruby_elsif_def( + unique int id: @ruby_elsif, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_end_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_end_block, index] +ruby_end_block_child( + int ruby_end_block: @ruby_end_block ref, + int index: int ref, + unique int child: @ruby_end_block_child_type ref +); + +ruby_end_block_def( + unique int id: @ruby_end_block, + int loc: @location ref +); + +@ruby_ensure_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_ensure, index] +ruby_ensure_child( + int ruby_ensure: @ruby_ensure ref, + int index: int ref, + unique int child: @ruby_ensure_child_type ref +); + +ruby_ensure_def( + unique int id: @ruby_ensure, + int loc: @location ref +); + +ruby_exception_variable_def( + unique int id: @ruby_exception_variable, + int child: @ruby_underscore_lhs ref, + int loc: @location ref +); + +@ruby_exceptions_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_exceptions, index] +ruby_exceptions_child( + int ruby_exceptions: @ruby_exceptions ref, + int index: int ref, + unique int child: @ruby_exceptions_child_type ref +); + +ruby_exceptions_def( + unique int id: @ruby_exceptions, + int loc: @location ref +); + +@ruby_for_pattern_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +ruby_for_def( + unique int id: @ruby_for, + int body: @ruby_do ref, + int pattern: @ruby_for_pattern_type ref, + int value: @ruby_in ref, + int loc: @location ref +); + +@ruby_hash_child_type = @ruby_hash_splat_argument | @ruby_pair + +#keyset[ruby_hash, index] +ruby_hash_child( + int ruby_hash: @ruby_hash ref, + int index: int ref, + unique int child: @ruby_hash_child_type ref +); + +ruby_hash_def( + unique int id: @ruby_hash, + int loc: @location ref +); + +ruby_hash_splat_argument_def( + unique int id: @ruby_hash_splat_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_hash_splat_parameter_name( + unique int ruby_hash_splat_parameter: @ruby_hash_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_hash_splat_parameter_def( + unique int id: @ruby_hash_splat_parameter, + int loc: @location ref +); + +@ruby_heredoc_body_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_heredoc_content | @ruby_token_heredoc_end + +#keyset[ruby_heredoc_body, index] +ruby_heredoc_body_child( + int ruby_heredoc_body: @ruby_heredoc_body ref, + int index: int ref, + unique int child: @ruby_heredoc_body_child_type ref +); + +ruby_heredoc_body_def( + unique int id: @ruby_heredoc_body, + int loc: @location ref +); + +@ruby_if_alternative_type = @ruby_else | @ruby_elsif + +ruby_if_alternative( + unique int ruby_if: @ruby_if ref, + unique int alternative: @ruby_if_alternative_type ref +); + +ruby_if_consequence( + unique int ruby_if: @ruby_if ref, + unique int consequence: @ruby_then ref +); + +ruby_if_def( + unique int id: @ruby_if, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_if_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_if_modifier_def( + unique int id: @ruby_if_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_if_modifier_condition_type ref, + int loc: @location ref +); + +ruby_in_def( + unique int id: @ruby_in, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_interpolation_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_interpolation, index] +ruby_interpolation_child( + int ruby_interpolation: @ruby_interpolation ref, + int index: int ref, + unique int child: @ruby_interpolation_child_type ref +); + +ruby_interpolation_def( + unique int id: @ruby_interpolation, + int loc: @location ref +); + +ruby_keyword_parameter_value( + unique int ruby_keyword_parameter: @ruby_keyword_parameter ref, + unique int value: @ruby_underscore_arg ref +); + +ruby_keyword_parameter_def( + unique int id: @ruby_keyword_parameter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_lambda_body_type = @ruby_block | @ruby_do_block + +ruby_lambda_parameters( + unique int ruby_lambda: @ruby_lambda ref, + unique int parameters: @ruby_lambda_parameters ref +); + +ruby_lambda_def( + unique int id: @ruby_lambda, + int body: @ruby_lambda_body_type ref, + int loc: @location ref +); + +@ruby_lambda_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_lambda_parameters, index] +ruby_lambda_parameters_child( + int ruby_lambda_parameters: @ruby_lambda_parameters ref, + int index: int ref, + unique int child: @ruby_lambda_parameters_child_type ref +); + +ruby_lambda_parameters_def( + unique int id: @ruby_lambda_parameters, + int loc: @location ref +); + +@ruby_left_assignment_list_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_left_assignment_list, index] +ruby_left_assignment_list_child( + int ruby_left_assignment_list: @ruby_left_assignment_list ref, + int index: int ref, + unique int child: @ruby_left_assignment_list_child_type ref +); + +ruby_left_assignment_list_def( + unique int id: @ruby_left_assignment_list, + int loc: @location ref +); + +ruby_method_parameters( + unique int ruby_method: @ruby_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_method, index] +ruby_method_child( + int ruby_method: @ruby_method ref, + int index: int ref, + unique int child: @ruby_method_child_type ref +); + +ruby_method_def( + unique int id: @ruby_method, + int name: @ruby_underscore_method_name ref, + int loc: @location ref +); + +@ruby_method_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_method_parameters, index] +ruby_method_parameters_child( + int ruby_method_parameters: @ruby_method_parameters ref, + int index: int ref, + unique int child: @ruby_method_parameters_child_type ref +); + +ruby_method_parameters_def( + unique int id: @ruby_method_parameters, + int loc: @location ref +); + +@ruby_module_name_type = @ruby_scope_resolution | @ruby_token_constant + +@ruby_module_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_module, index] +ruby_module_child( + int ruby_module: @ruby_module ref, + int index: int ref, + unique int child: @ruby_module_child_type ref +); + +ruby_module_def( + unique int id: @ruby_module, + int name: @ruby_module_name_type ref, + int loc: @location ref +); + +ruby_next_child( + unique int ruby_next: @ruby_next ref, + unique int child: @ruby_argument_list ref +); + +ruby_next_def( + unique int id: @ruby_next, + int loc: @location ref +); + +case @ruby_operator_assignment.operator of + 0 = @ruby_operator_assignment_percentequal +| 1 = @ruby_operator_assignment_ampersandampersandequal +| 2 = @ruby_operator_assignment_ampersandequal +| 3 = @ruby_operator_assignment_starstarequal +| 4 = @ruby_operator_assignment_starequal +| 5 = @ruby_operator_assignment_plusequal +| 6 = @ruby_operator_assignment_minusequal +| 7 = @ruby_operator_assignment_slashequal +| 8 = @ruby_operator_assignment_langlelangleequal +| 9 = @ruby_operator_assignment_ranglerangleequal +| 10 = @ruby_operator_assignment_caretequal +| 11 = @ruby_operator_assignment_pipeequal +| 12 = @ruby_operator_assignment_pipepipeequal +; + + +@ruby_operator_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_operator_assignment_def( + unique int id: @ruby_operator_assignment, + int left: @ruby_underscore_lhs ref, + int operator: int ref, + int right: @ruby_operator_assignment_right_type ref, + int loc: @location ref +); + +ruby_optional_parameter_def( + unique int id: @ruby_optional_parameter, + int name: @ruby_token_identifier ref, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_pair_key_type = @ruby_string__ | @ruby_token_hash_key_symbol | @ruby_underscore_arg + +ruby_pair_def( + unique int id: @ruby_pair, + int key__: @ruby_pair_key_type ref, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_parenthesized_statements_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_parenthesized_statements, index] +ruby_parenthesized_statements_child( + int ruby_parenthesized_statements: @ruby_parenthesized_statements ref, + int index: int ref, + unique int child: @ruby_parenthesized_statements_child_type ref +); + +ruby_parenthesized_statements_def( + unique int id: @ruby_parenthesized_statements, + int loc: @location ref +); + +@ruby_pattern_child_type = @ruby_splat_argument | @ruby_underscore_arg + +ruby_pattern_def( + unique int id: @ruby_pattern, + int child: @ruby_pattern_child_type ref, + int loc: @location ref +); + +@ruby_program_child_type = @ruby_token_empty_statement | @ruby_token_uninterpreted | @ruby_underscore_statement + +#keyset[ruby_program, index] +ruby_program_child( + int ruby_program: @ruby_program ref, + int index: int ref, + unique int child: @ruby_program_child_type ref +); + +ruby_program_def( + unique int id: @ruby_program, + int loc: @location ref +); + +ruby_range_begin( + unique int ruby_range: @ruby_range ref, + unique int begin: @ruby_underscore_arg ref +); + +ruby_range_end( + unique int ruby_range: @ruby_range ref, + unique int end: @ruby_underscore_arg ref +); + +case @ruby_range.operator of + 0 = @ruby_range_dotdot +| 1 = @ruby_range_dotdotdot +; + + +ruby_range_def( + unique int id: @ruby_range, + int operator: int ref, + int loc: @location ref +); + +@ruby_rational_child_type = @ruby_token_float | @ruby_token_integer + +ruby_rational_def( + unique int id: @ruby_rational, + int child: @ruby_rational_child_type ref, + int loc: @location ref +); + +ruby_redo_child( + unique int ruby_redo: @ruby_redo ref, + unique int child: @ruby_argument_list ref +); + +ruby_redo_def( + unique int id: @ruby_redo, + int loc: @location ref +); + +@ruby_regex_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_regex, index] +ruby_regex_child( + int ruby_regex: @ruby_regex ref, + int index: int ref, + unique int child: @ruby_regex_child_type ref +); + +ruby_regex_def( + unique int id: @ruby_regex, + int loc: @location ref +); + +ruby_rescue_body( + unique int ruby_rescue: @ruby_rescue ref, + unique int body: @ruby_then ref +); + +ruby_rescue_exceptions( + unique int ruby_rescue: @ruby_rescue ref, + unique int exceptions: @ruby_exceptions ref +); + +ruby_rescue_variable( + unique int ruby_rescue: @ruby_rescue ref, + unique int variable: @ruby_exception_variable ref +); + +ruby_rescue_def( + unique int id: @ruby_rescue, + int loc: @location ref +); + +@ruby_rescue_modifier_handler_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_rescue_modifier_def( + unique int id: @ruby_rescue_modifier, + int body: @ruby_underscore_statement ref, + int handler: @ruby_rescue_modifier_handler_type ref, + int loc: @location ref +); + +ruby_rest_assignment_child( + unique int ruby_rest_assignment: @ruby_rest_assignment ref, + unique int child: @ruby_underscore_lhs ref +); + +ruby_rest_assignment_def( + unique int id: @ruby_rest_assignment, + int loc: @location ref +); + +ruby_retry_child( + unique int ruby_retry: @ruby_retry ref, + unique int child: @ruby_argument_list ref +); + +ruby_retry_def( + unique int id: @ruby_retry, + int loc: @location ref +); + +ruby_return_child( + unique int ruby_return: @ruby_return ref, + unique int child: @ruby_argument_list ref +); + +ruby_return_def( + unique int id: @ruby_return, + int loc: @location ref +); + +@ruby_right_assignment_list_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_right_assignment_list, index] +ruby_right_assignment_list_child( + int ruby_right_assignment_list: @ruby_right_assignment_list ref, + int index: int ref, + unique int child: @ruby_right_assignment_list_child_type ref +); + +ruby_right_assignment_list_def( + unique int id: @ruby_right_assignment_list, + int loc: @location ref +); + +@ruby_scope_resolution_name_type = @ruby_token_constant | @ruby_token_identifier + +ruby_scope_resolution_scope( + unique int ruby_scope_resolution: @ruby_scope_resolution ref, + unique int scope: @ruby_underscore_primary ref +); + +ruby_scope_resolution_def( + unique int id: @ruby_scope_resolution, + int name: @ruby_scope_resolution_name_type ref, + int loc: @location ref +); + +ruby_setter_def( + unique int id: @ruby_setter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_singleton_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_class, index] +ruby_singleton_class_child( + int ruby_singleton_class: @ruby_singleton_class ref, + int index: int ref, + unique int child: @ruby_singleton_class_child_type ref +); + +ruby_singleton_class_def( + unique int id: @ruby_singleton_class, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_singleton_method_object_type = @ruby_underscore_arg | @ruby_underscore_variable + +ruby_singleton_method_parameters( + unique int ruby_singleton_method: @ruby_singleton_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_singleton_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_method, index] +ruby_singleton_method_child( + int ruby_singleton_method: @ruby_singleton_method ref, + int index: int ref, + unique int child: @ruby_singleton_method_child_type ref +); + +ruby_singleton_method_def( + unique int id: @ruby_singleton_method, + int name: @ruby_underscore_method_name ref, + int object: @ruby_singleton_method_object_type ref, + int loc: @location ref +); + +ruby_splat_argument_def( + unique int id: @ruby_splat_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_splat_parameter_name( + unique int ruby_splat_parameter: @ruby_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_splat_parameter_def( + unique int id: @ruby_splat_parameter, + int loc: @location ref +); + +@ruby_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_string__, index] +ruby_string_child( + int ruby_string__: @ruby_string__ ref, + int index: int ref, + unique int child: @ruby_string_child_type ref +); + +ruby_string_def( + unique int id: @ruby_string__, + int loc: @location ref +); + +#keyset[ruby_string_array, index] +ruby_string_array_child( + int ruby_string_array: @ruby_string_array ref, + int index: int ref, + unique int child: @ruby_bare_string ref +); + +ruby_string_array_def( + unique int id: @ruby_string_array, + int loc: @location ref +); + +@ruby_subshell_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_subshell, index] +ruby_subshell_child( + int ruby_subshell: @ruby_subshell ref, + int index: int ref, + unique int child: @ruby_subshell_child_type ref +); + +ruby_subshell_def( + unique int id: @ruby_subshell, + int loc: @location ref +); + +@ruby_superclass_child_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_superclass_def( + unique int id: @ruby_superclass, + int child: @ruby_superclass_child_type ref, + int loc: @location ref +); + +#keyset[ruby_symbol_array, index] +ruby_symbol_array_child( + int ruby_symbol_array: @ruby_symbol_array ref, + int index: int ref, + unique int child: @ruby_bare_symbol ref +); + +ruby_symbol_array_def( + unique int id: @ruby_symbol_array, + int loc: @location ref +); + +@ruby_then_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_then, index] +ruby_then_child( + int ruby_then: @ruby_then ref, + int index: int ref, + unique int child: @ruby_then_child_type ref +); + +ruby_then_def( + unique int id: @ruby_then, + int loc: @location ref +); + +@ruby_unary_operand_type = @ruby_break | @ruby_call | @ruby_next | @ruby_parenthesized_statements | @ruby_return | @ruby_token_float | @ruby_token_integer | @ruby_underscore_arg | @ruby_yield + +case @ruby_unary.operator of + 0 = @ruby_unary_bang +| 1 = @ruby_unary_plus +| 2 = @ruby_unary_minus +| 3 = @ruby_unary_definedquestion +| 4 = @ruby_unary_not +| 5 = @ruby_unary_tilde +; + + +ruby_unary_def( + unique int id: @ruby_unary, + int operand: @ruby_unary_operand_type ref, + int operator: int ref, + int loc: @location ref +); + +#keyset[ruby_undef, index] +ruby_undef_child( + int ruby_undef: @ruby_undef ref, + int index: int ref, + unique int child: @ruby_underscore_method_name ref +); + +ruby_undef_def( + unique int id: @ruby_undef, + int loc: @location ref +); + +@ruby_unless_alternative_type = @ruby_else | @ruby_elsif + +ruby_unless_alternative( + unique int ruby_unless: @ruby_unless ref, + unique int alternative: @ruby_unless_alternative_type ref +); + +ruby_unless_consequence( + unique int ruby_unless: @ruby_unless ref, + unique int consequence: @ruby_then ref +); + +ruby_unless_def( + unique int id: @ruby_unless, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_unless_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_unless_modifier_def( + unique int id: @ruby_unless_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_unless_modifier_condition_type ref, + int loc: @location ref +); + +ruby_until_def( + unique int id: @ruby_until, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_until_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_until_modifier_def( + unique int id: @ruby_until_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_until_modifier_condition_type ref, + int loc: @location ref +); + +ruby_when_body( + unique int ruby_when: @ruby_when ref, + unique int body: @ruby_then ref +); + +#keyset[ruby_when, index] +ruby_when_pattern( + int ruby_when: @ruby_when ref, + int index: int ref, + unique int pattern: @ruby_pattern ref +); + +ruby_when_def( + unique int id: @ruby_when, + int loc: @location ref +); + +ruby_while_def( + unique int id: @ruby_while, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_while_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_while_modifier_def( + unique int id: @ruby_while_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_while_modifier_condition_type ref, + int loc: @location ref +); + +ruby_yield_child( + unique int ruby_yield: @ruby_yield ref, + unique int child: @ruby_argument_list ref +); + +ruby_yield_def( + unique int id: @ruby_yield, + int loc: @location ref +); + +ruby_tokeninfo( + unique int id: @ruby_token, + int kind: int ref, + string value: string ref, + int loc: @location ref +); + +case @ruby_token.kind of + 0 = @ruby_reserved_word +| 1 = @ruby_token_character +| 2 = @ruby_token_class_variable +| 3 = @ruby_token_comment +| 4 = @ruby_token_complex +| 5 = @ruby_token_constant +| 6 = @ruby_token_empty_statement +| 7 = @ruby_token_escape_sequence +| 8 = @ruby_token_false +| 9 = @ruby_token_float +| 10 = @ruby_token_global_variable +| 11 = @ruby_token_hash_key_symbol +| 12 = @ruby_token_heredoc_beginning +| 13 = @ruby_token_heredoc_content +| 14 = @ruby_token_heredoc_end +| 15 = @ruby_token_identifier +| 16 = @ruby_token_instance_variable +| 17 = @ruby_token_integer +| 18 = @ruby_token_nil +| 19 = @ruby_token_operator +| 20 = @ruby_token_self +| 21 = @ruby_token_simple_symbol +| 22 = @ruby_token_string_content +| 23 = @ruby_token_super +| 24 = @ruby_token_true +| 25 = @ruby_token_uninterpreted +; + + +@ruby_ast_node = @ruby_alias | @ruby_argument_list | @ruby_array | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_parameter | @ruby_block_parameters | @ruby_break | @ruby_call | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_for | @ruby_hash | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_modifier | @ruby_in | @ruby_interpolation | @ruby_keyword_parameter | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield + +@ruby_ast_node_parent = @file | @ruby_ast_node + +#keyset[parent, parent_index] +ruby_ast_node_parent( + int child: @ruby_ast_node ref, + int parent: @ruby_ast_node_parent ref, + int parent_index: int ref +); + +erb_comment_directive_def( + unique int id: @erb_comment_directive, + int child: @erb_token_comment ref, + int loc: @location ref +); + +erb_directive_def( + unique int id: @erb_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +erb_graphql_directive_def( + unique int id: @erb_graphql_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +erb_output_directive_def( + unique int id: @erb_output_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +@erb_template_child_type = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_token_content + +#keyset[erb_template, index] +erb_template_child( + int erb_template: @erb_template ref, + int index: int ref, + unique int child: @erb_template_child_type ref +); + +erb_template_def( + unique int id: @erb_template, + int loc: @location ref +); + +erb_tokeninfo( + unique int id: @erb_token, + int kind: int ref, + string value: string ref, + int loc: @location ref +); + +case @erb_token.kind of + 0 = @erb_reserved_word +| 1 = @erb_token_code +| 2 = @erb_token_comment +| 3 = @erb_token_content +; + + +@erb_ast_node = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_template | @erb_token + +@erb_ast_node_parent = @erb_ast_node | @file + +#keyset[parent, parent_index] +erb_ast_node_parent( + int child: @erb_ast_node ref, + int parent: @erb_ast_node_parent ref, + int parent_index: int ref +); + diff --git a/ruby/ql/lib/upgrades/31a238d080f3dd563d7225fc0458254617d1e5ba/ruby.dbscheme b/ruby/ql/lib/upgrades/31a238d080f3dd563d7225fc0458254617d1e5ba/ruby.dbscheme new file mode 100644 index 00000000000..f36dd8a35ce --- /dev/null +++ b/ruby/ql/lib/upgrades/31a238d080f3dd563d7225fc0458254617d1e5ba/ruby.dbscheme @@ -0,0 +1,1318 @@ +// CodeQL database schema for Ruby +// Automatically generated from the tree-sitter grammar; do not edit + +@location = @location_default + +locations_default( + unique int id: @location_default, + int file: @file ref, + int start_line: int ref, + int start_column: int ref, + int end_line: int ref, + int end_column: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +sourceLocationPrefix( + string prefix: string ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +case @diagnostic.severity of + 10 = @diagnostic_debug +| 20 = @diagnostic_info +| 30 = @diagnostic_warning +| 40 = @diagnostic_error +; + + +@ruby_underscore_arg = @ruby_assignment | @ruby_binary | @ruby_conditional | @ruby_operator_assignment | @ruby_range | @ruby_unary | @ruby_underscore_primary + +@ruby_underscore_lhs = @ruby_call | @ruby_element_reference | @ruby_scope_resolution | @ruby_token_false | @ruby_token_nil | @ruby_token_true | @ruby_underscore_variable + +@ruby_underscore_method_name = @ruby_delimited_symbol | @ruby_setter | @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_operator | @ruby_token_simple_symbol + +@ruby_underscore_primary = @ruby_array | @ruby_begin | @ruby_break | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_delimited_symbol | @ruby_for | @ruby_hash | @ruby_if | @ruby_lambda | @ruby_method | @ruby_module | @ruby_next | @ruby_parenthesized_statements | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_retry | @ruby_return | @ruby_singleton_class | @ruby_singleton_method | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_symbol_array | @ruby_token_character | @ruby_token_complex | @ruby_token_float | @ruby_token_heredoc_beginning | @ruby_token_integer | @ruby_token_simple_symbol | @ruby_unary | @ruby_underscore_lhs | @ruby_unless | @ruby_until | @ruby_while | @ruby_yield + +@ruby_underscore_statement = @ruby_alias | @ruby_assignment | @ruby_begin_block | @ruby_binary | @ruby_break | @ruby_call | @ruby_end_block | @ruby_if_modifier | @ruby_next | @ruby_operator_assignment | @ruby_rescue_modifier | @ruby_return | @ruby_unary | @ruby_undef | @ruby_underscore_arg | @ruby_unless_modifier | @ruby_until_modifier | @ruby_while_modifier | @ruby_yield + +@ruby_underscore_variable = @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_self | @ruby_token_super + +ruby_alias_def( + unique int id: @ruby_alias, + int alias: @ruby_underscore_method_name ref, + int name: @ruby_underscore_method_name ref, + int loc: @location ref +); + +@ruby_argument_list_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_argument_list, index] +ruby_argument_list_child( + int ruby_argument_list: @ruby_argument_list ref, + int index: int ref, + unique int child: @ruby_argument_list_child_type ref +); + +ruby_argument_list_def( + unique int id: @ruby_argument_list, + int loc: @location ref +); + +@ruby_array_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_array, index] +ruby_array_child( + int ruby_array: @ruby_array ref, + int index: int ref, + unique int child: @ruby_array_child_type ref +); + +ruby_array_def( + unique int id: @ruby_array, + int loc: @location ref +); + +@ruby_assignment_left_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +@ruby_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_right_assignment_list | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +ruby_assignment_def( + unique int id: @ruby_assignment, + int left: @ruby_assignment_left_type ref, + int right: @ruby_assignment_right_type ref, + int loc: @location ref +); + +@ruby_bare_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_string, index] +ruby_bare_string_child( + int ruby_bare_string: @ruby_bare_string ref, + int index: int ref, + unique int child: @ruby_bare_string_child_type ref +); + +ruby_bare_string_def( + unique int id: @ruby_bare_string, + int loc: @location ref +); + +@ruby_bare_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_symbol, index] +ruby_bare_symbol_child( + int ruby_bare_symbol: @ruby_bare_symbol ref, + int index: int ref, + unique int child: @ruby_bare_symbol_child_type ref +); + +ruby_bare_symbol_def( + unique int id: @ruby_bare_symbol, + int loc: @location ref +); + +@ruby_begin_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin, index] +ruby_begin_child( + int ruby_begin: @ruby_begin ref, + int index: int ref, + unique int child: @ruby_begin_child_type ref +); + +ruby_begin_def( + unique int id: @ruby_begin, + int loc: @location ref +); + +@ruby_begin_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin_block, index] +ruby_begin_block_child( + int ruby_begin_block: @ruby_begin_block ref, + int index: int ref, + unique int child: @ruby_begin_block_child_type ref +); + +ruby_begin_block_def( + unique int id: @ruby_begin_block, + int loc: @location ref +); + +@ruby_binary_left_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +case @ruby_binary.operator of + 0 = @ruby_binary_bangequal +| 1 = @ruby_binary_bangtilde +| 2 = @ruby_binary_percent +| 3 = @ruby_binary_ampersand +| 4 = @ruby_binary_ampersandampersand +| 5 = @ruby_binary_star +| 6 = @ruby_binary_starstar +| 7 = @ruby_binary_plus +| 8 = @ruby_binary_minus +| 9 = @ruby_binary_slash +| 10 = @ruby_binary_langle +| 11 = @ruby_binary_langlelangle +| 12 = @ruby_binary_langleequal +| 13 = @ruby_binary_langleequalrangle +| 14 = @ruby_binary_equalequal +| 15 = @ruby_binary_equalequalequal +| 16 = @ruby_binary_equaltilde +| 17 = @ruby_binary_rangle +| 18 = @ruby_binary_rangleequal +| 19 = @ruby_binary_ranglerangle +| 20 = @ruby_binary_caret +| 21 = @ruby_binary_and +| 22 = @ruby_binary_or +| 23 = @ruby_binary_pipe +| 24 = @ruby_binary_pipepipe +; + + +@ruby_binary_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_binary_def( + unique int id: @ruby_binary, + int left: @ruby_binary_left_type ref, + int operator: int ref, + int right: @ruby_binary_right_type ref, + int loc: @location ref +); + +ruby_block_parameters( + unique int ruby_block: @ruby_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_block, index] +ruby_block_child( + int ruby_block: @ruby_block ref, + int index: int ref, + unique int child: @ruby_block_child_type ref +); + +ruby_block_def( + unique int id: @ruby_block, + int loc: @location ref +); + +ruby_block_argument_def( + unique int id: @ruby_block_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_block_parameter_def( + unique int id: @ruby_block_parameter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_block_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_identifier + +#keyset[ruby_block_parameters, index] +ruby_block_parameters_child( + int ruby_block_parameters: @ruby_block_parameters ref, + int index: int ref, + unique int child: @ruby_block_parameters_child_type ref +); + +ruby_block_parameters_def( + unique int id: @ruby_block_parameters, + int loc: @location ref +); + +ruby_break_child( + unique int ruby_break: @ruby_break ref, + unique int child: @ruby_argument_list ref +); + +ruby_break_def( + unique int id: @ruby_break, + int loc: @location ref +); + +ruby_call_arguments( + unique int ruby_call: @ruby_call ref, + unique int arguments: @ruby_argument_list ref +); + +@ruby_call_block_type = @ruby_block | @ruby_do_block + +ruby_call_block( + unique int ruby_call: @ruby_call ref, + unique int block: @ruby_call_block_type ref +); + +@ruby_call_method_type = @ruby_argument_list | @ruby_scope_resolution | @ruby_token_operator | @ruby_underscore_variable + +@ruby_call_receiver_type = @ruby_call | @ruby_underscore_primary + +ruby_call_receiver( + unique int ruby_call: @ruby_call ref, + unique int receiver: @ruby_call_receiver_type ref +); + +ruby_call_def( + unique int id: @ruby_call, + int method: @ruby_call_method_type ref, + int loc: @location ref +); + +ruby_case_value( + unique int ruby_case__: @ruby_case__ ref, + unique int value: @ruby_underscore_statement ref +); + +@ruby_case_child_type = @ruby_else | @ruby_when + +#keyset[ruby_case__, index] +ruby_case_child( + int ruby_case__: @ruby_case__ ref, + int index: int ref, + unique int child: @ruby_case_child_type ref +); + +ruby_case_def( + unique int id: @ruby_case__, + int loc: @location ref +); + +#keyset[ruby_chained_string, index] +ruby_chained_string_child( + int ruby_chained_string: @ruby_chained_string ref, + int index: int ref, + unique int child: @ruby_string__ ref +); + +ruby_chained_string_def( + unique int id: @ruby_chained_string, + int loc: @location ref +); + +@ruby_class_name_type = @ruby_scope_resolution | @ruby_token_constant + +ruby_class_superclass( + unique int ruby_class: @ruby_class ref, + unique int superclass: @ruby_superclass ref +); + +@ruby_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_class, index] +ruby_class_child( + int ruby_class: @ruby_class ref, + int index: int ref, + unique int child: @ruby_class_child_type ref +); + +ruby_class_def( + unique int id: @ruby_class, + int name: @ruby_class_name_type ref, + int loc: @location ref +); + +ruby_conditional_def( + unique int id: @ruby_conditional, + int alternative: @ruby_underscore_arg ref, + int condition: @ruby_underscore_arg ref, + int consequence: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_delimited_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_delimited_symbol, index] +ruby_delimited_symbol_child( + int ruby_delimited_symbol: @ruby_delimited_symbol ref, + int index: int ref, + unique int child: @ruby_delimited_symbol_child_type ref +); + +ruby_delimited_symbol_def( + unique int id: @ruby_delimited_symbol, + int loc: @location ref +); + +@ruby_destructured_left_assignment_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_destructured_left_assignment, index] +ruby_destructured_left_assignment_child( + int ruby_destructured_left_assignment: @ruby_destructured_left_assignment ref, + int index: int ref, + unique int child: @ruby_destructured_left_assignment_child_type ref +); + +ruby_destructured_left_assignment_def( + unique int id: @ruby_destructured_left_assignment, + int loc: @location ref +); + +@ruby_destructured_parameter_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_identifier + +#keyset[ruby_destructured_parameter, index] +ruby_destructured_parameter_child( + int ruby_destructured_parameter: @ruby_destructured_parameter ref, + int index: int ref, + unique int child: @ruby_destructured_parameter_child_type ref +); + +ruby_destructured_parameter_def( + unique int id: @ruby_destructured_parameter, + int loc: @location ref +); + +@ruby_do_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do, index] +ruby_do_child( + int ruby_do: @ruby_do ref, + int index: int ref, + unique int child: @ruby_do_child_type ref +); + +ruby_do_def( + unique int id: @ruby_do, + int loc: @location ref +); + +ruby_do_block_parameters( + unique int ruby_do_block: @ruby_do_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_do_block_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do_block, index] +ruby_do_block_child( + int ruby_do_block: @ruby_do_block ref, + int index: int ref, + unique int child: @ruby_do_block_child_type ref +); + +ruby_do_block_def( + unique int id: @ruby_do_block, + int loc: @location ref +); + +@ruby_element_reference_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_element_reference, index] +ruby_element_reference_child( + int ruby_element_reference: @ruby_element_reference ref, + int index: int ref, + unique int child: @ruby_element_reference_child_type ref +); + +ruby_element_reference_def( + unique int id: @ruby_element_reference, + int object: @ruby_underscore_primary ref, + int loc: @location ref +); + +@ruby_else_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_else, index] +ruby_else_child( + int ruby_else: @ruby_else ref, + int index: int ref, + unique int child: @ruby_else_child_type ref +); + +ruby_else_def( + unique int id: @ruby_else, + int loc: @location ref +); + +@ruby_elsif_alternative_type = @ruby_else | @ruby_elsif + +ruby_elsif_alternative( + unique int ruby_elsif: @ruby_elsif ref, + unique int alternative: @ruby_elsif_alternative_type ref +); + +ruby_elsif_consequence( + unique int ruby_elsif: @ruby_elsif ref, + unique int consequence: @ruby_then ref +); + +ruby_elsif_def( + unique int id: @ruby_elsif, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_end_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_end_block, index] +ruby_end_block_child( + int ruby_end_block: @ruby_end_block ref, + int index: int ref, + unique int child: @ruby_end_block_child_type ref +); + +ruby_end_block_def( + unique int id: @ruby_end_block, + int loc: @location ref +); + +@ruby_ensure_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_ensure, index] +ruby_ensure_child( + int ruby_ensure: @ruby_ensure ref, + int index: int ref, + unique int child: @ruby_ensure_child_type ref +); + +ruby_ensure_def( + unique int id: @ruby_ensure, + int loc: @location ref +); + +ruby_exception_variable_def( + unique int id: @ruby_exception_variable, + int child: @ruby_underscore_lhs ref, + int loc: @location ref +); + +@ruby_exceptions_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_exceptions, index] +ruby_exceptions_child( + int ruby_exceptions: @ruby_exceptions ref, + int index: int ref, + unique int child: @ruby_exceptions_child_type ref +); + +ruby_exceptions_def( + unique int id: @ruby_exceptions, + int loc: @location ref +); + +@ruby_for_pattern_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +ruby_for_def( + unique int id: @ruby_for, + int body: @ruby_do ref, + int pattern: @ruby_for_pattern_type ref, + int value: @ruby_in ref, + int loc: @location ref +); + +@ruby_hash_child_type = @ruby_hash_splat_argument | @ruby_pair + +#keyset[ruby_hash, index] +ruby_hash_child( + int ruby_hash: @ruby_hash ref, + int index: int ref, + unique int child: @ruby_hash_child_type ref +); + +ruby_hash_def( + unique int id: @ruby_hash, + int loc: @location ref +); + +ruby_hash_splat_argument_def( + unique int id: @ruby_hash_splat_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_hash_splat_parameter_name( + unique int ruby_hash_splat_parameter: @ruby_hash_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_hash_splat_parameter_def( + unique int id: @ruby_hash_splat_parameter, + int loc: @location ref +); + +@ruby_heredoc_body_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_heredoc_content | @ruby_token_heredoc_end + +#keyset[ruby_heredoc_body, index] +ruby_heredoc_body_child( + int ruby_heredoc_body: @ruby_heredoc_body ref, + int index: int ref, + unique int child: @ruby_heredoc_body_child_type ref +); + +ruby_heredoc_body_def( + unique int id: @ruby_heredoc_body, + int loc: @location ref +); + +@ruby_if_alternative_type = @ruby_else | @ruby_elsif + +ruby_if_alternative( + unique int ruby_if: @ruby_if ref, + unique int alternative: @ruby_if_alternative_type ref +); + +ruby_if_consequence( + unique int ruby_if: @ruby_if ref, + unique int consequence: @ruby_then ref +); + +ruby_if_def( + unique int id: @ruby_if, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_if_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_if_modifier_def( + unique int id: @ruby_if_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_if_modifier_condition_type ref, + int loc: @location ref +); + +ruby_in_def( + unique int id: @ruby_in, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_interpolation_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_interpolation, index] +ruby_interpolation_child( + int ruby_interpolation: @ruby_interpolation ref, + int index: int ref, + unique int child: @ruby_interpolation_child_type ref +); + +ruby_interpolation_def( + unique int id: @ruby_interpolation, + int loc: @location ref +); + +ruby_keyword_parameter_value( + unique int ruby_keyword_parameter: @ruby_keyword_parameter ref, + unique int value: @ruby_underscore_arg ref +); + +ruby_keyword_parameter_def( + unique int id: @ruby_keyword_parameter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_lambda_body_type = @ruby_block | @ruby_do_block + +ruby_lambda_parameters( + unique int ruby_lambda: @ruby_lambda ref, + unique int parameters: @ruby_lambda_parameters ref +); + +ruby_lambda_def( + unique int id: @ruby_lambda, + int body: @ruby_lambda_body_type ref, + int loc: @location ref +); + +@ruby_lambda_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_identifier + +#keyset[ruby_lambda_parameters, index] +ruby_lambda_parameters_child( + int ruby_lambda_parameters: @ruby_lambda_parameters ref, + int index: int ref, + unique int child: @ruby_lambda_parameters_child_type ref +); + +ruby_lambda_parameters_def( + unique int id: @ruby_lambda_parameters, + int loc: @location ref +); + +@ruby_left_assignment_list_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_left_assignment_list, index] +ruby_left_assignment_list_child( + int ruby_left_assignment_list: @ruby_left_assignment_list ref, + int index: int ref, + unique int child: @ruby_left_assignment_list_child_type ref +); + +ruby_left_assignment_list_def( + unique int id: @ruby_left_assignment_list, + int loc: @location ref +); + +ruby_method_parameters( + unique int ruby_method: @ruby_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_method, index] +ruby_method_child( + int ruby_method: @ruby_method ref, + int index: int ref, + unique int child: @ruby_method_child_type ref +); + +ruby_method_def( + unique int id: @ruby_method, + int name: @ruby_underscore_method_name ref, + int loc: @location ref +); + +@ruby_method_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_identifier + +#keyset[ruby_method_parameters, index] +ruby_method_parameters_child( + int ruby_method_parameters: @ruby_method_parameters ref, + int index: int ref, + unique int child: @ruby_method_parameters_child_type ref +); + +ruby_method_parameters_def( + unique int id: @ruby_method_parameters, + int loc: @location ref +); + +@ruby_module_name_type = @ruby_scope_resolution | @ruby_token_constant + +@ruby_module_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_module, index] +ruby_module_child( + int ruby_module: @ruby_module ref, + int index: int ref, + unique int child: @ruby_module_child_type ref +); + +ruby_module_def( + unique int id: @ruby_module, + int name: @ruby_module_name_type ref, + int loc: @location ref +); + +ruby_next_child( + unique int ruby_next: @ruby_next ref, + unique int child: @ruby_argument_list ref +); + +ruby_next_def( + unique int id: @ruby_next, + int loc: @location ref +); + +case @ruby_operator_assignment.operator of + 0 = @ruby_operator_assignment_percentequal +| 1 = @ruby_operator_assignment_ampersandampersandequal +| 2 = @ruby_operator_assignment_ampersandequal +| 3 = @ruby_operator_assignment_starstarequal +| 4 = @ruby_operator_assignment_starequal +| 5 = @ruby_operator_assignment_plusequal +| 6 = @ruby_operator_assignment_minusequal +| 7 = @ruby_operator_assignment_slashequal +| 8 = @ruby_operator_assignment_langlelangleequal +| 9 = @ruby_operator_assignment_ranglerangleequal +| 10 = @ruby_operator_assignment_caretequal +| 11 = @ruby_operator_assignment_pipeequal +| 12 = @ruby_operator_assignment_pipepipeequal +; + + +@ruby_operator_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_operator_assignment_def( + unique int id: @ruby_operator_assignment, + int left: @ruby_underscore_lhs ref, + int operator: int ref, + int right: @ruby_operator_assignment_right_type ref, + int loc: @location ref +); + +ruby_optional_parameter_def( + unique int id: @ruby_optional_parameter, + int name: @ruby_token_identifier ref, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_pair_key_type = @ruby_string__ | @ruby_token_hash_key_symbol | @ruby_underscore_arg + +ruby_pair_def( + unique int id: @ruby_pair, + int key__: @ruby_pair_key_type ref, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_parenthesized_statements_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_parenthesized_statements, index] +ruby_parenthesized_statements_child( + int ruby_parenthesized_statements: @ruby_parenthesized_statements ref, + int index: int ref, + unique int child: @ruby_parenthesized_statements_child_type ref +); + +ruby_parenthesized_statements_def( + unique int id: @ruby_parenthesized_statements, + int loc: @location ref +); + +@ruby_pattern_child_type = @ruby_splat_argument | @ruby_underscore_arg + +ruby_pattern_def( + unique int id: @ruby_pattern, + int child: @ruby_pattern_child_type ref, + int loc: @location ref +); + +@ruby_program_child_type = @ruby_token_empty_statement | @ruby_token_uninterpreted | @ruby_underscore_statement + +#keyset[ruby_program, index] +ruby_program_child( + int ruby_program: @ruby_program ref, + int index: int ref, + unique int child: @ruby_program_child_type ref +); + +ruby_program_def( + unique int id: @ruby_program, + int loc: @location ref +); + +ruby_range_begin( + unique int ruby_range: @ruby_range ref, + unique int begin: @ruby_underscore_arg ref +); + +ruby_range_end( + unique int ruby_range: @ruby_range ref, + unique int end: @ruby_underscore_arg ref +); + +case @ruby_range.operator of + 0 = @ruby_range_dotdot +| 1 = @ruby_range_dotdotdot +; + + +ruby_range_def( + unique int id: @ruby_range, + int operator: int ref, + int loc: @location ref +); + +@ruby_rational_child_type = @ruby_token_float | @ruby_token_integer + +ruby_rational_def( + unique int id: @ruby_rational, + int child: @ruby_rational_child_type ref, + int loc: @location ref +); + +ruby_redo_child( + unique int ruby_redo: @ruby_redo ref, + unique int child: @ruby_argument_list ref +); + +ruby_redo_def( + unique int id: @ruby_redo, + int loc: @location ref +); + +@ruby_regex_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_regex, index] +ruby_regex_child( + int ruby_regex: @ruby_regex ref, + int index: int ref, + unique int child: @ruby_regex_child_type ref +); + +ruby_regex_def( + unique int id: @ruby_regex, + int loc: @location ref +); + +ruby_rescue_body( + unique int ruby_rescue: @ruby_rescue ref, + unique int body: @ruby_then ref +); + +ruby_rescue_exceptions( + unique int ruby_rescue: @ruby_rescue ref, + unique int exceptions: @ruby_exceptions ref +); + +ruby_rescue_variable( + unique int ruby_rescue: @ruby_rescue ref, + unique int variable: @ruby_exception_variable ref +); + +ruby_rescue_def( + unique int id: @ruby_rescue, + int loc: @location ref +); + +@ruby_rescue_modifier_handler_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_rescue_modifier_def( + unique int id: @ruby_rescue_modifier, + int body: @ruby_underscore_statement ref, + int handler: @ruby_rescue_modifier_handler_type ref, + int loc: @location ref +); + +ruby_rest_assignment_child( + unique int ruby_rest_assignment: @ruby_rest_assignment ref, + unique int child: @ruby_underscore_lhs ref +); + +ruby_rest_assignment_def( + unique int id: @ruby_rest_assignment, + int loc: @location ref +); + +ruby_retry_child( + unique int ruby_retry: @ruby_retry ref, + unique int child: @ruby_argument_list ref +); + +ruby_retry_def( + unique int id: @ruby_retry, + int loc: @location ref +); + +ruby_return_child( + unique int ruby_return: @ruby_return ref, + unique int child: @ruby_argument_list ref +); + +ruby_return_def( + unique int id: @ruby_return, + int loc: @location ref +); + +@ruby_right_assignment_list_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_right_assignment_list, index] +ruby_right_assignment_list_child( + int ruby_right_assignment_list: @ruby_right_assignment_list ref, + int index: int ref, + unique int child: @ruby_right_assignment_list_child_type ref +); + +ruby_right_assignment_list_def( + unique int id: @ruby_right_assignment_list, + int loc: @location ref +); + +@ruby_scope_resolution_name_type = @ruby_token_constant | @ruby_token_identifier + +ruby_scope_resolution_scope( + unique int ruby_scope_resolution: @ruby_scope_resolution ref, + unique int scope: @ruby_underscore_primary ref +); + +ruby_scope_resolution_def( + unique int id: @ruby_scope_resolution, + int name: @ruby_scope_resolution_name_type ref, + int loc: @location ref +); + +ruby_setter_def( + unique int id: @ruby_setter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_singleton_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_class, index] +ruby_singleton_class_child( + int ruby_singleton_class: @ruby_singleton_class ref, + int index: int ref, + unique int child: @ruby_singleton_class_child_type ref +); + +ruby_singleton_class_def( + unique int id: @ruby_singleton_class, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_singleton_method_object_type = @ruby_underscore_arg | @ruby_underscore_variable + +ruby_singleton_method_parameters( + unique int ruby_singleton_method: @ruby_singleton_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_singleton_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_method, index] +ruby_singleton_method_child( + int ruby_singleton_method: @ruby_singleton_method ref, + int index: int ref, + unique int child: @ruby_singleton_method_child_type ref +); + +ruby_singleton_method_def( + unique int id: @ruby_singleton_method, + int name: @ruby_underscore_method_name ref, + int object: @ruby_singleton_method_object_type ref, + int loc: @location ref +); + +ruby_splat_argument_def( + unique int id: @ruby_splat_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_splat_parameter_name( + unique int ruby_splat_parameter: @ruby_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_splat_parameter_def( + unique int id: @ruby_splat_parameter, + int loc: @location ref +); + +@ruby_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_string__, index] +ruby_string_child( + int ruby_string__: @ruby_string__ ref, + int index: int ref, + unique int child: @ruby_string_child_type ref +); + +ruby_string_def( + unique int id: @ruby_string__, + int loc: @location ref +); + +#keyset[ruby_string_array, index] +ruby_string_array_child( + int ruby_string_array: @ruby_string_array ref, + int index: int ref, + unique int child: @ruby_bare_string ref +); + +ruby_string_array_def( + unique int id: @ruby_string_array, + int loc: @location ref +); + +@ruby_subshell_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_subshell, index] +ruby_subshell_child( + int ruby_subshell: @ruby_subshell ref, + int index: int ref, + unique int child: @ruby_subshell_child_type ref +); + +ruby_subshell_def( + unique int id: @ruby_subshell, + int loc: @location ref +); + +@ruby_superclass_child_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_superclass_def( + unique int id: @ruby_superclass, + int child: @ruby_superclass_child_type ref, + int loc: @location ref +); + +#keyset[ruby_symbol_array, index] +ruby_symbol_array_child( + int ruby_symbol_array: @ruby_symbol_array ref, + int index: int ref, + unique int child: @ruby_bare_symbol ref +); + +ruby_symbol_array_def( + unique int id: @ruby_symbol_array, + int loc: @location ref +); + +@ruby_then_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_then, index] +ruby_then_child( + int ruby_then: @ruby_then ref, + int index: int ref, + unique int child: @ruby_then_child_type ref +); + +ruby_then_def( + unique int id: @ruby_then, + int loc: @location ref +); + +@ruby_unary_operand_type = @ruby_break | @ruby_call | @ruby_next | @ruby_parenthesized_statements | @ruby_return | @ruby_token_float | @ruby_token_integer | @ruby_underscore_arg | @ruby_yield + +case @ruby_unary.operator of + 0 = @ruby_unary_bang +| 1 = @ruby_unary_plus +| 2 = @ruby_unary_minus +| 3 = @ruby_unary_definedquestion +| 4 = @ruby_unary_not +| 5 = @ruby_unary_tilde +; + + +ruby_unary_def( + unique int id: @ruby_unary, + int operand: @ruby_unary_operand_type ref, + int operator: int ref, + int loc: @location ref +); + +#keyset[ruby_undef, index] +ruby_undef_child( + int ruby_undef: @ruby_undef ref, + int index: int ref, + unique int child: @ruby_underscore_method_name ref +); + +ruby_undef_def( + unique int id: @ruby_undef, + int loc: @location ref +); + +@ruby_unless_alternative_type = @ruby_else | @ruby_elsif + +ruby_unless_alternative( + unique int ruby_unless: @ruby_unless ref, + unique int alternative: @ruby_unless_alternative_type ref +); + +ruby_unless_consequence( + unique int ruby_unless: @ruby_unless ref, + unique int consequence: @ruby_then ref +); + +ruby_unless_def( + unique int id: @ruby_unless, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_unless_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_unless_modifier_def( + unique int id: @ruby_unless_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_unless_modifier_condition_type ref, + int loc: @location ref +); + +ruby_until_def( + unique int id: @ruby_until, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_until_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_until_modifier_def( + unique int id: @ruby_until_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_until_modifier_condition_type ref, + int loc: @location ref +); + +ruby_when_body( + unique int ruby_when: @ruby_when ref, + unique int body: @ruby_then ref +); + +#keyset[ruby_when, index] +ruby_when_pattern( + int ruby_when: @ruby_when ref, + int index: int ref, + unique int pattern: @ruby_pattern ref +); + +ruby_when_def( + unique int id: @ruby_when, + int loc: @location ref +); + +ruby_while_def( + unique int id: @ruby_while, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_while_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_while_modifier_def( + unique int id: @ruby_while_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_while_modifier_condition_type ref, + int loc: @location ref +); + +ruby_yield_child( + unique int ruby_yield: @ruby_yield ref, + unique int child: @ruby_argument_list ref +); + +ruby_yield_def( + unique int id: @ruby_yield, + int loc: @location ref +); + +ruby_tokeninfo( + unique int id: @ruby_token, + int kind: int ref, + string value: string ref, + int loc: @location ref +); + +case @ruby_token.kind of + 0 = @ruby_reserved_word +| 1 = @ruby_token_character +| 2 = @ruby_token_class_variable +| 3 = @ruby_token_comment +| 4 = @ruby_token_complex +| 5 = @ruby_token_constant +| 6 = @ruby_token_empty_statement +| 7 = @ruby_token_escape_sequence +| 8 = @ruby_token_false +| 9 = @ruby_token_float +| 10 = @ruby_token_forward_argument +| 11 = @ruby_token_forward_parameter +| 12 = @ruby_token_global_variable +| 13 = @ruby_token_hash_key_symbol +| 14 = @ruby_token_heredoc_beginning +| 15 = @ruby_token_heredoc_content +| 16 = @ruby_token_heredoc_end +| 17 = @ruby_token_identifier +| 18 = @ruby_token_instance_variable +| 19 = @ruby_token_integer +| 20 = @ruby_token_nil +| 21 = @ruby_token_operator +| 22 = @ruby_token_self +| 23 = @ruby_token_simple_symbol +| 24 = @ruby_token_string_content +| 25 = @ruby_token_super +| 26 = @ruby_token_true +| 27 = @ruby_token_uninterpreted +; + + +@ruby_ast_node = @ruby_alias | @ruby_argument_list | @ruby_array | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_parameter | @ruby_block_parameters | @ruby_break | @ruby_call | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_for | @ruby_hash | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_modifier | @ruby_in | @ruby_interpolation | @ruby_keyword_parameter | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield + +@ruby_ast_node_parent = @file | @ruby_ast_node + +#keyset[parent, parent_index] +ruby_ast_node_parent( + int child: @ruby_ast_node ref, + int parent: @ruby_ast_node_parent ref, + int parent_index: int ref +); + +erb_comment_directive_def( + unique int id: @erb_comment_directive, + int child: @erb_token_comment ref, + int loc: @location ref +); + +erb_directive_def( + unique int id: @erb_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +erb_graphql_directive_def( + unique int id: @erb_graphql_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +erb_output_directive_def( + unique int id: @erb_output_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +@erb_template_child_type = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_token_content + +#keyset[erb_template, index] +erb_template_child( + int erb_template: @erb_template ref, + int index: int ref, + unique int child: @erb_template_child_type ref +); + +erb_template_def( + unique int id: @erb_template, + int loc: @location ref +); + +erb_tokeninfo( + unique int id: @erb_token, + int kind: int ref, + string value: string ref, + int loc: @location ref +); + +case @erb_token.kind of + 0 = @erb_reserved_word +| 1 = @erb_token_code +| 2 = @erb_token_comment +| 3 = @erb_token_content +; + + +@erb_ast_node = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_template | @erb_token + +@erb_ast_node_parent = @erb_ast_node | @file + +#keyset[parent, parent_index] +erb_ast_node_parent( + int child: @erb_ast_node ref, + int parent: @erb_ast_node_parent ref, + int parent_index: int ref +); + diff --git a/ruby/ql/lib/upgrades/31a238d080f3dd563d7225fc0458254617d1e5ba/ruby_tokeninfo.ql b/ruby/ql/lib/upgrades/31a238d080f3dd563d7225fc0458254617d1e5ba/ruby_tokeninfo.ql new file mode 100644 index 00000000000..8c9d98afcf4 --- /dev/null +++ b/ruby/ql/lib/upgrades/31a238d080f3dd563d7225fc0458254617d1e5ba/ruby_tokeninfo.ql @@ -0,0 +1,14 @@ +private class RubyToken extends @ruby_token { + string toString() { none() } +} + +private class Location extends @location { + string toString() { none() } +} + +bindingset[kind] +private int newKind(int kind) { if kind >= 10 then result = kind + 2 else result = kind } + +from RubyToken id, int kind, string value, Location loc +where ruby_tokeninfo(id, kind, value, loc) +select id, newKind(kind), value, loc diff --git a/ruby/ql/lib/upgrades/31a238d080f3dd563d7225fc0458254617d1e5ba/upgrade.properties b/ruby/ql/lib/upgrades/31a238d080f3dd563d7225fc0458254617d1e5ba/upgrade.properties new file mode 100644 index 00000000000..563037caa4c --- /dev/null +++ b/ruby/ql/lib/upgrades/31a238d080f3dd563d7225fc0458254617d1e5ba/upgrade.properties @@ -0,0 +1,3 @@ +description: Re-number @ruby_token.kind +compatibility: full +ruby_tokeninfo.rel: run ruby_tokeninfo.qlo diff --git a/ruby/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/old.dbscheme b/ruby/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/old.dbscheme new file mode 100644 index 00000000000..40be81bc208 --- /dev/null +++ b/ruby/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/old.dbscheme @@ -0,0 +1,1267 @@ +// CodeQL database schema for Ruby +// Automatically generated from the tree-sitter grammar; do not edit + +@location = @location_default + +locations_default( + unique int id: @location_default, + int file: @file ref, + int start_line: int ref, + int start_column: int ref, + int end_line: int ref, + int end_column: int ref +); + +@sourceline = @file + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref +); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +sourceLocationPrefix( + string prefix: string ref +); + +@underscore_arg = @assignment | @binary | @conditional | @operator_assignment | @range | @unary | @underscore_primary + +@underscore_lhs = @call | @element_reference | @scope_resolution | @token_false | @token_nil | @token_true | @underscore_variable + +@underscore_method_name = @delimited_symbol | @setter | @token_class_variable | @token_constant | @token_global_variable | @token_identifier | @token_instance_variable | @token_operator | @token_simple_symbol + +@underscore_primary = @array | @begin | @break | @case__ | @chained_string | @class | @delimited_symbol | @for | @hash | @if | @lambda | @method | @module | @next | @parenthesized_statements | @rational | @redo | @regex | @retry | @return | @singleton_class | @singleton_method | @string__ | @string_array | @subshell | @symbol_array | @token_character | @token_complex | @token_float | @token_heredoc_beginning | @token_integer | @token_simple_symbol | @unary | @underscore_lhs | @unless | @until | @while | @yield + +@underscore_statement = @alias | @assignment | @begin_block | @binary | @break | @call | @end_block | @if_modifier | @next | @operator_assignment | @rescue_modifier | @return | @unary | @undef | @underscore_arg | @unless_modifier | @until_modifier | @while_modifier | @yield + +@underscore_variable = @token_class_variable | @token_constant | @token_global_variable | @token_identifier | @token_instance_variable | @token_self | @token_super + +alias_def( + unique int id: @alias, + int alias: @underscore_method_name ref, + int name: @underscore_method_name ref, + int loc: @location ref +); + +@argument_list_child_type = @block_argument | @break | @call | @hash_splat_argument | @next | @pair | @return | @splat_argument | @underscore_arg | @yield + +#keyset[argument_list, index] +argument_list_child( + int argument_list: @argument_list ref, + int index: int ref, + unique int child: @argument_list_child_type ref +); + +argument_list_def( + unique int id: @argument_list, + int loc: @location ref +); + +@array_child_type = @block_argument | @break | @call | @hash_splat_argument | @next | @pair | @return | @splat_argument | @underscore_arg | @yield + +#keyset[array, index] +array_child( + int array: @array ref, + int index: int ref, + unique int child: @array_child_type ref +); + +array_def( + unique int id: @array, + int loc: @location ref +); + +@assignment_left_type = @left_assignment_list | @underscore_lhs + +@assignment_right_type = @break | @call | @next | @return | @right_assignment_list | @splat_argument | @underscore_arg | @yield + +assignment_def( + unique int id: @assignment, + int left: @assignment_left_type ref, + int right: @assignment_right_type ref, + int loc: @location ref +); + +@bare_string_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[bare_string, index] +bare_string_child( + int bare_string: @bare_string ref, + int index: int ref, + unique int child: @bare_string_child_type ref +); + +bare_string_def( + unique int id: @bare_string, + int loc: @location ref +); + +@bare_symbol_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[bare_symbol, index] +bare_symbol_child( + int bare_symbol: @bare_symbol ref, + int index: int ref, + unique int child: @bare_symbol_child_type ref +); + +bare_symbol_def( + unique int id: @bare_symbol, + int loc: @location ref +); + +@begin_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[begin, index] +begin_child( + int begin: @begin ref, + int index: int ref, + unique int child: @begin_child_type ref +); + +begin_def( + unique int id: @begin, + int loc: @location ref +); + +@begin_block_child_type = @token_empty_statement | @underscore_statement + +#keyset[begin_block, index] +begin_block_child( + int begin_block: @begin_block ref, + int index: int ref, + unique int child: @begin_block_child_type ref +); + +begin_block_def( + unique int id: @begin_block, + int loc: @location ref +); + +@binary_left_type = @break | @call | @next | @return | @underscore_arg | @yield + +case @binary.operator of + 0 = @binary_bangequal +| 1 = @binary_bangtilde +| 2 = @binary_percent +| 3 = @binary_ampersand +| 4 = @binary_ampersandampersand +| 5 = @binary_star +| 6 = @binary_starstar +| 7 = @binary_plus +| 8 = @binary_minus +| 9 = @binary_slash +| 10 = @binary_langle +| 11 = @binary_langlelangle +| 12 = @binary_langleequal +| 13 = @binary_langleequalrangle +| 14 = @binary_equalequal +| 15 = @binary_equalequalequal +| 16 = @binary_equaltilde +| 17 = @binary_rangle +| 18 = @binary_rangleequal +| 19 = @binary_ranglerangle +| 20 = @binary_caret +| 21 = @binary_and +| 22 = @binary_or +| 23 = @binary_pipe +| 24 = @binary_pipepipe +; + + +@binary_right_type = @break | @call | @next | @return | @underscore_arg | @yield + +binary_def( + unique int id: @binary, + int left: @binary_left_type ref, + int operator: int ref, + int right: @binary_right_type ref, + int loc: @location ref +); + +block_parameters( + unique int block: @block ref, + unique int parameters: @block_parameters ref +); + +@block_child_type = @token_empty_statement | @underscore_statement + +#keyset[block, index] +block_child( + int block: @block ref, + int index: int ref, + unique int child: @block_child_type ref +); + +block_def( + unique int id: @block, + int loc: @location ref +); + +block_argument_def( + unique int id: @block_argument, + int child: @underscore_arg ref, + int loc: @location ref +); + +block_parameter_def( + unique int id: @block_parameter, + int name: @token_identifier ref, + int loc: @location ref +); + +@block_parameters_child_type = @block_parameter | @destructured_parameter | @hash_splat_parameter | @keyword_parameter | @optional_parameter | @splat_parameter | @token_identifier + +#keyset[block_parameters, index] +block_parameters_child( + int block_parameters: @block_parameters ref, + int index: int ref, + unique int child: @block_parameters_child_type ref +); + +block_parameters_def( + unique int id: @block_parameters, + int loc: @location ref +); + +break_child( + unique int break: @break ref, + unique int child: @argument_list ref +); + +break_def( + unique int id: @break, + int loc: @location ref +); + +call_arguments( + unique int call: @call ref, + unique int arguments: @argument_list ref +); + +@call_block_type = @block | @do_block + +call_block( + unique int call: @call ref, + unique int block: @call_block_type ref +); + +@call_method_type = @argument_list | @scope_resolution | @token_operator | @underscore_variable + +@call_receiver_type = @call | @underscore_primary + +call_receiver( + unique int call: @call ref, + unique int receiver: @call_receiver_type ref +); + +call_def( + unique int id: @call, + int method: @call_method_type ref, + int loc: @location ref +); + +case_value( + unique int case__: @case__ ref, + unique int value: @underscore_statement ref +); + +@case_child_type = @else | @when + +#keyset[case__, index] +case_child( + int case__: @case__ ref, + int index: int ref, + unique int child: @case_child_type ref +); + +case_def( + unique int id: @case__, + int loc: @location ref +); + +#keyset[chained_string, index] +chained_string_child( + int chained_string: @chained_string ref, + int index: int ref, + unique int child: @string__ ref +); + +chained_string_def( + unique int id: @chained_string, + int loc: @location ref +); + +@class_name_type = @scope_resolution | @token_constant + +class_superclass( + unique int class: @class ref, + unique int superclass: @superclass ref +); + +@class_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[class, index] +class_child( + int class: @class ref, + int index: int ref, + unique int child: @class_child_type ref +); + +class_def( + unique int id: @class, + int name: @class_name_type ref, + int loc: @location ref +); + +conditional_def( + unique int id: @conditional, + int alternative: @underscore_arg ref, + int condition: @underscore_arg ref, + int consequence: @underscore_arg ref, + int loc: @location ref +); + +@delimited_symbol_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[delimited_symbol, index] +delimited_symbol_child( + int delimited_symbol: @delimited_symbol ref, + int index: int ref, + unique int child: @delimited_symbol_child_type ref +); + +delimited_symbol_def( + unique int id: @delimited_symbol, + int loc: @location ref +); + +@destructured_left_assignment_child_type = @destructured_left_assignment | @rest_assignment | @underscore_lhs + +#keyset[destructured_left_assignment, index] +destructured_left_assignment_child( + int destructured_left_assignment: @destructured_left_assignment ref, + int index: int ref, + unique int child: @destructured_left_assignment_child_type ref +); + +destructured_left_assignment_def( + unique int id: @destructured_left_assignment, + int loc: @location ref +); + +@destructured_parameter_child_type = @block_parameter | @destructured_parameter | @hash_splat_parameter | @keyword_parameter | @optional_parameter | @splat_parameter | @token_identifier + +#keyset[destructured_parameter, index] +destructured_parameter_child( + int destructured_parameter: @destructured_parameter ref, + int index: int ref, + unique int child: @destructured_parameter_child_type ref +); + +destructured_parameter_def( + unique int id: @destructured_parameter, + int loc: @location ref +); + +@do_child_type = @token_empty_statement | @underscore_statement + +#keyset[do, index] +do_child( + int do: @do ref, + int index: int ref, + unique int child: @do_child_type ref +); + +do_def( + unique int id: @do, + int loc: @location ref +); + +do_block_parameters( + unique int do_block: @do_block ref, + unique int parameters: @block_parameters ref +); + +@do_block_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[do_block, index] +do_block_child( + int do_block: @do_block ref, + int index: int ref, + unique int child: @do_block_child_type ref +); + +do_block_def( + unique int id: @do_block, + int loc: @location ref +); + +@element_reference_child_type = @block_argument | @break | @call | @hash_splat_argument | @next | @pair | @return | @splat_argument | @underscore_arg | @yield + +#keyset[element_reference, index] +element_reference_child( + int element_reference: @element_reference ref, + int index: int ref, + unique int child: @element_reference_child_type ref +); + +element_reference_def( + unique int id: @element_reference, + int object: @underscore_primary ref, + int loc: @location ref +); + +@else_child_type = @token_empty_statement | @underscore_statement + +#keyset[else, index] +else_child( + int else: @else ref, + int index: int ref, + unique int child: @else_child_type ref +); + +else_def( + unique int id: @else, + int loc: @location ref +); + +@elsif_alternative_type = @else | @elsif + +elsif_alternative( + unique int elsif: @elsif ref, + unique int alternative: @elsif_alternative_type ref +); + +elsif_consequence( + unique int elsif: @elsif ref, + unique int consequence: @then ref +); + +elsif_def( + unique int id: @elsif, + int condition: @underscore_statement ref, + int loc: @location ref +); + +@end_block_child_type = @token_empty_statement | @underscore_statement + +#keyset[end_block, index] +end_block_child( + int end_block: @end_block ref, + int index: int ref, + unique int child: @end_block_child_type ref +); + +end_block_def( + unique int id: @end_block, + int loc: @location ref +); + +@ensure_child_type = @token_empty_statement | @underscore_statement + +#keyset[ensure, index] +ensure_child( + int ensure: @ensure ref, + int index: int ref, + unique int child: @ensure_child_type ref +); + +ensure_def( + unique int id: @ensure, + int loc: @location ref +); + +exception_variable_def( + unique int id: @exception_variable, + int child: @underscore_lhs ref, + int loc: @location ref +); + +@exceptions_child_type = @splat_argument | @underscore_arg + +#keyset[exceptions, index] +exceptions_child( + int exceptions: @exceptions ref, + int index: int ref, + unique int child: @exceptions_child_type ref +); + +exceptions_def( + unique int id: @exceptions, + int loc: @location ref +); + +@for_pattern_type = @left_assignment_list | @underscore_lhs + +for_def( + unique int id: @for, + int body: @do ref, + int pattern: @for_pattern_type ref, + int value: @in ref, + int loc: @location ref +); + +@hash_child_type = @hash_splat_argument | @pair + +#keyset[hash, index] +hash_child( + int hash: @hash ref, + int index: int ref, + unique int child: @hash_child_type ref +); + +hash_def( + unique int id: @hash, + int loc: @location ref +); + +hash_splat_argument_def( + unique int id: @hash_splat_argument, + int child: @underscore_arg ref, + int loc: @location ref +); + +hash_splat_parameter_name( + unique int hash_splat_parameter: @hash_splat_parameter ref, + unique int name: @token_identifier ref +); + +hash_splat_parameter_def( + unique int id: @hash_splat_parameter, + int loc: @location ref +); + +@heredoc_body_child_type = @interpolation | @token_escape_sequence | @token_heredoc_content | @token_heredoc_end + +#keyset[heredoc_body, index] +heredoc_body_child( + int heredoc_body: @heredoc_body ref, + int index: int ref, + unique int child: @heredoc_body_child_type ref +); + +heredoc_body_def( + unique int id: @heredoc_body, + int loc: @location ref +); + +@if_alternative_type = @else | @elsif + +if_alternative( + unique int if: @if ref, + unique int alternative: @if_alternative_type ref +); + +if_consequence( + unique int if: @if ref, + unique int consequence: @then ref +); + +if_def( + unique int id: @if, + int condition: @underscore_statement ref, + int loc: @location ref +); + +@if_modifier_condition_type = @break | @call | @next | @return | @underscore_arg | @yield + +if_modifier_def( + unique int id: @if_modifier, + int body: @underscore_statement ref, + int condition: @if_modifier_condition_type ref, + int loc: @location ref +); + +in_def( + unique int id: @in, + int child: @underscore_arg ref, + int loc: @location ref +); + +@interpolation_child_type = @token_empty_statement | @underscore_statement + +#keyset[interpolation, index] +interpolation_child( + int interpolation: @interpolation ref, + int index: int ref, + unique int child: @interpolation_child_type ref +); + +interpolation_def( + unique int id: @interpolation, + int loc: @location ref +); + +keyword_parameter_value( + unique int keyword_parameter: @keyword_parameter ref, + unique int value: @underscore_arg ref +); + +keyword_parameter_def( + unique int id: @keyword_parameter, + int name: @token_identifier ref, + int loc: @location ref +); + +@lambda_body_type = @block | @do_block + +lambda_parameters( + unique int lambda: @lambda ref, + unique int parameters: @lambda_parameters ref +); + +lambda_def( + unique int id: @lambda, + int body: @lambda_body_type ref, + int loc: @location ref +); + +@lambda_parameters_child_type = @block_parameter | @destructured_parameter | @hash_splat_parameter | @keyword_parameter | @optional_parameter | @splat_parameter | @token_identifier + +#keyset[lambda_parameters, index] +lambda_parameters_child( + int lambda_parameters: @lambda_parameters ref, + int index: int ref, + unique int child: @lambda_parameters_child_type ref +); + +lambda_parameters_def( + unique int id: @lambda_parameters, + int loc: @location ref +); + +@left_assignment_list_child_type = @destructured_left_assignment | @rest_assignment | @underscore_lhs + +#keyset[left_assignment_list, index] +left_assignment_list_child( + int left_assignment_list: @left_assignment_list ref, + int index: int ref, + unique int child: @left_assignment_list_child_type ref +); + +left_assignment_list_def( + unique int id: @left_assignment_list, + int loc: @location ref +); + +method_parameters( + unique int method: @method ref, + unique int parameters: @method_parameters ref +); + +@method_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[method, index] +method_child( + int method: @method ref, + int index: int ref, + unique int child: @method_child_type ref +); + +method_def( + unique int id: @method, + int name: @underscore_method_name ref, + int loc: @location ref +); + +@method_parameters_child_type = @block_parameter | @destructured_parameter | @hash_splat_parameter | @keyword_parameter | @optional_parameter | @splat_parameter | @token_identifier + +#keyset[method_parameters, index] +method_parameters_child( + int method_parameters: @method_parameters ref, + int index: int ref, + unique int child: @method_parameters_child_type ref +); + +method_parameters_def( + unique int id: @method_parameters, + int loc: @location ref +); + +@module_name_type = @scope_resolution | @token_constant + +@module_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[module, index] +module_child( + int module: @module ref, + int index: int ref, + unique int child: @module_child_type ref +); + +module_def( + unique int id: @module, + int name: @module_name_type ref, + int loc: @location ref +); + +next_child( + unique int next: @next ref, + unique int child: @argument_list ref +); + +next_def( + unique int id: @next, + int loc: @location ref +); + +case @operator_assignment.operator of + 0 = @operator_assignment_percentequal +| 1 = @operator_assignment_ampersandampersandequal +| 2 = @operator_assignment_ampersandequal +| 3 = @operator_assignment_starstarequal +| 4 = @operator_assignment_starequal +| 5 = @operator_assignment_plusequal +| 6 = @operator_assignment_minusequal +| 7 = @operator_assignment_slashequal +| 8 = @operator_assignment_langlelangleequal +| 9 = @operator_assignment_ranglerangleequal +| 10 = @operator_assignment_caretequal +| 11 = @operator_assignment_pipeequal +| 12 = @operator_assignment_pipepipeequal +; + + +@operator_assignment_right_type = @break | @call | @next | @return | @underscore_arg | @yield + +operator_assignment_def( + unique int id: @operator_assignment, + int left: @underscore_lhs ref, + int operator: int ref, + int right: @operator_assignment_right_type ref, + int loc: @location ref +); + +optional_parameter_def( + unique int id: @optional_parameter, + int name: @token_identifier ref, + int value: @underscore_arg ref, + int loc: @location ref +); + +@pair_key_type = @string__ | @token_hash_key_symbol | @underscore_arg + +pair_def( + unique int id: @pair, + int key__: @pair_key_type ref, + int value: @underscore_arg ref, + int loc: @location ref +); + +@parenthesized_statements_child_type = @token_empty_statement | @underscore_statement + +#keyset[parenthesized_statements, index] +parenthesized_statements_child( + int parenthesized_statements: @parenthesized_statements ref, + int index: int ref, + unique int child: @parenthesized_statements_child_type ref +); + +parenthesized_statements_def( + unique int id: @parenthesized_statements, + int loc: @location ref +); + +@pattern_child_type = @splat_argument | @underscore_arg + +pattern_def( + unique int id: @pattern, + int child: @pattern_child_type ref, + int loc: @location ref +); + +@program_child_type = @token_empty_statement | @token_uninterpreted | @underscore_statement + +#keyset[program, index] +program_child( + int program: @program ref, + int index: int ref, + unique int child: @program_child_type ref +); + +program_def( + unique int id: @program, + int loc: @location ref +); + +range_begin( + unique int range: @range ref, + unique int begin: @underscore_arg ref +); + +range_end( + unique int range: @range ref, + unique int end: @underscore_arg ref +); + +case @range.operator of + 0 = @range_dotdot +| 1 = @range_dotdotdot +; + + +range_def( + unique int id: @range, + int operator: int ref, + int loc: @location ref +); + +@rational_child_type = @token_float | @token_integer + +rational_def( + unique int id: @rational, + int child: @rational_child_type ref, + int loc: @location ref +); + +redo_child( + unique int redo: @redo ref, + unique int child: @argument_list ref +); + +redo_def( + unique int id: @redo, + int loc: @location ref +); + +@regex_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[regex, index] +regex_child( + int regex: @regex ref, + int index: int ref, + unique int child: @regex_child_type ref +); + +regex_def( + unique int id: @regex, + int loc: @location ref +); + +rescue_body( + unique int rescue: @rescue ref, + unique int body: @then ref +); + +rescue_exceptions( + unique int rescue: @rescue ref, + unique int exceptions: @exceptions ref +); + +rescue_variable( + unique int rescue: @rescue ref, + unique int variable: @exception_variable ref +); + +rescue_def( + unique int id: @rescue, + int loc: @location ref +); + +@rescue_modifier_handler_type = @break | @call | @next | @return | @underscore_arg | @yield + +rescue_modifier_def( + unique int id: @rescue_modifier, + int body: @underscore_statement ref, + int handler: @rescue_modifier_handler_type ref, + int loc: @location ref +); + +rest_assignment_child( + unique int rest_assignment: @rest_assignment ref, + unique int child: @underscore_lhs ref +); + +rest_assignment_def( + unique int id: @rest_assignment, + int loc: @location ref +); + +retry_child( + unique int retry: @retry ref, + unique int child: @argument_list ref +); + +retry_def( + unique int id: @retry, + int loc: @location ref +); + +return_child( + unique int return: @return ref, + unique int child: @argument_list ref +); + +return_def( + unique int id: @return, + int loc: @location ref +); + +@right_assignment_list_child_type = @splat_argument | @underscore_arg + +#keyset[right_assignment_list, index] +right_assignment_list_child( + int right_assignment_list: @right_assignment_list ref, + int index: int ref, + unique int child: @right_assignment_list_child_type ref +); + +right_assignment_list_def( + unique int id: @right_assignment_list, + int loc: @location ref +); + +@scope_resolution_name_type = @token_constant | @token_identifier + +scope_resolution_scope( + unique int scope_resolution: @scope_resolution ref, + unique int scope: @underscore_primary ref +); + +scope_resolution_def( + unique int id: @scope_resolution, + int name: @scope_resolution_name_type ref, + int loc: @location ref +); + +setter_def( + unique int id: @setter, + int name: @token_identifier ref, + int loc: @location ref +); + +@singleton_class_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[singleton_class, index] +singleton_class_child( + int singleton_class: @singleton_class ref, + int index: int ref, + unique int child: @singleton_class_child_type ref +); + +singleton_class_def( + unique int id: @singleton_class, + int value: @underscore_arg ref, + int loc: @location ref +); + +@singleton_method_object_type = @underscore_arg | @underscore_variable + +singleton_method_parameters( + unique int singleton_method: @singleton_method ref, + unique int parameters: @method_parameters ref +); + +@singleton_method_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[singleton_method, index] +singleton_method_child( + int singleton_method: @singleton_method ref, + int index: int ref, + unique int child: @singleton_method_child_type ref +); + +singleton_method_def( + unique int id: @singleton_method, + int name: @underscore_method_name ref, + int object: @singleton_method_object_type ref, + int loc: @location ref +); + +splat_argument_def( + unique int id: @splat_argument, + int child: @underscore_arg ref, + int loc: @location ref +); + +splat_parameter_name( + unique int splat_parameter: @splat_parameter ref, + unique int name: @token_identifier ref +); + +splat_parameter_def( + unique int id: @splat_parameter, + int loc: @location ref +); + +@string_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[string__, index] +string_child( + int string__: @string__ ref, + int index: int ref, + unique int child: @string_child_type ref +); + +string_def( + unique int id: @string__, + int loc: @location ref +); + +#keyset[string_array, index] +string_array_child( + int string_array: @string_array ref, + int index: int ref, + unique int child: @bare_string ref +); + +string_array_def( + unique int id: @string_array, + int loc: @location ref +); + +@subshell_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[subshell, index] +subshell_child( + int subshell: @subshell ref, + int index: int ref, + unique int child: @subshell_child_type ref +); + +subshell_def( + unique int id: @subshell, + int loc: @location ref +); + +@superclass_child_type = @break | @call | @next | @return | @underscore_arg | @yield + +superclass_def( + unique int id: @superclass, + int child: @superclass_child_type ref, + int loc: @location ref +); + +#keyset[symbol_array, index] +symbol_array_child( + int symbol_array: @symbol_array ref, + int index: int ref, + unique int child: @bare_symbol ref +); + +symbol_array_def( + unique int id: @symbol_array, + int loc: @location ref +); + +@then_child_type = @token_empty_statement | @underscore_statement + +#keyset[then, index] +then_child( + int then: @then ref, + int index: int ref, + unique int child: @then_child_type ref +); + +then_def( + unique int id: @then, + int loc: @location ref +); + +@unary_operand_type = @break | @call | @next | @parenthesized_statements | @return | @token_float | @token_integer | @underscore_arg | @yield + +case @unary.operator of + 0 = @unary_bang +| 1 = @unary_plus +| 2 = @unary_minus +| 3 = @unary_definedquestion +| 4 = @unary_not +| 5 = @unary_tilde +; + + +unary_def( + unique int id: @unary, + int operand: @unary_operand_type ref, + int operator: int ref, + int loc: @location ref +); + +#keyset[undef, index] +undef_child( + int undef: @undef ref, + int index: int ref, + unique int child: @underscore_method_name ref +); + +undef_def( + unique int id: @undef, + int loc: @location ref +); + +@unless_alternative_type = @else | @elsif + +unless_alternative( + unique int unless: @unless ref, + unique int alternative: @unless_alternative_type ref +); + +unless_consequence( + unique int unless: @unless ref, + unique int consequence: @then ref +); + +unless_def( + unique int id: @unless, + int condition: @underscore_statement ref, + int loc: @location ref +); + +@unless_modifier_condition_type = @break | @call | @next | @return | @underscore_arg | @yield + +unless_modifier_def( + unique int id: @unless_modifier, + int body: @underscore_statement ref, + int condition: @unless_modifier_condition_type ref, + int loc: @location ref +); + +until_def( + unique int id: @until, + int body: @do ref, + int condition: @underscore_statement ref, + int loc: @location ref +); + +@until_modifier_condition_type = @break | @call | @next | @return | @underscore_arg | @yield + +until_modifier_def( + unique int id: @until_modifier, + int body: @underscore_statement ref, + int condition: @until_modifier_condition_type ref, + int loc: @location ref +); + +when_body( + unique int when: @when ref, + unique int body: @then ref +); + +#keyset[when, index] +when_pattern( + int when: @when ref, + int index: int ref, + unique int pattern: @pattern ref +); + +when_def( + unique int id: @when, + int loc: @location ref +); + +while_def( + unique int id: @while, + int body: @do ref, + int condition: @underscore_statement ref, + int loc: @location ref +); + +@while_modifier_condition_type = @break | @call | @next | @return | @underscore_arg | @yield + +while_modifier_def( + unique int id: @while_modifier, + int body: @underscore_statement ref, + int condition: @while_modifier_condition_type ref, + int loc: @location ref +); + +yield_child( + unique int yield: @yield ref, + unique int child: @argument_list ref +); + +yield_def( + unique int id: @yield, + int loc: @location ref +); + +tokeninfo( + unique int id: @token, + int kind: int ref, + int file: @file ref, + int idx: int ref, + string value: string ref, + int loc: @location ref +); + +case @token.kind of + 0 = @reserved_word +| 1 = @token_character +| 2 = @token_class_variable +| 3 = @token_comment +| 4 = @token_complex +| 5 = @token_constant +| 6 = @token_empty_statement +| 7 = @token_escape_sequence +| 8 = @token_false +| 9 = @token_float +| 10 = @token_global_variable +| 11 = @token_hash_key_symbol +| 12 = @token_heredoc_beginning +| 13 = @token_heredoc_content +| 14 = @token_heredoc_end +| 15 = @token_identifier +| 16 = @token_instance_variable +| 17 = @token_integer +| 18 = @token_nil +| 19 = @token_operator +| 20 = @token_self +| 21 = @token_simple_symbol +| 22 = @token_string_content +| 23 = @token_super +| 24 = @token_true +| 25 = @token_uninterpreted +; + + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +case @diagnostic.severity of + 10 = @diagnostic_debug +| 20 = @diagnostic_info +| 30 = @diagnostic_warning +| 40 = @diagnostic_error +; + + +@ast_node = @alias | @argument_list | @array | @assignment | @bare_string | @bare_symbol | @begin | @begin_block | @binary | @block | @block_argument | @block_parameter | @block_parameters | @break | @call | @case__ | @chained_string | @class | @conditional | @delimited_symbol | @destructured_left_assignment | @destructured_parameter | @do | @do_block | @element_reference | @else | @elsif | @end_block | @ensure | @exception_variable | @exceptions | @for | @hash | @hash_splat_argument | @hash_splat_parameter | @heredoc_body | @if | @if_modifier | @in | @interpolation | @keyword_parameter | @lambda | @lambda_parameters | @left_assignment_list | @method | @method_parameters | @module | @next | @operator_assignment | @optional_parameter | @pair | @parenthesized_statements | @pattern | @program | @range | @rational | @redo | @regex | @rescue | @rescue_modifier | @rest_assignment | @retry | @return | @right_assignment_list | @scope_resolution | @setter | @singleton_class | @singleton_method | @splat_argument | @splat_parameter | @string__ | @string_array | @subshell | @superclass | @symbol_array | @then | @token | @unary | @undef | @unless | @unless_modifier | @until | @until_modifier | @when | @while | @while_modifier | @yield + +@ast_node_parent = @ast_node | @file + +#keyset[parent, parent_index] +ast_node_parent( + int child: @ast_node ref, + int parent: @ast_node_parent ref, + int parent_index: int ref +); + diff --git a/ruby/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/ruby.dbscheme b/ruby/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/ruby.dbscheme new file mode 100644 index 00000000000..09a494ce67d --- /dev/null +++ b/ruby/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/ruby.dbscheme @@ -0,0 +1,1333 @@ +// CodeQL database schema for Ruby +// Automatically generated from the tree-sitter grammar; do not edit + +@location = @location_default + +locations_default( + unique int id: @location_default, + int file: @file ref, + int start_line: int ref, + int start_column: int ref, + int end_line: int ref, + int end_column: int ref +); + +@sourceline = @file + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref +); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +sourceLocationPrefix( + string prefix: string ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +case @diagnostic.severity of + 10 = @diagnostic_debug +| 20 = @diagnostic_info +| 30 = @diagnostic_warning +| 40 = @diagnostic_error +; + + +@ruby_underscore_arg = @ruby_assignment | @ruby_binary | @ruby_conditional | @ruby_operator_assignment | @ruby_range | @ruby_unary | @ruby_underscore_primary + +@ruby_underscore_lhs = @ruby_call | @ruby_element_reference | @ruby_scope_resolution | @ruby_token_false | @ruby_token_nil | @ruby_token_true | @ruby_underscore_variable + +@ruby_underscore_method_name = @ruby_delimited_symbol | @ruby_setter | @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_operator | @ruby_token_simple_symbol + +@ruby_underscore_primary = @ruby_array | @ruby_begin | @ruby_break | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_delimited_symbol | @ruby_for | @ruby_hash | @ruby_if | @ruby_lambda | @ruby_method | @ruby_module | @ruby_next | @ruby_parenthesized_statements | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_retry | @ruby_return | @ruby_singleton_class | @ruby_singleton_method | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_symbol_array | @ruby_token_character | @ruby_token_complex | @ruby_token_float | @ruby_token_heredoc_beginning | @ruby_token_integer | @ruby_token_simple_symbol | @ruby_unary | @ruby_underscore_lhs | @ruby_unless | @ruby_until | @ruby_while | @ruby_yield + +@ruby_underscore_statement = @ruby_alias | @ruby_assignment | @ruby_begin_block | @ruby_binary | @ruby_break | @ruby_call | @ruby_end_block | @ruby_if_modifier | @ruby_next | @ruby_operator_assignment | @ruby_rescue_modifier | @ruby_return | @ruby_unary | @ruby_undef | @ruby_underscore_arg | @ruby_unless_modifier | @ruby_until_modifier | @ruby_while_modifier | @ruby_yield + +@ruby_underscore_variable = @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_self | @ruby_token_super + +ruby_alias_def( + unique int id: @ruby_alias, + int alias: @ruby_underscore_method_name ref, + int name: @ruby_underscore_method_name ref, + int loc: @location ref +); + +@ruby_argument_list_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_argument_list, index] +ruby_argument_list_child( + int ruby_argument_list: @ruby_argument_list ref, + int index: int ref, + unique int child: @ruby_argument_list_child_type ref +); + +ruby_argument_list_def( + unique int id: @ruby_argument_list, + int loc: @location ref +); + +@ruby_array_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_array, index] +ruby_array_child( + int ruby_array: @ruby_array ref, + int index: int ref, + unique int child: @ruby_array_child_type ref +); + +ruby_array_def( + unique int id: @ruby_array, + int loc: @location ref +); + +@ruby_assignment_left_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +@ruby_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_right_assignment_list | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +ruby_assignment_def( + unique int id: @ruby_assignment, + int left: @ruby_assignment_left_type ref, + int right: @ruby_assignment_right_type ref, + int loc: @location ref +); + +@ruby_bare_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_string, index] +ruby_bare_string_child( + int ruby_bare_string: @ruby_bare_string ref, + int index: int ref, + unique int child: @ruby_bare_string_child_type ref +); + +ruby_bare_string_def( + unique int id: @ruby_bare_string, + int loc: @location ref +); + +@ruby_bare_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_symbol, index] +ruby_bare_symbol_child( + int ruby_bare_symbol: @ruby_bare_symbol ref, + int index: int ref, + unique int child: @ruby_bare_symbol_child_type ref +); + +ruby_bare_symbol_def( + unique int id: @ruby_bare_symbol, + int loc: @location ref +); + +@ruby_begin_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin, index] +ruby_begin_child( + int ruby_begin: @ruby_begin ref, + int index: int ref, + unique int child: @ruby_begin_child_type ref +); + +ruby_begin_def( + unique int id: @ruby_begin, + int loc: @location ref +); + +@ruby_begin_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin_block, index] +ruby_begin_block_child( + int ruby_begin_block: @ruby_begin_block ref, + int index: int ref, + unique int child: @ruby_begin_block_child_type ref +); + +ruby_begin_block_def( + unique int id: @ruby_begin_block, + int loc: @location ref +); + +@ruby_binary_left_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +case @ruby_binary.operator of + 0 = @ruby_binary_bangequal +| 1 = @ruby_binary_bangtilde +| 2 = @ruby_binary_percent +| 3 = @ruby_binary_ampersand +| 4 = @ruby_binary_ampersandampersand +| 5 = @ruby_binary_star +| 6 = @ruby_binary_starstar +| 7 = @ruby_binary_plus +| 8 = @ruby_binary_minus +| 9 = @ruby_binary_slash +| 10 = @ruby_binary_langle +| 11 = @ruby_binary_langlelangle +| 12 = @ruby_binary_langleequal +| 13 = @ruby_binary_langleequalrangle +| 14 = @ruby_binary_equalequal +| 15 = @ruby_binary_equalequalequal +| 16 = @ruby_binary_equaltilde +| 17 = @ruby_binary_rangle +| 18 = @ruby_binary_rangleequal +| 19 = @ruby_binary_ranglerangle +| 20 = @ruby_binary_caret +| 21 = @ruby_binary_and +| 22 = @ruby_binary_or +| 23 = @ruby_binary_pipe +| 24 = @ruby_binary_pipepipe +; + + +@ruby_binary_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_binary_def( + unique int id: @ruby_binary, + int left: @ruby_binary_left_type ref, + int operator: int ref, + int right: @ruby_binary_right_type ref, + int loc: @location ref +); + +ruby_block_parameters( + unique int ruby_block: @ruby_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_block, index] +ruby_block_child( + int ruby_block: @ruby_block ref, + int index: int ref, + unique int child: @ruby_block_child_type ref +); + +ruby_block_def( + unique int id: @ruby_block, + int loc: @location ref +); + +ruby_block_argument_def( + unique int id: @ruby_block_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_block_parameter_def( + unique int id: @ruby_block_parameter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_block_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_block_parameters, index] +ruby_block_parameters_child( + int ruby_block_parameters: @ruby_block_parameters ref, + int index: int ref, + unique int child: @ruby_block_parameters_child_type ref +); + +ruby_block_parameters_def( + unique int id: @ruby_block_parameters, + int loc: @location ref +); + +ruby_break_child( + unique int ruby_break: @ruby_break ref, + unique int child: @ruby_argument_list ref +); + +ruby_break_def( + unique int id: @ruby_break, + int loc: @location ref +); + +ruby_call_arguments( + unique int ruby_call: @ruby_call ref, + unique int arguments: @ruby_argument_list ref +); + +@ruby_call_block_type = @ruby_block | @ruby_do_block + +ruby_call_block( + unique int ruby_call: @ruby_call ref, + unique int block: @ruby_call_block_type ref +); + +@ruby_call_method_type = @ruby_argument_list | @ruby_scope_resolution | @ruby_token_operator | @ruby_underscore_variable + +@ruby_call_receiver_type = @ruby_call | @ruby_underscore_primary + +ruby_call_receiver( + unique int ruby_call: @ruby_call ref, + unique int receiver: @ruby_call_receiver_type ref +); + +ruby_call_def( + unique int id: @ruby_call, + int method: @ruby_call_method_type ref, + int loc: @location ref +); + +ruby_case_value( + unique int ruby_case__: @ruby_case__ ref, + unique int value: @ruby_underscore_statement ref +); + +@ruby_case_child_type = @ruby_else | @ruby_when + +#keyset[ruby_case__, index] +ruby_case_child( + int ruby_case__: @ruby_case__ ref, + int index: int ref, + unique int child: @ruby_case_child_type ref +); + +ruby_case_def( + unique int id: @ruby_case__, + int loc: @location ref +); + +#keyset[ruby_chained_string, index] +ruby_chained_string_child( + int ruby_chained_string: @ruby_chained_string ref, + int index: int ref, + unique int child: @ruby_string__ ref +); + +ruby_chained_string_def( + unique int id: @ruby_chained_string, + int loc: @location ref +); + +@ruby_class_name_type = @ruby_scope_resolution | @ruby_token_constant + +ruby_class_superclass( + unique int ruby_class: @ruby_class ref, + unique int superclass: @ruby_superclass ref +); + +@ruby_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_class, index] +ruby_class_child( + int ruby_class: @ruby_class ref, + int index: int ref, + unique int child: @ruby_class_child_type ref +); + +ruby_class_def( + unique int id: @ruby_class, + int name: @ruby_class_name_type ref, + int loc: @location ref +); + +ruby_conditional_def( + unique int id: @ruby_conditional, + int alternative: @ruby_underscore_arg ref, + int condition: @ruby_underscore_arg ref, + int consequence: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_delimited_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_delimited_symbol, index] +ruby_delimited_symbol_child( + int ruby_delimited_symbol: @ruby_delimited_symbol ref, + int index: int ref, + unique int child: @ruby_delimited_symbol_child_type ref +); + +ruby_delimited_symbol_def( + unique int id: @ruby_delimited_symbol, + int loc: @location ref +); + +@ruby_destructured_left_assignment_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_destructured_left_assignment, index] +ruby_destructured_left_assignment_child( + int ruby_destructured_left_assignment: @ruby_destructured_left_assignment ref, + int index: int ref, + unique int child: @ruby_destructured_left_assignment_child_type ref +); + +ruby_destructured_left_assignment_def( + unique int id: @ruby_destructured_left_assignment, + int loc: @location ref +); + +@ruby_destructured_parameter_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_destructured_parameter, index] +ruby_destructured_parameter_child( + int ruby_destructured_parameter: @ruby_destructured_parameter ref, + int index: int ref, + unique int child: @ruby_destructured_parameter_child_type ref +); + +ruby_destructured_parameter_def( + unique int id: @ruby_destructured_parameter, + int loc: @location ref +); + +@ruby_do_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do, index] +ruby_do_child( + int ruby_do: @ruby_do ref, + int index: int ref, + unique int child: @ruby_do_child_type ref +); + +ruby_do_def( + unique int id: @ruby_do, + int loc: @location ref +); + +ruby_do_block_parameters( + unique int ruby_do_block: @ruby_do_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_do_block_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do_block, index] +ruby_do_block_child( + int ruby_do_block: @ruby_do_block ref, + int index: int ref, + unique int child: @ruby_do_block_child_type ref +); + +ruby_do_block_def( + unique int id: @ruby_do_block, + int loc: @location ref +); + +@ruby_element_reference_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_element_reference, index] +ruby_element_reference_child( + int ruby_element_reference: @ruby_element_reference ref, + int index: int ref, + unique int child: @ruby_element_reference_child_type ref +); + +ruby_element_reference_def( + unique int id: @ruby_element_reference, + int object: @ruby_underscore_primary ref, + int loc: @location ref +); + +@ruby_else_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_else, index] +ruby_else_child( + int ruby_else: @ruby_else ref, + int index: int ref, + unique int child: @ruby_else_child_type ref +); + +ruby_else_def( + unique int id: @ruby_else, + int loc: @location ref +); + +@ruby_elsif_alternative_type = @ruby_else | @ruby_elsif + +ruby_elsif_alternative( + unique int ruby_elsif: @ruby_elsif ref, + unique int alternative: @ruby_elsif_alternative_type ref +); + +ruby_elsif_consequence( + unique int ruby_elsif: @ruby_elsif ref, + unique int consequence: @ruby_then ref +); + +ruby_elsif_def( + unique int id: @ruby_elsif, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_end_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_end_block, index] +ruby_end_block_child( + int ruby_end_block: @ruby_end_block ref, + int index: int ref, + unique int child: @ruby_end_block_child_type ref +); + +ruby_end_block_def( + unique int id: @ruby_end_block, + int loc: @location ref +); + +@ruby_ensure_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_ensure, index] +ruby_ensure_child( + int ruby_ensure: @ruby_ensure ref, + int index: int ref, + unique int child: @ruby_ensure_child_type ref +); + +ruby_ensure_def( + unique int id: @ruby_ensure, + int loc: @location ref +); + +ruby_exception_variable_def( + unique int id: @ruby_exception_variable, + int child: @ruby_underscore_lhs ref, + int loc: @location ref +); + +@ruby_exceptions_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_exceptions, index] +ruby_exceptions_child( + int ruby_exceptions: @ruby_exceptions ref, + int index: int ref, + unique int child: @ruby_exceptions_child_type ref +); + +ruby_exceptions_def( + unique int id: @ruby_exceptions, + int loc: @location ref +); + +@ruby_for_pattern_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +ruby_for_def( + unique int id: @ruby_for, + int body: @ruby_do ref, + int pattern: @ruby_for_pattern_type ref, + int value: @ruby_in ref, + int loc: @location ref +); + +@ruby_hash_child_type = @ruby_hash_splat_argument | @ruby_pair + +#keyset[ruby_hash, index] +ruby_hash_child( + int ruby_hash: @ruby_hash ref, + int index: int ref, + unique int child: @ruby_hash_child_type ref +); + +ruby_hash_def( + unique int id: @ruby_hash, + int loc: @location ref +); + +ruby_hash_splat_argument_def( + unique int id: @ruby_hash_splat_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_hash_splat_parameter_name( + unique int ruby_hash_splat_parameter: @ruby_hash_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_hash_splat_parameter_def( + unique int id: @ruby_hash_splat_parameter, + int loc: @location ref +); + +@ruby_heredoc_body_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_heredoc_content | @ruby_token_heredoc_end + +#keyset[ruby_heredoc_body, index] +ruby_heredoc_body_child( + int ruby_heredoc_body: @ruby_heredoc_body ref, + int index: int ref, + unique int child: @ruby_heredoc_body_child_type ref +); + +ruby_heredoc_body_def( + unique int id: @ruby_heredoc_body, + int loc: @location ref +); + +@ruby_if_alternative_type = @ruby_else | @ruby_elsif + +ruby_if_alternative( + unique int ruby_if: @ruby_if ref, + unique int alternative: @ruby_if_alternative_type ref +); + +ruby_if_consequence( + unique int ruby_if: @ruby_if ref, + unique int consequence: @ruby_then ref +); + +ruby_if_def( + unique int id: @ruby_if, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_if_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_if_modifier_def( + unique int id: @ruby_if_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_if_modifier_condition_type ref, + int loc: @location ref +); + +ruby_in_def( + unique int id: @ruby_in, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_interpolation_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_interpolation, index] +ruby_interpolation_child( + int ruby_interpolation: @ruby_interpolation ref, + int index: int ref, + unique int child: @ruby_interpolation_child_type ref +); + +ruby_interpolation_def( + unique int id: @ruby_interpolation, + int loc: @location ref +); + +ruby_keyword_parameter_value( + unique int ruby_keyword_parameter: @ruby_keyword_parameter ref, + unique int value: @ruby_underscore_arg ref +); + +ruby_keyword_parameter_def( + unique int id: @ruby_keyword_parameter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_lambda_body_type = @ruby_block | @ruby_do_block + +ruby_lambda_parameters( + unique int ruby_lambda: @ruby_lambda ref, + unique int parameters: @ruby_lambda_parameters ref +); + +ruby_lambda_def( + unique int id: @ruby_lambda, + int body: @ruby_lambda_body_type ref, + int loc: @location ref +); + +@ruby_lambda_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_lambda_parameters, index] +ruby_lambda_parameters_child( + int ruby_lambda_parameters: @ruby_lambda_parameters ref, + int index: int ref, + unique int child: @ruby_lambda_parameters_child_type ref +); + +ruby_lambda_parameters_def( + unique int id: @ruby_lambda_parameters, + int loc: @location ref +); + +@ruby_left_assignment_list_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_left_assignment_list, index] +ruby_left_assignment_list_child( + int ruby_left_assignment_list: @ruby_left_assignment_list ref, + int index: int ref, + unique int child: @ruby_left_assignment_list_child_type ref +); + +ruby_left_assignment_list_def( + unique int id: @ruby_left_assignment_list, + int loc: @location ref +); + +ruby_method_parameters( + unique int ruby_method: @ruby_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_method, index] +ruby_method_child( + int ruby_method: @ruby_method ref, + int index: int ref, + unique int child: @ruby_method_child_type ref +); + +ruby_method_def( + unique int id: @ruby_method, + int name: @ruby_underscore_method_name ref, + int loc: @location ref +); + +@ruby_method_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_method_parameters, index] +ruby_method_parameters_child( + int ruby_method_parameters: @ruby_method_parameters ref, + int index: int ref, + unique int child: @ruby_method_parameters_child_type ref +); + +ruby_method_parameters_def( + unique int id: @ruby_method_parameters, + int loc: @location ref +); + +@ruby_module_name_type = @ruby_scope_resolution | @ruby_token_constant + +@ruby_module_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_module, index] +ruby_module_child( + int ruby_module: @ruby_module ref, + int index: int ref, + unique int child: @ruby_module_child_type ref +); + +ruby_module_def( + unique int id: @ruby_module, + int name: @ruby_module_name_type ref, + int loc: @location ref +); + +ruby_next_child( + unique int ruby_next: @ruby_next ref, + unique int child: @ruby_argument_list ref +); + +ruby_next_def( + unique int id: @ruby_next, + int loc: @location ref +); + +case @ruby_operator_assignment.operator of + 0 = @ruby_operator_assignment_percentequal +| 1 = @ruby_operator_assignment_ampersandampersandequal +| 2 = @ruby_operator_assignment_ampersandequal +| 3 = @ruby_operator_assignment_starstarequal +| 4 = @ruby_operator_assignment_starequal +| 5 = @ruby_operator_assignment_plusequal +| 6 = @ruby_operator_assignment_minusequal +| 7 = @ruby_operator_assignment_slashequal +| 8 = @ruby_operator_assignment_langlelangleequal +| 9 = @ruby_operator_assignment_ranglerangleequal +| 10 = @ruby_operator_assignment_caretequal +| 11 = @ruby_operator_assignment_pipeequal +| 12 = @ruby_operator_assignment_pipepipeequal +; + + +@ruby_operator_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_operator_assignment_def( + unique int id: @ruby_operator_assignment, + int left: @ruby_underscore_lhs ref, + int operator: int ref, + int right: @ruby_operator_assignment_right_type ref, + int loc: @location ref +); + +ruby_optional_parameter_def( + unique int id: @ruby_optional_parameter, + int name: @ruby_token_identifier ref, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_pair_key_type = @ruby_string__ | @ruby_token_hash_key_symbol | @ruby_underscore_arg + +ruby_pair_def( + unique int id: @ruby_pair, + int key__: @ruby_pair_key_type ref, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_parenthesized_statements_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_parenthesized_statements, index] +ruby_parenthesized_statements_child( + int ruby_parenthesized_statements: @ruby_parenthesized_statements ref, + int index: int ref, + unique int child: @ruby_parenthesized_statements_child_type ref +); + +ruby_parenthesized_statements_def( + unique int id: @ruby_parenthesized_statements, + int loc: @location ref +); + +@ruby_pattern_child_type = @ruby_splat_argument | @ruby_underscore_arg + +ruby_pattern_def( + unique int id: @ruby_pattern, + int child: @ruby_pattern_child_type ref, + int loc: @location ref +); + +@ruby_program_child_type = @ruby_token_empty_statement | @ruby_token_uninterpreted | @ruby_underscore_statement + +#keyset[ruby_program, index] +ruby_program_child( + int ruby_program: @ruby_program ref, + int index: int ref, + unique int child: @ruby_program_child_type ref +); + +ruby_program_def( + unique int id: @ruby_program, + int loc: @location ref +); + +ruby_range_begin( + unique int ruby_range: @ruby_range ref, + unique int begin: @ruby_underscore_arg ref +); + +ruby_range_end( + unique int ruby_range: @ruby_range ref, + unique int end: @ruby_underscore_arg ref +); + +case @ruby_range.operator of + 0 = @ruby_range_dotdot +| 1 = @ruby_range_dotdotdot +; + + +ruby_range_def( + unique int id: @ruby_range, + int operator: int ref, + int loc: @location ref +); + +@ruby_rational_child_type = @ruby_token_float | @ruby_token_integer + +ruby_rational_def( + unique int id: @ruby_rational, + int child: @ruby_rational_child_type ref, + int loc: @location ref +); + +ruby_redo_child( + unique int ruby_redo: @ruby_redo ref, + unique int child: @ruby_argument_list ref +); + +ruby_redo_def( + unique int id: @ruby_redo, + int loc: @location ref +); + +@ruby_regex_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_regex, index] +ruby_regex_child( + int ruby_regex: @ruby_regex ref, + int index: int ref, + unique int child: @ruby_regex_child_type ref +); + +ruby_regex_def( + unique int id: @ruby_regex, + int loc: @location ref +); + +ruby_rescue_body( + unique int ruby_rescue: @ruby_rescue ref, + unique int body: @ruby_then ref +); + +ruby_rescue_exceptions( + unique int ruby_rescue: @ruby_rescue ref, + unique int exceptions: @ruby_exceptions ref +); + +ruby_rescue_variable( + unique int ruby_rescue: @ruby_rescue ref, + unique int variable: @ruby_exception_variable ref +); + +ruby_rescue_def( + unique int id: @ruby_rescue, + int loc: @location ref +); + +@ruby_rescue_modifier_handler_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_rescue_modifier_def( + unique int id: @ruby_rescue_modifier, + int body: @ruby_underscore_statement ref, + int handler: @ruby_rescue_modifier_handler_type ref, + int loc: @location ref +); + +ruby_rest_assignment_child( + unique int ruby_rest_assignment: @ruby_rest_assignment ref, + unique int child: @ruby_underscore_lhs ref +); + +ruby_rest_assignment_def( + unique int id: @ruby_rest_assignment, + int loc: @location ref +); + +ruby_retry_child( + unique int ruby_retry: @ruby_retry ref, + unique int child: @ruby_argument_list ref +); + +ruby_retry_def( + unique int id: @ruby_retry, + int loc: @location ref +); + +ruby_return_child( + unique int ruby_return: @ruby_return ref, + unique int child: @ruby_argument_list ref +); + +ruby_return_def( + unique int id: @ruby_return, + int loc: @location ref +); + +@ruby_right_assignment_list_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_right_assignment_list, index] +ruby_right_assignment_list_child( + int ruby_right_assignment_list: @ruby_right_assignment_list ref, + int index: int ref, + unique int child: @ruby_right_assignment_list_child_type ref +); + +ruby_right_assignment_list_def( + unique int id: @ruby_right_assignment_list, + int loc: @location ref +); + +@ruby_scope_resolution_name_type = @ruby_token_constant | @ruby_token_identifier + +ruby_scope_resolution_scope( + unique int ruby_scope_resolution: @ruby_scope_resolution ref, + unique int scope: @ruby_underscore_primary ref +); + +ruby_scope_resolution_def( + unique int id: @ruby_scope_resolution, + int name: @ruby_scope_resolution_name_type ref, + int loc: @location ref +); + +ruby_setter_def( + unique int id: @ruby_setter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_singleton_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_class, index] +ruby_singleton_class_child( + int ruby_singleton_class: @ruby_singleton_class ref, + int index: int ref, + unique int child: @ruby_singleton_class_child_type ref +); + +ruby_singleton_class_def( + unique int id: @ruby_singleton_class, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_singleton_method_object_type = @ruby_underscore_arg | @ruby_underscore_variable + +ruby_singleton_method_parameters( + unique int ruby_singleton_method: @ruby_singleton_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_singleton_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_method, index] +ruby_singleton_method_child( + int ruby_singleton_method: @ruby_singleton_method ref, + int index: int ref, + unique int child: @ruby_singleton_method_child_type ref +); + +ruby_singleton_method_def( + unique int id: @ruby_singleton_method, + int name: @ruby_underscore_method_name ref, + int object: @ruby_singleton_method_object_type ref, + int loc: @location ref +); + +ruby_splat_argument_def( + unique int id: @ruby_splat_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_splat_parameter_name( + unique int ruby_splat_parameter: @ruby_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_splat_parameter_def( + unique int id: @ruby_splat_parameter, + int loc: @location ref +); + +@ruby_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_string__, index] +ruby_string_child( + int ruby_string__: @ruby_string__ ref, + int index: int ref, + unique int child: @ruby_string_child_type ref +); + +ruby_string_def( + unique int id: @ruby_string__, + int loc: @location ref +); + +#keyset[ruby_string_array, index] +ruby_string_array_child( + int ruby_string_array: @ruby_string_array ref, + int index: int ref, + unique int child: @ruby_bare_string ref +); + +ruby_string_array_def( + unique int id: @ruby_string_array, + int loc: @location ref +); + +@ruby_subshell_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_subshell, index] +ruby_subshell_child( + int ruby_subshell: @ruby_subshell ref, + int index: int ref, + unique int child: @ruby_subshell_child_type ref +); + +ruby_subshell_def( + unique int id: @ruby_subshell, + int loc: @location ref +); + +@ruby_superclass_child_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_superclass_def( + unique int id: @ruby_superclass, + int child: @ruby_superclass_child_type ref, + int loc: @location ref +); + +#keyset[ruby_symbol_array, index] +ruby_symbol_array_child( + int ruby_symbol_array: @ruby_symbol_array ref, + int index: int ref, + unique int child: @ruby_bare_symbol ref +); + +ruby_symbol_array_def( + unique int id: @ruby_symbol_array, + int loc: @location ref +); + +@ruby_then_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_then, index] +ruby_then_child( + int ruby_then: @ruby_then ref, + int index: int ref, + unique int child: @ruby_then_child_type ref +); + +ruby_then_def( + unique int id: @ruby_then, + int loc: @location ref +); + +@ruby_unary_operand_type = @ruby_break | @ruby_call | @ruby_next | @ruby_parenthesized_statements | @ruby_return | @ruby_token_float | @ruby_token_integer | @ruby_underscore_arg | @ruby_yield + +case @ruby_unary.operator of + 0 = @ruby_unary_bang +| 1 = @ruby_unary_plus +| 2 = @ruby_unary_minus +| 3 = @ruby_unary_definedquestion +| 4 = @ruby_unary_not +| 5 = @ruby_unary_tilde +; + + +ruby_unary_def( + unique int id: @ruby_unary, + int operand: @ruby_unary_operand_type ref, + int operator: int ref, + int loc: @location ref +); + +#keyset[ruby_undef, index] +ruby_undef_child( + int ruby_undef: @ruby_undef ref, + int index: int ref, + unique int child: @ruby_underscore_method_name ref +); + +ruby_undef_def( + unique int id: @ruby_undef, + int loc: @location ref +); + +@ruby_unless_alternative_type = @ruby_else | @ruby_elsif + +ruby_unless_alternative( + unique int ruby_unless: @ruby_unless ref, + unique int alternative: @ruby_unless_alternative_type ref +); + +ruby_unless_consequence( + unique int ruby_unless: @ruby_unless ref, + unique int consequence: @ruby_then ref +); + +ruby_unless_def( + unique int id: @ruby_unless, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_unless_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_unless_modifier_def( + unique int id: @ruby_unless_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_unless_modifier_condition_type ref, + int loc: @location ref +); + +ruby_until_def( + unique int id: @ruby_until, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_until_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_until_modifier_def( + unique int id: @ruby_until_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_until_modifier_condition_type ref, + int loc: @location ref +); + +ruby_when_body( + unique int ruby_when: @ruby_when ref, + unique int body: @ruby_then ref +); + +#keyset[ruby_when, index] +ruby_when_pattern( + int ruby_when: @ruby_when ref, + int index: int ref, + unique int pattern: @ruby_pattern ref +); + +ruby_when_def( + unique int id: @ruby_when, + int loc: @location ref +); + +ruby_while_def( + unique int id: @ruby_while, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_while_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_while_modifier_def( + unique int id: @ruby_while_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_while_modifier_condition_type ref, + int loc: @location ref +); + +ruby_yield_child( + unique int ruby_yield: @ruby_yield ref, + unique int child: @ruby_argument_list ref +); + +ruby_yield_def( + unique int id: @ruby_yield, + int loc: @location ref +); + +ruby_tokeninfo( + unique int id: @ruby_token, + int kind: int ref, + int file: @file ref, + int idx: int ref, + string value: string ref, + int loc: @location ref +); + +case @ruby_token.kind of + 0 = @ruby_reserved_word +| 1 = @ruby_token_character +| 2 = @ruby_token_class_variable +| 3 = @ruby_token_comment +| 4 = @ruby_token_complex +| 5 = @ruby_token_constant +| 6 = @ruby_token_empty_statement +| 7 = @ruby_token_escape_sequence +| 8 = @ruby_token_false +| 9 = @ruby_token_float +| 10 = @ruby_token_global_variable +| 11 = @ruby_token_hash_key_symbol +| 12 = @ruby_token_heredoc_beginning +| 13 = @ruby_token_heredoc_content +| 14 = @ruby_token_heredoc_end +| 15 = @ruby_token_identifier +| 16 = @ruby_token_instance_variable +| 17 = @ruby_token_integer +| 18 = @ruby_token_nil +| 19 = @ruby_token_operator +| 20 = @ruby_token_self +| 21 = @ruby_token_simple_symbol +| 22 = @ruby_token_string_content +| 23 = @ruby_token_super +| 24 = @ruby_token_true +| 25 = @ruby_token_uninterpreted +; + + +@ruby_ast_node = @ruby_alias | @ruby_argument_list | @ruby_array | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_parameter | @ruby_block_parameters | @ruby_break | @ruby_call | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_for | @ruby_hash | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_modifier | @ruby_in | @ruby_interpolation | @ruby_keyword_parameter | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield + +@ruby_ast_node_parent = @file | @ruby_ast_node + +#keyset[parent, parent_index] +ruby_ast_node_parent( + int child: @ruby_ast_node ref, + int parent: @ruby_ast_node_parent ref, + int parent_index: int ref +); + +erb_comment_directive_def( + unique int id: @erb_comment_directive, + int child: @erb_token_comment ref, + int loc: @location ref +); + +erb_directive_def( + unique int id: @erb_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +erb_graphql_directive_def( + unique int id: @erb_graphql_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +erb_output_directive_def( + unique int id: @erb_output_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +@erb_template_child_type = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_token_content + +#keyset[erb_template, index] +erb_template_child( + int erb_template: @erb_template ref, + int index: int ref, + unique int child: @erb_template_child_type ref +); + +erb_template_def( + unique int id: @erb_template, + int loc: @location ref +); + +erb_tokeninfo( + unique int id: @erb_token, + int kind: int ref, + int file: @file ref, + int idx: int ref, + string value: string ref, + int loc: @location ref +); + +case @erb_token.kind of + 0 = @erb_reserved_word +| 1 = @erb_token_code +| 2 = @erb_token_comment +| 3 = @erb_token_content +; + + +@erb_ast_node = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_template | @erb_token + +@erb_ast_node_parent = @erb_ast_node | @file + +#keyset[parent, parent_index] +erb_ast_node_parent( + int child: @erb_ast_node ref, + int parent: @erb_ast_node_parent ref, + int parent_index: int ref +); + diff --git a/ruby/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/ruby.dbscheme.stats b/ruby/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/ruby.dbscheme.stats new file mode 100644 index 00000000000..665de5190c2 --- /dev/null +++ b/ruby/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/ruby.dbscheme.stats @@ -0,0 +1,25558 @@ +<dbstats> + <typesizes><e> + <k>@alias</k> + <v>398</v> + </e> + <e> + <k>@argument_list</k> + <v>215845</v> + </e> + <e> + <k>@array</k> + <v>10382</v> + </e> + <e> + <k>@assignment</k> + <v>39638</v> + </e> + <e> + <k>@bare_string</k> + <v>3009</v> + </e> + <e> + <k>@bare_symbol</k> + <v>687</v> + </e> + <e> + <k>@begin</k> + <v>613</v> + </e> + <e> + <k>@begin_block</k> + <v>0</v> + </e> + <e> + <k>@binary_ampersand</k> + <v>41</v> + </e> + <e> + <k>@binary_ampersandampersand</k> + <v>2775</v> + </e> + <e> + <k>@binary_and</k> + <v>85</v> + </e> + <e> + <k>@binary_bangequal</k> + <v>498</v> + </e> + <e> + <k>@binary_bangtilde</k> + <v>36</v> + </e> + <e> + <k>@binary_caret</k> + <v>29</v> + </e> + <e> + <k>@binary_equalequal</k> + <v>2506</v> + </e> + <e> + <k>@binary_equalequalequal</k> + <v>173</v> + </e> + <e> + <k>@binary_equaltilde</k> + <v>237</v> + </e> + <e> + <k>@binary_langle</k> + <v>430</v> + </e> + <e> + <k>@binary_langleequal</k> + <v>84</v> + </e> + <e> + <k>@binary_langleequalrangle</k> + <v>83</v> + </e> + <e> + <k>@binary_langlelangle</k> + <v>3285</v> + </e> + <e> + <k>@binary_minus</k> + <v>624</v> + </e> + <e> + <k>@binary_or</k> + <v>3</v> + </e> + <e> + <k>@binary_percent</k> + <v>140</v> + </e> + <e> + <k>@binary_pipe</k> + <v>42</v> + </e> + <e> + <k>@binary_pipepipe</k> + <v>2531</v> + </e> + <e> + <k>@binary_plus</k> + <v>1490</v> + </e> + <e> + <k>@binary_rangle</k> + <v>772</v> + </e> + <e> + <k>@binary_rangleequal</k> + <v>125</v> + </e> + <e> + <k>@binary_ranglerangle</k> + <v>6</v> + </e> + <e> + <k>@binary_slash</k> + <v>136</v> + </e> + <e> + <k>@binary_star</k> + <v>364</v> + </e> + <e> + <k>@binary_starstar</k> + <v>33</v> + </e> + <e> + <k>@block</k> + <v>21214</v> + </e> + <e> + <k>@block_argument</k> + <v>1770</v> + </e> + <e> + <k>@block_parameter</k> + <v>658</v> + </e> + <e> + <k>@block_parameters</k> + <v>7227</v> + </e> + <e> + <k>@break</k> + <v>214</v> + </e> + <e> + <k>@call</k> + <v>303820</v> + </e> + <e> + <k>@case__</k> + <v>377</v> + </e> + <e> + <k>@chained_string</k> + <v>268</v> + </e> + <e> + <k>@class</k> + <v>5194</v> + </e> + <e> + <k>@conditional</k> + <v>1120</v> + </e> + <e> + <k>@delimited_symbol</k> + <v>384</v> + </e> + <e> + <k>@destructured_left_assignment</k> + <v>1</v> + </e> + <e> + <k>@destructured_parameter</k> + <v>62</v> + </e> + <e> + <k>@diagnostic_debug</k> + <v>0</v> + </e> + <e> + <k>@diagnostic_error</k> + <v>64</v> + </e> + <e> + <k>@diagnostic_info</k> + <v>0</v> + </e> + <e> + <k>@diagnostic_warning</k> + <v>0</v> + </e> + <e> + <k>@do</k> + <v>117</v> + </e> + <e> + <k>@do_block</k> + <v>41659</v> + </e> + <e> + <k>@element_reference</k> + <v>25582</v> + </e> + <e> + <k>@else</k> + <v>2122</v> + </e> + <e> + <k>@elsif</k> + <v>491</v> + </e> + <e> + <k>@end_block</k> + <v>0</v> + </e> + <e> + <k>@ensure</k> + <v>1125</v> + </e> + <e> + <k>@exception_variable</k> + <v>309</v> + </e> + <e> + <k>@exceptions</k> + <v>429</v> + </e> + <e> + <k>@file</k> + <v>6322</v> + </e> + <e> + <k>@folder</k> + <v>1489</v> + </e> + <e> + <k>@for</k> + <v>1</v> + </e> + <e> + <k>@hash</k> + <v>8122</v> + </e> + <e> + <k>@hash_splat_argument</k> + <v>398</v> + </e> + <e> + <k>@hash_splat_parameter</k> + <v>413</v> + </e> + <e> + <k>@heredoc_body</k> + <v>1610</v> + </e> + <e> + <k>@if</k> + <v>5713</v> + </e> + <e> + <k>@if_modifier</k> + <v>4276</v> + </e> + <e> + <k>@in</k> + <v>1</v> + </e> + <e> + <k>@interpolation</k> + <v>11773</v> + </e> + <e> + <k>@keyword_parameter</k> + <v>1099</v> + </e> + <e> + <k>@lambda</k> + <v>660</v> + </e> + <e> + <k>@lambda_parameters</k> + <v>190</v> + </e> + <e> + <k>@left_assignment_list</k> + <v>773</v> + </e> + <e> + <k>@location_default</k> + <v>2604972</v> + </e> + <e> + <k>@method</k> + <v>30455</v> + </e> + <e> + <k>@method_parameters</k> + <v>8920</v> + </e> + <e> + <k>@module</k> + <v>4441</v> + </e> + <e> + <k>@next</k> + <v>653</v> + </e> + <e> + <k>@operator_assignment_ampersandampersandequal</k> + <v>5</v> + </e> + <e> + <k>@operator_assignment_ampersandequal</k> + <v>5</v> + </e> + <e> + <k>@operator_assignment_caretequal</k> + <v>0</v> + </e> + <e> + <k>@operator_assignment_langlelangleequal</k> + <v>0</v> + </e> + <e> + <k>@operator_assignment_minusequal</k> + <v>62</v> + </e> + <e> + <k>@operator_assignment_percentequal</k> + <v>2</v> + </e> + <e> + <k>@operator_assignment_pipeequal</k> + <v>44</v> + </e> + <e> + <k>@operator_assignment_pipepipeequal</k> + <v>1417</v> + </e> + <e> + <k>@operator_assignment_plusequal</k> + <v>506</v> + </e> + <e> + <k>@operator_assignment_ranglerangleequal</k> + <v>0</v> + </e> + <e> + <k>@operator_assignment_slashequal</k> + <v>3</v> + </e> + <e> + <k>@operator_assignment_starequal</k> + <v>2</v> + </e> + <e> + <k>@operator_assignment_starstarequal</k> + <v>0</v> + </e> + <e> + <k>@optional_parameter</k> + <v>2047</v> + </e> + <e> + <k>@pair</k> + <v>62127</v> + </e> + <e> + <k>@parenthesized_statements</k> + <v>1652</v> + </e> + <e> + <k>@pattern</k> + <v>1186</v> + </e> + <e> + <k>@program</k> + <v>6322</v> + </e> + <e> + <k>@range_dotdot</k> + <v>424</v> + </e> + <e> + <k>@range_dotdotdot</k> + <v>122</v> + </e> + <e> + <k>@rational</k> + <v>2</v> + </e> + <e> + <k>@redo</k> + <v>0</v> + </e> + <e> + <k>@regex</k> + <v>3979</v> + </e> + <e> + <k>@rescue</k> + <v>634</v> + </e> + <e> + <k>@rescue_modifier</k> + <v>174</v> + </e> + <e> + <k>@reserved_word</k> + <v>1009882</v> + </e> + <e> + <k>@rest_assignment</k> + <v>18</v> + </e> + <e> + <k>@retry</k> + <v>9</v> + </e> + <e> + <k>@return</k> + <v>2601</v> + </e> + <e> + <k>@right_assignment_list</k> + <v>414</v> + </e> + <e> + <k>@scope_resolution</k> + <v>22918</v> + </e> + <e> + <k>@setter</k> + <v>186</v> + </e> + <e> + <k>@singleton_class</k> + <v>191</v> + </e> + <e> + <k>@singleton_method</k> + <v>2020</v> + </e> + <e> + <k>@splat_argument</k> + <v>683</v> + </e> + <e> + <k>@splat_parameter</k> + <v>921</v> + </e> + <e> + <k>@string__</k> + <v>91253</v> + </e> + <e> + <k>@string_array</k> + <v>938</v> + </e> + <e> + <k>@subshell</k> + <v>130</v> + </e> + <e> + <k>@superclass</k> + <v>4108</v> + </e> + <e> + <k>@symbol_array</k> + <v>139</v> + </e> + <e> + <k>@then</k> + <v>7609</v> + </e> + <e> + <k>@token_character</k> + <v>11</v> + </e> + <e> + <k>@token_class_variable</k> + <v>238</v> + </e> + <e> + <k>@token_comment</k> + <v>55974</v> + </e> + <e> + <k>@token_complex</k> + <v>0</v> + </e> + <e> + <k>@token_constant</k> + <v>86706</v> + </e> + <e> + <k>@token_empty_statement</k> + <v>0</v> + </e> + <e> + <k>@token_escape_sequence</k> + <v>19932</v> + </e> + <e> + <k>@token_false</k> + <v>5175</v> + </e> + <e> + <k>@token_float</k> + <v>3122</v> + </e> + <e> + <k>@token_global_variable</k> + <v>724</v> + </e> + <e> + <k>@token_hash_key_symbol</k> + <v>60562</v> + </e> + <e> + <k>@token_heredoc_beginning</k> + <v>1610</v> + </e> + <e> + <k>@token_heredoc_content</k> + <v>3662</v> + </e> + <e> + <k>@token_heredoc_end</k> + <v>1610</v> + </e> + <e> + <k>@token_identifier</k> + <v>456774</v> + </e> + <e> + <k>@token_instance_variable</k> + <v>25234</v> + </e> + <e> + <k>@token_integer</k> + <v>32694</v> + </e> + <e> + <k>@token_nil</k> + <v>4047</v> + </e> + <e> + <k>@token_operator</k> + <v>195</v> + </e> + <e> + <k>@token_self</k> + <v>4040</v> + </e> + <e> + <k>@token_simple_symbol</k> + <v>82353</v> + </e> + <e> + <k>@token_string_content</k> + <v>115192</v> + </e> + <e> + <k>@token_super</k> + <v>1554</v> + </e> + <e> + <k>@token_true</k> + <v>7308</v> + </e> + <e> + <k>@token_uninterpreted</k> + <v>0</v> + </e> + <e> + <k>@unary_bang</k> + <v>1682</v> + </e> + <e> + <k>@unary_definedquestion</k> + <v>247</v> + </e> + <e> + <k>@unary_minus</k> + <v>651</v> + </e> + <e> + <k>@unary_not</k> + <v>10</v> + </e> + <e> + <k>@unary_plus</k> + <v>436</v> + </e> + <e> + <k>@unary_tilde</k> + <v>5</v> + </e> + <e> + <k>@undef</k> + <v>13</v> + </e> + <e> + <k>@unless</k> + <v>497</v> + </e> + <e> + <k>@unless_modifier</k> + <v>1404</v> + </e> + <e> + <k>@until</k> + <v>16</v> + </e> + <e> + <k>@until_modifier</k> + <v>13</v> + </e> + <e> + <k>@when</k> + <v>987</v> + </e> + <e> + <k>@while</k> + <v>106</v> + </e> + <e> + <k>@while_modifier</k> + <v>9</v> + </e> + <e> + <k>@yield</k> + <v>841</v> + </e> + </typesizes> + <stats><relation> + <name>alias_def</name> + <cardinality>398</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>398</v> + </e> + <e> + <k>alias</k> + <v>398</v> + </e> + <e> + <k>name</k> + <v>398</v> + </e> + <e> + <k>loc</k> + <v>398</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>alias</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>398</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>398</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>398</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>alias</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>398</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>alias</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>398</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>alias</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>398</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>398</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>alias</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>398</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>398</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>398</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>alias</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>398</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>398</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>argument_list_child</name> + <cardinality>276770</cardinality> + <columnsizes> + <e> + <k>argument_list</k> + <v>215777</v> + </e> + <e> + <k>index</k> + <v>109</v> + </e> + <e> + <k>child</k> + <v>276770</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>argument_list</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>176126</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>26678</v> + </b> + <b> + <a>3</a> + <b>33</b> + <v>12972</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>argument_list</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>176126</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>26678</v> + </b> + <b> + <a>3</a> + <b>33</b> + <v>12972</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>argument_list</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>37</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>10</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>3</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>6</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>6</v> + </b> + <b> + <a>9</a> + <b>12</b> + <v>6</v> + </b> + <b> + <a>14</a> + <b>17</b> + <v>6</v> + </b> + <b> + <a>24</a> + <b>43</b> + <v>6</v> + </b> + <b> + <a>103</a> + <b>235</b> + <v>6</v> + </b> + <b> + <a>535</a> + <b>1428</b> + <v>6</v> + </b> + <b> + <a>3806</a> + <b>11634</b> + <v>6</v> + </b> + <b> + <a>63305</a> + <b>63306</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>37</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>10</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>3</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>6</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>6</v> + </b> + <b> + <a>9</a> + <b>12</b> + <v>6</v> + </b> + <b> + <a>14</a> + <b>17</b> + <v>6</v> + </b> + <b> + <a>24</a> + <b>43</b> + <v>6</v> + </b> + <b> + <a>103</a> + <b>235</b> + <v>6</v> + </b> + <b> + <a>535</a> + <b>1428</b> + <v>6</v> + </b> + <b> + <a>3806</a> + <b>11634</b> + <v>6</v> + </b> + <b> + <a>63305</a> + <b>63306</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>argument_list</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>276770</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>276770</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>argument_list_def</name> + <cardinality>215845</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>215845</v> + </e> + <e> + <k>loc</k> + <v>215845</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>215845</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>215845</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>array_child</name> + <cardinality>19631</cardinality> + <columnsizes> + <e> + <k>array</k> + <v>8772</v> + </e> + <e> + <k>index</k> + <v>93</v> + </e> + <e> + <k>child</k> + <v>19631</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>array</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2906</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>3796</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>1270</v> + </b> + <b> + <a>4</a> + <b>9</b> + <v>667</v> + </b> + <b> + <a>9</a> + <b>94</b> + <v>133</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>array</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2906</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>3796</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>1270</v> + </b> + <b> + <a>4</a> + <b>9</b> + <v>667</v> + </b> + <b> + <a>9</a> + <b>94</b> + <v>133</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>array</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>27</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>13</v> + </b> + <b> + <a>4</a> + <b>6</b> + <v>8</v> + </b> + <b> + <a>6</a> + <b>11</b> + <v>7</v> + </b> + <b> + <a>12</a> + <b>21</b> + <v>7</v> + </b> + <b> + <a>23</a> + <b>35</b> + <v>7</v> + </b> + <b> + <a>36</a> + <b>134</b> + <v>7</v> + </b> + <b> + <a>169</a> + <b>5867</b> + <v>7</v> + </b> + <b> + <a>8772</a> + <b>8773</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>27</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>13</v> + </b> + <b> + <a>4</a> + <b>6</b> + <v>8</v> + </b> + <b> + <a>6</a> + <b>11</b> + <v>7</v> + </b> + <b> + <a>12</a> + <b>21</b> + <v>7</v> + </b> + <b> + <a>23</a> + <b>35</b> + <v>7</v> + </b> + <b> + <a>36</a> + <b>134</b> + <v>7</v> + </b> + <b> + <a>169</a> + <b>5867</b> + <v>7</v> + </b> + <b> + <a>8772</a> + <b>8773</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>array</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>19631</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>19631</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>array_def</name> + <cardinality>10382</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>10382</v> + </e> + <e> + <k>loc</k> + <v>10382</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>10382</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>10382</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>assignment_def</name> + <cardinality>39638</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>39638</v> + </e> + <e> + <k>left</k> + <v>39638</v> + </e> + <e> + <k>right</k> + <v>39638</v> + </e> + <e> + <k>loc</k> + <v>39638</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>39638</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>39638</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>39638</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>39638</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>39638</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>39638</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>39638</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>39638</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>39638</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>39638</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>39638</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>39638</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ast_node_parent</name> + <cardinality>2689484</cardinality> + <columnsizes> + <e> + <k>child</k> + <v>2689484</v> + </e> + <e> + <k>parent</k> + <v>882787</v> + </e> + <e> + <k>parent_index</k> + <v>586</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>child</src> + <trg>parent</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2689484</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>parent_index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2689484</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parent</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>95994</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>128409</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>476622</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>115559</v> + </b> + <b> + <a>5</a> + <b>173</b> + <v>66200</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parent</src> + <trg>parent_index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>95994</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>128409</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>476622</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>115559</v> + </b> + <b> + <a>5</a> + <b>173</b> + <v>66200</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parent_index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>126</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>68</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>54</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>13</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>51</v> + </b> + <b> + <a>7</a> + <b>15</b> + <v>51</v> + </b> + <b> + <a>15</a> + <b>27</b> + <v>47</v> + </b> + <b> + <a>28</a> + <b>58</b> + <v>44</v> + </b> + <b> + <a>61</a> + <b>147</b> + <v>44</v> + </b> + <b> + <a>161</a> + <b>923</b> + <v>44</v> + </b> + <b> + <a>1058</a> + <b>258994</b> + <v>40</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parent_index</src> + <trg>parent</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>126</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>68</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>54</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>13</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>51</v> + </b> + <b> + <a>7</a> + <b>15</b> + <v>51</v> + </b> + <b> + <a>15</a> + <b>27</b> + <v>47</v> + </b> + <b> + <a>28</a> + <b>58</b> + <v>44</v> + </b> + <b> + <a>61</a> + <b>147</b> + <v>44</v> + </b> + <b> + <a>161</a> + <b>923</b> + <v>44</v> + </b> + <b> + <a>1058</a> + <b>258994</b> + <v>40</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>bare_string_child</name> + <cardinality>3021</cardinality> + <columnsizes> + <e> + <k>bare_string</k> + <v>3009</v> + </e> + <e> + <k>index</k> + <v>2</v> + </e> + <e> + <k>child</k> + <v>3021</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>bare_string</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2997</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>12</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>bare_string</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2997</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>12</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>bare_string</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>12</a> + <b>13</b> + <v>1</v> + </b> + <b> + <a>3009</a> + <b>3010</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>12</a> + <b>13</b> + <v>1</v> + </b> + <b> + <a>3009</a> + <b>3010</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>bare_string</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3021</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3021</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>bare_string_def</name> + <cardinality>3009</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>3009</v> + </e> + <e> + <k>loc</k> + <v>3009</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3009</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3009</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>bare_symbol_child</name> + <cardinality>687</cardinality> + <columnsizes> + <e> + <k>bare_symbol</k> + <v>687</v> + </e> + <e> + <k>index</k> + <v>1</v> + </e> + <e> + <k>child</k> + <v>687</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>bare_symbol</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>687</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>bare_symbol</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>687</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>bare_symbol</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>672</a> + <b>673</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>672</a> + <b>673</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>bare_symbol</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>687</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>687</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>bare_symbol_def</name> + <cardinality>687</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>687</v> + </e> + <e> + <k>loc</k> + <v>687</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>687</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>687</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>begin_block_child</name> + <cardinality>0</cardinality> + <columnsizes> + <e> + <k>begin_block</k> + <v>0</v> + </e> + <e> + <k>index</k> + <v>0</v> + </e> + <e> + <k>child</k> + <v>0</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>begin_block</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>begin_block</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>begin_block</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>begin_block</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>begin_block_def</name> + <cardinality>0</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>0</v> + </e> + <e> + <k>loc</k> + <v>0</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>begin_child</name> + <cardinality>2106</cardinality> + <columnsizes> + <e> + <k>begin</k> + <v>613</v> + </e> + <e> + <k>index</k> + <v>34</v> + </e> + <e> + <k>child</k> + <v>2106</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>begin</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>31</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>278</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>126</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>68</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>52</v> + </b> + <b> + <a>7</a> + <b>13</b> + <v>46</v> + </b> + <b> + <a>13</a> + <b>35</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>begin</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>31</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>278</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>126</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>68</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>52</v> + </b> + <b> + <a>7</a> + <b>13</b> + <v>46</v> + </b> + <b> + <a>13</a> + <b>35</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>begin</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>14</v> + </b> + <b> + <a>5</a> + <b>12</b> + <v>3</v> + </b> + <b> + <a>14</a> + <b>30</b> + <v>3</v> + </b> + <b> + <a>41</a> + <b>74</b> + <v>3</v> + </b> + <b> + <a>105</a> + <b>297</b> + <v>3</v> + </b> + <b> + <a>568</a> + <b>600</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>14</v> + </b> + <b> + <a>5</a> + <b>12</b> + <v>3</v> + </b> + <b> + <a>14</a> + <b>30</b> + <v>3</v> + </b> + <b> + <a>41</a> + <b>74</b> + <v>3</v> + </b> + <b> + <a>105</a> + <b>297</b> + <v>3</v> + </b> + <b> + <a>568</a> + <b>600</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>begin</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2106</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2106</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>begin_def</name> + <cardinality>613</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>613</v> + </e> + <e> + <k>loc</k> + <v>613</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>613</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>613</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>binary_def</name> + <cardinality>13969</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>13969</v> + </e> + <e> + <k>left</k> + <v>13969</v> + </e> + <e> + <k>operator</k> + <v>23</v> + </e> + <e> + <k>right</k> + <v>13969</v> + </e> + <e> + <k>loc</k> + <v>13969</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13969</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13969</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13969</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13969</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13969</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13969</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13969</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13969</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + <b> + <a>9</a> + <b>15</b> + <v>2</v> + </b> + <b> + <a>22</a> + <b>23</b> + <v>1</v> + </b> + <b> + <a>36</a> + <b>37</b> + <v>2</v> + </b> + <b> + <a>83</a> + <b>98</b> + <v>2</v> + </b> + <b> + <a>114</a> + <b>115</b> + <v>1</v> + </b> + <b> + <a>123</a> + <b>124</b> + <v>2</v> + </b> + <b> + <a>232</a> + <b>303</b> + <v>2</v> + </b> + <b> + <a>421</a> + <b>488</b> + <v>2</v> + </b> + <b> + <a>610</a> + <b>756</b> + <v>2</v> + </b> + <b> + <a>1172</a> + <b>1377</b> + <v>2</v> + </b> + <b> + <a>2448</a> + <b>2474</b> + <v>2</v> + </b> + <b> + <a>2711</a> + <b>2712</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + <b> + <a>9</a> + <b>15</b> + <v>2</v> + </b> + <b> + <a>22</a> + <b>23</b> + <v>1</v> + </b> + <b> + <a>36</a> + <b>37</b> + <v>2</v> + </b> + <b> + <a>83</a> + <b>98</b> + <v>2</v> + </b> + <b> + <a>114</a> + <b>115</b> + <v>1</v> + </b> + <b> + <a>123</a> + <b>124</b> + <v>2</v> + </b> + <b> + <a>232</a> + <b>303</b> + <v>2</v> + </b> + <b> + <a>421</a> + <b>488</b> + <v>2</v> + </b> + <b> + <a>610</a> + <b>756</b> + <v>2</v> + </b> + <b> + <a>1172</a> + <b>1377</b> + <v>2</v> + </b> + <b> + <a>2448</a> + <b>2474</b> + <v>2</v> + </b> + <b> + <a>2711</a> + <b>2712</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + <b> + <a>9</a> + <b>15</b> + <v>2</v> + </b> + <b> + <a>22</a> + <b>23</b> + <v>1</v> + </b> + <b> + <a>36</a> + <b>37</b> + <v>2</v> + </b> + <b> + <a>83</a> + <b>98</b> + <v>2</v> + </b> + <b> + <a>114</a> + <b>115</b> + <v>1</v> + </b> + <b> + <a>123</a> + <b>124</b> + <v>2</v> + </b> + <b> + <a>232</a> + <b>303</b> + <v>2</v> + </b> + <b> + <a>421</a> + <b>488</b> + <v>2</v> + </b> + <b> + <a>610</a> + <b>756</b> + <v>2</v> + </b> + <b> + <a>1172</a> + <b>1377</b> + <v>2</v> + </b> + <b> + <a>2448</a> + <b>2474</b> + <v>2</v> + </b> + <b> + <a>2711</a> + <b>2712</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + <b> + <a>9</a> + <b>15</b> + <v>2</v> + </b> + <b> + <a>22</a> + <b>23</b> + <v>1</v> + </b> + <b> + <a>36</a> + <b>37</b> + <v>2</v> + </b> + <b> + <a>83</a> + <b>98</b> + <v>2</v> + </b> + <b> + <a>114</a> + <b>115</b> + <v>1</v> + </b> + <b> + <a>123</a> + <b>124</b> + <v>2</v> + </b> + <b> + <a>232</a> + <b>303</b> + <v>2</v> + </b> + <b> + <a>421</a> + <b>488</b> + <v>2</v> + </b> + <b> + <a>610</a> + <b>756</b> + <v>2</v> + </b> + <b> + <a>1172</a> + <b>1377</b> + <v>2</v> + </b> + <b> + <a>2448</a> + <b>2474</b> + <v>2</v> + </b> + <b> + <a>2711</a> + <b>2712</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13969</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13969</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13969</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13969</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13969</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13969</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13969</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13969</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>block_argument_def</name> + <cardinality>1770</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1770</v> + </e> + <e> + <k>child</k> + <v>1770</v> + </e> + <e> + <k>loc</k> + <v>1770</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1770</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1770</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1770</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1770</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1770</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1770</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>block_child</name> + <cardinality>21204</cardinality> + <columnsizes> + <e> + <k>block</k> + <v>21163</v> + </e> + <e> + <k>index</k> + <v>13</v> + </e> + <e> + <k>child</k> + <v>21204</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>block</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>21136</v> + </b> + <b> + <a>2</a> + <b>5</b> + <v>27</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>block</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>21136</v> + </b> + <b> + <a>2</a> + <b>5</b> + <v>27</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>block</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>3</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>3</v> + </b> + <b> + <a>6209</a> + <b>6210</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>3</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>3</v> + </b> + <b> + <a>6209</a> + <b>6210</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>block</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>21204</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>21204</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>block_def</name> + <cardinality>21214</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>21214</v> + </e> + <e> + <k>loc</k> + <v>21214</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>21214</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>21214</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>block_parameter_def</name> + <cardinality>658</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>658</v> + </e> + <e> + <k>name</k> + <v>658</v> + </e> + <e> + <k>loc</k> + <v>658</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>658</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>658</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>658</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>658</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>658</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>658</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>block_parameters</name> + <cardinality>2662</cardinality> + <columnsizes> + <e> + <k>block</k> + <v>2662</v> + </e> + <e> + <k>parameters</k> + <v>2662</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>block</src> + <trg>parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2662</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parameters</src> + <trg>block</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2662</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>block_parameters_child</name> + <cardinality>8428</cardinality> + <columnsizes> + <e> + <k>block_parameters</k> + <v>7227</v> + </e> + <e> + <k>index</k> + <v>5</v> + </e> + <e> + <k>child</k> + <v>8428</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>block_parameters</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6166</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>963</v> + </b> + <b> + <a>3</a> + <b>6</b> + <v>98</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>block_parameters</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6166</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>963</v> + </b> + <b> + <a>3</a> + <b>6</b> + <v>98</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>block_parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>33</a> + <b>34</b> + <v>1</v> + </b> + <b> + <a>98</a> + <b>99</b> + <v>1</v> + </b> + <b> + <a>1061</a> + <b>1062</b> + <v>1</v> + </b> + <b> + <a>7227</a> + <b>7228</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>33</a> + <b>34</b> + <v>1</v> + </b> + <b> + <a>98</a> + <b>99</b> + <v>1</v> + </b> + <b> + <a>1061</a> + <b>1062</b> + <v>1</v> + </b> + <b> + <a>7227</a> + <b>7228</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>block_parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8428</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8428</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>block_parameters_def</name> + <cardinality>7227</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>7227</v> + </e> + <e> + <k>loc</k> + <v>7227</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>7227</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>7227</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>break_child</name> + <cardinality>10</cardinality> + <columnsizes> + <e> + <k>break</k> + <v>10</v> + </e> + <e> + <k>child</k> + <v>10</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>break</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>10</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>break</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>10</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>break_def</name> + <cardinality>214</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>214</v> + </e> + <e> + <k>loc</k> + <v>214</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>214</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>214</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>call_arguments</name> + <cardinality>215041</cardinality> + <columnsizes> + <e> + <k>call</k> + <v>215041</v> + </e> + <e> + <k>arguments</k> + <v>215041</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>call</src> + <trg>arguments</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>215041</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>arguments</src> + <trg>call</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>215041</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>call_block</name> + <cardinality>62301</cardinality> + <columnsizes> + <e> + <k>call</k> + <v>62301</v> + </e> + <e> + <k>block</k> + <v>62301</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>call</src> + <trg>block</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>62301</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>block</src> + <trg>call</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>62301</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>call_def</name> + <cardinality>303820</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>303820</v> + </e> + <e> + <k>method</k> + <v>303820</v> + </e> + <e> + <k>loc</k> + <v>303820</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>method</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>303820</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>303820</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>method</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>303820</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>method</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>303820</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>303820</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>method</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>303820</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>call_receiver</name> + <cardinality>166508</cardinality> + <columnsizes> + <e> + <k>call</k> + <v>166508</v> + </e> + <e> + <k>receiver</k> + <v>166508</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>call</src> + <trg>receiver</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>166508</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>receiver</src> + <trg>call</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>166508</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>case_child</name> + <cardinality>1267</cardinality> + <columnsizes> + <e> + <k>case__</k> + <v>377</v> + </e> + <e> + <k>index</k> + <v>22</v> + </e> + <e> + <k>child</k> + <v>1267</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>case__</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>10</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>102</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>156</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>60</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>29</v> + </b> + <b> + <a>7</a> + <b>23</b> + <v>20</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>case__</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>10</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>102</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>156</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>60</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>29</v> + </b> + <b> + <a>7</a> + <b>23</b> + <v>20</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>case__</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>2</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>2</v> + </b> + <b> + <a>8</a> + <b>11</b> + <v>2</v> + </b> + <b> + <a>13</a> + <b>21</b> + <v>2</v> + </b> + <b> + <a>30</a> + <b>50</b> + <v>2</v> + </b> + <b> + <a>109</a> + <b>266</b> + <v>2</v> + </b> + <b> + <a>367</a> + <b>378</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>2</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>2</v> + </b> + <b> + <a>8</a> + <b>11</b> + <v>2</v> + </b> + <b> + <a>13</a> + <b>21</b> + <v>2</v> + </b> + <b> + <a>30</a> + <b>50</b> + <v>2</v> + </b> + <b> + <a>109</a> + <b>266</b> + <v>2</v> + </b> + <b> + <a>367</a> + <b>378</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>case__</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1267</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1267</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>case_def</name> + <cardinality>377</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>377</v> + </e> + <e> + <k>loc</k> + <v>377</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>377</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>377</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>case_value</name> + <cardinality>365</cardinality> + <columnsizes> + <e> + <k>case__</k> + <v>365</v> + </e> + <e> + <k>value</k> + <v>365</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>case__</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>365</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>case__</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>365</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>chained_string_child</name> + <cardinality>1031</cardinality> + <columnsizes> + <e> + <k>chained_string</k> + <v>268</v> + </e> + <e> + <k>index</k> + <v>12</v> + </e> + <e> + <k>child</k> + <v>1031</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>chained_string</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>85</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>62</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>43</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>38</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>20</v> + </b> + <b> + <a>8</a> + <b>13</b> + <v>20</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>chained_string</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>85</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>62</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>43</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>38</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>20</v> + </b> + <b> + <a>8</a> + <b>13</b> + <v>20</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>chained_string</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>1</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>1</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>1</v> + </b> + <b> + <a>20</a> + <b>21</b> + <v>1</v> + </b> + <b> + <a>32</a> + <b>33</b> + <v>1</v> + </b> + <b> + <a>40</a> + <b>41</b> + <v>1</v> + </b> + <b> + <a>78</a> + <b>79</b> + <v>1</v> + </b> + <b> + <a>121</a> + <b>122</b> + <v>1</v> + </b> + <b> + <a>183</a> + <b>184</b> + <v>1</v> + </b> + <b> + <a>268</a> + <b>269</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>1</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>1</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>1</v> + </b> + <b> + <a>20</a> + <b>21</b> + <v>1</v> + </b> + <b> + <a>32</a> + <b>33</b> + <v>1</v> + </b> + <b> + <a>40</a> + <b>41</b> + <v>1</v> + </b> + <b> + <a>78</a> + <b>79</b> + <v>1</v> + </b> + <b> + <a>121</a> + <b>122</b> + <v>1</v> + </b> + <b> + <a>183</a> + <b>184</b> + <v>1</v> + </b> + <b> + <a>268</a> + <b>269</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>chained_string</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1031</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1031</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>chained_string_def</name> + <cardinality>268</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>268</v> + </e> + <e> + <k>loc</k> + <v>268</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>268</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>268</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>class_child</name> + <cardinality>40498</cardinality> + <columnsizes> + <e> + <k>class</k> + <v>4669</v> + </e> + <e> + <k>index</k> + <v>305</v> + </e> + <e> + <k>child</k> + <v>40498</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>class</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1013</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>726</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>465</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>399</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>296</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>430</v> + </b> + <b> + <a>8</a> + <b>11</b> + <v>402</v> + </b> + <b> + <a>11</a> + <b>16</b> + <v>373</v> + </b> + <b> + <a>16</a> + <b>33</b> + <v>355</v> + </b> + <b> + <a>33</a> + <b>306</b> + <v>210</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>class</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1013</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>726</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>465</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>399</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>296</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>430</v> + </b> + <b> + <a>8</a> + <b>11</b> + <v>402</v> + </b> + <b> + <a>11</a> + <b>16</b> + <v>373</v> + </b> + <b> + <a>16</a> + <b>33</b> + <v>355</v> + </b> + <b> + <a>33</a> + <b>306</b> + <v>210</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>class</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>25</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>8</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>24</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>24</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>24</v> + </b> + <b> + <a>8</a> + <b>10</b> + <v>12</v> + </b> + <b> + <a>10</a> + <b>12</b> + <v>24</v> + </b> + <b> + <a>12</a> + <b>17</b> + <v>25</v> + </b> + <b> + <a>17</a> + <b>30</b> + <v>24</v> + </b> + <b> + <a>31</a> + <b>50</b> + <v>24</v> + </b> + <b> + <a>52</a> + <b>86</b> + <v>23</v> + </b> + <b> + <a>86</a> + <b>177</b> + <v>23</v> + </b> + <b> + <a>183</a> + <b>638</b> + <v>23</v> + </b> + <b> + <a>698</a> + <b>4670</b> + <v>14</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>25</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>8</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>24</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>24</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>24</v> + </b> + <b> + <a>8</a> + <b>10</b> + <v>12</v> + </b> + <b> + <a>10</a> + <b>12</b> + <v>24</v> + </b> + <b> + <a>12</a> + <b>17</b> + <v>25</v> + </b> + <b> + <a>17</a> + <b>30</b> + <v>24</v> + </b> + <b> + <a>31</a> + <b>50</b> + <v>24</v> + </b> + <b> + <a>52</a> + <b>86</b> + <v>23</v> + </b> + <b> + <a>86</a> + <b>177</b> + <v>23</v> + </b> + <b> + <a>183</a> + <b>638</b> + <v>23</v> + </b> + <b> + <a>698</a> + <b>4670</b> + <v>14</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>class</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>40498</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>40498</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>class_def</name> + <cardinality>5194</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>5194</v> + </e> + <e> + <k>name</k> + <v>5194</v> + </e> + <e> + <k>loc</k> + <v>5194</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5194</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5194</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5194</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5194</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5194</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5194</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>class_superclass</name> + <cardinality>4108</cardinality> + <columnsizes> + <e> + <k>class</k> + <v>4108</v> + </e> + <e> + <k>superclass</k> + <v>4108</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>class</src> + <trg>superclass</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4108</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>superclass</src> + <trg>class</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4108</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>conditional_def</name> + <cardinality>1120</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1120</v> + </e> + <e> + <k>alternative</k> + <v>1120</v> + </e> + <e> + <k>condition</k> + <v>1120</v> + </e> + <e> + <k>consequence</k> + <v>1120</v> + </e> + <e> + <k>loc</k> + <v>1120</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>alternative</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1120</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1120</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>consequence</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1120</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1120</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>alternative</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1120</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>alternative</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1120</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>alternative</src> + <trg>consequence</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1120</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>alternative</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1120</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1120</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>alternative</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1120</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>consequence</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1120</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1120</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>consequence</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1120</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>consequence</src> + <trg>alternative</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1120</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>consequence</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1120</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>consequence</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1120</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1120</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>alternative</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1120</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1120</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>consequence</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1120</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>containerparent</name> + <cardinality>7808</cardinality> + <columnsizes> + <e> + <k>parent</k> + <v>1489</v> + </e> + <e> + <k>child</k> + <v>7808</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>parent</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>538</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>306</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>119</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>160</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>102</v> + </b> + <b> + <a>6</a> + <b>10</b> + <v>115</v> + </b> + <b> + <a>10</a> + <b>30</b> + <v>112</v> + </b> + <b> + <a>30</a> + <b>295</b> + <v>34</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>parent</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>7808</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>delimited_symbol_child</name> + <cardinality>529</cardinality> + <columnsizes> + <e> + <k>delimited_symbol</k> + <v>384</v> + </e> + <e> + <k>index</k> + <v>8</v> + </e> + <e> + <k>child</k> + <v>529</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>delimited_symbol</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>290</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>72</v> + </b> + <b> + <a>3</a> + <b>9</b> + <v>22</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>delimited_symbol</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>290</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>72</v> + </b> + <b> + <a>3</a> + <b>9</b> + <v>22</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>delimited_symbol</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>1</v> + </b> + <b> + <a>12</a> + <b>13</b> + <v>1</v> + </b> + <b> + <a>22</a> + <b>23</b> + <v>1</v> + </b> + <b> + <a>94</a> + <b>95</b> + <v>1</v> + </b> + <b> + <a>384</a> + <b>385</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>1</v> + </b> + <b> + <a>12</a> + <b>13</b> + <v>1</v> + </b> + <b> + <a>22</a> + <b>23</b> + <v>1</v> + </b> + <b> + <a>94</a> + <b>95</b> + <v>1</v> + </b> + <b> + <a>384</a> + <b>385</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>delimited_symbol</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>529</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>529</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>delimited_symbol_def</name> + <cardinality>384</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>384</v> + </e> + <e> + <k>loc</k> + <v>384</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>384</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>384</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>destructured_left_assignment_child</name> + <cardinality>2</cardinality> + <columnsizes> + <e> + <k>destructured_left_assignment</k> + <v>1</v> + </e> + <e> + <k>index</k> + <v>2</v> + </e> + <e> + <k>child</k> + <v>2</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>destructured_left_assignment</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>destructured_left_assignment</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>destructured_left_assignment</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>destructured_left_assignment</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>destructured_left_assignment_def</name> + <cardinality>1</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1</v> + </e> + <e> + <k>loc</k> + <v>1</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>destructured_parameter_child</name> + <cardinality>127</cardinality> + <columnsizes> + <e> + <k>destructured_parameter</k> + <v>62</v> + </e> + <e> + <k>index</k> + <v>4</v> + </e> + <e> + <k>child</k> + <v>127</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>destructured_parameter</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>54</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>5</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>destructured_parameter</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>54</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>5</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>destructured_parameter</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>59</a> + <b>60</b> + <v>1</v> + </b> + <b> + <a>62</a> + <b>63</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>59</a> + <b>60</b> + <v>1</v> + </b> + <b> + <a>62</a> + <b>63</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>destructured_parameter</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>127</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>127</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>destructured_parameter_def</name> + <cardinality>62</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>62</v> + </e> + <e> + <k>loc</k> + <v>62</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>62</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>62</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>diagnostics</name> + <cardinality>64</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>64</v> + </e> + <e> + <k>severity</k> + <v>3</v> + </e> + <e> + <k>error_tag</k> + <v>3</v> + </e> + <e> + <k>error_message</k> + <v>13</v> + </e> + <e> + <k>full_error_message</k> + <v>61</v> + </e> + <e> + <k>location</k> + <v>64</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>severity</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>64</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>error_tag</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>64</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>error_message</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>64</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>full_error_message</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>64</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>location</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>64</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>severity</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>19</a> + <b>20</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>severity</src> + <trg>error_tag</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>severity</src> + <trg>error_message</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>4</a> + <b>5</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>severity</src> + <trg>full_error_message</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>18</a> + <b>19</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>severity</src> + <trg>location</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>19</a> + <b>20</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>error_tag</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>19</a> + <b>20</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>error_tag</src> + <trg>severity</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>error_tag</src> + <trg>error_message</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>4</a> + <b>5</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>error_tag</src> + <trg>full_error_message</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>18</a> + <b>19</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>error_tag</src> + <trg>location</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>19</a> + <b>20</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>error_message</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>10</v> + </b> + <b> + <a>16</a> + <b>17</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>error_message</src> + <trg>severity</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>error_message</src> + <trg>error_tag</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>error_message</src> + <trg>full_error_message</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>10</v> + </b> + <b> + <a>15</a> + <b>16</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>error_message</src> + <trg>location</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>10</v> + </b> + <b> + <a>16</a> + <b>17</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>full_error_message</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>57</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>full_error_message</src> + <trg>severity</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>61</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>full_error_message</src> + <trg>error_tag</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>61</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>full_error_message</src> + <trg>error_message</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>61</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>full_error_message</src> + <trg>location</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>57</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>location</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>64</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>location</src> + <trg>severity</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>64</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>location</src> + <trg>error_tag</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>64</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>location</src> + <trg>error_message</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>64</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>location</src> + <trg>full_error_message</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>64</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>do_block_child</name> + <cardinality>121320</cardinality> + <columnsizes> + <e> + <k>do_block</k> + <v>41642</v> + </e> + <e> + <k>index</k> + <v>245</v> + </e> + <e> + <k>child</k> + <v>121320</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>do_block</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13351</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>11657</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>6680</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>3602</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>3347</v> + </b> + <b> + <a>7</a> + <b>73</b> + <v>3002</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>do_block</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13351</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>11657</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>6680</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>3602</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>3347</v> + </b> + <b> + <a>7</a> + <b>73</b> + <v>3002</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>do_block</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>47</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>37</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>20</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>20</v> + </b> + <b> + <a>8</a> + <b>16</b> + <v>20</v> + </b> + <b> + <a>17</a> + <b>33</b> + <v>20</v> + </b> + <b> + <a>35</a> + <b>71</b> + <v>20</v> + </b> + <b> + <a>84</a> + <b>245</b> + <v>20</v> + </b> + <b> + <a>294</a> + <b>1246</b> + <v>20</v> + </b> + <b> + <a>1863</a> + <b>12218</b> + <v>17</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>47</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>37</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>20</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>20</v> + </b> + <b> + <a>8</a> + <b>16</b> + <v>20</v> + </b> + <b> + <a>17</a> + <b>33</b> + <v>20</v> + </b> + <b> + <a>35</a> + <b>71</b> + <v>20</v> + </b> + <b> + <a>84</a> + <b>245</b> + <v>20</v> + </b> + <b> + <a>294</a> + <b>1246</b> + <v>20</v> + </b> + <b> + <a>1863</a> + <b>12218</b> + <v>17</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>do_block</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>121320</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>121320</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>do_block_def</name> + <cardinality>41659</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>41659</v> + </e> + <e> + <k>loc</k> + <v>41659</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>41659</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>41659</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>do_block_parameters</name> + <cardinality>4565</cardinality> + <columnsizes> + <e> + <k>do_block</k> + <v>4565</v> + </e> + <e> + <k>parameters</k> + <v>4565</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>do_block</src> + <trg>parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4565</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parameters</src> + <trg>do_block</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4565</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>do_child</name> + <cardinality>268</cardinality> + <columnsizes> + <e> + <k>do</k> + <v>114</v> + </e> + <e> + <k>index</k> + <v>18</v> + </e> + <e> + <k>child</k> + <v>268</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>do</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>34</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>48</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>17</v> + </b> + <b> + <a>4</a> + <b>6</b> + <v>9</v> + </b> + <b> + <a>6</a> + <b>19</b> + <v>5</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>do</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>34</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>48</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>17</v> + </b> + <b> + <a>4</a> + <b>6</b> + <v>9</v> + </b> + <b> + <a>6</a> + <b>19</b> + <v>5</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>do</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>3</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>1</v> + </b> + <b> + <a>14</a> + <b>15</b> + <v>1</v> + </b> + <b> + <a>31</a> + <b>32</b> + <v>1</v> + </b> + <b> + <a>78</a> + <b>79</b> + <v>1</v> + </b> + <b> + <a>112</a> + <b>113</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>3</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>1</v> + </b> + <b> + <a>14</a> + <b>15</b> + <v>1</v> + </b> + <b> + <a>31</a> + <b>32</b> + <v>1</v> + </b> + <b> + <a>78</a> + <b>79</b> + <v>1</v> + </b> + <b> + <a>112</a> + <b>113</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>do</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>268</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>268</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>do_def</name> + <cardinality>117</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>117</v> + </e> + <e> + <k>loc</k> + <v>117</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>117</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>117</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>element_reference_child</name> + <cardinality>25626</cardinality> + <columnsizes> + <e> + <k>element_reference</k> + <v>25580</v> + </e> + <e> + <k>index</k> + <v>2</v> + </e> + <e> + <k>child</k> + <v>25626</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>element_reference</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>25534</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>46</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>element_reference</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>25534</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>46</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>element_reference</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>45</a> + <b>46</b> + <v>1</v> + </b> + <b> + <a>24988</a> + <b>24989</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>45</a> + <b>46</b> + <v>1</v> + </b> + <b> + <a>24988</a> + <b>24989</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>element_reference</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>25626</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>25626</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>element_reference_def</name> + <cardinality>25582</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>25582</v> + </e> + <e> + <k>object</k> + <v>25582</v> + </e> + <e> + <k>loc</k> + <v>25582</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>object</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>25582</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>25582</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>object</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>25582</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>object</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>25582</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>25582</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>object</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>25582</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>else_child</name> + <cardinality>2696</cardinality> + <columnsizes> + <e> + <k>else</k> + <v>2119</v> + </e> + <e> + <k>index</k> + <v>11</v> + </e> + <e> + <k>child</k> + <v>2696</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>else</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1786</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>207</v> + </b> + <b> + <a>3</a> + <b>12</b> + <v>126</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>else</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1786</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>207</v> + </b> + <b> + <a>3</a> + <b>12</b> + <v>126</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>else</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>1</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>1</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>1</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>1</v> + </b> + <b> + <a>16</a> + <b>17</b> + <v>1</v> + </b> + <b> + <a>27</a> + <b>28</b> + <v>1</v> + </b> + <b> + <a>53</a> + <b>54</b> + <v>1</v> + </b> + <b> + <a>126</a> + <b>127</b> + <v>1</v> + </b> + <b> + <a>333</a> + <b>334</b> + <v>1</v> + </b> + <b> + <a>2119</a> + <b>2120</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>1</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>1</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>1</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>1</v> + </b> + <b> + <a>16</a> + <b>17</b> + <v>1</v> + </b> + <b> + <a>27</a> + <b>28</b> + <v>1</v> + </b> + <b> + <a>53</a> + <b>54</b> + <v>1</v> + </b> + <b> + <a>126</a> + <b>127</b> + <v>1</v> + </b> + <b> + <a>333</a> + <b>334</b> + <v>1</v> + </b> + <b> + <a>2119</a> + <b>2120</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>else</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2696</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2696</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>else_def</name> + <cardinality>2122</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>2122</v> + </e> + <e> + <k>loc</k> + <v>2122</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2122</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2122</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>elsif_alternative</name> + <cardinality>271</cardinality> + <columnsizes> + <e> + <k>elsif</k> + <v>271</v> + </e> + <e> + <k>alternative</k> + <v>271</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>elsif</src> + <trg>alternative</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>271</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>alternative</src> + <trg>elsif</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>271</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>elsif_consequence</name> + <cardinality>491</cardinality> + <columnsizes> + <e> + <k>elsif</k> + <v>491</v> + </e> + <e> + <k>consequence</k> + <v>491</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>elsif</src> + <trg>consequence</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>491</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>consequence</src> + <trg>elsif</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>491</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>elsif_def</name> + <cardinality>491</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>491</v> + </e> + <e> + <k>condition</k> + <v>491</v> + </e> + <e> + <k>loc</k> + <v>491</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>491</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>491</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>491</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>491</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>491</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>491</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>end_block_child</name> + <cardinality>0</cardinality> + <columnsizes> + <e> + <k>end_block</k> + <v>0</v> + </e> + <e> + <k>index</k> + <v>0</v> + </e> + <e> + <k>child</k> + <v>0</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>end_block</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>end_block</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>end_block</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>end_block</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>end_block_def</name> + <cardinality>0</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>0</v> + </e> + <e> + <k>loc</k> + <v>0</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ensure_child</name> + <cardinality>1517</cardinality> + <columnsizes> + <e> + <k>ensure</k> + <v>1125</v> + </e> + <e> + <k>index</k> + <v>16</v> + </e> + <e> + <k>child</k> + <v>1517</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>ensure</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>878</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>156</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>85</v> + </b> + <b> + <a>6</a> + <b>17</b> + <v>6</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ensure</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>878</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>156</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>85</v> + </b> + <b> + <a>6</a> + <b>17</b> + <v>6</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>ensure</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>8</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>2</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>2</v> + </b> + <b> + <a>16</a> + <b>17</b> + <v>1</v> + </b> + <b> + <a>91</a> + <b>92</b> + <v>1</v> + </b> + <b> + <a>247</a> + <b>248</b> + <v>1</v> + </b> + <b> + <a>1125</a> + <b>1126</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>8</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>2</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>2</v> + </b> + <b> + <a>16</a> + <b>17</b> + <v>1</v> + </b> + <b> + <a>91</a> + <b>92</b> + <v>1</v> + </b> + <b> + <a>247</a> + <b>248</b> + <v>1</v> + </b> + <b> + <a>1125</a> + <b>1126</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>ensure</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1517</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1517</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>ensure_def</name> + <cardinality>1125</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1125</v> + </e> + <e> + <k>loc</k> + <v>1125</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1125</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1125</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>exception_variable_def</name> + <cardinality>309</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>309</v> + </e> + <e> + <k>child</k> + <v>309</v> + </e> + <e> + <k>loc</k> + <v>309</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>309</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>309</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>309</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>309</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>309</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>309</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>exceptions_child</name> + <cardinality>496</cardinality> + <columnsizes> + <e> + <k>exceptions</k> + <v>429</v> + </e> + <e> + <k>index</k> + <v>8</v> + </e> + <e> + <k>child</k> + <v>496</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>exceptions</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>386</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>30</v> + </b> + <b> + <a>3</a> + <b>9</b> + <v>12</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>exceptions</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>386</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>30</v> + </b> + <b> + <a>3</a> + <b>9</b> + <v>12</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>exceptions</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>4</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>1</v> + </b> + <b> + <a>12</a> + <b>13</b> + <v>1</v> + </b> + <b> + <a>42</a> + <b>43</b> + <v>1</v> + </b> + <b> + <a>420</a> + <b>421</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>4</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>1</v> + </b> + <b> + <a>12</a> + <b>13</b> + <v>1</v> + </b> + <b> + <a>42</a> + <b>43</b> + <v>1</v> + </b> + <b> + <a>420</a> + <b>421</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>exceptions</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>496</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>496</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>exceptions_def</name> + <cardinality>429</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>429</v> + </e> + <e> + <k>loc</k> + <v>429</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>429</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>429</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>files</name> + <cardinality>6322</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>6322</v> + </e> + <e> + <k>name</k> + <v>6322</v> + </e> + <e> + <k>simple</k> + <v>4840</v> + </e> + <e> + <k>ext</k> + <v>6</v> + </e> + <e> + <k>fromSource</k> + <v>3</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6322</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>simple</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6322</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>ext</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6322</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>fromSource</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6322</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6322</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>simple</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6322</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>ext</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6322</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>fromSource</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6322</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>simple</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4212</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>429</v> + </b> + <b> + <a>3</a> + <b>45</b> + <v>197</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>simple</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4212</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>429</v> + </b> + <b> + <a>3</a> + <b>45</b> + <v>197</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>simple</src> + <trg>ext</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4840</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>simple</src> + <trg>fromSource</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4840</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ext</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>429</a> + <b>430</b> + <v>3</v> + </b> + <b> + <a>1426</a> + <b>1427</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ext</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>429</a> + <b>430</b> + <v>3</v> + </b> + <b> + <a>1426</a> + <b>1427</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ext</src> + <trg>simple</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>220</a> + <b>221</b> + <v>3</v> + </b> + <b> + <a>1200</a> + <b>1201</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>ext</src> + <trg>fromSource</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>fromSource</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1855</a> + <b>1856</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>fromSource</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1855</a> + <b>1856</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>fromSource</src> + <trg>simple</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1420</a> + <b>1421</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>fromSource</src> + <trg>ext</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>folders</name> + <cardinality>1489</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1489</v> + </e> + <e> + <k>name</k> + <v>1489</v> + </e> + <e> + <k>simple</k> + <v>630</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1489</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>simple</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1489</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1489</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>simple</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1489</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>simple</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>337</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>153</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>51</v> + </b> + <b> + <a>4</a> + <b>7</b> + <v>54</v> + </b> + <b> + <a>7</a> + <b>54</b> + <v>34</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>simple</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>337</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>153</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>51</v> + </b> + <b> + <a>4</a> + <b>7</b> + <v>54</v> + </b> + <b> + <a>7</a> + <b>54</b> + <v>34</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>for_def</name> + <cardinality>1</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1</v> + </e> + <e> + <k>body</k> + <v>1</v> + </e> + <e> + <k>pattern</k> + <v>1</v> + </e> + <e> + <k>value</k> + <v>1</v> + </e> + <e> + <k>loc</k> + <v>1</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>pattern</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>pattern</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>pattern</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>pattern</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>pattern</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>pattern</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>pattern</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>pattern</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>hash_child</name> + <cardinality>14706</cardinality> + <columnsizes> + <e> + <k>hash</k> + <v>6654</v> + </e> + <e> + <k>index</k> + <v>113</v> + </e> + <e> + <k>child</k> + <v>14706</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>hash</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3375</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1747</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>688</v> + </b> + <b> + <a>4</a> + <b>6</b> + <v>516</v> + </b> + <b> + <a>6</a> + <b>112</b> + <v>325</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>hash</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3375</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1747</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>688</v> + </b> + <b> + <a>4</a> + <b>6</b> + <v>516</v> + </b> + <b> + <a>6</a> + <b>112</b> + <v>325</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>hash</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>42</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>20</v> + </b> + <b> + <a>3</a> + <b>10</b> + <v>4</v> + </b> + <b> + <a>10</a> + <b>11</b> + <v>9</v> + </b> + <b> + <a>11</a> + <b>23</b> + <v>9</v> + </b> + <b> + <a>23</a> + <b>32</b> + <v>8</v> + </b> + <b> + <a>34</a> + <b>70</b> + <v>9</v> + </b> + <b> + <a>82</a> + <b>3204</b> + <v>9</v> + </b> + <b> + <a>6500</a> + <b>6501</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>42</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>20</v> + </b> + <b> + <a>3</a> + <b>10</b> + <v>4</v> + </b> + <b> + <a>10</a> + <b>11</b> + <v>9</v> + </b> + <b> + <a>11</a> + <b>23</b> + <v>9</v> + </b> + <b> + <a>23</a> + <b>32</b> + <v>8</v> + </b> + <b> + <a>34</a> + <b>70</b> + <v>9</v> + </b> + <b> + <a>82</a> + <b>3204</b> + <v>9</v> + </b> + <b> + <a>6500</a> + <b>6501</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>hash</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14706</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14706</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>hash_def</name> + <cardinality>8122</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>8122</v> + </e> + <e> + <k>loc</k> + <v>8122</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8122</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8122</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>hash_splat_argument_def</name> + <cardinality>398</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>398</v> + </e> + <e> + <k>child</k> + <v>398</v> + </e> + <e> + <k>loc</k> + <v>398</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>398</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>398</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>398</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>398</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>398</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>398</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>hash_splat_parameter_def</name> + <cardinality>413</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>413</v> + </e> + <e> + <k>loc</k> + <v>413</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>413</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>413</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>hash_splat_parameter_name</name> + <cardinality>350</cardinality> + <columnsizes> + <e> + <k>hash_splat_parameter</k> + <v>350</v> + </e> + <e> + <k>name</k> + <v>350</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>hash_splat_parameter</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>350</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>hash_splat_parameter</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>350</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>heredoc_body_child</name> + <cardinality>7402</cardinality> + <columnsizes> + <e> + <k>heredoc_body</k> + <v>1610</v> + </e> + <e> + <k>index</k> + <v>73</v> + </e> + <e> + <k>child</k> + <v>7402</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>heredoc_body</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>903</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>187</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>247</v> + </b> + <b> + <a>7</a> + <b>9</b> + <v>94</v> + </b> + <b> + <a>10</a> + <b>15</b> + <v>124</v> + </b> + <b> + <a>16</a> + <b>73</b> + <v>51</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>heredoc_body</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>903</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>187</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>247</v> + </b> + <b> + <a>7</a> + <b>9</b> + <v>94</v> + </b> + <b> + <a>10</a> + <b>15</b> + <v>124</v> + </b> + <b> + <a>16</a> + <b>73</b> + <v>51</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>heredoc_body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>22</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>6</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>4</v> + </b> + <b> + <a>4</a> + <b>7</b> + <v>5</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>3</v> + </b> + <b> + <a>9</a> + <b>12</b> + <v>6</v> + </b> + <b> + <a>12</a> + <b>22</b> + <v>6</v> + </b> + <b> + <a>24</a> + <b>51</b> + <v>6</v> + </b> + <b> + <a>77</a> + <b>173</b> + <v>6</v> + </b> + <b> + <a>260</a> + <b>691</b> + <v>6</v> + </b> + <b> + <a>1573</a> + <b>1574</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>22</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>6</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>4</v> + </b> + <b> + <a>4</a> + <b>7</b> + <v>5</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>3</v> + </b> + <b> + <a>9</a> + <b>12</b> + <v>6</v> + </b> + <b> + <a>12</a> + <b>22</b> + <v>6</v> + </b> + <b> + <a>24</a> + <b>51</b> + <v>6</v> + </b> + <b> + <a>77</a> + <b>173</b> + <v>6</v> + </b> + <b> + <a>260</a> + <b>691</b> + <v>6</v> + </b> + <b> + <a>1573</a> + <b>1574</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>heredoc_body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>7402</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>7402</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>heredoc_body_def</name> + <cardinality>1610</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1610</v> + </e> + <e> + <k>loc</k> + <v>1610</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1610</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1610</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>if_alternative</name> + <cardinality>1962</cardinality> + <columnsizes> + <e> + <k>if</k> + <v>1962</v> + </e> + <e> + <k>alternative</k> + <v>1962</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>if</src> + <trg>alternative</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1962</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>alternative</src> + <trg>if</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1962</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>if_consequence</name> + <cardinality>5694</cardinality> + <columnsizes> + <e> + <k>if</k> + <v>5694</v> + </e> + <e> + <k>consequence</k> + <v>5694</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>if</src> + <trg>consequence</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5694</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>consequence</src> + <trg>if</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5694</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>if_def</name> + <cardinality>5713</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>5713</v> + </e> + <e> + <k>condition</k> + <v>5713</v> + </e> + <e> + <k>loc</k> + <v>5713</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5713</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5713</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5713</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5713</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5713</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5713</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>if_modifier_def</name> + <cardinality>4276</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>4276</v> + </e> + <e> + <k>body</k> + <v>4276</v> + </e> + <e> + <k>condition</k> + <v>4276</v> + </e> + <e> + <k>loc</k> + <v>4276</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4276</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4276</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4276</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4276</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4276</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4276</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4276</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4276</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4276</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4276</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4276</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4276</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>in_def</name> + <cardinality>1</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1</v> + </e> + <e> + <k>child</k> + <v>1</v> + </e> + <e> + <k>loc</k> + <v>1</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>interpolation_child</name> + <cardinality>11773</cardinality> + <columnsizes> + <e> + <k>interpolation</k> + <v>11773</v> + </e> + <e> + <k>index</k> + <v>1</v> + </e> + <e> + <k>child</k> + <v>11773</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>interpolation</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>11773</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>interpolation</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>11773</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>interpolation</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>11501</a> + <b>11502</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>11501</a> + <b>11502</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>interpolation</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>11773</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>11773</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>interpolation_def</name> + <cardinality>11773</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>11773</v> + </e> + <e> + <k>loc</k> + <v>11773</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>11773</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>11773</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>keyword_parameter_def</name> + <cardinality>1099</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1099</v> + </e> + <e> + <k>name</k> + <v>1099</v> + </e> + <e> + <k>loc</k> + <v>1099</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1099</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1099</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1099</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1099</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1099</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1099</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>keyword_parameter_value</name> + <cardinality>812</cardinality> + <columnsizes> + <e> + <k>keyword_parameter</k> + <v>812</v> + </e> + <e> + <k>value</k> + <v>812</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>keyword_parameter</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>812</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>keyword_parameter</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>812</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>lambda_def</name> + <cardinality>660</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>660</v> + </e> + <e> + <k>body</k> + <v>660</v> + </e> + <e> + <k>loc</k> + <v>660</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>660</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>660</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>660</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>660</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>660</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>660</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>lambda_parameters</name> + <cardinality>190</cardinality> + <columnsizes> + <e> + <k>lambda</k> + <v>190</v> + </e> + <e> + <k>parameters</k> + <v>190</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>lambda</src> + <trg>parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>190</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parameters</src> + <trg>lambda</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>190</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>lambda_parameters_child</name> + <cardinality>243</cardinality> + <columnsizes> + <e> + <k>lambda_parameters</k> + <v>185</v> + </e> + <e> + <k>index</k> + <v>4</v> + </e> + <e> + <k>child</k> + <v>243</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>lambda_parameters</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>147</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>24</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>14</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>lambda_parameters</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>147</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>24</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>14</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>lambda_parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>6</a> + <b>7</b> + <v>1</v> + </b> + <b> + <a>14</a> + <b>15</b> + <v>1</v> + </b> + <b> + <a>38</a> + <b>39</b> + <v>1</v> + </b> + <b> + <a>185</a> + <b>186</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>6</a> + <b>7</b> + <v>1</v> + </b> + <b> + <a>14</a> + <b>15</b> + <v>1</v> + </b> + <b> + <a>38</a> + <b>39</b> + <v>1</v> + </b> + <b> + <a>185</a> + <b>186</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>lambda_parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>243</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>243</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>lambda_parameters_def</name> + <cardinality>190</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>190</v> + </e> + <e> + <k>loc</k> + <v>190</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>190</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>190</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>left_assignment_list_child</name> + <cardinality>1721</cardinality> + <columnsizes> + <e> + <k>left_assignment_list</k> + <v>773</v> + </e> + <e> + <k>index</k> + <v>8</v> + </e> + <e> + <k>child</k> + <v>1721</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>left_assignment_list</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>632</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>118</v> + </b> + <b> + <a>4</a> + <b>9</b> + <v>21</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left_assignment_list</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>632</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>118</v> + </b> + <b> + <a>4</a> + <b>9</b> + <v>21</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>left_assignment_list</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>2</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>21</a> + <b>22</b> + <v>1</v> + </b> + <b> + <a>139</a> + <b>140</b> + <v>1</v> + </b> + <b> + <a>771</a> + <b>772</b> + <v>1</v> + </b> + <b> + <a>773</a> + <b>774</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>2</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>21</a> + <b>22</b> + <v>1</v> + </b> + <b> + <a>139</a> + <b>140</b> + <v>1</v> + </b> + <b> + <a>771</a> + <b>772</b> + <v>1</v> + </b> + <b> + <a>773</a> + <b>774</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>left_assignment_list</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1721</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1721</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>left_assignment_list_def</name> + <cardinality>773</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>773</v> + </e> + <e> + <k>loc</k> + <v>773</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>773</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>773</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>locations_default</name> + <cardinality>2604972</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>2604972</v> + </e> + <e> + <k>file</k> + <v>6322</v> + </e> + <e> + <k>start_line</k> + <v>4526</v> + </e> + <e> + <k>start_column</k> + <v>1060</v> + </e> + <e> + <k>end_line</k> + <v>4526</v> + </e> + <e> + <k>end_column</k> + <v>1070</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>file</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2604972</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>start_line</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2604972</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>start_column</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2604972</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>end_line</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2604972</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>end_column</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2604972</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>file</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>28</b> + <v>487</v> + </b> + <b> + <a>28</a> + <b>42</b> + <v>484</v> + </b> + <b> + <a>42</a> + <b>63</b> + <v>477</v> + </b> + <b> + <a>63</a> + <b>83</b> + <v>477</v> + </b> + <b> + <a>83</a> + <b>100</b> + <v>477</v> + </b> + <b> + <a>100</a> + <b>137</b> + <v>480</v> + </b> + <b> + <a>137</a> + <b>181</b> + <v>497</v> + </b> + <b> + <a>181</a> + <b>228</b> + <v>477</v> + </b> + <b> + <a>228</a> + <b>310</b> + <v>484</v> + </b> + <b> + <a>311</a> + <b>416</b> + <v>480</v> + </b> + <b> + <a>416</a> + <b>593</b> + <v>480</v> + </b> + <b> + <a>593</a> + <b>1000</b> + <v>477</v> + </b> + <b> + <a>1002</a> + <b>4862</b> + <v>477</v> + </b> + <b> + <a>4991</a> + <b>11002</b> + <v>64</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>file</src> + <trg>start_line</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>5</b> + <v>368</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>460</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>412</v> + </b> + <b> + <a>8</a> + <b>10</b> + <v>507</v> + </b> + <b> + <a>10</a> + <b>13</b> + <v>572</v> + </b> + <b> + <a>13</a> + <b>17</b> + <v>555</v> + </b> + <b> + <a>17</a> + <b>22</b> + <v>559</v> + </b> + <b> + <a>22</a> + <b>29</b> + <v>524</v> + </b> + <b> + <a>29</a> + <b>39</b> + <v>538</v> + </b> + <b> + <a>39</a> + <b>52</b> + <v>477</v> + </b> + <b> + <a>52</a> + <b>76</b> + <v>480</v> + </b> + <b> + <a>76</a> + <b>150</b> + <v>477</v> + </b> + <b> + <a>150</a> + <b>1135</b> + <v>388</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>file</src> + <trg>start_column</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>16</b> + <v>487</v> + </b> + <b> + <a>16</a> + <b>22</b> + <v>528</v> + </b> + <b> + <a>22</a> + <b>30</b> + <v>501</v> + </b> + <b> + <a>30</a> + <b>38</b> + <v>524</v> + </b> + <b> + <a>38</a> + <b>44</b> + <v>490</v> + </b> + <b> + <a>44</a> + <b>51</b> + <v>480</v> + </b> + <b> + <a>51</a> + <b>59</b> + <v>494</v> + </b> + <b> + <a>59</a> + <b>67</b> + <v>490</v> + </b> + <b> + <a>67</a> + <b>74</b> + <v>497</v> + </b> + <b> + <a>74</a> + <b>84</b> + <v>507</v> + </b> + <b> + <a>84</a> + <b>95</b> + <v>490</v> + </b> + <b> + <a>95</a> + <b>114</b> + <v>477</v> + </b> + <b> + <a>114</a> + <b>241</b> + <v>351</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>file</src> + <trg>end_line</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>5</b> + <v>368</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>449</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>422</v> + </b> + <b> + <a>8</a> + <b>10</b> + <v>507</v> + </b> + <b> + <a>10</a> + <b>13</b> + <v>572</v> + </b> + <b> + <a>13</a> + <b>17</b> + <v>555</v> + </b> + <b> + <a>17</a> + <b>22</b> + <v>559</v> + </b> + <b> + <a>22</a> + <b>29</b> + <v>524</v> + </b> + <b> + <a>29</a> + <b>39</b> + <v>538</v> + </b> + <b> + <a>39</a> + <b>52</b> + <v>477</v> + </b> + <b> + <a>52</a> + <b>76</b> + <v>480</v> + </b> + <b> + <a>76</a> + <b>150</b> + <v>477</v> + </b> + <b> + <a>150</a> + <b>1135</b> + <v>388</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>file</src> + <trg>end_column</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>19</b> + <v>576</v> + </b> + <b> + <a>19</a> + <b>26</b> + <v>484</v> + </b> + <b> + <a>26</a> + <b>34</b> + <v>501</v> + </b> + <b> + <a>34</a> + <b>42</b> + <v>490</v> + </b> + <b> + <a>42</a> + <b>48</b> + <v>477</v> + </b> + <b> + <a>48</a> + <b>57</b> + <v>521</v> + </b> + <b> + <a>57</a> + <b>64</b> + <v>480</v> + </b> + <b> + <a>64</a> + <b>72</b> + <v>511</v> + </b> + <b> + <a>72</a> + <b>79</b> + <v>480</v> + </b> + <b> + <a>79</a> + <b>89</b> + <v>511</v> + </b> + <b> + <a>89</a> + <b>101</b> + <v>511</v> + </b> + <b> + <a>101</a> + <b>121</b> + <v>497</v> + </b> + <b> + <a>121</a> + <b>246</b> + <v>279</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>start_line</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>371</v> + </b> + <b> + <a>2</a> + <b>8</b> + <v>163</v> + </b> + <b> + <a>8</a> + <b>11</b> + <v>344</v> + </b> + <b> + <a>11</a> + <b>23</b> + <v>344</v> + </b> + <b> + <a>23</a> + <b>39</b> + <v>344</v> + </b> + <b> + <a>39</a> + <b>77</b> + <v>344</v> + </b> + <b> + <a>77</a> + <b>114</b> + <v>340</v> + </b> + <b> + <a>114</a> + <b>154</b> + <v>340</v> + </b> + <b> + <a>154</a> + <b>208</b> + <v>340</v> + </b> + <b> + <a>208</a> + <b>287</b> + <v>344</v> + </b> + <b> + <a>287</a> + <b>482</b> + <v>344</v> + </b> + <b> + <a>485</a> + <b>1012</b> + <v>340</v> + </b> + <b> + <a>1015</a> + <b>2889</b> + <v>340</v> + </b> + <b> + <a>2913</a> + <b>14685</b> + <v>221</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>start_line</src> + <trg>file</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1250</v> + </b> + <b> + <a>2</a> + <b>5</b> + <v>412</v> + </b> + <b> + <a>5</a> + <b>11</b> + <v>398</v> + </b> + <b> + <a>11</a> + <b>14</b> + <v>310</v> + </b> + <b> + <a>14</a> + <b>18</b> + <v>368</v> + </b> + <b> + <a>18</a> + <b>25</b> + <v>398</v> + </b> + <b> + <a>25</a> + <b>39</b> + <v>340</v> + </b> + <b> + <a>39</a> + <b>78</b> + <v>340</v> + </b> + <b> + <a>78</a> + <b>173</b> + <v>344</v> + </b> + <b> + <a>178</a> + <b>1184</b> + <v>340</v> + </b> + <b> + <a>1187</a> + <b>1680</b> + <v>20</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>start_line</src> + <trg>start_column</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>374</v> + </b> + <b> + <a>2</a> + <b>5</b> + <v>146</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>357</v> + </b> + <b> + <a>8</a> + <b>16</b> + <v>398</v> + </b> + <b> + <a>16</a> + <b>25</b> + <v>340</v> + </b> + <b> + <a>25</a> + <b>39</b> + <v>347</v> + </b> + <b> + <a>39</a> + <b>49</b> + <v>357</v> + </b> + <b> + <a>49</a> + <b>59</b> + <v>351</v> + </b> + <b> + <a>59</a> + <b>71</b> + <v>351</v> + </b> + <b> + <a>71</a> + <b>80</b> + <v>354</v> + </b> + <b> + <a>80</a> + <b>93</b> + <v>354</v> + </b> + <b> + <a>93</a> + <b>114</b> + <v>340</v> + </b> + <b> + <a>114</a> + <b>163</b> + <v>344</v> + </b> + <b> + <a>163</a> + <b>220</b> + <v>105</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>start_line</src> + <trg>end_line</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1135</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>743</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>412</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>327</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>361</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>279</v> + </b> + <b> + <a>7</a> + <b>10</b> + <v>388</v> + </b> + <b> + <a>10</a> + <b>15</b> + <v>381</v> + </b> + <b> + <a>15</a> + <b>25</b> + <v>357</v> + </b> + <b> + <a>25</a> + <b>260</b> + <v>139</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>start_line</src> + <trg>end_column</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>374</v> + </b> + <b> + <a>2</a> + <b>6</b> + <v>170</v> + </b> + <b> + <a>6</a> + <b>9</b> + <v>385</v> + </b> + <b> + <a>9</a> + <b>17</b> + <v>391</v> + </b> + <b> + <a>17</a> + <b>28</b> + <v>340</v> + </b> + <b> + <a>28</a> + <b>43</b> + <v>340</v> + </b> + <b> + <a>43</a> + <b>53</b> + <v>361</v> + </b> + <b> + <a>53</a> + <b>64</b> + <v>364</v> + </b> + <b> + <a>64</a> + <b>75</b> + <v>347</v> + </b> + <b> + <a>75</a> + <b>84</b> + <v>340</v> + </b> + <b> + <a>84</a> + <b>97</b> + <v>340</v> + </b> + <b> + <a>97</a> + <b>120</b> + <v>347</v> + </b> + <b> + <a>120</a> + <b>174</b> + <v>351</v> + </b> + <b> + <a>175</a> + <b>222</b> + <v>68</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>start_column</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>81</v> + </b> + <b> + <a>2</a> + <b>4</b> + <v>92</v> + </b> + <b> + <a>4</a> + <b>10</b> + <v>88</v> + </b> + <b> + <a>10</a> + <b>21</b> + <v>81</v> + </b> + <b> + <a>21</a> + <b>34</b> + <v>81</v> + </b> + <b> + <a>36</a> + <b>62</b> + <v>81</v> + </b> + <b> + <a>63</a> + <b>116</b> + <v>81</v> + </b> + <b> + <a>130</a> + <b>342</b> + <v>81</v> + </b> + <b> + <a>369</a> + <b>1166</b> + <v>81</v> + </b> + <b> + <a>1182</a> + <b>2945</b> + <v>81</v> + </b> + <b> + <a>3082</a> + <b>7421</b> + <v>81</v> + </b> + <b> + <a>7778</a> + <b>11207</b> + <v>81</v> + </b> + <b> + <a>11280</a> + <b>41035</b> + <v>61</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>start_column</src> + <trg>file</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>126</v> + </b> + <b> + <a>2</a> + <b>4</b> + <v>85</v> + </b> + <b> + <a>4</a> + <b>9</b> + <v>81</v> + </b> + <b> + <a>9</a> + <b>17</b> + <v>88</v> + </b> + <b> + <a>17</a> + <b>27</b> + <v>81</v> + </b> + <b> + <a>27</a> + <b>55</b> + <v>85</v> + </b> + <b> + <a>56</a> + <b>125</b> + <v>81</v> + </b> + <b> + <a>125</a> + <b>305</b> + <v>81</v> + </b> + <b> + <a>317</a> + <b>585</b> + <v>81</v> + </b> + <b> + <a>598</a> + <b>998</b> + <v>81</v> + </b> + <b> + <a>1042</a> + <b>1223</b> + <v>81</v> + </b> + <b> + <a>1227</a> + <b>1315</b> + <v>85</v> + </b> + <b> + <a>1357</a> + <b>1550</b> + <v>17</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>start_column</src> + <trg>start_line</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>112</v> + </b> + <b> + <a>2</a> + <b>4</b> + <v>85</v> + </b> + <b> + <a>4</a> + <b>9</b> + <v>81</v> + </b> + <b> + <a>9</a> + <b>17</b> + <v>85</v> + </b> + <b> + <a>17</a> + <b>27</b> + <v>78</v> + </b> + <b> + <a>27</a> + <b>44</b> + <v>81</v> + </b> + <b> + <a>44</a> + <b>85</b> + <v>81</v> + </b> + <b> + <a>85</a> + <b>177</b> + <v>81</v> + </b> + <b> + <a>186</a> + <b>351</b> + <v>81</v> + </b> + <b> + <a>354</a> + <b>553</b> + <v>81</v> + </b> + <b> + <a>553</a> + <b>759</b> + <v>81</v> + </b> + <b> + <a>760</a> + <b>810</b> + <v>81</v> + </b> + <b> + <a>810</a> + <b>1028</b> + <v>44</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>start_column</src> + <trg>end_line</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>109</v> + </b> + <b> + <a>2</a> + <b>4</b> + <v>85</v> + </b> + <b> + <a>4</a> + <b>9</b> + <v>85</v> + </b> + <b> + <a>9</a> + <b>17</b> + <v>85</v> + </b> + <b> + <a>17</a> + <b>28</b> + <v>95</v> + </b> + <b> + <a>28</a> + <b>53</b> + <v>81</v> + </b> + <b> + <a>57</a> + <b>103</b> + <v>81</v> + </b> + <b> + <a>109</a> + <b>204</b> + <v>81</v> + </b> + <b> + <a>204</a> + <b>389</b> + <v>81</v> + </b> + <b> + <a>426</a> + <b>609</b> + <v>81</v> + </b> + <b> + <a>619</a> + <b>786</b> + <v>81</v> + </b> + <b> + <a>786</a> + <b>846</b> + <v>81</v> + </b> + <b> + <a>877</a> + <b>1028</b> + <v>27</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>start_column</src> + <trg>end_column</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>109</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>68</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>81</v> + </b> + <b> + <a>5</a> + <b>9</b> + <v>81</v> + </b> + <b> + <a>9</a> + <b>12</b> + <v>85</v> + </b> + <b> + <a>12</a> + <b>19</b> + <v>95</v> + </b> + <b> + <a>19</a> + <b>27</b> + <v>88</v> + </b> + <b> + <a>27</a> + <b>43</b> + <v>81</v> + </b> + <b> + <a>43</a> + <b>66</b> + <v>81</v> + </b> + <b> + <a>66</a> + <b>89</b> + <v>85</v> + </b> + <b> + <a>90</a> + <b>121</b> + <v>85</v> + </b> + <b> + <a>126</a> + <b>149</b> + <v>81</v> + </b> + <b> + <a>149</a> + <b>187</b> + <v>34</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end_line</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>3</b> + <v>102</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>340</v> + </b> + <b> + <a>4</a> + <b>8</b> + <v>344</v> + </b> + <b> + <a>8</a> + <b>21</b> + <v>374</v> + </b> + <b> + <a>21</a> + <b>35</b> + <v>340</v> + </b> + <b> + <a>35</a> + <b>71</b> + <v>340</v> + </b> + <b> + <a>71</a> + <b>106</b> + <v>340</v> + </b> + <b> + <a>106</a> + <b>146</b> + <v>340</v> + </b> + <b> + <a>146</a> + <b>202</b> + <v>340</v> + </b> + <b> + <a>202</a> + <b>264</b> + <v>344</v> + </b> + <b> + <a>264</a> + <b>433</b> + <v>340</v> + </b> + <b> + <a>436</a> + <b>889</b> + <v>340</v> + </b> + <b> + <a>891</a> + <b>2113</b> + <v>340</v> + </b> + <b> + <a>2173</a> + <b>13376</b> + <v>293</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end_line</src> + <trg>file</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1250</v> + </b> + <b> + <a>2</a> + <b>5</b> + <v>412</v> + </b> + <b> + <a>5</a> + <b>11</b> + <v>398</v> + </b> + <b> + <a>11</a> + <b>14</b> + <v>310</v> + </b> + <b> + <a>14</a> + <b>18</b> + <v>368</v> + </b> + <b> + <a>18</a> + <b>25</b> + <v>398</v> + </b> + <b> + <a>25</a> + <b>39</b> + <v>340</v> + </b> + <b> + <a>39</a> + <b>78</b> + <v>340</v> + </b> + <b> + <a>78</a> + <b>173</b> + <v>344</v> + </b> + <b> + <a>178</a> + <b>1184</b> + <v>340</v> + </b> + <b> + <a>1188</a> + <b>1680</b> + <v>20</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end_line</src> + <trg>start_line</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1083</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>698</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>419</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>351</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>354</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>224</v> + </b> + <b> + <a>7</a> + <b>10</b> + <v>415</v> + </b> + <b> + <a>10</a> + <b>16</b> + <v>391</v> + </b> + <b> + <a>16</a> + <b>25</b> + <v>340</v> + </b> + <b> + <a>25</a> + <b>36</b> + <v>245</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end_line</src> + <trg>start_column</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>27</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>419</v> + </b> + <b> + <a>3</a> + <b>6</b> + <v>340</v> + </b> + <b> + <a>6</a> + <b>14</b> + <v>340</v> + </b> + <b> + <a>14</a> + <b>21</b> + <v>351</v> + </b> + <b> + <a>21</a> + <b>36</b> + <v>351</v> + </b> + <b> + <a>36</a> + <b>47</b> + <v>357</v> + </b> + <b> + <a>47</a> + <b>57</b> + <v>361</v> + </b> + <b> + <a>57</a> + <b>68</b> + <v>347</v> + </b> + <b> + <a>68</a> + <b>77</b> + <v>347</v> + </b> + <b> + <a>77</a> + <b>89</b> + <v>368</v> + </b> + <b> + <a>89</a> + <b>106</b> + <v>340</v> + </b> + <b> + <a>106</a> + <b>137</b> + <v>344</v> + </b> + <b> + <a>138</a> + <b>220</b> + <v>228</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end_line</src> + <trg>end_column</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>371</v> + </b> + <b> + <a>2</a> + <b>5</b> + <v>149</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>354</v> + </b> + <b> + <a>8</a> + <b>16</b> + <v>391</v> + </b> + <b> + <a>16</a> + <b>26</b> + <v>351</v> + </b> + <b> + <a>26</a> + <b>41</b> + <v>361</v> + </b> + <b> + <a>41</a> + <b>52</b> + <v>368</v> + </b> + <b> + <a>52</a> + <b>63</b> + <v>368</v> + </b> + <b> + <a>63</a> + <b>74</b> + <v>351</v> + </b> + <b> + <a>74</a> + <b>84</b> + <v>354</v> + </b> + <b> + <a>84</a> + <b>98</b> + <v>361</v> + </b> + <b> + <a>98</a> + <b>122</b> + <v>368</v> + </b> + <b> + <a>122</a> + <b>189</b> + <v>344</v> + </b> + <b> + <a>191</a> + <b>225</b> + <v>30</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end_column</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>57</v> + </b> + <b> + <a>2</a> + <b>5</b> + <v>85</v> + </b> + <b> + <a>5</a> + <b>10</b> + <v>85</v> + </b> + <b> + <a>10</a> + <b>22</b> + <v>81</v> + </b> + <b> + <a>22</a> + <b>33</b> + <v>81</v> + </b> + <b> + <a>33</a> + <b>64</b> + <v>85</v> + </b> + <b> + <a>64</a> + <b>130</b> + <v>81</v> + </b> + <b> + <a>131</a> + <b>339</b> + <v>81</v> + </b> + <b> + <a>356</a> + <b>1222</b> + <v>81</v> + </b> + <b> + <a>1283</a> + <b>2975</b> + <v>81</v> + </b> + <b> + <a>3087</a> + <b>6873</b> + <v>81</v> + </b> + <b> + <a>7069</a> + <b>10622</b> + <v>81</v> + </b> + <b> + <a>10676</a> + <b>12015</b> + <v>81</v> + </b> + <b> + <a>12205</a> + <b>18687</b> + <v>20</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end_column</src> + <trg>file</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>109</v> + </b> + <b> + <a>2</a> + <b>4</b> + <v>78</v> + </b> + <b> + <a>4</a> + <b>8</b> + <v>85</v> + </b> + <b> + <a>8</a> + <b>15</b> + <v>85</v> + </b> + <b> + <a>15</a> + <b>25</b> + <v>85</v> + </b> + <b> + <a>25</a> + <b>42</b> + <v>81</v> + </b> + <b> + <a>42</a> + <b>90</b> + <v>81</v> + </b> + <b> + <a>92</a> + <b>222</b> + <v>81</v> + </b> + <b> + <a>235</a> + <b>503</b> + <v>81</v> + </b> + <b> + <a>558</a> + <b>952</b> + <v>81</v> + </b> + <b> + <a>959</a> + <b>1224</b> + <v>81</v> + </b> + <b> + <a>1233</a> + <b>1313</b> + <v>81</v> + </b> + <b> + <a>1314</a> + <b>1577</b> + <v>54</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end_column</src> + <trg>start_line</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>98</v> + </b> + <b> + <a>2</a> + <b>4</b> + <v>81</v> + </b> + <b> + <a>4</a> + <b>8</b> + <v>92</v> + </b> + <b> + <a>8</a> + <b>16</b> + <v>85</v> + </b> + <b> + <a>16</a> + <b>26</b> + <v>85</v> + </b> + <b> + <a>26</a> + <b>46</b> + <v>88</v> + </b> + <b> + <a>46</a> + <b>92</b> + <v>85</v> + </b> + <b> + <a>94</a> + <b>194</b> + <v>81</v> + </b> + <b> + <a>204</a> + <b>364</b> + <v>81</v> + </b> + <b> + <a>384</a> + <b>582</b> + <v>81</v> + </b> + <b> + <a>586</a> + <b>789</b> + <v>81</v> + </b> + <b> + <a>789</a> + <b>827</b> + <v>81</v> + </b> + <b> + <a>827</a> + <b>949</b> + <v>44</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end_column</src> + <trg>start_column</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>64</v> + </b> + <b> + <a>2</a> + <b>5</b> + <v>98</v> + </b> + <b> + <a>5</a> + <b>9</b> + <v>92</v> + </b> + <b> + <a>9</a> + <b>14</b> + <v>88</v> + </b> + <b> + <a>14</a> + <b>23</b> + <v>95</v> + </b> + <b> + <a>23</a> + <b>38</b> + <v>85</v> + </b> + <b> + <a>38</a> + <b>47</b> + <v>81</v> + </b> + <b> + <a>47</a> + <b>56</b> + <v>88</v> + </b> + <b> + <a>57</a> + <b>68</b> + <v>81</v> + </b> + <b> + <a>69</a> + <b>80</b> + <v>81</v> + </b> + <b> + <a>80</a> + <b>91</b> + <v>88</v> + </b> + <b> + <a>91</a> + <b>100</b> + <v>81</v> + </b> + <b> + <a>100</a> + <b>111</b> + <v>40</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end_column</src> + <trg>end_line</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>102</v> + </b> + <b> + <a>2</a> + <b>4</b> + <v>81</v> + </b> + <b> + <a>4</a> + <b>8</b> + <v>88</v> + </b> + <b> + <a>8</a> + <b>16</b> + <v>88</v> + </b> + <b> + <a>16</a> + <b>26</b> + <v>85</v> + </b> + <b> + <a>26</a> + <b>46</b> + <v>85</v> + </b> + <b> + <a>46</a> + <b>91</b> + <v>85</v> + </b> + <b> + <a>92</a> + <b>205</b> + <v>81</v> + </b> + <b> + <a>206</a> + <b>363</b> + <v>81</v> + </b> + <b> + <a>381</a> + <b>569</b> + <v>81</v> + </b> + <b> + <a>581</a> + <b>789</b> + <v>81</v> + </b> + <b> + <a>789</a> + <b>827</b> + <v>81</v> + </b> + <b> + <a>827</a> + <b>906</b> + <v>44</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>method_child</name> + <cardinality>81847</cardinality> + <columnsizes> + <e> + <k>method</k> + <v>30153</v> + </e> + <e> + <k>index</k> + <v>76</v> + </e> + <e> + <k>child</k> + <v>81847</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>method</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13610</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>5547</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>3895</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>2405</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>2533</v> + </b> + <b> + <a>7</a> + <b>77</b> + <v>2163</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>method</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13610</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>5547</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>3895</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>2405</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>2533</v> + </b> + <b> + <a>7</a> + <b>77</b> + <v>2163</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>method</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>7</v> + </b> + <b> + <a>2</a> + <b>4</b> + <v>2</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>9</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>10</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>7</v> + </b> + <b> + <a>7</a> + <b>12</b> + <v>5</v> + </b> + <b> + <a>13</a> + <b>19</b> + <v>6</v> + </b> + <b> + <a>20</a> + <b>36</b> + <v>6</v> + </b> + <b> + <a>43</a> + <b>114</b> + <v>6</v> + </b> + <b> + <a>148</a> + <b>401</b> + <v>6</v> + </b> + <b> + <a>501</a> + <b>2164</b> + <v>6</v> + </b> + <b> + <a>3155</a> + <b>30154</b> + <v>6</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>7</v> + </b> + <b> + <a>2</a> + <b>4</b> + <v>2</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>9</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>10</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>7</v> + </b> + <b> + <a>7</a> + <b>12</b> + <v>5</v> + </b> + <b> + <a>13</a> + <b>19</b> + <v>6</v> + </b> + <b> + <a>20</a> + <b>36</b> + <v>6</v> + </b> + <b> + <a>43</a> + <b>114</b> + <v>6</v> + </b> + <b> + <a>148</a> + <b>401</b> + <v>6</v> + </b> + <b> + <a>501</a> + <b>2164</b> + <v>6</v> + </b> + <b> + <a>3155</a> + <b>30154</b> + <v>6</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>method</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>81847</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>81847</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>method_def</name> + <cardinality>30455</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>30455</v> + </e> + <e> + <k>name</k> + <v>30455</v> + </e> + <e> + <k>loc</k> + <v>30455</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>30455</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>30455</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>30455</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>30455</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>30455</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>30455</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>method_parameters</name> + <cardinality>8396</cardinality> + <columnsizes> + <e> + <k>method</k> + <v>8396</v> + </e> + <e> + <k>parameters</k> + <v>8396</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>method</src> + <trg>parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8396</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parameters</src> + <trg>method</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8396</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>method_parameters_child</name> + <cardinality>14612</cardinality> + <columnsizes> + <e> + <k>method_parameters</k> + <v>8834</v> + </e> + <e> + <k>index</k> + <v>11</v> + </e> + <e> + <k>child</k> + <v>14612</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>method_parameters</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5307</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>2176</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>851</v> + </b> + <b> + <a>4</a> + <b>12</b> + <v>500</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>method_parameters</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5307</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>2176</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>851</v> + </b> + <b> + <a>4</a> + <b>12</b> + <v>500</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>method_parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>3</a> + <b>4</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>25</a> + <b>26</b> + <v>1</v> + </b> + <b> + <a>44</a> + <b>45</b> + <v>1</v> + </b> + <b> + <a>96</a> + <b>97</b> + <v>1</v> + </b> + <b> + <a>218</a> + <b>219</b> + <v>1</v> + </b> + <b> + <a>500</a> + <b>501</b> + <v>1</v> + </b> + <b> + <a>1351</a> + <b>1352</b> + <v>1</v> + </b> + <b> + <a>3527</a> + <b>3528</b> + <v>1</v> + </b> + <b> + <a>8834</a> + <b>8835</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>3</a> + <b>4</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>25</a> + <b>26</b> + <v>1</v> + </b> + <b> + <a>44</a> + <b>45</b> + <v>1</v> + </b> + <b> + <a>96</a> + <b>97</b> + <v>1</v> + </b> + <b> + <a>218</a> + <b>219</b> + <v>1</v> + </b> + <b> + <a>500</a> + <b>501</b> + <v>1</v> + </b> + <b> + <a>1351</a> + <b>1352</b> + <v>1</v> + </b> + <b> + <a>3527</a> + <b>3528</b> + <v>1</v> + </b> + <b> + <a>8834</a> + <b>8835</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>method_parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14612</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14612</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>method_parameters_def</name> + <cardinality>8920</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>8920</v> + </e> + <e> + <k>loc</k> + <v>8920</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8920</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8920</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>module_child</name> + <cardinality>9655</cardinality> + <columnsizes> + <e> + <k>module</k> + <v>3290</v> + </e> + <e> + <k>index</k> + <v>119</v> + </e> + <e> + <k>child</k> + <v>9655</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>module</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2339</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>275</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>237</v> + </b> + <b> + <a>5</a> + <b>11</b> + <v>259</v> + </b> + <b> + <a>11</a> + <b>120</b> + <v>180</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>module</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2339</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>275</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>237</v> + </b> + <b> + <a>5</a> + <b>11</b> + <v>259</v> + </b> + <b> + <a>11</a> + <b>120</b> + <v>180</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>module</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>9</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>25</v> + </b> + <b> + <a>4</a> + <b>6</b> + <v>11</v> + </b> + <b> + <a>6</a> + <b>9</b> + <v>9</v> + </b> + <b> + <a>9</a> + <b>14</b> + <v>9</v> + </b> + <b> + <a>15</a> + <b>23</b> + <v>10</v> + </b> + <b> + <a>23</a> + <b>35</b> + <v>9</v> + </b> + <b> + <a>35</a> + <b>73</b> + <v>9</v> + </b> + <b> + <a>79</a> + <b>166</b> + <v>9</v> + </b> + <b> + <a>180</a> + <b>677</b> + <v>9</v> + </b> + <b> + <a>951</a> + <b>3291</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>8</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>9</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>25</v> + </b> + <b> + <a>4</a> + <b>6</b> + <v>11</v> + </b> + <b> + <a>6</a> + <b>9</b> + <v>9</v> + </b> + <b> + <a>9</a> + <b>14</b> + <v>9</v> + </b> + <b> + <a>15</a> + <b>23</b> + <v>10</v> + </b> + <b> + <a>23</a> + <b>35</b> + <v>9</v> + </b> + <b> + <a>35</a> + <b>73</b> + <v>9</v> + </b> + <b> + <a>79</a> + <b>166</b> + <v>9</v> + </b> + <b> + <a>180</a> + <b>677</b> + <v>9</v> + </b> + <b> + <a>951</a> + <b>3291</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>module</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9655</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9655</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>module_def</name> + <cardinality>4441</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>4441</v> + </e> + <e> + <k>name</k> + <v>4441</v> + </e> + <e> + <k>loc</k> + <v>4441</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4441</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4441</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4441</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4441</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4441</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4441</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>next_child</name> + <cardinality>16</cardinality> + <columnsizes> + <e> + <k>next</k> + <v>16</v> + </e> + <e> + <k>child</k> + <v>16</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>next</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>next</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>next_def</name> + <cardinality>653</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>653</v> + </e> + <e> + <k>loc</k> + <v>653</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>653</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>653</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>numlines</name> + <cardinality>0</cardinality> + <columnsizes> + <e> + <k>element_id</k> + <v>0</v> + </e> + <e> + <k>num_lines</k> + <v>0</v> + </e> + <e> + <k>num_code</k> + <v>0</v> + </e> + <e> + <k>num_comment</k> + <v>0</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>element_id</src> + <trg>num_lines</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>element_id</src> + <trg>num_code</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>element_id</src> + <trg>num_comment</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>num_lines</src> + <trg>element_id</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>num_lines</src> + <trg>num_code</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>num_lines</src> + <trg>num_comment</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>num_code</src> + <trg>element_id</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>num_code</src> + <trg>num_lines</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>num_code</src> + <trg>num_comment</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>num_comment</src> + <trg>element_id</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>num_comment</src> + <trg>num_lines</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + <dep> + <src>num_comment</src> + <trg>num_code</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>operator_assignment_def</name> + <cardinality>2002</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>2002</v> + </e> + <e> + <k>left</k> + <v>2002</v> + </e> + <e> + <k>operator</k> + <v>6</v> + </e> + <e> + <k>right</k> + <v>2002</v> + </e> + <e> + <k>loc</k> + <v>2002</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2002</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2002</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2002</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2002</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2002</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2002</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2002</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>left</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2002</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>61</a> + <b>62</b> + <v>1</v> + </b> + <b> + <a>495</a> + <b>496</b> + <v>1</v> + </b> + <b> + <a>1385</a> + <b>1386</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>61</a> + <b>62</b> + <v>1</v> + </b> + <b> + <a>495</a> + <b>496</b> + <v>1</v> + </b> + <b> + <a>1385</a> + <b>1386</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>61</a> + <b>62</b> + <v>1</v> + </b> + <b> + <a>495</a> + <b>496</b> + <v>1</v> + </b> + <b> + <a>1385</a> + <b>1386</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>1</v> + </b> + <b> + <a>61</a> + <b>62</b> + <v>1</v> + </b> + <b> + <a>495</a> + <b>496</b> + <v>1</v> + </b> + <b> + <a>1385</a> + <b>1386</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2002</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2002</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2002</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2002</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2002</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>left</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2002</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2002</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>right</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2002</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>optional_parameter_def</name> + <cardinality>2047</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>2047</v> + </e> + <e> + <k>name</k> + <v>2047</v> + </e> + <e> + <k>value</k> + <v>2047</v> + </e> + <e> + <k>loc</k> + <v>2047</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2047</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2047</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2047</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2047</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2047</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2047</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2047</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2047</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2047</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2047</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2047</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2047</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>pair_def</name> + <cardinality>62127</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>62127</v> + </e> + <e> + <k>key__</k> + <v>62127</v> + </e> + <e> + <k>value</k> + <v>62127</v> + </e> + <e> + <k>loc</k> + <v>62127</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>key__</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>62127</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>62127</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>62127</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>key__</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>62127</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>key__</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>62127</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>key__</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>62127</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>62127</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>key__</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>62127</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>62127</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>62127</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>key__</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>62127</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>62127</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>parenthesized_statements_child</name> + <cardinality>1653</cardinality> + <columnsizes> + <e> + <k>parenthesized_statements</k> + <v>1652</v> + </e> + <e> + <k>index</k> + <v>2</v> + </e> + <e> + <k>child</k> + <v>1653</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>parenthesized_statements</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1651</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parenthesized_statements</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1651</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>parenthesized_statements</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>1614</a> + <b>1615</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>1614</a> + <b>1615</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>parenthesized_statements</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1653</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1653</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>parenthesized_statements_def</name> + <cardinality>1652</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1652</v> + </e> + <e> + <k>loc</k> + <v>1652</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1652</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1652</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>pattern_def</name> + <cardinality>1186</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1186</v> + </e> + <e> + <k>child</k> + <v>1186</v> + </e> + <e> + <k>loc</k> + <v>1186</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1186</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1186</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1186</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1186</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1186</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1186</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>program_child</name> + <cardinality>14591</cardinality> + <columnsizes> + <e> + <k>program</k> + <v>6251</v> + </e> + <e> + <k>index</k> + <v>139</v> + </e> + <e> + <k>child</k> + <v>14591</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>program</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3289</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1625</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>504</v> + </b> + <b> + <a>4</a> + <b>7</b> + <v>484</v> + </b> + <b> + <a>7</a> + <b>42</b> + <v>347</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>program</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3289</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1625</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>504</v> + </b> + <b> + <a>4</a> + <b>7</b> + <v>484</v> + </b> + <b> + <a>7</a> + <b>42</b> + <v>347</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>program</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>30</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>6</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>20</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>10</v> + </b> + <b> + <a>8</a> + <b>12</b> + <v>10</v> + </b> + <b> + <a>12</a> + <b>20</b> + <v>10</v> + </b> + <b> + <a>20</a> + <b>34</b> + <v>10</v> + </b> + <b> + <a>41</a> + <b>58</b> + <v>10</v> + </b> + <b> + <a>76</a> + <b>103</b> + <v>10</v> + </b> + <b> + <a>135</a> + <b>245</b> + <v>10</v> + </b> + <b> + <a>392</a> + <b>1835</b> + <v>10</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>30</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>6</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>20</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>10</v> + </b> + <b> + <a>8</a> + <b>12</b> + <v>10</v> + </b> + <b> + <a>12</a> + <b>20</b> + <v>10</v> + </b> + <b> + <a>20</a> + <b>34</b> + <v>10</v> + </b> + <b> + <a>41</a> + <b>58</b> + <v>10</v> + </b> + <b> + <a>76</a> + <b>103</b> + <v>10</v> + </b> + <b> + <a>135</a> + <b>245</b> + <v>10</v> + </b> + <b> + <a>392</a> + <b>1835</b> + <v>10</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>program</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14591</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14591</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>program_def</name> + <cardinality>6322</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>6322</v> + </e> + <e> + <k>loc</k> + <v>6322</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6322</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6322</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>range_begin</name> + <cardinality>539</cardinality> + <columnsizes> + <e> + <k>range</k> + <v>539</v> + </e> + <e> + <k>begin</k> + <v>539</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>range</src> + <trg>begin</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>539</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>begin</src> + <trg>range</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>539</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>range_def</name> + <cardinality>546</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>546</v> + </e> + <e> + <k>operator</k> + <v>2</v> + </e> + <e> + <k>loc</k> + <v>546</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>546</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>546</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>122</a> + <b>123</b> + <v>1</v> + </b> + <b> + <a>424</a> + <b>425</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>122</a> + <b>123</b> + <v>1</v> + </b> + <b> + <a>424</a> + <b>425</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>546</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>546</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>range_end</name> + <cardinality>463</cardinality> + <columnsizes> + <e> + <k>range</k> + <v>463</v> + </e> + <e> + <k>end</k> + <v>463</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>range</src> + <trg>end</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>463</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>end</src> + <trg>range</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>463</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>rational_def</name> + <cardinality>2</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>2</v> + </e> + <e> + <k>child</k> + <v>2</v> + </e> + <e> + <k>loc</k> + <v>2</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>redo_child</name> + <cardinality>0</cardinality> + <columnsizes> + <e> + <k>redo</k> + <v>0</v> + </e> + <e> + <k>child</k> + <v>0</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>redo</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>redo</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>redo_def</name> + <cardinality>0</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>0</v> + </e> + <e> + <k>loc</k> + <v>0</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs/> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>regex_child</name> + <cardinality>13433</cardinality> + <columnsizes> + <e> + <k>regex</k> + <v>3974</v> + </e> + <e> + <k>index</k> + <v>43</v> + </e> + <e> + <k>child</k> + <v>13433</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>regex</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2038</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>220</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>522</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>152</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>337</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>317</v> + </b> + <b> + <a>8</a> + <b>16</b> + <v>313</v> + </b> + <b> + <a>16</a> + <b>44</b> + <v>75</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>regex</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2038</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>220</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>522</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>152</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>337</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>317</v> + </b> + <b> + <a>8</a> + <b>16</b> + <v>313</v> + </b> + <b> + <a>16</a> + <b>44</b> + <v>75</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>regex</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>4</v> + </b> + <b> + <a>4</a> + <b>7</b> + <v>3</v> + </b> + <b> + <a>7</a> + <b>11</b> + <v>3</v> + </b> + <b> + <a>12</a> + <b>17</b> + <v>3</v> + </b> + <b> + <a>17</a> + <b>18</b> + <v>2</v> + </b> + <b> + <a>20</a> + <b>22</b> + <v>3</v> + </b> + <b> + <a>23</a> + <b>26</b> + <v>2</v> + </b> + <b> + <a>26</a> + <b>32</b> + <v>3</v> + </b> + <b> + <a>33</a> + <b>41</b> + <v>3</v> + </b> + <b> + <a>58</a> + <b>94</b> + <v>3</v> + </b> + <b> + <a>104</a> + <b>163</b> + <v>3</v> + </b> + <b> + <a>221</a> + <b>328</b> + <v>3</v> + </b> + <b> + <a>388</a> + <b>706</b> + <v>3</v> + </b> + <b> + <a>1042</a> + <b>1717</b> + <v>3</v> + </b> + <b> + <a>1936</a> + <b>3975</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>4</v> + </b> + <b> + <a>4</a> + <b>7</b> + <v>3</v> + </b> + <b> + <a>7</a> + <b>11</b> + <v>3</v> + </b> + <b> + <a>12</a> + <b>17</b> + <v>3</v> + </b> + <b> + <a>17</a> + <b>18</b> + <v>2</v> + </b> + <b> + <a>20</a> + <b>22</b> + <v>3</v> + </b> + <b> + <a>23</a> + <b>26</b> + <v>2</v> + </b> + <b> + <a>26</a> + <b>32</b> + <v>3</v> + </b> + <b> + <a>33</a> + <b>41</b> + <v>3</v> + </b> + <b> + <a>58</a> + <b>94</b> + <v>3</v> + </b> + <b> + <a>104</a> + <b>163</b> + <v>3</v> + </b> + <b> + <a>221</a> + <b>328</b> + <v>3</v> + </b> + <b> + <a>388</a> + <b>706</b> + <v>3</v> + </b> + <b> + <a>1042</a> + <b>1717</b> + <v>3</v> + </b> + <b> + <a>1936</a> + <b>3975</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>regex</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13433</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13433</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>regex_def</name> + <cardinality>3979</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>3979</v> + </e> + <e> + <k>loc</k> + <v>3979</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3979</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3979</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>rescue_body</name> + <cardinality>539</cardinality> + <columnsizes> + <e> + <k>rescue</k> + <v>539</v> + </e> + <e> + <k>body</k> + <v>539</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>rescue</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>539</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>rescue</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>539</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>rescue_def</name> + <cardinality>634</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>634</v> + </e> + <e> + <k>loc</k> + <v>634</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>634</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>634</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>rescue_exceptions</name> + <cardinality>429</cardinality> + <columnsizes> + <e> + <k>rescue</k> + <v>429</v> + </e> + <e> + <k>exceptions</k> + <v>429</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>rescue</src> + <trg>exceptions</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>429</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>exceptions</src> + <trg>rescue</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>429</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>rescue_modifier_def</name> + <cardinality>174</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>174</v> + </e> + <e> + <k>body</k> + <v>174</v> + </e> + <e> + <k>handler</k> + <v>174</v> + </e> + <e> + <k>loc</k> + <v>174</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>174</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>handler</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>174</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>174</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>174</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>handler</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>174</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>174</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>handler</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>174</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>handler</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>174</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>handler</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>174</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>174</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>174</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>handler</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>174</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>rescue_variable</name> + <cardinality>309</cardinality> + <columnsizes> + <e> + <k>rescue</k> + <v>309</v> + </e> + <e> + <k>variable</k> + <v>309</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>rescue</src> + <trg>variable</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>309</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>variable</src> + <trg>rescue</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>309</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>rest_assignment_child</name> + <cardinality>7</cardinality> + <columnsizes> + <e> + <k>rest_assignment</k> + <v>7</v> + </e> + <e> + <k>child</k> + <v>7</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>rest_assignment</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>7</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>rest_assignment</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>7</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>rest_assignment_def</name> + <cardinality>18</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>18</v> + </e> + <e> + <k>loc</k> + <v>18</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>18</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>18</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>retry_child</name> + <cardinality>0</cardinality> + <columnsizes> + <e> + <k>retry</k> + <v>0</v> + </e> + <e> + <k>child</k> + <v>0</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>retry</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>retry</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>retry_def</name> + <cardinality>9</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>9</v> + </e> + <e> + <k>loc</k> + <v>9</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>return_child</name> + <cardinality>1611</cardinality> + <columnsizes> + <e> + <k>return</k> + <v>1611</v> + </e> + <e> + <k>child</k> + <v>1611</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>return</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1611</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>return</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1611</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>return_def</name> + <cardinality>2601</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>2601</v> + </e> + <e> + <k>loc</k> + <v>2601</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2601</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2601</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>right_assignment_list_child</name> + <cardinality>880</cardinality> + <columnsizes> + <e> + <k>right_assignment_list</k> + <v>414</v> + </e> + <e> + <k>index</k> + <v>5</v> + </e> + <e> + <k>child</k> + <v>880</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>right_assignment_list</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>374</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>38</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>right_assignment_list</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>374</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>38</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>right_assignment_list</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + <b> + <a>10</a> + <b>11</b> + <v>1</v> + </b> + <b> + <a>40</a> + <b>41</b> + <v>1</v> + </b> + <b> + <a>414</a> + <b>415</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>1</v> + </b> + <b> + <a>10</a> + <b>11</b> + <v>1</v> + </b> + <b> + <a>40</a> + <b>41</b> + <v>1</v> + </b> + <b> + <a>414</a> + <b>415</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>right_assignment_list</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>880</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>880</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>right_assignment_list_def</name> + <cardinality>414</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>414</v> + </e> + <e> + <k>loc</k> + <v>414</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>414</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>414</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>scope_resolution_def</name> + <cardinality>22918</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>22918</v> + </e> + <e> + <k>name</k> + <v>22918</v> + </e> + <e> + <k>loc</k> + <v>22918</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>22918</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>22918</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>22918</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>22918</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>22918</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>22918</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>scope_resolution_scope</name> + <cardinality>22255</cardinality> + <columnsizes> + <e> + <k>scope_resolution</k> + <v>22255</v> + </e> + <e> + <k>scope</k> + <v>22255</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>scope_resolution</src> + <trg>scope</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>22255</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>scope</src> + <trg>scope_resolution</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>22255</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>setter_def</name> + <cardinality>186</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>186</v> + </e> + <e> + <k>name</k> + <v>186</v> + </e> + <e> + <k>loc</k> + <v>186</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>186</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>186</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>186</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>186</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>186</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>186</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>singleton_class_child</name> + <cardinality>731</cardinality> + <columnsizes> + <e> + <k>singleton_class</k> + <v>191</v> + </e> + <e> + <k>index</k> + <v>24</v> + </e> + <e> + <k>child</k> + <v>731</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>singleton_class</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>89</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>21</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>12</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>15</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>11</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>17</v> + </b> + <b> + <a>8</a> + <b>13</b> + <v>14</v> + </b> + <b> + <a>13</a> + <b>25</b> + <v>12</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>singleton_class</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>89</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>21</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>12</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>15</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>11</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>17</v> + </b> + <b> + <a>8</a> + <b>13</b> + <v>14</v> + </b> + <b> + <a>13</a> + <b>25</b> + <v>12</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>singleton_class</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>3</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>2</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>2</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>2</v> + </b> + <b> + <a>12</a> + <b>18</b> + <v>2</v> + </b> + <b> + <a>18</a> + <b>20</b> + <v>2</v> + </b> + <b> + <a>22</a> + <b>27</b> + <v>2</v> + </b> + <b> + <a>33</a> + <b>44</b> + <v>2</v> + </b> + <b> + <a>54</a> + <b>70</b> + <v>2</v> + </b> + <b> + <a>81</a> + <b>103</b> + <v>2</v> + </b> + <b> + <a>191</a> + <b>192</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>3</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>2</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>2</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>2</v> + </b> + <b> + <a>12</a> + <b>18</b> + <v>2</v> + </b> + <b> + <a>18</a> + <b>20</b> + <v>2</v> + </b> + <b> + <a>22</a> + <b>27</b> + <v>2</v> + </b> + <b> + <a>33</a> + <b>44</b> + <v>2</v> + </b> + <b> + <a>54</a> + <b>70</b> + <v>2</v> + </b> + <b> + <a>81</a> + <b>103</b> + <v>2</v> + </b> + <b> + <a>191</a> + <b>192</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>singleton_class</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>731</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>731</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>singleton_class_def</name> + <cardinality>191</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>191</v> + </e> + <e> + <k>value</k> + <v>191</v> + </e> + <e> + <k>loc</k> + <v>191</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>191</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>191</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>191</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>191</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>191</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>191</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>singleton_method_child</name> + <cardinality>5021</cardinality> + <columnsizes> + <e> + <k>singleton_method</k> + <v>2020</v> + </e> + <e> + <k>index</k> + <v>27</v> + </e> + <e> + <k>child</k> + <v>5021</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>singleton_method</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1132</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>308</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>180</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>126</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>159</v> + </b> + <b> + <a>8</a> + <b>28</b> + <v>113</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>singleton_method</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1132</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>308</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>180</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>126</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>159</v> + </b> + <b> + <a>8</a> + <b>28</b> + <v>113</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>singleton_method</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>3</a> + <b>4</b> + <v>2</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>2</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>4</v> + </b> + <b> + <a>7</a> + <b>9</b> + <v>2</v> + </b> + <b> + <a>11</a> + <b>16</b> + <v>2</v> + </b> + <b> + <a>22</a> + <b>27</b> + <v>2</v> + </b> + <b> + <a>30</a> + <b>37</b> + <v>2</v> + </b> + <b> + <a>48</a> + <b>64</b> + <v>2</v> + </b> + <b> + <a>87</a> + <b>112</b> + <v>2</v> + </b> + <b> + <a>142</a> + <b>195</b> + <v>2</v> + </b> + <b> + <a>267</a> + <b>392</b> + <v>2</v> + </b> + <b> + <a>567</a> + <b>869</b> + <v>2</v> + </b> + <b> + <a>1974</a> + <b>1975</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>3</a> + <b>4</b> + <v>2</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>2</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>4</v> + </b> + <b> + <a>7</a> + <b>9</b> + <v>2</v> + </b> + <b> + <a>11</a> + <b>16</b> + <v>2</v> + </b> + <b> + <a>22</a> + <b>27</b> + <v>2</v> + </b> + <b> + <a>30</a> + <b>37</b> + <v>2</v> + </b> + <b> + <a>48</a> + <b>64</b> + <v>2</v> + </b> + <b> + <a>87</a> + <b>112</b> + <v>2</v> + </b> + <b> + <a>142</a> + <b>195</b> + <v>2</v> + </b> + <b> + <a>267</a> + <b>392</b> + <v>2</v> + </b> + <b> + <a>567</a> + <b>869</b> + <v>2</v> + </b> + <b> + <a>1974</a> + <b>1975</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>singleton_method</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5021</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5021</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>singleton_method_def</name> + <cardinality>2020</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>2020</v> + </e> + <e> + <k>name</k> + <v>2020</v> + </e> + <e> + <k>object</k> + <v>2020</v> + </e> + <e> + <k>loc</k> + <v>2020</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2020</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>object</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2020</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2020</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2020</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>object</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2020</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2020</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>object</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2020</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>object</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2020</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>object</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2020</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2020</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2020</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>object</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2020</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>singleton_method_parameters</name> + <cardinality>1272</cardinality> + <columnsizes> + <e> + <k>singleton_method</k> + <v>1272</v> + </e> + <e> + <k>parameters</k> + <v>1272</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>singleton_method</src> + <trg>parameters</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1272</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>parameters</src> + <trg>singleton_method</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1272</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>sourceLocationPrefix</name> + <cardinality>3</cardinality> + <columnsizes> + <e> + <k>prefix</k> + <v>3</v> + </e> + </columnsizes> + <dependencies/> + </relation> + <relation> + <name>splat_argument_def</name> + <cardinality>683</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>683</v> + </e> + <e> + <k>child</k> + <v>683</v> + </e> + <e> + <k>loc</k> + <v>683</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>683</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>683</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>683</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>683</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>683</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>683</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>splat_parameter_def</name> + <cardinality>921</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>921</v> + </e> + <e> + <k>loc</k> + <v>921</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>921</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>921</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>splat_parameter_name</name> + <cardinality>748</cardinality> + <columnsizes> + <e> + <k>splat_parameter</k> + <v>748</v> + </e> + <e> + <k>name</k> + <v>748</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>splat_parameter</src> + <trg>name</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>748</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>name</src> + <trg>splat_parameter</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>748</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>string_array_child</name> + <cardinality>3009</cardinality> + <columnsizes> + <e> + <k>string_array</k> + <v>932</v> + </e> + <e> + <k>index</k> + <v>88</v> + </e> + <e> + <k>child</k> + <v>3009</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>string_array</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>200</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>300</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>241</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>66</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>71</v> + </b> + <b> + <a>8</a> + <b>89</b> + <v>54</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>string_array</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>200</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>300</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>241</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>66</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>71</v> + </b> + <b> + <a>8</a> + <b>89</b> + <v>54</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>string_array</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>38</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>6</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>6</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>12</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>8</v> + </b> + <b> + <a>11</a> + <b>29</b> + <v>7</v> + </b> + <b> + <a>33</a> + <b>126</b> + <v>7</v> + </b> + <b> + <a>191</a> + <b>933</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>38</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>6</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>6</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>12</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>8</v> + </b> + <b> + <a>11</a> + <b>29</b> + <v>7</v> + </b> + <b> + <a>33</a> + <b>126</b> + <v>7</v> + </b> + <b> + <a>191</a> + <b>933</b> + <v>4</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>string_array</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3009</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>3009</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>string_array_def</name> + <cardinality>938</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>938</v> + </e> + <e> + <k>loc</k> + <v>938</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>938</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>938</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>string_child</name> + <cardinality>124227</cardinality> + <columnsizes> + <e> + <k>string__</k> + <v>90152</v> + </e> + <e> + <k>index</k> + <v>124</v> + </e> + <e> + <k>child</k> + <v>124227</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>string__</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>83280</v> + </b> + <b> + <a>2</a> + <b>63</b> + <v>6791</v> + </b> + <b> + <a>64</a> + <b>125</b> + <v>81</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>string__</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>83280</v> + </b> + <b> + <a>2</a> + <b>63</b> + <v>6791</v> + </b> + <b> + <a>64</a> + <b>125</b> + <v>81</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>string__</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>19</b> + <v>4</v> + </b> + <b> + <a>61</a> + <b>62</b> + <v>13</v> + </b> + <b> + <a>62</a> + <b>63</b> + <v>37</v> + </b> + <b> + <a>64</a> + <b>82</b> + <v>8</v> + </b> + <b> + <a>142</a> + <b>144</b> + <v>10</v> + </b> + <b> + <a>144</a> + <b>190</b> + <v>10</v> + </b> + <b> + <a>190</a> + <b>206</b> + <v>10</v> + </b> + <b> + <a>209</a> + <b>352</b> + <v>10</v> + </b> + <b> + <a>381</a> + <b>465</b> + <v>10</v> + </b> + <b> + <a>470</a> + <b>3443</b> + <v>10</v> + </b> + <b> + <a>6872</a> + <b>90153</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>19</b> + <v>4</v> + </b> + <b> + <a>61</a> + <b>62</b> + <v>13</v> + </b> + <b> + <a>62</a> + <b>63</b> + <v>37</v> + </b> + <b> + <a>64</a> + <b>82</b> + <v>8</v> + </b> + <b> + <a>142</a> + <b>144</b> + <v>10</v> + </b> + <b> + <a>144</a> + <b>190</b> + <v>10</v> + </b> + <b> + <a>190</a> + <b>206</b> + <v>10</v> + </b> + <b> + <a>209</a> + <b>352</b> + <v>10</v> + </b> + <b> + <a>381</a> + <b>465</b> + <v>10</v> + </b> + <b> + <a>470</a> + <b>3443</b> + <v>10</v> + </b> + <b> + <a>6872</a> + <b>90153</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>string__</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>124227</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>124227</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>string_def</name> + <cardinality>91253</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>91253</v> + </e> + <e> + <k>loc</k> + <v>91253</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>91253</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>91253</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>subshell_child</name> + <cardinality>214</cardinality> + <columnsizes> + <e> + <k>subshell</k> + <v>83</v> + </e> + <e> + <k>index</k> + <v>8</v> + </e> + <e> + <k>child</k> + <v>214</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>subshell</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>29</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>9</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>30</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>6</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>7</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>subshell</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>29</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>9</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>30</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>6</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>7</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>subshell</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>1</v> + </b> + <b> + <a>14</a> + <b>15</b> + <v>1</v> + </b> + <b> + <a>44</a> + <b>45</b> + <v>1</v> + </b> + <b> + <a>53</a> + <b>54</b> + <v>1</v> + </b> + <b> + <a>82</a> + <b>83</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>1</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>1</v> + </b> + <b> + <a>14</a> + <b>15</b> + <v>1</v> + </b> + <b> + <a>44</a> + <b>45</b> + <v>1</v> + </b> + <b> + <a>53</a> + <b>54</b> + <v>1</v> + </b> + <b> + <a>82</a> + <b>83</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>subshell</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>214</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>214</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>subshell_def</name> + <cardinality>130</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>130</v> + </e> + <e> + <k>loc</k> + <v>130</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>130</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>130</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>superclass_def</name> + <cardinality>4108</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>4108</v> + </e> + <e> + <k>child</k> + <v>4108</v> + </e> + <e> + <k>loc</k> + <v>4108</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4108</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4108</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4108</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4108</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4108</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4108</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>symbol_array_child</name> + <cardinality>687</cardinality> + <columnsizes> + <e> + <k>symbol_array</k> + <v>139</v> + </e> + <e> + <k>index</k> + <v>32</v> + </e> + <e> + <k>child</k> + <v>687</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>symbol_array</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>50</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>25</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>13</v> + </b> + <b> + <a>4</a> + <b>6</b> + <v>8</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>7</v> + </b> + <b> + <a>7</a> + <b>10</b> + <v>12</v> + </b> + <b> + <a>10</a> + <b>16</b> + <v>12</v> + </b> + <b> + <a>16</a> + <b>33</b> + <v>10</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>symbol_array</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>50</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>25</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>13</v> + </b> + <b> + <a>4</a> + <b>6</b> + <v>8</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>7</v> + </b> + <b> + <a>7</a> + <b>10</b> + <v>12</v> + </b> + <b> + <a>10</a> + <b>16</b> + <v>12</v> + </b> + <b> + <a>16</a> + <b>33</b> + <v>10</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>symbol_array</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>3</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>3</v> + </b> + <b> + <a>4</a> + <b>6</b> + <v>2</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>2</v> + </b> + <b> + <a>9</a> + <b>11</b> + <v>2</v> + </b> + <b> + <a>13</a> + <b>17</b> + <v>2</v> + </b> + <b> + <a>17</a> + <b>20</b> + <v>2</v> + </b> + <b> + <a>21</a> + <b>23</b> + <v>2</v> + </b> + <b> + <a>25</a> + <b>29</b> + <v>2</v> + </b> + <b> + <a>34</a> + <b>42</b> + <v>2</v> + </b> + <b> + <a>42</a> + <b>50</b> + <v>2</v> + </b> + <b> + <a>62</a> + <b>88</b> + <v>2</v> + </b> + <b> + <a>136</a> + <b>137</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>5</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>3</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>3</v> + </b> + <b> + <a>4</a> + <b>6</b> + <v>2</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>2</v> + </b> + <b> + <a>9</a> + <b>11</b> + <v>2</v> + </b> + <b> + <a>13</a> + <b>17</b> + <v>2</v> + </b> + <b> + <a>17</a> + <b>20</b> + <v>2</v> + </b> + <b> + <a>21</a> + <b>23</b> + <v>2</v> + </b> + <b> + <a>25</a> + <b>29</b> + <v>2</v> + </b> + <b> + <a>34</a> + <b>42</b> + <v>2</v> + </b> + <b> + <a>42</a> + <b>50</b> + <v>2</v> + </b> + <b> + <a>62</a> + <b>88</b> + <v>2</v> + </b> + <b> + <a>136</a> + <b>137</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>symbol_array</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>687</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>687</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>symbol_array_def</name> + <cardinality>139</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>139</v> + </e> + <e> + <k>loc</k> + <v>139</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>139</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>139</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>then_child</name> + <cardinality>12972</cardinality> + <columnsizes> + <e> + <k>then</k> + <v>7609</v> + </e> + <e> + <k>index</k> + <v>35</v> + </e> + <e> + <k>child</k> + <v>12972</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>then</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4705</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1734</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>641</v> + </b> + <b> + <a>4</a> + <b>36</b> + <v>528</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>then</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>4705</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>1734</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>641</v> + </b> + <b> + <a>4</a> + <b>36</b> + <v>528</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>then</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>2</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>4</v> + </b> + <b> + <a>7</a> + <b>10</b> + <v>3</v> + </b> + <b> + <a>10</a> + <b>26</b> + <v>3</v> + </b> + <b> + <a>42</a> + <b>86</b> + <v>3</v> + </b> + <b> + <a>152</a> + <b>517</b> + <v>3</v> + </b> + <b> + <a>1143</a> + <b>7434</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>14</v> + </b> + <b> + <a>3</a> + <b>5</b> + <v>2</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>4</v> + </b> + <b> + <a>7</a> + <b>10</b> + <v>3</v> + </b> + <b> + <a>10</a> + <b>26</b> + <v>3</v> + </b> + <b> + <a>42</a> + <b>86</b> + <v>3</v> + </b> + <b> + <a>152</a> + <b>517</b> + <v>3</v> + </b> + <b> + <a>1143</a> + <b>7434</b> + <v>3</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>then</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>12972</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>12972</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>then_def</name> + <cardinality>7609</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>7609</v> + </e> + <e> + <k>loc</k> + <v>7609</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>7609</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>7609</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>tokeninfo</name> + <cardinality>1812992</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1812992</v> + </e> + <e> + <k>kind</k> + <v>23</v> + </e> + <e> + <k>file</k> + <v>3633</v> + </e> + <e> + <k>idx</k> + <v>30516</v> + </e> + <e> + <k>value</k> + <v>81342</v> + </e> + <e> + <k>loc</k> + <v>1812967</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>kind</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1812992</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>file</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1812992</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>idx</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1812992</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1812992</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1812992</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>kind</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>25</b> + <v>2</v> + </b> + <b> + <a>106</a> + <b>182</b> + <v>2</v> + </b> + <b> + <a>431</a> + <b>1506</b> + <v>2</v> + </b> + <b> + <a>1573</a> + <b>1574</b> + <v>2</v> + </b> + <b> + <a>3578</a> + <b>3605</b> + <v>2</v> + </b> + <b> + <a>3720</a> + <b>5057</b> + <v>2</v> + </b> + <b> + <a>7139</a> + <b>8328</b> + <v>2</v> + </b> + <b> + <a>12622</a> + <b>15383</b> + <v>2</v> + </b> + <b> + <a>21595</a> + <b>49328</b> + <v>2</v> + </b> + <b> + <a>49585</a> + <b>70852</b> + <v>2</v> + </b> + <b> + <a>82879</a> + <b>445442</b> + <v>2</v> + </b> + <b> + <a>986482</a> + <b>986483</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>kind</src> + <trg>file</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>24</b> + <v>2</v> + </b> + <b> + <a>24</a> + <b>81</b> + <v>2</v> + </b> + <b> + <a>127</a> + <b>128</b> + <v>1</v> + </b> + <b> + <a>448</a> + <b>449</b> + <v>3</v> + </b> + <b> + <a>486</a> + <b>502</b> + <v>2</v> + </b> + <b> + <a>794</a> + <b>904</b> + <v>2</v> + </b> + <b> + <a>1254</a> + <b>1305</b> + <v>2</v> + </b> + <b> + <a>1347</a> + <b>1610</b> + <v>2</v> + </b> + <b> + <a>2282</a> + <b>2363</b> + <v>2</v> + </b> + <b> + <a>2879</a> + <b>3394</b> + <v>2</v> + </b> + <b> + <a>3498</a> + <b>3531</b> + <v>2</v> + </b> + <b> + <a>3538</a> + <b>3539</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>kind</src> + <trg>idx</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>25</b> + <v>2</v> + </b> + <b> + <a>99</a> + <b>136</b> + <v>2</v> + </b> + <b> + <a>331</a> + <b>422</b> + <v>2</v> + </b> + <b> + <a>1015</a> + <b>1027</b> + <v>2</v> + </b> + <b> + <a>1775</a> + <b>2075</b> + <v>2</v> + </b> + <b> + <a>2128</a> + <b>2308</b> + <v>2</v> + </b> + <b> + <a>3338</a> + <b>3381</b> + <v>2</v> + </b> + <b> + <a>3467</a> + <b>4192</b> + <v>2</v> + </b> + <b> + <a>6552</a> + <b>8526</b> + <v>2</v> + </b> + <b> + <a>9771</a> + <b>9941</b> + <v>2</v> + </b> + <b> + <a>11957</a> + <b>21770</b> + <v>2</v> + </b> + <b> + <a>26505</a> + <b>26506</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>kind</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6</v> + </b> + <b> + <a>5</a> + <b>32</b> + <v>2</v> + </b> + <b> + <a>42</a> + <b>50</b> + <v>2</v> + </b> + <b> + <a>52</a> + <b>53</b> + <v>1</v> + </b> + <b> + <a>118</a> + <b>119</b> + <v>2</v> + </b> + <b> + <a>136</a> + <b>510</b> + <v>2</v> + </b> + <b> + <a>1614</a> + <b>2649</b> + <v>2</v> + </b> + <b> + <a>3380</a> + <b>4151</b> + <v>2</v> + </b> + <b> + <a>7019</a> + <b>8929</b> + <v>2</v> + </b> + <b> + <a>17094</a> + <b>38702</b> + <v>2</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>kind</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>25</b> + <v>2</v> + </b> + <b> + <a>106</a> + <b>182</b> + <v>2</v> + </b> + <b> + <a>431</a> + <b>1506</b> + <v>2</v> + </b> + <b> + <a>1573</a> + <b>1574</b> + <v>2</v> + </b> + <b> + <a>3578</a> + <b>3605</b> + <v>2</v> + </b> + <b> + <a>3720</a> + <b>5057</b> + <v>2</v> + </b> + <b> + <a>7139</a> + <b>8328</b> + <v>2</v> + </b> + <b> + <a>12622</a> + <b>15383</b> + <v>2</v> + </b> + <b> + <a>21595</a> + <b>49328</b> + <v>2</v> + </b> + <b> + <a>49585</a> + <b>70852</b> + <v>2</v> + </b> + <b> + <a>82879</a> + <b>445442</b> + <v>2</v> + </b> + <b> + <a>986482</a> + <b>986483</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>file</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>21</b> + <v>327</v> + </b> + <b> + <a>21</a> + <b>28</b> + <v>294</v> + </b> + <b> + <a>28</a> + <b>34</b> + <v>294</v> + </b> + <b> + <a>34</a> + <b>47</b> + <v>279</v> + </b> + <b> + <a>47</a> + <b>63</b> + <v>277</v> + </b> + <b> + <a>63</a> + <b>85</b> + <v>277</v> + </b> + <b> + <a>85</a> + <b>128</b> + <v>275</v> + </b> + <b> + <a>128</a> + <b>190</b> + <v>273</v> + </b> + <b> + <a>190</a> + <b>287</b> + <v>274</v> + </b> + <b> + <a>287</a> + <b>477</b> + <v>273</v> + </b> + <b> + <a>477</a> + <b>824</b> + <v>273</v> + </b> + <b> + <a>830</a> + <b>1793</b> + <v>273</v> + </b> + <b> + <a>1795</a> + <b>29810</b> + <v>238</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>file</src> + <trg>kind</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>6</b> + <v>300</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>600</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>340</v> + </b> + <b> + <a>8</a> + <b>9</b> + <v>560</v> + </b> + <b> + <a>9</a> + <b>10</b> + <v>552</v> + </b> + <b> + <a>10</a> + <b>11</b> + <v>387</v> + </b> + <b> + <a>11</a> + <b>12</b> + <v>304</v> + </b> + <b> + <a>12</a> + <b>14</b> + <v>327</v> + </b> + <b> + <a>14</a> + <b>22</b> + <v>256</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>file</src> + <trg>idx</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>21</b> + <v>327</v> + </b> + <b> + <a>21</a> + <b>28</b> + <v>294</v> + </b> + <b> + <a>28</a> + <b>34</b> + <v>294</v> + </b> + <b> + <a>34</a> + <b>47</b> + <v>279</v> + </b> + <b> + <a>47</a> + <b>63</b> + <v>277</v> + </b> + <b> + <a>63</a> + <b>85</b> + <v>277</v> + </b> + <b> + <a>85</a> + <b>128</b> + <v>275</v> + </b> + <b> + <a>128</a> + <b>190</b> + <v>273</v> + </b> + <b> + <a>190</a> + <b>287</b> + <v>274</v> + </b> + <b> + <a>287</a> + <b>477</b> + <v>273</v> + </b> + <b> + <a>477</a> + <b>824</b> + <v>273</v> + </b> + <b> + <a>830</a> + <b>1793</b> + <v>273</v> + </b> + <b> + <a>1795</a> + <b>29810</b> + <v>238</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>file</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>18</b> + <v>259</v> + </b> + <b> + <a>18</a> + <b>21</b> + <v>318</v> + </b> + <b> + <a>21</a> + <b>24</b> + <v>309</v> + </b> + <b> + <a>24</a> + <b>29</b> + <v>322</v> + </b> + <b> + <a>29</a> + <b>35</b> + <v>303</v> + </b> + <b> + <a>35</a> + <b>42</b> + <v>294</v> + </b> + <b> + <a>42</a> + <b>53</b> + <v>287</v> + </b> + <b> + <a>53</a> + <b>67</b> + <v>284</v> + </b> + <b> + <a>67</a> + <b>86</b> + <v>279</v> + </b> + <b> + <a>86</a> + <b>121</b> + <v>277</v> + </b> + <b> + <a>121</a> + <b>175</b> + <v>274</v> + </b> + <b> + <a>175</a> + <b>328</b> + <v>273</v> + </b> + <b> + <a>328</a> + <b>1615</b> + <v>149</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>file</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>21</b> + <v>327</v> + </b> + <b> + <a>21</a> + <b>28</b> + <v>294</v> + </b> + <b> + <a>28</a> + <b>34</b> + <v>294</v> + </b> + <b> + <a>34</a> + <b>47</b> + <v>279</v> + </b> + <b> + <a>47</a> + <b>63</b> + <v>277</v> + </b> + <b> + <a>63</a> + <b>85</b> + <v>277</v> + </b> + <b> + <a>85</a> + <b>128</b> + <v>275</v> + </b> + <b> + <a>128</a> + <b>190</b> + <v>273</v> + </b> + <b> + <a>190</a> + <b>287</b> + <v>274</v> + </b> + <b> + <a>287</a> + <b>477</b> + <v>273</v> + </b> + <b> + <a>477</a> + <b>824</b> + <v>273</v> + </b> + <b> + <a>830</a> + <b>1793</b> + <v>273</v> + </b> + <b> + <a>1795</a> + <b>29810</b> + <v>238</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>idx</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6221</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>281</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>6071</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>2334</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>2773</v> + </b> + <b> + <a>8</a> + <b>11</b> + <v>2410</v> + </b> + <b> + <a>11</a> + <b>22</b> + <v>2394</v> + </b> + <b> + <a>22</a> + <b>42</b> + <v>2451</v> + </b> + <b> + <a>42</a> + <b>108</b> + <v>2292</v> + </b> + <b> + <a>108</a> + <b>438</b> + <v>2289</v> + </b> + <b> + <a>439</a> + <b>3550</b> + <v>996</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>idx</src> + <trg>kind</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>7969</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>6564</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>4657</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>2483</v> + </b> + <b> + <a>5</a> + <b>6</b> + <v>1748</v> + </b> + <b> + <a>6</a> + <b>8</b> + <v>2280</v> + </b> + <b> + <a>8</a> + <b>12</b> + <v>2760</v> + </b> + <b> + <a>12</a> + <b>22</b> + <v>2050</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>idx</src> + <trg>file</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6221</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>281</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>6071</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>2334</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>2773</v> + </b> + <b> + <a>8</a> + <b>11</b> + <v>2410</v> + </b> + <b> + <a>11</a> + <b>22</b> + <v>2394</v> + </b> + <b> + <a>22</a> + <b>42</b> + <v>2451</v> + </b> + <b> + <a>42</a> + <b>108</b> + <v>2292</v> + </b> + <b> + <a>108</a> + <b>438</b> + <v>2289</v> + </b> + <b> + <a>439</a> + <b>3550</b> + <v>996</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>idx</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6252</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>904</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>5907</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>2078</v> + </b> + <b> + <a>5</a> + <b>7</b> + <v>2404</v> + </b> + <b> + <a>7</a> + <b>10</b> + <v>2623</v> + </b> + <b> + <a>10</a> + <b>18</b> + <v>2586</v> + </b> + <b> + <a>18</a> + <b>31</b> + <v>2381</v> + </b> + <b> + <a>31</a> + <b>65</b> + <v>2313</v> + </b> + <b> + <a>65</a> + <b>215</b> + <v>2293</v> + </b> + <b> + <a>215</a> + <b>1962</b> + <v>768</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>idx</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>6221</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>281</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>6071</v> + </b> + <b> + <a>4</a> + <b>5</b> + <v>2334</v> + </b> + <b> + <a>5</a> + <b>8</b> + <v>2773</v> + </b> + <b> + <a>8</a> + <b>11</b> + <v>2410</v> + </b> + <b> + <a>11</a> + <b>22</b> + <v>2394</v> + </b> + <b> + <a>22</a> + <b>42</b> + <v>2451</v> + </b> + <b> + <a>42</a> + <b>108</b> + <v>2292</v> + </b> + <b> + <a>108</a> + <b>438</b> + <v>2289</v> + </b> + <b> + <a>439</a> + <b>3550</b> + <v>996</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>47906</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>11908</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>5748</v> + </b> + <b> + <a>4</a> + <b>7</b> + <v>6839</v> + </b> + <b> + <a>7</a> + <b>25</b> + <v>6164</v> + </b> + <b> + <a>25</a> + <b>163812</b> + <v>2775</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>kind</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>77153</v> + </b> + <b> + <a>2</a> + <b>5</b> + <v>4189</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>file</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>62308</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>8151</v> + </b> + <b> + <a>3</a> + <b>6</b> + <v>6181</v> + </b> + <b> + <a>6</a> + <b>3447</b> + <v>4700</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>idx</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>48028</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>11906</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>5746</v> + </b> + <b> + <a>4</a> + <b>7</b> + <v>6815</v> + </b> + <b> + <a>7</a> + <b>25</b> + <v>6150</v> + </b> + <b> + <a>25</a> + <b>15624</b> + <v>2694</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>value</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>47907</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>11907</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>5748</v> + </b> + <b> + <a>4</a> + <b>7</b> + <v>6839</v> + </b> + <b> + <a>7</a> + <b>25</b> + <v>6165</v> + </b> + <b> + <a>25</a> + <b>163812</b> + <v>2774</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1812942</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>24</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>kind</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1812942</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>24</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>file</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1812967</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>idx</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1812942</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>24</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>value</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1812967</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>unary_def</name> + <cardinality>2445</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>2445</v> + </e> + <e> + <k>operand</k> + <v>2445</v> + </e> + <e> + <k>operator</k> + <v>5</v> + </e> + <e> + <k>loc</k> + <v>2445</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>operand</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2445</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2445</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2445</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operand</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2445</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operand</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2445</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operand</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2445</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>10</a> + <b>11</b> + <v>1</v> + </b> + <b> + <a>60</a> + <b>61</b> + <v>1</v> + </b> + <b> + <a>142</a> + <b>143</b> + <v>1</v> + </b> + <b> + <a>533</a> + <b>534</b> + <v>1</v> + </b> + <b> + <a>1644</a> + <b>1645</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>operand</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>10</a> + <b>11</b> + <v>1</v> + </b> + <b> + <a>60</a> + <b>61</b> + <v>1</v> + </b> + <b> + <a>142</a> + <b>143</b> + <v>1</v> + </b> + <b> + <a>533</a> + <b>534</b> + <v>1</v> + </b> + <b> + <a>1644</a> + <b>1645</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>operator</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>10</a> + <b>11</b> + <v>1</v> + </b> + <b> + <a>60</a> + <b>61</b> + <v>1</v> + </b> + <b> + <a>142</a> + <b>143</b> + <v>1</v> + </b> + <b> + <a>533</a> + <b>534</b> + <v>1</v> + </b> + <b> + <a>1644</a> + <b>1645</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2445</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>operand</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2445</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>operator</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>2445</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>undef_child</name> + <cardinality>13</cardinality> + <columnsizes> + <e> + <k>undef</k> + <v>13</v> + </e> + <e> + <k>index</k> + <v>1</v> + </e> + <e> + <k>child</k> + <v>13</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>undef</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>undef</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>undef</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>13</a> + <b>14</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>13</a> + <b>14</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>undef</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>undef_def</name> + <cardinality>13</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>13</v> + </e> + <e> + <k>loc</k> + <v>13</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>unless_alternative</name> + <cardinality>11</cardinality> + <columnsizes> + <e> + <k>unless</k> + <v>11</v> + </e> + <e> + <k>alternative</k> + <v>11</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>unless</src> + <trg>alternative</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>11</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>alternative</src> + <trg>unless</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>11</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>unless_consequence</name> + <cardinality>494</cardinality> + <columnsizes> + <e> + <k>unless</k> + <v>494</v> + </e> + <e> + <k>consequence</k> + <v>494</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>unless</src> + <trg>consequence</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>494</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>consequence</src> + <trg>unless</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>494</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>unless_def</name> + <cardinality>497</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>497</v> + </e> + <e> + <k>condition</k> + <v>497</v> + </e> + <e> + <k>loc</k> + <v>497</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>497</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>497</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>497</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>497</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>497</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>497</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>unless_modifier_def</name> + <cardinality>1404</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>1404</v> + </e> + <e> + <k>body</k> + <v>1404</v> + </e> + <e> + <k>condition</k> + <v>1404</v> + </e> + <e> + <k>loc</k> + <v>1404</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1404</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1404</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1404</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1404</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1404</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1404</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1404</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1404</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1404</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1404</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1404</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1404</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>until_def</name> + <cardinality>16</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>16</v> + </e> + <e> + <k>body</k> + <v>16</v> + </e> + <e> + <k>condition</k> + <v>16</v> + </e> + <e> + <k>loc</k> + <v>16</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>16</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>until_modifier_def</name> + <cardinality>13</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>13</v> + </e> + <e> + <k>body</k> + <v>13</v> + </e> + <e> + <k>condition</k> + <v>13</v> + </e> + <e> + <k>loc</k> + <v>13</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>13</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>when_body</name> + <cardinality>980</cardinality> + <columnsizes> + <e> + <k>when</k> + <v>980</v> + </e> + <e> + <k>body</k> + <v>980</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>when</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>980</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>when</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>980</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>when_def</name> + <cardinality>987</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>987</v> + </e> + <e> + <k>loc</k> + <v>987</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>987</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>987</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>when_pattern</name> + <cardinality>1186</cardinality> + <columnsizes> + <e> + <k>when</k> + <v>987</v> + </e> + <e> + <k>index</k> + <v>14</v> + </e> + <e> + <k>pattern</k> + <v>1186</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>when</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>863</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>94</v> + </b> + <b> + <a>3</a> + <b>15</b> + <v>30</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>when</src> + <trg>pattern</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>863</v> + </b> + <b> + <a>2</a> + <b>3</b> + <v>94</v> + </b> + <b> + <a>3</a> + <b>15</b> + <v>30</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>when</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>4</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>4</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>1</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>1</v> + </b> + <b> + <a>12</a> + <b>13</b> + <v>1</v> + </b> + <b> + <a>30</a> + <b>31</b> + <v>1</v> + </b> + <b> + <a>124</a> + <b>125</b> + <v>1</v> + </b> + <b> + <a>987</a> + <b>988</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>index</src> + <trg>pattern</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>2</a> + <b>3</b> + <v>4</v> + </b> + <b> + <a>3</a> + <b>4</b> + <v>4</v> + </b> + <b> + <a>6</a> + <b>7</b> + <v>1</v> + </b> + <b> + <a>7</a> + <b>8</b> + <v>1</v> + </b> + <b> + <a>12</a> + <b>13</b> + <v>1</v> + </b> + <b> + <a>30</a> + <b>31</b> + <v>1</v> + </b> + <b> + <a>124</a> + <b>125</b> + <v>1</v> + </b> + <b> + <a>987</a> + <b>988</b> + <v>1</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>pattern</src> + <trg>when</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1186</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>pattern</src> + <trg>index</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>1186</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>while_def</name> + <cardinality>106</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>106</v> + </e> + <e> + <k>body</k> + <v>106</v> + </e> + <e> + <k>condition</k> + <v>106</v> + </e> + <e> + <k>loc</k> + <v>106</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>106</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>106</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>106</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>106</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>106</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>106</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>106</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>106</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>106</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>106</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>106</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>106</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>while_modifier_def</name> + <cardinality>9</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>9</v> + </e> + <e> + <k>body</k> + <v>9</v> + </e> + <e> + <k>condition</k> + <v>9</v> + </e> + <e> + <k>loc</k> + <v>9</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>body</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>condition</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>body</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>condition</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>9</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>yield_child</name> + <cardinality>371</cardinality> + <columnsizes> + <e> + <k>yield</k> + <v>371</v> + </e> + <e> + <k>child</k> + <v>371</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>yield</src> + <trg>child</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>371</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>child</src> + <trg>yield</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>371</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + <relation> + <name>yield_def</name> + <cardinality>841</cardinality> + <columnsizes> + <e> + <k>id</k> + <v>841</v> + </e> + <e> + <k>loc</k> + <v>841</v> + </e> + </columnsizes> + <dependencies> + <dep> + <src>id</src> + <trg>loc</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>841</v> + </b> + </bs> + </hist> + </val> + </dep> + <dep> + <src>loc</src> + <trg>id</trg> + <val> + <hist> + <budget>12</budget> + <bs> + <b> + <a>1</a> + <b>2</b> + <v>841</v> + </b> + </bs> + </hist> + </val> + </dep> + </dependencies> + </relation> + </stats> +</dbstats> diff --git a/ruby/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/upgrade.properties b/ruby/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/upgrade.properties new file mode 100644 index 00000000000..2c4e8cc41e8 --- /dev/null +++ b/ruby/ql/lib/upgrades/40be81bc2086eb0368f33c770e0a84817bb340c3/upgrade.properties @@ -0,0 +1,165 @@ +description: Add ERB tables and rename Ruby tables +compatibility: backwards +ruby_alias_def.rel: reorder alias_def.rel ( int id, int alias, int name, int loc ) id alias name loc +ruby_argument_list_child.rel: reorder argument_list_child.rel ( int ruby_argument_list, int index, int child ) ruby_argument_list index child +ruby_argument_list_def.rel: reorder argument_list_def.rel ( int id, int loc ) id loc +ruby_array_child.rel: reorder array_child.rel ( int ruby_array, int index, int child ) ruby_array index child +ruby_array_def.rel: reorder array_def.rel ( int id, int loc ) id loc +ruby_assignment_def.rel: reorder assignment_def.rel ( int id, int left, int right, int loc ) id left right loc +ruby_bare_string_child.rel: reorder bare_string_child.rel ( int ruby_bare_string, int index, int child ) ruby_bare_string index child +ruby_bare_string_def.rel: reorder bare_string_def.rel ( int id, int loc ) id loc +ruby_bare_symbol_child.rel: reorder bare_symbol_child.rel ( int ruby_bare_symbol, int index, int child ) ruby_bare_symbol index child +ruby_bare_symbol_def.rel: reorder bare_symbol_def.rel ( int id, int loc ) id loc +ruby_begin_child.rel: reorder begin_child.rel ( int ruby_begin, int index, int child ) ruby_begin index child +ruby_begin_def.rel: reorder begin_def.rel ( int id, int loc ) id loc +ruby_begin_block_child.rel: reorder begin_block_child.rel ( int ruby_begin_block, int index, int child ) ruby_begin_block index child +ruby_begin_block_def.rel: reorder begin_block_def.rel ( int id, int loc ) id loc +ruby_binary_def.rel: reorder binary_def.rel ( int id, int left, int operator, int right, int loc ) id left operator right loc +ruby_block_parameters.rel: reorder block_parameters.rel ( int ruby_block, int parameters ) ruby_block parameters +ruby_block_child.rel: reorder block_child.rel ( int ruby_block, int index, int child ) ruby_block index child +ruby_block_def.rel: reorder block_def.rel ( int id, int loc ) id loc +ruby_block_argument_def.rel: reorder block_argument_def.rel ( int id, int child, int loc ) id child loc +ruby_block_parameter_def.rel: reorder block_parameter_def.rel ( int id, int name, int loc ) id name loc +ruby_block_parameters_child.rel: reorder block_parameters_child.rel ( int ruby_block_parameters, int index, int child ) ruby_block_parameters index child +ruby_block_parameters_def.rel: reorder block_parameters_def.rel ( int id, int loc ) id loc +ruby_break_child.rel: reorder break_child.rel ( int ruby_break, int child ) ruby_break child +ruby_break_def.rel: reorder break_def.rel ( int id, int loc ) id loc +ruby_call_arguments.rel: reorder call_arguments.rel ( int ruby_call, int arguments ) ruby_call arguments +ruby_call_block.rel: reorder call_block.rel ( int ruby_call, int block ) ruby_call block +ruby_call_receiver.rel: reorder call_receiver.rel ( int ruby_call, int receiver ) ruby_call receiver +ruby_call_def.rel: reorder call_def.rel ( int id, int method, int loc ) id method loc +ruby_case_value.rel: reorder case_value.rel ( int ruby_case__, int value ) ruby_case__ value +ruby_case_child.rel: reorder case_child.rel ( int ruby_case__, int index, int child ) ruby_case__ index child +ruby_case_def.rel: reorder case_def.rel ( int id, int loc ) id loc +ruby_chained_string_child.rel: reorder chained_string_child.rel ( int ruby_chained_string, int index, int child ) ruby_chained_string index child +ruby_chained_string_def.rel: reorder chained_string_def.rel ( int id, int loc ) id loc +ruby_class_superclass.rel: reorder class_superclass.rel ( int ruby_class, int superclass ) ruby_class superclass +ruby_class_child.rel: reorder class_child.rel ( int ruby_class, int index, int child ) ruby_class index child +ruby_class_def.rel: reorder class_def.rel ( int id, int name, int loc ) id name loc +ruby_conditional_def.rel: reorder conditional_def.rel ( int id, int alternative, int condition, int consequence, int loc ) id alternative condition consequence loc +ruby_delimited_symbol_child.rel: reorder delimited_symbol_child.rel ( int ruby_delimited_symbol, int index, int child ) ruby_delimited_symbol index child +ruby_delimited_symbol_def.rel: reorder delimited_symbol_def.rel ( int id, int loc ) id loc +ruby_destructured_left_assignment_child.rel: reorder destructured_left_assignment_child.rel ( int ruby_destructured_left_assignment, int index, int child ) ruby_destructured_left_assignment index child +ruby_destructured_left_assignment_def.rel: reorder destructured_left_assignment_def.rel ( int id, int loc ) id loc +ruby_destructured_parameter_child.rel: reorder destructured_parameter_child.rel ( int ruby_destructured_parameter, int index, int child ) ruby_destructured_parameter index child +ruby_destructured_parameter_def.rel: reorder destructured_parameter_def.rel ( int id, int loc ) id loc +ruby_do_child.rel: reorder do_child.rel ( int ruby_do, int index, int child ) ruby_do index child +ruby_do_def.rel: reorder do_def.rel ( int id, int loc ) id loc +ruby_do_block_parameters.rel: reorder do_block_parameters.rel ( int ruby_do_block, int parameters ) ruby_do_block parameters +ruby_do_block_child.rel: reorder do_block_child.rel ( int ruby_do_block, int index, int child ) ruby_do_block index child +ruby_do_block_def.rel: reorder do_block_def.rel ( int id, int loc ) id loc +ruby_element_reference_child.rel: reorder element_reference_child.rel ( int ruby_element_reference, int index, int child ) ruby_element_reference index child +ruby_element_reference_def.rel: reorder element_reference_def.rel ( int id, int object, int loc ) id object loc +ruby_else_child.rel: reorder else_child.rel ( int ruby_else, int index, int child ) ruby_else index child +ruby_else_def.rel: reorder else_def.rel ( int id, int loc ) id loc +ruby_elsif_alternative.rel: reorder elsif_alternative.rel ( int ruby_elsif, int alternative ) ruby_elsif alternative +ruby_elsif_consequence.rel: reorder elsif_consequence.rel ( int ruby_elsif, int consequence ) ruby_elsif consequence +ruby_elsif_def.rel: reorder elsif_def.rel ( int id, int condition, int loc ) id condition loc +ruby_end_block_child.rel: reorder end_block_child.rel ( int ruby_end_block, int index, int child ) ruby_end_block index child +ruby_end_block_def.rel: reorder end_block_def.rel ( int id, int loc ) id loc +ruby_ensure_child.rel: reorder ensure_child.rel ( int ruby_ensure, int index, int child ) ruby_ensure index child +ruby_ensure_def.rel: reorder ensure_def.rel ( int id, int loc ) id loc +ruby_exception_variable_def.rel: reorder exception_variable_def.rel ( int id, int child, int loc ) id child loc +ruby_exceptions_child.rel: reorder exceptions_child.rel ( int ruby_exceptions, int index, int child ) ruby_exceptions index child +ruby_exceptions_def.rel: reorder exceptions_def.rel ( int id, int loc ) id loc +ruby_for_def.rel: reorder for_def.rel ( int id, int body, int pattern, int value, int loc ) id body pattern value loc +ruby_hash_child.rel: reorder hash_child.rel ( int ruby_hash, int index, int child ) ruby_hash index child +ruby_hash_def.rel: reorder hash_def.rel ( int id, int loc ) id loc +ruby_hash_splat_argument_def.rel: reorder hash_splat_argument_def.rel ( int id, int child, int loc ) id child loc +ruby_hash_splat_parameter_name.rel: reorder hash_splat_parameter_name.rel ( int ruby_hash_splat_parameter, int name ) ruby_hash_splat_parameter name +ruby_hash_splat_parameter_def.rel: reorder hash_splat_parameter_def.rel ( int id, int loc ) id loc +ruby_heredoc_body_child.rel: reorder heredoc_body_child.rel ( int ruby_heredoc_body, int index, int child ) ruby_heredoc_body index child +ruby_heredoc_body_def.rel: reorder heredoc_body_def.rel ( int id, int loc ) id loc +ruby_if_alternative.rel: reorder if_alternative.rel ( int ruby_if, int alternative ) ruby_if alternative +ruby_if_consequence.rel: reorder if_consequence.rel ( int ruby_if, int consequence ) ruby_if consequence +ruby_if_def.rel: reorder if_def.rel ( int id, int condition, int loc ) id condition loc +ruby_if_modifier_def.rel: reorder if_modifier_def.rel ( int id, int body, int condition, int loc ) id body condition loc +ruby_in_def.rel: reorder in_def.rel ( int id, int child, int loc ) id child loc +ruby_interpolation_child.rel: reorder interpolation_child.rel ( int ruby_interpolation, int index, int child ) ruby_interpolation index child +ruby_interpolation_def.rel: reorder interpolation_def.rel ( int id, int loc ) id loc +ruby_keyword_parameter_value.rel: reorder keyword_parameter_value.rel ( int ruby_keyword_parameter, int value ) ruby_keyword_parameter value +ruby_keyword_parameter_def.rel: reorder keyword_parameter_def.rel ( int id, int name, int loc ) id name loc +ruby_lambda_parameters.rel: reorder lambda_parameters.rel ( int ruby_lambda, int parameters ) ruby_lambda parameters +ruby_lambda_def.rel: reorder lambda_def.rel ( int id, int body, int loc ) id body loc +ruby_lambda_parameters_child.rel: reorder lambda_parameters_child.rel ( int ruby_lambda_parameters, int index, int child ) ruby_lambda_parameters index child +ruby_lambda_parameters_def.rel: reorder lambda_parameters_def.rel ( int id, int loc ) id loc +ruby_left_assignment_list_child.rel: reorder left_assignment_list_child.rel ( int ruby_left_assignment_list, int index, int child ) ruby_left_assignment_list index child +ruby_left_assignment_list_def.rel: reorder left_assignment_list_def.rel ( int id, int loc ) id loc +ruby_method_parameters.rel: reorder method_parameters.rel ( int ruby_method, int parameters ) ruby_method parameters +ruby_method_child.rel: reorder method_child.rel ( int ruby_method, int index, int child ) ruby_method index child +ruby_method_def.rel: reorder method_def.rel ( int id, int name, int loc ) id name loc +ruby_method_parameters_child.rel: reorder method_parameters_child.rel ( int ruby_method_parameters, int index, int child ) ruby_method_parameters index child +ruby_method_parameters_def.rel: reorder method_parameters_def.rel ( int id, int loc ) id loc +ruby_module_child.rel: reorder module_child.rel ( int ruby_module, int index, int child ) ruby_module index child +ruby_module_def.rel: reorder module_def.rel ( int id, int name, int loc ) id name loc +ruby_next_child.rel: reorder next_child.rel ( int ruby_next, int child ) ruby_next child +ruby_next_def.rel: reorder next_def.rel ( int id, int loc ) id loc +ruby_operator_assignment_def.rel: reorder operator_assignment_def.rel ( int id, int left, int operator, int right, int loc ) id left operator right loc +ruby_optional_parameter_def.rel: reorder optional_parameter_def.rel ( int id, int name, int value, int loc ) id name value loc +ruby_pair_def.rel: reorder pair_def.rel ( int id, int key__, int value, int loc ) id key__ value loc +ruby_parenthesized_statements_child.rel: reorder parenthesized_statements_child.rel ( int ruby_parenthesized_statements, int index, int child ) ruby_parenthesized_statements index child +ruby_parenthesized_statements_def.rel: reorder parenthesized_statements_def.rel ( int id, int loc ) id loc +ruby_pattern_def.rel: reorder pattern_def.rel ( int id, int child, int loc ) id child loc +ruby_program_child.rel: reorder program_child.rel ( int ruby_program, int index, int child ) ruby_program index child +ruby_program_def.rel: reorder program_def.rel ( int id, int loc ) id loc +ruby_range_begin.rel: reorder range_begin.rel ( int ruby_range, int begin ) ruby_range begin +ruby_range_end.rel: reorder range_end.rel ( int ruby_range, int end ) ruby_range end +ruby_range_def.rel: reorder range_def.rel ( int id, int operator, int loc ) id operator loc +ruby_rational_def.rel: reorder rational_def.rel ( int id, int child, int loc ) id child loc +ruby_redo_child.rel: reorder redo_child.rel ( int ruby_redo, int child ) ruby_redo child +ruby_redo_def.rel: reorder redo_def.rel ( int id, int loc ) id loc +ruby_regex_child.rel: reorder regex_child.rel ( int ruby_regex, int index, int child ) ruby_regex index child +ruby_regex_def.rel: reorder regex_def.rel ( int id, int loc ) id loc +ruby_rescue_body.rel: reorder rescue_body.rel ( int ruby_rescue, int body ) ruby_rescue body +ruby_rescue_exceptions.rel: reorder rescue_exceptions.rel ( int ruby_rescue, int exceptions ) ruby_rescue exceptions +ruby_rescue_variable.rel: reorder rescue_variable.rel ( int ruby_rescue, int variable ) ruby_rescue variable +ruby_rescue_def.rel: reorder rescue_def.rel ( int id, int loc ) id loc +ruby_rescue_modifier_def.rel: reorder rescue_modifier_def.rel ( int id, int body, int handler, int loc ) id body handler loc +ruby_rest_assignment_child.rel: reorder rest_assignment_child.rel ( int ruby_rest_assignment, int child ) ruby_rest_assignment child +ruby_rest_assignment_def.rel: reorder rest_assignment_def.rel ( int id, int loc ) id loc +ruby_retry_child.rel: reorder retry_child.rel ( int ruby_retry, int child ) ruby_retry child +ruby_retry_def.rel: reorder retry_def.rel ( int id, int loc ) id loc +ruby_return_child.rel: reorder return_child.rel ( int ruby_return, int child ) ruby_return child +ruby_return_def.rel: reorder return_def.rel ( int id, int loc ) id loc +ruby_right_assignment_list_child.rel: reorder right_assignment_list_child.rel ( int ruby_right_assignment_list, int index, int child ) ruby_right_assignment_list index child +ruby_right_assignment_list_def.rel: reorder right_assignment_list_def.rel ( int id, int loc ) id loc +ruby_scope_resolution_scope.rel: reorder scope_resolution_scope.rel ( int ruby_scope_resolution, int scope ) ruby_scope_resolution scope +ruby_scope_resolution_def.rel: reorder scope_resolution_def.rel ( int id, int name, int loc ) id name loc +ruby_setter_def.rel: reorder setter_def.rel ( int id, int name, int loc ) id name loc +ruby_singleton_class_child.rel: reorder singleton_class_child.rel ( int ruby_singleton_class, int index, int child ) ruby_singleton_class index child +ruby_singleton_class_def.rel: reorder singleton_class_def.rel ( int id, int value, int loc ) id value loc +ruby_singleton_method_parameters.rel: reorder singleton_method_parameters.rel ( int ruby_singleton_method, int parameters ) ruby_singleton_method parameters +ruby_singleton_method_child.rel: reorder singleton_method_child.rel ( int ruby_singleton_method, int index, int child ) ruby_singleton_method index child +ruby_singleton_method_def.rel: reorder singleton_method_def.rel ( int id, int name, int object, int loc ) id name object loc +ruby_splat_argument_def.rel: reorder splat_argument_def.rel ( int id, int child, int loc ) id child loc +ruby_splat_parameter_name.rel: reorder splat_parameter_name.rel ( int ruby_splat_parameter, int name ) ruby_splat_parameter name +ruby_splat_parameter_def.rel: reorder splat_parameter_def.rel ( int id, int loc ) id loc +ruby_string_child.rel: reorder string_child.rel ( int ruby_string__, int index, int child ) ruby_string__ index child +ruby_string_def.rel: reorder string_def.rel ( int id, int loc ) id loc +ruby_string_array_child.rel: reorder string_array_child.rel ( int ruby_string_array, int index, int child ) ruby_string_array index child +ruby_string_array_def.rel: reorder string_array_def.rel ( int id, int loc ) id loc +ruby_subshell_child.rel: reorder subshell_child.rel ( int ruby_subshell, int index, int child ) ruby_subshell index child +ruby_subshell_def.rel: reorder subshell_def.rel ( int id, int loc ) id loc +ruby_superclass_def.rel: reorder superclass_def.rel ( int id, int child, int loc ) id child loc +ruby_symbol_array_child.rel: reorder symbol_array_child.rel ( int ruby_symbol_array, int index, int child ) ruby_symbol_array index child +ruby_symbol_array_def.rel: reorder symbol_array_def.rel ( int id, int loc ) id loc +ruby_then_child.rel: reorder then_child.rel ( int ruby_then, int index, int child ) ruby_then index child +ruby_then_def.rel: reorder then_def.rel ( int id, int loc ) id loc +ruby_unary_def.rel: reorder unary_def.rel ( int id, int operand, int operator, int loc ) id operand operator loc +ruby_undef_child.rel: reorder undef_child.rel ( int ruby_undef, int index, int child ) ruby_undef index child +ruby_undef_def.rel: reorder undef_def.rel ( int id, int loc ) id loc +ruby_unless_alternative.rel: reorder unless_alternative.rel ( int ruby_unless, int alternative ) ruby_unless alternative +ruby_unless_consequence.rel: reorder unless_consequence.rel ( int ruby_unless, int consequence ) ruby_unless consequence +ruby_unless_def.rel: reorder unless_def.rel ( int id, int condition, int loc ) id condition loc +ruby_unless_modifier_def.rel: reorder unless_modifier_def.rel ( int id, int body, int condition, int loc ) id body condition loc +ruby_until_def.rel: reorder until_def.rel ( int id, int body, int condition, int loc ) id body condition loc +ruby_until_modifier_def.rel: reorder until_modifier_def.rel ( int id, int body, int condition, int loc ) id body condition loc +ruby_when_body.rel: reorder when_body.rel ( int ruby_when, int body ) ruby_when body +ruby_when_pattern.rel: reorder when_pattern.rel ( int ruby_when, int index, int pattern ) ruby_when index pattern +ruby_when_def.rel: reorder when_def.rel ( int id, int loc ) id loc +ruby_while_def.rel: reorder while_def.rel ( int id, int body, int condition, int loc ) id body condition loc +ruby_while_modifier_def.rel: reorder while_modifier_def.rel ( int id, int body, int condition, int loc ) id body condition loc +ruby_yield_child.rel: reorder yield_child.rel ( int ruby_yield, int child ) ruby_yield child +ruby_yield_def.rel: reorder yield_def.rel ( int id, int loc ) id loc +ruby_tokeninfo.rel: reorder tokeninfo.rel ( int id, int kind, int file, int idx, string value, int loc ) id kind file idx value loc +ruby_ast_node_parent.rel: reorder ast_node_parent.rel ( int child, int parent, int parent_index) child parent parent_index diff --git a/ruby/ql/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/old.dbscheme b/ruby/ql/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/old.dbscheme new file mode 100644 index 00000000000..8725deeb2fa --- /dev/null +++ b/ruby/ql/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/old.dbscheme @@ -0,0 +1,1250 @@ +// CodeQL database schema for Ruby +// Automatically generated from the tree-sitter grammar; do not edit + +@location = @location_default + +locations_default( + unique int id: @location_default, + int file: @file ref, + int start_line: int ref, + int start_column: int ref, + int end_line: int ref, + int end_column: int ref +); + +@sourceline = @file + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref +); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +sourceLocationPrefix( + string prefix: string ref +); + +@underscore_arg = @assignment | @binary | @conditional | @operator_assignment | @range | @unary | @underscore_primary + +@underscore_lhs = @call | @element_reference | @scope_resolution | @token_false | @token_nil | @token_true | @underscore_variable + +@underscore_method_name = @delimited_symbol | @setter | @token_class_variable | @token_constant | @token_global_variable | @token_identifier | @token_instance_variable | @token_operator | @token_simple_symbol + +@underscore_primary = @array | @begin | @break | @case__ | @chained_string | @class | @delimited_symbol | @for | @hash | @if | @lambda | @method | @module | @next | @parenthesized_statements | @rational | @redo | @regex | @retry | @return | @singleton_class | @singleton_method | @string__ | @string_array | @subshell | @symbol_array | @token_character | @token_complex | @token_float | @token_heredoc_beginning | @token_integer | @token_simple_symbol | @unary | @underscore_lhs | @unless | @until | @while | @yield + +@underscore_statement = @alias | @assignment | @begin_block | @binary | @break | @call | @end_block | @if_modifier | @next | @operator_assignment | @rescue_modifier | @return | @unary | @undef | @underscore_arg | @unless_modifier | @until_modifier | @while_modifier | @yield + +@underscore_variable = @token_class_variable | @token_constant | @token_global_variable | @token_identifier | @token_instance_variable | @token_self | @token_super + +alias_def( + unique int id: @alias, + int alias: @underscore_method_name ref, + int name: @underscore_method_name ref, + int loc: @location ref +); + +@argument_list_child_type = @block_argument | @break | @call | @hash_splat_argument | @next | @pair | @return | @splat_argument | @underscore_arg | @yield + +#keyset[argument_list, index] +argument_list_child( + int argument_list: @argument_list ref, + int index: int ref, + unique int child: @argument_list_child_type ref +); + +argument_list_def( + unique int id: @argument_list, + int loc: @location ref +); + +@array_child_type = @block_argument | @break | @call | @hash_splat_argument | @next | @pair | @return | @splat_argument | @underscore_arg | @yield + +#keyset[array, index] +array_child( + int array: @array ref, + int index: int ref, + unique int child: @array_child_type ref +); + +array_def( + unique int id: @array, + int loc: @location ref +); + +@assignment_left_type = @left_assignment_list | @underscore_lhs + +@assignment_right_type = @break | @call | @next | @return | @right_assignment_list | @splat_argument | @underscore_arg | @yield + +assignment_def( + unique int id: @assignment, + int left: @assignment_left_type ref, + int right: @assignment_right_type ref, + int loc: @location ref +); + +@bare_string_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[bare_string, index] +bare_string_child( + int bare_string: @bare_string ref, + int index: int ref, + unique int child: @bare_string_child_type ref +); + +bare_string_def( + unique int id: @bare_string, + int loc: @location ref +); + +@bare_symbol_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[bare_symbol, index] +bare_symbol_child( + int bare_symbol: @bare_symbol ref, + int index: int ref, + unique int child: @bare_symbol_child_type ref +); + +bare_symbol_def( + unique int id: @bare_symbol, + int loc: @location ref +); + +@begin_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[begin, index] +begin_child( + int begin: @begin ref, + int index: int ref, + unique int child: @begin_child_type ref +); + +begin_def( + unique int id: @begin, + int loc: @location ref +); + +@begin_block_child_type = @token_empty_statement | @underscore_statement + +#keyset[begin_block, index] +begin_block_child( + int begin_block: @begin_block ref, + int index: int ref, + unique int child: @begin_block_child_type ref +); + +begin_block_def( + unique int id: @begin_block, + int loc: @location ref +); + +@binary_left_type = @break | @call | @next | @return | @underscore_arg | @yield + +case @binary.operator of + 0 = @binary_bangequal +| 1 = @binary_bangtilde +| 2 = @binary_percent +| 3 = @binary_ampersand +| 4 = @binary_ampersandampersand +| 5 = @binary_star +| 6 = @binary_starstar +| 7 = @binary_plus +| 8 = @binary_minus +| 9 = @binary_slash +| 10 = @binary_langle +| 11 = @binary_langlelangle +| 12 = @binary_langleequal +| 13 = @binary_langleequalrangle +| 14 = @binary_equalequal +| 15 = @binary_equalequalequal +| 16 = @binary_equaltilde +| 17 = @binary_rangle +| 18 = @binary_rangleequal +| 19 = @binary_ranglerangle +| 20 = @binary_caret +| 21 = @binary_and +| 22 = @binary_or +| 23 = @binary_pipe +| 24 = @binary_pipepipe +; + + +@binary_right_type = @break | @call | @next | @return | @underscore_arg | @yield + +binary_def( + unique int id: @binary, + int left: @binary_left_type ref, + int operator: int ref, + int right: @binary_right_type ref, + int loc: @location ref +); + +block_parameters( + unique int block: @block ref, + unique int parameters: @block_parameters ref +); + +@block_child_type = @token_empty_statement | @underscore_statement + +#keyset[block, index] +block_child( + int block: @block ref, + int index: int ref, + unique int child: @block_child_type ref +); + +block_def( + unique int id: @block, + int loc: @location ref +); + +block_argument_def( + unique int id: @block_argument, + int child: @underscore_arg ref, + int loc: @location ref +); + +block_parameter_def( + unique int id: @block_parameter, + int name: @token_identifier ref, + int loc: @location ref +); + +@block_parameters_child_type = @block_parameter | @destructured_parameter | @hash_splat_parameter | @keyword_parameter | @optional_parameter | @splat_parameter | @token_identifier + +#keyset[block_parameters, index] +block_parameters_child( + int block_parameters: @block_parameters ref, + int index: int ref, + unique int child: @block_parameters_child_type ref +); + +block_parameters_def( + unique int id: @block_parameters, + int loc: @location ref +); + +break_child( + unique int break: @break ref, + unique int child: @argument_list ref +); + +break_def( + unique int id: @break, + int loc: @location ref +); + +call_arguments( + unique int call: @call ref, + unique int arguments: @argument_list ref +); + +@call_block_type = @block | @do_block + +call_block( + unique int call: @call ref, + unique int block: @call_block_type ref +); + +@call_method_type = @argument_list | @scope_resolution | @token_operator | @underscore_variable + +@call_receiver_type = @call | @underscore_primary + +call_receiver( + unique int call: @call ref, + unique int receiver: @call_receiver_type ref +); + +call_def( + unique int id: @call, + int method: @call_method_type ref, + int loc: @location ref +); + +case_value( + unique int case__: @case__ ref, + unique int value: @underscore_statement ref +); + +@case_child_type = @else | @when + +#keyset[case__, index] +case_child( + int case__: @case__ ref, + int index: int ref, + unique int child: @case_child_type ref +); + +case_def( + unique int id: @case__, + int loc: @location ref +); + +#keyset[chained_string, index] +chained_string_child( + int chained_string: @chained_string ref, + int index: int ref, + unique int child: @string__ ref +); + +chained_string_def( + unique int id: @chained_string, + int loc: @location ref +); + +@class_name_type = @scope_resolution | @token_constant + +class_superclass( + unique int class: @class ref, + unique int superclass: @superclass ref +); + +@class_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[class, index] +class_child( + int class: @class ref, + int index: int ref, + unique int child: @class_child_type ref +); + +class_def( + unique int id: @class, + int name: @class_name_type ref, + int loc: @location ref +); + +conditional_def( + unique int id: @conditional, + int alternative: @underscore_arg ref, + int condition: @underscore_arg ref, + int consequence: @underscore_arg ref, + int loc: @location ref +); + +@delimited_symbol_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[delimited_symbol, index] +delimited_symbol_child( + int delimited_symbol: @delimited_symbol ref, + int index: int ref, + unique int child: @delimited_symbol_child_type ref +); + +delimited_symbol_def( + unique int id: @delimited_symbol, + int loc: @location ref +); + +@destructured_left_assignment_child_type = @destructured_left_assignment | @rest_assignment | @underscore_lhs + +#keyset[destructured_left_assignment, index] +destructured_left_assignment_child( + int destructured_left_assignment: @destructured_left_assignment ref, + int index: int ref, + unique int child: @destructured_left_assignment_child_type ref +); + +destructured_left_assignment_def( + unique int id: @destructured_left_assignment, + int loc: @location ref +); + +@destructured_parameter_child_type = @block_parameter | @destructured_parameter | @hash_splat_parameter | @keyword_parameter | @optional_parameter | @splat_parameter | @token_identifier + +#keyset[destructured_parameter, index] +destructured_parameter_child( + int destructured_parameter: @destructured_parameter ref, + int index: int ref, + unique int child: @destructured_parameter_child_type ref +); + +destructured_parameter_def( + unique int id: @destructured_parameter, + int loc: @location ref +); + +@do_child_type = @token_empty_statement | @underscore_statement + +#keyset[do, index] +do_child( + int do: @do ref, + int index: int ref, + unique int child: @do_child_type ref +); + +do_def( + unique int id: @do, + int loc: @location ref +); + +do_block_parameters( + unique int do_block: @do_block ref, + unique int parameters: @block_parameters ref +); + +@do_block_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[do_block, index] +do_block_child( + int do_block: @do_block ref, + int index: int ref, + unique int child: @do_block_child_type ref +); + +do_block_def( + unique int id: @do_block, + int loc: @location ref +); + +@element_reference_child_type = @block_argument | @break | @call | @hash_splat_argument | @next | @pair | @return | @splat_argument | @underscore_arg | @yield + +#keyset[element_reference, index] +element_reference_child( + int element_reference: @element_reference ref, + int index: int ref, + unique int child: @element_reference_child_type ref +); + +element_reference_def( + unique int id: @element_reference, + int object: @underscore_primary ref, + int loc: @location ref +); + +@else_child_type = @token_empty_statement | @underscore_statement + +#keyset[else, index] +else_child( + int else: @else ref, + int index: int ref, + unique int child: @else_child_type ref +); + +else_def( + unique int id: @else, + int loc: @location ref +); + +@elsif_alternative_type = @else | @elsif + +elsif_alternative( + unique int elsif: @elsif ref, + unique int alternative: @elsif_alternative_type ref +); + +elsif_consequence( + unique int elsif: @elsif ref, + unique int consequence: @then ref +); + +elsif_def( + unique int id: @elsif, + int condition: @underscore_statement ref, + int loc: @location ref +); + +@end_block_child_type = @token_empty_statement | @underscore_statement + +#keyset[end_block, index] +end_block_child( + int end_block: @end_block ref, + int index: int ref, + unique int child: @end_block_child_type ref +); + +end_block_def( + unique int id: @end_block, + int loc: @location ref +); + +@ensure_child_type = @token_empty_statement | @underscore_statement + +#keyset[ensure, index] +ensure_child( + int ensure: @ensure ref, + int index: int ref, + unique int child: @ensure_child_type ref +); + +ensure_def( + unique int id: @ensure, + int loc: @location ref +); + +exception_variable_def( + unique int id: @exception_variable, + int child: @underscore_lhs ref, + int loc: @location ref +); + +@exceptions_child_type = @splat_argument | @underscore_arg + +#keyset[exceptions, index] +exceptions_child( + int exceptions: @exceptions ref, + int index: int ref, + unique int child: @exceptions_child_type ref +); + +exceptions_def( + unique int id: @exceptions, + int loc: @location ref +); + +@for_pattern_type = @left_assignment_list | @underscore_lhs + +for_def( + unique int id: @for, + int body: @do ref, + int pattern: @for_pattern_type ref, + int value: @in ref, + int loc: @location ref +); + +@hash_child_type = @hash_splat_argument | @pair + +#keyset[hash, index] +hash_child( + int hash: @hash ref, + int index: int ref, + unique int child: @hash_child_type ref +); + +hash_def( + unique int id: @hash, + int loc: @location ref +); + +hash_splat_argument_def( + unique int id: @hash_splat_argument, + int child: @underscore_arg ref, + int loc: @location ref +); + +hash_splat_parameter_name( + unique int hash_splat_parameter: @hash_splat_parameter ref, + unique int name: @token_identifier ref +); + +hash_splat_parameter_def( + unique int id: @hash_splat_parameter, + int loc: @location ref +); + +@heredoc_body_child_type = @interpolation | @token_escape_sequence | @token_heredoc_content | @token_heredoc_end + +#keyset[heredoc_body, index] +heredoc_body_child( + int heredoc_body: @heredoc_body ref, + int index: int ref, + unique int child: @heredoc_body_child_type ref +); + +heredoc_body_def( + unique int id: @heredoc_body, + int loc: @location ref +); + +@if_alternative_type = @else | @elsif + +if_alternative( + unique int if: @if ref, + unique int alternative: @if_alternative_type ref +); + +if_consequence( + unique int if: @if ref, + unique int consequence: @then ref +); + +if_def( + unique int id: @if, + int condition: @underscore_statement ref, + int loc: @location ref +); + +@if_modifier_condition_type = @break | @call | @next | @return | @underscore_arg | @yield + +if_modifier_def( + unique int id: @if_modifier, + int body: @underscore_statement ref, + int condition: @if_modifier_condition_type ref, + int loc: @location ref +); + +in_def( + unique int id: @in, + int child: @underscore_arg ref, + int loc: @location ref +); + +@interpolation_child_type = @token_empty_statement | @underscore_statement + +#keyset[interpolation, index] +interpolation_child( + int interpolation: @interpolation ref, + int index: int ref, + unique int child: @interpolation_child_type ref +); + +interpolation_def( + unique int id: @interpolation, + int loc: @location ref +); + +keyword_parameter_value( + unique int keyword_parameter: @keyword_parameter ref, + unique int value: @underscore_arg ref +); + +keyword_parameter_def( + unique int id: @keyword_parameter, + int name: @token_identifier ref, + int loc: @location ref +); + +@lambda_body_type = @block | @do_block + +lambda_parameters( + unique int lambda: @lambda ref, + unique int parameters: @lambda_parameters ref +); + +lambda_def( + unique int id: @lambda, + int body: @lambda_body_type ref, + int loc: @location ref +); + +@lambda_parameters_child_type = @block_parameter | @destructured_parameter | @hash_splat_parameter | @keyword_parameter | @optional_parameter | @splat_parameter | @token_identifier + +#keyset[lambda_parameters, index] +lambda_parameters_child( + int lambda_parameters: @lambda_parameters ref, + int index: int ref, + unique int child: @lambda_parameters_child_type ref +); + +lambda_parameters_def( + unique int id: @lambda_parameters, + int loc: @location ref +); + +@left_assignment_list_child_type = @destructured_left_assignment | @rest_assignment | @underscore_lhs + +#keyset[left_assignment_list, index] +left_assignment_list_child( + int left_assignment_list: @left_assignment_list ref, + int index: int ref, + unique int child: @left_assignment_list_child_type ref +); + +left_assignment_list_def( + unique int id: @left_assignment_list, + int loc: @location ref +); + +method_parameters( + unique int method: @method ref, + unique int parameters: @method_parameters ref +); + +@method_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[method, index] +method_child( + int method: @method ref, + int index: int ref, + unique int child: @method_child_type ref +); + +method_def( + unique int id: @method, + int name: @underscore_method_name ref, + int loc: @location ref +); + +@method_parameters_child_type = @block_parameter | @destructured_parameter | @hash_splat_parameter | @keyword_parameter | @optional_parameter | @splat_parameter | @token_identifier + +#keyset[method_parameters, index] +method_parameters_child( + int method_parameters: @method_parameters ref, + int index: int ref, + unique int child: @method_parameters_child_type ref +); + +method_parameters_def( + unique int id: @method_parameters, + int loc: @location ref +); + +@module_name_type = @scope_resolution | @token_constant + +@module_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[module, index] +module_child( + int module: @module ref, + int index: int ref, + unique int child: @module_child_type ref +); + +module_def( + unique int id: @module, + int name: @module_name_type ref, + int loc: @location ref +); + +next_child( + unique int next: @next ref, + unique int child: @argument_list ref +); + +next_def( + unique int id: @next, + int loc: @location ref +); + +case @operator_assignment.operator of + 0 = @operator_assignment_percentequal +| 1 = @operator_assignment_ampersandampersandequal +| 2 = @operator_assignment_ampersandequal +| 3 = @operator_assignment_starstarequal +| 4 = @operator_assignment_starequal +| 5 = @operator_assignment_plusequal +| 6 = @operator_assignment_minusequal +| 7 = @operator_assignment_slashequal +| 8 = @operator_assignment_langlelangleequal +| 9 = @operator_assignment_ranglerangleequal +| 10 = @operator_assignment_caretequal +| 11 = @operator_assignment_pipeequal +| 12 = @operator_assignment_pipepipeequal +; + + +@operator_assignment_right_type = @break | @call | @next | @return | @underscore_arg | @yield + +operator_assignment_def( + unique int id: @operator_assignment, + int left: @underscore_lhs ref, + int operator: int ref, + int right: @operator_assignment_right_type ref, + int loc: @location ref +); + +optional_parameter_def( + unique int id: @optional_parameter, + int name: @token_identifier ref, + int value: @underscore_arg ref, + int loc: @location ref +); + +@pair_key_type = @string__ | @token_hash_key_symbol | @underscore_arg + +pair_def( + unique int id: @pair, + int key__: @pair_key_type ref, + int value: @underscore_arg ref, + int loc: @location ref +); + +@parenthesized_statements_child_type = @token_empty_statement | @underscore_statement + +#keyset[parenthesized_statements, index] +parenthesized_statements_child( + int parenthesized_statements: @parenthesized_statements ref, + int index: int ref, + unique int child: @parenthesized_statements_child_type ref +); + +parenthesized_statements_def( + unique int id: @parenthesized_statements, + int loc: @location ref +); + +@pattern_child_type = @splat_argument | @underscore_arg + +pattern_def( + unique int id: @pattern, + int child: @pattern_child_type ref, + int loc: @location ref +); + +@program_child_type = @token_empty_statement | @token_uninterpreted | @underscore_statement + +#keyset[program, index] +program_child( + int program: @program ref, + int index: int ref, + unique int child: @program_child_type ref +); + +program_def( + unique int id: @program, + int loc: @location ref +); + +range_begin( + unique int range: @range ref, + unique int begin: @underscore_arg ref +); + +range_end( + unique int range: @range ref, + unique int end: @underscore_arg ref +); + +case @range.operator of + 0 = @range_dotdot +| 1 = @range_dotdotdot +; + + +range_def( + unique int id: @range, + int operator: int ref, + int loc: @location ref +); + +@rational_child_type = @token_float | @token_integer + +rational_def( + unique int id: @rational, + int child: @rational_child_type ref, + int loc: @location ref +); + +redo_child( + unique int redo: @redo ref, + unique int child: @argument_list ref +); + +redo_def( + unique int id: @redo, + int loc: @location ref +); + +@regex_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[regex, index] +regex_child( + int regex: @regex ref, + int index: int ref, + unique int child: @regex_child_type ref +); + +regex_def( + unique int id: @regex, + int loc: @location ref +); + +rescue_body( + unique int rescue: @rescue ref, + unique int body: @then ref +); + +rescue_exceptions( + unique int rescue: @rescue ref, + unique int exceptions: @exceptions ref +); + +rescue_variable( + unique int rescue: @rescue ref, + unique int variable: @exception_variable ref +); + +rescue_def( + unique int id: @rescue, + int loc: @location ref +); + +@rescue_modifier_handler_type = @break | @call | @next | @return | @underscore_arg | @yield + +rescue_modifier_def( + unique int id: @rescue_modifier, + int body: @underscore_statement ref, + int handler: @rescue_modifier_handler_type ref, + int loc: @location ref +); + +rest_assignment_child( + unique int rest_assignment: @rest_assignment ref, + unique int child: @underscore_lhs ref +); + +rest_assignment_def( + unique int id: @rest_assignment, + int loc: @location ref +); + +retry_child( + unique int retry: @retry ref, + unique int child: @argument_list ref +); + +retry_def( + unique int id: @retry, + int loc: @location ref +); + +return_child( + unique int return: @return ref, + unique int child: @argument_list ref +); + +return_def( + unique int id: @return, + int loc: @location ref +); + +@right_assignment_list_child_type = @splat_argument | @underscore_arg + +#keyset[right_assignment_list, index] +right_assignment_list_child( + int right_assignment_list: @right_assignment_list ref, + int index: int ref, + unique int child: @right_assignment_list_child_type ref +); + +right_assignment_list_def( + unique int id: @right_assignment_list, + int loc: @location ref +); + +@scope_resolution_name_type = @token_constant | @token_identifier + +scope_resolution_scope( + unique int scope_resolution: @scope_resolution ref, + unique int scope: @underscore_primary ref +); + +scope_resolution_def( + unique int id: @scope_resolution, + int name: @scope_resolution_name_type ref, + int loc: @location ref +); + +setter_def( + unique int id: @setter, + int name: @token_identifier ref, + int loc: @location ref +); + +@singleton_class_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[singleton_class, index] +singleton_class_child( + int singleton_class: @singleton_class ref, + int index: int ref, + unique int child: @singleton_class_child_type ref +); + +singleton_class_def( + unique int id: @singleton_class, + int value: @underscore_arg ref, + int loc: @location ref +); + +@singleton_method_object_type = @underscore_arg | @underscore_variable + +singleton_method_parameters( + unique int singleton_method: @singleton_method ref, + unique int parameters: @method_parameters ref +); + +@singleton_method_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[singleton_method, index] +singleton_method_child( + int singleton_method: @singleton_method ref, + int index: int ref, + unique int child: @singleton_method_child_type ref +); + +singleton_method_def( + unique int id: @singleton_method, + int name: @underscore_method_name ref, + int object: @singleton_method_object_type ref, + int loc: @location ref +); + +splat_argument_def( + unique int id: @splat_argument, + int child: @underscore_arg ref, + int loc: @location ref +); + +splat_parameter_name( + unique int splat_parameter: @splat_parameter ref, + unique int name: @token_identifier ref +); + +splat_parameter_def( + unique int id: @splat_parameter, + int loc: @location ref +); + +@string_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[string__, index] +string_child( + int string__: @string__ ref, + int index: int ref, + unique int child: @string_child_type ref +); + +string_def( + unique int id: @string__, + int loc: @location ref +); + +#keyset[string_array, index] +string_array_child( + int string_array: @string_array ref, + int index: int ref, + unique int child: @bare_string ref +); + +string_array_def( + unique int id: @string_array, + int loc: @location ref +); + +@subshell_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[subshell, index] +subshell_child( + int subshell: @subshell ref, + int index: int ref, + unique int child: @subshell_child_type ref +); + +subshell_def( + unique int id: @subshell, + int loc: @location ref +); + +@superclass_child_type = @break | @call | @next | @return | @underscore_arg | @yield + +superclass_def( + unique int id: @superclass, + int child: @superclass_child_type ref, + int loc: @location ref +); + +#keyset[symbol_array, index] +symbol_array_child( + int symbol_array: @symbol_array ref, + int index: int ref, + unique int child: @bare_symbol ref +); + +symbol_array_def( + unique int id: @symbol_array, + int loc: @location ref +); + +@then_child_type = @token_empty_statement | @underscore_statement + +#keyset[then, index] +then_child( + int then: @then ref, + int index: int ref, + unique int child: @then_child_type ref +); + +then_def( + unique int id: @then, + int loc: @location ref +); + +@unary_operand_type = @break | @call | @next | @parenthesized_statements | @return | @token_float | @token_integer | @underscore_arg | @yield + +case @unary.operator of + 0 = @unary_bang +| 1 = @unary_plus +| 2 = @unary_minus +| 3 = @unary_definedquestion +| 4 = @unary_not +| 5 = @unary_tilde +; + + +unary_def( + unique int id: @unary, + int operand: @unary_operand_type ref, + int operator: int ref, + int loc: @location ref +); + +#keyset[undef, index] +undef_child( + int undef: @undef ref, + int index: int ref, + unique int child: @underscore_method_name ref +); + +undef_def( + unique int id: @undef, + int loc: @location ref +); + +@unless_alternative_type = @else | @elsif + +unless_alternative( + unique int unless: @unless ref, + unique int alternative: @unless_alternative_type ref +); + +unless_consequence( + unique int unless: @unless ref, + unique int consequence: @then ref +); + +unless_def( + unique int id: @unless, + int condition: @underscore_statement ref, + int loc: @location ref +); + +@unless_modifier_condition_type = @break | @call | @next | @return | @underscore_arg | @yield + +unless_modifier_def( + unique int id: @unless_modifier, + int body: @underscore_statement ref, + int condition: @unless_modifier_condition_type ref, + int loc: @location ref +); + +until_def( + unique int id: @until, + int body: @do ref, + int condition: @underscore_statement ref, + int loc: @location ref +); + +@until_modifier_condition_type = @break | @call | @next | @return | @underscore_arg | @yield + +until_modifier_def( + unique int id: @until_modifier, + int body: @underscore_statement ref, + int condition: @until_modifier_condition_type ref, + int loc: @location ref +); + +when_body( + unique int when: @when ref, + unique int body: @then ref +); + +#keyset[when, index] +when_pattern( + int when: @when ref, + int index: int ref, + unique int pattern: @pattern ref +); + +when_def( + unique int id: @when, + int loc: @location ref +); + +while_def( + unique int id: @while, + int body: @do ref, + int condition: @underscore_statement ref, + int loc: @location ref +); + +@while_modifier_condition_type = @break | @call | @next | @return | @underscore_arg | @yield + +while_modifier_def( + unique int id: @while_modifier, + int body: @underscore_statement ref, + int condition: @while_modifier_condition_type ref, + int loc: @location ref +); + +yield_child( + unique int yield: @yield ref, + unique int child: @argument_list ref +); + +yield_def( + unique int id: @yield, + int loc: @location ref +); + +tokeninfo( + unique int id: @token, + int kind: int ref, + int file: @file ref, + int idx: int ref, + string value: string ref, + int loc: @location ref +); + +case @token.kind of + 0 = @reserved_word +| 1 = @token_character +| 2 = @token_class_variable +| 3 = @token_comment +| 4 = @token_complex +| 5 = @token_constant +| 6 = @token_empty_statement +| 7 = @token_escape_sequence +| 8 = @token_false +| 9 = @token_float +| 10 = @token_global_variable +| 11 = @token_hash_key_symbol +| 12 = @token_heredoc_beginning +| 13 = @token_heredoc_content +| 14 = @token_heredoc_end +| 15 = @token_identifier +| 16 = @token_instance_variable +| 17 = @token_integer +| 18 = @token_nil +| 19 = @token_operator +| 20 = @token_self +| 21 = @token_simple_symbol +| 22 = @token_string_content +| 23 = @token_super +| 24 = @token_true +| 25 = @token_uninterpreted +; + + +@ast_node = @alias | @argument_list | @array | @assignment | @bare_string | @bare_symbol | @begin | @begin_block | @binary | @block | @block_argument | @block_parameter | @block_parameters | @break | @call | @case__ | @chained_string | @class | @conditional | @delimited_symbol | @destructured_left_assignment | @destructured_parameter | @do | @do_block | @element_reference | @else | @elsif | @end_block | @ensure | @exception_variable | @exceptions | @for | @hash | @hash_splat_argument | @hash_splat_parameter | @heredoc_body | @if | @if_modifier | @in | @interpolation | @keyword_parameter | @lambda | @lambda_parameters | @left_assignment_list | @method | @method_parameters | @module | @next | @operator_assignment | @optional_parameter | @pair | @parenthesized_statements | @pattern | @program | @range | @rational | @redo | @regex | @rescue | @rescue_modifier | @rest_assignment | @retry | @return | @right_assignment_list | @scope_resolution | @setter | @singleton_class | @singleton_method | @splat_argument | @splat_parameter | @string__ | @string_array | @subshell | @superclass | @symbol_array | @then | @token | @unary | @undef | @unless | @unless_modifier | @until | @until_modifier | @when | @while | @while_modifier | @yield + +@ast_node_parent = @ast_node | @file + +#keyset[parent, parent_index] +ast_node_parent( + int child: @ast_node ref, + int parent: @ast_node_parent ref, + int parent_index: int ref +); + diff --git a/ruby/ql/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/ruby.dbscheme b/ruby/ql/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/ruby.dbscheme new file mode 100644 index 00000000000..40be81bc208 --- /dev/null +++ b/ruby/ql/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/ruby.dbscheme @@ -0,0 +1,1267 @@ +// CodeQL database schema for Ruby +// Automatically generated from the tree-sitter grammar; do not edit + +@location = @location_default + +locations_default( + unique int id: @location_default, + int file: @file ref, + int start_line: int ref, + int start_column: int ref, + int end_line: int ref, + int end_column: int ref +); + +@sourceline = @file + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref +); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +sourceLocationPrefix( + string prefix: string ref +); + +@underscore_arg = @assignment | @binary | @conditional | @operator_assignment | @range | @unary | @underscore_primary + +@underscore_lhs = @call | @element_reference | @scope_resolution | @token_false | @token_nil | @token_true | @underscore_variable + +@underscore_method_name = @delimited_symbol | @setter | @token_class_variable | @token_constant | @token_global_variable | @token_identifier | @token_instance_variable | @token_operator | @token_simple_symbol + +@underscore_primary = @array | @begin | @break | @case__ | @chained_string | @class | @delimited_symbol | @for | @hash | @if | @lambda | @method | @module | @next | @parenthesized_statements | @rational | @redo | @regex | @retry | @return | @singleton_class | @singleton_method | @string__ | @string_array | @subshell | @symbol_array | @token_character | @token_complex | @token_float | @token_heredoc_beginning | @token_integer | @token_simple_symbol | @unary | @underscore_lhs | @unless | @until | @while | @yield + +@underscore_statement = @alias | @assignment | @begin_block | @binary | @break | @call | @end_block | @if_modifier | @next | @operator_assignment | @rescue_modifier | @return | @unary | @undef | @underscore_arg | @unless_modifier | @until_modifier | @while_modifier | @yield + +@underscore_variable = @token_class_variable | @token_constant | @token_global_variable | @token_identifier | @token_instance_variable | @token_self | @token_super + +alias_def( + unique int id: @alias, + int alias: @underscore_method_name ref, + int name: @underscore_method_name ref, + int loc: @location ref +); + +@argument_list_child_type = @block_argument | @break | @call | @hash_splat_argument | @next | @pair | @return | @splat_argument | @underscore_arg | @yield + +#keyset[argument_list, index] +argument_list_child( + int argument_list: @argument_list ref, + int index: int ref, + unique int child: @argument_list_child_type ref +); + +argument_list_def( + unique int id: @argument_list, + int loc: @location ref +); + +@array_child_type = @block_argument | @break | @call | @hash_splat_argument | @next | @pair | @return | @splat_argument | @underscore_arg | @yield + +#keyset[array, index] +array_child( + int array: @array ref, + int index: int ref, + unique int child: @array_child_type ref +); + +array_def( + unique int id: @array, + int loc: @location ref +); + +@assignment_left_type = @left_assignment_list | @underscore_lhs + +@assignment_right_type = @break | @call | @next | @return | @right_assignment_list | @splat_argument | @underscore_arg | @yield + +assignment_def( + unique int id: @assignment, + int left: @assignment_left_type ref, + int right: @assignment_right_type ref, + int loc: @location ref +); + +@bare_string_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[bare_string, index] +bare_string_child( + int bare_string: @bare_string ref, + int index: int ref, + unique int child: @bare_string_child_type ref +); + +bare_string_def( + unique int id: @bare_string, + int loc: @location ref +); + +@bare_symbol_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[bare_symbol, index] +bare_symbol_child( + int bare_symbol: @bare_symbol ref, + int index: int ref, + unique int child: @bare_symbol_child_type ref +); + +bare_symbol_def( + unique int id: @bare_symbol, + int loc: @location ref +); + +@begin_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[begin, index] +begin_child( + int begin: @begin ref, + int index: int ref, + unique int child: @begin_child_type ref +); + +begin_def( + unique int id: @begin, + int loc: @location ref +); + +@begin_block_child_type = @token_empty_statement | @underscore_statement + +#keyset[begin_block, index] +begin_block_child( + int begin_block: @begin_block ref, + int index: int ref, + unique int child: @begin_block_child_type ref +); + +begin_block_def( + unique int id: @begin_block, + int loc: @location ref +); + +@binary_left_type = @break | @call | @next | @return | @underscore_arg | @yield + +case @binary.operator of + 0 = @binary_bangequal +| 1 = @binary_bangtilde +| 2 = @binary_percent +| 3 = @binary_ampersand +| 4 = @binary_ampersandampersand +| 5 = @binary_star +| 6 = @binary_starstar +| 7 = @binary_plus +| 8 = @binary_minus +| 9 = @binary_slash +| 10 = @binary_langle +| 11 = @binary_langlelangle +| 12 = @binary_langleequal +| 13 = @binary_langleequalrangle +| 14 = @binary_equalequal +| 15 = @binary_equalequalequal +| 16 = @binary_equaltilde +| 17 = @binary_rangle +| 18 = @binary_rangleequal +| 19 = @binary_ranglerangle +| 20 = @binary_caret +| 21 = @binary_and +| 22 = @binary_or +| 23 = @binary_pipe +| 24 = @binary_pipepipe +; + + +@binary_right_type = @break | @call | @next | @return | @underscore_arg | @yield + +binary_def( + unique int id: @binary, + int left: @binary_left_type ref, + int operator: int ref, + int right: @binary_right_type ref, + int loc: @location ref +); + +block_parameters( + unique int block: @block ref, + unique int parameters: @block_parameters ref +); + +@block_child_type = @token_empty_statement | @underscore_statement + +#keyset[block, index] +block_child( + int block: @block ref, + int index: int ref, + unique int child: @block_child_type ref +); + +block_def( + unique int id: @block, + int loc: @location ref +); + +block_argument_def( + unique int id: @block_argument, + int child: @underscore_arg ref, + int loc: @location ref +); + +block_parameter_def( + unique int id: @block_parameter, + int name: @token_identifier ref, + int loc: @location ref +); + +@block_parameters_child_type = @block_parameter | @destructured_parameter | @hash_splat_parameter | @keyword_parameter | @optional_parameter | @splat_parameter | @token_identifier + +#keyset[block_parameters, index] +block_parameters_child( + int block_parameters: @block_parameters ref, + int index: int ref, + unique int child: @block_parameters_child_type ref +); + +block_parameters_def( + unique int id: @block_parameters, + int loc: @location ref +); + +break_child( + unique int break: @break ref, + unique int child: @argument_list ref +); + +break_def( + unique int id: @break, + int loc: @location ref +); + +call_arguments( + unique int call: @call ref, + unique int arguments: @argument_list ref +); + +@call_block_type = @block | @do_block + +call_block( + unique int call: @call ref, + unique int block: @call_block_type ref +); + +@call_method_type = @argument_list | @scope_resolution | @token_operator | @underscore_variable + +@call_receiver_type = @call | @underscore_primary + +call_receiver( + unique int call: @call ref, + unique int receiver: @call_receiver_type ref +); + +call_def( + unique int id: @call, + int method: @call_method_type ref, + int loc: @location ref +); + +case_value( + unique int case__: @case__ ref, + unique int value: @underscore_statement ref +); + +@case_child_type = @else | @when + +#keyset[case__, index] +case_child( + int case__: @case__ ref, + int index: int ref, + unique int child: @case_child_type ref +); + +case_def( + unique int id: @case__, + int loc: @location ref +); + +#keyset[chained_string, index] +chained_string_child( + int chained_string: @chained_string ref, + int index: int ref, + unique int child: @string__ ref +); + +chained_string_def( + unique int id: @chained_string, + int loc: @location ref +); + +@class_name_type = @scope_resolution | @token_constant + +class_superclass( + unique int class: @class ref, + unique int superclass: @superclass ref +); + +@class_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[class, index] +class_child( + int class: @class ref, + int index: int ref, + unique int child: @class_child_type ref +); + +class_def( + unique int id: @class, + int name: @class_name_type ref, + int loc: @location ref +); + +conditional_def( + unique int id: @conditional, + int alternative: @underscore_arg ref, + int condition: @underscore_arg ref, + int consequence: @underscore_arg ref, + int loc: @location ref +); + +@delimited_symbol_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[delimited_symbol, index] +delimited_symbol_child( + int delimited_symbol: @delimited_symbol ref, + int index: int ref, + unique int child: @delimited_symbol_child_type ref +); + +delimited_symbol_def( + unique int id: @delimited_symbol, + int loc: @location ref +); + +@destructured_left_assignment_child_type = @destructured_left_assignment | @rest_assignment | @underscore_lhs + +#keyset[destructured_left_assignment, index] +destructured_left_assignment_child( + int destructured_left_assignment: @destructured_left_assignment ref, + int index: int ref, + unique int child: @destructured_left_assignment_child_type ref +); + +destructured_left_assignment_def( + unique int id: @destructured_left_assignment, + int loc: @location ref +); + +@destructured_parameter_child_type = @block_parameter | @destructured_parameter | @hash_splat_parameter | @keyword_parameter | @optional_parameter | @splat_parameter | @token_identifier + +#keyset[destructured_parameter, index] +destructured_parameter_child( + int destructured_parameter: @destructured_parameter ref, + int index: int ref, + unique int child: @destructured_parameter_child_type ref +); + +destructured_parameter_def( + unique int id: @destructured_parameter, + int loc: @location ref +); + +@do_child_type = @token_empty_statement | @underscore_statement + +#keyset[do, index] +do_child( + int do: @do ref, + int index: int ref, + unique int child: @do_child_type ref +); + +do_def( + unique int id: @do, + int loc: @location ref +); + +do_block_parameters( + unique int do_block: @do_block ref, + unique int parameters: @block_parameters ref +); + +@do_block_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[do_block, index] +do_block_child( + int do_block: @do_block ref, + int index: int ref, + unique int child: @do_block_child_type ref +); + +do_block_def( + unique int id: @do_block, + int loc: @location ref +); + +@element_reference_child_type = @block_argument | @break | @call | @hash_splat_argument | @next | @pair | @return | @splat_argument | @underscore_arg | @yield + +#keyset[element_reference, index] +element_reference_child( + int element_reference: @element_reference ref, + int index: int ref, + unique int child: @element_reference_child_type ref +); + +element_reference_def( + unique int id: @element_reference, + int object: @underscore_primary ref, + int loc: @location ref +); + +@else_child_type = @token_empty_statement | @underscore_statement + +#keyset[else, index] +else_child( + int else: @else ref, + int index: int ref, + unique int child: @else_child_type ref +); + +else_def( + unique int id: @else, + int loc: @location ref +); + +@elsif_alternative_type = @else | @elsif + +elsif_alternative( + unique int elsif: @elsif ref, + unique int alternative: @elsif_alternative_type ref +); + +elsif_consequence( + unique int elsif: @elsif ref, + unique int consequence: @then ref +); + +elsif_def( + unique int id: @elsif, + int condition: @underscore_statement ref, + int loc: @location ref +); + +@end_block_child_type = @token_empty_statement | @underscore_statement + +#keyset[end_block, index] +end_block_child( + int end_block: @end_block ref, + int index: int ref, + unique int child: @end_block_child_type ref +); + +end_block_def( + unique int id: @end_block, + int loc: @location ref +); + +@ensure_child_type = @token_empty_statement | @underscore_statement + +#keyset[ensure, index] +ensure_child( + int ensure: @ensure ref, + int index: int ref, + unique int child: @ensure_child_type ref +); + +ensure_def( + unique int id: @ensure, + int loc: @location ref +); + +exception_variable_def( + unique int id: @exception_variable, + int child: @underscore_lhs ref, + int loc: @location ref +); + +@exceptions_child_type = @splat_argument | @underscore_arg + +#keyset[exceptions, index] +exceptions_child( + int exceptions: @exceptions ref, + int index: int ref, + unique int child: @exceptions_child_type ref +); + +exceptions_def( + unique int id: @exceptions, + int loc: @location ref +); + +@for_pattern_type = @left_assignment_list | @underscore_lhs + +for_def( + unique int id: @for, + int body: @do ref, + int pattern: @for_pattern_type ref, + int value: @in ref, + int loc: @location ref +); + +@hash_child_type = @hash_splat_argument | @pair + +#keyset[hash, index] +hash_child( + int hash: @hash ref, + int index: int ref, + unique int child: @hash_child_type ref +); + +hash_def( + unique int id: @hash, + int loc: @location ref +); + +hash_splat_argument_def( + unique int id: @hash_splat_argument, + int child: @underscore_arg ref, + int loc: @location ref +); + +hash_splat_parameter_name( + unique int hash_splat_parameter: @hash_splat_parameter ref, + unique int name: @token_identifier ref +); + +hash_splat_parameter_def( + unique int id: @hash_splat_parameter, + int loc: @location ref +); + +@heredoc_body_child_type = @interpolation | @token_escape_sequence | @token_heredoc_content | @token_heredoc_end + +#keyset[heredoc_body, index] +heredoc_body_child( + int heredoc_body: @heredoc_body ref, + int index: int ref, + unique int child: @heredoc_body_child_type ref +); + +heredoc_body_def( + unique int id: @heredoc_body, + int loc: @location ref +); + +@if_alternative_type = @else | @elsif + +if_alternative( + unique int if: @if ref, + unique int alternative: @if_alternative_type ref +); + +if_consequence( + unique int if: @if ref, + unique int consequence: @then ref +); + +if_def( + unique int id: @if, + int condition: @underscore_statement ref, + int loc: @location ref +); + +@if_modifier_condition_type = @break | @call | @next | @return | @underscore_arg | @yield + +if_modifier_def( + unique int id: @if_modifier, + int body: @underscore_statement ref, + int condition: @if_modifier_condition_type ref, + int loc: @location ref +); + +in_def( + unique int id: @in, + int child: @underscore_arg ref, + int loc: @location ref +); + +@interpolation_child_type = @token_empty_statement | @underscore_statement + +#keyset[interpolation, index] +interpolation_child( + int interpolation: @interpolation ref, + int index: int ref, + unique int child: @interpolation_child_type ref +); + +interpolation_def( + unique int id: @interpolation, + int loc: @location ref +); + +keyword_parameter_value( + unique int keyword_parameter: @keyword_parameter ref, + unique int value: @underscore_arg ref +); + +keyword_parameter_def( + unique int id: @keyword_parameter, + int name: @token_identifier ref, + int loc: @location ref +); + +@lambda_body_type = @block | @do_block + +lambda_parameters( + unique int lambda: @lambda ref, + unique int parameters: @lambda_parameters ref +); + +lambda_def( + unique int id: @lambda, + int body: @lambda_body_type ref, + int loc: @location ref +); + +@lambda_parameters_child_type = @block_parameter | @destructured_parameter | @hash_splat_parameter | @keyword_parameter | @optional_parameter | @splat_parameter | @token_identifier + +#keyset[lambda_parameters, index] +lambda_parameters_child( + int lambda_parameters: @lambda_parameters ref, + int index: int ref, + unique int child: @lambda_parameters_child_type ref +); + +lambda_parameters_def( + unique int id: @lambda_parameters, + int loc: @location ref +); + +@left_assignment_list_child_type = @destructured_left_assignment | @rest_assignment | @underscore_lhs + +#keyset[left_assignment_list, index] +left_assignment_list_child( + int left_assignment_list: @left_assignment_list ref, + int index: int ref, + unique int child: @left_assignment_list_child_type ref +); + +left_assignment_list_def( + unique int id: @left_assignment_list, + int loc: @location ref +); + +method_parameters( + unique int method: @method ref, + unique int parameters: @method_parameters ref +); + +@method_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[method, index] +method_child( + int method: @method ref, + int index: int ref, + unique int child: @method_child_type ref +); + +method_def( + unique int id: @method, + int name: @underscore_method_name ref, + int loc: @location ref +); + +@method_parameters_child_type = @block_parameter | @destructured_parameter | @hash_splat_parameter | @keyword_parameter | @optional_parameter | @splat_parameter | @token_identifier + +#keyset[method_parameters, index] +method_parameters_child( + int method_parameters: @method_parameters ref, + int index: int ref, + unique int child: @method_parameters_child_type ref +); + +method_parameters_def( + unique int id: @method_parameters, + int loc: @location ref +); + +@module_name_type = @scope_resolution | @token_constant + +@module_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[module, index] +module_child( + int module: @module ref, + int index: int ref, + unique int child: @module_child_type ref +); + +module_def( + unique int id: @module, + int name: @module_name_type ref, + int loc: @location ref +); + +next_child( + unique int next: @next ref, + unique int child: @argument_list ref +); + +next_def( + unique int id: @next, + int loc: @location ref +); + +case @operator_assignment.operator of + 0 = @operator_assignment_percentequal +| 1 = @operator_assignment_ampersandampersandequal +| 2 = @operator_assignment_ampersandequal +| 3 = @operator_assignment_starstarequal +| 4 = @operator_assignment_starequal +| 5 = @operator_assignment_plusequal +| 6 = @operator_assignment_minusequal +| 7 = @operator_assignment_slashequal +| 8 = @operator_assignment_langlelangleequal +| 9 = @operator_assignment_ranglerangleequal +| 10 = @operator_assignment_caretequal +| 11 = @operator_assignment_pipeequal +| 12 = @operator_assignment_pipepipeequal +; + + +@operator_assignment_right_type = @break | @call | @next | @return | @underscore_arg | @yield + +operator_assignment_def( + unique int id: @operator_assignment, + int left: @underscore_lhs ref, + int operator: int ref, + int right: @operator_assignment_right_type ref, + int loc: @location ref +); + +optional_parameter_def( + unique int id: @optional_parameter, + int name: @token_identifier ref, + int value: @underscore_arg ref, + int loc: @location ref +); + +@pair_key_type = @string__ | @token_hash_key_symbol | @underscore_arg + +pair_def( + unique int id: @pair, + int key__: @pair_key_type ref, + int value: @underscore_arg ref, + int loc: @location ref +); + +@parenthesized_statements_child_type = @token_empty_statement | @underscore_statement + +#keyset[parenthesized_statements, index] +parenthesized_statements_child( + int parenthesized_statements: @parenthesized_statements ref, + int index: int ref, + unique int child: @parenthesized_statements_child_type ref +); + +parenthesized_statements_def( + unique int id: @parenthesized_statements, + int loc: @location ref +); + +@pattern_child_type = @splat_argument | @underscore_arg + +pattern_def( + unique int id: @pattern, + int child: @pattern_child_type ref, + int loc: @location ref +); + +@program_child_type = @token_empty_statement | @token_uninterpreted | @underscore_statement + +#keyset[program, index] +program_child( + int program: @program ref, + int index: int ref, + unique int child: @program_child_type ref +); + +program_def( + unique int id: @program, + int loc: @location ref +); + +range_begin( + unique int range: @range ref, + unique int begin: @underscore_arg ref +); + +range_end( + unique int range: @range ref, + unique int end: @underscore_arg ref +); + +case @range.operator of + 0 = @range_dotdot +| 1 = @range_dotdotdot +; + + +range_def( + unique int id: @range, + int operator: int ref, + int loc: @location ref +); + +@rational_child_type = @token_float | @token_integer + +rational_def( + unique int id: @rational, + int child: @rational_child_type ref, + int loc: @location ref +); + +redo_child( + unique int redo: @redo ref, + unique int child: @argument_list ref +); + +redo_def( + unique int id: @redo, + int loc: @location ref +); + +@regex_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[regex, index] +regex_child( + int regex: @regex ref, + int index: int ref, + unique int child: @regex_child_type ref +); + +regex_def( + unique int id: @regex, + int loc: @location ref +); + +rescue_body( + unique int rescue: @rescue ref, + unique int body: @then ref +); + +rescue_exceptions( + unique int rescue: @rescue ref, + unique int exceptions: @exceptions ref +); + +rescue_variable( + unique int rescue: @rescue ref, + unique int variable: @exception_variable ref +); + +rescue_def( + unique int id: @rescue, + int loc: @location ref +); + +@rescue_modifier_handler_type = @break | @call | @next | @return | @underscore_arg | @yield + +rescue_modifier_def( + unique int id: @rescue_modifier, + int body: @underscore_statement ref, + int handler: @rescue_modifier_handler_type ref, + int loc: @location ref +); + +rest_assignment_child( + unique int rest_assignment: @rest_assignment ref, + unique int child: @underscore_lhs ref +); + +rest_assignment_def( + unique int id: @rest_assignment, + int loc: @location ref +); + +retry_child( + unique int retry: @retry ref, + unique int child: @argument_list ref +); + +retry_def( + unique int id: @retry, + int loc: @location ref +); + +return_child( + unique int return: @return ref, + unique int child: @argument_list ref +); + +return_def( + unique int id: @return, + int loc: @location ref +); + +@right_assignment_list_child_type = @splat_argument | @underscore_arg + +#keyset[right_assignment_list, index] +right_assignment_list_child( + int right_assignment_list: @right_assignment_list ref, + int index: int ref, + unique int child: @right_assignment_list_child_type ref +); + +right_assignment_list_def( + unique int id: @right_assignment_list, + int loc: @location ref +); + +@scope_resolution_name_type = @token_constant | @token_identifier + +scope_resolution_scope( + unique int scope_resolution: @scope_resolution ref, + unique int scope: @underscore_primary ref +); + +scope_resolution_def( + unique int id: @scope_resolution, + int name: @scope_resolution_name_type ref, + int loc: @location ref +); + +setter_def( + unique int id: @setter, + int name: @token_identifier ref, + int loc: @location ref +); + +@singleton_class_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[singleton_class, index] +singleton_class_child( + int singleton_class: @singleton_class ref, + int index: int ref, + unique int child: @singleton_class_child_type ref +); + +singleton_class_def( + unique int id: @singleton_class, + int value: @underscore_arg ref, + int loc: @location ref +); + +@singleton_method_object_type = @underscore_arg | @underscore_variable + +singleton_method_parameters( + unique int singleton_method: @singleton_method ref, + unique int parameters: @method_parameters ref +); + +@singleton_method_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[singleton_method, index] +singleton_method_child( + int singleton_method: @singleton_method ref, + int index: int ref, + unique int child: @singleton_method_child_type ref +); + +singleton_method_def( + unique int id: @singleton_method, + int name: @underscore_method_name ref, + int object: @singleton_method_object_type ref, + int loc: @location ref +); + +splat_argument_def( + unique int id: @splat_argument, + int child: @underscore_arg ref, + int loc: @location ref +); + +splat_parameter_name( + unique int splat_parameter: @splat_parameter ref, + unique int name: @token_identifier ref +); + +splat_parameter_def( + unique int id: @splat_parameter, + int loc: @location ref +); + +@string_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[string__, index] +string_child( + int string__: @string__ ref, + int index: int ref, + unique int child: @string_child_type ref +); + +string_def( + unique int id: @string__, + int loc: @location ref +); + +#keyset[string_array, index] +string_array_child( + int string_array: @string_array ref, + int index: int ref, + unique int child: @bare_string ref +); + +string_array_def( + unique int id: @string_array, + int loc: @location ref +); + +@subshell_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[subshell, index] +subshell_child( + int subshell: @subshell ref, + int index: int ref, + unique int child: @subshell_child_type ref +); + +subshell_def( + unique int id: @subshell, + int loc: @location ref +); + +@superclass_child_type = @break | @call | @next | @return | @underscore_arg | @yield + +superclass_def( + unique int id: @superclass, + int child: @superclass_child_type ref, + int loc: @location ref +); + +#keyset[symbol_array, index] +symbol_array_child( + int symbol_array: @symbol_array ref, + int index: int ref, + unique int child: @bare_symbol ref +); + +symbol_array_def( + unique int id: @symbol_array, + int loc: @location ref +); + +@then_child_type = @token_empty_statement | @underscore_statement + +#keyset[then, index] +then_child( + int then: @then ref, + int index: int ref, + unique int child: @then_child_type ref +); + +then_def( + unique int id: @then, + int loc: @location ref +); + +@unary_operand_type = @break | @call | @next | @parenthesized_statements | @return | @token_float | @token_integer | @underscore_arg | @yield + +case @unary.operator of + 0 = @unary_bang +| 1 = @unary_plus +| 2 = @unary_minus +| 3 = @unary_definedquestion +| 4 = @unary_not +| 5 = @unary_tilde +; + + +unary_def( + unique int id: @unary, + int operand: @unary_operand_type ref, + int operator: int ref, + int loc: @location ref +); + +#keyset[undef, index] +undef_child( + int undef: @undef ref, + int index: int ref, + unique int child: @underscore_method_name ref +); + +undef_def( + unique int id: @undef, + int loc: @location ref +); + +@unless_alternative_type = @else | @elsif + +unless_alternative( + unique int unless: @unless ref, + unique int alternative: @unless_alternative_type ref +); + +unless_consequence( + unique int unless: @unless ref, + unique int consequence: @then ref +); + +unless_def( + unique int id: @unless, + int condition: @underscore_statement ref, + int loc: @location ref +); + +@unless_modifier_condition_type = @break | @call | @next | @return | @underscore_arg | @yield + +unless_modifier_def( + unique int id: @unless_modifier, + int body: @underscore_statement ref, + int condition: @unless_modifier_condition_type ref, + int loc: @location ref +); + +until_def( + unique int id: @until, + int body: @do ref, + int condition: @underscore_statement ref, + int loc: @location ref +); + +@until_modifier_condition_type = @break | @call | @next | @return | @underscore_arg | @yield + +until_modifier_def( + unique int id: @until_modifier, + int body: @underscore_statement ref, + int condition: @until_modifier_condition_type ref, + int loc: @location ref +); + +when_body( + unique int when: @when ref, + unique int body: @then ref +); + +#keyset[when, index] +when_pattern( + int when: @when ref, + int index: int ref, + unique int pattern: @pattern ref +); + +when_def( + unique int id: @when, + int loc: @location ref +); + +while_def( + unique int id: @while, + int body: @do ref, + int condition: @underscore_statement ref, + int loc: @location ref +); + +@while_modifier_condition_type = @break | @call | @next | @return | @underscore_arg | @yield + +while_modifier_def( + unique int id: @while_modifier, + int body: @underscore_statement ref, + int condition: @while_modifier_condition_type ref, + int loc: @location ref +); + +yield_child( + unique int yield: @yield ref, + unique int child: @argument_list ref +); + +yield_def( + unique int id: @yield, + int loc: @location ref +); + +tokeninfo( + unique int id: @token, + int kind: int ref, + int file: @file ref, + int idx: int ref, + string value: string ref, + int loc: @location ref +); + +case @token.kind of + 0 = @reserved_word +| 1 = @token_character +| 2 = @token_class_variable +| 3 = @token_comment +| 4 = @token_complex +| 5 = @token_constant +| 6 = @token_empty_statement +| 7 = @token_escape_sequence +| 8 = @token_false +| 9 = @token_float +| 10 = @token_global_variable +| 11 = @token_hash_key_symbol +| 12 = @token_heredoc_beginning +| 13 = @token_heredoc_content +| 14 = @token_heredoc_end +| 15 = @token_identifier +| 16 = @token_instance_variable +| 17 = @token_integer +| 18 = @token_nil +| 19 = @token_operator +| 20 = @token_self +| 21 = @token_simple_symbol +| 22 = @token_string_content +| 23 = @token_super +| 24 = @token_true +| 25 = @token_uninterpreted +; + + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +case @diagnostic.severity of + 10 = @diagnostic_debug +| 20 = @diagnostic_info +| 30 = @diagnostic_warning +| 40 = @diagnostic_error +; + + +@ast_node = @alias | @argument_list | @array | @assignment | @bare_string | @bare_symbol | @begin | @begin_block | @binary | @block | @block_argument | @block_parameter | @block_parameters | @break | @call | @case__ | @chained_string | @class | @conditional | @delimited_symbol | @destructured_left_assignment | @destructured_parameter | @do | @do_block | @element_reference | @else | @elsif | @end_block | @ensure | @exception_variable | @exceptions | @for | @hash | @hash_splat_argument | @hash_splat_parameter | @heredoc_body | @if | @if_modifier | @in | @interpolation | @keyword_parameter | @lambda | @lambda_parameters | @left_assignment_list | @method | @method_parameters | @module | @next | @operator_assignment | @optional_parameter | @pair | @parenthesized_statements | @pattern | @program | @range | @rational | @redo | @regex | @rescue | @rescue_modifier | @rest_assignment | @retry | @return | @right_assignment_list | @scope_resolution | @setter | @singleton_class | @singleton_method | @splat_argument | @splat_parameter | @string__ | @string_array | @subshell | @superclass | @symbol_array | @then | @token | @unary | @undef | @unless | @unless_modifier | @until | @until_modifier | @when | @while | @while_modifier | @yield + +@ast_node_parent = @ast_node | @file + +#keyset[parent, parent_index] +ast_node_parent( + int child: @ast_node ref, + int parent: @ast_node_parent ref, + int parent_index: int ref +); + diff --git a/ruby/ql/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/upgrade.properties b/ruby/ql/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/upgrade.properties new file mode 100644 index 00000000000..2261e7e5111 --- /dev/null +++ b/ruby/ql/lib/upgrades/8725deeb2fa6627c45235f18b7c121c35498dac7/upgrade.properties @@ -0,0 +1,2 @@ +description: Create an empty diagnostics table +compatibility: backwards diff --git a/ruby/ql/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/old.dbscheme b/ruby/ql/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/old.dbscheme new file mode 100644 index 00000000000..b5aef9c93ae --- /dev/null +++ b/ruby/ql/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/old.dbscheme @@ -0,0 +1,1320 @@ +// CodeQL database schema for Ruby +// Automatically generated from the tree-sitter grammar; do not edit + +@location = @location_default + +locations_default( + unique int id: @location_default, + int file: @file ref, + int start_line: int ref, + int start_column: int ref, + int end_line: int ref, + int end_column: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +sourceLocationPrefix( + string prefix: string ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +case @diagnostic.severity of + 10 = @diagnostic_debug +| 20 = @diagnostic_info +| 30 = @diagnostic_warning +| 40 = @diagnostic_error +; + + +@ruby_underscore_arg = @ruby_assignment | @ruby_binary | @ruby_conditional | @ruby_operator_assignment | @ruby_range | @ruby_unary | @ruby_underscore_primary + +@ruby_underscore_lhs = @ruby_call | @ruby_element_reference | @ruby_scope_resolution | @ruby_token_false | @ruby_token_nil | @ruby_token_true | @ruby_underscore_variable + +@ruby_underscore_method_name = @ruby_delimited_symbol | @ruby_setter | @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_operator | @ruby_token_simple_symbol + +@ruby_underscore_primary = @ruby_array | @ruby_begin | @ruby_break | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_delimited_symbol | @ruby_for | @ruby_hash | @ruby_if | @ruby_lambda | @ruby_method | @ruby_module | @ruby_next | @ruby_parenthesized_statements | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_retry | @ruby_return | @ruby_singleton_class | @ruby_singleton_method | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_symbol_array | @ruby_token_character | @ruby_token_complex | @ruby_token_float | @ruby_token_heredoc_beginning | @ruby_token_integer | @ruby_token_simple_symbol | @ruby_unary | @ruby_underscore_lhs | @ruby_unless | @ruby_until | @ruby_while | @ruby_yield + +@ruby_underscore_statement = @ruby_alias | @ruby_assignment | @ruby_begin_block | @ruby_binary | @ruby_break | @ruby_call | @ruby_end_block | @ruby_if_modifier | @ruby_next | @ruby_operator_assignment | @ruby_rescue_modifier | @ruby_return | @ruby_unary | @ruby_undef | @ruby_underscore_arg | @ruby_unless_modifier | @ruby_until_modifier | @ruby_while_modifier | @ruby_yield + +@ruby_underscore_variable = @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_self | @ruby_token_super + +ruby_alias_def( + unique int id: @ruby_alias, + int alias: @ruby_underscore_method_name ref, + int name: @ruby_underscore_method_name ref, + int loc: @location ref +); + +@ruby_argument_list_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_argument_list, index] +ruby_argument_list_child( + int ruby_argument_list: @ruby_argument_list ref, + int index: int ref, + unique int child: @ruby_argument_list_child_type ref +); + +ruby_argument_list_def( + unique int id: @ruby_argument_list, + int loc: @location ref +); + +@ruby_array_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_array, index] +ruby_array_child( + int ruby_array: @ruby_array ref, + int index: int ref, + unique int child: @ruby_array_child_type ref +); + +ruby_array_def( + unique int id: @ruby_array, + int loc: @location ref +); + +@ruby_assignment_left_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +@ruby_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_right_assignment_list | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +ruby_assignment_def( + unique int id: @ruby_assignment, + int left: @ruby_assignment_left_type ref, + int right: @ruby_assignment_right_type ref, + int loc: @location ref +); + +@ruby_bare_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_string, index] +ruby_bare_string_child( + int ruby_bare_string: @ruby_bare_string ref, + int index: int ref, + unique int child: @ruby_bare_string_child_type ref +); + +ruby_bare_string_def( + unique int id: @ruby_bare_string, + int loc: @location ref +); + +@ruby_bare_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_symbol, index] +ruby_bare_symbol_child( + int ruby_bare_symbol: @ruby_bare_symbol ref, + int index: int ref, + unique int child: @ruby_bare_symbol_child_type ref +); + +ruby_bare_symbol_def( + unique int id: @ruby_bare_symbol, + int loc: @location ref +); + +@ruby_begin_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin, index] +ruby_begin_child( + int ruby_begin: @ruby_begin ref, + int index: int ref, + unique int child: @ruby_begin_child_type ref +); + +ruby_begin_def( + unique int id: @ruby_begin, + int loc: @location ref +); + +@ruby_begin_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin_block, index] +ruby_begin_block_child( + int ruby_begin_block: @ruby_begin_block ref, + int index: int ref, + unique int child: @ruby_begin_block_child_type ref +); + +ruby_begin_block_def( + unique int id: @ruby_begin_block, + int loc: @location ref +); + +@ruby_binary_left_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +case @ruby_binary.operator of + 0 = @ruby_binary_bangequal +| 1 = @ruby_binary_bangtilde +| 2 = @ruby_binary_percent +| 3 = @ruby_binary_ampersand +| 4 = @ruby_binary_ampersandampersand +| 5 = @ruby_binary_star +| 6 = @ruby_binary_starstar +| 7 = @ruby_binary_plus +| 8 = @ruby_binary_minus +| 9 = @ruby_binary_slash +| 10 = @ruby_binary_langle +| 11 = @ruby_binary_langlelangle +| 12 = @ruby_binary_langleequal +| 13 = @ruby_binary_langleequalrangle +| 14 = @ruby_binary_equalequal +| 15 = @ruby_binary_equalequalequal +| 16 = @ruby_binary_equaltilde +| 17 = @ruby_binary_rangle +| 18 = @ruby_binary_rangleequal +| 19 = @ruby_binary_ranglerangle +| 20 = @ruby_binary_caret +| 21 = @ruby_binary_and +| 22 = @ruby_binary_or +| 23 = @ruby_binary_pipe +| 24 = @ruby_binary_pipepipe +; + + +@ruby_binary_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_binary_def( + unique int id: @ruby_binary, + int left: @ruby_binary_left_type ref, + int operator: int ref, + int right: @ruby_binary_right_type ref, + int loc: @location ref +); + +ruby_block_parameters( + unique int ruby_block: @ruby_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_block, index] +ruby_block_child( + int ruby_block: @ruby_block ref, + int index: int ref, + unique int child: @ruby_block_child_type ref +); + +ruby_block_def( + unique int id: @ruby_block, + int loc: @location ref +); + +ruby_block_argument_def( + unique int id: @ruby_block_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_block_parameter_def( + unique int id: @ruby_block_parameter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_block_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_block_parameters, index] +ruby_block_parameters_child( + int ruby_block_parameters: @ruby_block_parameters ref, + int index: int ref, + unique int child: @ruby_block_parameters_child_type ref +); + +ruby_block_parameters_def( + unique int id: @ruby_block_parameters, + int loc: @location ref +); + +ruby_break_child( + unique int ruby_break: @ruby_break ref, + unique int child: @ruby_argument_list ref +); + +ruby_break_def( + unique int id: @ruby_break, + int loc: @location ref +); + +ruby_call_arguments( + unique int ruby_call: @ruby_call ref, + unique int arguments: @ruby_argument_list ref +); + +@ruby_call_block_type = @ruby_block | @ruby_do_block + +ruby_call_block( + unique int ruby_call: @ruby_call ref, + unique int block: @ruby_call_block_type ref +); + +@ruby_call_method_type = @ruby_argument_list | @ruby_scope_resolution | @ruby_token_operator | @ruby_underscore_variable + +@ruby_call_receiver_type = @ruby_call | @ruby_underscore_primary + +ruby_call_receiver( + unique int ruby_call: @ruby_call ref, + unique int receiver: @ruby_call_receiver_type ref +); + +ruby_call_def( + unique int id: @ruby_call, + int method: @ruby_call_method_type ref, + int loc: @location ref +); + +ruby_case_value( + unique int ruby_case__: @ruby_case__ ref, + unique int value: @ruby_underscore_statement ref +); + +@ruby_case_child_type = @ruby_else | @ruby_when + +#keyset[ruby_case__, index] +ruby_case_child( + int ruby_case__: @ruby_case__ ref, + int index: int ref, + unique int child: @ruby_case_child_type ref +); + +ruby_case_def( + unique int id: @ruby_case__, + int loc: @location ref +); + +#keyset[ruby_chained_string, index] +ruby_chained_string_child( + int ruby_chained_string: @ruby_chained_string ref, + int index: int ref, + unique int child: @ruby_string__ ref +); + +ruby_chained_string_def( + unique int id: @ruby_chained_string, + int loc: @location ref +); + +@ruby_class_name_type = @ruby_scope_resolution | @ruby_token_constant + +ruby_class_superclass( + unique int ruby_class: @ruby_class ref, + unique int superclass: @ruby_superclass ref +); + +@ruby_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_class, index] +ruby_class_child( + int ruby_class: @ruby_class ref, + int index: int ref, + unique int child: @ruby_class_child_type ref +); + +ruby_class_def( + unique int id: @ruby_class, + int name: @ruby_class_name_type ref, + int loc: @location ref +); + +ruby_conditional_def( + unique int id: @ruby_conditional, + int alternative: @ruby_underscore_arg ref, + int condition: @ruby_underscore_arg ref, + int consequence: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_delimited_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_delimited_symbol, index] +ruby_delimited_symbol_child( + int ruby_delimited_symbol: @ruby_delimited_symbol ref, + int index: int ref, + unique int child: @ruby_delimited_symbol_child_type ref +); + +ruby_delimited_symbol_def( + unique int id: @ruby_delimited_symbol, + int loc: @location ref +); + +@ruby_destructured_left_assignment_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_destructured_left_assignment, index] +ruby_destructured_left_assignment_child( + int ruby_destructured_left_assignment: @ruby_destructured_left_assignment ref, + int index: int ref, + unique int child: @ruby_destructured_left_assignment_child_type ref +); + +ruby_destructured_left_assignment_def( + unique int id: @ruby_destructured_left_assignment, + int loc: @location ref +); + +@ruby_destructured_parameter_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_destructured_parameter, index] +ruby_destructured_parameter_child( + int ruby_destructured_parameter: @ruby_destructured_parameter ref, + int index: int ref, + unique int child: @ruby_destructured_parameter_child_type ref +); + +ruby_destructured_parameter_def( + unique int id: @ruby_destructured_parameter, + int loc: @location ref +); + +@ruby_do_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do, index] +ruby_do_child( + int ruby_do: @ruby_do ref, + int index: int ref, + unique int child: @ruby_do_child_type ref +); + +ruby_do_def( + unique int id: @ruby_do, + int loc: @location ref +); + +ruby_do_block_parameters( + unique int ruby_do_block: @ruby_do_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_do_block_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do_block, index] +ruby_do_block_child( + int ruby_do_block: @ruby_do_block ref, + int index: int ref, + unique int child: @ruby_do_block_child_type ref +); + +ruby_do_block_def( + unique int id: @ruby_do_block, + int loc: @location ref +); + +@ruby_element_reference_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_element_reference, index] +ruby_element_reference_child( + int ruby_element_reference: @ruby_element_reference ref, + int index: int ref, + unique int child: @ruby_element_reference_child_type ref +); + +ruby_element_reference_def( + unique int id: @ruby_element_reference, + int object: @ruby_underscore_primary ref, + int loc: @location ref +); + +@ruby_else_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_else, index] +ruby_else_child( + int ruby_else: @ruby_else ref, + int index: int ref, + unique int child: @ruby_else_child_type ref +); + +ruby_else_def( + unique int id: @ruby_else, + int loc: @location ref +); + +@ruby_elsif_alternative_type = @ruby_else | @ruby_elsif + +ruby_elsif_alternative( + unique int ruby_elsif: @ruby_elsif ref, + unique int alternative: @ruby_elsif_alternative_type ref +); + +ruby_elsif_consequence( + unique int ruby_elsif: @ruby_elsif ref, + unique int consequence: @ruby_then ref +); + +ruby_elsif_def( + unique int id: @ruby_elsif, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_end_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_end_block, index] +ruby_end_block_child( + int ruby_end_block: @ruby_end_block ref, + int index: int ref, + unique int child: @ruby_end_block_child_type ref +); + +ruby_end_block_def( + unique int id: @ruby_end_block, + int loc: @location ref +); + +@ruby_ensure_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_ensure, index] +ruby_ensure_child( + int ruby_ensure: @ruby_ensure ref, + int index: int ref, + unique int child: @ruby_ensure_child_type ref +); + +ruby_ensure_def( + unique int id: @ruby_ensure, + int loc: @location ref +); + +ruby_exception_variable_def( + unique int id: @ruby_exception_variable, + int child: @ruby_underscore_lhs ref, + int loc: @location ref +); + +@ruby_exceptions_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_exceptions, index] +ruby_exceptions_child( + int ruby_exceptions: @ruby_exceptions ref, + int index: int ref, + unique int child: @ruby_exceptions_child_type ref +); + +ruby_exceptions_def( + unique int id: @ruby_exceptions, + int loc: @location ref +); + +@ruby_for_pattern_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +ruby_for_def( + unique int id: @ruby_for, + int body: @ruby_do ref, + int pattern: @ruby_for_pattern_type ref, + int value: @ruby_in ref, + int loc: @location ref +); + +@ruby_hash_child_type = @ruby_hash_splat_argument | @ruby_pair + +#keyset[ruby_hash, index] +ruby_hash_child( + int ruby_hash: @ruby_hash ref, + int index: int ref, + unique int child: @ruby_hash_child_type ref +); + +ruby_hash_def( + unique int id: @ruby_hash, + int loc: @location ref +); + +ruby_hash_splat_argument_def( + unique int id: @ruby_hash_splat_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_hash_splat_parameter_name( + unique int ruby_hash_splat_parameter: @ruby_hash_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_hash_splat_parameter_def( + unique int id: @ruby_hash_splat_parameter, + int loc: @location ref +); + +@ruby_heredoc_body_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_heredoc_content | @ruby_token_heredoc_end + +#keyset[ruby_heredoc_body, index] +ruby_heredoc_body_child( + int ruby_heredoc_body: @ruby_heredoc_body ref, + int index: int ref, + unique int child: @ruby_heredoc_body_child_type ref +); + +ruby_heredoc_body_def( + unique int id: @ruby_heredoc_body, + int loc: @location ref +); + +@ruby_if_alternative_type = @ruby_else | @ruby_elsif + +ruby_if_alternative( + unique int ruby_if: @ruby_if ref, + unique int alternative: @ruby_if_alternative_type ref +); + +ruby_if_consequence( + unique int ruby_if: @ruby_if ref, + unique int consequence: @ruby_then ref +); + +ruby_if_def( + unique int id: @ruby_if, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_if_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_if_modifier_def( + unique int id: @ruby_if_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_if_modifier_condition_type ref, + int loc: @location ref +); + +ruby_in_def( + unique int id: @ruby_in, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_interpolation_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_interpolation, index] +ruby_interpolation_child( + int ruby_interpolation: @ruby_interpolation ref, + int index: int ref, + unique int child: @ruby_interpolation_child_type ref +); + +ruby_interpolation_def( + unique int id: @ruby_interpolation, + int loc: @location ref +); + +ruby_keyword_parameter_value( + unique int ruby_keyword_parameter: @ruby_keyword_parameter ref, + unique int value: @ruby_underscore_arg ref +); + +ruby_keyword_parameter_def( + unique int id: @ruby_keyword_parameter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_lambda_body_type = @ruby_block | @ruby_do_block + +ruby_lambda_parameters( + unique int ruby_lambda: @ruby_lambda ref, + unique int parameters: @ruby_lambda_parameters ref +); + +ruby_lambda_def( + unique int id: @ruby_lambda, + int body: @ruby_lambda_body_type ref, + int loc: @location ref +); + +@ruby_lambda_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_lambda_parameters, index] +ruby_lambda_parameters_child( + int ruby_lambda_parameters: @ruby_lambda_parameters ref, + int index: int ref, + unique int child: @ruby_lambda_parameters_child_type ref +); + +ruby_lambda_parameters_def( + unique int id: @ruby_lambda_parameters, + int loc: @location ref +); + +@ruby_left_assignment_list_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_left_assignment_list, index] +ruby_left_assignment_list_child( + int ruby_left_assignment_list: @ruby_left_assignment_list ref, + int index: int ref, + unique int child: @ruby_left_assignment_list_child_type ref +); + +ruby_left_assignment_list_def( + unique int id: @ruby_left_assignment_list, + int loc: @location ref +); + +ruby_method_parameters( + unique int ruby_method: @ruby_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_method, index] +ruby_method_child( + int ruby_method: @ruby_method ref, + int index: int ref, + unique int child: @ruby_method_child_type ref +); + +ruby_method_def( + unique int id: @ruby_method, + int name: @ruby_underscore_method_name ref, + int loc: @location ref +); + +@ruby_method_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_method_parameters, index] +ruby_method_parameters_child( + int ruby_method_parameters: @ruby_method_parameters ref, + int index: int ref, + unique int child: @ruby_method_parameters_child_type ref +); + +ruby_method_parameters_def( + unique int id: @ruby_method_parameters, + int loc: @location ref +); + +@ruby_module_name_type = @ruby_scope_resolution | @ruby_token_constant + +@ruby_module_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_module, index] +ruby_module_child( + int ruby_module: @ruby_module ref, + int index: int ref, + unique int child: @ruby_module_child_type ref +); + +ruby_module_def( + unique int id: @ruby_module, + int name: @ruby_module_name_type ref, + int loc: @location ref +); + +ruby_next_child( + unique int ruby_next: @ruby_next ref, + unique int child: @ruby_argument_list ref +); + +ruby_next_def( + unique int id: @ruby_next, + int loc: @location ref +); + +case @ruby_operator_assignment.operator of + 0 = @ruby_operator_assignment_percentequal +| 1 = @ruby_operator_assignment_ampersandampersandequal +| 2 = @ruby_operator_assignment_ampersandequal +| 3 = @ruby_operator_assignment_starstarequal +| 4 = @ruby_operator_assignment_starequal +| 5 = @ruby_operator_assignment_plusequal +| 6 = @ruby_operator_assignment_minusequal +| 7 = @ruby_operator_assignment_slashequal +| 8 = @ruby_operator_assignment_langlelangleequal +| 9 = @ruby_operator_assignment_ranglerangleequal +| 10 = @ruby_operator_assignment_caretequal +| 11 = @ruby_operator_assignment_pipeequal +| 12 = @ruby_operator_assignment_pipepipeequal +; + + +@ruby_operator_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_operator_assignment_def( + unique int id: @ruby_operator_assignment, + int left: @ruby_underscore_lhs ref, + int operator: int ref, + int right: @ruby_operator_assignment_right_type ref, + int loc: @location ref +); + +ruby_optional_parameter_def( + unique int id: @ruby_optional_parameter, + int name: @ruby_token_identifier ref, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_pair_key_type = @ruby_string__ | @ruby_token_hash_key_symbol | @ruby_underscore_arg + +ruby_pair_def( + unique int id: @ruby_pair, + int key__: @ruby_pair_key_type ref, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_parenthesized_statements_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_parenthesized_statements, index] +ruby_parenthesized_statements_child( + int ruby_parenthesized_statements: @ruby_parenthesized_statements ref, + int index: int ref, + unique int child: @ruby_parenthesized_statements_child_type ref +); + +ruby_parenthesized_statements_def( + unique int id: @ruby_parenthesized_statements, + int loc: @location ref +); + +@ruby_pattern_child_type = @ruby_splat_argument | @ruby_underscore_arg + +ruby_pattern_def( + unique int id: @ruby_pattern, + int child: @ruby_pattern_child_type ref, + int loc: @location ref +); + +@ruby_program_child_type = @ruby_token_empty_statement | @ruby_token_uninterpreted | @ruby_underscore_statement + +#keyset[ruby_program, index] +ruby_program_child( + int ruby_program: @ruby_program ref, + int index: int ref, + unique int child: @ruby_program_child_type ref +); + +ruby_program_def( + unique int id: @ruby_program, + int loc: @location ref +); + +ruby_range_begin( + unique int ruby_range: @ruby_range ref, + unique int begin: @ruby_underscore_arg ref +); + +ruby_range_end( + unique int ruby_range: @ruby_range ref, + unique int end: @ruby_underscore_arg ref +); + +case @ruby_range.operator of + 0 = @ruby_range_dotdot +| 1 = @ruby_range_dotdotdot +; + + +ruby_range_def( + unique int id: @ruby_range, + int operator: int ref, + int loc: @location ref +); + +@ruby_rational_child_type = @ruby_token_float | @ruby_token_integer + +ruby_rational_def( + unique int id: @ruby_rational, + int child: @ruby_rational_child_type ref, + int loc: @location ref +); + +ruby_redo_child( + unique int ruby_redo: @ruby_redo ref, + unique int child: @ruby_argument_list ref +); + +ruby_redo_def( + unique int id: @ruby_redo, + int loc: @location ref +); + +@ruby_regex_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_regex, index] +ruby_regex_child( + int ruby_regex: @ruby_regex ref, + int index: int ref, + unique int child: @ruby_regex_child_type ref +); + +ruby_regex_def( + unique int id: @ruby_regex, + int loc: @location ref +); + +ruby_rescue_body( + unique int ruby_rescue: @ruby_rescue ref, + unique int body: @ruby_then ref +); + +ruby_rescue_exceptions( + unique int ruby_rescue: @ruby_rescue ref, + unique int exceptions: @ruby_exceptions ref +); + +ruby_rescue_variable( + unique int ruby_rescue: @ruby_rescue ref, + unique int variable: @ruby_exception_variable ref +); + +ruby_rescue_def( + unique int id: @ruby_rescue, + int loc: @location ref +); + +@ruby_rescue_modifier_handler_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_rescue_modifier_def( + unique int id: @ruby_rescue_modifier, + int body: @ruby_underscore_statement ref, + int handler: @ruby_rescue_modifier_handler_type ref, + int loc: @location ref +); + +ruby_rest_assignment_child( + unique int ruby_rest_assignment: @ruby_rest_assignment ref, + unique int child: @ruby_underscore_lhs ref +); + +ruby_rest_assignment_def( + unique int id: @ruby_rest_assignment, + int loc: @location ref +); + +ruby_retry_child( + unique int ruby_retry: @ruby_retry ref, + unique int child: @ruby_argument_list ref +); + +ruby_retry_def( + unique int id: @ruby_retry, + int loc: @location ref +); + +ruby_return_child( + unique int ruby_return: @ruby_return ref, + unique int child: @ruby_argument_list ref +); + +ruby_return_def( + unique int id: @ruby_return, + int loc: @location ref +); + +@ruby_right_assignment_list_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_right_assignment_list, index] +ruby_right_assignment_list_child( + int ruby_right_assignment_list: @ruby_right_assignment_list ref, + int index: int ref, + unique int child: @ruby_right_assignment_list_child_type ref +); + +ruby_right_assignment_list_def( + unique int id: @ruby_right_assignment_list, + int loc: @location ref +); + +@ruby_scope_resolution_name_type = @ruby_token_constant | @ruby_token_identifier + +ruby_scope_resolution_scope( + unique int ruby_scope_resolution: @ruby_scope_resolution ref, + unique int scope: @ruby_underscore_primary ref +); + +ruby_scope_resolution_def( + unique int id: @ruby_scope_resolution, + int name: @ruby_scope_resolution_name_type ref, + int loc: @location ref +); + +ruby_setter_def( + unique int id: @ruby_setter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_singleton_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_class, index] +ruby_singleton_class_child( + int ruby_singleton_class: @ruby_singleton_class ref, + int index: int ref, + unique int child: @ruby_singleton_class_child_type ref +); + +ruby_singleton_class_def( + unique int id: @ruby_singleton_class, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_singleton_method_object_type = @ruby_underscore_arg | @ruby_underscore_variable + +ruby_singleton_method_parameters( + unique int ruby_singleton_method: @ruby_singleton_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_singleton_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_method, index] +ruby_singleton_method_child( + int ruby_singleton_method: @ruby_singleton_method ref, + int index: int ref, + unique int child: @ruby_singleton_method_child_type ref +); + +ruby_singleton_method_def( + unique int id: @ruby_singleton_method, + int name: @ruby_underscore_method_name ref, + int object: @ruby_singleton_method_object_type ref, + int loc: @location ref +); + +ruby_splat_argument_def( + unique int id: @ruby_splat_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_splat_parameter_name( + unique int ruby_splat_parameter: @ruby_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_splat_parameter_def( + unique int id: @ruby_splat_parameter, + int loc: @location ref +); + +@ruby_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_string__, index] +ruby_string_child( + int ruby_string__: @ruby_string__ ref, + int index: int ref, + unique int child: @ruby_string_child_type ref +); + +ruby_string_def( + unique int id: @ruby_string__, + int loc: @location ref +); + +#keyset[ruby_string_array, index] +ruby_string_array_child( + int ruby_string_array: @ruby_string_array ref, + int index: int ref, + unique int child: @ruby_bare_string ref +); + +ruby_string_array_def( + unique int id: @ruby_string_array, + int loc: @location ref +); + +@ruby_subshell_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_subshell, index] +ruby_subshell_child( + int ruby_subshell: @ruby_subshell ref, + int index: int ref, + unique int child: @ruby_subshell_child_type ref +); + +ruby_subshell_def( + unique int id: @ruby_subshell, + int loc: @location ref +); + +@ruby_superclass_child_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_superclass_def( + unique int id: @ruby_superclass, + int child: @ruby_superclass_child_type ref, + int loc: @location ref +); + +#keyset[ruby_symbol_array, index] +ruby_symbol_array_child( + int ruby_symbol_array: @ruby_symbol_array ref, + int index: int ref, + unique int child: @ruby_bare_symbol ref +); + +ruby_symbol_array_def( + unique int id: @ruby_symbol_array, + int loc: @location ref +); + +@ruby_then_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_then, index] +ruby_then_child( + int ruby_then: @ruby_then ref, + int index: int ref, + unique int child: @ruby_then_child_type ref +); + +ruby_then_def( + unique int id: @ruby_then, + int loc: @location ref +); + +@ruby_unary_operand_type = @ruby_break | @ruby_call | @ruby_next | @ruby_parenthesized_statements | @ruby_return | @ruby_token_float | @ruby_token_integer | @ruby_underscore_arg | @ruby_yield + +case @ruby_unary.operator of + 0 = @ruby_unary_bang +| 1 = @ruby_unary_plus +| 2 = @ruby_unary_minus +| 3 = @ruby_unary_definedquestion +| 4 = @ruby_unary_not +| 5 = @ruby_unary_tilde +; + + +ruby_unary_def( + unique int id: @ruby_unary, + int operand: @ruby_unary_operand_type ref, + int operator: int ref, + int loc: @location ref +); + +#keyset[ruby_undef, index] +ruby_undef_child( + int ruby_undef: @ruby_undef ref, + int index: int ref, + unique int child: @ruby_underscore_method_name ref +); + +ruby_undef_def( + unique int id: @ruby_undef, + int loc: @location ref +); + +@ruby_unless_alternative_type = @ruby_else | @ruby_elsif + +ruby_unless_alternative( + unique int ruby_unless: @ruby_unless ref, + unique int alternative: @ruby_unless_alternative_type ref +); + +ruby_unless_consequence( + unique int ruby_unless: @ruby_unless ref, + unique int consequence: @ruby_then ref +); + +ruby_unless_def( + unique int id: @ruby_unless, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_unless_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_unless_modifier_def( + unique int id: @ruby_unless_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_unless_modifier_condition_type ref, + int loc: @location ref +); + +ruby_until_def( + unique int id: @ruby_until, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_until_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_until_modifier_def( + unique int id: @ruby_until_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_until_modifier_condition_type ref, + int loc: @location ref +); + +ruby_when_body( + unique int ruby_when: @ruby_when ref, + unique int body: @ruby_then ref +); + +#keyset[ruby_when, index] +ruby_when_pattern( + int ruby_when: @ruby_when ref, + int index: int ref, + unique int pattern: @ruby_pattern ref +); + +ruby_when_def( + unique int id: @ruby_when, + int loc: @location ref +); + +ruby_while_def( + unique int id: @ruby_while, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_while_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_while_modifier_def( + unique int id: @ruby_while_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_while_modifier_condition_type ref, + int loc: @location ref +); + +ruby_yield_child( + unique int ruby_yield: @ruby_yield ref, + unique int child: @ruby_argument_list ref +); + +ruby_yield_def( + unique int id: @ruby_yield, + int loc: @location ref +); + +ruby_tokeninfo( + unique int id: @ruby_token, + int kind: int ref, + int file: @file ref, + int idx: int ref, + string value: string ref, + int loc: @location ref +); + +case @ruby_token.kind of + 0 = @ruby_reserved_word +| 1 = @ruby_token_character +| 2 = @ruby_token_class_variable +| 3 = @ruby_token_comment +| 4 = @ruby_token_complex +| 5 = @ruby_token_constant +| 6 = @ruby_token_empty_statement +| 7 = @ruby_token_escape_sequence +| 8 = @ruby_token_false +| 9 = @ruby_token_float +| 10 = @ruby_token_global_variable +| 11 = @ruby_token_hash_key_symbol +| 12 = @ruby_token_heredoc_beginning +| 13 = @ruby_token_heredoc_content +| 14 = @ruby_token_heredoc_end +| 15 = @ruby_token_identifier +| 16 = @ruby_token_instance_variable +| 17 = @ruby_token_integer +| 18 = @ruby_token_nil +| 19 = @ruby_token_operator +| 20 = @ruby_token_self +| 21 = @ruby_token_simple_symbol +| 22 = @ruby_token_string_content +| 23 = @ruby_token_super +| 24 = @ruby_token_true +| 25 = @ruby_token_uninterpreted +; + + +@ruby_ast_node = @ruby_alias | @ruby_argument_list | @ruby_array | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_parameter | @ruby_block_parameters | @ruby_break | @ruby_call | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_for | @ruby_hash | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_modifier | @ruby_in | @ruby_interpolation | @ruby_keyword_parameter | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield + +@ruby_ast_node_parent = @file | @ruby_ast_node + +#keyset[parent, parent_index] +ruby_ast_node_parent( + int child: @ruby_ast_node ref, + int parent: @ruby_ast_node_parent ref, + int parent_index: int ref +); + +erb_comment_directive_def( + unique int id: @erb_comment_directive, + int child: @erb_token_comment ref, + int loc: @location ref +); + +erb_directive_def( + unique int id: @erb_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +erb_graphql_directive_def( + unique int id: @erb_graphql_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +erb_output_directive_def( + unique int id: @erb_output_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +@erb_template_child_type = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_token_content + +#keyset[erb_template, index] +erb_template_child( + int erb_template: @erb_template ref, + int index: int ref, + unique int child: @erb_template_child_type ref +); + +erb_template_def( + unique int id: @erb_template, + int loc: @location ref +); + +erb_tokeninfo( + unique int id: @erb_token, + int kind: int ref, + int file: @file ref, + int idx: int ref, + string value: string ref, + int loc: @location ref +); + +case @erb_token.kind of + 0 = @erb_reserved_word +| 1 = @erb_token_code +| 2 = @erb_token_comment +| 3 = @erb_token_content +; + + +@erb_ast_node = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_template | @erb_token + +@erb_ast_node_parent = @erb_ast_node | @file + +#keyset[parent, parent_index] +erb_ast_node_parent( + int child: @erb_ast_node ref, + int parent: @erb_ast_node_parent ref, + int parent_index: int ref +); + diff --git a/ruby/ql/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/ruby.dbscheme b/ruby/ql/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/ruby.dbscheme new file mode 100644 index 00000000000..31a238d080f --- /dev/null +++ b/ruby/ql/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/ruby.dbscheme @@ -0,0 +1,1316 @@ +// CodeQL database schema for Ruby +// Automatically generated from the tree-sitter grammar; do not edit + +@location = @location_default + +locations_default( + unique int id: @location_default, + int file: @file ref, + int start_line: int ref, + int start_column: int ref, + int end_line: int ref, + int end_column: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +sourceLocationPrefix( + string prefix: string ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +case @diagnostic.severity of + 10 = @diagnostic_debug +| 20 = @diagnostic_info +| 30 = @diagnostic_warning +| 40 = @diagnostic_error +; + + +@ruby_underscore_arg = @ruby_assignment | @ruby_binary | @ruby_conditional | @ruby_operator_assignment | @ruby_range | @ruby_unary | @ruby_underscore_primary + +@ruby_underscore_lhs = @ruby_call | @ruby_element_reference | @ruby_scope_resolution | @ruby_token_false | @ruby_token_nil | @ruby_token_true | @ruby_underscore_variable + +@ruby_underscore_method_name = @ruby_delimited_symbol | @ruby_setter | @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_operator | @ruby_token_simple_symbol + +@ruby_underscore_primary = @ruby_array | @ruby_begin | @ruby_break | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_delimited_symbol | @ruby_for | @ruby_hash | @ruby_if | @ruby_lambda | @ruby_method | @ruby_module | @ruby_next | @ruby_parenthesized_statements | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_retry | @ruby_return | @ruby_singleton_class | @ruby_singleton_method | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_symbol_array | @ruby_token_character | @ruby_token_complex | @ruby_token_float | @ruby_token_heredoc_beginning | @ruby_token_integer | @ruby_token_simple_symbol | @ruby_unary | @ruby_underscore_lhs | @ruby_unless | @ruby_until | @ruby_while | @ruby_yield + +@ruby_underscore_statement = @ruby_alias | @ruby_assignment | @ruby_begin_block | @ruby_binary | @ruby_break | @ruby_call | @ruby_end_block | @ruby_if_modifier | @ruby_next | @ruby_operator_assignment | @ruby_rescue_modifier | @ruby_return | @ruby_unary | @ruby_undef | @ruby_underscore_arg | @ruby_unless_modifier | @ruby_until_modifier | @ruby_while_modifier | @ruby_yield + +@ruby_underscore_variable = @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_self | @ruby_token_super + +ruby_alias_def( + unique int id: @ruby_alias, + int alias: @ruby_underscore_method_name ref, + int name: @ruby_underscore_method_name ref, + int loc: @location ref +); + +@ruby_argument_list_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_argument_list, index] +ruby_argument_list_child( + int ruby_argument_list: @ruby_argument_list ref, + int index: int ref, + unique int child: @ruby_argument_list_child_type ref +); + +ruby_argument_list_def( + unique int id: @ruby_argument_list, + int loc: @location ref +); + +@ruby_array_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_array, index] +ruby_array_child( + int ruby_array: @ruby_array ref, + int index: int ref, + unique int child: @ruby_array_child_type ref +); + +ruby_array_def( + unique int id: @ruby_array, + int loc: @location ref +); + +@ruby_assignment_left_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +@ruby_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_right_assignment_list | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +ruby_assignment_def( + unique int id: @ruby_assignment, + int left: @ruby_assignment_left_type ref, + int right: @ruby_assignment_right_type ref, + int loc: @location ref +); + +@ruby_bare_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_string, index] +ruby_bare_string_child( + int ruby_bare_string: @ruby_bare_string ref, + int index: int ref, + unique int child: @ruby_bare_string_child_type ref +); + +ruby_bare_string_def( + unique int id: @ruby_bare_string, + int loc: @location ref +); + +@ruby_bare_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_symbol, index] +ruby_bare_symbol_child( + int ruby_bare_symbol: @ruby_bare_symbol ref, + int index: int ref, + unique int child: @ruby_bare_symbol_child_type ref +); + +ruby_bare_symbol_def( + unique int id: @ruby_bare_symbol, + int loc: @location ref +); + +@ruby_begin_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin, index] +ruby_begin_child( + int ruby_begin: @ruby_begin ref, + int index: int ref, + unique int child: @ruby_begin_child_type ref +); + +ruby_begin_def( + unique int id: @ruby_begin, + int loc: @location ref +); + +@ruby_begin_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin_block, index] +ruby_begin_block_child( + int ruby_begin_block: @ruby_begin_block ref, + int index: int ref, + unique int child: @ruby_begin_block_child_type ref +); + +ruby_begin_block_def( + unique int id: @ruby_begin_block, + int loc: @location ref +); + +@ruby_binary_left_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +case @ruby_binary.operator of + 0 = @ruby_binary_bangequal +| 1 = @ruby_binary_bangtilde +| 2 = @ruby_binary_percent +| 3 = @ruby_binary_ampersand +| 4 = @ruby_binary_ampersandampersand +| 5 = @ruby_binary_star +| 6 = @ruby_binary_starstar +| 7 = @ruby_binary_plus +| 8 = @ruby_binary_minus +| 9 = @ruby_binary_slash +| 10 = @ruby_binary_langle +| 11 = @ruby_binary_langlelangle +| 12 = @ruby_binary_langleequal +| 13 = @ruby_binary_langleequalrangle +| 14 = @ruby_binary_equalequal +| 15 = @ruby_binary_equalequalequal +| 16 = @ruby_binary_equaltilde +| 17 = @ruby_binary_rangle +| 18 = @ruby_binary_rangleequal +| 19 = @ruby_binary_ranglerangle +| 20 = @ruby_binary_caret +| 21 = @ruby_binary_and +| 22 = @ruby_binary_or +| 23 = @ruby_binary_pipe +| 24 = @ruby_binary_pipepipe +; + + +@ruby_binary_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_binary_def( + unique int id: @ruby_binary, + int left: @ruby_binary_left_type ref, + int operator: int ref, + int right: @ruby_binary_right_type ref, + int loc: @location ref +); + +ruby_block_parameters( + unique int ruby_block: @ruby_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_block, index] +ruby_block_child( + int ruby_block: @ruby_block ref, + int index: int ref, + unique int child: @ruby_block_child_type ref +); + +ruby_block_def( + unique int id: @ruby_block, + int loc: @location ref +); + +ruby_block_argument_def( + unique int id: @ruby_block_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_block_parameter_def( + unique int id: @ruby_block_parameter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_block_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_block_parameters, index] +ruby_block_parameters_child( + int ruby_block_parameters: @ruby_block_parameters ref, + int index: int ref, + unique int child: @ruby_block_parameters_child_type ref +); + +ruby_block_parameters_def( + unique int id: @ruby_block_parameters, + int loc: @location ref +); + +ruby_break_child( + unique int ruby_break: @ruby_break ref, + unique int child: @ruby_argument_list ref +); + +ruby_break_def( + unique int id: @ruby_break, + int loc: @location ref +); + +ruby_call_arguments( + unique int ruby_call: @ruby_call ref, + unique int arguments: @ruby_argument_list ref +); + +@ruby_call_block_type = @ruby_block | @ruby_do_block + +ruby_call_block( + unique int ruby_call: @ruby_call ref, + unique int block: @ruby_call_block_type ref +); + +@ruby_call_method_type = @ruby_argument_list | @ruby_scope_resolution | @ruby_token_operator | @ruby_underscore_variable + +@ruby_call_receiver_type = @ruby_call | @ruby_underscore_primary + +ruby_call_receiver( + unique int ruby_call: @ruby_call ref, + unique int receiver: @ruby_call_receiver_type ref +); + +ruby_call_def( + unique int id: @ruby_call, + int method: @ruby_call_method_type ref, + int loc: @location ref +); + +ruby_case_value( + unique int ruby_case__: @ruby_case__ ref, + unique int value: @ruby_underscore_statement ref +); + +@ruby_case_child_type = @ruby_else | @ruby_when + +#keyset[ruby_case__, index] +ruby_case_child( + int ruby_case__: @ruby_case__ ref, + int index: int ref, + unique int child: @ruby_case_child_type ref +); + +ruby_case_def( + unique int id: @ruby_case__, + int loc: @location ref +); + +#keyset[ruby_chained_string, index] +ruby_chained_string_child( + int ruby_chained_string: @ruby_chained_string ref, + int index: int ref, + unique int child: @ruby_string__ ref +); + +ruby_chained_string_def( + unique int id: @ruby_chained_string, + int loc: @location ref +); + +@ruby_class_name_type = @ruby_scope_resolution | @ruby_token_constant + +ruby_class_superclass( + unique int ruby_class: @ruby_class ref, + unique int superclass: @ruby_superclass ref +); + +@ruby_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_class, index] +ruby_class_child( + int ruby_class: @ruby_class ref, + int index: int ref, + unique int child: @ruby_class_child_type ref +); + +ruby_class_def( + unique int id: @ruby_class, + int name: @ruby_class_name_type ref, + int loc: @location ref +); + +ruby_conditional_def( + unique int id: @ruby_conditional, + int alternative: @ruby_underscore_arg ref, + int condition: @ruby_underscore_arg ref, + int consequence: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_delimited_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_delimited_symbol, index] +ruby_delimited_symbol_child( + int ruby_delimited_symbol: @ruby_delimited_symbol ref, + int index: int ref, + unique int child: @ruby_delimited_symbol_child_type ref +); + +ruby_delimited_symbol_def( + unique int id: @ruby_delimited_symbol, + int loc: @location ref +); + +@ruby_destructured_left_assignment_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_destructured_left_assignment, index] +ruby_destructured_left_assignment_child( + int ruby_destructured_left_assignment: @ruby_destructured_left_assignment ref, + int index: int ref, + unique int child: @ruby_destructured_left_assignment_child_type ref +); + +ruby_destructured_left_assignment_def( + unique int id: @ruby_destructured_left_assignment, + int loc: @location ref +); + +@ruby_destructured_parameter_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_destructured_parameter, index] +ruby_destructured_parameter_child( + int ruby_destructured_parameter: @ruby_destructured_parameter ref, + int index: int ref, + unique int child: @ruby_destructured_parameter_child_type ref +); + +ruby_destructured_parameter_def( + unique int id: @ruby_destructured_parameter, + int loc: @location ref +); + +@ruby_do_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do, index] +ruby_do_child( + int ruby_do: @ruby_do ref, + int index: int ref, + unique int child: @ruby_do_child_type ref +); + +ruby_do_def( + unique int id: @ruby_do, + int loc: @location ref +); + +ruby_do_block_parameters( + unique int ruby_do_block: @ruby_do_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_do_block_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do_block, index] +ruby_do_block_child( + int ruby_do_block: @ruby_do_block ref, + int index: int ref, + unique int child: @ruby_do_block_child_type ref +); + +ruby_do_block_def( + unique int id: @ruby_do_block, + int loc: @location ref +); + +@ruby_element_reference_child_type = @ruby_block_argument | @ruby_break | @ruby_call | @ruby_hash_splat_argument | @ruby_next | @ruby_pair | @ruby_return | @ruby_splat_argument | @ruby_underscore_arg | @ruby_yield + +#keyset[ruby_element_reference, index] +ruby_element_reference_child( + int ruby_element_reference: @ruby_element_reference ref, + int index: int ref, + unique int child: @ruby_element_reference_child_type ref +); + +ruby_element_reference_def( + unique int id: @ruby_element_reference, + int object: @ruby_underscore_primary ref, + int loc: @location ref +); + +@ruby_else_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_else, index] +ruby_else_child( + int ruby_else: @ruby_else ref, + int index: int ref, + unique int child: @ruby_else_child_type ref +); + +ruby_else_def( + unique int id: @ruby_else, + int loc: @location ref +); + +@ruby_elsif_alternative_type = @ruby_else | @ruby_elsif + +ruby_elsif_alternative( + unique int ruby_elsif: @ruby_elsif ref, + unique int alternative: @ruby_elsif_alternative_type ref +); + +ruby_elsif_consequence( + unique int ruby_elsif: @ruby_elsif ref, + unique int consequence: @ruby_then ref +); + +ruby_elsif_def( + unique int id: @ruby_elsif, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_end_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_end_block, index] +ruby_end_block_child( + int ruby_end_block: @ruby_end_block ref, + int index: int ref, + unique int child: @ruby_end_block_child_type ref +); + +ruby_end_block_def( + unique int id: @ruby_end_block, + int loc: @location ref +); + +@ruby_ensure_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_ensure, index] +ruby_ensure_child( + int ruby_ensure: @ruby_ensure ref, + int index: int ref, + unique int child: @ruby_ensure_child_type ref +); + +ruby_ensure_def( + unique int id: @ruby_ensure, + int loc: @location ref +); + +ruby_exception_variable_def( + unique int id: @ruby_exception_variable, + int child: @ruby_underscore_lhs ref, + int loc: @location ref +); + +@ruby_exceptions_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_exceptions, index] +ruby_exceptions_child( + int ruby_exceptions: @ruby_exceptions ref, + int index: int ref, + unique int child: @ruby_exceptions_child_type ref +); + +ruby_exceptions_def( + unique int id: @ruby_exceptions, + int loc: @location ref +); + +@ruby_for_pattern_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +ruby_for_def( + unique int id: @ruby_for, + int body: @ruby_do ref, + int pattern: @ruby_for_pattern_type ref, + int value: @ruby_in ref, + int loc: @location ref +); + +@ruby_hash_child_type = @ruby_hash_splat_argument | @ruby_pair + +#keyset[ruby_hash, index] +ruby_hash_child( + int ruby_hash: @ruby_hash ref, + int index: int ref, + unique int child: @ruby_hash_child_type ref +); + +ruby_hash_def( + unique int id: @ruby_hash, + int loc: @location ref +); + +ruby_hash_splat_argument_def( + unique int id: @ruby_hash_splat_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_hash_splat_parameter_name( + unique int ruby_hash_splat_parameter: @ruby_hash_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_hash_splat_parameter_def( + unique int id: @ruby_hash_splat_parameter, + int loc: @location ref +); + +@ruby_heredoc_body_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_heredoc_content | @ruby_token_heredoc_end + +#keyset[ruby_heredoc_body, index] +ruby_heredoc_body_child( + int ruby_heredoc_body: @ruby_heredoc_body ref, + int index: int ref, + unique int child: @ruby_heredoc_body_child_type ref +); + +ruby_heredoc_body_def( + unique int id: @ruby_heredoc_body, + int loc: @location ref +); + +@ruby_if_alternative_type = @ruby_else | @ruby_elsif + +ruby_if_alternative( + unique int ruby_if: @ruby_if ref, + unique int alternative: @ruby_if_alternative_type ref +); + +ruby_if_consequence( + unique int ruby_if: @ruby_if ref, + unique int consequence: @ruby_then ref +); + +ruby_if_def( + unique int id: @ruby_if, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_if_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_if_modifier_def( + unique int id: @ruby_if_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_if_modifier_condition_type ref, + int loc: @location ref +); + +ruby_in_def( + unique int id: @ruby_in, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_interpolation_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_interpolation, index] +ruby_interpolation_child( + int ruby_interpolation: @ruby_interpolation ref, + int index: int ref, + unique int child: @ruby_interpolation_child_type ref +); + +ruby_interpolation_def( + unique int id: @ruby_interpolation, + int loc: @location ref +); + +ruby_keyword_parameter_value( + unique int ruby_keyword_parameter: @ruby_keyword_parameter ref, + unique int value: @ruby_underscore_arg ref +); + +ruby_keyword_parameter_def( + unique int id: @ruby_keyword_parameter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_lambda_body_type = @ruby_block | @ruby_do_block + +ruby_lambda_parameters( + unique int ruby_lambda: @ruby_lambda ref, + unique int parameters: @ruby_lambda_parameters ref +); + +ruby_lambda_def( + unique int id: @ruby_lambda, + int body: @ruby_lambda_body_type ref, + int loc: @location ref +); + +@ruby_lambda_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_lambda_parameters, index] +ruby_lambda_parameters_child( + int ruby_lambda_parameters: @ruby_lambda_parameters ref, + int index: int ref, + unique int child: @ruby_lambda_parameters_child_type ref +); + +ruby_lambda_parameters_def( + unique int id: @ruby_lambda_parameters, + int loc: @location ref +); + +@ruby_left_assignment_list_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_left_assignment_list, index] +ruby_left_assignment_list_child( + int ruby_left_assignment_list: @ruby_left_assignment_list ref, + int index: int ref, + unique int child: @ruby_left_assignment_list_child_type ref +); + +ruby_left_assignment_list_def( + unique int id: @ruby_left_assignment_list, + int loc: @location ref +); + +ruby_method_parameters( + unique int ruby_method: @ruby_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_method, index] +ruby_method_child( + int ruby_method: @ruby_method ref, + int index: int ref, + unique int child: @ruby_method_child_type ref +); + +ruby_method_def( + unique int id: @ruby_method, + int name: @ruby_underscore_method_name ref, + int loc: @location ref +); + +@ruby_method_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_identifier + +#keyset[ruby_method_parameters, index] +ruby_method_parameters_child( + int ruby_method_parameters: @ruby_method_parameters ref, + int index: int ref, + unique int child: @ruby_method_parameters_child_type ref +); + +ruby_method_parameters_def( + unique int id: @ruby_method_parameters, + int loc: @location ref +); + +@ruby_module_name_type = @ruby_scope_resolution | @ruby_token_constant + +@ruby_module_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_module, index] +ruby_module_child( + int ruby_module: @ruby_module ref, + int index: int ref, + unique int child: @ruby_module_child_type ref +); + +ruby_module_def( + unique int id: @ruby_module, + int name: @ruby_module_name_type ref, + int loc: @location ref +); + +ruby_next_child( + unique int ruby_next: @ruby_next ref, + unique int child: @ruby_argument_list ref +); + +ruby_next_def( + unique int id: @ruby_next, + int loc: @location ref +); + +case @ruby_operator_assignment.operator of + 0 = @ruby_operator_assignment_percentequal +| 1 = @ruby_operator_assignment_ampersandampersandequal +| 2 = @ruby_operator_assignment_ampersandequal +| 3 = @ruby_operator_assignment_starstarequal +| 4 = @ruby_operator_assignment_starequal +| 5 = @ruby_operator_assignment_plusequal +| 6 = @ruby_operator_assignment_minusequal +| 7 = @ruby_operator_assignment_slashequal +| 8 = @ruby_operator_assignment_langlelangleequal +| 9 = @ruby_operator_assignment_ranglerangleequal +| 10 = @ruby_operator_assignment_caretequal +| 11 = @ruby_operator_assignment_pipeequal +| 12 = @ruby_operator_assignment_pipepipeequal +; + + +@ruby_operator_assignment_right_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_operator_assignment_def( + unique int id: @ruby_operator_assignment, + int left: @ruby_underscore_lhs ref, + int operator: int ref, + int right: @ruby_operator_assignment_right_type ref, + int loc: @location ref +); + +ruby_optional_parameter_def( + unique int id: @ruby_optional_parameter, + int name: @ruby_token_identifier ref, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_pair_key_type = @ruby_string__ | @ruby_token_hash_key_symbol | @ruby_underscore_arg + +ruby_pair_def( + unique int id: @ruby_pair, + int key__: @ruby_pair_key_type ref, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_parenthesized_statements_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_parenthesized_statements, index] +ruby_parenthesized_statements_child( + int ruby_parenthesized_statements: @ruby_parenthesized_statements ref, + int index: int ref, + unique int child: @ruby_parenthesized_statements_child_type ref +); + +ruby_parenthesized_statements_def( + unique int id: @ruby_parenthesized_statements, + int loc: @location ref +); + +@ruby_pattern_child_type = @ruby_splat_argument | @ruby_underscore_arg + +ruby_pattern_def( + unique int id: @ruby_pattern, + int child: @ruby_pattern_child_type ref, + int loc: @location ref +); + +@ruby_program_child_type = @ruby_token_empty_statement | @ruby_token_uninterpreted | @ruby_underscore_statement + +#keyset[ruby_program, index] +ruby_program_child( + int ruby_program: @ruby_program ref, + int index: int ref, + unique int child: @ruby_program_child_type ref +); + +ruby_program_def( + unique int id: @ruby_program, + int loc: @location ref +); + +ruby_range_begin( + unique int ruby_range: @ruby_range ref, + unique int begin: @ruby_underscore_arg ref +); + +ruby_range_end( + unique int ruby_range: @ruby_range ref, + unique int end: @ruby_underscore_arg ref +); + +case @ruby_range.operator of + 0 = @ruby_range_dotdot +| 1 = @ruby_range_dotdotdot +; + + +ruby_range_def( + unique int id: @ruby_range, + int operator: int ref, + int loc: @location ref +); + +@ruby_rational_child_type = @ruby_token_float | @ruby_token_integer + +ruby_rational_def( + unique int id: @ruby_rational, + int child: @ruby_rational_child_type ref, + int loc: @location ref +); + +ruby_redo_child( + unique int ruby_redo: @ruby_redo ref, + unique int child: @ruby_argument_list ref +); + +ruby_redo_def( + unique int id: @ruby_redo, + int loc: @location ref +); + +@ruby_regex_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_regex, index] +ruby_regex_child( + int ruby_regex: @ruby_regex ref, + int index: int ref, + unique int child: @ruby_regex_child_type ref +); + +ruby_regex_def( + unique int id: @ruby_regex, + int loc: @location ref +); + +ruby_rescue_body( + unique int ruby_rescue: @ruby_rescue ref, + unique int body: @ruby_then ref +); + +ruby_rescue_exceptions( + unique int ruby_rescue: @ruby_rescue ref, + unique int exceptions: @ruby_exceptions ref +); + +ruby_rescue_variable( + unique int ruby_rescue: @ruby_rescue ref, + unique int variable: @ruby_exception_variable ref +); + +ruby_rescue_def( + unique int id: @ruby_rescue, + int loc: @location ref +); + +@ruby_rescue_modifier_handler_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_rescue_modifier_def( + unique int id: @ruby_rescue_modifier, + int body: @ruby_underscore_statement ref, + int handler: @ruby_rescue_modifier_handler_type ref, + int loc: @location ref +); + +ruby_rest_assignment_child( + unique int ruby_rest_assignment: @ruby_rest_assignment ref, + unique int child: @ruby_underscore_lhs ref +); + +ruby_rest_assignment_def( + unique int id: @ruby_rest_assignment, + int loc: @location ref +); + +ruby_retry_child( + unique int ruby_retry: @ruby_retry ref, + unique int child: @ruby_argument_list ref +); + +ruby_retry_def( + unique int id: @ruby_retry, + int loc: @location ref +); + +ruby_return_child( + unique int ruby_return: @ruby_return ref, + unique int child: @ruby_argument_list ref +); + +ruby_return_def( + unique int id: @ruby_return, + int loc: @location ref +); + +@ruby_right_assignment_list_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_right_assignment_list, index] +ruby_right_assignment_list_child( + int ruby_right_assignment_list: @ruby_right_assignment_list ref, + int index: int ref, + unique int child: @ruby_right_assignment_list_child_type ref +); + +ruby_right_assignment_list_def( + unique int id: @ruby_right_assignment_list, + int loc: @location ref +); + +@ruby_scope_resolution_name_type = @ruby_token_constant | @ruby_token_identifier + +ruby_scope_resolution_scope( + unique int ruby_scope_resolution: @ruby_scope_resolution ref, + unique int scope: @ruby_underscore_primary ref +); + +ruby_scope_resolution_def( + unique int id: @ruby_scope_resolution, + int name: @ruby_scope_resolution_name_type ref, + int loc: @location ref +); + +ruby_setter_def( + unique int id: @ruby_setter, + int name: @ruby_token_identifier ref, + int loc: @location ref +); + +@ruby_singleton_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_class, index] +ruby_singleton_class_child( + int ruby_singleton_class: @ruby_singleton_class ref, + int index: int ref, + unique int child: @ruby_singleton_class_child_type ref +); + +ruby_singleton_class_def( + unique int id: @ruby_singleton_class, + int value: @ruby_underscore_arg ref, + int loc: @location ref +); + +@ruby_singleton_method_object_type = @ruby_underscore_arg | @ruby_underscore_variable + +ruby_singleton_method_parameters( + unique int ruby_singleton_method: @ruby_singleton_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_singleton_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_method, index] +ruby_singleton_method_child( + int ruby_singleton_method: @ruby_singleton_method ref, + int index: int ref, + unique int child: @ruby_singleton_method_child_type ref +); + +ruby_singleton_method_def( + unique int id: @ruby_singleton_method, + int name: @ruby_underscore_method_name ref, + int object: @ruby_singleton_method_object_type ref, + int loc: @location ref +); + +ruby_splat_argument_def( + unique int id: @ruby_splat_argument, + int child: @ruby_underscore_arg ref, + int loc: @location ref +); + +ruby_splat_parameter_name( + unique int ruby_splat_parameter: @ruby_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_splat_parameter_def( + unique int id: @ruby_splat_parameter, + int loc: @location ref +); + +@ruby_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_string__, index] +ruby_string_child( + int ruby_string__: @ruby_string__ ref, + int index: int ref, + unique int child: @ruby_string_child_type ref +); + +ruby_string_def( + unique int id: @ruby_string__, + int loc: @location ref +); + +#keyset[ruby_string_array, index] +ruby_string_array_child( + int ruby_string_array: @ruby_string_array ref, + int index: int ref, + unique int child: @ruby_bare_string ref +); + +ruby_string_array_def( + unique int id: @ruby_string_array, + int loc: @location ref +); + +@ruby_subshell_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_subshell, index] +ruby_subshell_child( + int ruby_subshell: @ruby_subshell ref, + int index: int ref, + unique int child: @ruby_subshell_child_type ref +); + +ruby_subshell_def( + unique int id: @ruby_subshell, + int loc: @location ref +); + +@ruby_superclass_child_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_superclass_def( + unique int id: @ruby_superclass, + int child: @ruby_superclass_child_type ref, + int loc: @location ref +); + +#keyset[ruby_symbol_array, index] +ruby_symbol_array_child( + int ruby_symbol_array: @ruby_symbol_array ref, + int index: int ref, + unique int child: @ruby_bare_symbol ref +); + +ruby_symbol_array_def( + unique int id: @ruby_symbol_array, + int loc: @location ref +); + +@ruby_then_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_then, index] +ruby_then_child( + int ruby_then: @ruby_then ref, + int index: int ref, + unique int child: @ruby_then_child_type ref +); + +ruby_then_def( + unique int id: @ruby_then, + int loc: @location ref +); + +@ruby_unary_operand_type = @ruby_break | @ruby_call | @ruby_next | @ruby_parenthesized_statements | @ruby_return | @ruby_token_float | @ruby_token_integer | @ruby_underscore_arg | @ruby_yield + +case @ruby_unary.operator of + 0 = @ruby_unary_bang +| 1 = @ruby_unary_plus +| 2 = @ruby_unary_minus +| 3 = @ruby_unary_definedquestion +| 4 = @ruby_unary_not +| 5 = @ruby_unary_tilde +; + + +ruby_unary_def( + unique int id: @ruby_unary, + int operand: @ruby_unary_operand_type ref, + int operator: int ref, + int loc: @location ref +); + +#keyset[ruby_undef, index] +ruby_undef_child( + int ruby_undef: @ruby_undef ref, + int index: int ref, + unique int child: @ruby_underscore_method_name ref +); + +ruby_undef_def( + unique int id: @ruby_undef, + int loc: @location ref +); + +@ruby_unless_alternative_type = @ruby_else | @ruby_elsif + +ruby_unless_alternative( + unique int ruby_unless: @ruby_unless ref, + unique int alternative: @ruby_unless_alternative_type ref +); + +ruby_unless_consequence( + unique int ruby_unless: @ruby_unless ref, + unique int consequence: @ruby_then ref +); + +ruby_unless_def( + unique int id: @ruby_unless, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_unless_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_unless_modifier_def( + unique int id: @ruby_unless_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_unless_modifier_condition_type ref, + int loc: @location ref +); + +ruby_until_def( + unique int id: @ruby_until, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_until_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_until_modifier_def( + unique int id: @ruby_until_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_until_modifier_condition_type ref, + int loc: @location ref +); + +ruby_when_body( + unique int ruby_when: @ruby_when ref, + unique int body: @ruby_then ref +); + +#keyset[ruby_when, index] +ruby_when_pattern( + int ruby_when: @ruby_when ref, + int index: int ref, + unique int pattern: @ruby_pattern ref +); + +ruby_when_def( + unique int id: @ruby_when, + int loc: @location ref +); + +ruby_while_def( + unique int id: @ruby_while, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref, + int loc: @location ref +); + +@ruby_while_modifier_condition_type = @ruby_break | @ruby_call | @ruby_next | @ruby_return | @ruby_underscore_arg | @ruby_yield + +ruby_while_modifier_def( + unique int id: @ruby_while_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_while_modifier_condition_type ref, + int loc: @location ref +); + +ruby_yield_child( + unique int ruby_yield: @ruby_yield ref, + unique int child: @ruby_argument_list ref +); + +ruby_yield_def( + unique int id: @ruby_yield, + int loc: @location ref +); + +ruby_tokeninfo( + unique int id: @ruby_token, + int kind: int ref, + string value: string ref, + int loc: @location ref +); + +case @ruby_token.kind of + 0 = @ruby_reserved_word +| 1 = @ruby_token_character +| 2 = @ruby_token_class_variable +| 3 = @ruby_token_comment +| 4 = @ruby_token_complex +| 5 = @ruby_token_constant +| 6 = @ruby_token_empty_statement +| 7 = @ruby_token_escape_sequence +| 8 = @ruby_token_false +| 9 = @ruby_token_float +| 10 = @ruby_token_global_variable +| 11 = @ruby_token_hash_key_symbol +| 12 = @ruby_token_heredoc_beginning +| 13 = @ruby_token_heredoc_content +| 14 = @ruby_token_heredoc_end +| 15 = @ruby_token_identifier +| 16 = @ruby_token_instance_variable +| 17 = @ruby_token_integer +| 18 = @ruby_token_nil +| 19 = @ruby_token_operator +| 20 = @ruby_token_self +| 21 = @ruby_token_simple_symbol +| 22 = @ruby_token_string_content +| 23 = @ruby_token_super +| 24 = @ruby_token_true +| 25 = @ruby_token_uninterpreted +; + + +@ruby_ast_node = @ruby_alias | @ruby_argument_list | @ruby_array | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_parameter | @ruby_block_parameters | @ruby_break | @ruby_call | @ruby_case__ | @ruby_chained_string | @ruby_class | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_for | @ruby_hash | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_modifier | @ruby_in | @ruby_interpolation | @ruby_keyword_parameter | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield + +@ruby_ast_node_parent = @file | @ruby_ast_node + +#keyset[parent, parent_index] +ruby_ast_node_parent( + int child: @ruby_ast_node ref, + int parent: @ruby_ast_node_parent ref, + int parent_index: int ref +); + +erb_comment_directive_def( + unique int id: @erb_comment_directive, + int child: @erb_token_comment ref, + int loc: @location ref +); + +erb_directive_def( + unique int id: @erb_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +erb_graphql_directive_def( + unique int id: @erb_graphql_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +erb_output_directive_def( + unique int id: @erb_output_directive, + int child: @erb_token_code ref, + int loc: @location ref +); + +@erb_template_child_type = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_token_content + +#keyset[erb_template, index] +erb_template_child( + int erb_template: @erb_template ref, + int index: int ref, + unique int child: @erb_template_child_type ref +); + +erb_template_def( + unique int id: @erb_template, + int loc: @location ref +); + +erb_tokeninfo( + unique int id: @erb_token, + int kind: int ref, + string value: string ref, + int loc: @location ref +); + +case @erb_token.kind of + 0 = @erb_reserved_word +| 1 = @erb_token_code +| 2 = @erb_token_comment +| 3 = @erb_token_content +; + + +@erb_ast_node = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_template | @erb_token + +@erb_ast_node_parent = @erb_ast_node | @file + +#keyset[parent, parent_index] +erb_ast_node_parent( + int child: @erb_ast_node ref, + int parent: @erb_ast_node_parent ref, + int parent_index: int ref +); + diff --git a/ruby/ql/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/upgrade.properties b/ruby/ql/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/upgrade.properties new file mode 100644 index 00000000000..b3c3c7ccf63 --- /dev/null +++ b/ruby/ql/lib/upgrades/b5aef9c93ae64f848017d2dcb760eed916ab0cdd/upgrade.properties @@ -0,0 +1,4 @@ +description: Removed unused columns from the `*_tokeninfo` relations +compatibility: full +ruby_tokeninfo.rel: reorder ruby_tokeninfo.rel (int id, int kind, int file, int idx, string value, int loc) id kind value loc +erb_tokeninfo.rel: reorder erb_tokeninfo.rel (int id, int kind, int file, int idx, string value, int loc) id kind value loc diff --git a/ruby/ql/lib/upgrades/initial/ruby.dbscheme b/ruby/ql/lib/upgrades/initial/ruby.dbscheme new file mode 100644 index 00000000000..8725deeb2fa --- /dev/null +++ b/ruby/ql/lib/upgrades/initial/ruby.dbscheme @@ -0,0 +1,1250 @@ +// CodeQL database schema for Ruby +// Automatically generated from the tree-sitter grammar; do not edit + +@location = @location_default + +locations_default( + unique int id: @location_default, + int file: @file ref, + int start_line: int ref, + int start_column: int ref, + int end_line: int ref, + int end_column: int ref +); + +@sourceline = @file + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref +); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +sourceLocationPrefix( + string prefix: string ref +); + +@underscore_arg = @assignment | @binary | @conditional | @operator_assignment | @range | @unary | @underscore_primary + +@underscore_lhs = @call | @element_reference | @scope_resolution | @token_false | @token_nil | @token_true | @underscore_variable + +@underscore_method_name = @delimited_symbol | @setter | @token_class_variable | @token_constant | @token_global_variable | @token_identifier | @token_instance_variable | @token_operator | @token_simple_symbol + +@underscore_primary = @array | @begin | @break | @case__ | @chained_string | @class | @delimited_symbol | @for | @hash | @if | @lambda | @method | @module | @next | @parenthesized_statements | @rational | @redo | @regex | @retry | @return | @singleton_class | @singleton_method | @string__ | @string_array | @subshell | @symbol_array | @token_character | @token_complex | @token_float | @token_heredoc_beginning | @token_integer | @token_simple_symbol | @unary | @underscore_lhs | @unless | @until | @while | @yield + +@underscore_statement = @alias | @assignment | @begin_block | @binary | @break | @call | @end_block | @if_modifier | @next | @operator_assignment | @rescue_modifier | @return | @unary | @undef | @underscore_arg | @unless_modifier | @until_modifier | @while_modifier | @yield + +@underscore_variable = @token_class_variable | @token_constant | @token_global_variable | @token_identifier | @token_instance_variable | @token_self | @token_super + +alias_def( + unique int id: @alias, + int alias: @underscore_method_name ref, + int name: @underscore_method_name ref, + int loc: @location ref +); + +@argument_list_child_type = @block_argument | @break | @call | @hash_splat_argument | @next | @pair | @return | @splat_argument | @underscore_arg | @yield + +#keyset[argument_list, index] +argument_list_child( + int argument_list: @argument_list ref, + int index: int ref, + unique int child: @argument_list_child_type ref +); + +argument_list_def( + unique int id: @argument_list, + int loc: @location ref +); + +@array_child_type = @block_argument | @break | @call | @hash_splat_argument | @next | @pair | @return | @splat_argument | @underscore_arg | @yield + +#keyset[array, index] +array_child( + int array: @array ref, + int index: int ref, + unique int child: @array_child_type ref +); + +array_def( + unique int id: @array, + int loc: @location ref +); + +@assignment_left_type = @left_assignment_list | @underscore_lhs + +@assignment_right_type = @break | @call | @next | @return | @right_assignment_list | @splat_argument | @underscore_arg | @yield + +assignment_def( + unique int id: @assignment, + int left: @assignment_left_type ref, + int right: @assignment_right_type ref, + int loc: @location ref +); + +@bare_string_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[bare_string, index] +bare_string_child( + int bare_string: @bare_string ref, + int index: int ref, + unique int child: @bare_string_child_type ref +); + +bare_string_def( + unique int id: @bare_string, + int loc: @location ref +); + +@bare_symbol_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[bare_symbol, index] +bare_symbol_child( + int bare_symbol: @bare_symbol ref, + int index: int ref, + unique int child: @bare_symbol_child_type ref +); + +bare_symbol_def( + unique int id: @bare_symbol, + int loc: @location ref +); + +@begin_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[begin, index] +begin_child( + int begin: @begin ref, + int index: int ref, + unique int child: @begin_child_type ref +); + +begin_def( + unique int id: @begin, + int loc: @location ref +); + +@begin_block_child_type = @token_empty_statement | @underscore_statement + +#keyset[begin_block, index] +begin_block_child( + int begin_block: @begin_block ref, + int index: int ref, + unique int child: @begin_block_child_type ref +); + +begin_block_def( + unique int id: @begin_block, + int loc: @location ref +); + +@binary_left_type = @break | @call | @next | @return | @underscore_arg | @yield + +case @binary.operator of + 0 = @binary_bangequal +| 1 = @binary_bangtilde +| 2 = @binary_percent +| 3 = @binary_ampersand +| 4 = @binary_ampersandampersand +| 5 = @binary_star +| 6 = @binary_starstar +| 7 = @binary_plus +| 8 = @binary_minus +| 9 = @binary_slash +| 10 = @binary_langle +| 11 = @binary_langlelangle +| 12 = @binary_langleequal +| 13 = @binary_langleequalrangle +| 14 = @binary_equalequal +| 15 = @binary_equalequalequal +| 16 = @binary_equaltilde +| 17 = @binary_rangle +| 18 = @binary_rangleequal +| 19 = @binary_ranglerangle +| 20 = @binary_caret +| 21 = @binary_and +| 22 = @binary_or +| 23 = @binary_pipe +| 24 = @binary_pipepipe +; + + +@binary_right_type = @break | @call | @next | @return | @underscore_arg | @yield + +binary_def( + unique int id: @binary, + int left: @binary_left_type ref, + int operator: int ref, + int right: @binary_right_type ref, + int loc: @location ref +); + +block_parameters( + unique int block: @block ref, + unique int parameters: @block_parameters ref +); + +@block_child_type = @token_empty_statement | @underscore_statement + +#keyset[block, index] +block_child( + int block: @block ref, + int index: int ref, + unique int child: @block_child_type ref +); + +block_def( + unique int id: @block, + int loc: @location ref +); + +block_argument_def( + unique int id: @block_argument, + int child: @underscore_arg ref, + int loc: @location ref +); + +block_parameter_def( + unique int id: @block_parameter, + int name: @token_identifier ref, + int loc: @location ref +); + +@block_parameters_child_type = @block_parameter | @destructured_parameter | @hash_splat_parameter | @keyword_parameter | @optional_parameter | @splat_parameter | @token_identifier + +#keyset[block_parameters, index] +block_parameters_child( + int block_parameters: @block_parameters ref, + int index: int ref, + unique int child: @block_parameters_child_type ref +); + +block_parameters_def( + unique int id: @block_parameters, + int loc: @location ref +); + +break_child( + unique int break: @break ref, + unique int child: @argument_list ref +); + +break_def( + unique int id: @break, + int loc: @location ref +); + +call_arguments( + unique int call: @call ref, + unique int arguments: @argument_list ref +); + +@call_block_type = @block | @do_block + +call_block( + unique int call: @call ref, + unique int block: @call_block_type ref +); + +@call_method_type = @argument_list | @scope_resolution | @token_operator | @underscore_variable + +@call_receiver_type = @call | @underscore_primary + +call_receiver( + unique int call: @call ref, + unique int receiver: @call_receiver_type ref +); + +call_def( + unique int id: @call, + int method: @call_method_type ref, + int loc: @location ref +); + +case_value( + unique int case__: @case__ ref, + unique int value: @underscore_statement ref +); + +@case_child_type = @else | @when + +#keyset[case__, index] +case_child( + int case__: @case__ ref, + int index: int ref, + unique int child: @case_child_type ref +); + +case_def( + unique int id: @case__, + int loc: @location ref +); + +#keyset[chained_string, index] +chained_string_child( + int chained_string: @chained_string ref, + int index: int ref, + unique int child: @string__ ref +); + +chained_string_def( + unique int id: @chained_string, + int loc: @location ref +); + +@class_name_type = @scope_resolution | @token_constant + +class_superclass( + unique int class: @class ref, + unique int superclass: @superclass ref +); + +@class_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[class, index] +class_child( + int class: @class ref, + int index: int ref, + unique int child: @class_child_type ref +); + +class_def( + unique int id: @class, + int name: @class_name_type ref, + int loc: @location ref +); + +conditional_def( + unique int id: @conditional, + int alternative: @underscore_arg ref, + int condition: @underscore_arg ref, + int consequence: @underscore_arg ref, + int loc: @location ref +); + +@delimited_symbol_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[delimited_symbol, index] +delimited_symbol_child( + int delimited_symbol: @delimited_symbol ref, + int index: int ref, + unique int child: @delimited_symbol_child_type ref +); + +delimited_symbol_def( + unique int id: @delimited_symbol, + int loc: @location ref +); + +@destructured_left_assignment_child_type = @destructured_left_assignment | @rest_assignment | @underscore_lhs + +#keyset[destructured_left_assignment, index] +destructured_left_assignment_child( + int destructured_left_assignment: @destructured_left_assignment ref, + int index: int ref, + unique int child: @destructured_left_assignment_child_type ref +); + +destructured_left_assignment_def( + unique int id: @destructured_left_assignment, + int loc: @location ref +); + +@destructured_parameter_child_type = @block_parameter | @destructured_parameter | @hash_splat_parameter | @keyword_parameter | @optional_parameter | @splat_parameter | @token_identifier + +#keyset[destructured_parameter, index] +destructured_parameter_child( + int destructured_parameter: @destructured_parameter ref, + int index: int ref, + unique int child: @destructured_parameter_child_type ref +); + +destructured_parameter_def( + unique int id: @destructured_parameter, + int loc: @location ref +); + +@do_child_type = @token_empty_statement | @underscore_statement + +#keyset[do, index] +do_child( + int do: @do ref, + int index: int ref, + unique int child: @do_child_type ref +); + +do_def( + unique int id: @do, + int loc: @location ref +); + +do_block_parameters( + unique int do_block: @do_block ref, + unique int parameters: @block_parameters ref +); + +@do_block_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[do_block, index] +do_block_child( + int do_block: @do_block ref, + int index: int ref, + unique int child: @do_block_child_type ref +); + +do_block_def( + unique int id: @do_block, + int loc: @location ref +); + +@element_reference_child_type = @block_argument | @break | @call | @hash_splat_argument | @next | @pair | @return | @splat_argument | @underscore_arg | @yield + +#keyset[element_reference, index] +element_reference_child( + int element_reference: @element_reference ref, + int index: int ref, + unique int child: @element_reference_child_type ref +); + +element_reference_def( + unique int id: @element_reference, + int object: @underscore_primary ref, + int loc: @location ref +); + +@else_child_type = @token_empty_statement | @underscore_statement + +#keyset[else, index] +else_child( + int else: @else ref, + int index: int ref, + unique int child: @else_child_type ref +); + +else_def( + unique int id: @else, + int loc: @location ref +); + +@elsif_alternative_type = @else | @elsif + +elsif_alternative( + unique int elsif: @elsif ref, + unique int alternative: @elsif_alternative_type ref +); + +elsif_consequence( + unique int elsif: @elsif ref, + unique int consequence: @then ref +); + +elsif_def( + unique int id: @elsif, + int condition: @underscore_statement ref, + int loc: @location ref +); + +@end_block_child_type = @token_empty_statement | @underscore_statement + +#keyset[end_block, index] +end_block_child( + int end_block: @end_block ref, + int index: int ref, + unique int child: @end_block_child_type ref +); + +end_block_def( + unique int id: @end_block, + int loc: @location ref +); + +@ensure_child_type = @token_empty_statement | @underscore_statement + +#keyset[ensure, index] +ensure_child( + int ensure: @ensure ref, + int index: int ref, + unique int child: @ensure_child_type ref +); + +ensure_def( + unique int id: @ensure, + int loc: @location ref +); + +exception_variable_def( + unique int id: @exception_variable, + int child: @underscore_lhs ref, + int loc: @location ref +); + +@exceptions_child_type = @splat_argument | @underscore_arg + +#keyset[exceptions, index] +exceptions_child( + int exceptions: @exceptions ref, + int index: int ref, + unique int child: @exceptions_child_type ref +); + +exceptions_def( + unique int id: @exceptions, + int loc: @location ref +); + +@for_pattern_type = @left_assignment_list | @underscore_lhs + +for_def( + unique int id: @for, + int body: @do ref, + int pattern: @for_pattern_type ref, + int value: @in ref, + int loc: @location ref +); + +@hash_child_type = @hash_splat_argument | @pair + +#keyset[hash, index] +hash_child( + int hash: @hash ref, + int index: int ref, + unique int child: @hash_child_type ref +); + +hash_def( + unique int id: @hash, + int loc: @location ref +); + +hash_splat_argument_def( + unique int id: @hash_splat_argument, + int child: @underscore_arg ref, + int loc: @location ref +); + +hash_splat_parameter_name( + unique int hash_splat_parameter: @hash_splat_parameter ref, + unique int name: @token_identifier ref +); + +hash_splat_parameter_def( + unique int id: @hash_splat_parameter, + int loc: @location ref +); + +@heredoc_body_child_type = @interpolation | @token_escape_sequence | @token_heredoc_content | @token_heredoc_end + +#keyset[heredoc_body, index] +heredoc_body_child( + int heredoc_body: @heredoc_body ref, + int index: int ref, + unique int child: @heredoc_body_child_type ref +); + +heredoc_body_def( + unique int id: @heredoc_body, + int loc: @location ref +); + +@if_alternative_type = @else | @elsif + +if_alternative( + unique int if: @if ref, + unique int alternative: @if_alternative_type ref +); + +if_consequence( + unique int if: @if ref, + unique int consequence: @then ref +); + +if_def( + unique int id: @if, + int condition: @underscore_statement ref, + int loc: @location ref +); + +@if_modifier_condition_type = @break | @call | @next | @return | @underscore_arg | @yield + +if_modifier_def( + unique int id: @if_modifier, + int body: @underscore_statement ref, + int condition: @if_modifier_condition_type ref, + int loc: @location ref +); + +in_def( + unique int id: @in, + int child: @underscore_arg ref, + int loc: @location ref +); + +@interpolation_child_type = @token_empty_statement | @underscore_statement + +#keyset[interpolation, index] +interpolation_child( + int interpolation: @interpolation ref, + int index: int ref, + unique int child: @interpolation_child_type ref +); + +interpolation_def( + unique int id: @interpolation, + int loc: @location ref +); + +keyword_parameter_value( + unique int keyword_parameter: @keyword_parameter ref, + unique int value: @underscore_arg ref +); + +keyword_parameter_def( + unique int id: @keyword_parameter, + int name: @token_identifier ref, + int loc: @location ref +); + +@lambda_body_type = @block | @do_block + +lambda_parameters( + unique int lambda: @lambda ref, + unique int parameters: @lambda_parameters ref +); + +lambda_def( + unique int id: @lambda, + int body: @lambda_body_type ref, + int loc: @location ref +); + +@lambda_parameters_child_type = @block_parameter | @destructured_parameter | @hash_splat_parameter | @keyword_parameter | @optional_parameter | @splat_parameter | @token_identifier + +#keyset[lambda_parameters, index] +lambda_parameters_child( + int lambda_parameters: @lambda_parameters ref, + int index: int ref, + unique int child: @lambda_parameters_child_type ref +); + +lambda_parameters_def( + unique int id: @lambda_parameters, + int loc: @location ref +); + +@left_assignment_list_child_type = @destructured_left_assignment | @rest_assignment | @underscore_lhs + +#keyset[left_assignment_list, index] +left_assignment_list_child( + int left_assignment_list: @left_assignment_list ref, + int index: int ref, + unique int child: @left_assignment_list_child_type ref +); + +left_assignment_list_def( + unique int id: @left_assignment_list, + int loc: @location ref +); + +method_parameters( + unique int method: @method ref, + unique int parameters: @method_parameters ref +); + +@method_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[method, index] +method_child( + int method: @method ref, + int index: int ref, + unique int child: @method_child_type ref +); + +method_def( + unique int id: @method, + int name: @underscore_method_name ref, + int loc: @location ref +); + +@method_parameters_child_type = @block_parameter | @destructured_parameter | @hash_splat_parameter | @keyword_parameter | @optional_parameter | @splat_parameter | @token_identifier + +#keyset[method_parameters, index] +method_parameters_child( + int method_parameters: @method_parameters ref, + int index: int ref, + unique int child: @method_parameters_child_type ref +); + +method_parameters_def( + unique int id: @method_parameters, + int loc: @location ref +); + +@module_name_type = @scope_resolution | @token_constant + +@module_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[module, index] +module_child( + int module: @module ref, + int index: int ref, + unique int child: @module_child_type ref +); + +module_def( + unique int id: @module, + int name: @module_name_type ref, + int loc: @location ref +); + +next_child( + unique int next: @next ref, + unique int child: @argument_list ref +); + +next_def( + unique int id: @next, + int loc: @location ref +); + +case @operator_assignment.operator of + 0 = @operator_assignment_percentequal +| 1 = @operator_assignment_ampersandampersandequal +| 2 = @operator_assignment_ampersandequal +| 3 = @operator_assignment_starstarequal +| 4 = @operator_assignment_starequal +| 5 = @operator_assignment_plusequal +| 6 = @operator_assignment_minusequal +| 7 = @operator_assignment_slashequal +| 8 = @operator_assignment_langlelangleequal +| 9 = @operator_assignment_ranglerangleequal +| 10 = @operator_assignment_caretequal +| 11 = @operator_assignment_pipeequal +| 12 = @operator_assignment_pipepipeequal +; + + +@operator_assignment_right_type = @break | @call | @next | @return | @underscore_arg | @yield + +operator_assignment_def( + unique int id: @operator_assignment, + int left: @underscore_lhs ref, + int operator: int ref, + int right: @operator_assignment_right_type ref, + int loc: @location ref +); + +optional_parameter_def( + unique int id: @optional_parameter, + int name: @token_identifier ref, + int value: @underscore_arg ref, + int loc: @location ref +); + +@pair_key_type = @string__ | @token_hash_key_symbol | @underscore_arg + +pair_def( + unique int id: @pair, + int key__: @pair_key_type ref, + int value: @underscore_arg ref, + int loc: @location ref +); + +@parenthesized_statements_child_type = @token_empty_statement | @underscore_statement + +#keyset[parenthesized_statements, index] +parenthesized_statements_child( + int parenthesized_statements: @parenthesized_statements ref, + int index: int ref, + unique int child: @parenthesized_statements_child_type ref +); + +parenthesized_statements_def( + unique int id: @parenthesized_statements, + int loc: @location ref +); + +@pattern_child_type = @splat_argument | @underscore_arg + +pattern_def( + unique int id: @pattern, + int child: @pattern_child_type ref, + int loc: @location ref +); + +@program_child_type = @token_empty_statement | @token_uninterpreted | @underscore_statement + +#keyset[program, index] +program_child( + int program: @program ref, + int index: int ref, + unique int child: @program_child_type ref +); + +program_def( + unique int id: @program, + int loc: @location ref +); + +range_begin( + unique int range: @range ref, + unique int begin: @underscore_arg ref +); + +range_end( + unique int range: @range ref, + unique int end: @underscore_arg ref +); + +case @range.operator of + 0 = @range_dotdot +| 1 = @range_dotdotdot +; + + +range_def( + unique int id: @range, + int operator: int ref, + int loc: @location ref +); + +@rational_child_type = @token_float | @token_integer + +rational_def( + unique int id: @rational, + int child: @rational_child_type ref, + int loc: @location ref +); + +redo_child( + unique int redo: @redo ref, + unique int child: @argument_list ref +); + +redo_def( + unique int id: @redo, + int loc: @location ref +); + +@regex_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[regex, index] +regex_child( + int regex: @regex ref, + int index: int ref, + unique int child: @regex_child_type ref +); + +regex_def( + unique int id: @regex, + int loc: @location ref +); + +rescue_body( + unique int rescue: @rescue ref, + unique int body: @then ref +); + +rescue_exceptions( + unique int rescue: @rescue ref, + unique int exceptions: @exceptions ref +); + +rescue_variable( + unique int rescue: @rescue ref, + unique int variable: @exception_variable ref +); + +rescue_def( + unique int id: @rescue, + int loc: @location ref +); + +@rescue_modifier_handler_type = @break | @call | @next | @return | @underscore_arg | @yield + +rescue_modifier_def( + unique int id: @rescue_modifier, + int body: @underscore_statement ref, + int handler: @rescue_modifier_handler_type ref, + int loc: @location ref +); + +rest_assignment_child( + unique int rest_assignment: @rest_assignment ref, + unique int child: @underscore_lhs ref +); + +rest_assignment_def( + unique int id: @rest_assignment, + int loc: @location ref +); + +retry_child( + unique int retry: @retry ref, + unique int child: @argument_list ref +); + +retry_def( + unique int id: @retry, + int loc: @location ref +); + +return_child( + unique int return: @return ref, + unique int child: @argument_list ref +); + +return_def( + unique int id: @return, + int loc: @location ref +); + +@right_assignment_list_child_type = @splat_argument | @underscore_arg + +#keyset[right_assignment_list, index] +right_assignment_list_child( + int right_assignment_list: @right_assignment_list ref, + int index: int ref, + unique int child: @right_assignment_list_child_type ref +); + +right_assignment_list_def( + unique int id: @right_assignment_list, + int loc: @location ref +); + +@scope_resolution_name_type = @token_constant | @token_identifier + +scope_resolution_scope( + unique int scope_resolution: @scope_resolution ref, + unique int scope: @underscore_primary ref +); + +scope_resolution_def( + unique int id: @scope_resolution, + int name: @scope_resolution_name_type ref, + int loc: @location ref +); + +setter_def( + unique int id: @setter, + int name: @token_identifier ref, + int loc: @location ref +); + +@singleton_class_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[singleton_class, index] +singleton_class_child( + int singleton_class: @singleton_class ref, + int index: int ref, + unique int child: @singleton_class_child_type ref +); + +singleton_class_def( + unique int id: @singleton_class, + int value: @underscore_arg ref, + int loc: @location ref +); + +@singleton_method_object_type = @underscore_arg | @underscore_variable + +singleton_method_parameters( + unique int singleton_method: @singleton_method ref, + unique int parameters: @method_parameters ref +); + +@singleton_method_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement + +#keyset[singleton_method, index] +singleton_method_child( + int singleton_method: @singleton_method ref, + int index: int ref, + unique int child: @singleton_method_child_type ref +); + +singleton_method_def( + unique int id: @singleton_method, + int name: @underscore_method_name ref, + int object: @singleton_method_object_type ref, + int loc: @location ref +); + +splat_argument_def( + unique int id: @splat_argument, + int child: @underscore_arg ref, + int loc: @location ref +); + +splat_parameter_name( + unique int splat_parameter: @splat_parameter ref, + unique int name: @token_identifier ref +); + +splat_parameter_def( + unique int id: @splat_parameter, + int loc: @location ref +); + +@string_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[string__, index] +string_child( + int string__: @string__ ref, + int index: int ref, + unique int child: @string_child_type ref +); + +string_def( + unique int id: @string__, + int loc: @location ref +); + +#keyset[string_array, index] +string_array_child( + int string_array: @string_array ref, + int index: int ref, + unique int child: @bare_string ref +); + +string_array_def( + unique int id: @string_array, + int loc: @location ref +); + +@subshell_child_type = @interpolation | @token_escape_sequence | @token_string_content + +#keyset[subshell, index] +subshell_child( + int subshell: @subshell ref, + int index: int ref, + unique int child: @subshell_child_type ref +); + +subshell_def( + unique int id: @subshell, + int loc: @location ref +); + +@superclass_child_type = @break | @call | @next | @return | @underscore_arg | @yield + +superclass_def( + unique int id: @superclass, + int child: @superclass_child_type ref, + int loc: @location ref +); + +#keyset[symbol_array, index] +symbol_array_child( + int symbol_array: @symbol_array ref, + int index: int ref, + unique int child: @bare_symbol ref +); + +symbol_array_def( + unique int id: @symbol_array, + int loc: @location ref +); + +@then_child_type = @token_empty_statement | @underscore_statement + +#keyset[then, index] +then_child( + int then: @then ref, + int index: int ref, + unique int child: @then_child_type ref +); + +then_def( + unique int id: @then, + int loc: @location ref +); + +@unary_operand_type = @break | @call | @next | @parenthesized_statements | @return | @token_float | @token_integer | @underscore_arg | @yield + +case @unary.operator of + 0 = @unary_bang +| 1 = @unary_plus +| 2 = @unary_minus +| 3 = @unary_definedquestion +| 4 = @unary_not +| 5 = @unary_tilde +; + + +unary_def( + unique int id: @unary, + int operand: @unary_operand_type ref, + int operator: int ref, + int loc: @location ref +); + +#keyset[undef, index] +undef_child( + int undef: @undef ref, + int index: int ref, + unique int child: @underscore_method_name ref +); + +undef_def( + unique int id: @undef, + int loc: @location ref +); + +@unless_alternative_type = @else | @elsif + +unless_alternative( + unique int unless: @unless ref, + unique int alternative: @unless_alternative_type ref +); + +unless_consequence( + unique int unless: @unless ref, + unique int consequence: @then ref +); + +unless_def( + unique int id: @unless, + int condition: @underscore_statement ref, + int loc: @location ref +); + +@unless_modifier_condition_type = @break | @call | @next | @return | @underscore_arg | @yield + +unless_modifier_def( + unique int id: @unless_modifier, + int body: @underscore_statement ref, + int condition: @unless_modifier_condition_type ref, + int loc: @location ref +); + +until_def( + unique int id: @until, + int body: @do ref, + int condition: @underscore_statement ref, + int loc: @location ref +); + +@until_modifier_condition_type = @break | @call | @next | @return | @underscore_arg | @yield + +until_modifier_def( + unique int id: @until_modifier, + int body: @underscore_statement ref, + int condition: @until_modifier_condition_type ref, + int loc: @location ref +); + +when_body( + unique int when: @when ref, + unique int body: @then ref +); + +#keyset[when, index] +when_pattern( + int when: @when ref, + int index: int ref, + unique int pattern: @pattern ref +); + +when_def( + unique int id: @when, + int loc: @location ref +); + +while_def( + unique int id: @while, + int body: @do ref, + int condition: @underscore_statement ref, + int loc: @location ref +); + +@while_modifier_condition_type = @break | @call | @next | @return | @underscore_arg | @yield + +while_modifier_def( + unique int id: @while_modifier, + int body: @underscore_statement ref, + int condition: @while_modifier_condition_type ref, + int loc: @location ref +); + +yield_child( + unique int yield: @yield ref, + unique int child: @argument_list ref +); + +yield_def( + unique int id: @yield, + int loc: @location ref +); + +tokeninfo( + unique int id: @token, + int kind: int ref, + int file: @file ref, + int idx: int ref, + string value: string ref, + int loc: @location ref +); + +case @token.kind of + 0 = @reserved_word +| 1 = @token_character +| 2 = @token_class_variable +| 3 = @token_comment +| 4 = @token_complex +| 5 = @token_constant +| 6 = @token_empty_statement +| 7 = @token_escape_sequence +| 8 = @token_false +| 9 = @token_float +| 10 = @token_global_variable +| 11 = @token_hash_key_symbol +| 12 = @token_heredoc_beginning +| 13 = @token_heredoc_content +| 14 = @token_heredoc_end +| 15 = @token_identifier +| 16 = @token_instance_variable +| 17 = @token_integer +| 18 = @token_nil +| 19 = @token_operator +| 20 = @token_self +| 21 = @token_simple_symbol +| 22 = @token_string_content +| 23 = @token_super +| 24 = @token_true +| 25 = @token_uninterpreted +; + + +@ast_node = @alias | @argument_list | @array | @assignment | @bare_string | @bare_symbol | @begin | @begin_block | @binary | @block | @block_argument | @block_parameter | @block_parameters | @break | @call | @case__ | @chained_string | @class | @conditional | @delimited_symbol | @destructured_left_assignment | @destructured_parameter | @do | @do_block | @element_reference | @else | @elsif | @end_block | @ensure | @exception_variable | @exceptions | @for | @hash | @hash_splat_argument | @hash_splat_parameter | @heredoc_body | @if | @if_modifier | @in | @interpolation | @keyword_parameter | @lambda | @lambda_parameters | @left_assignment_list | @method | @method_parameters | @module | @next | @operator_assignment | @optional_parameter | @pair | @parenthesized_statements | @pattern | @program | @range | @rational | @redo | @regex | @rescue | @rescue_modifier | @rest_assignment | @retry | @return | @right_assignment_list | @scope_resolution | @setter | @singleton_class | @singleton_method | @splat_argument | @splat_parameter | @string__ | @string_array | @subshell | @superclass | @symbol_array | @then | @token | @unary | @undef | @unless | @unless_modifier | @until | @until_modifier | @when | @while | @while_modifier | @yield + +@ast_node_parent = @ast_node | @file + +#keyset[parent, parent_index] +ast_node_parent( + int child: @ast_node ref, + int parent: @ast_node_parent ref, + int parent_index: int ref +); + diff --git a/ruby/ql/src/AlertSuppression.ql b/ruby/ql/src/AlertSuppression.ql new file mode 100644 index 00000000000..b10c4ecbb45 --- /dev/null +++ b/ruby/ql/src/AlertSuppression.ql @@ -0,0 +1,82 @@ +/** + * @name Alert suppression + * @description Generates information about alert suppressions. + * @kind alert-suppression + * @id rb/alert-suppression + */ + +import ruby +import codeql.ruby.ast.internal.TreeSitter + +/** + * An alert suppression comment. + */ +class SuppressionComment extends Ruby::Comment { + string annotation; + + SuppressionComment() { + // suppression comments must be single-line + this.getLocation().getStartLine() = this.getLocation().getEndLine() and + exists(string text | text = commentText(this) | + // match `lgtm[...]` anywhere in the comment + annotation = text.regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _) + or + // match `lgtm` at the start of the comment and after semicolon + annotation = text.regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _).trim() + ) + } + + /** + * Gets the text of this suppression comment. + */ + string getText() { result = commentText(this) } + + /** Gets the suppression annotation in this comment. */ + string getAnnotation() { result = annotation } + + /** + * Holds if this comment applies to the range from column `startcolumn` of line `startline` + * to column `endcolumn` of line `endline` in file `filepath`. + */ + predicate covers(string filepath, int startline, int startcolumn, int endline, int endcolumn) { + this.getLocation().hasLocationInfo(filepath, startline, _, endline, endcolumn) and + startcolumn = 1 + } + + /** Gets the scope of this suppression. */ + SuppressionScope getScope() { this = result.getSuppressionComment() } +} + +private string commentText(Ruby::Comment comment) { result = comment.getValue().suffix(1) } + +/** + * The scope of an alert suppression comment. + */ +class SuppressionScope extends @ruby_token_comment { + SuppressionScope() { this instanceof SuppressionComment } + + /** Gets a suppression comment with this scope. */ + SuppressionComment getSuppressionComment() { result = this } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.(SuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn) + } + + /** Gets a textual representation of this element. */ + string toString() { result = "suppression range" } +} + +from SuppressionComment c +select c, // suppression comment + c.getText(), // text of suppression comment (excluding delimiters) + c.getAnnotation(), // text of suppression annotation + c.getScope() // scope of suppression diff --git a/ruby/ql/src/codeql-suites/ruby-code-scanning.qls b/ruby/ql/src/codeql-suites/ruby-code-scanning.qls new file mode 100644 index 00000000000..cf9f7605b2b --- /dev/null +++ b/ruby/ql/src/codeql-suites/ruby-code-scanning.qls @@ -0,0 +1,5 @@ +- description: Standard Code Scanning queries for Ruby +- queries: . +- apply: code-scanning-selectors.yml + from: codeql/suite-helpers + diff --git a/ruby/ql/src/codeql-suites/ruby-lgtm-full.qls b/ruby/ql/src/codeql-suites/ruby-lgtm-full.qls new file mode 100644 index 00000000000..11fe09a9681 --- /dev/null +++ b/ruby/ql/src/codeql-suites/ruby-lgtm-full.qls @@ -0,0 +1,12 @@ +- description: Standard LGTM queries for Ruby, including ones not displayed by default +- queries: . +- apply: lgtm-selectors.yml + from: codeql/suite-helpers +# These are only for IDE use. +- exclude: + tags contain: + - ide-contextual-queries/local-definitions + - ide-contextual-queries/local-references +- include: + id: rb/lines-of-code-in-files + diff --git a/ruby/ql/src/codeql-suites/ruby-lgtm.qls b/ruby/ql/src/codeql-suites/ruby-lgtm.qls new file mode 100644 index 00000000000..d13847da7e0 --- /dev/null +++ b/ruby/ql/src/codeql-suites/ruby-lgtm.qls @@ -0,0 +1,4 @@ +- description: Standard LGTM queries for Ruby +- apply: codeql-suites/ruby-lgtm-full.qls +- apply: lgtm-displayed-only.yml + from: codeql/suite-helpers diff --git a/ruby/ql/src/codeql-suites/ruby-security-and-quality.qls b/ruby/ql/src/codeql-suites/ruby-security-and-quality.qls new file mode 100644 index 00000000000..588a074cb50 --- /dev/null +++ b/ruby/ql/src/codeql-suites/ruby-security-and-quality.qls @@ -0,0 +1,4 @@ +- description: Security-and-quality queries for Ruby +- queries: . +- apply: security-and-quality-selectors.yml + from: codeql/suite-helpers diff --git a/ruby/ql/src/codeql-suites/ruby-security-extended.qls b/ruby/ql/src/codeql-suites/ruby-security-extended.qls new file mode 100644 index 00000000000..250341643ea --- /dev/null +++ b/ruby/ql/src/codeql-suites/ruby-security-extended.qls @@ -0,0 +1,5 @@ +- description: Security-extended queries for Ruby +- queries: . +- apply: security-extended-selectors.yml + from: codeql/suite-helpers + diff --git a/ruby/ql/src/experimental/README.md b/ruby/ql/src/experimental/README.md new file mode 100644 index 00000000000..ed83d8d4ab0 --- /dev/null +++ b/ruby/ql/src/experimental/README.md @@ -0,0 +1 @@ +This directory contains [experimental](../../docs/experimental.md) CodeQL queries and libraries. diff --git a/ruby/ql/src/experimental/performance/UseDetect.ql b/ruby/ql/src/experimental/performance/UseDetect.ql new file mode 100644 index 00000000000..f5fcf6df4fb --- /dev/null +++ b/ruby/ql/src/experimental/performance/UseDetect.ql @@ -0,0 +1,64 @@ +/** + * @name Use detect + * @description Use 'detect' instead of 'select' followed by 'first' or 'last'. + * @kind problem + * @problem.severity warning + * @id rb/use-detect + * @tags performance rubocop + * @precision high + */ + +// This is an implementation of the Rubocop rule +// https://github.com/rubocop/rubocop-performance/blob/master/lib/rubocop/cop/performance/detect.rb +import ruby +import codeql.ruby.dataflow.SSA + +/** A call that extracts the first or last element of a list. */ +class EndCall extends MethodCall { + string detect; + + EndCall() { + detect = "detect" and + ( + this.getMethodName() = "first" and + this.getNumberOfArguments() = 0 + or + this.getNumberOfArguments() = 1 and + this.getArgument(0).(IntegerLiteral).getValueText() = "0" + ) + or + detect = "reverse_detect" and + ( + this.getMethodName() = "last" and + this.getNumberOfArguments() = 0 + or + this.getNumberOfArguments() = 1 and + this.getArgument(0).(UnaryMinusExpr).getOperand().(IntegerLiteral).getValueText() = "1" + ) + } + + string detectCall() { result = detect } +} + +Expr getUniqueRead(Expr e) { + exists(AssignExpr ae | + e = ae.getRightOperand() and + forex(Ssa::WriteDefinition def | def.getWriteAccess() = ae.getLeftOperand() | + strictcount(def.getARead()) = 1 and + not def = any(Ssa::PhiNode phi).getAnInput() and + def.getARead() = result.getAControlFlowNode() + ) + ) +} + +class SelectBlock extends MethodCall { + SelectBlock() { + this.getMethodName() in ["select", "filter", "find_all"] and + exists(this.getBlock()) + } +} + +from EndCall call, SelectBlock selectBlock +where getUniqueRead*(selectBlock) = call.getReceiver() +select call, "Replace this call and $@ with '" + call.detectCall() + "'.", selectBlock, + "'select' call" diff --git a/ruby/ql/src/filters/ClassifyFiles.ql b/ruby/ql/src/filters/ClassifyFiles.ql new file mode 100644 index 00000000000..d194523e09d --- /dev/null +++ b/ruby/ql/src/filters/ClassifyFiles.ql @@ -0,0 +1,20 @@ +/** + * @name Classify files + * @description This query produces a list of all files in a database + * that are classified as generated code or test code. + * + * Used by LGTM. + * @kind file-classifier + * @id rb/file-classifier + */ + +import ruby +import codeql.ruby.filters.GeneratedCode + +predicate classify(File f, string category) { + f instanceof GeneratedCodeFile and category = "generated" +} + +from File f, string category +where classify(f, category) +select f, category diff --git a/ruby/ql/src/ide-contextual-queries/localDefinitions.ql b/ruby/ql/src/ide-contextual-queries/localDefinitions.ql new file mode 100644 index 00000000000..81c5e449bb1 --- /dev/null +++ b/ruby/ql/src/ide-contextual-queries/localDefinitions.ql @@ -0,0 +1,20 @@ +/** + * @name Jump-to-definition links + * @description Generates use-definition pairs that provide the data + * for jump-to-definition in the code viewer. + * @kind definitions + * @id ruby/ide-jump-to-definition + * @tags ide-contextual-queries/local-definitions + */ + +import codeql.IDEContextual +import codeql.ruby.AST + +external string selectedSourceFile(); + +from AstNode e, Variable def, string kind +where + e = def.getAnAccess() and + kind = "local variable" and + e.getLocation().getFile() = getFileBySourceArchiveName(selectedSourceFile()) +select e, def, kind diff --git a/ruby/ql/src/ide-contextual-queries/localReferences.ql b/ruby/ql/src/ide-contextual-queries/localReferences.ql new file mode 100644 index 00000000000..713b363e60f --- /dev/null +++ b/ruby/ql/src/ide-contextual-queries/localReferences.ql @@ -0,0 +1,21 @@ +/** + * @name Find-references links + * @description Generates use-definition pairs that provide the data + * for find-references in the code viewer. + * @kind definitions + * @id ruby/ide-find-references + * @tags ide-contextual-queries/local-references + */ + +import codeql.IDEContextual +import codeql.ruby.AST +import codeql.ruby.ast.Variable + +external string selectedSourceFile(); + +from AstNode e, Variable def, string kind +where + e = def.getAnAccess() and + kind = "local variable" and + def.getLocation().getFile() = getFileBySourceArchiveName(selectedSourceFile()) +select e, def, kind diff --git a/ruby/ql/src/ide-contextual-queries/printAst.ql b/ruby/ql/src/ide-contextual-queries/printAst.ql new file mode 100644 index 00000000000..cd5b9a4a3b2 --- /dev/null +++ b/ruby/ql/src/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 ruby/print-ast + * @kind graph + * @tags ide-contextual-queries/print-ast + */ + +private import codeql.IDEContextual +private import codeql.ruby.AST +private import codeql.ruby.printAst + +/** + * The source file to generate an AST from. + */ +external string selectedSourceFile(); + +/** + * Overrides the configuration to print only 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/ruby/ql/src/qlpack.lock.yml b/ruby/ql/src/qlpack.lock.yml new file mode 100644 index 00000000000..0bef0f691a9 --- /dev/null +++ b/ruby/ql/src/qlpack.lock.yml @@ -0,0 +1,6 @@ +--- +dependencies: + codeql/suite-helpers: + version: 0.0.2 +compiled: false +lockVersion: 1.0.0 diff --git a/ruby/ql/src/qlpack.yml b/ruby/ql/src/qlpack.yml new file mode 100644 index 00000000000..1c346968c43 --- /dev/null +++ b/ruby/ql/src/qlpack.yml @@ -0,0 +1,7 @@ +name: codeql/ruby-queries +version: 0.0.2 +suites: codeql-suites +defaultSuiteFile: codeql-suites/ruby-code-scanning.qls +dependencies: + codeql/ruby-all: "*" + codeql/suite-helpers: "*" diff --git a/ruby/ql/src/queries.xml b/ruby/ql/src/queries.xml new file mode 100644 index 00000000000..a7ce9735ef4 --- /dev/null +++ b/ruby/ql/src/queries.xml @@ -0,0 +1 @@ +<queries language="ruby"/> diff --git a/ruby/ql/src/queries/analysis/Definitions.ql b/ruby/ql/src/queries/analysis/Definitions.ql new file mode 100644 index 00000000000..aff97bbc345 --- /dev/null +++ b/ruby/ql/src/queries/analysis/Definitions.ql @@ -0,0 +1,81 @@ +/** + * @name Definitions + * @description Jump to definition helper query. + * @kind definitions + * @id rb/jump-to-definition + */ + +/* + * TODO: + * - should `Foo.new` point to `Foo#initialize`? + */ + +import ruby +import codeql.ruby.ast.internal.Module +import codeql.ruby.dataflow.SSA + +from DefLoc loc, Expr src, Expr target, string kind +where + ConstantDefLoc(src, target) = loc and kind = "constant" + or + MethodLoc(src, target) = loc and kind = "method" + or + LocalVariableLoc(src, target) = loc and kind = "variable" + or + InstanceVariableLoc(src, target) = loc and kind = "instance variable" + or + ClassVariableLoc(src, target) = loc and kind = "class variable" +select src, target, kind + +/** + * Definition location info for different identifiers. + * Each branch holds two values that are subclasses of `Expr`. + * The first is the "source" - some usage of an identifier. + * The second is the "target" - the definition of that identifier. + */ +newtype DefLoc = + /** A constant, module or class. */ + ConstantDefLoc(ConstantReadAccess read, ConstantWriteAccess write) { write = definitionOf(read) } or + /** A method call. */ + MethodLoc(MethodCall call, Method meth) { meth = call.getATarget() } or + /** A local variable. */ + LocalVariableLoc(VariableReadAccess read, VariableWriteAccess write) { + exists(Ssa::WriteDefinition w | + write = w.getWriteAccess() and + read = w.getARead().getExpr() and + not read.isSynthesized() + ) + } or + /** An instance variable */ + InstanceVariableLoc(InstanceVariableReadAccess read, InstanceVariableWriteAccess write) { + /* + * We consider instance variables to be "defined" in the initialize method of their enclosing class. + * If that method doesn't exist, we won't provide any jump-to-def information for the instance variable. + */ + + exists(Method m | + m.getAChild+() = write and + m.getName() = "initialize" and + write.getVariable() = read.getVariable() + ) + } or + /** A class variable */ + ClassVariableLoc(ClassVariableReadAccess read, ClassVariableWriteAccess write) { + read.getVariable() = write.getVariable() and + not exists(MethodBase m | m.getAChild+() = write) + } + +/** + * Gets the constant write that defines the given constant. + * Modules often don't have a unique definition, as they are opened multiple times in different + * files. In these cases we arbitrarily pick the definition with the lexicographically least + * location. + */ +ConstantWriteAccess definitionOf(ConstantReadAccess r) { + result = + min(ConstantWriteAccess w | + w.getQualifiedName() = resolveConstant(r) + | + w order by w.getLocation().toString() + ) +} diff --git a/ruby/ql/src/queries/diagnostics/ExtractionErrors.ql b/ruby/ql/src/queries/diagnostics/ExtractionErrors.ql new file mode 100644 index 00000000000..5c55d984337 --- /dev/null +++ b/ruby/ql/src/queries/diagnostics/ExtractionErrors.ql @@ -0,0 +1,18 @@ +/** + * @name Extraction errors + * @description List all extraction errors for files in the source code directory. + * @kind diagnostic + * @id rb/diagnostics/extraction-errors + */ + +import ruby +import codeql.ruby.Diagnostics + +/** Gets the SARIF severity to associate an error. */ +int getSeverity() { result = 2 } + +from ExtractionError error, File f +where + f = error.getLocation().getFile() and + exists(f.getRelativePath()) +select error, "Extraction failed in " + f + " with error " + error.getMessage(), getSeverity() diff --git a/ruby/ql/src/queries/diagnostics/SuccessfullyExtractedFiles.ql b/ruby/ql/src/queries/diagnostics/SuccessfullyExtractedFiles.ql new file mode 100644 index 00000000000..74f95763d8a --- /dev/null +++ b/ruby/ql/src/queries/diagnostics/SuccessfullyExtractedFiles.ql @@ -0,0 +1,16 @@ +/** + * @name Successfully extracted files + * @description Lists all files in the source code directory that were extracted + * without encountering an error in the file. + * @kind diagnostic + * @id rb/diagnostics/successfully-extracted-files + */ + +import ruby +import codeql.ruby.Diagnostics + +from File f +where + not exists(ExtractionError e | e.getLocation().getFile() = f) and + exists(f.getRelativePath()) +select f, "" diff --git a/ruby/ql/src/queries/metrics/FLines.ql b/ruby/ql/src/queries/metrics/FLines.ql new file mode 100644 index 00000000000..97c319fbf73 --- /dev/null +++ b/ruby/ql/src/queries/metrics/FLines.ql @@ -0,0 +1,13 @@ +/** + * @name Number of lines + * @kind metric + * @description The number of lines in each file. + * @metricType file + * @id rb/lines-per-file + */ + +import ruby + +from RubyFile f, int n +where n = f.getNumberOfLines() +select f, n order by n desc diff --git a/ruby/ql/src/queries/metrics/FLinesOfCode.ql b/ruby/ql/src/queries/metrics/FLinesOfCode.ql new file mode 100644 index 00000000000..0c1d15960cc --- /dev/null +++ b/ruby/ql/src/queries/metrics/FLinesOfCode.ql @@ -0,0 +1,14 @@ +/** + * @name Lines of code in files + * @kind metric + * @description Measures the number of lines of code in each file, ignoring lines that + * contain only comments or whitespace. + * @metricType file + * @id rb/lines-of-code-in-files + */ + +import ruby + +from RubyFile f, int n +where n = f.getNumberOfLinesOfCode() +select f, n order by n desc diff --git a/ruby/ql/src/queries/metrics/FLinesOfComments.ql b/ruby/ql/src/queries/metrics/FLinesOfComments.ql new file mode 100644 index 00000000000..8af882f13d1 --- /dev/null +++ b/ruby/ql/src/queries/metrics/FLinesOfComments.ql @@ -0,0 +1,13 @@ +/** + * @name Lines of comments in files + * @kind metric + * @description Measures the number of lines of comments in each file. + * @metricType file + * @id rb/lines-of-comments-in-files + */ + +import ruby + +from RubyFile f, int n +where n = f.getNumberOfLinesOfComments() +select f, n order by n desc diff --git a/ruby/ql/src/queries/security/cwe-022/PathInjection.qhelp b/ruby/ql/src/queries/security/cwe-022/PathInjection.qhelp new file mode 100644 index 00000000000..537201ec15f --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-022/PathInjection.qhelp @@ -0,0 +1,61 @@ +<!DOCTYPE qhelp PUBLIC + "-//Semmle//qhelp//EN" + "qhelp.dtd"> +<qhelp> + +<overview> +<p> +Accessing files using paths constructed from user-controlled data can allow an +attacker to access unexpected resources. This can result in sensitive +information being revealed or deleted, or an attacker being able to influence +behavior by modifying unexpected files. +</p> +</overview> + +<recommendation> +<p> +Validate user input before using it to construct a file path, either using an +off-the-shelf library like <code>ActiveStorage::Filename#sanitized</code> in +Rails, or by performing custom validation. +</p> + +<p> +Ideally, follow these rules: +</p> + +<ul> +<li>Do not allow more than a single "." character.</li> +<li>Do not allow directory separators such as "/" or "\" (depending on the file +system).</li> +<li>Do not rely on simply replacing problematic sequences such as "../". For +example, after applying this filter to ".../...//", the resulting string would +still be "../".</li> +<li>Use a whitelist of known good patterns.</li> +</ul> +</recommendation> + +<example> +<p> +In the first example, a file name is read from an HTTP request and then used to +access a file. However, a malicious user could enter a file name which is an +absolute path, such as <code>"/etc/passwd"</code>. +</p> + +<p> +In the second example, it appears that the user is restricted to opening a file +within the <code>"user"</code> home directory. However, a malicious user could +enter a file name containing special characters. For example, the string +<code>"../../etc/passwd"</code> will result in the code reading the file located +at <code>"/home/user/../../etc/passwd"</code>, which is the system's password +file. This file would then be sent back to the user, giving them access to all +the system's passwords. +</p> + +<sample src="examples/tainted_path.rb" /> +</example> + +<references> +<li>OWASP: <a href="https://owasp.org/www-community/attacks/Path_Traversal">Path Traversal</a>.</li> +<li>Rails: <a href="https://api.rubyonrails.org/classes/ActiveStorage/Filename.html#method-i-sanitized">ActiveStorage::Filename#sanitized</a>.</li> +</references> +</qhelp> diff --git a/ruby/ql/src/queries/security/cwe-022/PathInjection.ql b/ruby/ql/src/queries/security/cwe-022/PathInjection.ql new file mode 100644 index 00000000000..eb52f8e4531 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-022/PathInjection.ql @@ -0,0 +1,26 @@ +/** + * @name Uncontrolled data used in path expression + * @description Accessing paths influenced by users can allow an attacker to access + * unexpected resources. + * @kind path-problem + * @problem.severity error + * @security-severity 7.5 + * @precision high + * @id rb/path-injection + * @tags security + * external/cwe/cwe-022 + * external/cwe/cwe-023 + * external/cwe/cwe-036 + * external/cwe/cwe-073 + * external/cwe/cwe-099 + */ + +import ruby +import codeql.ruby.security.PathInjectionQuery +import codeql.ruby.DataFlow +import DataFlow::PathGraph + +from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink +where cfg.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "This path depends on $@.", source.getNode(), + "a user-provided value" diff --git a/ruby/ql/src/queries/security/cwe-022/examples/tainted_path.rb b/ruby/ql/src/queries/security/cwe-022/examples/tainted_path.rb new file mode 100644 index 00000000000..b9c6b68ac1e --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-022/examples/tainted_path.rb @@ -0,0 +1,11 @@ +class FilesController < ActionController::Base + def first_example + # BAD: This could read any file on the file system + @content = File.read params[:path] + end + + def second_example + # BAD: This could still read any file on the file system + @content = File.read "/home/user/#{ params[:path] }" + end +end diff --git a/ruby/ql/src/queries/security/cwe-078/CommandInjection.qhelp b/ruby/ql/src/queries/security/cwe-078/CommandInjection.qhelp new file mode 100644 index 00000000000..d01465d1dae --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-078/CommandInjection.qhelp @@ -0,0 +1,44 @@ +<!DOCTYPE qhelp PUBLIC + "-//Semmle//qhelp//EN" + "qhelp.dtd"> +<qhelp> +<overview> +<p>Code that passes user input directly to +<code>Kernel.system</code>, <code>Kernel.exec</code>, or some other library +routine that executes a command, allows the user to execute malicious +code.</p> + +</overview> +<recommendation> + +<p>If possible, use hard-coded string literals to specify the command to run +or library to load. Instead of passing the user input directly to the +process or library function, examine the user input and then choose +among hard-coded string literals.</p> + +<p>If the applicable libraries or commands cannot be determined at +compile time, then add code to verify that the user input string is +safe before using it.</p> + +</recommendation> +<example> + +<p>The following example shows code that takes a shell script that can be changed +maliciously by a user, and passes it straight to <code>Kernel.system</code> +without examining it first.</p> + +<sample src="examples/command_injection.rb" /> + +</example> +<references> + +<li> +OWASP: +<a href="https://www.owasp.org/index.php/Command_Injection">Command Injection</a>. +</li> + +<!-- LocalWords: CWE untrusted unsanitized Runtime + --> + +</references> +</qhelp> diff --git a/ruby/ql/src/queries/security/cwe-078/CommandInjection.ql b/ruby/ql/src/queries/security/cwe-078/CommandInjection.ql new file mode 100644 index 00000000000..4c2dda966b9 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-078/CommandInjection.ql @@ -0,0 +1,25 @@ +/** + * @name Uncontrolled command line + * @description Using externally controlled strings in a command line may allow a malicious + * user to change the meaning of the command. + * @kind path-problem + * @problem.severity error + * @security-severity 9.8 + * @precision high + * @id rb/command-line-injection + * @tags correctness + * security + * external/cwe/cwe-078 + * external/cwe/cwe-088 + */ + +import ruby +import codeql.ruby.security.CommandInjectionQuery +import DataFlow::PathGraph + +from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink, Source sourceNode +where + config.hasFlowPath(source, sink) and + sourceNode = source.getNode() +select sink.getNode(), source, sink, "This command depends on $@.", sourceNode, + sourceNode.getSourceType() diff --git a/ruby/ql/src/queries/security/cwe-078/KernelOpen.qhelp b/ruby/ql/src/queries/security/cwe-078/KernelOpen.qhelp new file mode 100644 index 00000000000..eea2281cc4a --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-078/KernelOpen.qhelp @@ -0,0 +1,46 @@ +<!DOCTYPE qhelp PUBLIC + "-//Semmle//qhelp//EN" + "qhelp.dtd"> +<qhelp> +<overview> +<p>If <code>Kernel.open</code> is given a file name that starts with a <code>|</code> +character, it will execute the remaining string as a shell command. If a +malicious user can control the file name, they can execute arbitrary code. +The same vulnerability applies to <code>IO.read</code>. +</p> + +</overview> +<recommendation> + +<p>Use <code>File.open</code> instead of <code>Kernel.open</code>, as the former +does not have this vulnerability. Similarly, use <code>File.read</code> instead +of <code>IO.read</code>.</p> + +</recommendation> +<example> + +<p> +The following example shows code that calls <code>Kernel.open</code> on a +user-supplied file path. +</p> + +<sample src="examples/kernel_open.rb" /> + +<p>Instead, <code>File.open</code> should be used, as in the following example.</p> + +<sample src="examples/file_open.rb" /> + +</example> +<references> + +<li> +OWASP: +<a href="https://www.owasp.org/index.php/Command_Injection">Command Injection</a>. +</li> + +<li> +Example CVE: <a href="https://www.ruby-lang.org/en/news/2021/05/02/os-command-injection-in-rdoc/">Command Injection in RDoc</a>. +</li> + +</references> +</qhelp> diff --git a/ruby/ql/src/queries/security/cwe-078/KernelOpen.ql b/ruby/ql/src/queries/security/cwe-078/KernelOpen.ql new file mode 100644 index 00000000000..5bb02183915 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-078/KernelOpen.ql @@ -0,0 +1,76 @@ +/** + * @name Use of `Kernel.open` or `IO.read` + * @description Using `Kernel.open` or `IO.read` may allow a malicious + * user to execute arbitrary system commands. + * @kind path-problem + * @problem.severity error + * @security-severity 9.8 + * @precision high + * @id rb/kernel-open + * @tags correctness + * security + * external/cwe/cwe-078 + * external/cwe/cwe-088 + * external/cwe/cwe-073 + */ + +import ruby +import codeql.ruby.ApiGraphs +import codeql.ruby.frameworks.StandardLibrary +import codeql.ruby.TaintTracking +import codeql.ruby.dataflow.BarrierGuards +import codeql.ruby.dataflow.RemoteFlowSources +import DataFlow::PathGraph + +/** + * Method calls that have a suggested replacement. + */ +abstract class Replacement extends DataFlow::CallNode { + abstract string getFrom(); + + abstract string getTo(); +} + +class KernelOpenCall extends KernelMethodCall, Replacement { + KernelOpenCall() { this.getMethodName() = "open" } + + override string getFrom() { result = "Kernel.open" } + + override string getTo() { result = "File.open" } +} + +class IOReadCall extends DataFlow::CallNode, Replacement { + IOReadCall() { this = API::getTopLevelMember("IO").getAMethodCall("read") } + + override string getFrom() { result = "IO.read" } + + override string getTo() { result = "File.read" } +} + +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "KernelOpen" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { + exists(KernelOpenCall c | c.getArgument(0) = sink) + or + exists(IOReadCall c | c.getArgument(0) = sink) + } + + override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { + guard instanceof StringConstCompare or + guard instanceof StringConstArrayInclusionCall + } +} + +from + Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink, + DataFlow::Node sourceNode, DataFlow::CallNode call +where + config.hasFlowPath(source, sink) and + sourceNode = source.getNode() and + call.asExpr().getExpr().(MethodCall).getArgument(0) = sink.getNode().asExpr().getExpr() +select sink.getNode(), source, sink, + "This call to " + call.(Replacement).getFrom() + + " depends on a user-provided value. Replace it with " + call.(Replacement).getTo() + "." diff --git a/ruby/ql/src/queries/security/cwe-078/examples/command_injection.rb b/ruby/ql/src/queries/security/cwe-078/examples/command_injection.rb new file mode 100644 index 00000000000..e5d43363464 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-078/examples/command_injection.rb @@ -0,0 +1,6 @@ +class UsersController < ActionController::Base + def create + command = params[:command] + system(command) # BAD + end +end diff --git a/ruby/ql/src/queries/security/cwe-078/examples/file_open.rb b/ruby/ql/src/queries/security/cwe-078/examples/file_open.rb new file mode 100644 index 00000000000..3ce1f44817f --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-078/examples/file_open.rb @@ -0,0 +1,6 @@ +class UsersController < ActionController::Base + def create + filename = params[:filename] + File.open(filename) + end + end \ No newline at end of file diff --git a/ruby/ql/src/queries/security/cwe-078/examples/kernel_open.rb b/ruby/ql/src/queries/security/cwe-078/examples/kernel_open.rb new file mode 100644 index 00000000000..84f8bc8db7d --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-078/examples/kernel_open.rb @@ -0,0 +1,6 @@ +class UsersController < ActionController::Base + def create + filename = params[:filename] + open(filename) # BAD + end +end \ No newline at end of file diff --git a/ruby/ql/src/queries/security/cwe-079/ReflectedXSS.qhelp b/ruby/ql/src/queries/security/cwe-079/ReflectedXSS.qhelp new file mode 100644 index 00000000000..85bd15cabcd --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-079/ReflectedXSS.qhelp @@ -0,0 +1,55 @@ +<!DOCTYPE qhelp PUBLIC + "-//Semmle//qhelp//EN" + "qhelp.dtd"> +<qhelp> + + <overview> + <p> + Directly writing user input (for example, an HTTP request parameter) to a webpage, + without properly sanitizing the input first, allows for a cross-site scripting + vulnerability. + </p> + </overview> + + <recommendation> + <p> + To guard against cross-site scripting, escape user input before writing it + to the page. Some frameworks, such as Rails, perform this escaping + implicitly and by default. + </p> + + <p> + Take care when using methods such as <code>html_safe</code> or + <code>raw</code>. They can be used to emit a string without escaping + it, and should only be used when the string has already been manually + escaped (for example, with the Rails <code>html_escape</code> method), or when + the content is otherwise guaranteed to be safe (such as a hard-coded string). + </p> + </recommendation> + + <example> + <p> + The following example is safe because the + <code>params[:user_name]</code> content within the output tags will be + HTML-escaped automatically before being emitted. + </p> + <sample src="examples/reflected_xss_safe.html.erb" /> + + <p> + However, the following example is unsafe because user-controlled input is + emitted without escaping, since it is marked as <code>html_safe</code>. + </p> + <sample src="examples/reflected_xss_unsafe.html.erb" /> + </example> + + <references> + <li> + OWASP: + <a href="https://cheatsheetseries.owasp.org/cheatsheets/Ruby_on_Rails_Cheat_Sheet.html#cross-site-scripting-xss">XSS + Ruby on Rails Cheatsheet</a>. + </li> + <li> + Wikipedia: <a href="http://en.wikipedia.org/wiki/Cross-site_scripting">Cross-site scripting</a>. + </li> + </references> +</qhelp> diff --git a/ruby/ql/src/queries/security/cwe-079/ReflectedXSS.ql b/ruby/ql/src/queries/security/cwe-079/ReflectedXSS.ql new file mode 100644 index 00000000000..d3f95f69fea --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-079/ReflectedXSS.ql @@ -0,0 +1,24 @@ +/** + * @name Reflected server-side cross-site scripting + * @description Writing user input directly to a web page + * allows for a cross-site scripting vulnerability. + * @kind path-problem + * @problem.severity error + * @security-severity 6.1 + * @sub-severity high + * @precision high + * @id rb/reflected-xss + * @tags security + * external/cwe/cwe-079 + * external/cwe/cwe-116 + */ + +import ruby +import codeql.ruby.security.ReflectedXSSQuery +import codeql.ruby.DataFlow +import DataFlow::PathGraph + +from ReflectedXSS::Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Cross-site scripting vulnerability due to $@.", + source.getNode(), "a user-provided value" diff --git a/ruby/ql/src/queries/security/cwe-079/StoredXSS.qhelp b/ruby/ql/src/queries/security/cwe-079/StoredXSS.qhelp new file mode 100644 index 00000000000..71b9f2aa3ef --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-079/StoredXSS.qhelp @@ -0,0 +1,71 @@ +<!DOCTYPE qhelp PUBLIC +"-//Semmle//qhelp//EN" +"qhelp.dtd"> +<qhelp> + <overview> + <p> + Directly writing an uncontrolled stored value (for example, a database + field) to a webpage, without properly sanitizing the value first, allows + for a cross-site scripting vulnerability. + </p> + <p> + This kind of vulnerability is also called <i>stored</i> cross-site + scripting, to distinguish it from other types of cross-site scripting. + </p> + </overview> + + <recommendation> + <p> + To guard against stored cross-site scripting, consider escaping before + using uncontrolled stored values to create HTML content. Some frameworks, + such as Rails, perform this escaping implicitly and by default. + </p> + + <p> + Take care when using methods such as <code>html_safe</code> or + <code>raw</code>. They can be used to emit a string without escaping + it, and should only be used when the string has already been manually + escaped (for example, with the Rails <code>html_escape</code> method), + or when the content is otherwise guaranteed to be safe (such as a + hard-coded string). + </p> + </recommendation> + + <example> + <p> + The following example is safe because the + <code>user.name</code> content within the output tags will be + HTML-escaped automatically before being emitted. + </p> + <sample src="examples/stored_xss_rails_safe.html.erb" /> + + <p> + However, the following example may be unsafe because + <code>user.name</code> is emitted without escaping, since it is marked as + <code>html_safe</code>. If the <code>name</code> is not sanitized before + being written to the database, then an attacker could use this to insert + arbitrary content into the HTML output, including scripts. + </p> + <sample src="examples/stored_xss_rails_unsafe.html.erb" /> + + <p> + In the next example, content from a file on disk is inserted literally + into HTML content. This approach is sometimes used to load script + content, such as extensions for a web application, from files on disk. + Care should taken in these cases to ensure both that the loaded files are + trusted, and that the file cannot be modified by untrusted users. + </p> + <sample src="examples/stored_xss_file_unsafe.html.erb" /> + </example> + + <references> + <li> + OWASP: + <a href="https://cheatsheetseries.owasp.org/cheatsheets/Ruby_on_Rails_Cheat_Sheet.html#cross-site-scripting-xss">XSS + Ruby on Rails Cheatsheet</a>. + </li> + <li> + Wikipedia: <a href="http://en.wikipedia.org/wiki/Cross-site_scripting">Cross-site scripting</a>. + </li> + </references> +</qhelp> diff --git a/ruby/ql/src/queries/security/cwe-079/StoredXSS.ql b/ruby/ql/src/queries/security/cwe-079/StoredXSS.ql new file mode 100644 index 00000000000..e473d5c31e9 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-079/StoredXSS.ql @@ -0,0 +1,23 @@ +/** + * @name Stored cross-site scripting + * @description Using uncontrolled stored values in HTML allows for + * a stored cross-site scripting vulnerability. + * @kind path-problem + * @problem.severity error + * @security-severity 6.1 + * @precision high + * @id rb/stored-xss + * @tags security + * external/cwe/cwe-079 + * external/cwe/cwe-116 + */ + +import ruby +import codeql.ruby.security.StoredXSSQuery +import codeql.ruby.DataFlow +import DataFlow::PathGraph + +from StoredXSS::Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Cross-site scripting vulnerability due to $@", + source.getNode(), "stored value" diff --git a/ruby/ql/src/queries/security/cwe-079/examples/reflected_xss_safe.html.erb b/ruby/ql/src/queries/security/cwe-079/examples/reflected_xss_safe.html.erb new file mode 100644 index 00000000000..5e247b25b7e --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-079/examples/reflected_xss_safe.html.erb @@ -0,0 +1 @@ +<p>Hello <%= params[:user_name] %>!</p> diff --git a/ruby/ql/src/queries/security/cwe-079/examples/reflected_xss_unsafe.html.erb b/ruby/ql/src/queries/security/cwe-079/examples/reflected_xss_unsafe.html.erb new file mode 100644 index 00000000000..b1501af1f0d --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-079/examples/reflected_xss_unsafe.html.erb @@ -0,0 +1 @@ +<p>Hello <%= params[:user_name].html_safe %>!</p> diff --git a/ruby/ql/src/queries/security/cwe-079/examples/stored_xss_file_unsafe.html.erb b/ruby/ql/src/queries/security/cwe-079/examples/stored_xss_file_unsafe.html.erb new file mode 100644 index 00000000000..c8601f41893 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-079/examples/stored_xss_file_unsafe.html.erb @@ -0,0 +1,3 @@ +<script> + <%= File.read(File.join(SCRIPT_DIR, "script.js")).html_safe %> +</script> diff --git a/ruby/ql/src/queries/security/cwe-079/examples/stored_xss_rails_safe.html.erb b/ruby/ql/src/queries/security/cwe-079/examples/stored_xss_rails_safe.html.erb new file mode 100644 index 00000000000..1bdb46c8d11 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-079/examples/stored_xss_rails_safe.html.erb @@ -0,0 +1,2 @@ +<% user = User.find(1) %> +<p>Hello <%= user.name %>!</p> diff --git a/ruby/ql/src/queries/security/cwe-079/examples/stored_xss_rails_unsafe.html.erb b/ruby/ql/src/queries/security/cwe-079/examples/stored_xss_rails_unsafe.html.erb new file mode 100644 index 00000000000..65ef93355fa --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-079/examples/stored_xss_rails_unsafe.html.erb @@ -0,0 +1,2 @@ +<% user = User.find(1) %> +<p>Hello <%= user.name.html_safe %>!</p> diff --git a/ruby/ql/src/queries/security/cwe-089/SqlInjection.qhelp b/ruby/ql/src/queries/security/cwe-089/SqlInjection.qhelp new file mode 100644 index 00000000000..b477a49ca69 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-089/SqlInjection.qhelp @@ -0,0 +1,64 @@ +<!DOCTYPE qhelp PUBLIC + "-//Semmle//qhelp//EN" + "qhelp.dtd"> +<qhelp> + +<overview> +<p> +If a database query (such as a SQL or NoSQL query) is built from +user-provided data without sufficient sanitization, a malicious user +may be able to run malicious database queries. +</p> +</overview> + +<recommendation> +<p> +Most database connector libraries offer a way of safely embedding +untrusted data into a query by means of query parameters or +prepared statements. +</p> +</recommendation> + +<example> +<p> +In the following Rails example, an <code>ActionController</code> class +has a <code>text_bio</code> method to handle requests to fetch a biography +for a specified user. +</p> + +<p> +The user is specified using a parameter, <code>user_name</code> provided by +the client. This value is accessible using the <code>params</code> method. +</p> + +<p> +The method illustrates three different ways to construct and execute an SQL +query to find the user by name. +</p> + +<p> +In the first case, the parameter <code>user_name</code> is inserted into an +SQL fragment using string interpolation. The parameter is user-supplied and +is not sanitized. An attacker could use this to construct SQL queries that +were not intended to be executed here. +</p> + +<p> +The second case uses string concatenation and is vulnerable in the same way +that the first case is. +</p> + +<p> +In the third case, the name is passed in a hash instead. +<code>ActiveRecord</code> will construct a parameterized SQL query that is not +vulnerable to SQL injection attacks. +</p> + +<sample src="examples/SqlInjection.rb" /> +</example> + +<references> +<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/SQL_injection">SQL injection</a>.</li> +<li>OWASP: <a href="https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html">SQL Injection Prevention Cheat Sheet</a>.</li> +</references> +</qhelp> diff --git a/ruby/ql/src/queries/security/cwe-089/SqlInjection.ql b/ruby/ql/src/queries/security/cwe-089/SqlInjection.ql new file mode 100644 index 00000000000..de795e34e71 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-089/SqlInjection.ql @@ -0,0 +1,39 @@ +/** + * @name SQL query built from user-controlled sources + * @description Building a SQL query from user-controlled sources is vulnerable to insertion of + * malicious SQL code by the user. + * @kind path-problem + * @problem.severity error + * @security-severity 8.8 + * @precision high + * @id rb/sql-injection + * @tags security + * external/cwe/cwe-089 + * external/owasp/owasp-a1 + */ + +import ruby +import codeql.ruby.Concepts +import codeql.ruby.DataFlow +import codeql.ruby.dataflow.BarrierGuards +import codeql.ruby.dataflow.RemoteFlowSources +import codeql.ruby.TaintTracking +import DataFlow::PathGraph + +class SQLInjectionConfiguration extends TaintTracking::Configuration { + SQLInjectionConfiguration() { this = "SQLInjectionConfiguration" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof SqlExecution } + + override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { + guard instanceof StringConstCompare or + guard instanceof StringConstArrayInclusionCall + } +} + +from SQLInjectionConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "This SQL query depends on $@.", source.getNode(), + "a user-provided value" diff --git a/ruby/ql/src/queries/security/cwe-089/examples/SqlInjection.rb b/ruby/ql/src/queries/security/cwe-089/examples/SqlInjection.rb new file mode 100644 index 00000000000..24d6b2ad59b --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-089/examples/SqlInjection.rb @@ -0,0 +1,15 @@ +class UserController < ActionController::Base + def text_bio + # BAD -- Using string interpolation + user = User.find_by "name = '#{params[:user_name]}'" + + # BAD -- Using string concatenation + find_str = "name = '" + params[:user_name] + "'" + user = User.find_by(find_str) + + # GOOD -- Using a hash to parameterize arguments + user = User.find_by name: params[:user_name] + + render plain: user&.text_bio + end +end diff --git a/ruby/ql/src/queries/security/cwe-094/CodeInjection.qhelp b/ruby/ql/src/queries/security/cwe-094/CodeInjection.qhelp new file mode 100644 index 00000000000..216b045e4b7 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-094/CodeInjection.qhelp @@ -0,0 +1,46 @@ +<!DOCTYPE qhelp PUBLIC + "-//Semmle//qhelp//EN" + "qhelp.dtd"> +<qhelp> + +<overview> +<p> +Directly evaluating user input (for example, an HTTP request parameter) as code without first +sanitizing the input allows an attacker arbitrary code execution. This can occur when user +input is passed to code that interprets it as an expression to be +evaluated, using methods such as <code>Kernel.eval</code> or <code>Kernel.send</code>. +</p> +</overview> + +<recommendation> +<p> +Avoid including user input in any expression that may be dynamically evaluated. If user input must +be included, use context-specific escaping before including it. +It is important that the correct escaping is used for the type of evaluation that will occur. +</p> +</recommendation> + +<example> +<p> +The following example shows two functions setting a name from a request. +The first function uses <code>eval</code> to execute the <code>set_name</code> method. +This is dangerous as it can allow a malicious user to execute arbitrary code on the server. +For example, the user could supply the value <code>"' + exec('rm -rf') + '"</code> +to destroy the server's file system. +The second function calls the <code>set_name</code> method directly and is thus safe. + +</p> + +<sample src="examples/code_injection.rb" /> +</example> + +<references> +<li> +OWASP: +<a href="https://www.owasp.org/index.php/Code_Injection">Code Injection</a>. +</li> +<li> +Wikipedia: <a href="https://en.wikipedia.org/wiki/Code_injection">Code Injection</a>. +</li> +</references> +</qhelp> diff --git a/ruby/ql/src/queries/security/cwe-094/CodeInjection.ql b/ruby/ql/src/queries/security/cwe-094/CodeInjection.ql new file mode 100644 index 00000000000..60e8e32c2f6 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-094/CodeInjection.ql @@ -0,0 +1,27 @@ +/** + * @name Code injection + * @description Interpreting unsanitized user input as code allows a malicious user to perform arbitrary + * code execution. + * @kind path-problem + * @problem.severity error + * @security-severity 9.3 + * @sub-severity high + * @precision high + * @id rb/code-injection + * @tags security + * external/owasp/owasp-a1 + * external/cwe/cwe-094 + * external/cwe/cwe-095 + * external/cwe/cwe-116 + */ + +import ruby +import codeql.ruby.security.CodeInjectionQuery +import DataFlow::PathGraph + +from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink, Source sourceNode +where + config.hasFlowPath(source, sink) and + sourceNode = source.getNode() +select sink.getNode(), source, sink, "This code execution depends on $@.", sourceNode, + "a user-provided value" diff --git a/ruby/ql/src/queries/security/cwe-094/examples/code_injection.rb b/ruby/ql/src/queries/security/cwe-094/examples/code_injection.rb new file mode 100644 index 00000000000..3ff3cf6cca6 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-094/examples/code_injection.rb @@ -0,0 +1,17 @@ +class UsersController < ActionController::Base + # BAD - Allow user to define code to be run. + def create_bad + first_name = params[:first_name] + eval("set_name(#{first_name})") + end + + # GOOD - Call code directly + def create_good + first_name = params[:first_name] + set_name(first_name) + end + + def set_name(name) + @name = name + end +end diff --git a/ruby/ql/src/queries/security/cwe-1333/PolynomialReDoS.qhelp b/ruby/ql/src/queries/security/cwe-1333/PolynomialReDoS.qhelp new file mode 100644 index 00000000000..0a93a3a245c --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-1333/PolynomialReDoS.qhelp @@ -0,0 +1,113 @@ +<!DOCTYPE qhelp PUBLIC +"-//Semmle//qhelp//EN" +"qhelp.dtd"> + +<qhelp> + + <include src="ReDoSIntroduction.inc.qhelp" /> + + <example> + <p> + + Consider this use of a regular expression, which removes + all leading and trailing whitespace in a string: + + </p> + + <sample language="ruby"> + text.gsub!(/^\s+|\s+$/, '') # BAD + </sample> + + <p> + + The sub-expression <code>"\s+$"</code> will match the + whitespace characters in <code>text</code> from left to + right, but it can start matching anywhere within a + whitespace sequence. This is problematic for strings + that do <strong>not</strong> end with a whitespace + character. Such a string will force the regular + expression engine to process each whitespace sequence + once per whitespace character in the sequence. + + </p> + + <p> + + This ultimately means that the time cost of trimming a + string is quadratic in the length of the string. So a + string like <code>"a b"</code> will take milliseconds to + process, but a similar string with a million spaces + instead of just one will take several minutes. + + </p> + + <p> + + Avoid this problem by rewriting the regular expression + to not contain the ambiguity about when to start + matching whitespace sequences. For instance, by using a + negative look-behind + (<code>/^\s+|(?<!\s)\s+$/</code>), or just by using + the built-in strip method (<code>text.strip!</code>). + + </p> + + <p> + + Note that the sub-expression <code>"^\s+"</code> is + <strong>not</strong> problematic as the <code>^</code> + anchor restricts when that sub-expression can start + matching, and as the regular expression engine matches + from left to right. + + </p> + + </example> + + <example> + + <p> + + As a similar, but slightly subtler problem, consider the + regular expression that matches lines with numbers, possibly written + using scientific notation: + </p> + + <sample language="ruby"> + /^0\.\d+E?\d+$/ # BAD + </sample> + + <p> + + The problem with this regular expression is in the + sub-expression <code>\d+E?\d+</code> because the second + <code>\d+</code> can start matching digits anywhere + after the first match of the first <code>\d+</code> if + there is no <code>E</code> in the input string. + + </p> + + <p> + + This is problematic for strings that do + <strong>not</strong> end with a digit. Such a string + will force the regular expression engine to process each + digit sequence once per digit in the sequence, again + leading to a quadratic time complexity. + + </p> + + <p> + + To make the processing faster, the regular expression + should be rewritten such that the two <code>\d+</code> + sub-expressions do not have overlapping matches: + <code>/^0\.\d+(E\d+)?$/</code>. + + </p> + + </example> + + <include src="ReDoSReferences.inc.qhelp"/> + +</qhelp> diff --git a/ruby/ql/src/queries/security/cwe-1333/PolynomialReDoS.ql b/ruby/ql/src/queries/security/cwe-1333/PolynomialReDoS.ql new file mode 100644 index 00000000000..9ee914c3bf0 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-1333/PolynomialReDoS.ql @@ -0,0 +1,31 @@ +/** + * @name Polynomial regular expression used on uncontrolled data + * @description A regular expression that can require polynomial time + * to match may be vulnerable to denial-of-service attacks. + * @kind path-problem + * @problem.severity warning + * @security-severity 7.5 + * @precision high + * @id rb/polynomial-redos + * @tags security + * external/cwe/cwe-1333 + * external/cwe/cwe-730 + * external/cwe/cwe-400 + */ + +import DataFlow::PathGraph +import codeql.ruby.DataFlow +import codeql.ruby.regexp.PolynomialReDoSQuery +import codeql.ruby.regexp.SuperlinearBackTracking + +from + PolynomialReDoS::Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink, + PolynomialReDoS::Sink sinkNode, PolynomialBackTrackingTerm regexp +where + config.hasFlowPath(source, sink) and + sinkNode = sink.getNode() and + regexp = sinkNode.getRegExp() +select sinkNode.getHighlight(), source, sink, + "This $@ that depends on $@ may run slow on strings " + regexp.getPrefixMessage() + + "with many repetitions of '" + regexp.getPumpString() + "'.", regexp, "regular expression", + source.getNode(), "a user-provided value" diff --git a/ruby/ql/src/queries/security/cwe-1333/ReDoS.qhelp b/ruby/ql/src/queries/security/cwe-1333/ReDoS.qhelp new file mode 100644 index 00000000000..4c85702a0d3 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-1333/ReDoS.qhelp @@ -0,0 +1,28 @@ +<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd"> +<qhelp> + <include src="ReDoSIntroduction.inc.qhelp" /> + <example> + <p>Consider this regular expression:</p> + <sample language="ruby"> + /^_(__|.)+_$/ + </sample> + <p> + Its sub-expression <code>"(__|.)+?"</code> can match the string + <code>"__"</code> either by the first alternative <code>"__"</code> to the + left of the <code>"|"</code> operator, or by two repetitions of the second + alternative <code>"."</code> to the right. Thus, a string consisting of an + odd number of underscores followed by some other character will cause the + regular expression engine to run for an exponential amount of time before + rejecting the input. + </p> + <p> + This problem can be avoided by rewriting the regular expression to remove + the ambiguity between the two branches of the alternative inside the + repetition: + </p> + <sample language="ruby"> + /^_(__|[^_])+_$/ + </sample> + </example> + <include src="ReDoSReferences.inc.qhelp"/> +</qhelp> diff --git a/ruby/ql/src/queries/security/cwe-1333/ReDoS.ql b/ruby/ql/src/queries/security/cwe-1333/ReDoS.ql new file mode 100644 index 00000000000..234772240e3 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-1333/ReDoS.ql @@ -0,0 +1,25 @@ +/** + * @name Inefficient regular expression + * @description A regular expression that requires exponential time to match certain inputs + * can be a performance bottleneck, and may be vulnerable to denial-of-service + * attacks. + * @kind problem + * @problem.severity error + * @security-severity 7.5 + * @precision high + * @id rb/redos + * @tags security + * external/cwe/cwe-1333 + * external/cwe/cwe-730 + * external/cwe/cwe-400 + */ + +import codeql.ruby.regexp.ExponentialBackTracking +import codeql.ruby.regexp.ReDoSUtil +import codeql.ruby.regexp.RegExpTreeView + +from RegExpTerm t, string pump, State s, string prefixMsg +where hasReDoSResult(t, pump, s, prefixMsg) +select t, + "This part of the regular expression may cause exponential backtracking on strings " + prefixMsg + + "containing many repetitions of '" + pump + "'." diff --git a/ruby/ql/src/queries/security/cwe-1333/ReDoSIntroduction.inc.qhelp b/ruby/ql/src/queries/security/cwe-1333/ReDoSIntroduction.inc.qhelp new file mode 100644 index 00000000000..fd3c39bfaa7 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-1333/ReDoSIntroduction.inc.qhelp @@ -0,0 +1,37 @@ +<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd"> +<qhelp> + <overview> + <p> + Some regular expressions take a long time to match certain input strings + to the point where the time it takes to match a string of length <i>n</i> + is proportional to <i>n<sup>k</sup></i> or even <i>2<sup>n</sup></i>. + Such regular expressions can negatively affect performance, or even allow + a malicious user to perform a Denial of Service ("DoS") attack by crafting + an expensive input string for the regular expression to match. + </p> + <p> + The regular expression engine used by the Ruby interpreter (MRI) uses + backtracking non-deterministic finite automata to implement regular + expression matching. While this approach is space-efficient and allows + supporting advanced features like capture groups, it is not time-efficient + in general. The worst-case time complexity of such an automaton can be + polynomial or even exponential, meaning that for strings of a certain + shape, increasing the input length by ten characters may make the + automaton about 1000 times slower. + </p> + <p> + Typically, a regular expression is affected by this problem if it contains + a repetition of the form <code>r*</code> or <code>r+</code> where the + sub-expression <code>r</code> is ambiguous in the sense that it can match + some string in multiple ways. More information about the precise + circumstances can be found in the references. + </p> + </overview> + <recommendation> + <p> + Modify the regular expression to remove the ambiguity, or ensure that the + strings matched with the regular expression are short enough that the + time-complexity does not matter. + </p> + </recommendation> +</qhelp> \ No newline at end of file diff --git a/ruby/ql/src/queries/security/cwe-1333/ReDoSReferences.inc.qhelp b/ruby/ql/src/queries/security/cwe-1333/ReDoSReferences.inc.qhelp new file mode 100644 index 00000000000..d8bb9eb9ee0 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-1333/ReDoSReferences.inc.qhelp @@ -0,0 +1,13 @@ +<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd"> +<qhelp> + <references> + <li> OWASP: + <a href="https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS">Regular expression Denial of Service - ReDoS</a>. + </li> + <li>Wikipedia: <a href="https://en.wikipedia.org/wiki/ReDoS">ReDoS</a>.</li> + <li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Time_complexity">Time complexity</a>.</li> + <li>James Kirrage, Asiri Rathnayake, Hayo Thielecke: + <a href="http://www.cs.bham.ac.uk/~hxt/research/reg-exp-sec.pdf">Static Analysis for Regular Expression Denial-of-Service Attack</a>. + </li> + </references> +</qhelp> diff --git a/ruby/ql/src/queries/security/cwe-295/RequestWithoutValidation.qhelp b/ruby/ql/src/queries/security/cwe-295/RequestWithoutValidation.qhelp new file mode 100644 index 00000000000..c94cd04d03d --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-295/RequestWithoutValidation.qhelp @@ -0,0 +1,52 @@ +<!DOCTYPE qhelp PUBLIC + "-//Semmle//qhelp//EN" + "qhelp.dtd"> +<qhelp> +<overview> +<p> +Certificate validation is the standard authentication method of a secure TLS +connection. Without it, there is no guarantee about who the other party of a TLS +connection is, making man-in-the-middle attacks more likely to occur. +</p> + +<p> +When testing software that uses TLS connections, it may be useful to +disable the certificate validation temporarily. But disabling it in +production environments is strongly discouraged, unless an alternative +method of authentication is used. +</p> +</overview> + +<recommendation> +<p> +Do not disable certificate validation for TLS connections. +</p> +</recommendation> + +<example> + +<p> +The following example shows an HTTPS connection that makes a GET request to a +remote server. But the connection is not secure since the +<code>verify_mode</code> option of the connection is set to +<code>OpenSSL::SSL::VERIFY_NONE</code>. As a consequence, anyone can impersonate +the remote server. +</p> + +<sample src="examples/RequestWithoutValidation.rb"/> + +<p> +To make the connection secure, the <code>verify_mode</code> option should have +its default value, or be explicitly set to +<code>OpenSSL::SSL::VERIFY_PEER</code>. +</p> + +</example> + +<references> +<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Transport_Layer_Security">Transport Layer Security (TLS)</a></li> +<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Man-in-the-middle_attack">Man-in-the-middle attack</a></li> +<li>Ruby-doc: <a href="https://ruby-doc.org/stdlib-3.0.2/libdoc/net/http/rdoc/Net/HTTP.html">Net::HTTP</a></li> +</references> + +</qhelp> \ No newline at end of file diff --git a/ruby/ql/src/queries/security/cwe-295/RequestWithoutValidation.ql b/ruby/ql/src/queries/security/cwe-295/RequestWithoutValidation.ql new file mode 100644 index 00000000000..e9b236897bc --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-295/RequestWithoutValidation.ql @@ -0,0 +1,20 @@ +/** + * @name Request without certificate validation + * @description Making a request without certificate validation can allow + * man-in-the-middle attacks. + * @kind problem + * @problem.severity warning + * @security-severity 7.5 + * @precision medium + * @id rb/request-without-cert-validation + * @tags security + * external/cwe/cwe-295 + */ + +import ruby +import codeql.ruby.Concepts +import codeql.ruby.DataFlow + +from HTTP::Client::Request request, DataFlow::Node disablingNode +where request.disablesCertificateValidation(disablingNode) +select request, "This request may run with $@.", disablingNode, "certificate validation disabled" diff --git a/ruby/ql/src/queries/security/cwe-295/examples/RequestWithoutValidation.rb b/ruby/ql/src/queries/security/cwe-295/examples/RequestWithoutValidation.rb new file mode 100644 index 00000000000..d7ab1df197a --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-295/examples/RequestWithoutValidation.rb @@ -0,0 +1,9 @@ +require "net/https" +require "uri" + +uri = URI.parse "https://example.com/" +http = Net::HTTP.new uri.host, uri.port +http.use_ssl = true +http.verify_mode = OpenSSL::SSL::VERIFY_NONE +request = Net::HTTP::Get.new uri.request_uri +puts http.request(request).body diff --git a/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp b/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp new file mode 100644 index 00000000000..c720cd78745 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp @@ -0,0 +1,60 @@ +<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd"> +<qhelp> + +<overview> +<p> +Deserializing untrusted data using any method that allows the construction of +arbitrary objects is easily exploitable and, in many cases, allows an attacker +to execute arbitrary code. +</p> +</overview> + +<recommendation> +<p> +Avoid deserialization of untrusted data if possible. If the architecture permits +it, use serialization formats that cannot represent arbitarary objects. For +libraries that support it, such as the Ruby standard library's <code>JSON</code> +module, ensure that the parser is configured to disable +deserialization of arbitrary objects. +</p> +</recommendation> + +<example> +<p> +The following example calls the <code>Marshal.load</code>, +<code>JSON.load</code>, <code>YAML.load</code>, and <code>Oj.load</code> methods +on data from an HTTP request. Since these methods are capable of deserializing +to arbitrary objects, this is inherently unsafe. +</p> +<sample src="examples/UnsafeDeserializationBad.rb"/> +<p> +Using <code>JSON.parse</code> and <code>YAML.safe_load</code> instead, as in the +following example, removes the vulnerability. Similarly, calling +<code>Oj.load</code> with any mode other than <code>:object</code> is safe, as +is calling <code>Oj.safe_load</code>. Note that there is no safe way to deserialize +untrusted data using <code>Marshal</code>. +</p> +<sample src="examples/UnsafeDeserializationGood.rb"/> +</example> + +<references> + +<li> +OWASP vulnerability description: +<a href="https://www.owasp.org/index.php/Deserialization_of_untrusted_data">deserialization of untrusted data</a>. +</li> +<li> +Ruby documentation: <a href="https://docs.ruby-lang.org/en/3.0.0/doc/security_rdoc.html">guidance on deserializing objects safely</a>. +</li> +<li> +Ruby documentation: <a href="https://ruby-doc.org/core-3.0.2/Marshal.html#module-Marshal-label-Security+considerations">security guidance on the Marshal library</a>. +</li> +<li> +Ruby documentation: <a href="https://ruby-doc.org/stdlib-3.0.2/libdoc/json/rdoc/JSON.html#method-i-load">security guidance on JSON.load</a>. +</li> +<li> +Ruby documentation: <a href="https://ruby-doc.org/stdlib-3.0.2/libdoc/yaml/rdoc/YAML.html#module-YAML-label-Security">security guidance on the YAML library</a>. +</li> +</references> + +</qhelp> diff --git a/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.ql b/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.ql new file mode 100644 index 00000000000..0df3b7c8d67 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.ql @@ -0,0 +1,21 @@ +/** + * @name Deserialization of user-controlled data + * @description Deserializing user-controlled data may allow attackers to + * execute arbitrary code. + * @kind path-problem + * @problem.severity warning + * @security-severity 9.8 + * @precision high + * @id rb/unsafe-deserialization + * @tags security + * external/cwe/cwe-502 + */ + +import ruby +import DataFlow::PathGraph +import codeql.ruby.DataFlow +import codeql.ruby.security.UnsafeDeserializationQuery + +from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink +where cfg.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Unsafe deserialization of $@.", source.getNode(), "user input" diff --git a/ruby/ql/src/queries/security/cwe-502/examples/UnsafeDeserializationBad.rb b/ruby/ql/src/queries/security/cwe-502/examples/UnsafeDeserializationBad.rb new file mode 100644 index 00000000000..935221fd68c --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-502/examples/UnsafeDeserializationBad.rb @@ -0,0 +1,26 @@ +require 'json' +require 'yaml' +require 'oj' + +class UserController < ActionController::Base + def marshal_example + data = Base64.decode64 params[:data] + object = Marshal.load data + # ... + end + + def json_example + object = JSON.load params[:json] + # ... + end + + def yaml_example + object = YAML.load params[:yaml] + # ... + end + + def oj_example + object = Oj.load params[:json] + # ... + end +end \ No newline at end of file diff --git a/ruby/ql/src/queries/security/cwe-502/examples/UnsafeDeserializationGood.rb b/ruby/ql/src/queries/security/cwe-502/examples/UnsafeDeserializationGood.rb new file mode 100644 index 00000000000..d04121775ed --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-502/examples/UnsafeDeserializationGood.rb @@ -0,0 +1,20 @@ +require 'json' + +class UserController < ActionController::Base + def safe_json_example + object = JSON.parse params[:json] + # ... + end + + def safe_yaml_example + object = YAML.safe_load params[:yaml] + # ... + end + + def safe_oj_example + object = Oj.load params[:yaml], { mode: :strict } + # or + object = Oj.safe_load params[:yaml] + # ... + end +end \ No newline at end of file diff --git a/ruby/ql/src/queries/security/cwe-601/UrlRedirect.qhelp b/ruby/ql/src/queries/security/cwe-601/UrlRedirect.qhelp new file mode 100644 index 00000000000..307580d9cc1 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-601/UrlRedirect.qhelp @@ -0,0 +1,44 @@ +<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd"> +<qhelp> + +<overview> +<p> +Directly incorporating user input into a URL redirect request without validating the input +can facilitate phishing attacks. In these attacks, unsuspecting users can be redirected to a +malicious site that looks very similar to the real site they intend to visit, but which is +controlled by the attacker. +</p> +</overview> + +<recommendation> +<p> +To guard against untrusted URL redirection, it is advisable to avoid putting user input +directly into a redirect URL. Instead, maintain a list of authorized +redirects on the server; then choose from that list based on the user input provided. +</p> +</recommendation> + +<example> +<p> +The following example shows an HTTP request parameter being used directly in a URL redirect +without validating the input, which facilitates phishing attacks: +</p> + +<sample src="examples/redirect_bad.rb"/> + +<p> +One way to remedy the problem is to validate the user input against a known fixed string +before doing the redirection: +</p> + +<sample src="examples/redirect_good.rb"/> +</example> + +<references> +<li>OWASP: <a href="https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html"> + XSS Unvalidated Redirects and Forwards Cheat Sheet</a>.</li> +<li>Rails Guides: <a href="https://guides.rubyonrails.org/security.html#redirection-and-files"> + Redirection and Files</a>.</li> +</references> + +</qhelp> diff --git a/ruby/ql/src/queries/security/cwe-601/UrlRedirect.ql b/ruby/ql/src/queries/security/cwe-601/UrlRedirect.ql new file mode 100644 index 00000000000..aeaa4c29dc5 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-601/UrlRedirect.ql @@ -0,0 +1,22 @@ +/** + * @name URL redirection from remote source + * @description URL redirection based on unvalidated user input + * may cause redirection to malicious web sites. + * @kind path-problem + * @problem.severity error + * @security-severity 6.1 + * @sub-severity low + * @id rb/url-redirection + * @tags security + * external/cwe/cwe-601 + * @precision high + */ + +import ruby +import codeql.ruby.security.UrlRedirectQuery +import codeql.ruby.DataFlow::DataFlow::PathGraph + +from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Untrusted URL redirection due to $@.", source.getNode(), + "a user-provided value" diff --git a/ruby/ql/src/queries/security/cwe-601/examples/redirect_bad.rb b/ruby/ql/src/queries/security/cwe-601/examples/redirect_bad.rb new file mode 100644 index 00000000000..f0a32bac603 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-601/examples/redirect_bad.rb @@ -0,0 +1,5 @@ +class HelloController < ActionController::Base + def hello + redirect_to params[:url] + end +end diff --git a/ruby/ql/src/queries/security/cwe-601/examples/redirect_good.rb b/ruby/ql/src/queries/security/cwe-601/examples/redirect_good.rb new file mode 100644 index 00000000000..f5241f48659 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-601/examples/redirect_good.rb @@ -0,0 +1,11 @@ +class HelloController < ActionController::Base + VALID_REDIRECT = "http://cwe.mitre.org/data/definitions/601.html" + + def hello + if params[:url] == VALID_REDIRECT + redirect_to params[:url] + else + # error + end + end +end diff --git a/ruby/ql/src/queries/security/cwe-611/Xxe.qhelp b/ruby/ql/src/queries/security/cwe-611/Xxe.qhelp new file mode 100644 index 00000000000..edd99bed23f --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-611/Xxe.qhelp @@ -0,0 +1,55 @@ +<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd"> +<qhelp> + +<overview> +<p> +Parsing untrusted XML files with a weakly configured XML parser may lead to an +XML External Entity (XXE) attack. This type of attack uses external entity references +to access arbitrary files on a system, carry out denial-of-service (DoS) attacks, or server-side +request forgery. Even when the result of parsing is not returned to the user, DoS attacks are still possible +and out-of-band data retrieval techniques may allow attackers to steal sensitive data. +</p> +</overview> + +<recommendation> +<p> +The easiest way to prevent XXE attacks is to disable external entity handling when +parsing untrusted data. How this is done depends on the library being used. Note that some +libraries, such as <code>rexml</code>, <code>nokogiri</code> and <code>libxml-ruby</code>, +disable entity expansion by default, so unless you have explicitly enabled entity expansion, +no further action needs to be taken. +</p> +</recommendation> + +<example> +<p> +The following example uses the <code>nokogiri</code> XML parser to parse a string <code>xmlSrc</code>. +If that string is from an untrusted source, this code may be vulnerable to an XXE attack, since +the parser is invoked with the <code>noent</code> option set: +</p> +<sample src="examples/Xxe.rb"/> + +<p> +To guard against XXE attacks, the <code>noent</code> option should be omitted or cleared +(e.g. using <code>nonoent</code>). This means that no entity expansion is undertaken at all, +not even for standard internal entities such as <code>&amp;</code> or <code>&gt;</code>. +If desired, these entities can be expanded in a separate step using utility functions. +</p> +<sample src="examples/XxeGood.rb"/> +</example> + +<references> +<li> +OWASP: +<a href="https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Processing">XML External Entity (XXE) Processing</a>. +</li> +<li> +Timothy Morgen: +<a href="https://research.nccgroup.com/2014/05/19/xml-schema-dtd-and-entity-attacks-a-compendium-of-known-techniques/">XML Schema, DTD, and Entity Attacks</a>. +</li> +<li> +Timur Yunusov, Alexey Osipov: +<a href="https://www.slideshare.net/qqlan/bh-ready-v4">XML Out-Of-Band Data Retrieval</a>. +</li> +</references> +</qhelp> diff --git a/ruby/ql/src/queries/security/cwe-611/Xxe.ql b/ruby/ql/src/queries/security/cwe-611/Xxe.ql new file mode 100644 index 00000000000..c7eae21333e --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-611/Xxe.ql @@ -0,0 +1,43 @@ +/** + * @name XML external entity expansion + * @description Parsing user input as an XML document with external + * entity expansion is vulnerable to XXE attacks. + * @kind path-problem + * @problem.severity error + * @security-severity 9.1 + * @precision high + * @id rb/xxe + * @tags security + * external/cwe/cwe-611 + * external/cwe/cwe-776 + * external/cwe/cwe-827 + */ + +import ruby +import codeql.ruby.dataflow.RemoteFlowSources +import codeql.ruby.TaintTracking +import codeql.ruby.Concepts +import codeql.ruby.DataFlow +import DataFlow::PathGraph + +class UnsafeXxeSink extends DataFlow::ExprNode { + UnsafeXxeSink() { + exists(XmlParserCall parse | + parse.getInput() = this and + parse.externalEntitiesEnabled() + ) + } +} + +class XxeConfig extends TaintTracking::Configuration { + XxeConfig() { this = "XXE.ql::XxeConfig" } + + override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof UnsafeXxeSink } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, XxeConfig conf +where conf.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Unsafe parsing of XML file from $@.", source.getNode(), + "user input" diff --git a/ruby/ql/src/queries/security/cwe-611/examples/Xxe.rb b/ruby/ql/src/queries/security/cwe-611/examples/Xxe.rb new file mode 100644 index 00000000000..28a14396365 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-611/examples/Xxe.rb @@ -0,0 +1,12 @@ +require "nokogiri" + +def process_data1 + xmlSrc = request.body + doc = Nokogiri::XML.parse(xmlSrc, nil, nil, Nokogiri::XML::ParseOptions::NOENT) # BAD +end + +def process_data2 + xmlSrc = request.body + doc = Nokogiri::XML.parse(xmlSrc) { |config| config.noent } # BAD +end + diff --git a/ruby/ql/src/queries/security/cwe-611/examples/XxeGood.rb b/ruby/ql/src/queries/security/cwe-611/examples/XxeGood.rb new file mode 100644 index 00000000000..a1af15612e7 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-611/examples/XxeGood.rb @@ -0,0 +1,12 @@ +require "nokogiri" + +def process_data1 + xmlSrc = request.body + doc = Nokogiri::XML.parse(xmlSrc) # GOOD +end + +def process_data2 + xmlSrc = request.body + doc = Nokogiri::XML.parse(xmlSrc) { |config| config.nonoent } # GOOD +end + diff --git a/ruby/ql/src/queries/security/cwe-732/WeakFilePermissions.qhelp b/ruby/ql/src/queries/security/cwe-732/WeakFilePermissions.qhelp new file mode 100644 index 00000000000..a5f8997e911 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-732/WeakFilePermissions.qhelp @@ -0,0 +1,26 @@ +<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd"> +<qhelp> + +<overview> +<p> +When creating a file, POSIX systems allow permissions to be specified +for owner, group and others separately. Permissions should be kept as +strict as possible, preventing access to the files contents by other users. +</p> + +</overview> + +<recommendation> +<p> +Restrict the file permissions of files to prevent any but the owner being able to read or write to that file +</p> +</recommendation> + +<references> +<li> +Wikipedia: +<a href="https://en.wikipedia.org/wiki/File_system_permissions">File system permissions</a>. +</li> +</references> + +</qhelp> diff --git a/ruby/ql/src/queries/security/cwe-732/WeakFilePermissions.ql b/ruby/ql/src/queries/security/cwe-732/WeakFilePermissions.ql new file mode 100644 index 00000000000..793eafe04bd --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-732/WeakFilePermissions.ql @@ -0,0 +1,64 @@ +/** + * @name Overly permissive file permissions + * @description Allowing files to be readable or writable by users other than the owner may allow sensitive information to be accessed. + * @kind path-problem + * @problem.severity warning + * @security-severity 7.8 + * @id rb/overly-permissive-file + * @tags external/cwe/cwe-732 + * security + * @precision low + */ + +import ruby +import codeql.ruby.Concepts +import codeql.ruby.DataFlow +import DataFlow::PathGraph +import codeql.ruby.ApiGraphs + +bindingset[p] +int world_permission(int p) { result = p.bitAnd(7) } + +// 70 oct = 56 dec +bindingset[p] +int group_permission(int p) { result = p.bitAnd(56) } + +bindingset[p] +string access(int p) { + p.bitAnd(2) != 0 and result = "writable" + or + p.bitAnd(4) != 0 and result = "readable" +} + +/** An expression specifing a file permission that allows group/others read or write access */ +class PermissivePermissionsExpr extends Expr { + // TODO: non-literal expressions? + PermissivePermissionsExpr() { + exists(int perm, string acc | + perm = this.(IntegerLiteral).getValue() and + (acc = access(world_permission(perm)) or acc = access(group_permission(perm))) + ) + or + // adding/setting read or write permissions for all/group/other + this.(StringLiteral).getValueText().regexpMatch(".*[ago][^-=+]*[+=][xXst]*[rw].*") + } +} + +class PermissivePermissionsConfig extends DataFlow::Configuration { + PermissivePermissionsConfig() { this = "PermissivePermissionsConfig" } + + override predicate isSource(DataFlow::Node source) { + exists(PermissivePermissionsExpr ppe | source.asExpr().getExpr() = ppe) + } + + override predicate isSink(DataFlow::Node sink) { + exists(FileSystemPermissionModification mod | mod.getAPermissionNode() = sink) + } +} + +from + DataFlow::PathNode source, DataFlow::PathNode sink, PermissivePermissionsConfig conf, + FileSystemPermissionModification mod +where conf.hasFlowPath(source, sink) and mod.getAPermissionNode() = sink.getNode() +select source.getNode(), source, sink, "Overly permissive mask in $@ sets file to $@.", mod, + mod.toString(), source.getNode(), source.getNode().toString() diff --git a/ruby/ql/src/queries/security/cwe-798/HardcodedCredentials.qhelp b/ruby/ql/src/queries/security/cwe-798/HardcodedCredentials.qhelp new file mode 100644 index 00000000000..65122bd2d19 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-798/HardcodedCredentials.qhelp @@ -0,0 +1,79 @@ +<!DOCTYPE qhelp PUBLIC + "-//Semmle//qhelp//EN" + "qhelp.dtd"> +<qhelp> + +<overview> +<p> +Including unencrypted hard-coded inbound or outbound authentication credentials within source code +or configuration files is dangerous because the credentials may be easily discovered. +</p> +<p> +Source or configuration files containing hard-coded credentials may be visible to an attacker. For +example, the source code may be open source, or it may be leaked or accidentally revealed. +</p> +<p> +For inbound authentication, hard-coded credentials may allow unauthorized access to the system. This +is particularly problematic if the credential is hard-coded in the source code, because it cannot be +disabled easily. For outbound authentication, the hard-coded credentials may provide an attacker with +privileged information or unauthorized access to some other system. +</p> + +</overview> +<recommendation> + +<p> +Remove hard-coded credentials, such as user names, passwords and certificates, from source code, +placing them in configuration files or other data stores if necessary. If possible, store +configuration files including credential data separately from the source code, in a secure location +with restricted access. +</p> + +<p> +For outbound authentication details, consider encrypting the credentials or the enclosing data +stores or configuration files, and using permissions to restrict access. +</p> + +<p> +For inbound authentication details, consider hashing passwords using standard library functions +where possible. For example, <code>OpenSSL::KDF.pbkdf2_hmac</code>. +</p> + +</recommendation> +<example> + +<p> +The following examples shows different types of inbound and outbound authentication. +</p> + +<p> +In the first case, <code>RackAppBad</code>, we accept a password from a remote user, and compare +it against a plaintext string literal. If an attacker acquires the source code they can observe +the password, and can log in to the system. Furthermore, if such an intrusion was discovered, the +application would need to be rewritten and redeployed in order to change the password. +</p> + +<p> +In the second case, <code>RackAppGood</code>, the password is compared to a hashed and salted +password stored in a configuration file, using <code>OpenSSL::KDF.pbkdf2_hmac</code>. +In this case, access to the source code or the assembly would not reveal the password to an +attacker. Even access to the configuration file containing the password hash and salt would be of +little value to an attacker, as it is usually extremely difficult to reverse engineer the password +from the hash and salt. In a real application care should be taken to make the string comparison +of the hashed input against the hashed password take close to constant time, as this will make +timing attacks more difficult. +</p> + +<sample src="HardcodedCredentials.rb" /> + +</example> +<references> + +<li> +OWASP: +<a href="https://www.owasp.org/index.php/Use_of_hard-coded_password">XSS +Use of hard-coded password</a>. +</li> + +</references> +</qhelp> diff --git a/ruby/ql/src/queries/security/cwe-798/HardcodedCredentials.ql b/ruby/ql/src/queries/security/cwe-798/HardcodedCredentials.ql new file mode 100644 index 00000000000..c887793031d --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-798/HardcodedCredentials.ql @@ -0,0 +1,155 @@ +/** + * @name Hard-coded credentials + * @description Credentials are hard coded in the source code of the application. + * @kind path-problem + * @problem.severity error + * @security-severity 9.8 + * @precision high + * @id rb/hardcoded-credentials + * @tags security + * external/cwe/cwe-259 + * external/cwe/cwe-321 + * external/cwe/cwe-798 + */ + +import ruby +import codeql.ruby.DataFlow +import DataFlow::PathGraph +import codeql.ruby.TaintTracking +import codeql.ruby.controlflow.CfgNodes + +bindingset[char, fraction] +predicate fewer_characters_than(StringLiteral str, string char, float fraction) { + exists(string text, int chars | + text = str.getValueText() and + chars = count(int i | text.charAt(i) = char) + | + /* Allow one character */ + chars = 1 or + chars < text.length() * fraction + ) +} + +predicate possible_reflective_name(string name) { + // TODO: implement this? + none() +} + +int char_count(StringLiteral str) { result = count(string c | c = str.getValueText().charAt(_)) } + +predicate capitalized_word(StringLiteral str) { str.getValueText().regexpMatch("[A-Z][a-z]+") } + +predicate format_string(StringLiteral str) { str.getValueText().matches("%{%}%") } + +predicate maybeCredential(Expr e) { + /* A string that is not too short and unlikely to be text or an identifier. */ + exists(StringLiteral str | str = e | + /* At least 10 characters */ + str.getValueText().length() > 9 and + /* Not too much whitespace */ + fewer_characters_than(str, " ", 0.05) and + /* or underscores */ + fewer_characters_than(str, "_", 0.2) and + /* Not too repetitive */ + exists(int chars | chars = char_count(str) | + chars > 15 or + chars * 3 > str.getValueText().length() * 2 + ) and + not possible_reflective_name(str.getValueText()) and + not capitalized_word(str) and + not format_string(str) + ) + or + /* Or, an integer with over 32 bits */ + exists(IntegerLiteral lit | lit = e | + not exists(lit.getValue()) and + /* Not a set of flags or round number */ + not lit.getValueText().matches("%00%") + ) +} + +class HardcodedValueSource extends DataFlow::Node { + HardcodedValueSource() { maybeCredential(this.asExpr().getExpr()) } +} + +/** + * Gets a regular expression for matching names of locations (variables, parameters, keys) that + * indicate the value being held is a credential. + */ +private string getACredentialRegExp() { + result = "(?i).*pass(wd|word|code|phrase)(?!.*question).*" or + result = "(?i).*(puid|username|userid).*" or + result = "(?i).*(cert)(?!.*(format|name)).*" +} + +bindingset[name] +private predicate maybeCredentialName(string name) { + name.regexpMatch(getACredentialRegExp()) and + not name.suffix(name.length() - 4) = "file" +} + +// Positional parameter +private DataFlow::Node credentialParameter() { + exists(Method m, NamedParameter p, int idx | + result.asParameter() = p and + p = m.getParameter(idx) and + maybeCredentialName(p.getName()) + ) +} + +// Keyword argument +private Expr credentialKeywordArgument() { + exists(MethodCall mc, string argKey | + result = mc.getKeywordArgument(argKey) and + maybeCredentialName(argKey) + ) +} + +// An equality check against a credential value +private Expr credentialComparison() { + exists(EqualityOperation op, VariableReadAccess vra | + maybeCredentialName(vra.getVariable().getName()) and + ( + op.getLeftOperand() = result and + op.getRightOperand() = vra + or + op.getLeftOperand() = vra and op.getRightOperand() = result + ) + ) +} + +private predicate isCredentialSink(DataFlow::Node node) { + node = credentialParameter() + or + node.asExpr().getExpr() = credentialKeywordArgument() + or + node.asExpr().getExpr() = credentialComparison() +} + +class CredentialSink extends DataFlow::Node { + CredentialSink() { isCredentialSink(this) } +} + +class HardcodedCredentialsConfiguration extends DataFlow::Configuration { + HardcodedCredentialsConfiguration() { this = "HardcodedCredentialsConfiguration" } + + override predicate isSource(DataFlow::Node source) { source instanceof HardcodedValueSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof CredentialSink } + + override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(ExprNodes::BinaryOperationCfgNode binop | + ( + binop.getLeftOperand() = node1.asExpr() or + binop.getRightOperand() = node1.asExpr() + ) and + binop = node2.asExpr() and + // string concatenation + binop.getExpr() instanceof AddExpr + ) + } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, HardcodedCredentialsConfiguration conf +where conf.hasFlowPath(source, sink) +select source.getNode(), source, sink, "Use of $@.", source.getNode(), "hardcoded credentials" diff --git a/ruby/ql/src/queries/security/cwe-798/HardcodedCredentials.rb b/ruby/ql/src/queries/security/cwe-798/HardcodedCredentials.rb new file mode 100644 index 00000000000..66f9e6bcbb1 --- /dev/null +++ b/ruby/ql/src/queries/security/cwe-798/HardcodedCredentials.rb @@ -0,0 +1,40 @@ +require 'rack' +require 'yaml' +require 'openssl' + +class RackAppBad + def call(env) + req = Rack::Request.new(env) + password = req.params['password'] + + # BAD: Inbound authentication made by comparison to string literal + if password == 'myPa55word' + [200, {'Content-type' => 'text/plain'}, ['OK']] + else + [403, {'Content-type' => 'text/plain'}, ['Permission denied']] + end + end +end + +class RackAppGood + def call(env) + req = Rack::Request.new(env) + password = req.params['password'] + + config_file = YAML.load_file('config.yml') + hashed_password = config_file['hashed_password'] + salt = [config_file['salt']].pack('H*') + + #GOOD: Inbound authentication made by comparing to a hash password from a config file. + hash = OpenSSL::Digest::SHA256.new + dk = OpenSSL::KDF.pbkdf2_hmac( + password, salt: salt, hash: hash, iterations: 100_000, length: hash.digest_length + ) + hashed_input = dk.unpack('H*').first + if hashed_password == hashed_input + [200, {'Content-type' => 'text/plain'}, ['OK']] + else + [403, {'Content-type' => 'text/plain'}, ['Permission denied']] + end + end +end diff --git a/ruby/ql/src/queries/summary/LinesOfCode.ql b/ruby/ql/src/queries/summary/LinesOfCode.ql new file mode 100644 index 00000000000..f727cf504d9 --- /dev/null +++ b/ruby/ql/src/queries/summary/LinesOfCode.ql @@ -0,0 +1,15 @@ +/** + * @id rb/summary/lines-of-code + * @name Total lines of Ruby code in the database + * @description The total number of lines of Ruby code from the source code + * directory, including external libraries and auto-generated files. This is a + * useful metric of the size of a database. This query counts the lines of + * code, excluding whitespace or comments. + * @kind metric + * @tags summary + * lines-of-code + */ + +import ruby + +select sum(RubyFile f | exists(f.getRelativePath()) | f.getNumberOfLinesOfCode()) diff --git a/ruby/ql/src/queries/summary/LinesOfUserCode.ql b/ruby/ql/src/queries/summary/LinesOfUserCode.ql new file mode 100644 index 00000000000..19f4f46fb8d --- /dev/null +++ b/ruby/ql/src/queries/summary/LinesOfUserCode.ql @@ -0,0 +1,19 @@ +/** + * @id rb/summary/lines-of-user-code + * @name Total Lines of user written Ruby code in the database + * @description The total number of lines of Ruby code from the source code + * directory, excluding external library and auto-generated files. This + * query counts the lines of code, excluding whitespace or comments. + * @kind metric + * @tags summary + */ + +import ruby + +select sum(RubyFile f | + f.fromSource() and + exists(f.getRelativePath()) and + not f.getAbsolutePath().matches("%/vendor/%") + | + f.getNumberOfLinesOfCode() + ) diff --git a/ruby/ql/src/queries/summary/NumberOfFilesExtractedWithErrors.ql b/ruby/ql/src/queries/summary/NumberOfFilesExtractedWithErrors.ql new file mode 100644 index 00000000000..1a68d2c57e6 --- /dev/null +++ b/ruby/ql/src/queries/summary/NumberOfFilesExtractedWithErrors.ql @@ -0,0 +1,15 @@ +/** + * @id rb/summary/number-of-files-extracted-with-errors + * @name Total number of files that were extracted with errors + * @description The total number of Ruby code files that we extracted, but where + * at least one extraction error occurred in the process. + * @kind metric + * @tags summary + */ + +import ruby +import codeql.ruby.Diagnostics + +select count(File f | + exists(ExtractionError e | e.getLocation().getFile() = f) and exists(f.getRelativePath()) + ) diff --git a/ruby/ql/src/queries/summary/NumberOfSuccessfullyExtractedFiles.ql b/ruby/ql/src/queries/summary/NumberOfSuccessfullyExtractedFiles.ql new file mode 100644 index 00000000000..356989935e1 --- /dev/null +++ b/ruby/ql/src/queries/summary/NumberOfSuccessfullyExtractedFiles.ql @@ -0,0 +1,15 @@ +/** + * @id rb/summary/number-of-successfully-extracted-files + * @name Total number of files that were extracted without error + * @description The total number of Ruby code files that we extracted without + * encountering any extraction errors + * @kind metric + * @tags summary + */ + +import ruby +import codeql.ruby.Diagnostics + +select count(File f | + not exists(ExtractionError e | e.getLocation().getFile() = f) and exists(f.getRelativePath()) + ) diff --git a/ruby/ql/src/queries/variables/DeadStoreOfLocal.ql b/ruby/ql/src/queries/variables/DeadStoreOfLocal.ql new file mode 100644 index 00000000000..5ce06a0c182 --- /dev/null +++ b/ruby/ql/src/queries/variables/DeadStoreOfLocal.ql @@ -0,0 +1,28 @@ +/** + * @name Useless assignment to local variable + * @description An assignment to a local variable that is not used later on, or whose value is always + * overwritten, has no effect. + * @kind problem + * @problem.severity warning + * @id rb/useless-assignment-to-local + * @tags maintainability + * external/cwe/cwe-563 + * @precision low + */ + +import ruby +import codeql.ruby.dataflow.SSA + +class RelevantLocalVariableWriteAccess extends LocalVariableWriteAccess { + RelevantLocalVariableWriteAccess() { + not this.getVariable().getName().charAt(0) = "_" and + not this = any(Parameter p).getAVariable().getDefiningAccess() + } +} + +from RelevantLocalVariableWriteAccess write, LocalVariable v +where + v = write.getVariable() and + exists(write.getAControlFlowNode()) and + not exists(Ssa::WriteDefinition def | def.getWriteAccess() = write) +select write, "This assignment to $@ is useless, since its value is never read.", v, v.getName() diff --git a/ruby/ql/src/queries/variables/UninitializedLocal.ql b/ruby/ql/src/queries/variables/UninitializedLocal.ql new file mode 100644 index 00000000000..ef134eddd70 --- /dev/null +++ b/ruby/ql/src/queries/variables/UninitializedLocal.ql @@ -0,0 +1,32 @@ +/** + * @name Potentially uninitialized local variable + * @description Using a local variable before it is initialized gives the variable a default + * 'nil' value. + * @kind problem + * @problem.severity error + * @id rb/uninitialized-local-variable + * @tags reliability + * correctness + * @precision low + */ + +import ruby +import codeql.ruby.dataflow.SSA + +class RelevantLocalVariableReadAccess extends LocalVariableReadAccess { + RelevantLocalVariableReadAccess() { + not exists(MethodCall c | + c.getReceiver() = this and + c.getMethodName() = "nil?" + ) + } +} + +from RelevantLocalVariableReadAccess read, LocalVariable v +where + v = read.getVariable() and + exists(Ssa::Definition def | + def.getAnUltimateDefinition() instanceof Ssa::UninitializedDefinition and + read = def.getARead().getExpr() + ) +select read, "Local variable $@ may be used before it is initialized.", v, v.getName() diff --git a/ruby/ql/src/queries/variables/UnusedParameter.ql b/ruby/ql/src/queries/variables/UnusedParameter.ql new file mode 100644 index 00000000000..1aa1a6bc462 --- /dev/null +++ b/ruby/ql/src/queries/variables/UnusedParameter.ql @@ -0,0 +1,27 @@ +/** + * @name Unused parameter. + * @description A parameter that is not used later on, or whose value is always overwritten, + * can be removed. + * @kind problem + * @problem.severity warning + * @id rb/unused-parameter + * @tags maintainability + * external/cwe/cwe-563 + * @precision low + */ + +import ruby +import codeql.ruby.dataflow.SSA + +class RelevantParameterVariable extends LocalVariable { + RelevantParameterVariable() { + exists(Parameter p | + this = p.getAVariable() and + not this.getName().charAt(0) = "_" + ) + } +} + +from RelevantParameterVariable v +where not exists(Ssa::WriteDefinition def | def.getWriteAccess() = v.getDefiningAccess()) +select v, "Unused parameter." diff --git a/ruby/ql/test/TestUtilities/InlineExpectationsTest.qll b/ruby/ql/test/TestUtilities/InlineExpectationsTest.qll new file mode 100644 index 00000000000..52a790cca28 --- /dev/null +++ b/ruby/ql/test/TestUtilities/InlineExpectationsTest.qll @@ -0,0 +1,346 @@ +/** + * Provides a library for writing QL tests whose success or failure is based on expected results + * embedded in the test source code as comments, rather than the contents of an `.expected` file + * (in that the `.expected` file should always be empty). + * + * To add this framework to a new language: + * - Add a file `InlineExpectationsTestPrivate.qll` that defines a `ExpectationComment` class. This class + * must support a `getContents` method that returns the contents of the given comment, _excluding_ + * the comment indicator itself. It should also define `toString` and `getLocation` as usual. + * + * To create a new inline expectations test: + * - Declare a class that extends `InlineExpectationsTest`. In the characteristic predicate of the + * new class, bind `this` to a unique string (usually the name of the test). + * - Override the `hasActualResult()` predicate to produce the actual results of the query. For each + * result, specify a `Location`, a text description of the element for which the result was + * reported, a short string to serve as the tag to identify expected results for this test, and the + * expected value of the result. + * - Override `getARelevantTag()` to return the set of tags that can be produced by + * `hasActualResult()`. Often this is just a single tag. + * + * Example: + * ```ql + * class ConstantValueTest extends InlineExpectationsTest { + * ConstantValueTest() { this = "ConstantValueTest" } + * + * override string getARelevantTag() { + * // We only use one tag for this test. + * result = "const" + * } + * + * override predicate hasActualResult( + * Location location, string element, string tag, string value + * ) { + * exists(Expr e | + * tag = "const" and // The tag for this test. + * value = e.getValue() and // The expected value. Will only hold for constant expressions. + * location = e.getLocation() and // The location of the result to be reported. + * element = e.toString() // The display text for the result. + * ) + * } + * } + * ``` + * + * There is no need to write a `select` clause or query predicate. All of the differences between + * expected results and actual results will be reported in the `failures()` query predicate. + * + * To annotate the test source code with an expected result, place a comment starting with a `$` on the + * same line as the expected result, with text of the following format as the body of the comment: + * + * `tag=expected-value` + * + * Where `tag` is the value of the `tag` parameter from `hasActualResult()`, and `expected-value` is + * the value of the `value` parameter from `hasActualResult()`. The `=expected-value` portion may be + * omitted, in which case `expected-value` is treated as the empty string. Multiple expectations may + * be placed in the same comment. Any actual result that + * appears on a line that does not contain a matching expected result comment will be reported with + * a message of the form "Unexpected result: tag=value". Any expected result comment for which there + * is no matching actual result will be reported with a message of the form + * "Missing result: tag=expected-value". + * + * Example: + * ```cpp + * int i = x + 5; // $ const=5 + * int j = y + (7 - 3) // $ const=7 const=3 const=4 // The result of the subtraction is a constant. + * ``` + * + * For tests that contain known missing and spurious results, it is possible to further + * annotate that a particular expected result is known to be spurious, or that a particular + * missing result is known to be missing: + * + * `$ SPURIOUS: tag=expected-value` // Spurious result + * `$ MISSING: tag=expected-value` // Missing result + * + * A spurious expectation is treated as any other expected result, except that if there is no + * matching actual result, the message will be of the form "Fixed spurious result: tag=value". A + * missing expectation is treated as if there were no expected result, except that if a + * matching expected result is found, the message will be of the form + * "Fixed missing result: tag=value". + * + * A single line can contain all the expected, spurious and missing results of that line. For instance: + * `$ tag1=value1 SPURIOUS: tag2=value2 MISSING: tag3=value3`. + * + * If the same result value is expected for two or more tags on the same line, there is a shorthand + * notation available: + * + * `tag1,tag2=expected-value` + * + * is equivalent to: + * + * `tag1=expected-value tag2=expected-value` + */ + +private import InlineExpectationsTestPrivate + +/** + * Base class for tests with inline expectations. The test extends this class to provide the actual + * results of the query, which are then compared with the expected results in comments to produce a + * list of failure messages that point out where the actual results differ from the expected + * results. + */ +abstract class InlineExpectationsTest extends string { + bindingset[this] + InlineExpectationsTest() { any() } + + /** + * Returns all tags that can be generated by this test. Most tests will only ever produce a single + * tag. Any expected result comments for a tag that is not returned by the `getARelevantTag()` + * predicate for an active test will be ignored. This makes it possible to write multiple tests in + * different `.ql` files that all query the same source code. + */ + abstract string getARelevantTag(); + + /** + * Returns the actual results of the query that is being tested. Each result consist of the + * following values: + * - `location` - The source code location of the result. Any expected result comment must appear + * on the start line of this location. + * - `element` - Display text for the element on which the result is reported. + * - `tag` - The tag that marks this result as coming from this test. This must be one of the tags + * returned by `getARelevantTag()`. + * - `value` - The value of the result, which will be matched against the value associated with + * `tag` in any expected result comment on that line. + */ + abstract predicate hasActualResult(Location location, string element, string tag, string value); + + final predicate hasFailureMessage(FailureLocatable element, string message) { + exists(ActualResult actualResult | + actualResult.getTest() = this and + element = actualResult and + ( + exists(FalseNegativeExpectation falseNegative | + falseNegative.matchesActualResult(actualResult) and + message = "Fixed missing result:" + falseNegative.getExpectationText() + ) + or + not exists(ValidExpectation expectation | expectation.matchesActualResult(actualResult)) and + message = "Unexpected result: " + actualResult.getExpectationText() + ) + ) + or + exists(ValidExpectation expectation | + not exists(ActualResult actualResult | expectation.matchesActualResult(actualResult)) and + expectation.getTag() = getARelevantTag() and + element = expectation and + ( + expectation instanceof GoodExpectation and + message = "Missing result:" + expectation.getExpectationText() + or + expectation instanceof FalsePositiveExpectation and + message = "Fixed spurious result:" + expectation.getExpectationText() + ) + ) + or + exists(InvalidExpectation expectation | + element = expectation and + message = "Invalid expectation syntax: " + expectation.getExpectation() + ) + } +} + +/** + * RegEx pattern to match a comment containing one or more expected results. The comment must have + * `$` as its first non-whitespace character. Any subsequent character + * is treated as part of the expected results, except that the comment may contain a `//` sequence + * to treat the remainder of the line as a regular (non-interpreted) comment. + */ +private string expectationCommentPattern() { result = "\\s*\\$((?:[^/]|/[^/])*)(?://.*)?" } + +/** + * The possible columns in an expectation comment. The `TDefaultColumn` branch represents the first + * column in a comment. This column is not precedeeded by a name. `TNamedColumn(name)` represents a + * column containing expected results preceeded by the string `name:`. + */ +private newtype TColumn = + TDefaultColumn() or + TNamedColumn(string name) { name = ["MISSING", "SPURIOUS"] } + +bindingset[start, content] +private int getEndOfColumnPosition(int start, string content) { + result = + min(string name, int cand | + exists(TNamedColumn(name)) and + cand = content.indexOf(name + ":") and + cand >= start + | + cand + ) + or + not exists(string name | + exists(TNamedColumn(name)) and + content.indexOf(name + ":") >= start + ) and + result = content.length() +} + +private predicate getAnExpectation( + ExpectationComment comment, TColumn column, string expectation, string tags, string value +) { + exists(string content | + content = comment.getContents().regexpCapture(expectationCommentPattern(), 1) and + ( + column = TDefaultColumn() and + exists(int end | + end = getEndOfColumnPosition(0, content) and + expectation = content.prefix(end).regexpFind(expectationPattern(), _, _).trim() + ) + or + exists(string name, int start, int end | + column = TNamedColumn(name) and + start = content.indexOf(name + ":") + name.length() + 1 and + end = getEndOfColumnPosition(start, content) and + expectation = content.substring(start, end).regexpFind(expectationPattern(), _, _).trim() + ) + ) + ) and + tags = expectation.regexpCapture(expectationPattern(), 1) and + if exists(expectation.regexpCapture(expectationPattern(), 2)) + then value = expectation.regexpCapture(expectationPattern(), 2) + else value = "" +} + +private string getColumnString(TColumn column) { + column = TDefaultColumn() and result = "" + or + column = TNamedColumn(result) +} + +/** + * RegEx pattern to match a single expected result, not including the leading `$`. It consists of one or + * more comma-separated tags containing only letters, digits, `-` and `_` (note that the first character + * must not be a digit), optionally followed by `=` and the expected value. + */ +private string expectationPattern() { + exists(string tag, string tags, string value | + tag = "[A-Za-z-_][A-Za-z-_0-9]*" and + tags = "((?:" + tag + ")(?:\\s*,\\s*" + tag + ")*)" and + // In Python, we allow both `"` and `'` for strings, as well as the prefixes `bru`. + // For example, `b"foo"`. + value = "((?:[bru]*\"[^\"]*\"|[bru]*'[^']*'|\\S+)*)" and + result = tags + "(?:=" + value + ")?" + ) +} + +private newtype TFailureLocatable = + TActualResult( + InlineExpectationsTest test, Location location, string element, string tag, string value + ) { + test.hasActualResult(location, element, tag, value) + } or + TValidExpectation(ExpectationComment comment, string tag, string value, string knownFailure) { + exists(TColumn column, string tags | + getAnExpectation(comment, column, _, tags, value) and + tag = tags.splitAt(",") and + knownFailure = getColumnString(column) + ) + } or + TInvalidExpectation(ExpectationComment comment, string expectation) { + getAnExpectation(comment, _, expectation, _, _) and + not expectation.regexpMatch(expectationPattern()) + } + +class FailureLocatable extends TFailureLocatable { + string toString() { none() } + + Location getLocation() { none() } + + final string getExpectationText() { result = getTag() + "=" + getValue() } + + string getTag() { none() } + + string getValue() { none() } +} + +class ActualResult extends FailureLocatable, TActualResult { + InlineExpectationsTest test; + Location location; + string element; + string tag; + string value; + + ActualResult() { this = TActualResult(test, location, element, tag, value) } + + override string toString() { result = element } + + override Location getLocation() { result = location } + + InlineExpectationsTest getTest() { result = test } + + override string getTag() { result = tag } + + override string getValue() { result = value } +} + +abstract private class Expectation extends FailureLocatable { + ExpectationComment comment; + + override string toString() { result = comment.toString() } + + override Location getLocation() { result = comment.getLocation() } +} + +private class ValidExpectation extends Expectation, TValidExpectation { + string tag; + string value; + string knownFailure; + + ValidExpectation() { this = TValidExpectation(comment, tag, value, knownFailure) } + + override string getTag() { result = tag } + + override string getValue() { result = value } + + string getKnownFailure() { result = knownFailure } + + predicate matchesActualResult(ActualResult actualResult) { + getLocation().getStartLine() = actualResult.getLocation().getStartLine() and + getLocation().getFile() = actualResult.getLocation().getFile() and + getTag() = actualResult.getTag() and + getValue() = actualResult.getValue() + } +} + +/* Note: These next three classes correspond to all the possible values of type `TColumn`. */ +class GoodExpectation extends ValidExpectation { + GoodExpectation() { getKnownFailure() = "" } +} + +class FalsePositiveExpectation extends ValidExpectation { + FalsePositiveExpectation() { getKnownFailure() = "SPURIOUS" } +} + +class FalseNegativeExpectation extends ValidExpectation { + FalseNegativeExpectation() { getKnownFailure() = "MISSING" } +} + +class InvalidExpectation extends Expectation, TInvalidExpectation { + string expectation; + + InvalidExpectation() { this = TInvalidExpectation(comment, expectation) } + + string getExpectation() { result = expectation } +} + +query predicate failures(FailureLocatable element, string message) { + exists(InlineExpectationsTest test | test.hasFailureMessage(element, message)) +} diff --git a/ruby/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll b/ruby/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll new file mode 100644 index 00000000000..8906bf1dc39 --- /dev/null +++ b/ruby/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll @@ -0,0 +1,9 @@ +import ruby +import codeql.ruby.ast.internal.TreeSitter + +/** + * A class representing line comments in Ruby. + */ +class ExpectationComment extends Ruby::Comment { + string getContents() { result = this.getValue().suffix(1) } +} diff --git a/ruby/ql/test/library-tests/ast/Ast.expected b/ruby/ql/test/library-tests/ast/Ast.expected new file mode 100644 index 00000000000..c03b34dac99 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/Ast.expected @@ -0,0 +1,2230 @@ +gems/Gemfile: +# 1| [Toplevel] Gemfile +# 1| getStmt: [MethodCall] call to source +# 1| getReceiver: [Self, SelfVariableAccess] self +# 1| getArgument: [StringLiteral] "https://rubygems.org" +# 1| getComponent: [StringTextComponent] https://rubygems.org +# 3| getStmt: [MethodCall] call to gem +# 3| getReceiver: [Self, SelfVariableAccess] self +# 3| getArgument: [StringLiteral] "foo_gem" +# 3| getComponent: [StringTextComponent] foo_gem +# 3| getArgument: [StringLiteral] "~> 2.0" +# 3| getComponent: [StringTextComponent] ~> 2.0 +# 5| getStmt: [MethodCall] call to source +# 5| getReceiver: [Self, SelfVariableAccess] self +# 5| getArgument: [StringLiteral] "https://gems.example.com" +# 5| getComponent: [StringTextComponent] https://gems.example.com +# 5| getBlock: [DoBlock] do ... end +# 6| getStmt: [MethodCall] call to gem +# 6| getReceiver: [Self, SelfVariableAccess] self +# 6| getArgument: [StringLiteral] "my_gem" +# 6| getComponent: [StringTextComponent] my_gem +# 6| getArgument: [StringLiteral] "1.0" +# 6| getComponent: [StringTextComponent] 1.0 +# 7| getStmt: [MethodCall] call to gem +# 7| getReceiver: [Self, SelfVariableAccess] self +# 7| getArgument: [StringLiteral] "another_gem" +# 7| getComponent: [StringTextComponent] another_gem +# 7| getArgument: [StringLiteral] "3.1.4" +# 7| getComponent: [StringTextComponent] 3.1.4 +calls/calls.rb: +# 1| [Toplevel] calls.rb +# 2| getStmt: [MethodCall] call to foo +# 2| getReceiver: [Self, SelfVariableAccess] self +# 5| getStmt: [MethodCall] call to bar +# 5| getReceiver: [ConstantReadAccess] Foo +# 8| getStmt: [MethodCall] call to bar +# 8| getReceiver: [Self, SelfVariableAccess] self +# 11| getStmt: [MethodCall] call to bar +# 11| getReceiver: [IntegerLiteral] 123 +# 14| getStmt: [MethodCall] call to foo +# 14| getReceiver: [Self, SelfVariableAccess] self +# 14| getArgument: [IntegerLiteral] 0 +# 14| getArgument: [IntegerLiteral] 1 +# 14| getArgument: [IntegerLiteral] 2 +# 17| getStmt: [MethodCall] call to foo +# 17| getReceiver: [Self, SelfVariableAccess] self +# 17| getBlock: [BraceBlock] { ... } +# 17| getParameter: [SimpleParameter] x +# 17| getDefiningAccess: [LocalVariableAccess] x +# 17| getStmt: [AddExpr] ... + ... +# 17| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 17| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 20| getStmt: [MethodCall] call to foo +# 20| getReceiver: [Self, SelfVariableAccess] self +# 20| getBlock: [DoBlock] do ... end +# 20| getParameter: [SimpleParameter] x +# 20| getDefiningAccess: [LocalVariableAccess] x +# 21| getStmt: [AddExpr] ... + ... +# 21| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 21| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 25| getStmt: [MethodCall] call to bar +# 25| getReceiver: [IntegerLiteral] 123 +# 25| getArgument: [StringLiteral] "foo" +# 25| getComponent: [StringTextComponent] foo +# 25| getBlock: [DoBlock] do ... end +# 25| getParameter: [SimpleParameter] x +# 25| getDefiningAccess: [LocalVariableAccess] x +# 26| getStmt: [AddExpr] ... + ... +# 26| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 26| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 30| getStmt: [Method] method_that_yields +# 31| getStmt: [YieldCall] yield ... +# 35| getStmt: [Method] another_method_that_yields +# 36| getStmt: [YieldCall] yield ... +# 36| getArgument: [IntegerLiteral] 100 +# 36| getArgument: [IntegerLiteral] 200 +# 46| getStmt: [MethodCall] call to foo +# 46| getReceiver: [Self, SelfVariableAccess] self +# 47| getStmt: [MethodCall] call to foo +# 47| getReceiver: [ConstantReadAccess] X +# 50| getStmt: [ParenthesizedExpr] ( ... ) +# 50| getStmt: [MethodCall] call to foo +# 50| getReceiver: [Self, SelfVariableAccess] self +# 51| getStmt: [ParenthesizedExpr] ( ... ) +# 51| getStmt: [MethodCall] call to foo +# 51| getReceiver: [ConstantReadAccess] X +# 54| getStmt: [MethodCall] call to some_func +# 54| getReceiver: [Self, SelfVariableAccess] self +# 54| getArgument: [MethodCall] call to foo +# 54| getReceiver: [Self, SelfVariableAccess] self +# 55| getStmt: [MethodCall] call to some_func +# 55| getReceiver: [Self, SelfVariableAccess] self +# 55| getArgument: [MethodCall] call to foo +# 55| getReceiver: [ConstantReadAccess] X +# 58| getStmt: [ArrayLiteral] [...] +# 58| getElement: [MethodCall] call to foo +# 58| getReceiver: [Self, SelfVariableAccess] self +# 59| getStmt: [ArrayLiteral] [...] +# 59| getElement: [MethodCall] call to foo +# 59| getReceiver: [ConstantReadAccess] X +# 62| getStmt: [AssignExpr] ... = ... +# 62| getAnOperand/getLeftOperand: [LocalVariableAccess] var1 +# 62| getAnOperand/getRightOperand: [MethodCall] call to foo +# 62| getReceiver: [Self, SelfVariableAccess] self +# 63| getStmt: [AssignExpr] ... = ... +# 63| getAnOperand/getLeftOperand: [LocalVariableAccess] var1 +# 63| getAnOperand/getRightOperand: [MethodCall] call to foo +# 63| getReceiver: [ConstantReadAccess] X +# 66| getStmt: [AssignAddExpr] ... += ... +# 66| getAnOperand/getLeftOperand: [LocalVariableAccess] var1 +# 66| getAnOperand/getRightOperand: [MethodCall] call to bar +# 66| getReceiver: [Self, SelfVariableAccess] self +# 67| getStmt: [AssignAddExpr] ... += ... +# 67| getAnOperand/getLeftOperand: [LocalVariableAccess] var1 +# 67| getAnOperand/getRightOperand: [MethodCall] call to bar +# 67| getReceiver: [ConstantReadAccess] X +# 70| getStmt: [AssignExpr] ... = ... +# 70| getAnOperand/getLeftOperand: [LocalVariableAccess] var1 +# 70| getAnOperand/getRightOperand: [ArgumentList] ..., ... +# 70| getElement: [MethodCall] call to foo +# 70| getReceiver: [Self, SelfVariableAccess] self +# 70| getElement: [MethodCall] call to bar +# 70| getReceiver: [ConstantReadAccess] X +# 73| getStmt: [BeginExpr] begin ... +# 74| getStmt: [MethodCall] call to foo +# 74| getReceiver: [Self, SelfVariableAccess] self +# 75| getStmt: [MethodCall] call to foo +# 75| getReceiver: [ConstantReadAccess] X +# 79| getBeginBlock: [BeginBlock] BEGIN { ... } +# 79| getStmt: [MethodCall] call to foo +# 79| getReceiver: [Self, SelfVariableAccess] self +# 79| getStmt: [MethodCall] call to bar +# 79| getReceiver: [ConstantReadAccess] X +# 82| getStmt: [EndBlock] END { ... } +# 82| getStmt: [MethodCall] call to foo +# 82| getReceiver: [Self, SelfVariableAccess] self +# 82| getStmt: [MethodCall] call to bar +# 82| getReceiver: [ConstantReadAccess] X +# 85| getStmt: [AddExpr] ... + ... +# 85| getAnOperand/getLeftOperand/getReceiver: [MethodCall] call to foo +# 85| getReceiver: [Self, SelfVariableAccess] self +# 85| getAnOperand/getArgument/getRightOperand: [MethodCall] call to bar +# 85| getReceiver: [ConstantReadAccess] X +# 88| getStmt: [NotExpr] ! ... +# 88| getAnOperand/getOperand/getReceiver: [MethodCall] call to foo +# 88| getReceiver: [Self, SelfVariableAccess] self +# 89| getStmt: [ComplementExpr] ~ ... +# 89| getAnOperand/getOperand/getReceiver: [MethodCall] call to bar +# 89| getReceiver: [ConstantReadAccess] X +# 92| getStmt: [MethodCall] call to foo +# 92| getReceiver: [Self, SelfVariableAccess] self +# 92| getBlock: [BraceBlock] { ... } +# 92| getStmt: [MethodCall] call to bar +# 92| getReceiver: [Self, SelfVariableAccess] self +# 92| getStmt: [MethodCall] call to baz +# 92| getReceiver: [ConstantReadAccess] X +# 95| getStmt: [MethodCall] call to foo +# 95| getReceiver: [Self, SelfVariableAccess] self +# 95| getBlock: [DoBlock] do ... end +# 96| getStmt: [MethodCall] call to bar +# 96| getReceiver: [Self, SelfVariableAccess] self +# 97| getStmt: [MethodCall] call to baz +# 97| getReceiver: [ConstantReadAccess] X +# 101| getStmt: [MethodCall] call to bar +# 101| getReceiver: [MethodCall] call to foo +# 101| getReceiver: [Self, SelfVariableAccess] self +# 102| getStmt: [MethodCall] call to baz +# 102| getReceiver: [MethodCall] call to bar +# 102| getReceiver: [Self, SelfVariableAccess] self +# 106| getStmt: [CaseExpr] case ... +# 106| getValue: [MethodCall] call to foo +# 106| getReceiver: [Self, SelfVariableAccess] self +# 107| getBranch: [WhenExpr] when ... +# 107| getPattern: [MethodCall] call to bar +# 107| getReceiver: [Self, SelfVariableAccess] self +# 107| getBody: [StmtSequence] then ... +# 108| getStmt: [MethodCall] call to baz +# 108| getReceiver: [Self, SelfVariableAccess] self +# 110| getStmt: [CaseExpr] case ... +# 110| getValue: [MethodCall] call to foo +# 110| getReceiver: [ConstantReadAccess] X +# 111| getBranch: [WhenExpr] when ... +# 111| getPattern: [MethodCall] call to bar +# 111| getReceiver: [ConstantReadAccess] X +# 111| getBody: [StmtSequence] then ... +# 112| getStmt: [MethodCall] call to baz +# 112| getReceiver: [ConstantReadAccess] X +# 116| getStmt: [ClassDeclaration] MyClass +# 117| getStmt: [MethodCall] call to foo +# 117| getReceiver: [Self, SelfVariableAccess] self +# 118| getStmt: [MethodCall] call to bar +# 118| getReceiver: [ConstantReadAccess] X +# 122| getStmt: [ClassDeclaration] MyClass +# 122| getSuperclassExpr: [MethodCall] call to foo +# 122| getReceiver: [Self, SelfVariableAccess] self +# 124| getStmt: [ClassDeclaration] MyClass2 +# 124| getSuperclassExpr: [MethodCall] call to foo +# 124| getReceiver: [ConstantReadAccess] X +# 128| getStmt: [SingletonClass] class << ... +# 128| getValue: [MethodCall] call to foo +# 128| getReceiver: [Self, SelfVariableAccess] self +# 129| getStmt: [MethodCall] call to bar +# 129| getReceiver: [Self, SelfVariableAccess] self +# 131| getStmt: [SingletonClass] class << ... +# 131| getValue: [MethodCall] call to foo +# 131| getReceiver: [ConstantReadAccess] X +# 132| getStmt: [MethodCall] call to bar +# 132| getReceiver: [ConstantReadAccess] X +# 136| getStmt: [Method] some_method +# 137| getStmt: [MethodCall] call to foo +# 137| getReceiver: [Self, SelfVariableAccess] self +# 138| getStmt: [MethodCall] call to bar +# 138| getReceiver: [ConstantReadAccess] X +# 142| getStmt: [SingletonMethod] some_method +# 142| getObject: [MethodCall] call to foo +# 142| getReceiver: [Self, SelfVariableAccess] self +# 143| getStmt: [MethodCall] call to bar +# 143| getReceiver: [Self, SelfVariableAccess] self +# 144| getStmt: [MethodCall] call to baz +# 144| getReceiver: [ConstantReadAccess] X +# 148| getStmt: [Method] method_with_keyword_param +# 148| getParameter: [KeywordParameter] keyword +# 148| getDefiningAccess: [LocalVariableAccess] keyword +# 148| getDefaultValue: [MethodCall] call to foo +# 148| getReceiver: [Self, SelfVariableAccess] self +# 150| getStmt: [Method] method_with_keyword_param2 +# 150| getParameter: [KeywordParameter] keyword +# 150| getDefiningAccess: [LocalVariableAccess] keyword +# 150| getDefaultValue: [MethodCall] call to foo +# 150| getReceiver: [ConstantReadAccess] X +# 154| getStmt: [Method] method_with_optional_param +# 154| getParameter: [OptionalParameter] param +# 154| getDefiningAccess: [LocalVariableAccess] param +# 154| getDefaultValue: [MethodCall] call to foo +# 154| getReceiver: [Self, SelfVariableAccess] self +# 156| getStmt: [Method] method_with_optional_param2 +# 156| getParameter: [OptionalParameter] param +# 156| getDefiningAccess: [LocalVariableAccess] param +# 156| getDefaultValue: [MethodCall] call to foo +# 156| getReceiver: [ConstantReadAccess] X +# 160| getStmt: [ModuleDeclaration] SomeModule +# 161| getStmt: [MethodCall] call to foo +# 161| getReceiver: [Self, SelfVariableAccess] self +# 162| getStmt: [MethodCall] call to bar +# 162| getReceiver: [ConstantReadAccess] X +# 166| getStmt: [TernaryIfExpr] ... ? ... : ... +# 166| getCondition: [MethodCall] call to foo +# 166| getReceiver: [Self, SelfVariableAccess] self +# 166| getBranch/getThen: [MethodCall] call to bar +# 166| getReceiver: [Self, SelfVariableAccess] self +# 166| getBranch/getElse: [MethodCall] call to baz +# 166| getReceiver: [Self, SelfVariableAccess] self +# 167| getStmt: [TernaryIfExpr] ... ? ... : ... +# 167| getCondition: [MethodCall] call to foo +# 167| getReceiver: [ConstantReadAccess] X +# 167| getBranch/getThen: [MethodCall] call to bar +# 167| getReceiver: [ConstantReadAccess] X +# 167| getBranch/getElse: [MethodCall] call to baz +# 167| getReceiver: [ConstantReadAccess] X +# 170| getStmt: [IfExpr] if ... +# 170| getCondition: [MethodCall] call to foo +# 170| getReceiver: [Self, SelfVariableAccess] self +# 170| getBranch/getThen: [StmtSequence] then ... +# 171| getStmt: [MethodCall] call to wibble +# 171| getReceiver: [Self, SelfVariableAccess] self +# 172| getBranch/getElse: [IfExpr] elsif ... +# 172| getCondition: [MethodCall] call to bar +# 172| getReceiver: [Self, SelfVariableAccess] self +# 172| getBranch/getThen: [StmtSequence] then ... +# 173| getStmt: [MethodCall] call to wobble +# 173| getReceiver: [Self, SelfVariableAccess] self +# 174| getBranch/getElse: [StmtSequence] else ... +# 175| getStmt: [MethodCall] call to wabble +# 175| getReceiver: [Self, SelfVariableAccess] self +# 177| getStmt: [IfExpr] if ... +# 177| getCondition: [MethodCall] call to foo +# 177| getReceiver: [ConstantReadAccess] X +# 177| getBranch/getThen: [StmtSequence] then ... +# 178| getStmt: [MethodCall] call to wibble +# 178| getReceiver: [ConstantReadAccess] X +# 179| getBranch/getElse: [IfExpr] elsif ... +# 179| getCondition: [MethodCall] call to bar +# 179| getReceiver: [ConstantReadAccess] X +# 179| getBranch/getThen: [StmtSequence] then ... +# 180| getStmt: [MethodCall] call to wobble +# 180| getReceiver: [ConstantReadAccess] X +# 181| getBranch/getElse: [StmtSequence] else ... +# 182| getStmt: [MethodCall] call to wabble +# 182| getReceiver: [ConstantReadAccess] X +# 186| getStmt: [IfModifierExpr] ... if ... +# 186| getBody/getBranch: [MethodCall] call to bar +# 186| getReceiver: [Self, SelfVariableAccess] self +# 186| getCondition: [MethodCall] call to foo +# 186| getReceiver: [Self, SelfVariableAccess] self +# 187| getStmt: [IfModifierExpr] ... if ... +# 187| getBody/getBranch: [MethodCall] call to bar +# 187| getReceiver: [ConstantReadAccess] X +# 187| getCondition: [MethodCall] call to foo +# 187| getReceiver: [ConstantReadAccess] X +# 190| getStmt: [UnlessExpr] unless ... +# 190| getCondition: [MethodCall] call to foo +# 190| getReceiver: [Self, SelfVariableAccess] self +# 190| getBranch/getThen: [StmtSequence] then ... +# 191| getStmt: [MethodCall] call to bar +# 191| getReceiver: [Self, SelfVariableAccess] self +# 193| getStmt: [UnlessExpr] unless ... +# 193| getCondition: [MethodCall] call to foo +# 193| getReceiver: [ConstantReadAccess] X +# 193| getBranch/getThen: [StmtSequence] then ... +# 194| getStmt: [MethodCall] call to bar +# 194| getReceiver: [ConstantReadAccess] X +# 198| getStmt: [UnlessModifierExpr] ... unless ... +# 198| getBody/getBranch: [MethodCall] call to bar +# 198| getReceiver: [Self, SelfVariableAccess] self +# 198| getCondition: [MethodCall] call to foo +# 198| getReceiver: [Self, SelfVariableAccess] self +# 199| getStmt: [UnlessModifierExpr] ... unless ... +# 199| getBody/getBranch: [MethodCall] call to bar +# 199| getReceiver: [ConstantReadAccess] X +# 199| getCondition: [MethodCall] call to foo +# 199| getReceiver: [ConstantReadAccess] X +# 202| getStmt: [WhileExpr] while ... +# 202| getCondition: [MethodCall] call to foo +# 202| getReceiver: [Self, SelfVariableAccess] self +# 202| getBody: [StmtSequence] do ... +# 203| getStmt: [MethodCall] call to bar +# 203| getReceiver: [Self, SelfVariableAccess] self +# 205| getStmt: [WhileExpr] while ... +# 205| getCondition: [MethodCall] call to foo +# 205| getReceiver: [ConstantReadAccess] X +# 205| getBody: [StmtSequence] do ... +# 206| getStmt: [MethodCall] call to bar +# 206| getReceiver: [ConstantReadAccess] X +# 210| getStmt: [WhileModifierExpr] ... while ... +# 210| getBody: [MethodCall] call to bar +# 210| getReceiver: [Self, SelfVariableAccess] self +# 210| getCondition: [MethodCall] call to foo +# 210| getReceiver: [Self, SelfVariableAccess] self +# 211| getStmt: [WhileModifierExpr] ... while ... +# 211| getBody: [MethodCall] call to bar +# 211| getReceiver: [ConstantReadAccess] X +# 211| getCondition: [MethodCall] call to foo +# 211| getReceiver: [ConstantReadAccess] X +# 214| getStmt: [UntilExpr] until ... +# 214| getCondition: [MethodCall] call to foo +# 214| getReceiver: [Self, SelfVariableAccess] self +# 214| getBody: [StmtSequence] do ... +# 215| getStmt: [MethodCall] call to bar +# 215| getReceiver: [Self, SelfVariableAccess] self +# 217| getStmt: [UntilExpr] until ... +# 217| getCondition: [MethodCall] call to foo +# 217| getReceiver: [ConstantReadAccess] X +# 217| getBody: [StmtSequence] do ... +# 218| getStmt: [MethodCall] call to bar +# 218| getReceiver: [ConstantReadAccess] X +# 222| getStmt: [UntilModifierExpr] ... until ... +# 222| getBody: [MethodCall] call to bar +# 222| getReceiver: [Self, SelfVariableAccess] self +# 222| getCondition: [MethodCall] call to foo +# 222| getReceiver: [Self, SelfVariableAccess] self +# 223| getStmt: [UntilModifierExpr] ... until ... +# 223| getBody: [MethodCall] call to bar +# 223| getReceiver: [ConstantReadAccess] X +# 223| getCondition: [MethodCall] call to foo +# 223| getReceiver: [ConstantReadAccess] X +# 226| getStmt: [ForExpr] for ... in ... +# 226| getPattern: [LocalVariableAccess] x +# 226| <in>: [???] In +# 226| getValue: [MethodCall] call to bar +# 226| getReceiver: [Self, SelfVariableAccess] self +# 226| getBody: [StmtSequence] do ... +# 227| getStmt: [MethodCall] call to baz +# 227| getReceiver: [Self, SelfVariableAccess] self +# 229| getStmt: [ForExpr] for ... in ... +# 229| getPattern: [LocalVariableAccess] x +# 229| <in>: [???] In +# 229| getValue: [MethodCall] call to bar +# 229| getReceiver: [ConstantReadAccess] X +# 229| getBody: [StmtSequence] do ... +# 230| getStmt: [MethodCall] call to baz +# 230| getReceiver: [ConstantReadAccess] X +# 234| getStmt: [ElementReference] ...[...] +# 234| getReceiver: [MethodCall] call to foo +# 234| getReceiver: [Self, SelfVariableAccess] self +# 234| getArgument: [MethodCall] call to bar +# 234| getReceiver: [Self, SelfVariableAccess] self +# 235| getStmt: [ElementReference] ...[...] +# 235| getReceiver: [MethodCall] call to foo +# 235| getReceiver: [ConstantReadAccess] X +# 235| getArgument: [MethodCall] call to bar +# 235| getReceiver: [ConstantReadAccess] X +# 238| getStmt: [StringLiteral] "foo-#{...}-#{...}" +# 238| getComponent: [StringTextComponent] foo- +# 238| getComponent: [StringInterpolationComponent] #{...} +# 238| getStmt: [MethodCall] call to bar +# 238| getReceiver: [Self, SelfVariableAccess] self +# 238| getComponent: [StringTextComponent] - +# 238| getComponent: [StringInterpolationComponent] #{...} +# 238| getStmt: [MethodCall] call to baz +# 238| getReceiver: [ConstantReadAccess] X +# 241| getStmt: [ConstantReadAccess] Bar +# 241| getScopeExpr: [MethodCall] call to foo +# 241| getReceiver: [Self, SelfVariableAccess] self +# 242| getStmt: [ConstantReadAccess] Bar +# 242| getScopeExpr: [MethodCall] call to foo +# 242| getReceiver: [ConstantReadAccess] X +# 245| getStmt: [RangeLiteral] _ .. _ +# 245| getBegin: [MethodCall] call to foo +# 245| getReceiver: [Self, SelfVariableAccess] self +# 245| getEnd: [MethodCall] call to bar +# 245| getReceiver: [Self, SelfVariableAccess] self +# 246| getStmt: [RangeLiteral] _ .. _ +# 246| getBegin: [MethodCall] call to foo +# 246| getReceiver: [ConstantReadAccess] X +# 246| getEnd: [MethodCall] call to bar +# 246| getReceiver: [ConstantReadAccess] X +# 249| getStmt: [HashLiteral] {...} +# 249| getElement: [Pair] Pair +# 249| getKey: [MethodCall] call to foo +# 249| getReceiver: [Self, SelfVariableAccess] self +# 249| getValue: [MethodCall] call to bar +# 249| getReceiver: [Self, SelfVariableAccess] self +# 249| getElement: [Pair] Pair +# 249| getKey: [MethodCall] call to foo +# 249| getReceiver: [ConstantReadAccess] X +# 249| getValue: [MethodCall] call to bar +# 249| getReceiver: [ConstantReadAccess] X +# 252| getStmt: [BeginExpr] begin ... +# 253| getRescue: [RescueClause] rescue ... +# 253| getException: [MethodCall] call to foo +# 253| getReceiver: [Self, SelfVariableAccess] self +# 254| getEnsure: [StmtSequence] ensure ... +# 254| getStmt: [MethodCall] call to bar +# 254| getReceiver: [Self, SelfVariableAccess] self +# 256| getStmt: [BeginExpr] begin ... +# 257| getRescue: [RescueClause] rescue ... +# 257| getException: [MethodCall] call to foo +# 257| getReceiver: [ConstantReadAccess] X +# 258| getEnsure: [StmtSequence] ensure ... +# 258| getStmt: [MethodCall] call to bar +# 258| getReceiver: [ConstantReadAccess] X +# 262| getStmt: [RescueModifierExpr] ... rescue ... +# 262| getBody: [MethodCall] call to foo +# 262| getReceiver: [Self, SelfVariableAccess] self +# 262| getHandler: [MethodCall] call to bar +# 262| getReceiver: [Self, SelfVariableAccess] self +# 263| getStmt: [RescueModifierExpr] ... rescue ... +# 263| getBody: [MethodCall] call to foo +# 263| getReceiver: [ConstantReadAccess] X +# 263| getHandler: [MethodCall] call to bar +# 263| getReceiver: [ConstantReadAccess] X +# 266| getStmt: [MethodCall] call to foo +# 266| getReceiver: [Self, SelfVariableAccess] self +# 266| getArgument: [BlockArgument] &... +# 266| getValue: [MethodCall] call to bar +# 266| getReceiver: [Self, SelfVariableAccess] self +# 267| getStmt: [MethodCall] call to foo +# 267| getReceiver: [Self, SelfVariableAccess] self +# 267| getArgument: [BlockArgument] &... +# 267| getValue: [MethodCall] call to bar +# 267| getReceiver: [ConstantReadAccess] X +# 270| getStmt: [MethodCall] call to foo +# 270| getReceiver: [Self, SelfVariableAccess] self +# 270| getArgument: [SplatExpr] * ... +# 270| getAnOperand/getOperand/getReceiver: [MethodCall] call to bar +# 270| getReceiver: [Self, SelfVariableAccess] self +# 271| getStmt: [MethodCall] call to foo +# 271| getReceiver: [Self, SelfVariableAccess] self +# 271| getArgument: [SplatExpr] * ... +# 271| getAnOperand/getOperand/getReceiver: [MethodCall] call to bar +# 271| getReceiver: [ConstantReadAccess] X +# 274| getStmt: [MethodCall] call to foo +# 274| getReceiver: [Self, SelfVariableAccess] self +# 274| getArgument: [HashSplatExpr] ** ... +# 274| getAnOperand/getOperand/getReceiver: [MethodCall] call to bar +# 274| getReceiver: [Self, SelfVariableAccess] self +# 275| getStmt: [MethodCall] call to foo +# 275| getReceiver: [Self, SelfVariableAccess] self +# 275| getArgument: [HashSplatExpr] ** ... +# 275| getAnOperand/getOperand/getReceiver: [MethodCall] call to bar +# 275| getReceiver: [ConstantReadAccess] X +# 278| getStmt: [MethodCall] call to foo +# 278| getReceiver: [Self, SelfVariableAccess] self +# 278| getArgument: [Pair] Pair +# 278| getKey: [SymbolLiteral] :blah +# 278| getValue: [MethodCall] call to bar +# 278| getReceiver: [Self, SelfVariableAccess] self +# 279| getStmt: [MethodCall] call to foo +# 279| getReceiver: [Self, SelfVariableAccess] self +# 279| getArgument: [Pair] Pair +# 279| getKey: [SymbolLiteral] :blah +# 279| getValue: [MethodCall] call to bar +# 279| getReceiver: [ConstantReadAccess] X +# 284| getStmt: [ClassDeclaration] MyClass +# 285| getStmt: [Method] my_method +# 286| getStmt: [SuperCall] call to super +# 287| getStmt: [SuperCall] call to super +# 288| getStmt: [SuperCall] call to super +# 288| getArgument: [StringLiteral] "blah" +# 288| getComponent: [StringTextComponent] blah +# 289| getStmt: [SuperCall] call to super +# 289| getArgument: [IntegerLiteral] 1 +# 289| getArgument: [IntegerLiteral] 2 +# 289| getArgument: [IntegerLiteral] 3 +# 290| getStmt: [SuperCall] call to super +# 290| getBlock: [BraceBlock] { ... } +# 290| getParameter: [SimpleParameter] x +# 290| getDefiningAccess: [LocalVariableAccess] x +# 290| getStmt: [AddExpr] ... + ... +# 290| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 290| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 291| getStmt: [SuperCall] call to super +# 291| getBlock: [DoBlock] do ... end +# 291| getParameter: [SimpleParameter] x +# 291| getDefiningAccess: [LocalVariableAccess] x +# 291| getStmt: [MulExpr] ... * ... +# 291| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 291| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 2 +# 292| getStmt: [SuperCall] call to super +# 292| getArgument: [IntegerLiteral] 4 +# 292| getArgument: [IntegerLiteral] 5 +# 292| getBlock: [BraceBlock] { ... } +# 292| getParameter: [SimpleParameter] x +# 292| getDefiningAccess: [LocalVariableAccess] x +# 292| getStmt: [AddExpr] ... + ... +# 292| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 292| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 100 +# 293| getStmt: [SuperCall] call to super +# 293| getArgument: [IntegerLiteral] 6 +# 293| getArgument: [IntegerLiteral] 7 +# 293| getBlock: [DoBlock] do ... end +# 293| getParameter: [SimpleParameter] x +# 293| getDefiningAccess: [LocalVariableAccess] x +# 293| getStmt: [AddExpr] ... + ... +# 293| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 293| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 200 +# 301| getStmt: [ClassDeclaration] AnotherClass +# 302| getStmt: [Method] another_method +# 303| getStmt: [MethodCall] call to super +# 303| getReceiver: [MethodCall] call to foo +# 303| getReceiver: [Self, SelfVariableAccess] self +# 304| getStmt: [MethodCall] call to super +# 304| getReceiver: [Self, SelfVariableAccess] self +# 305| getStmt: [MethodCall] call to super +# 305| getReceiver: [SuperCall] call to super +# 310| getStmt: [MethodCall] call to call +# 310| getReceiver: [MethodCall] call to foo +# 310| getReceiver: [Self, SelfVariableAccess] self +# 311| getStmt: [MethodCall] call to call +# 311| getReceiver: [MethodCall] call to foo +# 311| getReceiver: [Self, SelfVariableAccess] self +# 311| getArgument: [IntegerLiteral] 1 +# 314| getStmt: [AssignExpr] ... = ... +# 314| getAnOperand/getLeftOperand: [MethodCall] call to foo +# 314| getReceiver: [Self, SelfVariableAccess] self +# 314| getAnOperand/getRightOperand: [IntegerLiteral] 10 +# 315| getStmt: [AssignExpr] ... = ... +# 315| getAnOperand/getLeftOperand: [ElementReference] ...[...] +# 315| getReceiver: [MethodCall] call to foo +# 315| getReceiver: [Self, SelfVariableAccess] self +# 315| getArgument: [IntegerLiteral] 0 +# 315| getAnOperand/getRightOperand: [IntegerLiteral] 10 +# 316| getStmt: [AssignExpr] ... = ... +# 316| getLeftOperand: [TuplePattern] (..., ...) +# 316| getElement: [MethodCall] call to foo +# 316| getReceiver: [Self, SelfVariableAccess] self +# 316| getElement: [MethodCall] call to bar +# 316| getReceiver: [Self, SelfVariableAccess] self +# 316| getElement: [ElementReference] ...[...] +# 316| getReceiver: [MethodCall] call to foo +# 316| getReceiver: [Self, SelfVariableAccess] self +# 316| getArgument: [IntegerLiteral] 4 +# 316| getAnOperand/getRightOperand: [ArrayLiteral] [...] +# 316| getElement: [IntegerLiteral] 1 +# 316| getElement: [IntegerLiteral] 2 +# 316| getElement: [IntegerLiteral] 3 +# 316| getElement: [IntegerLiteral] 4 +# 317| getStmt: [AssignExpr] ... = ... +# 317| getLeftOperand: [TuplePattern] (..., ...) +# 317| getElement: [LocalVariableAccess] a +# 317| getElement: [ElementReference] ...[...] +# 317| getReceiver: [MethodCall] call to foo +# 317| getReceiver: [Self, SelfVariableAccess] self +# 317| getArgument: [IntegerLiteral] 5 +# 317| getAnOperand/getRightOperand: [ArrayLiteral] [...] +# 317| getElement: [IntegerLiteral] 1 +# 317| getElement: [IntegerLiteral] 2 +# 317| getElement: [IntegerLiteral] 3 +# 318| getStmt: [AssignAddExpr] ... += ... +# 318| getAnOperand/getLeftOperand: [MethodCall] call to count +# 318| getReceiver: [Self, SelfVariableAccess] self +# 318| getAnOperand/getRightOperand: [IntegerLiteral] 1 +# 319| getStmt: [AssignAddExpr] ... += ... +# 319| getAnOperand/getLeftOperand: [ElementReference] ...[...] +# 319| getReceiver: [MethodCall] call to foo +# 319| getReceiver: [Self, SelfVariableAccess] self +# 319| getArgument: [IntegerLiteral] 0 +# 319| getAnOperand/getRightOperand: [IntegerLiteral] 1 +# 320| getStmt: [AssignMulExpr] ... *= ... +# 320| getAnOperand/getLeftOperand: [ElementReference] ...[...] +# 320| getReceiver: [MethodCall] call to bar +# 320| getReceiver: [MethodCall] call to foo +# 320| getReceiver: [Self, SelfVariableAccess] self +# 320| getArgument: [IntegerLiteral] 0 +# 320| getArgument: [MethodCall] call to baz +# 320| getReceiver: [MethodCall] call to foo +# 320| getReceiver: [Self, SelfVariableAccess] self +# 320| getArgument: [AddExpr] ... + ... +# 320| getAnOperand/getLeftOperand/getReceiver: [MethodCall] call to boo +# 320| getReceiver: [MethodCall] call to foo +# 320| getReceiver: [Self, SelfVariableAccess] self +# 320| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 320| getAnOperand/getRightOperand: [IntegerLiteral] 2 +# 323| getStmt: [Method] foo +# 323| getStmt: [MethodCall] call to bar +# 323| getReceiver: [Self, SelfVariableAccess] self +# 324| getStmt: [Method] foo +# 324| getStmt: [MethodCall] call to bar +# 324| getReceiver: [Self, SelfVariableAccess] self +# 325| getStmt: [Method] foo +# 325| getParameter: [SimpleParameter] x +# 325| getDefiningAccess: [LocalVariableAccess] x +# 325| getStmt: [MethodCall] call to bar +# 325| getReceiver: [Self, SelfVariableAccess] self +# 326| getStmt: [SingletonMethod] foo +# 326| getObject: [ConstantReadAccess] Object +# 326| getStmt: [MethodCall] call to bar +# 326| getReceiver: [Self, SelfVariableAccess] self +# 327| getStmt: [SingletonMethod] foo +# 327| getObject: [ConstantReadAccess] Object +# 327| getParameter: [SimpleParameter] x +# 327| getDefiningAccess: [LocalVariableAccess] x +# 327| getStmt: [MethodCall] call to bar +# 327| getReceiver: [Self, SelfVariableAccess] self +# 328| getStmt: [Method] foo +# 328| getStmt: [RescueModifierExpr] ... rescue ... +# 328| getBody: [MethodCall] call to bar +# 328| getReceiver: [Self, SelfVariableAccess] self +# 328| getHandler: [ParenthesizedExpr] ( ... ) +# 328| getStmt: [MethodCall] call to print +# 328| getReceiver: [Self, SelfVariableAccess] self +# 328| getArgument: [StringLiteral] "error" +# 328| getComponent: [StringTextComponent] error +# 331| getStmt: [Method] foo +# 331| getParameter: [ForwardParameter] ... +# 332| getStmt: [SuperCall] call to super +# 332| getArgument: [ForwardedArguments] ... +# 335| getStmt: [Method] foo +# 335| getParameter: [SimpleParameter] a +# 335| getDefiningAccess: [LocalVariableAccess] a +# 335| getParameter: [SimpleParameter] b +# 335| getDefiningAccess: [LocalVariableAccess] b +# 335| getParameter: [ForwardParameter] ... +# 336| getStmt: [MethodCall] call to bar +# 336| getReceiver: [Self, SelfVariableAccess] self +# 336| getArgument: [LocalVariableAccess] b +# 336| getArgument: [ForwardedArguments] ... +control/cases.rb: +# 1| [Toplevel] cases.rb +# 2| getStmt: [AssignExpr] ... = ... +# 2| getAnOperand/getLeftOperand: [LocalVariableAccess] a +# 2| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 3| getStmt: [AssignExpr] ... = ... +# 3| getAnOperand/getLeftOperand: [LocalVariableAccess] b +# 3| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 4| getStmt: [AssignExpr] ... = ... +# 4| getAnOperand/getLeftOperand: [LocalVariableAccess] c +# 4| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 5| getStmt: [AssignExpr] ... = ... +# 5| getAnOperand/getLeftOperand: [LocalVariableAccess] d +# 5| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 8| getStmt: [CaseExpr] case ... +# 8| getValue: [LocalVariableAccess] a +# 9| getBranch: [WhenExpr] when ... +# 9| getPattern: [LocalVariableAccess] b +# 9| getBody: [StmtSequence] then ... +# 10| getStmt: [IntegerLiteral] 100 +# 11| getBranch: [WhenExpr] when ... +# 11| getPattern: [LocalVariableAccess] c +# 11| getPattern: [LocalVariableAccess] d +# 11| getBody: [StmtSequence] then ... +# 12| getStmt: [IntegerLiteral] 200 +# 13| getBranch: [StmtSequence] else ... +# 14| getStmt: [IntegerLiteral] 300 +# 18| getStmt: [CaseExpr] case ... +# 19| getBranch: [WhenExpr] when ... +# 19| getPattern: [GTExpr] ... > ... +# 19| getAnOperand/getGreaterOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a +# 19| getAnOperand/getArgument/getLesserOperand/getRightOperand: [LocalVariableAccess] b +# 19| getBody: [StmtSequence] then ... +# 19| getStmt: [IntegerLiteral] 10 +# 20| getBranch: [WhenExpr] when ... +# 20| getPattern: [EqExpr] ... == ... +# 20| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a +# 20| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] b +# 20| getBody: [StmtSequence] then ... +# 20| getStmt: [IntegerLiteral] 20 +# 21| getBranch: [WhenExpr] when ... +# 21| getPattern: [LTExpr] ... < ... +# 21| getAnOperand/getLeftOperand/getLesserOperand/getReceiver: [LocalVariableAccess] a +# 21| getAnOperand/getArgument/getGreaterOperand/getRightOperand: [LocalVariableAccess] b +# 21| getBody: [StmtSequence] then ... +# 21| getStmt: [IntegerLiteral] 30 +modules/classes.rb: +# 2| [Toplevel] classes.rb +# 3| getStmt: [ClassDeclaration] Foo +# 7| getStmt: [ClassDeclaration] Bar +# 7| getSuperclassExpr: [ConstantReadAccess] BaseClass +# 11| getStmt: [ClassDeclaration] Baz +# 11| getSuperclassExpr: [MethodCall] call to superclass_for +# 11| getReceiver: [Self, SelfVariableAccess] self +# 11| getArgument: [SymbolLiteral] :baz +# 15| getStmt: [ModuleDeclaration] MyModule +# 16| getStmt: [ClassDeclaration] MyClass +# 16| getScopeExpr: [ConstantReadAccess] MyModule +# 20| getStmt: [ClassDeclaration] Wibble +# 21| getStmt: [Method] method_a +# 22| getStmt: [MethodCall] call to puts +# 22| getReceiver: [Self, SelfVariableAccess] self +# 22| getArgument: [StringLiteral] "a" +# 22| getComponent: [StringTextComponent] a +# 25| getStmt: [Method] method_b +# 26| getStmt: [MethodCall] call to puts +# 26| getReceiver: [Self, SelfVariableAccess] self +# 26| getArgument: [StringLiteral] "b" +# 26| getComponent: [StringTextComponent] b +# 29| getStmt: [MethodCall] call to some_method_call +# 29| getReceiver: [Self, SelfVariableAccess] self +# 30| getStmt: [AssignExpr] ... = ... +# 30| getAnOperand/getLeftOperand: [GlobalVariableAccess] $global_var +# 30| getAnOperand/getRightOperand: [IntegerLiteral] 123 +# 32| getStmt: [ClassDeclaration] ClassInWibble +# 35| getStmt: [ModuleDeclaration] ModuleInWibble +# 40| getStmt: [AssignExpr] ... = ... +# 40| getAnOperand/getLeftOperand: [LocalVariableAccess] x +# 40| getAnOperand/getRightOperand: [StringLiteral] "hello" +# 40| getComponent: [StringTextComponent] hello +# 41| getStmt: [SingletonClass] class << ... +# 41| getValue: [LocalVariableAccess] x +# 42| getStmt: [Method] length +# 43| getStmt: [MulExpr] ... * ... +# 43| getAnOperand/getLeftOperand/getReceiver: [IntegerLiteral] 100 +# 43| getAnOperand/getArgument/getRightOperand: [SuperCall] call to super +# 46| getStmt: [Method] wibble +# 47| getStmt: [MethodCall] call to puts +# 47| getReceiver: [Self, SelfVariableAccess] self +# 47| getArgument: [StringLiteral] "wibble" +# 47| getComponent: [StringTextComponent] wibble +# 50| getStmt: [MethodCall] call to another_method_call +# 50| getReceiver: [Self, SelfVariableAccess] self +# 51| getStmt: [AssignExpr] ... = ... +# 51| getAnOperand/getLeftOperand: [GlobalVariableAccess] $global_var2 +# 51| getAnOperand/getRightOperand: [IntegerLiteral] 456 +# 55| getStmt: [ClassDeclaration] MyClassInGlobalScope +control/conditionals.rb: +# 1| [Toplevel] conditionals.rb +# 2| getStmt: [AssignExpr] ... = ... +# 2| getAnOperand/getLeftOperand: [LocalVariableAccess] a +# 2| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 3| getStmt: [AssignExpr] ... = ... +# 3| getAnOperand/getLeftOperand: [LocalVariableAccess] b +# 3| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 4| getStmt: [AssignExpr] ... = ... +# 4| getAnOperand/getLeftOperand: [LocalVariableAccess] c +# 4| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 5| getStmt: [AssignExpr] ... = ... +# 5| getAnOperand/getLeftOperand: [LocalVariableAccess] d +# 5| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 6| getStmt: [AssignExpr] ... = ... +# 6| getAnOperand/getLeftOperand: [LocalVariableAccess] e +# 6| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 7| getStmt: [AssignExpr] ... = ... +# 7| getAnOperand/getLeftOperand: [LocalVariableAccess] f +# 7| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 10| getStmt: [IfExpr] if ... +# 10| getCondition: [GTExpr] ... > ... +# 10| getAnOperand/getGreaterOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a +# 10| getAnOperand/getArgument/getLesserOperand/getRightOperand: [LocalVariableAccess] b +# 10| getBranch/getThen: [StmtSequence] then ... +# 11| getStmt: [LocalVariableAccess] c +# 15| getStmt: [IfExpr] if ... +# 15| getCondition: [EqExpr] ... == ... +# 15| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a +# 15| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] b +# 15| getBranch/getThen: [StmtSequence] then ... +# 16| getStmt: [LocalVariableAccess] c +# 17| getBranch/getElse: [StmtSequence] else ... +# 18| getStmt: [LocalVariableAccess] d +# 22| getStmt: [IfExpr] if ... +# 22| getCondition: [EqExpr] ... == ... +# 22| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a +# 22| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 0 +# 22| getBranch/getThen: [StmtSequence] then ... +# 23| getStmt: [LocalVariableAccess] c +# 24| getBranch/getElse: [IfExpr] elsif ... +# 24| getCondition: [EqExpr] ... == ... +# 24| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a +# 24| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 24| getBranch/getThen: [StmtSequence] then ... +# 25| getStmt: [LocalVariableAccess] d +# 26| getBranch/getElse: [IfExpr] elsif ... +# 26| getCondition: [EqExpr] ... == ... +# 26| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a +# 26| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 2 +# 26| getBranch/getThen: [StmtSequence] then ... +# 27| getStmt: [LocalVariableAccess] e +# 28| getBranch/getElse: [StmtSequence] else ... +# 29| getStmt: [LocalVariableAccess] f +# 33| getStmt: [IfExpr] if ... +# 33| getCondition: [EqExpr] ... == ... +# 33| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a +# 33| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 0 +# 33| getBranch/getThen: [StmtSequence] then ... +# 34| getStmt: [LocalVariableAccess] b +# 35| getBranch/getElse: [IfExpr] elsif ... +# 35| getCondition: [EqExpr] ... == ... +# 35| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a +# 35| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 35| getBranch/getThen: [StmtSequence] then ... +# 36| getStmt: [LocalVariableAccess] c +# 40| getStmt: [UnlessExpr] unless ... +# 40| getCondition: [GTExpr] ... > ... +# 40| getAnOperand/getGreaterOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a +# 40| getAnOperand/getArgument/getLesserOperand/getRightOperand: [LocalVariableAccess] b +# 40| getBranch/getThen: [StmtSequence] then ... +# 41| getStmt: [LocalVariableAccess] c +# 45| getStmt: [UnlessExpr] unless ... +# 45| getCondition: [EqExpr] ... == ... +# 45| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a +# 45| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] b +# 45| getBranch/getThen: [StmtSequence] then ... +# 46| getStmt: [LocalVariableAccess] c +# 47| getBranch/getElse: [StmtSequence] else ... +# 48| getStmt: [LocalVariableAccess] d +# 52| getStmt: [IfModifierExpr] ... if ... +# 52| getBody/getBranch: [AssignExpr] ... = ... +# 52| getAnOperand/getLeftOperand: [LocalVariableAccess] a +# 52| getAnOperand/getRightOperand: [LocalVariableAccess] b +# 52| getCondition: [GTExpr] ... > ... +# 52| getAnOperand/getGreaterOperand/getLeftOperand/getReceiver: [LocalVariableAccess] c +# 52| getAnOperand/getArgument/getLesserOperand/getRightOperand: [LocalVariableAccess] d +# 55| getStmt: [UnlessModifierExpr] ... unless ... +# 55| getBody/getBranch: [AssignExpr] ... = ... +# 55| getAnOperand/getLeftOperand: [LocalVariableAccess] a +# 55| getAnOperand/getRightOperand: [LocalVariableAccess] b +# 55| getCondition: [LTExpr] ... < ... +# 55| getAnOperand/getLeftOperand/getLesserOperand/getReceiver: [LocalVariableAccess] c +# 55| getAnOperand/getArgument/getGreaterOperand/getRightOperand: [LocalVariableAccess] d +# 58| getStmt: [AssignExpr] ... = ... +# 58| getAnOperand/getLeftOperand: [LocalVariableAccess] a +# 58| getAnOperand/getRightOperand: [TernaryIfExpr] ... ? ... : ... +# 58| getCondition: [GTExpr] ... > ... +# 58| getAnOperand/getGreaterOperand/getLeftOperand/getReceiver: [LocalVariableAccess] b +# 58| getAnOperand/getArgument/getLesserOperand/getRightOperand: [LocalVariableAccess] c +# 58| getBranch/getThen: [AddExpr] ... + ... +# 58| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] d +# 58| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 58| getBranch/getElse: [SubExpr] ... - ... +# 58| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] e +# 58| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 2 +# 61| getStmt: [IfExpr] if ... +# 61| getCondition: [GTExpr] ... > ... +# 61| getAnOperand/getGreaterOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a +# 61| getAnOperand/getArgument/getLesserOperand/getRightOperand: [LocalVariableAccess] b +# 61| getBranch/getThen: [StmtSequence] then ... +# 62| getStmt: [LocalVariableAccess] c +# 63| getBranch/getElse: [StmtSequence] else ... +# 67| getStmt: [IfExpr] if ... +# 67| getCondition: [GTExpr] ... > ... +# 67| getAnOperand/getGreaterOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a +# 67| getAnOperand/getArgument/getLesserOperand/getRightOperand: [LocalVariableAccess] b +# 67| getBranch/getThen: [StmtSequence] then ... +# 68| getBranch/getElse: [StmtSequence] else ... +# 69| getStmt: [LocalVariableAccess] c +constants/constants.rb: +# 1| [Toplevel] constants.rb +# 1| getStmt: [ModuleDeclaration] ModuleA +# 2| getStmt: [ClassDeclaration] ClassA +# 3| getStmt: [AssignExpr] ... = ... +# 3| getAnOperand/getLeftOperand: [ConstantAssignment] CONST_A +# 3| getAnOperand/getRightOperand: [StringLiteral] "const_a" +# 3| getComponent: [StringTextComponent] const_a +# 6| getStmt: [AssignExpr] ... = ... +# 6| getAnOperand/getLeftOperand: [ConstantAssignment] CONST_B +# 6| getAnOperand/getRightOperand: [StringLiteral] "const_b" +# 6| getComponent: [StringTextComponent] const_b +# 8| getStmt: [ModuleDeclaration] ModuleB +# 9| getStmt: [ClassDeclaration] ClassB +# 9| getSuperclassExpr: [ConstantReadAccess] Base +# 12| getStmt: [ClassDeclaration] ClassC +# 12| getSuperclassExpr: [ConstantReadAccess] Z +# 12| getScopeExpr: [ConstantReadAccess] Y +# 12| getScopeExpr: [ConstantReadAccess] X +# 17| getStmt: [AssignExpr] ... = ... +# 17| getAnOperand/getLeftOperand: [ConstantAssignment] GREETING +# 17| getAnOperand/getRightOperand: [AddExpr] ... + ... +# 17| getAnOperand/getLeftOperand/getReceiver: [AddExpr] ... + ... +# 17| getAnOperand/getLeftOperand/getReceiver: [StringLiteral] "Hello" +# 17| getComponent: [StringTextComponent] Hello +# 17| getAnOperand/getArgument/getRightOperand: [ConstantReadAccess] CONST_A +# 17| getScopeExpr: [ConstantReadAccess] ClassA +# 17| getScopeExpr: [ConstantReadAccess] ModuleA +# 17| getAnOperand/getArgument/getRightOperand: [ConstantReadAccess] CONST_B +# 17| getScopeExpr: [ConstantReadAccess] ModuleA +# 19| getStmt: [Method] foo +# 20| getStmt: [AssignExpr] ... = ... +# 20| getAnOperand/getLeftOperand: [ConstantAssignment] Names +# 20| getAnOperand/getRightOperand: [ArrayLiteral] [...] +# 20| getElement: [StringLiteral] "Vera" +# 20| getComponent: [StringTextComponent] Vera +# 20| getElement: [StringLiteral] "Chuck" +# 20| getComponent: [StringTextComponent] Chuck +# 20| getElement: [StringLiteral] "Dave" +# 20| getComponent: [StringTextComponent] Dave +# 22| getStmt: [MethodCall] call to each +# 22| getReceiver: [ConstantReadAccess] Names +# 22| getBlock: [DoBlock] do ... end +# 22| getParameter: [SimpleParameter] name +# 22| getDefiningAccess: [LocalVariableAccess] name +# 23| getStmt: [MethodCall] call to puts +# 23| getReceiver: [Self, SelfVariableAccess] self +# 23| getArgument: [StringLiteral] "#{...} #{...}" +# 23| getComponent: [StringInterpolationComponent] #{...} +# 23| getStmt: [ConstantReadAccess] GREETING +# 23| getComponent: [StringTextComponent] +# 23| getComponent: [StringInterpolationComponent] #{...} +# 23| getStmt: [LocalVariableAccess] name +# 28| getStmt: [MethodCall] call to Array +# 28| getReceiver: [Self, SelfVariableAccess] self +# 28| getArgument: [StringLiteral] "foo" +# 28| getComponent: [StringTextComponent] foo +# 31| getStmt: [ClassDeclaration] ClassD +# 31| getScopeExpr: [ConstantReadAccess] ModuleA +# 31| getSuperclassExpr: [ConstantReadAccess] ClassA +# 31| getScopeExpr: [ConstantReadAccess] ModuleA +# 34| getStmt: [ModuleDeclaration] ModuleC +# 34| getScopeExpr: [ConstantReadAccess] ModuleA +# 37| getStmt: [AssignExpr] ... = ... +# 37| getAnOperand/getLeftOperand: [ConstantAssignment] MAX_SIZE +# 37| getScopeExpr: [ConstantReadAccess] ModuleB +# 37| getScopeExpr: [ConstantReadAccess] ModuleA +# 37| getAnOperand/getRightOperand: [IntegerLiteral] 1024 +# 39| getStmt: [MethodCall] call to puts +# 39| getReceiver: [Self, SelfVariableAccess] self +# 39| getArgument: [ConstantReadAccess] MAX_SIZE +# 39| getScopeExpr: [ConstantReadAccess] ModuleB +# 39| getScopeExpr: [ConstantReadAccess] ModuleA +# 41| getStmt: [MethodCall] call to puts +# 41| getReceiver: [Self, SelfVariableAccess] self +# 41| getArgument: [ConstantReadAccess] GREETING +# 42| getStmt: [MethodCall] call to puts +# 42| getReceiver: [Self, SelfVariableAccess] self +# 42| getArgument: [ConstantReadAccess] GREETING +literals/literals.rb: +# 1| [Toplevel] literals.rb +# 2| getStmt: [NilLiteral] nil +# 3| getStmt: [NilLiteral] NIL +# 4| getStmt: [BooleanLiteral] false +# 5| getStmt: [BooleanLiteral] FALSE +# 6| getStmt: [BooleanLiteral] true +# 7| getStmt: [BooleanLiteral] TRUE +# 10| getStmt: [IntegerLiteral] 1234 +# 11| getStmt: [IntegerLiteral] 5_678 +# 12| getStmt: [IntegerLiteral] 0 +# 13| getStmt: [IntegerLiteral] 0d900 +# 16| getStmt: [IntegerLiteral] 0x1234 +# 17| getStmt: [IntegerLiteral] 0xdeadbeef +# 18| getStmt: [IntegerLiteral] 0xF00D_face +# 21| getStmt: [IntegerLiteral] 0123 +# 22| getStmt: [IntegerLiteral] 0o234 +# 23| getStmt: [IntegerLiteral] 0O45_6 +# 26| getStmt: [IntegerLiteral] 0b10010100 +# 27| getStmt: [IntegerLiteral] 0B011_01101 +# 30| getStmt: [FloatLiteral] 12.34 +# 31| getStmt: [FloatLiteral] 1234e-2 +# 32| getStmt: [FloatLiteral] 1.234E1 +# 35| getStmt: [RationalLiteral] 23r +# 36| getStmt: [RationalLiteral] 9.85r +# 39| getStmt: [ComplexLiteral] 2i +# 46| getStmt: [StringLiteral] "" +# 47| getStmt: [StringLiteral] "" +# 48| getStmt: [StringLiteral] "hello" +# 48| getComponent: [StringTextComponent] hello +# 49| getStmt: [StringLiteral] "goodbye" +# 49| getComponent: [StringTextComponent] goodbye +# 50| getStmt: [StringLiteral] "string with escaped \" quote" +# 50| getComponent: [StringTextComponent] string with escaped +# 50| getComponent: [StringEscapeSequenceComponent] \" +# 50| getComponent: [StringTextComponent] quote +# 51| getStmt: [StringLiteral] "string with " quote" +# 51| getComponent: [StringTextComponent] string with " quote +# 52| getStmt: [StringLiteral] "foo bar baz" +# 52| getComponent: [StringTextComponent] foo bar baz +# 53| getStmt: [StringLiteral] "foo bar baz" +# 53| getComponent: [StringTextComponent] foo bar baz +# 54| getStmt: [StringLiteral] "foo ' bar " baz'" +# 54| getComponent: [StringTextComponent] foo ' bar " baz' +# 55| getStmt: [StringLiteral] "FOO ' BAR " BAZ'" +# 55| getComponent: [StringTextComponent] FOO ' BAR " BAZ' +# 56| getStmt: [StringLiteral] "foo\ bar" +# 56| getComponent: [StringTextComponent] foo\ bar +# 57| getStmt: [StringLiteral] "foo\ bar" +# 57| getComponent: [StringTextComponent] foo +# 57| getComponent: [StringEscapeSequenceComponent] \ +# 57| getComponent: [StringTextComponent] bar +# 58| getStmt: [StringLiteral] "2 + 2 = #{...}" +# 58| getComponent: [StringTextComponent] 2 + 2 = +# 58| getComponent: [StringInterpolationComponent] #{...} +# 58| getStmt: [AddExpr] ... + ... +# 58| getAnOperand/getLeftOperand/getReceiver: [IntegerLiteral] 2 +# 58| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 2 +# 59| getStmt: [StringLiteral] "3 + 4 = #{...}" +# 59| getComponent: [StringTextComponent] 3 + 4 = +# 59| getComponent: [StringInterpolationComponent] #{...} +# 59| getStmt: [AddExpr] ... + ... +# 59| getAnOperand/getLeftOperand/getReceiver: [IntegerLiteral] 3 +# 59| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 4 +# 60| getStmt: [StringLiteral] "2 + 2 = #{ 2 + 2 }" +# 60| getComponent: [StringTextComponent] 2 + 2 = #{ 2 + 2 } +# 61| getStmt: [StringLiteral] "3 + 4 = #{ 3 + 4 }" +# 61| getComponent: [StringTextComponent] 3 + 4 = #{ 3 + 4 } +# 62| getStmt: [StringConcatenation] "..." "..." +# 62| getString: [StringLiteral] "foo" +# 62| getComponent: [StringTextComponent] foo +# 62| getString: [StringLiteral] "bar" +# 62| getComponent: [StringTextComponent] bar +# 62| getString: [StringLiteral] "baz" +# 62| getComponent: [StringTextComponent] baz +# 63| getStmt: [StringConcatenation] "..." "..." +# 63| getString: [StringLiteral] "foo" +# 63| getComponent: [StringTextComponent] foo +# 63| getString: [StringLiteral] "bar" +# 63| getComponent: [StringTextComponent] bar +# 63| getString: [StringLiteral] "baz" +# 63| getComponent: [StringTextComponent] baz +# 64| getStmt: [StringConcatenation] "..." "..." +# 64| getString: [StringLiteral] "foo" +# 64| getComponent: [StringTextComponent] foo +# 64| getString: [StringLiteral] "bar#{...}" +# 64| getComponent: [StringTextComponent] bar +# 64| getComponent: [StringInterpolationComponent] #{...} +# 64| getStmt: [MulExpr] ... * ... +# 64| getAnOperand/getLeftOperand/getReceiver: [IntegerLiteral] 1 +# 64| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 64| getString: [StringLiteral] "baz" +# 64| getComponent: [StringTextComponent] baz +# 65| getStmt: [StringLiteral] "foo #{...} qux" +# 65| getComponent: [StringTextComponent] foo +# 65| getComponent: [StringInterpolationComponent] #{...} +# 65| getStmt: [StringLiteral] "bar #{...} baz" +# 65| getComponent: [StringTextComponent] bar +# 65| getComponent: [StringInterpolationComponent] #{...} +# 65| getStmt: [AddExpr] ... + ... +# 65| getAnOperand/getLeftOperand/getReceiver: [IntegerLiteral] 2 +# 65| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 3 +# 65| getComponent: [StringTextComponent] baz +# 65| getComponent: [StringTextComponent] qux +# 66| getStmt: [StringLiteral] "foo #{...}" +# 66| getComponent: [StringTextComponent] foo +# 66| getComponent: [StringInterpolationComponent] #{...} +# 66| getStmt: [MethodCall] call to blah +# 66| getReceiver: [Self, SelfVariableAccess] self +# 66| getStmt: [AddExpr] ... + ... +# 66| getAnOperand/getLeftOperand/getReceiver: [IntegerLiteral] 1 +# 66| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 9 +# 69| getStmt: [CharacterLiteral] ?x +# 70| getStmt: [CharacterLiteral] ?\n +# 71| getStmt: [CharacterLiteral] ?\s +# 72| getStmt: [CharacterLiteral] ?\\ +# 73| getStmt: [CharacterLiteral] ?\u{58} +# 74| getStmt: [CharacterLiteral] ?\C-a +# 75| getStmt: [CharacterLiteral] ?\M-a +# 76| getStmt: [CharacterLiteral] ?\M-\C-a +# 77| getStmt: [CharacterLiteral] ?\C-\M-a +# 80| getStmt: [SymbolLiteral] :"" +# 81| getStmt: [SymbolLiteral] :hello +# 82| getStmt: [SymbolLiteral] :"foo bar" +# 82| getComponent: [StringTextComponent] foo bar +# 83| getStmt: [SymbolLiteral] :"bar baz" +# 83| getComponent: [StringTextComponent] bar baz +# 84| getStmt: [HashLiteral] {...} +# 84| getElement: [Pair] Pair +# 84| getKey: [SymbolLiteral] :foo +# 84| getValue: [StringLiteral] "bar" +# 84| getComponent: [StringTextComponent] bar +# 85| getStmt: [SymbolLiteral] :"wibble" +# 85| getComponent: [StringTextComponent] wibble +# 86| getStmt: [SymbolLiteral] :"wibble wobble" +# 86| getComponent: [StringTextComponent] wibble wobble +# 87| getStmt: [SymbolLiteral] :"foo_#{...}" +# 87| getComponent: [StringTextComponent] foo_ +# 87| getComponent: [StringInterpolationComponent] #{...} +# 87| getStmt: [AddExpr] ... + ... +# 87| getAnOperand/getLeftOperand/getReceiver: [IntegerLiteral] 2 +# 87| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 2 +# 88| getStmt: [SymbolLiteral] :"foo_#{ 1 + 1 }" +# 88| getComponent: [StringTextComponent] foo_#{ 1 + 1 } +# 89| getStmt: [SymbolLiteral] :"foo_#{ 3 - 2 }" +# 89| getComponent: [StringTextComponent] foo_#{ 3 - 2 } +# 92| getStmt: [ArrayLiteral] [...] +# 93| getStmt: [ArrayLiteral] [...] +# 93| getElement: [IntegerLiteral] 1 +# 93| getElement: [IntegerLiteral] 2 +# 93| getElement: [IntegerLiteral] 3 +# 94| getStmt: [ArrayLiteral] [...] +# 94| getElement: [IntegerLiteral] 4 +# 94| getElement: [IntegerLiteral] 5 +# 94| getElement: [DivExpr] ... / ... +# 94| getAnOperand/getLeftOperand/getReceiver: [IntegerLiteral] 12 +# 94| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 2 +# 95| getStmt: [ArrayLiteral] [...] +# 95| getElement: [IntegerLiteral] 7 +# 95| getElement: [ArrayLiteral] [...] +# 95| getElement: [IntegerLiteral] 8 +# 95| getElement: [IntegerLiteral] 9 +# 98| getStmt: [ArrayLiteral] %w(...) +# 99| getStmt: [ArrayLiteral] %w(...) +# 99| getElement: [StringLiteral] "foo" +# 99| getComponent: [StringTextComponent] foo +# 99| getElement: [StringLiteral] "bar" +# 99| getComponent: [StringTextComponent] bar +# 99| getElement: [StringLiteral] "baz" +# 99| getComponent: [StringTextComponent] baz +# 100| getStmt: [ArrayLiteral] %w(...) +# 100| getElement: [StringLiteral] "foo" +# 100| getComponent: [StringTextComponent] foo +# 100| getElement: [StringLiteral] "bar" +# 100| getComponent: [StringTextComponent] bar +# 100| getElement: [StringLiteral] "baz" +# 100| getComponent: [StringTextComponent] baz +# 101| getStmt: [ArrayLiteral] %w(...) +# 101| getElement: [StringLiteral] "foo" +# 101| getComponent: [StringTextComponent] foo +# 101| getElement: [StringLiteral] "bar#{...}" +# 101| getComponent: [StringTextComponent] bar +# 101| getComponent: [StringInterpolationComponent] #{...} +# 101| getStmt: [AddExpr] ... + ... +# 101| getAnOperand/getLeftOperand/getReceiver: [IntegerLiteral] 1 +# 101| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 101| getElement: [StringLiteral] "baz" +# 101| getComponent: [StringTextComponent] baz +# 102| getStmt: [ArrayLiteral] %w(...) +# 102| getElement: [StringLiteral] "foo" +# 102| getComponent: [StringTextComponent] foo +# 102| getElement: [StringLiteral] "bar#{1+1}" +# 102| getComponent: [StringTextComponent] bar#{1+1} +# 102| getElement: [StringLiteral] "baz" +# 102| getComponent: [StringTextComponent] baz +# 105| getStmt: [ArrayLiteral] %i(...) +# 106| getStmt: [ArrayLiteral] %i(...) +# 106| getElement: [SymbolLiteral] :"foo" +# 106| getComponent: [StringTextComponent] foo +# 106| getElement: [SymbolLiteral] :"bar" +# 106| getComponent: [StringTextComponent] bar +# 106| getElement: [SymbolLiteral] :"baz" +# 106| getComponent: [StringTextComponent] baz +# 107| getStmt: [ArrayLiteral] %i(...) +# 107| getElement: [SymbolLiteral] :"foo" +# 107| getComponent: [StringTextComponent] foo +# 107| getElement: [SymbolLiteral] :"bar" +# 107| getComponent: [StringTextComponent] bar +# 107| getElement: [SymbolLiteral] :"baz" +# 107| getComponent: [StringTextComponent] baz +# 108| getStmt: [ArrayLiteral] %i(...) +# 108| getElement: [SymbolLiteral] :"foo" +# 108| getComponent: [StringTextComponent] foo +# 108| getElement: [SymbolLiteral] :"bar#{...}" +# 108| getComponent: [StringTextComponent] bar +# 108| getComponent: [StringInterpolationComponent] #{...} +# 108| getStmt: [AddExpr] ... + ... +# 108| getAnOperand/getLeftOperand/getReceiver: [IntegerLiteral] 2 +# 108| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 4 +# 108| getElement: [SymbolLiteral] :"baz" +# 108| getComponent: [StringTextComponent] baz +# 109| getStmt: [ArrayLiteral] %i(...) +# 109| getElement: [SymbolLiteral] :"foo" +# 109| getComponent: [StringTextComponent] foo +# 109| getElement: [SymbolLiteral] :"bar#{" +# 109| getComponent: [StringTextComponent] bar#{ +# 109| getElement: [SymbolLiteral] :"2" +# 109| getComponent: [StringTextComponent] 2 +# 109| getElement: [SymbolLiteral] :"+" +# 109| getComponent: [StringTextComponent] + +# 109| getElement: [SymbolLiteral] :"4" +# 109| getComponent: [StringTextComponent] 4 +# 109| getElement: [SymbolLiteral] :"}" +# 109| getComponent: [StringTextComponent] } +# 109| getElement: [SymbolLiteral] :"baz" +# 109| getComponent: [StringTextComponent] baz +# 112| getStmt: [HashLiteral] {...} +# 113| getStmt: [HashLiteral] {...} +# 113| getElement: [Pair] Pair +# 113| getKey: [SymbolLiteral] :foo +# 113| getValue: [IntegerLiteral] 1 +# 113| getElement: [Pair] Pair +# 113| getKey: [SymbolLiteral] :bar +# 113| getValue: [IntegerLiteral] 2 +# 113| getElement: [Pair] Pair +# 113| getKey: [StringLiteral] "baz" +# 113| getComponent: [StringTextComponent] baz +# 113| getValue: [IntegerLiteral] 3 +# 114| getStmt: [HashLiteral] {...} +# 114| getElement: [Pair] Pair +# 114| getKey: [SymbolLiteral] :foo +# 114| getValue: [IntegerLiteral] 7 +# 114| getElement: [HashSplatExpr] ** ... +# 114| getAnOperand/getOperand/getReceiver: [MethodCall] call to bar +# 114| getReceiver: [Self, SelfVariableAccess] self +# 117| getStmt: [ParenthesizedExpr] ( ... ) +# 117| getStmt: [RangeLiteral] _ .. _ +# 117| getBegin: [IntegerLiteral] 1 +# 117| getEnd: [IntegerLiteral] 10 +# 118| getStmt: [ParenthesizedExpr] ( ... ) +# 118| getStmt: [RangeLiteral] _ ... _ +# 118| getBegin: [IntegerLiteral] 1 +# 118| getEnd: [IntegerLiteral] 10 +# 119| getStmt: [ParenthesizedExpr] ( ... ) +# 119| getStmt: [RangeLiteral] _ .. _ +# 119| getBegin: [IntegerLiteral] 1 +# 119| getEnd: [IntegerLiteral] 0 +# 120| getStmt: [ParenthesizedExpr] ( ... ) +# 120| getStmt: [RangeLiteral] _ .. _ +# 120| getBegin: [MethodCall] call to start +# 120| getReceiver: [Self, SelfVariableAccess] self +# 120| getEnd: [AddExpr] ... + ... +# 120| getAnOperand/getLeftOperand/getReceiver: [IntegerLiteral] 2 +# 120| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 3 +# 121| getStmt: [ParenthesizedExpr] ( ... ) +# 121| getStmt: [RangeLiteral] _ .. _ +# 121| getBegin: [IntegerLiteral] 1 +# 122| getStmt: [ParenthesizedExpr] ( ... ) +# 122| getStmt: [RangeLiteral] _ .. _ +# 122| getEnd: [IntegerLiteral] 1 +# 123| getStmt: [ParenthesizedExpr] ( ... ) +# 123| getStmt: [SubExpr] ... - ... +# 123| getAnOperand/getLeftOperand/getReceiver: [RangeLiteral] _ .. _ +# 123| getBegin: [IntegerLiteral] 0 +# 123| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 126| getStmt: [SubshellLiteral] `ls -l` +# 126| getComponent: [StringTextComponent] ls -l +# 127| getStmt: [SubshellLiteral] `ls -l` +# 127| getComponent: [StringTextComponent] ls -l +# 128| getStmt: [SubshellLiteral] `du -d #{...}` +# 128| getComponent: [StringTextComponent] du -d +# 128| getComponent: [StringInterpolationComponent] #{...} +# 128| getStmt: [AddExpr] ... + ... +# 128| getAnOperand/getLeftOperand/getReceiver: [IntegerLiteral] 1 +# 128| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 129| getStmt: [SubshellLiteral] `du -d #{...}` +# 129| getComponent: [StringTextComponent] du -d +# 129| getComponent: [StringInterpolationComponent] #{...} +# 129| getStmt: [SubExpr] ... - ... +# 129| getAnOperand/getLeftOperand/getReceiver: [IntegerLiteral] 5 +# 129| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 4 +# 132| getStmt: [RegExpLiteral] // +# 133| getStmt: [RegExpLiteral] /foo/ +# 133| getParsed: [RegExpSequence] foo +# 133| 0: [RegExpConstant, RegExpNormalChar] f +# 133| 1: [RegExpConstant, RegExpNormalChar] o +# 133| 2: [RegExpConstant, RegExpNormalChar] o +# 133| getComponent: [StringTextComponent] foo +# 134| getStmt: [RegExpLiteral] /foo/ +# 134| getParsed: [RegExpSequence] foo +# 134| 0: [RegExpConstant, RegExpNormalChar] f +# 134| 1: [RegExpConstant, RegExpNormalChar] o +# 134| 2: [RegExpConstant, RegExpNormalChar] o +# 134| getComponent: [StringTextComponent] foo +# 135| getStmt: [RegExpLiteral] /foo+\sbar\S/ +# 135| getParsed: [RegExpSequence] foo+\sbar\S +# 135| 0: [RegExpConstant, RegExpNormalChar] f +# 135| 1: [RegExpConstant, RegExpNormalChar] o +# 135| 2: [RegExpPlus] o+ +# 135| 0: [RegExpConstant, RegExpNormalChar] o +# 135| 3: [RegExpCharacterClassEscape] \s +# 135| 4: [RegExpConstant, RegExpNormalChar] b +# 135| 5: [RegExpConstant, RegExpNormalChar] a +# 135| 6: [RegExpConstant, RegExpNormalChar] r +# 135| 7: [RegExpCharacterClassEscape] \S +# 135| getComponent: [StringTextComponent] foo+ +# 135| getComponent: [StringEscapeSequenceComponent] \s +# 135| getComponent: [StringTextComponent] bar +# 135| getComponent: [StringEscapeSequenceComponent] \S +# 136| getStmt: [RegExpLiteral] /foo#{...}bar/ +# 136| getComponent: [StringTextComponent] foo +# 136| getComponent: [StringInterpolationComponent] #{...} +# 136| getStmt: [AddExpr] ... + ... +# 136| getAnOperand/getLeftOperand/getReceiver: [IntegerLiteral] 1 +# 136| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 136| getComponent: [StringTextComponent] bar +# 137| getStmt: [RegExpLiteral] /foo/ +# 137| getParsed: [RegExpSequence] foo +# 137| 0: [RegExpConstant, RegExpNormalChar] f +# 137| 1: [RegExpConstant, RegExpNormalChar] o +# 137| 2: [RegExpConstant, RegExpNormalChar] o +# 137| getComponent: [StringTextComponent] foo +# 138| getStmt: [RegExpLiteral] // +# 139| getStmt: [RegExpLiteral] /foo/ +# 139| getParsed: [RegExpSequence] foo +# 139| 0: [RegExpConstant, RegExpNormalChar] f +# 139| 1: [RegExpConstant, RegExpNormalChar] o +# 139| 2: [RegExpConstant, RegExpNormalChar] o +# 139| getComponent: [StringTextComponent] foo +# 140| getStmt: [RegExpLiteral] /foo/ +# 140| getParsed: [RegExpSequence] foo +# 140| 0: [RegExpConstant, RegExpNormalChar] f +# 140| 1: [RegExpConstant, RegExpNormalChar] o +# 140| 2: [RegExpConstant, RegExpNormalChar] o +# 140| getComponent: [StringTextComponent] foo +# 141| getStmt: [RegExpLiteral] /foo+\sbar\S/ +# 141| getParsed: [RegExpSequence] foo+\sbar\S +# 141| 0: [RegExpConstant, RegExpNormalChar] f +# 141| 1: [RegExpConstant, RegExpNormalChar] o +# 141| 2: [RegExpPlus] o+ +# 141| 0: [RegExpConstant, RegExpNormalChar] o +# 141| 3: [RegExpCharacterClassEscape] \s +# 141| 4: [RegExpConstant, RegExpNormalChar] b +# 141| 5: [RegExpConstant, RegExpNormalChar] a +# 141| 6: [RegExpConstant, RegExpNormalChar] r +# 141| 7: [RegExpCharacterClassEscape] \S +# 141| getComponent: [StringTextComponent] foo+ +# 141| getComponent: [StringEscapeSequenceComponent] \s +# 141| getComponent: [StringTextComponent] bar +# 141| getComponent: [StringEscapeSequenceComponent] \S +# 142| getStmt: [RegExpLiteral] /foo#{...}bar/ +# 142| getComponent: [StringTextComponent] foo +# 142| getComponent: [StringInterpolationComponent] #{...} +# 142| getStmt: [AddExpr] ... + ... +# 142| getAnOperand/getLeftOperand/getReceiver: [IntegerLiteral] 1 +# 142| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 142| getComponent: [StringTextComponent] bar +# 143| getStmt: [RegExpLiteral] /foo/ +# 143| getParsed: [RegExpSequence] foo +# 143| 0: [RegExpConstant, RegExpNormalChar] f +# 143| 1: [RegExpConstant, RegExpNormalChar] o +# 143| 2: [RegExpConstant, RegExpNormalChar] o +# 143| getComponent: [StringTextComponent] foo +# 146| getStmt: [StringLiteral] "abcdefghijklmnopqrstuvwxyzabcdef" +# 146| getComponent: [StringTextComponent] abcdefghijklmnopqrstuvwxyzabcdef +# 147| getStmt: [StringLiteral] "foobarfoobarfoobarfoobarfooba..." +# 147| getComponent: [StringTextComponent] foobarfoobarfoobarfoobarfoobarfoo +# 148| getStmt: [StringLiteral] "foobar\\foobar\\foobar\\fooba..." +# 148| getComponent: [StringTextComponent] foobar +# 148| getComponent: [StringEscapeSequenceComponent] \\ +# 148| getComponent: [StringTextComponent] foobar +# 148| getComponent: [StringEscapeSequenceComponent] \\ +# 148| getComponent: [StringTextComponent] foobar +# 148| getComponent: [StringEscapeSequenceComponent] \\ +# 148| getComponent: [StringTextComponent] foobar +# 148| getComponent: [StringEscapeSequenceComponent] \\ +# 148| getComponent: [StringTextComponent] foobar +# 151| getStmt: [MethodCall] call to run_sql +# 151| getReceiver: [Self, SelfVariableAccess] self +# 151| getArgument: [HereDoc] <<SQL +# 151| getComponent: [StringTextComponent] +# 151| select * from table +# 151| +# 151| getArgument: [HereDoc] <<SQL +# 153| getComponent: [StringTextComponent] +# 153| where name = +# 154| getComponent: [StringInterpolationComponent] #{...} +# 154| getStmt: [MethodCall] call to name +# 154| getReceiver: [Self, SelfVariableAccess] self +# 154| getComponent: [StringTextComponent] +# 154| +# 157| getStmt: [Method] m +# 158| getStmt: [AssignExpr] ... = ... +# 158| getAnOperand/getLeftOperand: [LocalVariableAccess] query +# 158| getAnOperand/getRightOperand: [HereDoc] <<-BLA +# 158| getComponent: [StringTextComponent] +# 158| some text +# 159| getComponent: [StringEscapeSequenceComponent] \n +# 159| getComponent: [StringTextComponent] and some more +# 159| +# 163| getStmt: [AssignExpr] ... = ... +# 163| getAnOperand/getLeftOperand: [LocalVariableAccess] query +# 163| getAnOperand/getRightOperand: [HereDoc] <<~SQUIGGLY +# 163| getComponent: [StringTextComponent] +# 163| indented stuff +# 163| +# 167| getStmt: [AssignExpr] ... = ... +# 167| getAnOperand/getLeftOperand: [LocalVariableAccess] query +# 167| getAnOperand/getRightOperand: [HereDoc] <<"DOC" +# 167| getComponent: [StringTextComponent] +# 167| text with +# 168| getComponent: [StringInterpolationComponent] #{...} +# 168| getStmt: [MethodCall] call to interpolation +# 168| getReceiver: [Self, SelfVariableAccess] self +# 168| getComponent: [StringTextComponent] ! +# 168| +# 172| getStmt: [AssignExpr] ... = ... +# 172| getAnOperand/getLeftOperand: [LocalVariableAccess] query +# 172| getAnOperand/getRightOperand: [HereDoc] <<'DOC' +# 172| getComponent: [StringTextComponent] +# 172| text without +# 173| getComponent: [StringInterpolationComponent] #{...} +# 173| getStmt: [MethodCall] call to interpolation +# 173| getReceiver: [Self, SelfVariableAccess] self +# 173| getComponent: [StringTextComponent] ! +# 173| +# 176| getStmt: [AssignExpr] ... = ... +# 176| getAnOperand/getLeftOperand: [LocalVariableAccess] output +# 176| getAnOperand/getRightOperand: [HereDoc] <<`SCRIPT` +# 176| getComponent: [StringTextComponent] +# 176| cat file.txt +# 176| +control/loops.rb: +# 1| [Toplevel] loops.rb +# 2| getStmt: [AssignExpr] ... = ... +# 2| getAnOperand/getLeftOperand: [LocalVariableAccess] foo +# 2| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 3| getStmt: [AssignExpr] ... = ... +# 3| getAnOperand/getLeftOperand: [LocalVariableAccess] sum +# 3| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 4| getStmt: [AssignExpr] ... = ... +# 4| getAnOperand/getLeftOperand: [LocalVariableAccess] x +# 4| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 5| getStmt: [AssignExpr] ... = ... +# 5| getAnOperand/getLeftOperand: [LocalVariableAccess] y +# 5| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 6| getStmt: [AssignExpr] ... = ... +# 6| getAnOperand/getLeftOperand: [LocalVariableAccess] z +# 6| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 9| getStmt: [ForExpr] for ... in ... +# 9| getPattern: [LocalVariableAccess] n +# 9| <in>: [???] In +# 9| getValue: [RangeLiteral] _ .. _ +# 9| getBegin: [IntegerLiteral] 1 +# 9| getEnd: [IntegerLiteral] 10 +# 9| getBody: [StmtSequence] do ... +# 10| getStmt: [AssignAddExpr] ... += ... +# 10| getAnOperand/getLeftOperand: [LocalVariableAccess] sum +# 10| getAnOperand/getRightOperand: [LocalVariableAccess] n +# 11| getStmt: [AssignExpr] ... = ... +# 11| getAnOperand/getLeftOperand: [LocalVariableAccess] foo +# 11| getAnOperand/getRightOperand: [LocalVariableAccess] n +# 16| getStmt: [ForExpr] for ... in ... +# 16| getPattern: [LocalVariableAccess] n +# 16| <in>: [???] In +# 16| getValue: [RangeLiteral] _ .. _ +# 16| getBegin: [IntegerLiteral] 1 +# 16| getEnd: [IntegerLiteral] 10 +# 16| getBody: [StmtSequence] do ... +# 17| getStmt: [AssignAddExpr] ... += ... +# 17| getAnOperand/getLeftOperand: [LocalVariableAccess] sum +# 17| getAnOperand/getRightOperand: [LocalVariableAccess] n +# 18| getStmt: [AssignSubExpr] ... -= ... +# 18| getAnOperand/getLeftOperand: [LocalVariableAccess] foo +# 18| getAnOperand/getRightOperand: [LocalVariableAccess] n +# 22| getStmt: [ForExpr] for ... in ... +# 22| getPattern: [TuplePattern] (..., ...) +# 22| getElement: [LocalVariableAccess] key +# 22| getElement: [LocalVariableAccess] value +# 22| <in>: [???] In +# 22| getValue: [HashLiteral] {...} +# 22| getElement: [Pair] Pair +# 22| getKey: [SymbolLiteral] :foo +# 22| getValue: [IntegerLiteral] 0 +# 22| getElement: [Pair] Pair +# 22| getKey: [SymbolLiteral] :bar +# 22| getValue: [IntegerLiteral] 1 +# 22| getBody: [StmtSequence] do ... +# 23| getStmt: [AssignAddExpr] ... += ... +# 23| getAnOperand/getLeftOperand: [LocalVariableAccess] sum +# 23| getAnOperand/getRightOperand: [LocalVariableAccess] value +# 24| getStmt: [AssignMulExpr] ... *= ... +# 24| getAnOperand/getLeftOperand: [LocalVariableAccess] foo +# 24| getAnOperand/getRightOperand: [LocalVariableAccess] value +# 28| getStmt: [ForExpr] for ... in ... +# 28| getPattern: [TuplePattern] (..., ...) +# 28| getElement: [LocalVariableAccess] key +# 28| getElement: [LocalVariableAccess] value +# 28| <in>: [???] In +# 28| getValue: [HashLiteral] {...} +# 28| getElement: [Pair] Pair +# 28| getKey: [SymbolLiteral] :foo +# 28| getValue: [IntegerLiteral] 0 +# 28| getElement: [Pair] Pair +# 28| getKey: [SymbolLiteral] :bar +# 28| getValue: [IntegerLiteral] 1 +# 28| getBody: [StmtSequence] do ... +# 29| getStmt: [AssignAddExpr] ... += ... +# 29| getAnOperand/getLeftOperand: [LocalVariableAccess] sum +# 29| getAnOperand/getRightOperand: [LocalVariableAccess] value +# 30| getStmt: [AssignDivExpr] ... /= ... +# 30| getAnOperand/getLeftOperand: [LocalVariableAccess] foo +# 30| getAnOperand/getRightOperand: [LocalVariableAccess] value +# 31| getStmt: [BreakStmt] break +# 35| getStmt: [WhileExpr] while ... +# 35| getCondition: [LTExpr] ... < ... +# 35| getAnOperand/getLeftOperand/getLesserOperand/getReceiver: [LocalVariableAccess] x +# 35| getAnOperand/getArgument/getGreaterOperand/getRightOperand: [LocalVariableAccess] y +# 35| getBody: [StmtSequence] do ... +# 36| getStmt: [AssignAddExpr] ... += ... +# 36| getAnOperand/getLeftOperand: [LocalVariableAccess] x +# 36| getAnOperand/getRightOperand: [IntegerLiteral] 1 +# 37| getStmt: [AssignAddExpr] ... += ... +# 37| getAnOperand/getLeftOperand: [LocalVariableAccess] z +# 37| getAnOperand/getRightOperand: [IntegerLiteral] 1 +# 38| getStmt: [NextStmt] next +# 42| getStmt: [WhileExpr] while ... +# 42| getCondition: [LTExpr] ... < ... +# 42| getAnOperand/getLeftOperand/getLesserOperand/getReceiver: [LocalVariableAccess] x +# 42| getAnOperand/getArgument/getGreaterOperand/getRightOperand: [LocalVariableAccess] y +# 42| getBody: [StmtSequence] do ... +# 43| getStmt: [AssignAddExpr] ... += ... +# 43| getAnOperand/getLeftOperand: [LocalVariableAccess] x +# 43| getAnOperand/getRightOperand: [IntegerLiteral] 1 +# 44| getStmt: [AssignAddExpr] ... += ... +# 44| getAnOperand/getLeftOperand: [LocalVariableAccess] z +# 44| getAnOperand/getRightOperand: [IntegerLiteral] 2 +# 48| getStmt: [WhileModifierExpr] ... while ... +# 48| getBody: [AssignAddExpr] ... += ... +# 48| getAnOperand/getLeftOperand: [LocalVariableAccess] x +# 48| getAnOperand/getRightOperand: [IntegerLiteral] 1 +# 48| getCondition: [GEExpr] ... >= ... +# 48| getAnOperand/getGreaterOperand/getLeftOperand/getReceiver: [LocalVariableAccess] y +# 48| getAnOperand/getArgument/getLesserOperand/getRightOperand: [LocalVariableAccess] x +# 51| getStmt: [UntilExpr] until ... +# 51| getCondition: [EqExpr] ... == ... +# 51| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 51| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] y +# 51| getBody: [StmtSequence] do ... +# 52| getStmt: [AssignAddExpr] ... += ... +# 52| getAnOperand/getLeftOperand: [LocalVariableAccess] x +# 52| getAnOperand/getRightOperand: [IntegerLiteral] 1 +# 53| getStmt: [AssignSubExpr] ... -= ... +# 53| getAnOperand/getLeftOperand: [LocalVariableAccess] z +# 53| getAnOperand/getRightOperand: [IntegerLiteral] 1 +# 57| getStmt: [UntilExpr] until ... +# 57| getCondition: [GTExpr] ... > ... +# 57| getAnOperand/getGreaterOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 57| getAnOperand/getArgument/getLesserOperand/getRightOperand: [LocalVariableAccess] y +# 57| getBody: [StmtSequence] do ... +# 58| getStmt: [AssignAddExpr] ... += ... +# 58| getAnOperand/getLeftOperand: [LocalVariableAccess] x +# 58| getAnOperand/getRightOperand: [IntegerLiteral] 1 +# 59| getStmt: [AssignSubExpr] ... -= ... +# 59| getAnOperand/getLeftOperand: [LocalVariableAccess] z +# 59| getAnOperand/getRightOperand: [IntegerLiteral] 4 +# 63| getStmt: [UntilModifierExpr] ... until ... +# 63| getBody: [AssignSubExpr] ... -= ... +# 63| getAnOperand/getLeftOperand: [LocalVariableAccess] x +# 63| getAnOperand/getRightOperand: [IntegerLiteral] 1 +# 63| getCondition: [EqExpr] ... == ... +# 63| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 63| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 0 +# 66| getStmt: [WhileExpr] while ... +# 66| getCondition: [LTExpr] ... < ... +# 66| getAnOperand/getLeftOperand/getLesserOperand/getReceiver: [LocalVariableAccess] x +# 66| getAnOperand/getArgument/getGreaterOperand/getRightOperand: [LocalVariableAccess] y +# 66| getBody: [StmtSequence] do ... +misc/misc.erb: +# 2| [Toplevel] misc.erb +# 2| getStmt: [MethodCall] call to require_asset +# 2| getReceiver: [Self, SelfVariableAccess] self +# 2| getArgument: [StringLiteral] "main_include_admin.js" +# 2| getComponent: [StringTextComponent] main_include_admin.js +misc/misc.rb: +# 1| [Toplevel] misc.rb +# 1| getStmt: [AssignExpr] ... = ... +# 1| getAnOperand/getLeftOperand: [LocalVariableAccess] bar +# 1| getAnOperand/getRightOperand: [StringLiteral] "bar" +# 1| getComponent: [StringTextComponent] bar +# 3| getStmt: [UndefStmt] undef ... +# 3| getMethodName: [MethodName] foo +# 3| getMethodName: [MethodName] :foo +# 3| getMethodName: [MethodName] foo= +# 3| getMethodName: [MethodName] [] +# 3| getMethodName: [MethodName] []= +# 4| getStmt: [UndefStmt] undef ... +# 4| getMethodName: [MethodName] :"foo_#{...}" +# 4| getComponent: [StringTextComponent] foo_ +# 4| getComponent: [StringInterpolationComponent] #{...} +# 4| getStmt: [LocalVariableAccess] bar +# 5| getStmt: [UndefStmt] undef ... +# 5| getMethodName: [MethodName] nil +# 5| getMethodName: [MethodName] true +# 5| getMethodName: [MethodName] false +# 5| getMethodName: [MethodName] super +# 5| getMethodName: [MethodName] self +# 7| getStmt: [AliasStmt] alias ... +# 7| getNewName: [MethodName] new +# 7| getOldName: [MethodName] :old +# 8| getStmt: [AliasStmt] alias ... +# 8| getNewName: [MethodName] foo= +# 8| getOldName: [MethodName] []= +# 9| getStmt: [AliasStmt] alias ... +# 9| getNewName: [MethodName] super +# 9| getOldName: [MethodName] self +# 10| getStmt: [AliasStmt] alias ... +# 10| getNewName: [MethodName] :"\n#{...}" +# 10| getComponent: [StringEscapeSequenceComponent] \n +# 10| getComponent: [StringInterpolationComponent] #{...} +# 10| getStmt: [LocalVariableAccess] bar +# 10| getOldName: [MethodName] :"foo" +# 10| getComponent: [StringTextComponent] foo +modules/modules.rb: +# 1| [Toplevel] modules.rb +# 1| getStmt: [ModuleDeclaration] Empty +# 4| getStmt: [ModuleDeclaration] Foo +# 5| getStmt: [ModuleDeclaration] Bar +# 6| getStmt: [ClassDeclaration] ClassInFooBar +# 9| getStmt: [Method] method_in_foo_bar +# 12| getStmt: [MethodCall] call to puts +# 12| getReceiver: [Self, SelfVariableAccess] self +# 12| getArgument: [StringLiteral] "module Foo::Bar" +# 12| getComponent: [StringTextComponent] module Foo::Bar +# 13| getStmt: [AssignExpr] ... = ... +# 13| getAnOperand/getLeftOperand: [GlobalVariableAccess] $global_var +# 13| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 16| getStmt: [Method] method_in_foo +# 19| getStmt: [ClassDeclaration] ClassInFoo +# 22| getStmt: [MethodCall] call to puts +# 22| getReceiver: [Self, SelfVariableAccess] self +# 22| getArgument: [StringLiteral] "module Foo" +# 22| getComponent: [StringTextComponent] module Foo +# 23| getStmt: [AssignExpr] ... = ... +# 23| getAnOperand/getLeftOperand: [GlobalVariableAccess] $global_var +# 23| getAnOperand/getRightOperand: [IntegerLiteral] 1 +# 26| getStmt: [ModuleDeclaration] Foo +# 27| getStmt: [Method] method_in_another_definition_of_foo +# 30| getStmt: [ClassDeclaration] ClassInAnotherDefinitionOfFoo +# 33| getStmt: [MethodCall] call to puts +# 33| getReceiver: [Self, SelfVariableAccess] self +# 33| getArgument: [StringLiteral] "module Foo again" +# 33| getComponent: [StringTextComponent] module Foo again +# 34| getStmt: [AssignExpr] ... = ... +# 34| getAnOperand/getLeftOperand: [GlobalVariableAccess] $global_var +# 34| getAnOperand/getRightOperand: [IntegerLiteral] 2 +# 37| getStmt: [ModuleDeclaration] Bar +# 38| getStmt: [Method] method_a +# 41| getStmt: [Method] method_b +# 44| getStmt: [MethodCall] call to puts +# 44| getReceiver: [Self, SelfVariableAccess] self +# 44| getArgument: [StringLiteral] "module Bar" +# 44| getComponent: [StringTextComponent] module Bar +# 45| getStmt: [AssignExpr] ... = ... +# 45| getAnOperand/getLeftOperand: [GlobalVariableAccess] $global_var +# 45| getAnOperand/getRightOperand: [IntegerLiteral] 3 +# 48| getStmt: [ModuleDeclaration] Bar +# 48| getScopeExpr: [ConstantReadAccess] Foo +# 49| getStmt: [ClassDeclaration] ClassInAnotherDefinitionOfFooBar +# 52| getStmt: [Method] method_in_another_definition_of_foo_bar +# 55| getStmt: [MethodCall] call to puts +# 55| getReceiver: [Self, SelfVariableAccess] self +# 55| getArgument: [StringLiteral] "module Foo::Bar again" +# 55| getComponent: [StringTextComponent] module Foo::Bar again +# 56| getStmt: [AssignExpr] ... = ... +# 56| getAnOperand/getLeftOperand: [GlobalVariableAccess] $global_var +# 56| getAnOperand/getRightOperand: [IntegerLiteral] 4 +# 60| getStmt: [ModuleDeclaration] MyModuleInGlobalScope +# 63| getStmt: [ModuleDeclaration] Test +# 65| getStmt: [ModuleDeclaration] Foo1 +# 66| getStmt: [ClassDeclaration] Bar +# 66| getScopeExpr: [ConstantReadAccess] Foo1 +# 70| getStmt: [ModuleDeclaration] Foo2 +# 71| getStmt: [ModuleDeclaration] Foo2 +# 72| getStmt: [ClassDeclaration] Bar +# 72| getScopeExpr: [ConstantReadAccess] Foo2 +# 76| getStmt: [ModuleDeclaration] Foo3 +# 77| getStmt: [AssignExpr] ... = ... +# 77| getAnOperand/getLeftOperand: [ConstantAssignment] Foo3 +# 77| getAnOperand/getRightOperand: [ConstantReadAccess] Object +# 78| getStmt: [ClassDeclaration] Bar +# 78| getScopeExpr: [ConstantReadAccess] Foo3 +# 83| getStmt: [ModuleDeclaration] Other +# 84| getStmt: [ModuleDeclaration] Foo1 +# 88| getStmt: [ModuleDeclaration] IncludeTest +# 89| getStmt: [MethodCall] call to include +# 89| getReceiver: [Self, SelfVariableAccess] self +# 89| getArgument: [ConstantReadAccess] Test +# 90| getStmt: [MethodCall] call to module_eval +# 90| getReceiver: [ConstantReadAccess] Object +# 90| getBlock: [BraceBlock] { ... } +# 90| getStmt: [MethodCall] call to prepend +# 90| getReceiver: [Self, SelfVariableAccess] self +# 90| getArgument: [ConstantReadAccess] Other +# 91| getStmt: [ModuleDeclaration] Y +# 91| getScopeExpr: [ConstantReadAccess] Foo1 +# 95| getStmt: [ModuleDeclaration] IncludeTest2 +# 96| getStmt: [MethodCall] call to include +# 96| getReceiver: [Self, SelfVariableAccess] self +# 96| getArgument: [ConstantReadAccess] Test +# 97| getStmt: [ModuleDeclaration] Z +# 97| getScopeExpr: [ConstantReadAccess] Foo1 +# 101| getStmt: [ModuleDeclaration] PrependTest +# 102| getStmt: [MethodCall] call to prepend +# 102| getReceiver: [Self, SelfVariableAccess] self +# 102| getArgument: [ConstantReadAccess] Test +# 103| getStmt: [ModuleDeclaration] Y +# 103| getScopeExpr: [ConstantReadAccess] Foo2 +# 107| getStmt: [ModuleDeclaration] MM +# 108| getStmt: [ModuleDeclaration] MM +# 108| getScopeExpr: [ConstantReadAccess] MM +# 112| getStmt: [ClassDeclaration] YY +# 115| getStmt: [ModuleDeclaration] XX +# 116| getStmt: [ClassDeclaration] YY +# 116| getSuperclassExpr: [ConstantReadAccess] YY +# 120| getStmt: [ModuleDeclaration] Baz +# 120| getScopeExpr: [ConstantReadAccess] Bar +# 120| getScopeExpr: [ConstantReadAccess] Foo1 +# 120| getScopeExpr: [ConstantReadAccess] Test +operations/operations.rb: +# 1| [Toplevel] operations.rb +# 3| getStmt: [AssignExpr] ... = ... +# 3| getAnOperand/getLeftOperand: [LocalVariableAccess] a +# 3| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 4| getStmt: [AssignExpr] ... = ... +# 4| getAnOperand/getLeftOperand: [LocalVariableAccess] b +# 4| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 5| getStmt: [AssignExpr] ... = ... +# 5| getAnOperand/getLeftOperand: [LocalVariableAccess] bar +# 5| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 6| getStmt: [AssignExpr] ... = ... +# 6| getAnOperand/getLeftOperand: [LocalVariableAccess] base +# 6| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 7| getStmt: [AssignExpr] ... = ... +# 7| getAnOperand/getLeftOperand: [LocalVariableAccess] baz +# 7| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 8| getStmt: [AssignExpr] ... = ... +# 8| getAnOperand/getLeftOperand: [LocalVariableAccess] foo +# 8| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 9| getStmt: [AssignExpr] ... = ... +# 9| getAnOperand/getLeftOperand: [LocalVariableAccess] handle +# 9| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 10| getStmt: [AssignExpr] ... = ... +# 10| getAnOperand/getLeftOperand: [LocalVariableAccess] m +# 10| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 11| getStmt: [AssignExpr] ... = ... +# 11| getAnOperand/getLeftOperand: [LocalVariableAccess] mask +# 11| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 12| getStmt: [AssignExpr] ... = ... +# 12| getAnOperand/getLeftOperand: [LocalVariableAccess] n +# 12| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 13| getStmt: [AssignExpr] ... = ... +# 13| getAnOperand/getLeftOperand: [LocalVariableAccess] name +# 13| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 14| getStmt: [AssignExpr] ... = ... +# 14| getAnOperand/getLeftOperand: [LocalVariableAccess] num +# 14| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 15| getStmt: [AssignExpr] ... = ... +# 15| getAnOperand/getLeftOperand: [LocalVariableAccess] power +# 15| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 16| getStmt: [AssignExpr] ... = ... +# 16| getAnOperand/getLeftOperand: [LocalVariableAccess] qux +# 16| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 17| getStmt: [AssignExpr] ... = ... +# 17| getAnOperand/getLeftOperand: [LocalVariableAccess] w +# 17| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 18| getStmt: [AssignExpr] ... = ... +# 18| getAnOperand/getLeftOperand: [LocalVariableAccess] x +# 18| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 19| getStmt: [AssignExpr] ... = ... +# 19| getAnOperand/getLeftOperand: [LocalVariableAccess] y +# 19| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 20| getStmt: [AssignExpr] ... = ... +# 20| getAnOperand/getLeftOperand: [LocalVariableAccess] z +# 20| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 23| getStmt: [NotExpr] ! ... +# 23| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] a +# 24| getStmt: [NotExpr] not ... +# 24| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] b +# 25| getStmt: [UnaryPlusExpr] + ... +# 25| getAnOperand/getOperand/getReceiver: [IntegerLiteral] 14 +# 26| getStmt: [UnaryMinusExpr] - ... +# 26| getAnOperand/getOperand/getReceiver: [IntegerLiteral] 7 +# 27| getStmt: [ComplementExpr] ~ ... +# 27| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] x +# 28| getStmt: [DefinedExpr] defined? ... +# 28| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] foo +# 29| getStmt: [Method] foo +# 29| getStmt: [ReturnStmt] return +# 29| getValue: [ArgumentList] ..., ... +# 29| getElement: [IntegerLiteral] 1 +# 29| getElement: [SplatExpr] * ... +# 29| getAnOperand/getOperand/getReceiver: [ArrayLiteral] [...] +# 29| getElement: [IntegerLiteral] 2 +# 29| getElement: [Pair] Pair +# 29| getKey: [SymbolLiteral] :a +# 29| getValue: [IntegerLiteral] 3 +# 29| getElement: [HashSplatExpr] ** ... +# 29| getAnOperand/getOperand/getReceiver: [HashLiteral] {...} +# 29| getElement: [Pair] Pair +# 29| getKey: [SymbolLiteral] :b +# 29| getValue: [IntegerLiteral] 4 +# 29| getElement: [Pair] Pair +# 29| getKey: [SymbolLiteral] :c +# 29| getValue: [IntegerLiteral] 5 +# 32| getStmt: [AddExpr] ... + ... +# 32| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] w +# 32| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 234 +# 33| getStmt: [SubExpr] ... - ... +# 33| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 33| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 17 +# 34| getStmt: [MulExpr] ... * ... +# 34| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] y +# 34| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 10 +# 35| getStmt: [DivExpr] ... / ... +# 35| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] z +# 35| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 2 +# 36| getStmt: [ModuloExpr] ... % ... +# 36| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] num +# 36| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 2 +# 37| getStmt: [ExponentExpr] ... ** ... +# 37| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] base +# 37| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] power +# 40| getStmt: [LogicalAndExpr] ... && ... +# 40| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo +# 40| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] bar +# 41| getStmt: [LogicalAndExpr] ... and ... +# 41| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] baz +# 41| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] qux +# 42| getStmt: [LogicalOrExpr] ... or ... +# 42| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a +# 42| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] b +# 43| getStmt: [LogicalOrExpr] ... || ... +# 43| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 43| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] y +# 46| getStmt: [LShiftExpr] ... << ... +# 46| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 46| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 3 +# 47| getStmt: [RShiftExpr] ... >> ... +# 47| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] y +# 47| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 16 +# 48| getStmt: [BitwiseAndExpr] ... & ... +# 48| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo +# 48| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 0xff +# 49| getStmt: [BitwiseOrExpr] ... | ... +# 49| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] bar +# 49| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 0x02 +# 50| getStmt: [BitwiseXorExpr] ... ^ ... +# 50| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] baz +# 50| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] qux +# 53| getStmt: [EqExpr] ... == ... +# 53| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 53| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] y +# 54| getStmt: [NEExpr] ... != ... +# 54| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a +# 54| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 123 +# 55| getStmt: [CaseEqExpr] ... === ... +# 55| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] m +# 55| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n +# 58| getStmt: [GTExpr] ... > ... +# 58| getAnOperand/getGreaterOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 58| getAnOperand/getArgument/getLesserOperand/getRightOperand: [IntegerLiteral] 0 +# 59| getStmt: [GEExpr] ... >= ... +# 59| getAnOperand/getGreaterOperand/getLeftOperand/getReceiver: [LocalVariableAccess] y +# 59| getAnOperand/getArgument/getLesserOperand/getRightOperand: [IntegerLiteral] 100 +# 60| getStmt: [LTExpr] ... < ... +# 60| getAnOperand/getLeftOperand/getLesserOperand/getReceiver: [LocalVariableAccess] a +# 60| getAnOperand/getArgument/getGreaterOperand/getRightOperand: [LocalVariableAccess] b +# 61| getStmt: [LEExpr] ... <= ... +# 61| getAnOperand/getLeftOperand/getLesserOperand/getReceiver: [IntegerLiteral] 7 +# 61| getAnOperand/getArgument/getGreaterOperand/getRightOperand: [LocalVariableAccess] foo +# 64| getStmt: [SpaceshipExpr] ... <=> ... +# 64| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a +# 64| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] b +# 65| getStmt: [RegExpMatchExpr] ... =~ ... +# 65| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] name +# 65| getAnOperand/getArgument/getRightOperand: [RegExpLiteral] /foo.*/ +# 65| getParsed: [RegExpSequence] foo.* +# 65| 0: [RegExpConstant, RegExpNormalChar] f +# 65| 1: [RegExpConstant, RegExpNormalChar] o +# 65| 2: [RegExpConstant, RegExpNormalChar] o +# 65| 3: [RegExpStar] .* +# 65| 0: [RegExpDot] . +# 65| getComponent: [StringTextComponent] foo.* +# 66| getStmt: [NoRegExpMatchExpr] ... !~ ... +# 66| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] handle +# 66| getAnOperand/getArgument/getRightOperand: [RegExpLiteral] /.*bar/ +# 66| getParsed: [RegExpSequence] .*bar +# 66| 0: [RegExpStar] .* +# 66| 0: [RegExpDot] . +# 66| 1: [RegExpConstant, RegExpNormalChar] b +# 66| 2: [RegExpConstant, RegExpNormalChar] a +# 66| 3: [RegExpConstant, RegExpNormalChar] r +# 66| getComponent: [StringTextComponent] .*bar +# 69| getStmt: [AssignAddExpr] ... += ... +# 69| getAnOperand/getLeftOperand: [LocalVariableAccess] x +# 69| getAnOperand/getRightOperand: [IntegerLiteral] 128 +# 70| getStmt: [AssignSubExpr] ... -= ... +# 70| getAnOperand/getLeftOperand: [LocalVariableAccess] y +# 70| getAnOperand/getRightOperand: [IntegerLiteral] 32 +# 71| getStmt: [AssignMulExpr] ... *= ... +# 71| getAnOperand/getLeftOperand: [LocalVariableAccess] a +# 71| getAnOperand/getRightOperand: [IntegerLiteral] 12 +# 72| getStmt: [AssignDivExpr] ... /= ... +# 72| getAnOperand/getLeftOperand: [LocalVariableAccess] b +# 72| getAnOperand/getRightOperand: [IntegerLiteral] 4 +# 73| getStmt: [AssignModuloExpr] ... %= ... +# 73| getAnOperand/getLeftOperand: [LocalVariableAccess] z +# 73| getAnOperand/getRightOperand: [IntegerLiteral] 2 +# 74| getStmt: [AssignExponentExpr] ... **= ... +# 74| getAnOperand/getLeftOperand: [LocalVariableAccess] foo +# 74| getAnOperand/getRightOperand: [LocalVariableAccess] bar +# 77| getStmt: [AssignLogicalAndExpr] ... &&= ... +# 77| getAnOperand/getLeftOperand: [LocalVariableAccess] x +# 77| getAnOperand/getRightOperand: [LocalVariableAccess] y +# 78| getStmt: [AssignLogicalOrExpr] ... ||= ... +# 78| getAnOperand/getLeftOperand: [LocalVariableAccess] a +# 78| getAnOperand/getRightOperand: [LocalVariableAccess] b +# 81| getStmt: [AssignLShiftExpr] ... <<= ... +# 81| getAnOperand/getLeftOperand: [LocalVariableAccess] x +# 81| getAnOperand/getRightOperand: [IntegerLiteral] 2 +# 82| getStmt: [AssignRShiftExpr] ... >>= ... +# 82| getAnOperand/getLeftOperand: [LocalVariableAccess] y +# 82| getAnOperand/getRightOperand: [IntegerLiteral] 3 +# 83| getStmt: [AssignBitwiseAndExpr] ... &= ... +# 83| getAnOperand/getLeftOperand: [LocalVariableAccess] foo +# 83| getAnOperand/getRightOperand: [LocalVariableAccess] mask +# 84| getStmt: [AssignBitwiseOrExpr] ... |= ... +# 84| getAnOperand/getLeftOperand: [LocalVariableAccess] bar +# 84| getAnOperand/getRightOperand: [IntegerLiteral] 0x01 +# 85| getStmt: [AssignBitwiseXorExpr] ... ^= ... +# 85| getAnOperand/getLeftOperand: [LocalVariableAccess] baz +# 85| getAnOperand/getRightOperand: [LocalVariableAccess] qux +# 87| getStmt: [ClassDeclaration] X +# 88| getStmt: [AssignExpr] ... = ... +# 88| getAnOperand/getLeftOperand: [InstanceVariableAccess] @x +# 88| getAnOperand/getRightOperand: [IntegerLiteral] 1 +# 89| getStmt: [AssignAddExpr] ... += ... +# 89| getAnOperand/getLeftOperand: [InstanceVariableAccess] @x +# 89| getAnOperand/getRightOperand: [IntegerLiteral] 2 +# 91| getStmt: [AssignExpr] ... = ... +# 91| getAnOperand/getLeftOperand: [ClassVariableAccess] @@y +# 91| getAnOperand/getRightOperand: [IntegerLiteral] 3 +# 92| getStmt: [AssignDivExpr] ... /= ... +# 92| getAnOperand/getLeftOperand: [ClassVariableAccess] @@y +# 92| getAnOperand/getRightOperand: [IntegerLiteral] 4 +# 95| getStmt: [AssignExpr] ... = ... +# 95| getAnOperand/getLeftOperand: [GlobalVariableAccess] $global_var +# 95| getAnOperand/getRightOperand: [IntegerLiteral] 5 +# 96| getStmt: [AssignMulExpr] ... *= ... +# 96| getAnOperand/getLeftOperand: [GlobalVariableAccess] $global_var +# 96| getAnOperand/getRightOperand: [IntegerLiteral] 6 +params/params.rb: +# 1| [Toplevel] params.rb +# 4| getStmt: [Method] identifier_method_params +# 4| getParameter: [SimpleParameter] foo +# 4| getDefiningAccess: [LocalVariableAccess] foo +# 4| getParameter: [SimpleParameter] bar +# 4| getDefiningAccess: [LocalVariableAccess] bar +# 4| getParameter: [SimpleParameter] baz +# 4| getDefiningAccess: [LocalVariableAccess] baz +# 8| getStmt: [AssignExpr] ... = ... +# 8| getAnOperand/getLeftOperand: [LocalVariableAccess] hash +# 8| getAnOperand/getRightOperand: [HashLiteral] {...} +# 9| getStmt: [MethodCall] call to each +# 9| getReceiver: [LocalVariableAccess] hash +# 9| getBlock: [DoBlock] do ... end +# 9| getParameter: [SimpleParameter] key +# 9| getDefiningAccess: [LocalVariableAccess] key +# 9| getParameter: [SimpleParameter] value +# 9| getDefiningAccess: [LocalVariableAccess] value +# 10| getStmt: [MethodCall] call to puts +# 10| getReceiver: [Self, SelfVariableAccess] self +# 10| getArgument: [StringLiteral] "#{...} -> #{...}" +# 10| getComponent: [StringInterpolationComponent] #{...} +# 10| getStmt: [LocalVariableAccess] key +# 10| getComponent: [StringTextComponent] -> +# 10| getComponent: [StringInterpolationComponent] #{...} +# 10| getStmt: [LocalVariableAccess] value +# 14| getStmt: [AssignExpr] ... = ... +# 14| getAnOperand/getLeftOperand: [LocalVariableAccess] sum +# 14| getAnOperand/getRightOperand: [Lambda] -> { ... } +# 14| getParameter: [SimpleParameter] foo +# 14| getDefiningAccess: [LocalVariableAccess] foo +# 14| getParameter: [SimpleParameter] bar +# 14| getDefiningAccess: [LocalVariableAccess] bar +# 14| getStmt: [AddExpr] ... + ... +# 14| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo +# 14| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] bar +# 17| getStmt: [Method] destructured_method_param +# 17| getParameter: [TuplePatternParameter] (..., ...) +# 17| getElement: [LocalVariableAccess] a +# 17| getElement: [LocalVariableAccess] b +# 17| getElement: [LocalVariableAccess] c +# 21| getStmt: [AssignExpr] ... = ... +# 21| getAnOperand/getLeftOperand: [LocalVariableAccess] array +# 21| getAnOperand/getRightOperand: [ArrayLiteral] [...] +# 22| getStmt: [MethodCall] call to each +# 22| getReceiver: [LocalVariableAccess] array +# 22| getBlock: [BraceBlock] { ... } +# 22| getParameter: [TuplePatternParameter] (..., ...) +# 22| getElement: [LocalVariableAccess] a +# 22| getElement: [LocalVariableAccess] b +# 22| getStmt: [MethodCall] call to puts +# 22| getReceiver: [Self, SelfVariableAccess] self +# 22| getArgument: [AddExpr] ... + ... +# 22| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a +# 22| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] b +# 25| getStmt: [AssignExpr] ... = ... +# 25| getAnOperand/getLeftOperand: [LocalVariableAccess] sum_four_values +# 25| getAnOperand/getRightOperand: [Lambda] -> { ... } +# 25| getParameter: [TuplePatternParameter] (..., ...) +# 25| getElement: [LocalVariableAccess] first +# 25| getElement: [LocalVariableAccess] second +# 25| getParameter: [TuplePatternParameter] (..., ...) +# 25| getElement: [LocalVariableAccess] third +# 25| getElement: [LocalVariableAccess] fourth +# 26| getStmt: [AddExpr] ... + ... +# 26| getAnOperand/getLeftOperand/getReceiver: [AddExpr] ... + ... +# 26| getAnOperand/getLeftOperand/getReceiver: [AddExpr] ... + ... +# 26| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] first +# 26| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] second +# 26| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] third +# 26| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] fourth +# 30| getStmt: [Method] method_with_splat +# 30| getParameter: [SimpleParameter] wibble +# 30| getDefiningAccess: [LocalVariableAccess] wibble +# 30| getParameter: [SplatParameter] *splat +# 30| getDefiningAccess: [LocalVariableAccess] splat +# 30| getParameter: [HashSplatParameter] **double_splat +# 30| getDefiningAccess: [LocalVariableAccess] double_splat +# 34| getStmt: [MethodCall] call to each +# 34| getReceiver: [LocalVariableAccess] array +# 34| getBlock: [DoBlock] do ... end +# 34| getParameter: [SimpleParameter] val +# 34| getDefiningAccess: [LocalVariableAccess] val +# 34| getParameter: [SplatParameter] *splat +# 34| getDefiningAccess: [LocalVariableAccess] splat +# 34| getParameter: [HashSplatParameter] **double_splat +# 34| getDefiningAccess: [LocalVariableAccess] double_splat +# 38| getStmt: [AssignExpr] ... = ... +# 38| getAnOperand/getLeftOperand: [LocalVariableAccess] lambda_with_splats +# 38| getAnOperand/getRightOperand: [Lambda] -> { ... } +# 38| getParameter: [SimpleParameter] x +# 38| getDefiningAccess: [LocalVariableAccess] x +# 38| getParameter: [SplatParameter] *blah +# 38| getDefiningAccess: [LocalVariableAccess] blah +# 38| getParameter: [HashSplatParameter] **wibble +# 38| getDefiningAccess: [LocalVariableAccess] wibble +# 41| getStmt: [Method] method_with_keyword_params +# 41| getParameter: [SimpleParameter] x +# 41| getDefiningAccess: [LocalVariableAccess] x +# 41| getParameter: [KeywordParameter] foo +# 41| getDefiningAccess: [LocalVariableAccess] foo +# 41| getParameter: [KeywordParameter] bar +# 41| getDefiningAccess: [LocalVariableAccess] bar +# 41| getDefaultValue: [IntegerLiteral] 7 +# 42| getStmt: [AddExpr] ... + ... +# 42| getAnOperand/getLeftOperand/getReceiver: [AddExpr] ... + ... +# 42| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 42| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] foo +# 42| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] bar +# 46| getStmt: [Method] use_block_with_keyword +# 46| getParameter: [BlockParameter] &block +# 46| getDefiningAccess: [LocalVariableAccess] block +# 47| getStmt: [MethodCall] call to puts +# 47| getReceiver: [Self, SelfVariableAccess] self +# 47| getArgument: [MethodCall] call to call +# 47| getReceiver: [LocalVariableAccess] block +# 47| getArgument: [Pair] Pair +# 47| getKey: [SymbolLiteral] :bar +# 47| getValue: [IntegerLiteral] 2 +# 47| getArgument: [Pair] Pair +# 47| getKey: [SymbolLiteral] :foo +# 47| getValue: [IntegerLiteral] 3 +# 49| getStmt: [MethodCall] call to use_block_with_keyword +# 49| getReceiver: [Self, SelfVariableAccess] self +# 49| getBlock: [DoBlock] do ... end +# 49| getParameter: [KeywordParameter] xx +# 49| getDefiningAccess: [LocalVariableAccess] xx +# 49| getParameter: [KeywordParameter] yy +# 49| getDefiningAccess: [LocalVariableAccess] yy +# 49| getDefaultValue: [IntegerLiteral] 100 +# 50| getStmt: [AddExpr] ... + ... +# 50| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] xx +# 50| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] yy +# 53| getStmt: [AssignExpr] ... = ... +# 53| getAnOperand/getLeftOperand: [LocalVariableAccess] lambda_with_keyword_params +# 53| getAnOperand/getRightOperand: [Lambda] -> { ... } +# 53| getParameter: [SimpleParameter] x +# 53| getDefiningAccess: [LocalVariableAccess] x +# 53| getParameter: [KeywordParameter] y +# 53| getDefiningAccess: [LocalVariableAccess] y +# 53| getParameter: [KeywordParameter] z +# 53| getDefiningAccess: [LocalVariableAccess] z +# 53| getDefaultValue: [IntegerLiteral] 3 +# 54| getStmt: [AddExpr] ... + ... +# 54| getAnOperand/getLeftOperand/getReceiver: [AddExpr] ... + ... +# 54| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 54| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] y +# 54| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] z +# 58| getStmt: [Method] method_with_optional_params +# 58| getParameter: [SimpleParameter] val1 +# 58| getDefiningAccess: [LocalVariableAccess] val1 +# 58| getParameter: [OptionalParameter] val2 +# 58| getDefiningAccess: [LocalVariableAccess] val2 +# 58| getDefaultValue: [IntegerLiteral] 0 +# 58| getParameter: [OptionalParameter] val3 +# 58| getDefiningAccess: [LocalVariableAccess] val3 +# 58| getDefaultValue: [IntegerLiteral] 100 +# 62| getStmt: [Method] use_block_with_optional +# 62| getParameter: [BlockParameter] &block +# 62| getDefiningAccess: [LocalVariableAccess] block +# 63| getStmt: [MethodCall] call to call +# 63| getReceiver: [LocalVariableAccess] block +# 63| getArgument: [StringLiteral] "Zeus" +# 63| getComponent: [StringTextComponent] Zeus +# 65| getStmt: [MethodCall] call to use_block_with_optional +# 65| getReceiver: [Self, SelfVariableAccess] self +# 65| getBlock: [DoBlock] do ... end +# 65| getParameter: [SimpleParameter] name +# 65| getDefiningAccess: [LocalVariableAccess] name +# 65| getParameter: [OptionalParameter] age +# 65| getDefiningAccess: [LocalVariableAccess] age +# 65| getDefaultValue: [IntegerLiteral] 99 +# 66| getStmt: [MethodCall] call to puts +# 66| getReceiver: [Self, SelfVariableAccess] self +# 66| getArgument: [StringLiteral] "#{...} is #{...} years old" +# 66| getComponent: [StringInterpolationComponent] #{...} +# 66| getStmt: [LocalVariableAccess] name +# 66| getComponent: [StringTextComponent] is +# 66| getComponent: [StringInterpolationComponent] #{...} +# 66| getStmt: [LocalVariableAccess] age +# 66| getComponent: [StringTextComponent] years old +# 70| getStmt: [AssignExpr] ... = ... +# 70| getAnOperand/getLeftOperand: [LocalVariableAccess] lambda_with_optional_params +# 70| getAnOperand/getRightOperand: [Lambda] -> { ... } +# 70| getParameter: [SimpleParameter] a +# 70| getDefiningAccess: [LocalVariableAccess] a +# 70| getParameter: [OptionalParameter] b +# 70| getDefiningAccess: [LocalVariableAccess] b +# 70| getDefaultValue: [IntegerLiteral] 1000 +# 70| getParameter: [OptionalParameter] c +# 70| getDefiningAccess: [LocalVariableAccess] c +# 70| getDefaultValue: [IntegerLiteral] 20 +# 70| getStmt: [AddExpr] ... + ... +# 70| getAnOperand/getLeftOperand/getReceiver: [AddExpr] ... + ... +# 70| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a +# 70| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] b +# 70| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] c +erb/template.html.erb: +# 19| [Toplevel] template.html.erb +# 19| getStmt: [StringLiteral] "hello world" +# 19| getComponent: [StringTextComponent] hello world +# 25| getStmt: [AssignExpr] ... = ... +# 25| getAnOperand/getLeftOperand: [LocalVariableAccess] xs +# 25| getAnOperand/getRightOperand: [StringLiteral] "" +# 27| getStmt: [ForExpr] for ... in ... +# 27| getPattern: [LocalVariableAccess] x +# 27| <in>: [???] In +# 27| getValue: [ArrayLiteral] [...] +# 27| getElement: [StringLiteral] "foo" +# 27| getComponent: [StringTextComponent] foo +# 27| getElement: [StringLiteral] "bar" +# 27| getComponent: [StringTextComponent] bar +# 27| getElement: [StringLiteral] "baz" +# 27| getComponent: [StringTextComponent] baz +# 27| getBody: [StmtSequence] do ... +# 28| getStmt: [AssignAddExpr] ... += ... +# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] xs +# 28| getAnOperand/getRightOperand: [LocalVariableAccess] x +# 29| getStmt: [LocalVariableAccess] xs +gems/test.gemspec: +# 1| [Toplevel] test.gemspec +# 1| getStmt: [MethodCall] call to new +# 1| getReceiver: [ConstantReadAccess] Specification +# 1| getScopeExpr: [ConstantReadAccess] Gem +# 1| getBlock: [DoBlock] do ... end +# 1| getParameter: [SimpleParameter] s +# 1| getDefiningAccess: [LocalVariableAccess] s +# 2| getStmt: [AssignExpr] ... = ... +# 2| getAnOperand/getLeftOperand: [MethodCall] call to name +# 2| getReceiver: [LocalVariableAccess] s +# 2| getAnOperand/getRightOperand: [StringLiteral] "test" +# 2| getComponent: [StringTextComponent] test +# 3| getStmt: [AssignExpr] ... = ... +# 3| getAnOperand/getLeftOperand: [MethodCall] call to version +# 3| getReceiver: [LocalVariableAccess] s +# 3| getAnOperand/getRightOperand: [StringLiteral] "0.0.0" +# 3| getComponent: [StringTextComponent] 0.0.0 +# 4| getStmt: [AssignExpr] ... = ... +# 4| getAnOperand/getLeftOperand: [MethodCall] call to summary +# 4| getReceiver: [LocalVariableAccess] s +# 4| getAnOperand/getRightOperand: [StringLiteral] "foo!" +# 4| getComponent: [StringTextComponent] foo! +# 5| getStmt: [AssignExpr] ... = ... +# 5| getAnOperand/getLeftOperand: [MethodCall] call to description +# 5| getReceiver: [LocalVariableAccess] s +# 5| getAnOperand/getRightOperand: [StringLiteral] "A test" +# 5| getComponent: [StringTextComponent] A test +# 6| getStmt: [AssignExpr] ... = ... +# 6| getAnOperand/getLeftOperand: [MethodCall] call to authors +# 6| getReceiver: [LocalVariableAccess] s +# 6| getAnOperand/getRightOperand: [ArrayLiteral] [...] +# 6| getElement: [StringLiteral] "Mona Lisa" +# 6| getComponent: [StringTextComponent] Mona Lisa +# 7| getStmt: [AssignExpr] ... = ... +# 7| getAnOperand/getLeftOperand: [MethodCall] call to email +# 7| getReceiver: [LocalVariableAccess] s +# 7| getAnOperand/getRightOperand: [StringLiteral] "mona@example.com" +# 7| getComponent: [StringTextComponent] mona@example.com +# 8| getStmt: [AssignExpr] ... = ... +# 8| getAnOperand/getLeftOperand: [MethodCall] call to files +# 8| getReceiver: [LocalVariableAccess] s +# 8| getAnOperand/getRightOperand: [ArrayLiteral] [...] +# 8| getElement: [StringLiteral] "lib/test.rb" +# 8| getComponent: [StringTextComponent] lib/test.rb +# 9| getStmt: [AssignExpr] ... = ... +# 9| getAnOperand/getLeftOperand: [MethodCall] call to homepage +# 9| getReceiver: [LocalVariableAccess] s +# 9| getAnOperand/getRightOperand: [StringLiteral] "https://github.com/github/cod..." +# 9| getComponent: [StringTextComponent] https://github.com/github/codeql-ruby +gems/lib/test.rb: +# 1| [Toplevel] test.rb +# 1| getStmt: [ClassDeclaration] Foo +# 2| getStmt: [SingletonMethod] greet +# 2| getObject: [Self, SelfVariableAccess] self +# 3| getStmt: [MethodCall] call to puts +# 3| getReceiver: [Self, SelfVariableAccess] self +# 3| getArgument: [StringLiteral] "Hello" +# 3| getComponent: [StringTextComponent] Hello +modules/toplevel.rb: +# 1| [Toplevel] toplevel.rb +# 1| getStmt: [MethodCall] call to puts +# 1| getReceiver: [Self, SelfVariableAccess] self +# 1| getArgument: [StringLiteral] "world" +# 1| getComponent: [StringTextComponent] world +# 3| getStmt: [EndBlock] END { ... } +# 3| getStmt: [MethodCall] call to puts +# 3| getReceiver: [Self, SelfVariableAccess] self +# 3| getArgument: [StringLiteral] "!!!" +# 3| getComponent: [StringTextComponent] !!! +# 5| getBeginBlock: [BeginBlock] BEGIN { ... } +# 5| getStmt: [MethodCall] call to puts +# 5| getReceiver: [Self, SelfVariableAccess] self +# 5| getArgument: [StringLiteral] "hello" +# 5| getComponent: [StringTextComponent] hello diff --git a/ruby/ql/test/library-tests/ast/Ast.ql b/ruby/ql/test/library-tests/ast/Ast.ql new file mode 100644 index 00000000000..2e80254f0b0 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/Ast.ql @@ -0,0 +1,5 @@ +/** + * @kind graph + */ + +import codeql.ruby.printAst diff --git a/ruby/ql/test/library-tests/ast/AstDesugar.expected b/ruby/ql/test/library-tests/ast/AstDesugar.expected new file mode 100644 index 00000000000..dbd8aaad91c --- /dev/null +++ b/ruby/ql/test/library-tests/ast/AstDesugar.expected @@ -0,0 +1,631 @@ +calls/calls.rb: +# 58| [ArrayLiteral] [...] +# 58| getDesugared: [MethodCall] call to [] +# 58| getReceiver: [ConstantReadAccess] Array +# 58| getArgument: [MethodCall] call to foo +# 58| getReceiver: [Self, SelfVariableAccess] self +# 59| [ArrayLiteral] [...] +# 59| getDesugared: [MethodCall] call to [] +# 59| getReceiver: [ConstantReadAccess] Array +# 59| getArgument: [MethodCall] call to foo +# 59| getReceiver: [ConstantReadAccess] X +# 66| [AssignAddExpr] ... += ... +# 66| getDesugared: [AssignExpr] ... = ... +# 66| getAnOperand/getLeftOperand: [LocalVariableAccess] var1 +# 66| getAnOperand/getRightOperand: [AddExpr] ... + ... +# 66| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] var1 +# 66| getAnOperand/getArgument/getRightOperand: [MethodCall] call to bar +# 66| getReceiver: [Self, SelfVariableAccess] self +# 67| [AssignAddExpr] ... += ... +# 67| getDesugared: [AssignExpr] ... = ... +# 67| getAnOperand/getLeftOperand: [LocalVariableAccess] var1 +# 67| getAnOperand/getRightOperand: [AddExpr] ... + ... +# 67| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] var1 +# 67| getAnOperand/getArgument/getRightOperand: [MethodCall] call to bar +# 67| getReceiver: [ConstantReadAccess] X +# 314| [AssignExpr] ... = ... +# 314| getDesugared: [StmtSequence] ... +# 314| getStmt: [SetterMethodCall] call to foo= +# 314| getReceiver: [Self, SelfVariableAccess] self +# 314| getArgument: [AssignExpr] ... = ... +# 314| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 +# 314| getAnOperand/getRightOperand: [IntegerLiteral] 10 +# 314| getStmt: [LocalVariableAccess] __synth__0 +# 315| [AssignExpr] ... = ... +# 315| getDesugared: [StmtSequence] ... +# 315| getStmt: [SetterMethodCall] call to []= +# 315| getReceiver: [MethodCall] call to foo +# 315| getReceiver: [Self, SelfVariableAccess] self +# 315| getArgument: [AssignExpr] ... = ... +# 315| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 +# 315| getAnOperand/getRightOperand: [IntegerLiteral] 10 +# 315| getArgument: [IntegerLiteral] 0 +# 315| getStmt: [LocalVariableAccess] __synth__0 +# 316| [AssignExpr] ... = ... +# 316| getDesugared: [StmtSequence] ... +# 316| getStmt: [AssignExpr] ... = ... +# 316| getAnOperand/getLeftOperand: [MethodCall] call to foo +# 316| getDesugared: [StmtSequence] ... +# 316| getStmt: [SetterMethodCall] call to foo= +# 316| getReceiver: [Self, SelfVariableAccess] self +# 316| getArgument: [AssignExpr] ... = ... +# 316| getAnOperand/getRightOperand: [MethodCall] call to [] +# 316| getArgument: [IntegerLiteral] 0 +# 316| getReceiver: [LocalVariableAccess] __synth__0 +# 316| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 +# 316| getStmt: [LocalVariableAccess] __synth__0__1 +# 316| getStmt: [AssignExpr] ... = ... +# 316| getAnOperand/getLeftOperand: [MethodCall] call to bar +# 316| getDesugared: [StmtSequence] ... +# 316| getStmt: [SetterMethodCall] call to bar= +# 316| getReceiver: [Self, SelfVariableAccess] self +# 316| getArgument: [AssignExpr] ... = ... +# 316| getAnOperand/getRightOperand: [MethodCall] call to [] +# 316| getArgument: [RangeLiteral] _ .. _ +# 316| getBegin: [IntegerLiteral] 1 +# 316| getEnd: [IntegerLiteral] -2 +# 316| getReceiver: [LocalVariableAccess] __synth__0 +# 316| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 +# 316| getStmt: [LocalVariableAccess] __synth__0__1 +# 316| getStmt: [AssignExpr] ... = ... +# 316| getAnOperand/getLeftOperand: [ElementReference] ...[...] +# 316| getDesugared: [StmtSequence] ... +# 316| getStmt: [SetterMethodCall] call to []= +# 316| getReceiver: [MethodCall] call to foo +# 316| getReceiver: [Self, SelfVariableAccess] self +# 316| getArgument: [AssignExpr] ... = ... +# 316| getAnOperand/getRightOperand: [MethodCall] call to [] +# 316| getArgument: [IntegerLiteral] -1 +# 316| getReceiver: [LocalVariableAccess] __synth__0 +# 316| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 +# 316| getArgument: [IntegerLiteral] 4 +# 316| getStmt: [LocalVariableAccess] __synth__0__1 +# 316| getStmt: [AssignExpr] ... = ... +# 316| getAnOperand/getRightOperand: [SplatExpr] * ... +# 316| getAnOperand/getOperand/getReceiver: [ArrayLiteral] [...] +# 316| getDesugared: [MethodCall] call to [] +# 316| getReceiver: [ConstantReadAccess] Array +# 316| getArgument: [IntegerLiteral] 1 +# 316| getArgument: [IntegerLiteral] 2 +# 316| getArgument: [IntegerLiteral] 3 +# 316| getArgument: [IntegerLiteral] 4 +# 316| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 +# 317| [AssignExpr] ... = ... +# 317| getDesugared: [StmtSequence] ... +# 317| getStmt: [AssignExpr] ... = ... +# 317| getAnOperand/getLeftOperand: [LocalVariableAccess] a +# 317| getAnOperand/getRightOperand: [MethodCall] call to [] +# 317| getArgument: [IntegerLiteral] 0 +# 317| getReceiver: [LocalVariableAccess] __synth__0 +# 317| getStmt: [AssignExpr] ... = ... +# 317| getAnOperand/getLeftOperand: [ElementReference] ...[...] +# 317| getDesugared: [StmtSequence] ... +# 317| getStmt: [SetterMethodCall] call to []= +# 317| getReceiver: [MethodCall] call to foo +# 317| getReceiver: [Self, SelfVariableAccess] self +# 317| getArgument: [AssignExpr] ... = ... +# 317| getAnOperand/getRightOperand: [MethodCall] call to [] +# 317| getArgument: [RangeLiteral] _ .. _ +# 317| getBegin: [IntegerLiteral] 1 +# 317| getEnd: [IntegerLiteral] -1 +# 317| getReceiver: [LocalVariableAccess] __synth__0 +# 317| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 +# 317| getArgument: [IntegerLiteral] 5 +# 317| getStmt: [LocalVariableAccess] __synth__0__1 +# 317| getStmt: [AssignExpr] ... = ... +# 317| getAnOperand/getRightOperand: [SplatExpr] * ... +# 317| getAnOperand/getOperand/getReceiver: [ArrayLiteral] [...] +# 317| getDesugared: [MethodCall] call to [] +# 317| getReceiver: [ConstantReadAccess] Array +# 317| getArgument: [IntegerLiteral] 1 +# 317| getArgument: [IntegerLiteral] 2 +# 317| getArgument: [IntegerLiteral] 3 +# 317| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 +# 318| [AssignAddExpr] ... += ... +# 318| getDesugared: [StmtSequence] ... +# 318| getStmt: [AssignExpr] ... = ... +# 318| getAnOperand/getRightOperand: [Self, SelfVariableAccess] self +# 318| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 +# 318| getStmt: [SetterMethodCall] call to count= +# 318| getReceiver: [LocalVariableAccess] __synth__0 +# 318| getArgument: [LocalVariableAccess] __synth__1 +# 318| getStmt: [AssignExpr] ... = ... +# 318| getAnOperand/getRightOperand: [AddExpr] ... + ... +# 318| getAnOperand/getLeftOperand/getReceiver: [MethodCall] call to count +# 318| getReceiver: [LocalVariableAccess] __synth__0 +# 318| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 318| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__1 +# 318| getStmt: [LocalVariableAccess] __synth__1 +# 319| [AssignAddExpr] ... += ... +# 319| getDesugared: [StmtSequence] ... +# 319| getStmt: [AssignExpr] ... = ... +# 319| getAnOperand/getRightOperand: [MethodCall] call to foo +# 319| getReceiver: [Self, SelfVariableAccess] self +# 319| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 +# 319| getStmt: [SetterMethodCall] call to []= +# 319| getReceiver: [LocalVariableAccess] __synth__0 +# 319| getArgument: [LocalVariableAccess] __synth__1 +# 319| getArgument: [LocalVariableAccess] __synth__2 +# 319| getStmt: [AssignExpr] ... = ... +# 319| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 319| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__1 +# 319| getStmt: [AssignExpr] ... = ... +# 319| getAnOperand/getRightOperand: [AddExpr] ... + ... +# 319| getAnOperand/getLeftOperand/getReceiver: [MethodCall] call to [] +# 319| getReceiver: [LocalVariableAccess] __synth__0 +# 319| getArgument: [LocalVariableAccess] __synth__1 +# 319| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 319| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2 +# 319| getStmt: [LocalVariableAccess] __synth__2 +# 320| [AssignMulExpr] ... *= ... +# 320| getDesugared: [StmtSequence] ... +# 320| getStmt: [AssignExpr] ... = ... +# 320| getAnOperand/getRightOperand: [MethodCall] call to bar +# 320| getReceiver: [MethodCall] call to foo +# 320| getReceiver: [Self, SelfVariableAccess] self +# 320| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 +# 320| getStmt: [SetterMethodCall] call to []= +# 320| getReceiver: [LocalVariableAccess] __synth__0 +# 320| getArgument: [LocalVariableAccess] __synth__1 +# 320| getArgument: [LocalVariableAccess] __synth__2 +# 320| getArgument: [LocalVariableAccess] __synth__3 +# 320| getArgument: [LocalVariableAccess] __synth__4 +# 320| getStmt: [AssignExpr] ... = ... +# 320| getAnOperand/getRightOperand: [IntegerLiteral] 0 +# 320| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__1 +# 320| getStmt: [AssignExpr] ... = ... +# 320| getAnOperand/getRightOperand: [MethodCall] call to baz +# 320| getReceiver: [MethodCall] call to foo +# 320| getReceiver: [Self, SelfVariableAccess] self +# 320| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2 +# 320| getStmt: [AssignExpr] ... = ... +# 320| getAnOperand/getRightOperand: [AddExpr] ... + ... +# 320| getAnOperand/getLeftOperand/getReceiver: [MethodCall] call to boo +# 320| getReceiver: [MethodCall] call to foo +# 320| getReceiver: [Self, SelfVariableAccess] self +# 320| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 320| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__3 +# 320| getStmt: [AssignExpr] ... = ... +# 320| getAnOperand/getRightOperand: [MulExpr] ... * ... +# 320| getAnOperand/getLeftOperand/getReceiver: [MethodCall] call to [] +# 320| getReceiver: [LocalVariableAccess] __synth__0 +# 320| getArgument: [LocalVariableAccess] __synth__1 +# 320| getArgument: [LocalVariableAccess] __synth__2 +# 320| getArgument: [LocalVariableAccess] __synth__3 +# 320| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 2 +# 320| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__4 +# 320| getStmt: [LocalVariableAccess] __synth__4 +constants/constants.rb: +# 20| [ArrayLiteral] [...] +# 20| getDesugared: [MethodCall] call to [] +# 20| getReceiver: [ConstantReadAccess] Array +# 20| getArgument: [StringLiteral] "Vera" +# 20| getComponent: [StringTextComponent] Vera +# 20| getArgument: [StringLiteral] "Chuck" +# 20| getComponent: [StringTextComponent] Chuck +# 20| getArgument: [StringLiteral] "Dave" +# 20| getComponent: [StringTextComponent] Dave +literals/literals.rb: +# 92| [ArrayLiteral] [...] +# 92| getDesugared: [MethodCall] call to [] +# 92| getReceiver: [ConstantReadAccess] Array +# 93| [ArrayLiteral] [...] +# 93| getDesugared: [MethodCall] call to [] +# 93| getReceiver: [ConstantReadAccess] Array +# 93| getArgument: [IntegerLiteral] 1 +# 93| getArgument: [IntegerLiteral] 2 +# 93| getArgument: [IntegerLiteral] 3 +# 94| [ArrayLiteral] [...] +# 94| getDesugared: [MethodCall] call to [] +# 94| getReceiver: [ConstantReadAccess] Array +# 94| getArgument: [IntegerLiteral] 4 +# 94| getArgument: [IntegerLiteral] 5 +# 94| getArgument: [DivExpr] ... / ... +# 94| getAnOperand/getLeftOperand/getReceiver: [IntegerLiteral] 12 +# 94| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 2 +# 95| [ArrayLiteral] [...] +# 95| getDesugared: [MethodCall] call to [] +# 95| getReceiver: [ConstantReadAccess] Array +# 95| getArgument: [IntegerLiteral] 7 +# 95| getArgument: [ArrayLiteral] [...] +# 95| getDesugared: [MethodCall] call to [] +# 95| getReceiver: [ConstantReadAccess] Array +# 95| getArgument: [IntegerLiteral] 8 +# 95| getArgument: [IntegerLiteral] 9 +# 98| [ArrayLiteral] %w(...) +# 98| getDesugared: [MethodCall] call to [] +# 98| getReceiver: [ConstantReadAccess] Array +# 99| [ArrayLiteral] %w(...) +# 99| getDesugared: [MethodCall] call to [] +# 99| getReceiver: [ConstantReadAccess] Array +# 99| getArgument: [StringLiteral] "foo" +# 99| getComponent: [StringTextComponent] foo +# 99| getArgument: [StringLiteral] "bar" +# 99| getComponent: [StringTextComponent] bar +# 99| getArgument: [StringLiteral] "baz" +# 99| getComponent: [StringTextComponent] baz +# 100| [ArrayLiteral] %w(...) +# 100| getDesugared: [MethodCall] call to [] +# 100| getReceiver: [ConstantReadAccess] Array +# 100| getArgument: [StringLiteral] "foo" +# 100| getComponent: [StringTextComponent] foo +# 100| getArgument: [StringLiteral] "bar" +# 100| getComponent: [StringTextComponent] bar +# 100| getArgument: [StringLiteral] "baz" +# 100| getComponent: [StringTextComponent] baz +# 101| [ArrayLiteral] %w(...) +# 101| getDesugared: [MethodCall] call to [] +# 101| getReceiver: [ConstantReadAccess] Array +# 101| getArgument: [StringLiteral] "foo" +# 101| getComponent: [StringTextComponent] foo +# 101| getArgument: [StringLiteral] "bar#{...}" +# 101| getComponent: [StringTextComponent] bar +# 101| getComponent: [StringInterpolationComponent] #{...} +# 101| getStmt: [AddExpr] ... + ... +# 101| getAnOperand/getLeftOperand/getReceiver: [IntegerLiteral] 1 +# 101| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 101| getArgument: [StringLiteral] "baz" +# 101| getComponent: [StringTextComponent] baz +# 102| [ArrayLiteral] %w(...) +# 102| getDesugared: [MethodCall] call to [] +# 102| getReceiver: [ConstantReadAccess] Array +# 102| getArgument: [StringLiteral] "foo" +# 102| getComponent: [StringTextComponent] foo +# 102| getArgument: [StringLiteral] "bar#{1+1}" +# 102| getComponent: [StringTextComponent] bar#{1+1} +# 102| getArgument: [StringLiteral] "baz" +# 102| getComponent: [StringTextComponent] baz +# 105| [ArrayLiteral] %i(...) +# 105| getDesugared: [MethodCall] call to [] +# 105| getReceiver: [ConstantReadAccess] Array +# 106| [ArrayLiteral] %i(...) +# 106| getDesugared: [MethodCall] call to [] +# 106| getReceiver: [ConstantReadAccess] Array +# 106| getArgument: [SymbolLiteral] :"foo" +# 106| getComponent: [StringTextComponent] foo +# 106| getArgument: [SymbolLiteral] :"bar" +# 106| getComponent: [StringTextComponent] bar +# 106| getArgument: [SymbolLiteral] :"baz" +# 106| getComponent: [StringTextComponent] baz +# 107| [ArrayLiteral] %i(...) +# 107| getDesugared: [MethodCall] call to [] +# 107| getReceiver: [ConstantReadAccess] Array +# 107| getArgument: [SymbolLiteral] :"foo" +# 107| getComponent: [StringTextComponent] foo +# 107| getArgument: [SymbolLiteral] :"bar" +# 107| getComponent: [StringTextComponent] bar +# 107| getArgument: [SymbolLiteral] :"baz" +# 107| getComponent: [StringTextComponent] baz +# 108| [ArrayLiteral] %i(...) +# 108| getDesugared: [MethodCall] call to [] +# 108| getReceiver: [ConstantReadAccess] Array +# 108| getArgument: [SymbolLiteral] :"foo" +# 108| getComponent: [StringTextComponent] foo +# 108| getArgument: [SymbolLiteral] :"bar#{...}" +# 108| getComponent: [StringTextComponent] bar +# 108| getComponent: [StringInterpolationComponent] #{...} +# 108| getStmt: [AddExpr] ... + ... +# 108| getAnOperand/getLeftOperand/getReceiver: [IntegerLiteral] 2 +# 108| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 4 +# 108| getArgument: [SymbolLiteral] :"baz" +# 108| getComponent: [StringTextComponent] baz +# 109| [ArrayLiteral] %i(...) +# 109| getDesugared: [MethodCall] call to [] +# 109| getReceiver: [ConstantReadAccess] Array +# 109| getArgument: [SymbolLiteral] :"foo" +# 109| getComponent: [StringTextComponent] foo +# 109| getArgument: [SymbolLiteral] :"bar#{" +# 109| getComponent: [StringTextComponent] bar#{ +# 109| getArgument: [SymbolLiteral] :"2" +# 109| getComponent: [StringTextComponent] 2 +# 109| getArgument: [SymbolLiteral] :"+" +# 109| getComponent: [StringTextComponent] + +# 109| getArgument: [SymbolLiteral] :"4" +# 109| getComponent: [StringTextComponent] 4 +# 109| getArgument: [SymbolLiteral] :"}" +# 109| getComponent: [StringTextComponent] } +# 109| getArgument: [SymbolLiteral] :"baz" +# 109| getComponent: [StringTextComponent] baz +control/loops.rb: +# 10| [AssignAddExpr] ... += ... +# 10| getDesugared: [AssignExpr] ... = ... +# 10| getAnOperand/getLeftOperand: [LocalVariableAccess] sum +# 10| getAnOperand/getRightOperand: [AddExpr] ... + ... +# 10| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum +# 10| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n +# 17| [AssignAddExpr] ... += ... +# 17| getDesugared: [AssignExpr] ... = ... +# 17| getAnOperand/getLeftOperand: [LocalVariableAccess] sum +# 17| getAnOperand/getRightOperand: [AddExpr] ... + ... +# 17| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum +# 17| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n +# 18| [AssignSubExpr] ... -= ... +# 18| getDesugared: [AssignExpr] ... = ... +# 18| getAnOperand/getLeftOperand: [LocalVariableAccess] foo +# 18| getAnOperand/getRightOperand: [SubExpr] ... - ... +# 18| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo +# 18| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n +# 23| [AssignAddExpr] ... += ... +# 23| getDesugared: [AssignExpr] ... = ... +# 23| getAnOperand/getLeftOperand: [LocalVariableAccess] sum +# 23| getAnOperand/getRightOperand: [AddExpr] ... + ... +# 23| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum +# 23| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value +# 24| [AssignMulExpr] ... *= ... +# 24| getDesugared: [AssignExpr] ... = ... +# 24| getAnOperand/getLeftOperand: [LocalVariableAccess] foo +# 24| getAnOperand/getRightOperand: [MulExpr] ... * ... +# 24| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo +# 24| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value +# 29| [AssignAddExpr] ... += ... +# 29| getDesugared: [AssignExpr] ... = ... +# 29| getAnOperand/getLeftOperand: [LocalVariableAccess] sum +# 29| getAnOperand/getRightOperand: [AddExpr] ... + ... +# 29| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum +# 29| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value +# 30| [AssignDivExpr] ... /= ... +# 30| getDesugared: [AssignExpr] ... = ... +# 30| getAnOperand/getLeftOperand: [LocalVariableAccess] foo +# 30| getAnOperand/getRightOperand: [DivExpr] ... / ... +# 30| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo +# 30| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value +# 36| [AssignAddExpr] ... += ... +# 36| getDesugared: [AssignExpr] ... = ... +# 36| getAnOperand/getLeftOperand: [LocalVariableAccess] x +# 36| getAnOperand/getRightOperand: [AddExpr] ... + ... +# 36| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 36| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 37| [AssignAddExpr] ... += ... +# 37| getDesugared: [AssignExpr] ... = ... +# 37| getAnOperand/getLeftOperand: [LocalVariableAccess] z +# 37| getAnOperand/getRightOperand: [AddExpr] ... + ... +# 37| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] z +# 37| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 43| [AssignAddExpr] ... += ... +# 43| getDesugared: [AssignExpr] ... = ... +# 43| getAnOperand/getLeftOperand: [LocalVariableAccess] x +# 43| getAnOperand/getRightOperand: [AddExpr] ... + ... +# 43| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 43| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 44| [AssignAddExpr] ... += ... +# 44| getDesugared: [AssignExpr] ... = ... +# 44| getAnOperand/getLeftOperand: [LocalVariableAccess] z +# 44| getAnOperand/getRightOperand: [AddExpr] ... + ... +# 44| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] z +# 44| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 2 +# 48| [AssignAddExpr] ... += ... +# 48| getDesugared: [AssignExpr] ... = ... +# 48| getAnOperand/getLeftOperand: [LocalVariableAccess] x +# 48| getAnOperand/getRightOperand: [AddExpr] ... + ... +# 48| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 48| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 52| [AssignAddExpr] ... += ... +# 52| getDesugared: [AssignExpr] ... = ... +# 52| getAnOperand/getLeftOperand: [LocalVariableAccess] x +# 52| getAnOperand/getRightOperand: [AddExpr] ... + ... +# 52| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 52| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 53| [AssignSubExpr] ... -= ... +# 53| getDesugared: [AssignExpr] ... = ... +# 53| getAnOperand/getLeftOperand: [LocalVariableAccess] z +# 53| getAnOperand/getRightOperand: [SubExpr] ... - ... +# 53| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] z +# 53| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 58| [AssignAddExpr] ... += ... +# 58| getDesugared: [AssignExpr] ... = ... +# 58| getAnOperand/getLeftOperand: [LocalVariableAccess] x +# 58| getAnOperand/getRightOperand: [AddExpr] ... + ... +# 58| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 58| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +# 59| [AssignSubExpr] ... -= ... +# 59| getDesugared: [AssignExpr] ... = ... +# 59| getAnOperand/getLeftOperand: [LocalVariableAccess] z +# 59| getAnOperand/getRightOperand: [SubExpr] ... - ... +# 59| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] z +# 59| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 4 +# 63| [AssignSubExpr] ... -= ... +# 63| getDesugared: [AssignExpr] ... = ... +# 63| getAnOperand/getLeftOperand: [LocalVariableAccess] x +# 63| getAnOperand/getRightOperand: [SubExpr] ... - ... +# 63| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 63| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 +operations/operations.rb: +# 29| [ArrayLiteral] [...] +# 29| getDesugared: [MethodCall] call to [] +# 29| getReceiver: [ConstantReadAccess] Array +# 29| getArgument: [IntegerLiteral] 2 +# 69| [AssignAddExpr] ... += ... +# 69| getDesugared: [AssignExpr] ... = ... +# 69| getAnOperand/getLeftOperand: [LocalVariableAccess] x +# 69| getAnOperand/getRightOperand: [AddExpr] ... + ... +# 69| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 69| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 128 +# 70| [AssignSubExpr] ... -= ... +# 70| getDesugared: [AssignExpr] ... = ... +# 70| getAnOperand/getLeftOperand: [LocalVariableAccess] y +# 70| getAnOperand/getRightOperand: [SubExpr] ... - ... +# 70| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] y +# 70| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 32 +# 71| [AssignMulExpr] ... *= ... +# 71| getDesugared: [AssignExpr] ... = ... +# 71| getAnOperand/getLeftOperand: [LocalVariableAccess] a +# 71| getAnOperand/getRightOperand: [MulExpr] ... * ... +# 71| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a +# 71| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 12 +# 72| [AssignDivExpr] ... /= ... +# 72| getDesugared: [AssignExpr] ... = ... +# 72| getAnOperand/getLeftOperand: [LocalVariableAccess] b +# 72| getAnOperand/getRightOperand: [DivExpr] ... / ... +# 72| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] b +# 72| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 4 +# 73| [AssignModuloExpr] ... %= ... +# 73| getDesugared: [AssignExpr] ... = ... +# 73| getAnOperand/getLeftOperand: [LocalVariableAccess] z +# 73| getAnOperand/getRightOperand: [ModuloExpr] ... % ... +# 73| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] z +# 73| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 2 +# 74| [AssignExponentExpr] ... **= ... +# 74| getDesugared: [AssignExpr] ... = ... +# 74| getAnOperand/getLeftOperand: [LocalVariableAccess] foo +# 74| getAnOperand/getRightOperand: [ExponentExpr] ... ** ... +# 74| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo +# 74| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] bar +# 77| [AssignLogicalAndExpr] ... &&= ... +# 77| getDesugared: [AssignExpr] ... = ... +# 77| getAnOperand/getLeftOperand: [LocalVariableAccess] x +# 77| getAnOperand/getRightOperand: [LogicalAndExpr] ... && ... +# 77| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 77| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] y +# 78| [AssignLogicalOrExpr] ... ||= ... +# 78| getDesugared: [AssignExpr] ... = ... +# 78| getAnOperand/getLeftOperand: [LocalVariableAccess] a +# 78| getAnOperand/getRightOperand: [LogicalOrExpr] ... || ... +# 78| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a +# 78| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] b +# 81| [AssignLShiftExpr] ... <<= ... +# 81| getDesugared: [AssignExpr] ... = ... +# 81| getAnOperand/getLeftOperand: [LocalVariableAccess] x +# 81| getAnOperand/getRightOperand: [LShiftExpr] ... << ... +# 81| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] x +# 81| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 2 +# 82| [AssignRShiftExpr] ... >>= ... +# 82| getDesugared: [AssignExpr] ... = ... +# 82| getAnOperand/getLeftOperand: [LocalVariableAccess] y +# 82| getAnOperand/getRightOperand: [RShiftExpr] ... >> ... +# 82| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] y +# 82| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 3 +# 83| [AssignBitwiseAndExpr] ... &= ... +# 83| getDesugared: [AssignExpr] ... = ... +# 83| getAnOperand/getLeftOperand: [LocalVariableAccess] foo +# 83| getAnOperand/getRightOperand: [BitwiseAndExpr] ... & ... +# 83| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo +# 83| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] mask +# 84| [AssignBitwiseOrExpr] ... |= ... +# 84| getDesugared: [AssignExpr] ... = ... +# 84| getAnOperand/getLeftOperand: [LocalVariableAccess] bar +# 84| getAnOperand/getRightOperand: [BitwiseOrExpr] ... | ... +# 84| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] bar +# 84| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 0x01 +# 85| [AssignBitwiseXorExpr] ... ^= ... +# 85| getDesugared: [AssignExpr] ... = ... +# 85| getAnOperand/getLeftOperand: [LocalVariableAccess] baz +# 85| getAnOperand/getRightOperand: [BitwiseXorExpr] ... ^ ... +# 85| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] baz +# 85| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] qux +# 89| [AssignAddExpr] ... += ... +# 89| getDesugared: [AssignExpr] ... = ... +# 89| getAnOperand/getLeftOperand: [InstanceVariableAccess] @x +# 89| getAnOperand/getRightOperand: [AddExpr] ... + ... +# 89| getAnOperand/getLeftOperand/getReceiver: [InstanceVariableAccess] @x +# 89| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 2 +# 92| [AssignDivExpr] ... /= ... +# 92| getDesugared: [AssignExpr] ... = ... +# 92| getAnOperand/getLeftOperand: [ClassVariableAccess] @@y +# 92| getAnOperand/getRightOperand: [DivExpr] ... / ... +# 92| getAnOperand/getLeftOperand/getReceiver: [ClassVariableAccess] @@y +# 92| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 4 +# 96| [AssignMulExpr] ... *= ... +# 96| getDesugared: [AssignExpr] ... = ... +# 96| getAnOperand/getLeftOperand: [GlobalVariableAccess] $global_var +# 96| getAnOperand/getRightOperand: [MulExpr] ... * ... +# 96| getAnOperand/getLeftOperand/getReceiver: [GlobalVariableAccess] $global_var +# 96| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 6 +params/params.rb: +# 21| [ArrayLiteral] [...] +# 21| getDesugared: [MethodCall] call to [] +# 21| getReceiver: [ConstantReadAccess] Array +erb/template.html.erb: +# 27| [ArrayLiteral] [...] +# 27| getDesugared: [MethodCall] call to [] +# 27| getReceiver: [ConstantReadAccess] Array +# 27| getArgument: [StringLiteral] "foo" +# 27| getComponent: [StringTextComponent] foo +# 27| getArgument: [StringLiteral] "bar" +# 27| getComponent: [StringTextComponent] bar +# 27| getArgument: [StringLiteral] "baz" +# 27| getComponent: [StringTextComponent] baz +# 28| [AssignAddExpr] ... += ... +# 28| getDesugared: [AssignExpr] ... = ... +# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] xs +# 28| getAnOperand/getRightOperand: [AddExpr] ... + ... +# 28| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] xs +# 28| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] x +gems/test.gemspec: +# 2| [AssignExpr] ... = ... +# 2| getDesugared: [StmtSequence] ... +# 2| getStmt: [SetterMethodCall] call to name= +# 2| getReceiver: [LocalVariableAccess] s +# 2| getArgument: [AssignExpr] ... = ... +# 2| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 +# 2| getAnOperand/getRightOperand: [StringLiteral] "test" +# 2| getComponent: [StringTextComponent] test +# 2| getStmt: [LocalVariableAccess] __synth__0 +# 3| [AssignExpr] ... = ... +# 3| getDesugared: [StmtSequence] ... +# 3| getStmt: [SetterMethodCall] call to version= +# 3| getReceiver: [LocalVariableAccess] s +# 3| getArgument: [AssignExpr] ... = ... +# 3| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 +# 3| getAnOperand/getRightOperand: [StringLiteral] "0.0.0" +# 3| getComponent: [StringTextComponent] 0.0.0 +# 3| getStmt: [LocalVariableAccess] __synth__0 +# 4| [AssignExpr] ... = ... +# 4| getDesugared: [StmtSequence] ... +# 4| getStmt: [SetterMethodCall] call to summary= +# 4| getReceiver: [LocalVariableAccess] s +# 4| getArgument: [AssignExpr] ... = ... +# 4| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 +# 4| getAnOperand/getRightOperand: [StringLiteral] "foo!" +# 4| getComponent: [StringTextComponent] foo! +# 4| getStmt: [LocalVariableAccess] __synth__0 +# 5| [AssignExpr] ... = ... +# 5| getDesugared: [StmtSequence] ... +# 5| getStmt: [SetterMethodCall] call to description= +# 5| getReceiver: [LocalVariableAccess] s +# 5| getArgument: [AssignExpr] ... = ... +# 5| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 +# 5| getAnOperand/getRightOperand: [StringLiteral] "A test" +# 5| getComponent: [StringTextComponent] A test +# 5| getStmt: [LocalVariableAccess] __synth__0 +# 6| [AssignExpr] ... = ... +# 6| getDesugared: [StmtSequence] ... +# 6| getStmt: [SetterMethodCall] call to authors= +# 6| getReceiver: [LocalVariableAccess] s +# 6| getArgument: [AssignExpr] ... = ... +# 6| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 +# 6| getAnOperand/getRightOperand: [ArrayLiteral] [...] +# 6| getDesugared: [MethodCall] call to [] +# 6| getReceiver: [ConstantReadAccess] Array +# 6| getArgument: [StringLiteral] "Mona Lisa" +# 6| getComponent: [StringTextComponent] Mona Lisa +# 6| getStmt: [LocalVariableAccess] __synth__0 +# 7| [AssignExpr] ... = ... +# 7| getDesugared: [StmtSequence] ... +# 7| getStmt: [SetterMethodCall] call to email= +# 7| getReceiver: [LocalVariableAccess] s +# 7| getArgument: [AssignExpr] ... = ... +# 7| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 +# 7| getAnOperand/getRightOperand: [StringLiteral] "mona@example.com" +# 7| getComponent: [StringTextComponent] mona@example.com +# 7| getStmt: [LocalVariableAccess] __synth__0 +# 8| [AssignExpr] ... = ... +# 8| getDesugared: [StmtSequence] ... +# 8| getStmt: [SetterMethodCall] call to files= +# 8| getReceiver: [LocalVariableAccess] s +# 8| getArgument: [AssignExpr] ... = ... +# 8| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 +# 8| getAnOperand/getRightOperand: [ArrayLiteral] [...] +# 8| getDesugared: [MethodCall] call to [] +# 8| getReceiver: [ConstantReadAccess] Array +# 8| getArgument: [StringLiteral] "lib/test.rb" +# 8| getComponent: [StringTextComponent] lib/test.rb +# 8| getStmt: [LocalVariableAccess] __synth__0 +# 9| [AssignExpr] ... = ... +# 9| getDesugared: [StmtSequence] ... +# 9| getStmt: [SetterMethodCall] call to homepage= +# 9| getReceiver: [LocalVariableAccess] s +# 9| getArgument: [AssignExpr] ... = ... +# 9| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 +# 9| getAnOperand/getRightOperand: [StringLiteral] "https://github.com/github/cod..." +# 9| getComponent: [StringTextComponent] https://github.com/github/codeql-ruby +# 9| getStmt: [LocalVariableAccess] __synth__0 diff --git a/ruby/ql/test/library-tests/ast/AstDesugar.ql b/ruby/ql/test/library-tests/ast/AstDesugar.ql new file mode 100644 index 00000000000..ab7036adc48 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/AstDesugar.ql @@ -0,0 +1,23 @@ +/** + * @kind graph + */ + +import codeql.ruby.AST +import codeql.ruby.printAst +import codeql.ruby.ast.internal.Synthesis + +class DesugarPrintAstConfiguration extends PrintAstConfiguration { + override predicate shouldPrintNode(AstNode n) { + isDesugared(n) + or + exists(n.getDesugared()) + } + + override predicate shouldPrintAstEdge(AstNode parent, string edgeName, AstNode child) { + super.shouldPrintAstEdge(parent, edgeName, child) and + desugarLevel(parent) = desugarLevel(child) + or + child = parent.getDesugared() and + edgeName = "getDesugared" + } +} diff --git a/ruby/ql/test/library-tests/ast/ValueText.expected b/ruby/ql/test/library-tests/ast/ValueText.expected new file mode 100644 index 00000000000..4204a27a40f --- /dev/null +++ b/ruby/ql/test/library-tests/ast/ValueText.expected @@ -0,0 +1,559 @@ +| calls/calls.rb:11:1:11:3 | 123 | 123 | +| calls/calls.rb:14:5:14:5 | 0 | 0 | +| calls/calls.rb:14:8:14:8 | 1 | 1 | +| calls/calls.rb:14:11:14:11 | 2 | 2 | +| calls/calls.rb:17:15:17:15 | 1 | 1 | +| calls/calls.rb:21:7:21:7 | 1 | 1 | +| calls/calls.rb:25:1:25:3 | 123 | 123 | +| calls/calls.rb:25:9:25:13 | "foo" | foo | +| calls/calls.rb:26:7:26:7 | 1 | 1 | +| calls/calls.rb:36:9:36:11 | 100 | 100 | +| calls/calls.rb:36:14:36:16 | 200 | 200 | +| calls/calls.rb:278:5:278:8 | :blah | blah | +| calls/calls.rb:279:5:279:8 | :blah | blah | +| calls/calls.rb:288:11:288:16 | "blah" | blah | +| calls/calls.rb:289:11:289:11 | 1 | 1 | +| calls/calls.rb:289:14:289:14 | 2 | 2 | +| calls/calls.rb:289:17:289:17 | 3 | 3 | +| calls/calls.rb:290:21:290:21 | 1 | 1 | +| calls/calls.rb:291:22:291:22 | 2 | 2 | +| calls/calls.rb:292:11:292:11 | 4 | 4 | +| calls/calls.rb:292:14:292:14 | 5 | 5 | +| calls/calls.rb:292:26:292:28 | 100 | 100 | +| calls/calls.rb:293:11:293:11 | 6 | 6 | +| calls/calls.rb:293:14:293:14 | 7 | 7 | +| calls/calls.rb:293:27:293:29 | 200 | 200 | +| calls/calls.rb:311:6:311:6 | 1 | 1 | +| calls/calls.rb:314:1:314:8 | __synth__0 | 10 | +| calls/calls.rb:314:12:314:13 | 10 | 10 | +| calls/calls.rb:315:1:315:6 | __synth__0 | 10 | +| calls/calls.rb:315:5:315:5 | 0 | 0 | +| calls/calls.rb:315:10:315:11 | 10 | 10 | +| calls/calls.rb:316:1:316:8 | 0 | 0 | +| calls/calls.rb:316:12:316:19 | 1 | 1 | +| calls/calls.rb:316:12:316:19 | -2 | -2 | +| calls/calls.rb:316:22:316:27 | -1 | -1 | +| calls/calls.rb:316:26:316:26 | 4 | 4 | +| calls/calls.rb:316:32:316:32 | 1 | 1 | +| calls/calls.rb:316:35:316:35 | 2 | 2 | +| calls/calls.rb:316:38:316:38 | 3 | 3 | +| calls/calls.rb:316:41:316:41 | 4 | 4 | +| calls/calls.rb:317:1:317:1 | 0 | 0 | +| calls/calls.rb:317:5:317:10 | 1 | 1 | +| calls/calls.rb:317:5:317:10 | -1 | -1 | +| calls/calls.rb:317:9:317:9 | 5 | 5 | +| calls/calls.rb:317:15:317:15 | 1 | 1 | +| calls/calls.rb:317:18:317:18 | 2 | 2 | +| calls/calls.rb:317:21:317:21 | 3 | 3 | +| calls/calls.rb:318:15:318:15 | 1 | 1 | +| calls/calls.rb:319:5:319:5 | 0 | 0 | +| calls/calls.rb:319:5:319:5 | __synth__1 | 0 | +| calls/calls.rb:319:5:319:5 | __synth__1 | 0 | +| calls/calls.rb:319:11:319:11 | 1 | 1 | +| calls/calls.rb:320:9:320:9 | 0 | 0 | +| calls/calls.rb:320:9:320:9 | __synth__1 | 0 | +| calls/calls.rb:320:9:320:9 | __synth__1 | 0 | +| calls/calls.rb:320:31:320:31 | 1 | 1 | +| calls/calls.rb:320:37:320:37 | 2 | 2 | +| calls/calls.rb:328:31:328:37 | "error" | error | +| constants/constants.rb:3:19:3:27 | "const_a" | const_a | +| constants/constants.rb:6:15:6:23 | "const_b" | const_b | +| constants/constants.rb:17:12:17:18 | "Hello" | Hello | +| constants/constants.rb:17:22:17:45 | CONST_A | const_a | +| constants/constants.rb:17:49:17:64 | CONST_B | const_b | +| constants/constants.rb:20:14:20:19 | "Vera" | Vera | +| constants/constants.rb:20:22:20:28 | "Chuck" | Chuck | +| constants/constants.rb:20:31:20:36 | "Dave" | Dave | +| constants/constants.rb:28:11:28:15 | "foo" | foo | +| constants/constants.rb:37:30:37:33 | 1024 | 1024 | +| constants/constants.rb:39:6:39:31 | MAX_SIZE | 1024 | +| control/cases.rb:2:5:2:5 | 0 | 0 | +| control/cases.rb:3:5:3:5 | 0 | 0 | +| control/cases.rb:4:5:4:5 | 0 | 0 | +| control/cases.rb:5:5:5:5 | 0 | 0 | +| control/cases.rb:8:6:8:6 | a | 0 | +| control/cases.rb:9:6:9:6 | b | 0 | +| control/cases.rb:10:5:10:7 | 100 | 100 | +| control/cases.rb:11:6:11:6 | c | 0 | +| control/cases.rb:11:9:11:9 | d | 0 | +| control/cases.rb:12:5:12:7 | 200 | 200 | +| control/cases.rb:14:5:14:7 | 300 | 300 | +| control/cases.rb:19:6:19:6 | a | 0 | +| control/cases.rb:19:10:19:10 | b | 0 | +| control/cases.rb:19:18:19:19 | 10 | 10 | +| control/cases.rb:20:6:20:6 | a | 0 | +| control/cases.rb:20:11:20:11 | b | 0 | +| control/cases.rb:20:18:20:19 | 20 | 20 | +| control/cases.rb:21:6:21:6 | a | 0 | +| control/cases.rb:21:10:21:10 | b | 0 | +| control/cases.rb:21:18:21:19 | 30 | 30 | +| control/conditionals.rb:2:5:2:5 | 0 | 0 | +| control/conditionals.rb:3:5:3:5 | 0 | 0 | +| control/conditionals.rb:4:5:4:5 | 0 | 0 | +| control/conditionals.rb:5:5:5:5 | 0 | 0 | +| control/conditionals.rb:6:5:6:5 | 0 | 0 | +| control/conditionals.rb:7:5:7:5 | 0 | 0 | +| control/conditionals.rb:10:4:10:4 | a | 0 | +| control/conditionals.rb:10:8:10:8 | b | 0 | +| control/conditionals.rb:11:5:11:5 | c | 0 | +| control/conditionals.rb:15:4:15:4 | a | 0 | +| control/conditionals.rb:15:9:15:9 | b | 0 | +| control/conditionals.rb:16:5:16:5 | c | 0 | +| control/conditionals.rb:18:5:18:5 | d | 0 | +| control/conditionals.rb:22:4:22:4 | a | 0 | +| control/conditionals.rb:22:9:22:9 | 0 | 0 | +| control/conditionals.rb:23:5:23:5 | c | 0 | +| control/conditionals.rb:24:7:24:7 | a | 0 | +| control/conditionals.rb:24:12:24:12 | 1 | 1 | +| control/conditionals.rb:25:5:25:5 | d | 0 | +| control/conditionals.rb:26:7:26:7 | a | 0 | +| control/conditionals.rb:26:12:26:12 | 2 | 2 | +| control/conditionals.rb:27:5:27:5 | e | 0 | +| control/conditionals.rb:29:5:29:5 | f | 0 | +| control/conditionals.rb:33:4:33:4 | a | 0 | +| control/conditionals.rb:33:9:33:9 | 0 | 0 | +| control/conditionals.rb:34:5:34:5 | b | 0 | +| control/conditionals.rb:35:7:35:7 | a | 0 | +| control/conditionals.rb:35:12:35:12 | 1 | 1 | +| control/conditionals.rb:36:5:36:5 | c | 0 | +| control/conditionals.rb:40:8:40:8 | a | 0 | +| control/conditionals.rb:40:12:40:12 | b | 0 | +| control/conditionals.rb:41:5:41:5 | c | 0 | +| control/conditionals.rb:45:8:45:8 | a | 0 | +| control/conditionals.rb:45:13:45:13 | b | 0 | +| control/conditionals.rb:46:5:46:5 | c | 0 | +| control/conditionals.rb:48:5:48:5 | d | 0 | +| control/conditionals.rb:52:5:52:5 | b | 0 | +| control/conditionals.rb:52:10:52:10 | c | 0 | +| control/conditionals.rb:52:14:52:14 | d | 0 | +| control/conditionals.rb:55:5:55:5 | b | 0 | +| control/conditionals.rb:55:14:55:14 | c | 0 | +| control/conditionals.rb:55:18:55:18 | d | 0 | +| control/conditionals.rb:58:5:58:5 | b | 0 | +| control/conditionals.rb:58:9:58:9 | c | 0 | +| control/conditionals.rb:58:13:58:13 | d | 0 | +| control/conditionals.rb:58:13:58:17 | ... + ... | 1 | +| control/conditionals.rb:58:17:58:17 | 1 | 1 | +| control/conditionals.rb:58:21:58:21 | e | 0 | +| control/conditionals.rb:58:21:58:25 | ... - ... | -2 | +| control/conditionals.rb:58:25:58:25 | 2 | 2 | +| control/conditionals.rb:61:8:61:8 | b | 0 | +| control/conditionals.rb:62:5:62:5 | c | 0 | +| control/conditionals.rb:67:8:67:8 | b | 0 | +| control/conditionals.rb:69:5:69:5 | c | 0 | +| control/loops.rb:2:7:2:7 | 0 | 0 | +| control/loops.rb:3:7:3:7 | 0 | 0 | +| control/loops.rb:4:5:4:5 | 0 | 0 | +| control/loops.rb:5:5:5:5 | 0 | 0 | +| control/loops.rb:6:5:6:5 | 0 | 0 | +| control/loops.rb:9:10:9:10 | 1 | 1 | +| control/loops.rb:9:13:9:14 | 10 | 10 | +| control/loops.rb:16:10:16:10 | 1 | 1 | +| control/loops.rb:16:13:16:14 | 10 | 10 | +| control/loops.rb:22:20:22:22 | :foo | foo | +| control/loops.rb:22:25:22:25 | 0 | 0 | +| control/loops.rb:22:28:22:30 | :bar | bar | +| control/loops.rb:22:33:22:33 | 1 | 1 | +| control/loops.rb:28:22:28:24 | :foo | foo | +| control/loops.rb:28:27:28:27 | 0 | 0 | +| control/loops.rb:28:30:28:32 | :bar | bar | +| control/loops.rb:28:35:28:35 | 1 | 1 | +| control/loops.rb:35:11:35:11 | y | 0 | +| control/loops.rb:36:8:36:8 | 1 | 1 | +| control/loops.rb:37:8:37:8 | 1 | 1 | +| control/loops.rb:42:11:42:11 | y | 0 | +| control/loops.rb:43:8:43:8 | 1 | 1 | +| control/loops.rb:44:8:44:8 | 2 | 2 | +| control/loops.rb:48:6:48:6 | 1 | 1 | +| control/loops.rb:48:14:48:14 | y | 0 | +| control/loops.rb:51:12:51:12 | y | 0 | +| control/loops.rb:52:8:52:8 | 1 | 1 | +| control/loops.rb:53:8:53:8 | 1 | 1 | +| control/loops.rb:57:11:57:11 | y | 0 | +| control/loops.rb:58:8:58:8 | 1 | 1 | +| control/loops.rb:59:8:59:8 | 4 | 4 | +| control/loops.rb:63:6:63:6 | 1 | 1 | +| control/loops.rb:63:19:63:19 | 0 | 0 | +| control/loops.rb:66:11:66:11 | y | 0 | +| erb/template.html.erb:19:5:19:17 | "hello world" | hello world | +| erb/template.html.erb:25:9:25:10 | "" | | +| erb/template.html.erb:27:16:27:20 | "foo" | foo | +| erb/template.html.erb:27:23:27:27 | "bar" | bar | +| erb/template.html.erb:27:30:27:34 | "baz" | baz | +| gems/Gemfile:1:8:1:29 | "https://rubygems.org" | https://rubygems.org | +| gems/Gemfile:3:5:3:13 | "foo_gem" | foo_gem | +| gems/Gemfile:3:16:3:23 | "~> 2.0" | ~> 2.0 | +| gems/Gemfile:5:8:5:33 | "https://gems.example.com" | https://gems.example.com | +| gems/Gemfile:6:7:6:14 | "my_gem" | my_gem | +| gems/Gemfile:6:17:6:21 | "1.0" | 1.0 | +| gems/Gemfile:7:7:7:19 | "another_gem" | another_gem | +| gems/Gemfile:7:22:7:28 | "3.1.4" | 3.1.4 | +| gems/lib/test.rb:3:10:3:16 | "Hello" | Hello | +| gems/test.gemspec:2:3:2:8 | __synth__0 | test | +| gems/test.gemspec:2:19:2:24 | "test" | test | +| gems/test.gemspec:3:3:3:11 | __synth__0 | 0.0.0 | +| gems/test.gemspec:3:19:3:25 | "0.0.0" | 0.0.0 | +| gems/test.gemspec:4:3:4:11 | __synth__0 | foo! | +| gems/test.gemspec:4:19:4:24 | "foo!" | foo! | +| gems/test.gemspec:5:3:5:15 | __synth__0 | A test | +| gems/test.gemspec:5:19:5:26 | "A test" | A test | +| gems/test.gemspec:6:20:6:30 | "Mona Lisa" | Mona Lisa | +| gems/test.gemspec:7:3:7:9 | __synth__0 | mona@example.com | +| gems/test.gemspec:7:19:7:36 | "mona@example.com" | mona@example.com | +| gems/test.gemspec:8:20:8:32 | "lib/test.rb" | lib/test.rb | +| gems/test.gemspec:9:3:9:12 | __synth__0 | https://github.com/github/codeql-ruby | +| gems/test.gemspec:9:19:9:57 | "https://github.com/github/cod..." | https://github.com/github/codeql-ruby | +| literals/literals.rb:2:1:2:3 | nil | nil | +| literals/literals.rb:3:1:3:3 | NIL | NIL | +| literals/literals.rb:4:1:4:5 | false | false | +| literals/literals.rb:5:1:5:5 | FALSE | FALSE | +| literals/literals.rb:6:1:6:4 | true | true | +| literals/literals.rb:7:1:7:4 | TRUE | TRUE | +| literals/literals.rb:10:1:10:4 | 1234 | 1234 | +| literals/literals.rb:11:1:11:5 | 5_678 | 5_678 | +| literals/literals.rb:12:1:12:1 | 0 | 0 | +| literals/literals.rb:13:1:13:5 | 0d900 | 0d900 | +| literals/literals.rb:16:1:16:6 | 0x1234 | 0x1234 | +| literals/literals.rb:17:1:17:10 | 0xdeadbeef | 0xdeadbeef | +| literals/literals.rb:18:1:18:11 | 0xF00D_face | 0xF00D_face | +| literals/literals.rb:21:1:21:4 | 0123 | 0123 | +| literals/literals.rb:22:1:22:5 | 0o234 | 0o234 | +| literals/literals.rb:23:1:23:6 | 0O45_6 | 0O45_6 | +| literals/literals.rb:26:1:26:10 | 0b10010100 | 0b10010100 | +| literals/literals.rb:27:1:27:11 | 0B011_01101 | 0B011_01101 | +| literals/literals.rb:30:1:30:5 | 12.34 | 12.34 | +| literals/literals.rb:31:1:31:7 | 1234e-2 | 1234e-2 | +| literals/literals.rb:32:1:32:7 | 1.234E1 | 1.234E1 | +| literals/literals.rb:35:1:35:3 | 23r | 23r | +| literals/literals.rb:36:1:36:5 | 9.85r | 9.85r | +| literals/literals.rb:39:1:39:2 | 2i | 2i | +| literals/literals.rb:46:1:46:2 | "" | | +| literals/literals.rb:47:1:47:2 | "" | | +| literals/literals.rb:48:1:48:7 | "hello" | hello | +| literals/literals.rb:49:1:49:9 | "goodbye" | goodbye | +| literals/literals.rb:50:1:50:30 | "string with escaped \\" quote" | string with escaped \\" quote | +| literals/literals.rb:51:1:51:21 | "string with " quote" | string with " quote | +| literals/literals.rb:52:1:52:14 | "foo bar baz" | foo bar baz | +| literals/literals.rb:53:1:53:15 | "foo bar baz" | foo bar baz | +| literals/literals.rb:54:1:54:20 | "foo ' bar " baz'" | foo ' bar " baz' | +| literals/literals.rb:55:1:55:20 | "FOO ' BAR " BAZ'" | FOO ' BAR " BAZ' | +| literals/literals.rb:56:1:56:12 | "foo\\ bar" | foo\\ bar | +| literals/literals.rb:57:1:57:12 | "foo\\ bar" | foo\\ bar | +| literals/literals.rb:58:13:58:13 | 2 | 2 | +| literals/literals.rb:58:13:58:17 | ... + ... | 4 | +| literals/literals.rb:58:17:58:17 | 2 | 2 | +| literals/literals.rb:59:15:59:15 | 3 | 3 | +| literals/literals.rb:59:15:59:19 | ... + ... | 7 | +| literals/literals.rb:59:19:59:19 | 4 | 4 | +| literals/literals.rb:60:1:60:20 | "2 + 2 = #{ 2 + 2 }" | 2 + 2 = #{ 2 + 2 } | +| literals/literals.rb:61:1:61:22 | "3 + 4 = #{ 3 + 4 }" | 3 + 4 = #{ 3 + 4 } | +| literals/literals.rb:62:1:62:5 | "foo" | foo | +| literals/literals.rb:62:7:62:11 | "bar" | bar | +| literals/literals.rb:62:13:62:17 | "baz" | baz | +| literals/literals.rb:63:1:63:7 | "foo" | foo | +| literals/literals.rb:63:9:63:13 | "bar" | bar | +| literals/literals.rb:63:15:63:19 | "baz" | baz | +| literals/literals.rb:64:1:64:5 | "foo" | foo | +| literals/literals.rb:64:14:64:14 | 1 | 1 | +| literals/literals.rb:64:14:64:18 | ... * ... | 1 | +| literals/literals.rb:64:18:64:18 | 1 | 1 | +| literals/literals.rb:64:23:64:27 | "baz" | baz | +| literals/literals.rb:65:17:65:17 | 2 | 2 | +| literals/literals.rb:65:17:65:21 | ... + ... | 5 | +| literals/literals.rb:65:21:65:21 | 3 | 3 | +| literals/literals.rb:66:17:66:17 | 1 | 1 | +| literals/literals.rb:66:17:66:19 | ... + ... | 10 | +| literals/literals.rb:66:19:66:19 | 9 | 9 | +| literals/literals.rb:69:1:69:2 | ?x | ?x | +| literals/literals.rb:70:1:70:3 | ?\\n | ?\\n | +| literals/literals.rb:71:1:71:3 | ?\\s | ?\\s | +| literals/literals.rb:72:1:72:3 | ?\\\\ | ?\\\\ | +| literals/literals.rb:73:1:73:7 | ?\\u{58} | ?\\u{58} | +| literals/literals.rb:74:1:74:5 | ?\\C-a | ?\\C-a | +| literals/literals.rb:75:1:75:5 | ?\\M-a | ?\\M-a | +| literals/literals.rb:76:1:76:8 | ?\\M-\\C-a | ?\\M-\\C-a | +| literals/literals.rb:77:1:77:8 | ?\\C-\\M-a | ?\\C-\\M-a | +| literals/literals.rb:80:1:80:3 | :"" | | +| literals/literals.rb:81:1:81:6 | :hello | hello | +| literals/literals.rb:82:1:82:10 | :"foo bar" | foo bar | +| literals/literals.rb:83:1:83:10 | :"bar baz" | bar baz | +| literals/literals.rb:84:3:84:5 | :foo | foo | +| literals/literals.rb:84:8:84:12 | "bar" | bar | +| literals/literals.rb:85:1:85:10 | :"wibble" | wibble | +| literals/literals.rb:86:1:86:17 | :"wibble wobble" | wibble wobble | +| literals/literals.rb:87:10:87:10 | 2 | 2 | +| literals/literals.rb:87:10:87:14 | ... + ... | 4 | +| literals/literals.rb:87:14:87:14 | 2 | 2 | +| literals/literals.rb:88:1:88:17 | :"foo_#{ 1 + 1 }" | foo_#{ 1 + 1 } | +| literals/literals.rb:89:1:89:18 | :"foo_#{ 3 - 2 }" | foo_#{ 3 - 2 } | +| literals/literals.rb:93:2:93:2 | 1 | 1 | +| literals/literals.rb:93:5:93:5 | 2 | 2 | +| literals/literals.rb:93:8:93:8 | 3 | 3 | +| literals/literals.rb:94:2:94:2 | 4 | 4 | +| literals/literals.rb:94:5:94:5 | 5 | 5 | +| literals/literals.rb:94:8:94:9 | 12 | 12 | +| literals/literals.rb:94:8:94:13 | ... / ... | 6 | +| literals/literals.rb:94:13:94:13 | 2 | 2 | +| literals/literals.rb:95:2:95:2 | 7 | 7 | +| literals/literals.rb:95:6:95:6 | 8 | 8 | +| literals/literals.rb:95:9:95:9 | 9 | 9 | +| literals/literals.rb:99:4:99:6 | "foo" | foo | +| literals/literals.rb:99:8:99:10 | "bar" | bar | +| literals/literals.rb:99:12:99:14 | "baz" | baz | +| literals/literals.rb:100:4:100:6 | "foo" | foo | +| literals/literals.rb:100:8:100:10 | "bar" | bar | +| literals/literals.rb:100:12:100:14 | "baz" | baz | +| literals/literals.rb:101:4:101:6 | "foo" | foo | +| literals/literals.rb:101:13:101:13 | 1 | 1 | +| literals/literals.rb:101:13:101:15 | ... + ... | 2 | +| literals/literals.rb:101:15:101:15 | 1 | 1 | +| literals/literals.rb:101:18:101:20 | "baz" | baz | +| literals/literals.rb:102:4:102:6 | "foo" | foo | +| literals/literals.rb:102:8:102:16 | "bar#{1+1}" | bar#{1+1} | +| literals/literals.rb:102:18:102:20 | "baz" | baz | +| literals/literals.rb:106:4:106:6 | :"foo" | foo | +| literals/literals.rb:106:8:106:10 | :"bar" | bar | +| literals/literals.rb:106:12:106:14 | :"baz" | baz | +| literals/literals.rb:107:4:107:6 | :"foo" | foo | +| literals/literals.rb:107:8:107:10 | :"bar" | bar | +| literals/literals.rb:107:12:107:14 | :"baz" | baz | +| literals/literals.rb:108:4:108:6 | :"foo" | foo | +| literals/literals.rb:108:14:108:14 | 2 | 2 | +| literals/literals.rb:108:14:108:18 | ... + ... | 6 | +| literals/literals.rb:108:18:108:18 | 4 | 4 | +| literals/literals.rb:108:22:108:24 | :"baz" | baz | +| literals/literals.rb:109:4:109:6 | :"foo" | foo | +| literals/literals.rb:109:8:109:12 | :"bar#{" | bar#{ | +| literals/literals.rb:109:14:109:14 | :"2" | 2 | +| literals/literals.rb:109:16:109:16 | :"+" | + | +| literals/literals.rb:109:18:109:18 | :"4" | 4 | +| literals/literals.rb:109:20:109:20 | :"}" | } | +| literals/literals.rb:109:22:109:24 | :"baz" | baz | +| literals/literals.rb:113:3:113:5 | :foo | foo | +| literals/literals.rb:113:8:113:8 | 1 | 1 | +| literals/literals.rb:113:11:113:14 | :bar | bar | +| literals/literals.rb:113:19:113:19 | 2 | 2 | +| literals/literals.rb:113:22:113:26 | "baz" | baz | +| literals/literals.rb:113:31:113:31 | 3 | 3 | +| literals/literals.rb:114:3:114:5 | :foo | foo | +| literals/literals.rb:114:8:114:8 | 7 | 7 | +| literals/literals.rb:117:2:117:2 | 1 | 1 | +| literals/literals.rb:117:5:117:6 | 10 | 10 | +| literals/literals.rb:118:2:118:2 | 1 | 1 | +| literals/literals.rb:118:6:118:7 | 10 | 10 | +| literals/literals.rb:119:2:119:2 | 1 | 1 | +| literals/literals.rb:119:7:119:7 | 0 | 0 | +| literals/literals.rb:120:9:120:9 | 2 | 2 | +| literals/literals.rb:120:9:120:11 | ... + ... | 5 | +| literals/literals.rb:120:11:120:11 | 3 | 3 | +| literals/literals.rb:121:2:121:2 | 1 | 1 | +| literals/literals.rb:122:4:122:4 | 1 | 1 | +| literals/literals.rb:123:2:123:2 | 0 | 0 | +| literals/literals.rb:123:6:123:6 | 1 | 1 | +| literals/literals.rb:126:1:126:7 | `ls -l` | ls -l | +| literals/literals.rb:127:1:127:9 | `ls -l` | ls -l | +| literals/literals.rb:128:11:128:11 | 1 | 1 | +| literals/literals.rb:128:11:128:15 | ... + ... | 2 | +| literals/literals.rb:128:15:128:15 | 1 | 1 | +| literals/literals.rb:129:13:129:13 | 5 | 5 | +| literals/literals.rb:129:13:129:17 | ... - ... | 1 | +| literals/literals.rb:129:17:129:17 | 4 | 4 | +| literals/literals.rb:132:1:132:2 | // | | +| literals/literals.rb:133:1:133:5 | /foo/ | foo | +| literals/literals.rb:134:1:134:6 | /foo/ | foo | +| literals/literals.rb:135:1:135:13 | /foo+\\sbar\\S/ | foo+\\sbar\\S | +| literals/literals.rb:136:8:136:8 | 1 | 1 | +| literals/literals.rb:136:8:136:12 | ... + ... | 2 | +| literals/literals.rb:136:12:136:12 | 1 | 1 | +| literals/literals.rb:137:1:137:8 | /foo/ | foo | +| literals/literals.rb:138:1:138:4 | // | | +| literals/literals.rb:139:1:139:7 | /foo/ | foo | +| literals/literals.rb:140:1:140:8 | /foo/ | foo | +| literals/literals.rb:141:1:141:15 | /foo+\\sbar\\S/ | foo+\\sbar\\S | +| literals/literals.rb:142:10:142:10 | 1 | 1 | +| literals/literals.rb:142:10:142:14 | ... + ... | 2 | +| literals/literals.rb:142:14:142:14 | 1 | 1 | +| literals/literals.rb:143:1:143:10 | /foo/ | foo | +| literals/literals.rb:146:1:146:34 | "abcdefghijklmnopqrstuvwxyzabcdef" | abcdefghijklmnopqrstuvwxyzabcdef | +| literals/literals.rb:147:1:147:35 | "foobarfoobarfoobarfoobarfooba..." | foobarfoobarfoobarfoobarfoobarfoo | +| literals/literals.rb:148:1:148:40 | "foobar\\\\foobar\\\\foobar\\\\fooba..." | foobar\\\\foobar\\\\foobar\\\\foobar\\\\foobar | +| literals/literals.rb:151:9:151:13 | <<SQL | \nselect * from table\n | +| literals/literals.rb:158:11:158:16 | <<-BLA | \nsome text\\nand some more\n | +| literals/literals.rb:163:9:163:19 | <<~SQUIGGLY | \n indented stuff\n | +| literals/literals.rb:176:10:176:19 | <<`SCRIPT` | \n cat file.txt\n | +| misc/misc.erb:2:15:2:37 | "main_include_admin.js" | main_include_admin.js | +| misc/misc.rb:1:7:1:11 | "bar" | bar | +| misc/misc.rb:3:7:3:9 | foo | foo | +| misc/misc.rb:3:12:3:15 | :foo | foo | +| misc/misc.rb:3:18:3:21 | foo= | foo= | +| misc/misc.rb:3:24:3:25 | [] | [] | +| misc/misc.rb:3:28:3:30 | []= | []= | +| misc/misc.rb:4:15:4:17 | bar | bar | +| misc/misc.rb:5:7:5:9 | nil | nil | +| misc/misc.rb:5:12:5:15 | true | true | +| misc/misc.rb:5:18:5:22 | false | false | +| misc/misc.rb:5:25:5:29 | super | super | +| misc/misc.rb:5:32:5:35 | self | self | +| misc/misc.rb:7:7:7:9 | new | new | +| misc/misc.rb:7:11:7:14 | :old | old | +| misc/misc.rb:8:7:8:10 | foo= | foo= | +| misc/misc.rb:8:12:8:14 | []= | []= | +| misc/misc.rb:9:7:9:11 | super | super | +| misc/misc.rb:9:13:9:16 | self | self | +| misc/misc.rb:10:13:10:15 | bar | bar | +| misc/misc.rb:10:19:10:24 | :"foo" | foo | +| modules/classes.rb:11:28:11:31 | :baz | baz | +| modules/classes.rb:22:10:22:12 | "a" | a | +| modules/classes.rb:26:10:26:12 | "b" | b | +| modules/classes.rb:30:17:30:19 | 123 | 123 | +| modules/classes.rb:40:5:40:11 | "hello" | hello | +| modules/classes.rb:41:10:41:10 | x | hello | +| modules/classes.rb:43:5:43:7 | 100 | 100 | +| modules/classes.rb:47:10:47:17 | "wibble" | wibble | +| modules/classes.rb:51:18:51:20 | 456 | 456 | +| modules/modules.rb:12:10:12:26 | "module Foo::Bar" | module Foo::Bar | +| modules/modules.rb:13:19:13:19 | 0 | 0 | +| modules/modules.rb:22:8:22:19 | "module Foo" | module Foo | +| modules/modules.rb:23:17:23:17 | 1 | 1 | +| modules/modules.rb:33:8:33:25 | "module Foo again" | module Foo again | +| modules/modules.rb:34:17:34:17 | 2 | 2 | +| modules/modules.rb:44:8:44:19 | "module Bar" | module Bar | +| modules/modules.rb:45:17:45:17 | 3 | 3 | +| modules/modules.rb:55:8:55:30 | "module Foo::Bar again" | module Foo::Bar again | +| modules/modules.rb:56:17:56:17 | 4 | 4 | +| modules/toplevel.rb:1:6:1:12 | "world" | world | +| modules/toplevel.rb:3:12:3:16 | "!!!" | !!! | +| modules/toplevel.rb:5:14:5:20 | "hello" | hello | +| operations/operations.rb:3:5:3:5 | 0 | 0 | +| operations/operations.rb:4:5:4:5 | 0 | 0 | +| operations/operations.rb:5:7:5:7 | 0 | 0 | +| operations/operations.rb:6:8:6:8 | 0 | 0 | +| operations/operations.rb:7:7:7:7 | 0 | 0 | +| operations/operations.rb:8:7:8:7 | 0 | 0 | +| operations/operations.rb:9:10:9:10 | 0 | 0 | +| operations/operations.rb:10:5:10:5 | 0 | 0 | +| operations/operations.rb:11:8:11:8 | 0 | 0 | +| operations/operations.rb:12:5:12:5 | 0 | 0 | +| operations/operations.rb:13:8:13:8 | 0 | 0 | +| operations/operations.rb:14:7:14:7 | 0 | 0 | +| operations/operations.rb:15:9:15:9 | 0 | 0 | +| operations/operations.rb:16:7:16:7 | 0 | 0 | +| operations/operations.rb:17:5:17:5 | 0 | 0 | +| operations/operations.rb:18:5:18:5 | 0 | 0 | +| operations/operations.rb:19:5:19:5 | 0 | 0 | +| operations/operations.rb:20:5:20:5 | 0 | 0 | +| operations/operations.rb:23:2:23:2 | a | 0 | +| operations/operations.rb:24:5:24:5 | b | 0 | +| operations/operations.rb:25:2:25:3 | 14 | 14 | +| operations/operations.rb:26:2:26:2 | 7 | 7 | +| operations/operations.rb:27:2:27:2 | x | 0 | +| operations/operations.rb:28:10:28:12 | foo | 0 | +| operations/operations.rb:29:17:29:17 | 1 | 1 | +| operations/operations.rb:29:22:29:22 | 2 | 2 | +| operations/operations.rb:29:26:29:26 | :a | a | +| operations/operations.rb:29:28:29:28 | 3 | 3 | +| operations/operations.rb:29:34:29:34 | :b | b | +| operations/operations.rb:29:36:29:36 | 4 | 4 | +| operations/operations.rb:29:39:29:39 | :c | c | +| operations/operations.rb:29:41:29:41 | 5 | 5 | +| operations/operations.rb:32:1:32:1 | w | 0 | +| operations/operations.rb:32:1:32:7 | ... + ... | 234 | +| operations/operations.rb:32:5:32:7 | 234 | 234 | +| operations/operations.rb:33:1:33:1 | x | 0 | +| operations/operations.rb:33:1:33:6 | ... - ... | -17 | +| operations/operations.rb:33:5:33:6 | 17 | 17 | +| operations/operations.rb:34:1:34:1 | y | 0 | +| operations/operations.rb:34:1:34:6 | ... * ... | 0 | +| operations/operations.rb:34:5:34:6 | 10 | 10 | +| operations/operations.rb:35:1:35:1 | z | 0 | +| operations/operations.rb:35:1:35:5 | ... / ... | 0 | +| operations/operations.rb:35:5:35:5 | 2 | 2 | +| operations/operations.rb:36:1:36:3 | num | 0 | +| operations/operations.rb:36:7:36:7 | 2 | 2 | +| operations/operations.rb:37:1:37:4 | base | 0 | +| operations/operations.rb:37:9:37:13 | power | 0 | +| operations/operations.rb:40:1:40:3 | foo | 0 | +| operations/operations.rb:40:8:40:10 | bar | 0 | +| operations/operations.rb:41:1:41:3 | baz | 0 | +| operations/operations.rb:41:9:41:11 | qux | 0 | +| operations/operations.rb:42:1:42:1 | a | 0 | +| operations/operations.rb:42:6:42:6 | b | 0 | +| operations/operations.rb:43:1:43:1 | x | 0 | +| operations/operations.rb:43:6:43:6 | y | 0 | +| operations/operations.rb:46:1:46:1 | x | 0 | +| operations/operations.rb:46:6:46:6 | 3 | 3 | +| operations/operations.rb:47:1:47:1 | y | 0 | +| operations/operations.rb:47:6:47:7 | 16 | 16 | +| operations/operations.rb:48:1:48:3 | foo | 0 | +| operations/operations.rb:48:7:48:10 | 0xff | 0xff | +| operations/operations.rb:49:1:49:3 | bar | 0 | +| operations/operations.rb:49:7:49:10 | 0x02 | 0x02 | +| operations/operations.rb:50:1:50:3 | baz | 0 | +| operations/operations.rb:50:7:50:9 | qux | 0 | +| operations/operations.rb:53:1:53:1 | x | 0 | +| operations/operations.rb:53:6:53:6 | y | 0 | +| operations/operations.rb:54:1:54:1 | a | 0 | +| operations/operations.rb:54:6:54:8 | 123 | 123 | +| operations/operations.rb:55:1:55:1 | m | 0 | +| operations/operations.rb:55:7:55:7 | n | 0 | +| operations/operations.rb:58:1:58:1 | x | 0 | +| operations/operations.rb:58:5:58:5 | 0 | 0 | +| operations/operations.rb:59:1:59:1 | y | 0 | +| operations/operations.rb:59:6:59:8 | 100 | 100 | +| operations/operations.rb:60:1:60:1 | a | 0 | +| operations/operations.rb:60:5:60:5 | b | 0 | +| operations/operations.rb:61:1:61:1 | 7 | 7 | +| operations/operations.rb:61:6:61:8 | foo | 0 | +| operations/operations.rb:64:1:64:1 | a | 0 | +| operations/operations.rb:64:7:64:7 | b | 0 | +| operations/operations.rb:65:1:65:4 | name | 0 | +| operations/operations.rb:65:9:65:15 | /foo.*/ | foo.* | +| operations/operations.rb:66:1:66:6 | handle | 0 | +| operations/operations.rb:66:11:66:17 | /.*bar/ | .*bar | +| operations/operations.rb:69:1:69:1 | x | 0 | +| operations/operations.rb:69:3:69:4 | ... + ... | 128 | +| operations/operations.rb:69:6:69:8 | 128 | 128 | +| operations/operations.rb:70:1:70:1 | y | 0 | +| operations/operations.rb:70:3:70:4 | ... - ... | -32 | +| operations/operations.rb:70:6:70:7 | 32 | 32 | +| operations/operations.rb:71:1:71:1 | a | 0 | +| operations/operations.rb:71:3:71:4 | ... * ... | 0 | +| operations/operations.rb:71:6:71:7 | 12 | 12 | +| operations/operations.rb:72:1:72:1 | b | 0 | +| operations/operations.rb:72:3:72:4 | ... / ... | 0 | +| operations/operations.rb:72:6:72:6 | 4 | 4 | +| operations/operations.rb:73:1:73:1 | z | 0 | +| operations/operations.rb:73:6:73:6 | 2 | 2 | +| operations/operations.rb:74:1:74:3 | foo | 0 | +| operations/operations.rb:74:9:74:11 | bar | 0 | +| operations/operations.rb:77:2:77:2 | x | 128 | +| operations/operations.rb:77:8:77:8 | y | -32 | +| operations/operations.rb:78:2:78:2 | a | 0 | +| operations/operations.rb:78:8:78:8 | b | 0 | +| operations/operations.rb:81:8:81:8 | 2 | 2 | +| operations/operations.rb:82:2:82:2 | y | -32 | +| operations/operations.rb:82:8:82:8 | 3 | 3 | +| operations/operations.rb:83:9:83:12 | mask | 0 | +| operations/operations.rb:84:2:84:4 | bar | 0 | +| operations/operations.rb:84:9:84:12 | 0x01 | 0x01 | +| operations/operations.rb:85:2:85:4 | baz | 0 | +| operations/operations.rb:85:9:85:11 | qux | 0 | +| operations/operations.rb:88:8:88:8 | 1 | 1 | +| operations/operations.rb:89:9:89:9 | 2 | 2 | +| operations/operations.rb:91:9:91:9 | 3 | 3 | +| operations/operations.rb:92:10:92:10 | 4 | 4 | +| operations/operations.rb:95:15:95:15 | 5 | 5 | +| operations/operations.rb:96:16:96:16 | 6 | 6 | +| params/params.rb:41:46:41:46 | 7 | 7 | +| params/params.rb:47:19:47:21 | :bar | bar | +| params/params.rb:47:24:47:24 | 2 | 2 | +| params/params.rb:47:27:47:29 | :foo | foo | +| params/params.rb:47:32:47:32 | 3 | 3 | +| params/params.rb:49:37:49:39 | 100 | 100 | +| params/params.rb:53:44:53:44 | 3 | 3 | +| params/params.rb:58:46:58:46 | 0 | 0 | +| params/params.rb:58:56:58:58 | 100 | 100 | +| params/params.rb:63:14:63:19 | "Zeus" | Zeus | +| params/params.rb:65:41:65:42 | 99 | 99 | +| params/params.rb:70:42:70:45 | 1000 | 1000 | +| params/params.rb:70:52:70:53 | 20 | 20 | diff --git a/ruby/ql/test/library-tests/ast/ValueText.ql b/ruby/ql/test/library-tests/ast/ValueText.ql new file mode 100644 index 00000000000..53bcdefa18c --- /dev/null +++ b/ruby/ql/test/library-tests/ast/ValueText.ql @@ -0,0 +1,4 @@ +import ruby + +from Expr e +select e, e.getValueText() diff --git a/ruby/ql/test/library-tests/ast/calls/arguments.expected b/ruby/ql/test/library-tests/ast/calls/arguments.expected new file mode 100644 index 00000000000..526de133d0b --- /dev/null +++ b/ruby/ql/test/library-tests/ast/calls/arguments.expected @@ -0,0 +1,17 @@ +blockArguments +| calls.rb:266:5:266:8 | &... | calls.rb:266:6:266:8 | call to bar | +| calls.rb:267:5:267:11 | &... | calls.rb:267:6:267:11 | call to bar | +splatExpr +| calls.rb:270:5:270:8 | * ... | calls.rb:270:6:270:8 | call to bar | +| calls.rb:271:5:271:11 | * ... | calls.rb:271:6:271:11 | call to bar | +| calls.rb:316:31:316:42 | * ... | calls.rb:316:31:316:42 | [...] | +| calls.rb:317:14:317:22 | * ... | calls.rb:317:14:317:22 | [...] | +hashSplatExpr +| calls.rb:274:5:274:9 | ** ... | calls.rb:274:7:274:9 | call to bar | +| calls.rb:275:5:275:12 | ** ... | calls.rb:275:7:275:12 | call to bar | +keywordArguments +| calls.rb:278:5:278:13 | Pair | calls.rb:278:5:278:8 | :blah | calls.rb:278:11:278:13 | call to bar | +| calls.rb:279:5:279:16 | Pair | calls.rb:279:5:279:8 | :blah | calls.rb:279:11:279:16 | call to bar | +keywordArgumentsByKeyword +| calls.rb:278:1:278:14 | call to foo | blah | calls.rb:278:11:278:13 | call to bar | +| calls.rb:279:1:279:17 | call to foo | blah | calls.rb:279:11:279:16 | call to bar | diff --git a/ruby/ql/test/library-tests/ast/calls/arguments.ql b/ruby/ql/test/library-tests/ast/calls/arguments.ql new file mode 100644 index 00000000000..abe4ffa4933 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/calls/arguments.ql @@ -0,0 +1,15 @@ +import ruby + +query predicate blockArguments(BlockArgument a, Expr e) { e = a.getValue() } + +query predicate splatExpr(SplatExpr a, Expr e) { e = a.getOperand() } + +query predicate hashSplatExpr(HashSplatExpr a, Expr e) { e = a.getOperand() } + +query predicate keywordArguments(Pair a, Expr key, Expr value) { + exists(Call c | c.getAnArgument() = a and key = a.getKey() and value = a.getValue()) +} + +query predicate keywordArgumentsByKeyword(Call c, string keyword, Expr value) { + c.getKeywordArgument(keyword) = value +} diff --git a/ruby/ql/test/library-tests/ast/calls/calls.expected b/ruby/ql/test/library-tests/ast/calls/calls.expected new file mode 100644 index 00000000000..1e7ecd8fa7b --- /dev/null +++ b/ruby/ql/test/library-tests/ast/calls/calls.expected @@ -0,0 +1,379 @@ +callsWithNoReceiverArgumentsOrBlock +| calls.rb:31:3:31:7 | yield ... | (none) | +| calls.rb:286:5:286:9 | call to super | super | +| calls.rb:287:5:287:11 | call to super | super | +| calls.rb:305:5:305:9 | call to super | super | +callsWithArguments +| calls.rb:14:1:14:11 | call to foo | foo | 0 | calls.rb:14:5:14:5 | 0 | +| calls.rb:14:1:14:11 | call to foo | foo | 1 | calls.rb:14:8:14:8 | 1 | +| calls.rb:14:1:14:11 | call to foo | foo | 2 | calls.rb:14:11:14:11 | 2 | +| calls.rb:17:11:17:15 | ... + ... | + | 0 | calls.rb:17:15:17:15 | 1 | +| calls.rb:21:3:21:7 | ... + ... | + | 0 | calls.rb:21:7:21:7 | 1 | +| calls.rb:25:1:27:3 | call to bar | bar | 0 | calls.rb:25:9:25:13 | "foo" | +| calls.rb:26:3:26:7 | ... + ... | + | 0 | calls.rb:26:7:26:7 | 1 | +| calls.rb:36:3:36:16 | yield ... | (none) | 0 | calls.rb:36:9:36:11 | 100 | +| calls.rb:36:3:36:16 | yield ... | (none) | 1 | calls.rb:36:14:36:16 | 200 | +| calls.rb:54:1:54:14 | call to some_func | some_func | 0 | calls.rb:54:11:54:13 | call to foo | +| calls.rb:55:1:55:17 | call to some_func | some_func | 0 | calls.rb:55:11:55:16 | call to foo | +| calls.rb:58:1:58:5 | call to [] | [] | 0 | calls.rb:58:2:58:4 | call to foo | +| calls.rb:59:1:59:8 | call to [] | [] | 0 | calls.rb:59:2:59:7 | call to foo | +| calls.rb:66:6:66:7 | ... + ... | + | 0 | calls.rb:66:9:66:11 | call to bar | +| calls.rb:67:6:67:7 | ... + ... | + | 0 | calls.rb:67:9:67:14 | call to bar | +| calls.rb:85:1:85:12 | ... + ... | + | 0 | calls.rb:85:7:85:12 | call to bar | +| calls.rb:234:1:234:8 | ...[...] | [] | 0 | calls.rb:234:5:234:7 | call to bar | +| calls.rb:235:1:235:14 | ...[...] | [] | 0 | calls.rb:235:8:235:13 | call to bar | +| calls.rb:266:1:266:9 | call to foo | foo | 0 | calls.rb:266:5:266:8 | &... | +| calls.rb:267:1:267:12 | call to foo | foo | 0 | calls.rb:267:5:267:11 | &... | +| calls.rb:270:1:270:9 | call to foo | foo | 0 | calls.rb:270:5:270:8 | * ... | +| calls.rb:271:1:271:12 | call to foo | foo | 0 | calls.rb:271:5:271:11 | * ... | +| calls.rb:274:1:274:10 | call to foo | foo | 0 | calls.rb:274:5:274:9 | ** ... | +| calls.rb:275:1:275:13 | call to foo | foo | 0 | calls.rb:275:5:275:12 | ** ... | +| calls.rb:278:1:278:14 | call to foo | foo | 0 | calls.rb:278:5:278:13 | Pair | +| calls.rb:279:1:279:17 | call to foo | foo | 0 | calls.rb:279:5:279:16 | Pair | +| calls.rb:288:5:288:16 | call to super | super | 0 | calls.rb:288:11:288:16 | "blah" | +| calls.rb:289:5:289:17 | call to super | super | 0 | calls.rb:289:11:289:11 | 1 | +| calls.rb:289:5:289:17 | call to super | super | 1 | calls.rb:289:14:289:14 | 2 | +| calls.rb:289:5:289:17 | call to super | super | 2 | calls.rb:289:17:289:17 | 3 | +| calls.rb:290:17:290:21 | ... + ... | + | 0 | calls.rb:290:21:290:21 | 1 | +| calls.rb:291:18:291:22 | ... * ... | * | 0 | calls.rb:291:22:291:22 | 2 | +| calls.rb:292:5:292:30 | call to super | super | 0 | calls.rb:292:11:292:11 | 4 | +| calls.rb:292:5:292:30 | call to super | super | 1 | calls.rb:292:14:292:14 | 5 | +| calls.rb:292:22:292:28 | ... + ... | + | 0 | calls.rb:292:26:292:28 | 100 | +| calls.rb:293:5:293:33 | call to super | super | 0 | calls.rb:293:11:293:11 | 6 | +| calls.rb:293:5:293:33 | call to super | super | 1 | calls.rb:293:14:293:14 | 7 | +| calls.rb:293:23:293:29 | ... + ... | + | 0 | calls.rb:293:27:293:29 | 200 | +| calls.rb:311:1:311:7 | call to call | call | 0 | calls.rb:311:6:311:6 | 1 | +| calls.rb:314:1:314:8 | call to foo= | foo= | 0 | calls.rb:314:1:314:8 | ... = ... | +| calls.rb:315:1:315:6 | ...[...] | [] | 0 | calls.rb:315:5:315:5 | 0 | +| calls.rb:315:1:315:6 | call to []= | []= | 0 | calls.rb:315:5:315:5 | 0 | +| calls.rb:315:1:315:6 | call to []= | []= | 1 | calls.rb:315:1:315:6 | ... = ... | +| calls.rb:316:1:316:8 | call to [] | [] | 0 | calls.rb:316:1:316:8 | 0 | +| calls.rb:316:1:316:8 | call to foo= | foo= | 0 | calls.rb:316:1:316:8 | ... = ... | +| calls.rb:316:12:316:19 | call to [] | [] | 0 | calls.rb:316:12:316:19 | _ .. _ | +| calls.rb:316:12:316:19 | call to bar= | bar= | 0 | calls.rb:316:12:316:19 | ... = ... | +| calls.rb:316:22:316:27 | ...[...] | [] | 0 | calls.rb:316:26:316:26 | 4 | +| calls.rb:316:22:316:27 | call to [] | [] | 0 | calls.rb:316:22:316:27 | -1 | +| calls.rb:316:22:316:27 | call to []= | []= | 0 | calls.rb:316:26:316:26 | 4 | +| calls.rb:316:22:316:27 | call to []= | []= | 1 | calls.rb:316:22:316:27 | ... = ... | +| calls.rb:316:31:316:42 | call to [] | [] | 0 | calls.rb:316:32:316:32 | 1 | +| calls.rb:316:31:316:42 | call to [] | [] | 1 | calls.rb:316:35:316:35 | 2 | +| calls.rb:316:31:316:42 | call to [] | [] | 2 | calls.rb:316:38:316:38 | 3 | +| calls.rb:316:31:316:42 | call to [] | [] | 3 | calls.rb:316:41:316:41 | 4 | +| calls.rb:317:1:317:1 | call to [] | [] | 0 | calls.rb:317:1:317:1 | 0 | +| calls.rb:317:5:317:10 | ...[...] | [] | 0 | calls.rb:317:9:317:9 | 5 | +| calls.rb:317:5:317:10 | call to [] | [] | 0 | calls.rb:317:5:317:10 | _ .. _ | +| calls.rb:317:5:317:10 | call to []= | []= | 0 | calls.rb:317:9:317:9 | 5 | +| calls.rb:317:5:317:10 | call to []= | []= | 1 | calls.rb:317:5:317:10 | ... = ... | +| calls.rb:317:14:317:22 | call to [] | [] | 0 | calls.rb:317:15:317:15 | 1 | +| calls.rb:317:14:317:22 | call to [] | [] | 1 | calls.rb:317:18:317:18 | 2 | +| calls.rb:317:14:317:22 | call to [] | [] | 2 | calls.rb:317:21:317:21 | 3 | +| calls.rb:318:1:318:10 | call to count= | count= | 1 | calls.rb:318:12:318:13 | __synth__1 | +| calls.rb:318:12:318:13 | ... + ... | + | 0 | calls.rb:318:15:318:15 | 1 | +| calls.rb:319:1:319:6 | ...[...] | [] | 0 | calls.rb:319:5:319:5 | 0 | +| calls.rb:319:1:319:6 | call to [] | [] | 0 | calls.rb:319:5:319:5 | __synth__1 | +| calls.rb:319:1:319:6 | call to []= | []= | 0 | calls.rb:319:5:319:5 | __synth__1 | +| calls.rb:319:1:319:6 | call to []= | []= | 2 | calls.rb:319:8:319:9 | __synth__2 | +| calls.rb:319:8:319:9 | ... + ... | + | 0 | calls.rb:319:11:319:11 | 1 | +| calls.rb:320:1:320:32 | ...[...] | [] | 0 | calls.rb:320:9:320:9 | 0 | +| calls.rb:320:1:320:32 | ...[...] | [] | 1 | calls.rb:320:12:320:18 | call to baz | +| calls.rb:320:1:320:32 | ...[...] | [] | 2 | calls.rb:320:21:320:31 | ... + ... | +| calls.rb:320:1:320:32 | call to [] | [] | 0 | calls.rb:320:9:320:9 | __synth__1 | +| calls.rb:320:1:320:32 | call to [] | [] | 1 | calls.rb:320:12:320:18 | __synth__2 | +| calls.rb:320:1:320:32 | call to [] | [] | 2 | calls.rb:320:21:320:31 | __synth__3 | +| calls.rb:320:1:320:32 | call to []= | []= | 0 | calls.rb:320:9:320:9 | __synth__1 | +| calls.rb:320:1:320:32 | call to []= | []= | 1 | calls.rb:320:12:320:18 | __synth__2 | +| calls.rb:320:1:320:32 | call to []= | []= | 2 | calls.rb:320:21:320:31 | __synth__3 | +| calls.rb:320:1:320:32 | call to []= | []= | 4 | calls.rb:320:34:320:35 | __synth__4 | +| calls.rb:320:21:320:31 | ... + ... | + | 0 | calls.rb:320:31:320:31 | 1 | +| calls.rb:320:34:320:35 | ... * ... | * | 0 | calls.rb:320:37:320:37 | 2 | +| calls.rb:328:25:328:37 | call to print | print | 0 | calls.rb:328:31:328:37 | "error" | +| calls.rb:332:3:332:12 | call to super | super | 0 | calls.rb:332:9:332:11 | ... | +| calls.rb:336:3:336:13 | call to bar | bar | 0 | calls.rb:336:7:336:7 | b | +| calls.rb:336:3:336:13 | call to bar | bar | 1 | calls.rb:336:10:336:12 | ... | +callsWithReceiver +| calls.rb:2:1:2:5 | call to foo | calls.rb:2:1:2:5 | self | +| calls.rb:5:1:5:10 | call to bar | calls.rb:5:1:5:3 | Foo | +| calls.rb:8:1:8:7 | call to bar | calls.rb:8:1:8:7 | self | +| calls.rb:11:1:11:7 | call to bar | calls.rb:11:1:11:3 | 123 | +| calls.rb:14:1:14:11 | call to foo | calls.rb:14:1:14:11 | self | +| calls.rb:17:1:17:17 | call to foo | calls.rb:17:1:17:17 | self | +| calls.rb:17:11:17:15 | ... + ... | calls.rb:17:11:17:11 | x | +| calls.rb:20:1:22:3 | call to foo | calls.rb:20:1:22:3 | self | +| calls.rb:21:3:21:7 | ... + ... | calls.rb:21:3:21:3 | x | +| calls.rb:25:1:27:3 | call to bar | calls.rb:25:1:25:3 | 123 | +| calls.rb:26:3:26:7 | ... + ... | calls.rb:26:3:26:3 | x | +| calls.rb:46:1:46:3 | call to foo | calls.rb:46:1:46:3 | self | +| calls.rb:47:1:47:6 | call to foo | calls.rb:47:1:47:1 | X | +| calls.rb:50:2:50:4 | call to foo | calls.rb:50:2:50:4 | self | +| calls.rb:51:2:51:7 | call to foo | calls.rb:51:2:51:2 | X | +| calls.rb:54:1:54:14 | call to some_func | calls.rb:54:1:54:14 | self | +| calls.rb:54:11:54:13 | call to foo | calls.rb:54:11:54:13 | self | +| calls.rb:55:1:55:17 | call to some_func | calls.rb:55:1:55:17 | self | +| calls.rb:55:11:55:16 | call to foo | calls.rb:55:11:55:11 | X | +| calls.rb:58:1:58:5 | call to [] | calls.rb:58:1:58:5 | Array | +| calls.rb:58:2:58:4 | call to foo | calls.rb:58:2:58:4 | self | +| calls.rb:59:1:59:8 | call to [] | calls.rb:59:1:59:8 | Array | +| calls.rb:59:2:59:7 | call to foo | calls.rb:59:2:59:2 | X | +| calls.rb:62:8:62:10 | call to foo | calls.rb:62:8:62:10 | self | +| calls.rb:63:8:63:13 | call to foo | calls.rb:63:8:63:8 | X | +| calls.rb:66:6:66:7 | ... + ... | calls.rb:66:1:66:4 | var1 | +| calls.rb:66:9:66:11 | call to bar | calls.rb:66:9:66:11 | self | +| calls.rb:67:6:67:7 | ... + ... | calls.rb:67:1:67:4 | var1 | +| calls.rb:67:9:67:14 | call to bar | calls.rb:67:9:67:9 | X | +| calls.rb:70:8:70:10 | call to foo | calls.rb:70:8:70:10 | self | +| calls.rb:70:13:70:18 | call to bar | calls.rb:70:13:70:13 | X | +| calls.rb:74:3:74:5 | call to foo | calls.rb:74:3:74:5 | self | +| calls.rb:75:3:75:8 | call to foo | calls.rb:75:3:75:3 | X | +| calls.rb:79:9:79:11 | call to foo | calls.rb:79:9:79:11 | self | +| calls.rb:79:14:79:19 | call to bar | calls.rb:79:14:79:14 | X | +| calls.rb:82:7:82:9 | call to foo | calls.rb:82:7:82:9 | self | +| calls.rb:82:12:82:17 | call to bar | calls.rb:82:12:82:12 | X | +| calls.rb:85:1:85:3 | call to foo | calls.rb:85:1:85:3 | self | +| calls.rb:85:1:85:12 | ... + ... | calls.rb:85:1:85:3 | call to foo | +| calls.rb:85:7:85:12 | call to bar | calls.rb:85:7:85:7 | X | +| calls.rb:88:1:88:4 | ! ... | calls.rb:88:2:88:4 | call to foo | +| calls.rb:88:2:88:4 | call to foo | calls.rb:88:2:88:4 | self | +| calls.rb:89:1:89:7 | ~ ... | calls.rb:89:2:89:7 | call to bar | +| calls.rb:89:2:89:7 | call to bar | calls.rb:89:2:89:2 | X | +| calls.rb:92:1:92:21 | call to foo | calls.rb:92:1:92:21 | self | +| calls.rb:92:9:92:11 | call to bar | calls.rb:92:9:92:11 | self | +| calls.rb:92:14:92:19 | call to baz | calls.rb:92:14:92:14 | X | +| calls.rb:95:1:98:3 | call to foo | calls.rb:95:1:98:3 | self | +| calls.rb:96:3:96:5 | call to bar | calls.rb:96:3:96:5 | self | +| calls.rb:97:3:97:8 | call to baz | calls.rb:97:3:97:3 | X | +| calls.rb:101:1:101:3 | call to foo | calls.rb:101:1:101:3 | self | +| calls.rb:101:1:101:9 | call to bar | calls.rb:101:1:101:3 | call to foo | +| calls.rb:102:1:102:3 | call to bar | calls.rb:102:1:102:3 | self | +| calls.rb:102:1:102:9 | call to baz | calls.rb:102:1:102:3 | call to bar | +| calls.rb:106:6:106:8 | call to foo | calls.rb:106:6:106:8 | self | +| calls.rb:107:6:107:8 | call to bar | calls.rb:107:6:107:8 | self | +| calls.rb:108:3:108:5 | call to baz | calls.rb:108:3:108:5 | self | +| calls.rb:110:6:110:11 | call to foo | calls.rb:110:6:110:6 | X | +| calls.rb:111:6:111:11 | call to bar | calls.rb:111:6:111:6 | X | +| calls.rb:112:3:112:8 | call to baz | calls.rb:112:3:112:3 | X | +| calls.rb:117:3:117:5 | call to foo | calls.rb:117:3:117:5 | self | +| calls.rb:118:3:118:8 | call to bar | calls.rb:118:3:118:3 | X | +| calls.rb:122:17:122:19 | call to foo | calls.rb:122:17:122:19 | self | +| calls.rb:124:18:124:23 | call to foo | calls.rb:124:18:124:18 | X | +| calls.rb:128:10:128:12 | call to foo | calls.rb:128:10:128:12 | self | +| calls.rb:129:3:129:5 | call to bar | calls.rb:129:3:129:5 | self | +| calls.rb:131:10:131:15 | call to foo | calls.rb:131:10:131:10 | X | +| calls.rb:132:3:132:8 | call to bar | calls.rb:132:3:132:3 | X | +| calls.rb:137:3:137:5 | call to foo | calls.rb:137:3:137:5 | self | +| calls.rb:138:3:138:8 | call to bar | calls.rb:138:3:138:3 | X | +| calls.rb:142:5:142:7 | call to foo | calls.rb:142:5:142:7 | self | +| calls.rb:143:3:143:5 | call to bar | calls.rb:143:3:143:5 | self | +| calls.rb:144:3:144:8 | call to baz | calls.rb:144:3:144:3 | X | +| calls.rb:148:40:148:42 | call to foo | calls.rb:148:40:148:42 | self | +| calls.rb:150:41:150:46 | call to foo | calls.rb:150:41:150:41 | X | +| calls.rb:154:40:154:42 | call to foo | calls.rb:154:40:154:42 | self | +| calls.rb:156:41:156:46 | call to foo | calls.rb:156:41:156:41 | X | +| calls.rb:161:3:161:5 | call to foo | calls.rb:161:3:161:5 | self | +| calls.rb:162:3:162:8 | call to bar | calls.rb:162:3:162:3 | X | +| calls.rb:166:1:166:3 | call to foo | calls.rb:166:1:166:3 | self | +| calls.rb:166:7:166:9 | call to bar | calls.rb:166:7:166:9 | self | +| calls.rb:166:13:166:15 | call to baz | calls.rb:166:13:166:15 | self | +| calls.rb:167:1:167:6 | call to foo | calls.rb:167:1:167:1 | X | +| calls.rb:167:10:167:15 | call to bar | calls.rb:167:10:167:10 | X | +| calls.rb:167:19:167:24 | call to baz | calls.rb:167:19:167:19 | X | +| calls.rb:170:4:170:6 | call to foo | calls.rb:170:4:170:6 | self | +| calls.rb:171:3:171:8 | call to wibble | calls.rb:171:3:171:8 | self | +| calls.rb:172:7:172:9 | call to bar | calls.rb:172:7:172:9 | self | +| calls.rb:173:3:173:8 | call to wobble | calls.rb:173:3:173:8 | self | +| calls.rb:175:3:175:8 | call to wabble | calls.rb:175:3:175:8 | self | +| calls.rb:177:4:177:9 | call to foo | calls.rb:177:4:177:4 | X | +| calls.rb:178:3:178:11 | call to wibble | calls.rb:178:3:178:3 | X | +| calls.rb:179:7:179:12 | call to bar | calls.rb:179:7:179:7 | X | +| calls.rb:180:3:180:11 | call to wobble | calls.rb:180:3:180:3 | X | +| calls.rb:182:3:182:11 | call to wabble | calls.rb:182:3:182:3 | X | +| calls.rb:186:1:186:3 | call to bar | calls.rb:186:1:186:3 | self | +| calls.rb:186:8:186:10 | call to foo | calls.rb:186:8:186:10 | self | +| calls.rb:187:1:187:6 | call to bar | calls.rb:187:1:187:1 | X | +| calls.rb:187:11:187:16 | call to foo | calls.rb:187:11:187:11 | X | +| calls.rb:190:8:190:10 | call to foo | calls.rb:190:8:190:10 | self | +| calls.rb:191:3:191:5 | call to bar | calls.rb:191:3:191:5 | self | +| calls.rb:193:8:193:13 | call to foo | calls.rb:193:8:193:8 | X | +| calls.rb:194:3:194:8 | call to bar | calls.rb:194:3:194:3 | X | +| calls.rb:198:1:198:3 | call to bar | calls.rb:198:1:198:3 | self | +| calls.rb:198:12:198:14 | call to foo | calls.rb:198:12:198:14 | self | +| calls.rb:199:1:199:6 | call to bar | calls.rb:199:1:199:1 | X | +| calls.rb:199:15:199:20 | call to foo | calls.rb:199:15:199:15 | X | +| calls.rb:202:7:202:9 | call to foo | calls.rb:202:7:202:9 | self | +| calls.rb:203:3:203:5 | call to bar | calls.rb:203:3:203:5 | self | +| calls.rb:205:7:205:12 | call to foo | calls.rb:205:7:205:7 | X | +| calls.rb:206:3:206:8 | call to bar | calls.rb:206:3:206:3 | X | +| calls.rb:210:1:210:3 | call to bar | calls.rb:210:1:210:3 | self | +| calls.rb:210:11:210:13 | call to foo | calls.rb:210:11:210:13 | self | +| calls.rb:211:1:211:6 | call to bar | calls.rb:211:1:211:1 | X | +| calls.rb:211:14:211:19 | call to foo | calls.rb:211:14:211:14 | X | +| calls.rb:214:7:214:9 | call to foo | calls.rb:214:7:214:9 | self | +| calls.rb:215:3:215:5 | call to bar | calls.rb:215:3:215:5 | self | +| calls.rb:217:7:217:12 | call to foo | calls.rb:217:7:217:7 | X | +| calls.rb:218:3:218:8 | call to bar | calls.rb:218:3:218:3 | X | +| calls.rb:222:1:222:3 | call to bar | calls.rb:222:1:222:3 | self | +| calls.rb:222:11:222:13 | call to foo | calls.rb:222:11:222:13 | self | +| calls.rb:223:1:223:6 | call to bar | calls.rb:223:1:223:1 | X | +| calls.rb:223:14:223:19 | call to foo | calls.rb:223:14:223:14 | X | +| calls.rb:226:10:226:12 | call to bar | calls.rb:226:10:226:12 | self | +| calls.rb:227:3:227:5 | call to baz | calls.rb:227:3:227:5 | self | +| calls.rb:229:10:229:15 | call to bar | calls.rb:229:10:229:10 | X | +| calls.rb:230:3:230:8 | call to baz | calls.rb:230:3:230:3 | X | +| calls.rb:234:1:234:3 | call to foo | calls.rb:234:1:234:3 | self | +| calls.rb:234:1:234:8 | ...[...] | calls.rb:234:1:234:3 | call to foo | +| calls.rb:234:5:234:7 | call to bar | calls.rb:234:5:234:7 | self | +| calls.rb:235:1:235:6 | call to foo | calls.rb:235:1:235:1 | X | +| calls.rb:235:1:235:14 | ...[...] | calls.rb:235:1:235:6 | call to foo | +| calls.rb:235:8:235:13 | call to bar | calls.rb:235:8:235:8 | X | +| calls.rb:238:8:238:10 | call to bar | calls.rb:238:8:238:10 | self | +| calls.rb:238:15:238:20 | call to baz | calls.rb:238:15:238:15 | X | +| calls.rb:241:1:241:3 | call to foo | calls.rb:241:1:241:3 | self | +| calls.rb:242:1:242:6 | call to foo | calls.rb:242:1:242:1 | X | +| calls.rb:245:1:245:3 | call to foo | calls.rb:245:1:245:3 | self | +| calls.rb:245:6:245:8 | call to bar | calls.rb:245:6:245:8 | self | +| calls.rb:246:1:246:6 | call to foo | calls.rb:246:1:246:1 | X | +| calls.rb:246:9:246:14 | call to bar | calls.rb:246:9:246:9 | X | +| calls.rb:249:3:249:5 | call to foo | calls.rb:249:3:249:5 | self | +| calls.rb:249:10:249:12 | call to bar | calls.rb:249:10:249:12 | self | +| calls.rb:249:15:249:20 | call to foo | calls.rb:249:15:249:15 | X | +| calls.rb:249:25:249:30 | call to bar | calls.rb:249:25:249:25 | X | +| calls.rb:253:8:253:10 | call to foo | calls.rb:253:8:253:10 | self | +| calls.rb:254:8:254:10 | call to bar | calls.rb:254:8:254:10 | self | +| calls.rb:257:8:257:13 | call to foo | calls.rb:257:8:257:8 | X | +| calls.rb:258:8:258:13 | call to bar | calls.rb:258:8:258:8 | X | +| calls.rb:262:1:262:3 | call to foo | calls.rb:262:1:262:3 | self | +| calls.rb:262:12:262:14 | call to bar | calls.rb:262:12:262:14 | self | +| calls.rb:263:1:263:6 | call to foo | calls.rb:263:1:263:1 | X | +| calls.rb:263:15:263:20 | call to bar | calls.rb:263:15:263:15 | X | +| calls.rb:266:1:266:9 | call to foo | calls.rb:266:1:266:9 | self | +| calls.rb:266:6:266:8 | call to bar | calls.rb:266:6:266:8 | self | +| calls.rb:267:1:267:12 | call to foo | calls.rb:267:1:267:12 | self | +| calls.rb:267:6:267:11 | call to bar | calls.rb:267:6:267:6 | X | +| calls.rb:270:1:270:9 | call to foo | calls.rb:270:1:270:9 | self | +| calls.rb:270:5:270:8 | * ... | calls.rb:270:6:270:8 | call to bar | +| calls.rb:270:6:270:8 | call to bar | calls.rb:270:6:270:8 | self | +| calls.rb:271:1:271:12 | call to foo | calls.rb:271:1:271:12 | self | +| calls.rb:271:5:271:11 | * ... | calls.rb:271:6:271:11 | call to bar | +| calls.rb:271:6:271:11 | call to bar | calls.rb:271:6:271:6 | X | +| calls.rb:274:1:274:10 | call to foo | calls.rb:274:1:274:10 | self | +| calls.rb:274:5:274:9 | ** ... | calls.rb:274:7:274:9 | call to bar | +| calls.rb:274:7:274:9 | call to bar | calls.rb:274:7:274:9 | self | +| calls.rb:275:1:275:13 | call to foo | calls.rb:275:1:275:13 | self | +| calls.rb:275:5:275:12 | ** ... | calls.rb:275:7:275:12 | call to bar | +| calls.rb:275:7:275:12 | call to bar | calls.rb:275:7:275:7 | X | +| calls.rb:278:1:278:14 | call to foo | calls.rb:278:1:278:14 | self | +| calls.rb:278:11:278:13 | call to bar | calls.rb:278:11:278:13 | self | +| calls.rb:279:1:279:17 | call to foo | calls.rb:279:1:279:17 | self | +| calls.rb:279:11:279:16 | call to bar | calls.rb:279:11:279:11 | X | +| calls.rb:290:17:290:21 | ... + ... | calls.rb:290:17:290:17 | x | +| calls.rb:291:18:291:22 | ... * ... | calls.rb:291:18:291:18 | x | +| calls.rb:292:22:292:28 | ... + ... | calls.rb:292:22:292:22 | x | +| calls.rb:293:23:293:29 | ... + ... | calls.rb:293:23:293:23 | x | +| calls.rb:303:5:303:7 | call to foo | calls.rb:303:5:303:7 | self | +| calls.rb:303:5:303:13 | call to super | calls.rb:303:5:303:7 | call to foo | +| calls.rb:304:5:304:14 | call to super | calls.rb:304:5:304:8 | self | +| calls.rb:305:5:305:15 | call to super | calls.rb:305:5:305:9 | call to super | +| calls.rb:310:1:310:3 | call to foo | calls.rb:310:1:310:3 | self | +| calls.rb:310:1:310:6 | call to call | calls.rb:310:1:310:3 | call to foo | +| calls.rb:311:1:311:3 | call to foo | calls.rb:311:1:311:3 | self | +| calls.rb:311:1:311:7 | call to call | calls.rb:311:1:311:3 | call to foo | +| calls.rb:314:1:314:8 | call to foo | calls.rb:314:1:314:4 | self | +| calls.rb:314:1:314:8 | call to foo= | calls.rb:314:1:314:4 | self | +| calls.rb:315:1:315:3 | call to foo | calls.rb:315:1:315:3 | self | +| calls.rb:315:1:315:6 | ...[...] | calls.rb:315:1:315:3 | call to foo | +| calls.rb:315:1:315:6 | call to []= | calls.rb:315:1:315:3 | call to foo | +| calls.rb:316:1:316:8 | call to [] | calls.rb:316:1:316:8 | __synth__0 | +| calls.rb:316:1:316:8 | call to foo | calls.rb:316:1:316:4 | self | +| calls.rb:316:1:316:8 | call to foo= | calls.rb:316:1:316:4 | self | +| calls.rb:316:12:316:19 | call to [] | calls.rb:316:12:316:19 | __synth__0 | +| calls.rb:316:12:316:19 | call to bar | calls.rb:316:12:316:15 | self | +| calls.rb:316:12:316:19 | call to bar= | calls.rb:316:12:316:15 | self | +| calls.rb:316:22:316:24 | call to foo | calls.rb:316:22:316:24 | self | +| calls.rb:316:22:316:27 | ...[...] | calls.rb:316:22:316:24 | call to foo | +| calls.rb:316:22:316:27 | call to [] | calls.rb:316:22:316:27 | __synth__0 | +| calls.rb:316:22:316:27 | call to []= | calls.rb:316:22:316:24 | call to foo | +| calls.rb:316:31:316:42 | * ... | calls.rb:316:31:316:42 | [...] | +| calls.rb:316:31:316:42 | call to [] | calls.rb:316:31:316:42 | Array | +| calls.rb:317:1:317:1 | call to [] | calls.rb:317:1:317:1 | __synth__0 | +| calls.rb:317:5:317:7 | call to foo | calls.rb:317:5:317:7 | self | +| calls.rb:317:5:317:10 | ...[...] | calls.rb:317:5:317:7 | call to foo | +| calls.rb:317:5:317:10 | call to [] | calls.rb:317:5:317:10 | __synth__0 | +| calls.rb:317:5:317:10 | call to []= | calls.rb:317:5:317:7 | call to foo | +| calls.rb:317:14:317:22 | * ... | calls.rb:317:14:317:22 | [...] | +| calls.rb:317:14:317:22 | call to [] | calls.rb:317:14:317:22 | Array | +| calls.rb:318:1:318:10 | call to count | calls.rb:318:1:318:4 | __synth__0 | +| calls.rb:318:1:318:10 | call to count | calls.rb:318:1:318:4 | self | +| calls.rb:318:1:318:10 | call to count= | calls.rb:318:1:318:4 | __synth__0 | +| calls.rb:318:12:318:13 | ... + ... | calls.rb:318:1:318:10 | call to count | +| calls.rb:319:1:319:3 | call to foo | calls.rb:319:1:319:3 | self | +| calls.rb:319:1:319:6 | ...[...] | calls.rb:319:1:319:3 | call to foo | +| calls.rb:319:1:319:6 | call to [] | calls.rb:319:1:319:3 | __synth__0 | +| calls.rb:319:1:319:6 | call to []= | calls.rb:319:1:319:3 | __synth__0 | +| calls.rb:319:8:319:9 | ... + ... | calls.rb:319:1:319:6 | call to [] | +| calls.rb:320:1:320:3 | call to foo | calls.rb:320:1:320:3 | self | +| calls.rb:320:1:320:7 | call to bar | calls.rb:320:1:320:3 | call to foo | +| calls.rb:320:1:320:32 | ...[...] | calls.rb:320:1:320:7 | call to bar | +| calls.rb:320:1:320:32 | call to [] | calls.rb:320:1:320:7 | __synth__0 | +| calls.rb:320:1:320:32 | call to []= | calls.rb:320:1:320:7 | __synth__0 | +| calls.rb:320:12:320:14 | call to foo | calls.rb:320:12:320:14 | self | +| calls.rb:320:12:320:18 | call to baz | calls.rb:320:12:320:14 | call to foo | +| calls.rb:320:21:320:23 | call to foo | calls.rb:320:21:320:23 | self | +| calls.rb:320:21:320:27 | call to boo | calls.rb:320:21:320:23 | call to foo | +| calls.rb:320:21:320:31 | ... + ... | calls.rb:320:21:320:27 | call to boo | +| calls.rb:320:34:320:35 | ... * ... | calls.rb:320:1:320:32 | call to [] | +| calls.rb:323:11:323:13 | call to bar | calls.rb:323:11:323:13 | self | +| calls.rb:324:13:324:15 | call to bar | calls.rb:324:13:324:15 | self | +| calls.rb:325:14:325:16 | call to bar | calls.rb:325:14:325:16 | self | +| calls.rb:326:18:326:20 | call to bar | calls.rb:326:18:326:20 | self | +| calls.rb:327:22:327:24 | call to bar | calls.rb:327:22:327:24 | self | +| calls.rb:328:13:328:15 | call to bar | calls.rb:328:13:328:15 | self | +| calls.rb:328:25:328:37 | call to print | calls.rb:328:25:328:37 | self | +| calls.rb:336:3:336:13 | call to bar | calls.rb:336:3:336:13 | self | +callsWithBlock +| calls.rb:17:1:17:17 | call to foo | calls.rb:17:5:17:17 | { ... } | +| calls.rb:20:1:22:3 | call to foo | calls.rb:20:5:22:3 | do ... end | +| calls.rb:25:1:27:3 | call to bar | calls.rb:25:16:27:3 | do ... end | +| calls.rb:92:1:92:21 | call to foo | calls.rb:92:7:92:21 | { ... } | +| calls.rb:95:1:98:3 | call to foo | calls.rb:95:7:98:3 | do ... end | +| calls.rb:290:5:290:23 | call to super | calls.rb:290:11:290:23 | { ... } | +| calls.rb:291:5:291:26 | call to super | calls.rb:291:11:291:26 | do ... end | +| calls.rb:292:5:292:30 | call to super | calls.rb:292:16:292:30 | { ... } | +| calls.rb:293:5:293:33 | call to super | calls.rb:293:16:293:33 | do ... end | +yieldCalls +| calls.rb:31:3:31:7 | yield ... | +| calls.rb:36:3:36:16 | yield ... | +superCalls +| calls.rb:286:5:286:9 | call to super | +| calls.rb:287:5:287:11 | call to super | +| calls.rb:288:5:288:16 | call to super | +| calls.rb:289:5:289:17 | call to super | +| calls.rb:290:5:290:23 | call to super | +| calls.rb:291:5:291:26 | call to super | +| calls.rb:292:5:292:30 | call to super | +| calls.rb:293:5:293:33 | call to super | +| calls.rb:305:5:305:9 | call to super | +| calls.rb:332:3:332:12 | call to super | +superCallsWithArguments +| calls.rb:288:5:288:16 | call to super | 0 | calls.rb:288:11:288:16 | "blah" | +| calls.rb:289:5:289:17 | call to super | 0 | calls.rb:289:11:289:11 | 1 | +| calls.rb:289:5:289:17 | call to super | 1 | calls.rb:289:14:289:14 | 2 | +| calls.rb:289:5:289:17 | call to super | 2 | calls.rb:289:17:289:17 | 3 | +| calls.rb:292:5:292:30 | call to super | 0 | calls.rb:292:11:292:11 | 4 | +| calls.rb:292:5:292:30 | call to super | 1 | calls.rb:292:14:292:14 | 5 | +| calls.rb:293:5:293:33 | call to super | 0 | calls.rb:293:11:293:11 | 6 | +| calls.rb:293:5:293:33 | call to super | 1 | calls.rb:293:14:293:14 | 7 | +| calls.rb:332:3:332:12 | call to super | 0 | calls.rb:332:9:332:11 | ... | +superCallsWithBlock +| calls.rb:290:5:290:23 | call to super | calls.rb:290:11:290:23 | { ... } | +| calls.rb:291:5:291:26 | call to super | calls.rb:291:11:291:26 | do ... end | +| calls.rb:292:5:292:30 | call to super | calls.rb:292:16:292:30 | { ... } | +| calls.rb:293:5:293:33 | call to super | calls.rb:293:16:293:33 | do ... end | +setterCalls +| calls.rb:314:1:314:8 | call to foo= | +| calls.rb:315:1:315:6 | call to []= | +| calls.rb:316:1:316:8 | call to foo= | +| calls.rb:316:12:316:19 | call to bar= | +| calls.rb:316:22:316:27 | call to []= | +| calls.rb:317:5:317:10 | call to []= | +| calls.rb:318:1:318:10 | call to count= | +| calls.rb:319:1:319:6 | call to []= | +| calls.rb:320:1:320:32 | call to []= | diff --git a/ruby/ql/test/library-tests/ast/calls/calls.ql b/ruby/ql/test/library-tests/ast/calls/calls.ql new file mode 100644 index 00000000000..4d244c2cf4c --- /dev/null +++ b/ruby/ql/test/library-tests/ast/calls/calls.ql @@ -0,0 +1,33 @@ +import ruby + +private string getMethodName(Call c) { + result = c.(MethodCall).getMethodName() + or + not c instanceof MethodCall and result = "(none)" +} + +query predicate callsWithNoReceiverArgumentsOrBlock(Call c, string name) { + name = getMethodName(c) and + not exists(c.(MethodCall).getReceiver()) and + not exists(c.getAnArgument()) and + not exists(c.(MethodCall).getBlock()) +} + +query predicate callsWithArguments(Call c, string name, int n, Expr argN) { + name = getMethodName(c) and + argN = c.getArgument(n) +} + +query predicate callsWithReceiver(MethodCall c, Expr rcv) { rcv = c.getReceiver() } + +query predicate callsWithBlock(MethodCall c, Block b) { b = c.getBlock() } + +query predicate yieldCalls(YieldCall c) { any() } + +query predicate superCalls(SuperCall c) { any() } + +query predicate superCallsWithArguments(SuperCall c, int n, Expr argN) { argN = c.getArgument(n) } + +query predicate superCallsWithBlock(SuperCall c, Block b) { b = c.getBlock() } + +query predicate setterCalls(SetterMethodCall c) { any() } diff --git a/ruby/ql/test/library-tests/ast/calls/calls.rb b/ruby/ql/test/library-tests/ast/calls/calls.rb new file mode 100644 index 00000000000..f4bb18d9fe5 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/calls/calls.rb @@ -0,0 +1,337 @@ +# call with no receiver, arguments, or block +foo() + +# call whose name is a scope resolution +Foo::bar() + +# call whose name is a global scope resolution +::bar() + +# call with a receiver, no arguments or block +123.bar + +# call with arguments +foo 0, 1, 2 + +# call with curly brace block +foo { |x| x + 1 } + +# call with do block +foo do |x| + x + 1 +end + +# call with receiver, arguments, and a block +123.bar('foo') do |x| + x + 1 +end + +# a yield call +def method_that_yields + yield +end + +# a yield call with arguments +def another_method_that_yields + yield 100, 200 +end + +# ------------------------------------------------------------------------------ +# Calls without parentheses or arguments are parsed by tree-sitter simply as +# `identifier` nodes (or `scope_resolution` nodes whose `name` field is an +# `identifier), so here we test that our AST library correctly represents them +# as calls in all the following contexts. + +# root level (child of program) +foo +X::foo + +# in a parenthesized statement +(foo) +(X::foo) + +# in an argument list +some_func(foo) +some_func(X::foo) + +# in an array +[foo] +[X::foo] + +# RHS of an assignment +var1 = foo +var1 = X::foo + +# RHS an operator assignment +var1 += bar +var1 += X::bar + +# RHS assignment list +var1 = foo, X::bar + +# in a begin-end block +begin + foo + X::foo +end + +# in a BEGIN block +BEGIN { foo; X::bar } + +# in an END block +END { foo; X::bar } + +# both operands of a binary operation +foo + X::bar + +# unary operand +!foo +~X::bar + +# in a curly brace block +foo() { bar; X::baz } + +# in a do-end block +foo() do + bar + X::baz +end + +# the receiver in a call can itself be a call +foo.bar() +bar.baz() + +# the value for a case expr +# and the when pattern and body +case foo +when bar + baz +end +case X::foo +when X::bar + X::baz +end + +# in a class definition +class MyClass + foo + X::bar +end + +# in a superclass +class MyClass < foo +end +class MyClass2 < X::foo +end + +# in a singleton class value or body +class << foo + bar +end +class << X::foo + X::bar +end + +# in a method body +def some_method + foo + X::bar +end + +# in a singleton method object or body +def foo.some_method + bar + X::baz +end + +# in the default value for a keyword parameter +def method_with_keyword_param(keyword: foo) +end +def method_with_keyword_param2(keyword: X::foo) +end + +# in the default value for an optional parameter +def method_with_optional_param(param = foo) +end +def method_with_optional_param2(param = X::foo) +end + +# in a module +module SomeModule + foo + X::bar +end + +# ternary if: condition, consequence, and alternative can all be calls +foo ? bar : baz +X::foo ? X::bar : X::baz + +# if/elsif/else conditions and bodies +if foo + wibble +elsif bar + wobble +else + wabble +end +if X::foo + X::wibble +elsif X::bar + X::wobble +else + X::wabble +end + +# if-modifier condition/body +bar if foo +X::bar if X::foo + +# unless condition/body +unless foo + bar +end +unless X::foo + X::bar +end + +# unless-modifier condition/body +bar unless foo +X::bar unless X::foo + +# while loop condition/body +while foo do + bar +end +while X::foo do + X::bar +end + +# while-modifier loop condition/body +bar while foo +X::bar while X::foo + +# until loop condition/body +until foo do + bar +end +until X::foo do + X::bar +end + +# until-modifier loop condition/body +bar until foo +X::bar until X::foo + +# the collection being iterated over in a for loop, and the body +for x in bar + baz +end +for x in X::bar + X::baz +end + +# in an array indexing operation, both the object and the index can be calls +foo[bar] +X::foo[X::bar] + +# interpolation +"foo-#{bar}-#{X::baz}" + +# the scope in a scope resolution +foo::Bar +X::foo::Bar + +# in a range +foo..bar +X::foo..X::bar + +# the key/value in a hash pair +{ foo => bar, X::foo => X::bar } + +# rescue exceptions and ensure +begin +rescue foo +ensure bar +end +begin +rescue X::foo +ensure X::bar +end + +# rescue-modifier body and handler +foo rescue bar +X::foo rescue X::bar + +# block argument +foo(&bar) +foo(&X::bar) + +# splat argument +foo(*bar) +foo(*X::bar) + +# hash-splat argument +foo(**bar) +foo(**X::bar) + +# the value in a keyword argument +foo(blah: bar) +foo(blah: X::bar) + +# ------------------------------------------------------------------------------ +# calls to `super` + +class MyClass + def my_method + super + super() + super 'blah' + super 1, 2, 3 + super { |x| x + 1 } + super do |x| x * 2 end + super 4, 5 { |x| x + 100 } + super 6, 7 do |x| x + 200 end + end +end + +# ------------------------------------------------------------------------------ +# calls to methods simply named `super`, i.e. *not* calls to the same method in +# a parent classs, so these should be Call but not SuperCall + +class AnotherClass + def another_method + foo.super + self.super + super.super # we expect the receiver to be a SuperCall, while the outer call should not (it's just a regular Call) + end +end + +# calls without method name +foo.() +foo.(1) + +# setter calls +self.foo = 10 +foo[0] = 10 +self.foo, *self.bar, foo[4] = [1, 2, 3, 4] +a, *foo[5] = [1, 2, 3] +self.count += 1 +foo[0] += 1 +foo.bar[0, foo.baz, foo.boo + 1] *= 2 + +# endless method definitions +def foo = bar +def foo() = bar +def foo(x) = bar +def Object.foo = bar +def Object.foo (x) = bar +def foo() = bar rescue (print "error") + +# forward parameter and forwarded arguments +def foo(...) + super(...) +end + +def foo(a, b, ...) + bar(b, ...) +end diff --git a/ruby/ql/test/library-tests/ast/constants/constants.expected b/ruby/ql/test/library-tests/ast/constants/constants.expected new file mode 100644 index 00000000000..bbd289cd365 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/constants/constants.expected @@ -0,0 +1,70 @@ +constantAccess +| constants.rb:1:1:15:3 | ModuleA | write | ModuleA | ModuleDeclaration | +| constants.rb:2:5:4:7 | ClassA | write | ClassA | ClassDeclaration | +| constants.rb:3:9:3:15 | CONST_A | write | CONST_A | ConstantAssignment | +| constants.rb:6:5:6:11 | CONST_B | write | CONST_B | ConstantAssignment | +| constants.rb:8:5:14:7 | ModuleB | write | ModuleB | ModuleDeclaration | +| constants.rb:9:9:10:11 | ClassB | write | ClassB | ClassDeclaration | +| constants.rb:9:24:9:27 | Base | read | Base | ConstantReadAccess | +| constants.rb:12:9:13:11 | ClassC | write | ClassC | ClassDeclaration | +| constants.rb:12:24:12:24 | X | read | X | ConstantReadAccess | +| constants.rb:12:24:12:27 | Y | read | Y | ConstantReadAccess | +| constants.rb:12:24:12:30 | Z | read | Z | ConstantReadAccess | +| constants.rb:17:1:17:8 | GREETING | write | GREETING | ConstantAssignment | +| constants.rb:17:22:17:28 | ModuleA | read | ModuleA | ConstantReadAccess | +| constants.rb:17:22:17:36 | ClassA | read | ClassA | ConstantReadAccess | +| constants.rb:17:22:17:45 | CONST_A | read | CONST_A | ConstantReadAccess | +| constants.rb:17:49:17:55 | ModuleA | read | ModuleA | ConstantReadAccess | +| constants.rb:17:49:17:64 | CONST_B | read | CONST_B | ConstantReadAccess | +| constants.rb:20:5:20:9 | Names | write | Names | ConstantAssignment | +| constants.rb:20:13:20:37 | Array | read | Array | ConstantReadAccess | +| constants.rb:22:5:22:9 | Names | read | Names | ConstantReadAccess | +| constants.rb:23:18:23:25 | GREETING | read | GREETING | ConstantReadAccess | +| constants.rb:31:1:32:3 | ClassD | write | ClassD | ClassDeclaration | +| constants.rb:31:7:31:13 | ModuleA | read | ModuleA | ConstantReadAccess | +| constants.rb:31:25:31:31 | ModuleA | read | ModuleA | ConstantReadAccess | +| constants.rb:31:25:31:39 | ClassA | read | ClassA | ConstantReadAccess | +| constants.rb:34:1:35:3 | ModuleC | write | ModuleC | ModuleDeclaration | +| constants.rb:34:8:34:14 | ModuleA | read | ModuleA | ConstantReadAccess | +| constants.rb:37:1:37:7 | ModuleA | read | ModuleA | ConstantReadAccess | +| constants.rb:37:1:37:16 | ModuleB | read | ModuleB | ConstantReadAccess | +| constants.rb:37:1:37:26 | MAX_SIZE | write | MAX_SIZE | ConstantAssignment | +| constants.rb:39:6:39:12 | ModuleA | read | ModuleA | ConstantReadAccess | +| constants.rb:39:6:39:21 | ModuleB | read | ModuleB | ConstantReadAccess | +| constants.rb:39:6:39:31 | MAX_SIZE | read | MAX_SIZE | ConstantReadAccess | +| constants.rb:41:6:41:13 | GREETING | read | GREETING | ConstantReadAccess | +| constants.rb:42:6:42:15 | GREETING | read | GREETING | ConstantReadAccess | +getConst +| constants.rb:1:1:15:3 | ModuleA | CONST_B | constants.rb:6:15:6:23 | "const_b" | +| constants.rb:2:5:4:7 | ModuleA::ClassA | CONST_A | constants.rb:3:19:3:27 | "const_a" | +| file://:0:0:0:0 | Object | GREETING | constants.rb:17:12:17:64 | ... + ... | +lookupConst +| constants.rb:1:1:15:3 | ModuleA | CONST_B | constants.rb:6:15:6:23 | "const_b" | +| constants.rb:2:5:4:7 | ModuleA::ClassA | CONST_A | constants.rb:3:19:3:27 | "const_a" | +| constants.rb:2:5:4:7 | ModuleA::ClassA | GREETING | constants.rb:17:12:17:64 | ... + ... | +| constants.rb:8:5:14:7 | ModuleA::ModuleB | MAX_SIZE | constants.rb:37:30:37:33 | 1024 | +| constants.rb:9:9:10:11 | ModuleA::ModuleB::ClassB | GREETING | constants.rb:17:12:17:64 | ... + ... | +| constants.rb:12:9:13:11 | ModuleA::ModuleB::ClassC | GREETING | constants.rb:17:12:17:64 | ... + ... | +| constants.rb:31:1:32:3 | ModuleA::ClassD | CONST_A | constants.rb:3:19:3:27 | "const_a" | +| constants.rb:31:1:32:3 | ModuleA::ClassD | GREETING | constants.rb:17:12:17:64 | ... + ... | +| file://:0:0:0:0 | Object | GREETING | constants.rb:17:12:17:64 | ... + ... | +constantValue +| constants.rb:17:22:17:45 | CONST_A | constants.rb:3:19:3:27 | "const_a" | +| constants.rb:17:49:17:64 | CONST_B | constants.rb:6:15:6:23 | "const_b" | +| constants.rb:23:18:23:25 | GREETING | constants.rb:17:12:17:64 | ... + ... | +| constants.rb:39:6:39:31 | MAX_SIZE | constants.rb:37:30:37:33 | 1024 | +| constants.rb:41:6:41:13 | GREETING | constants.rb:17:12:17:64 | ... + ... | +| constants.rb:42:6:42:15 | GREETING | constants.rb:17:12:17:64 | ... + ... | +constantWriteAccessQualifiedName +| constants.rb:1:1:15:3 | ModuleA | ModuleA | +| constants.rb:2:5:4:7 | ClassA | ModuleA::ClassA | +| constants.rb:3:9:3:15 | CONST_A | ModuleA::ClassA::CONST_A | +| constants.rb:6:5:6:11 | CONST_B | ModuleA::CONST_B | +| constants.rb:8:5:14:7 | ModuleB | ModuleA::ModuleB | +| constants.rb:9:9:10:11 | ClassB | ModuleA::ModuleB::ClassB | +| constants.rb:12:9:13:11 | ClassC | ModuleA::ModuleB::ClassC | +| constants.rb:17:1:17:8 | GREETING | GREETING | +| constants.rb:20:5:20:9 | Names | Names | +| constants.rb:31:1:32:3 | ClassD | ClassD | +| constants.rb:34:1:35:3 | ModuleC | ModuleC | +| constants.rb:37:1:37:26 | MAX_SIZE | MAX_SIZE | diff --git a/ruby/ql/test/library-tests/ast/constants/constants.ql b/ruby/ql/test/library-tests/ast/constants/constants.ql new file mode 100644 index 00000000000..417b193bd38 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/constants/constants.ql @@ -0,0 +1,22 @@ +import ruby +import codeql.ruby.ast.internal.Module as M + +query predicate constantAccess(ConstantAccess a, string kind, string name, string cls) { + ( + a instanceof ConstantReadAccess and kind = "read" + or + a instanceof ConstantWriteAccess and kind = "write" + ) and + name = a.getName() and + cls = a.getAPrimaryQlClass() +} + +query Expr getConst(Module m, string name) { result = M::ExposedForTestingOnly::getConst(m, name) } + +query Expr lookupConst(Module m, string name) { result = M::lookupConst(m, name) } + +query predicate constantValue(ConstantReadAccess a, Expr e) { e = a.getValue() } + +query predicate constantWriteAccessQualifiedName(ConstantWriteAccess w, string qualifiedName) { + w.getQualifiedName() = qualifiedName +} diff --git a/ruby/ql/test/library-tests/ast/constants/constants.rb b/ruby/ql/test/library-tests/ast/constants/constants.rb new file mode 100644 index 00000000000..00acddef9d1 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/constants/constants.rb @@ -0,0 +1,42 @@ +module ModuleA + class ClassA + CONST_A = "const_a" + end + + CONST_B = "const_b" + + module ModuleB + class ClassB < Base + end + + class ClassC < X::Y::Z + end + end +end + +GREETING = 'Hello' + ModuleA::ClassA::CONST_A + ModuleA::CONST_B + +def foo + Names = ['Vera', 'Chuck', 'Dave'] + + Names.each do |name| + puts "#{ GREETING } #{ name }" + end + + # A call to Kernel::Array; despite beginning with an upper-case character, + # we don't consider this to be a constant access. + Array('foo') +end + +class ModuleA::ClassD < ModuleA::ClassA +end + +module ModuleA::ModuleC +end + +ModuleA::ModuleB::MAX_SIZE = 1024 + +puts ModuleA::ModuleB::MAX_SIZE + +puts GREETING +puts ::GREETING diff --git a/ruby/ql/test/library-tests/ast/control/CaseExpr.expected b/ruby/ql/test/library-tests/ast/control/CaseExpr.expected new file mode 100644 index 00000000000..ff2b223690a --- /dev/null +++ b/ruby/ql/test/library-tests/ast/control/CaseExpr.expected @@ -0,0 +1,22 @@ +caseValues +| cases.rb:8:1:15:3 | case ... | cases.rb:8:6:8:6 | a | +caseNoValues +| cases.rb:18:1:22:3 | case ... | +caseElseBranches +| cases.rb:8:1:15:3 | case ... | cases.rb:13:1:14:7 | else ... | +caseNoElseBranches +| cases.rb:18:1:22:3 | case ... | +caseWhenBranches +| cases.rb:8:1:15:3 | case ... | cases.rb:9:1:10:7 | when ... | 0 | cases.rb:9:6:9:6 | b | cases.rb:9:7:10:7 | then ... | +| cases.rb:8:1:15:3 | case ... | cases.rb:11:1:12:7 | when ... | 0 | cases.rb:11:6:11:6 | c | cases.rb:11:10:12:7 | then ... | +| cases.rb:8:1:15:3 | case ... | cases.rb:11:1:12:7 | when ... | 1 | cases.rb:11:9:11:9 | d | cases.rb:11:10:12:7 | then ... | +| cases.rb:18:1:22:3 | case ... | cases.rb:19:1:19:19 | when ... | 0 | cases.rb:19:6:19:10 | ... > ... | cases.rb:19:13:19:19 | then ... | +| cases.rb:18:1:22:3 | case ... | cases.rb:20:1:20:19 | when ... | 0 | cases.rb:20:6:20:11 | ... == ... | cases.rb:20:13:20:19 | then ... | +| cases.rb:18:1:22:3 | case ... | cases.rb:21:1:21:19 | when ... | 0 | cases.rb:21:6:21:10 | ... < ... | cases.rb:21:13:21:19 | then ... | +caseAllBranches +| cases.rb:8:1:15:3 | case ... | 0 | cases.rb:9:1:10:7 | when ... | +| cases.rb:8:1:15:3 | case ... | 1 | cases.rb:11:1:12:7 | when ... | +| cases.rb:8:1:15:3 | case ... | 2 | cases.rb:13:1:14:7 | else ... | +| cases.rb:18:1:22:3 | case ... | 0 | cases.rb:19:1:19:19 | when ... | +| cases.rb:18:1:22:3 | case ... | 1 | cases.rb:20:1:20:19 | when ... | +| cases.rb:18:1:22:3 | case ... | 2 | cases.rb:21:1:21:19 | when ... | diff --git a/ruby/ql/test/library-tests/ast/control/CaseExpr.ql b/ruby/ql/test/library-tests/ast/control/CaseExpr.ql new file mode 100644 index 00000000000..eaef9359f55 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/control/CaseExpr.ql @@ -0,0 +1,19 @@ +import ruby + +query predicate caseValues(CaseExpr c, Expr value) { value = c.getValue() } + +query predicate caseNoValues(CaseExpr c) { not exists(c.getValue()) } + +query predicate caseElseBranches(CaseExpr c, StmtSequence elseBranch) { + elseBranch = c.getElseBranch() +} + +query predicate caseNoElseBranches(CaseExpr c) { not exists(c.getElseBranch()) } + +query predicate caseWhenBranches(CaseExpr c, WhenExpr when, int pIndex, Expr p, StmtSequence body) { + when = c.getAWhenBranch() and + p = when.getPattern(pIndex) and + body = when.getBody() +} + +query predicate caseAllBranches(CaseExpr c, int n, Expr branch) { branch = c.getBranch(n) } diff --git a/ruby/ql/test/library-tests/ast/control/ConditionalExpr.expected b/ruby/ql/test/library-tests/ast/control/ConditionalExpr.expected new file mode 100644 index 00000000000..3d78655dbbb --- /dev/null +++ b/ruby/ql/test/library-tests/ast/control/ConditionalExpr.expected @@ -0,0 +1,43 @@ +conditionalExprs +| conditionals.rb:10:1:12:3 | if ... | IfExpr | conditionals.rb:10:4:10:8 | ... > ... | conditionals.rb:10:10:11:5 | then ... | true | +| conditionals.rb:15:1:19:3 | if ... | IfExpr | conditionals.rb:15:4:15:9 | ... == ... | conditionals.rb:15:10:16:5 | then ... | true | +| conditionals.rb:15:1:19:3 | if ... | IfExpr | conditionals.rb:15:4:15:9 | ... == ... | conditionals.rb:17:1:18:5 | else ... | false | +| conditionals.rb:22:1:30:3 | if ... | IfExpr | conditionals.rb:22:4:22:9 | ... == ... | conditionals.rb:22:11:23:5 | then ... | true | +| conditionals.rb:22:1:30:3 | if ... | IfExpr | conditionals.rb:22:4:22:9 | ... == ... | conditionals.rb:24:1:29:5 | elsif ... | false | +| conditionals.rb:24:1:29:5 | elsif ... | IfExpr | conditionals.rb:24:7:24:12 | ... == ... | conditionals.rb:24:14:25:5 | then ... | true | +| conditionals.rb:24:1:29:5 | elsif ... | IfExpr | conditionals.rb:24:7:24:12 | ... == ... | conditionals.rb:26:1:29:5 | elsif ... | false | +| conditionals.rb:26:1:29:5 | elsif ... | IfExpr | conditionals.rb:26:7:26:12 | ... == ... | conditionals.rb:26:14:27:5 | then ... | true | +| conditionals.rb:26:1:29:5 | elsif ... | IfExpr | conditionals.rb:26:7:26:12 | ... == ... | conditionals.rb:28:1:29:5 | else ... | false | +| conditionals.rb:33:1:37:3 | if ... | IfExpr | conditionals.rb:33:4:33:9 | ... == ... | conditionals.rb:33:10:34:5 | then ... | true | +| conditionals.rb:33:1:37:3 | if ... | IfExpr | conditionals.rb:33:4:33:9 | ... == ... | conditionals.rb:35:1:36:5 | elsif ... | false | +| conditionals.rb:35:1:36:5 | elsif ... | IfExpr | conditionals.rb:35:7:35:12 | ... == ... | conditionals.rb:35:13:36:5 | then ... | true | +| conditionals.rb:40:1:42:3 | unless ... | UnlessExpr | conditionals.rb:40:8:40:12 | ... > ... | conditionals.rb:40:14:41:5 | then ... | false | +| conditionals.rb:45:1:49:3 | unless ... | UnlessExpr | conditionals.rb:45:8:45:13 | ... == ... | conditionals.rb:45:14:46:5 | then ... | false | +| conditionals.rb:45:1:49:3 | unless ... | UnlessExpr | conditionals.rb:45:8:45:13 | ... == ... | conditionals.rb:47:1:48:5 | else ... | true | +| conditionals.rb:52:1:52:14 | ... if ... | IfModifierExpr | conditionals.rb:52:10:52:14 | ... > ... | conditionals.rb:52:1:52:5 | ... = ... | true | +| conditionals.rb:55:1:55:18 | ... unless ... | UnlessModifierExpr | conditionals.rb:55:14:55:18 | ... < ... | conditionals.rb:55:1:55:5 | ... = ... | false | +| conditionals.rb:58:5:58:25 | ... ? ... : ... | TernaryIfExpr | conditionals.rb:58:5:58:9 | ... > ... | conditionals.rb:58:13:58:17 | ... + ... | true | +| conditionals.rb:58:5:58:25 | ... ? ... : ... | TernaryIfExpr | conditionals.rb:58:5:58:9 | ... > ... | conditionals.rb:58:21:58:25 | ... - ... | false | +| conditionals.rb:61:1:64:3 | if ... | IfExpr | conditionals.rb:61:4:61:8 | ... > ... | conditionals.rb:61:10:62:5 | then ... | true | +| conditionals.rb:61:1:64:3 | if ... | IfExpr | conditionals.rb:61:4:61:8 | ... > ... | conditionals.rb:63:1:63:4 | else ... | false | +| conditionals.rb:67:1:70:3 | if ... | IfExpr | conditionals.rb:67:4:67:8 | ... > ... | conditionals.rb:67:10:67:13 | then ... | true | +| conditionals.rb:67:1:70:3 | if ... | IfExpr | conditionals.rb:67:4:67:8 | ... > ... | conditionals.rb:68:1:69:5 | else ... | false | +ifExprs +| conditionals.rb:10:1:12:3 | if ... | IfExpr | conditionals.rb:10:4:10:8 | ... > ... | conditionals.rb:10:10:11:5 | then ... | (none) | false | +| conditionals.rb:15:1:19:3 | if ... | IfExpr | conditionals.rb:15:4:15:9 | ... == ... | conditionals.rb:15:10:16:5 | then ... | else ... | false | +| conditionals.rb:22:1:30:3 | if ... | IfExpr | conditionals.rb:22:4:22:9 | ... == ... | conditionals.rb:22:11:23:5 | then ... | elsif ... | false | +| conditionals.rb:24:1:29:5 | elsif ... | IfExpr | conditionals.rb:24:7:24:12 | ... == ... | conditionals.rb:24:14:25:5 | then ... | elsif ... | true | +| conditionals.rb:26:1:29:5 | elsif ... | IfExpr | conditionals.rb:26:7:26:12 | ... == ... | conditionals.rb:26:14:27:5 | then ... | else ... | true | +| conditionals.rb:33:1:37:3 | if ... | IfExpr | conditionals.rb:33:4:33:9 | ... == ... | conditionals.rb:33:10:34:5 | then ... | elsif ... | false | +| conditionals.rb:35:1:36:5 | elsif ... | IfExpr | conditionals.rb:35:7:35:12 | ... == ... | conditionals.rb:35:13:36:5 | then ... | (none) | true | +| conditionals.rb:61:1:64:3 | if ... | IfExpr | conditionals.rb:61:4:61:8 | ... > ... | conditionals.rb:61:10:62:5 | then ... | else ... | false | +| conditionals.rb:67:1:70:3 | if ... | IfExpr | conditionals.rb:67:4:67:8 | ... > ... | conditionals.rb:67:10:67:13 | then ... | else ... | false | +unlessExprs +| conditionals.rb:40:1:42:3 | unless ... | UnlessExpr | conditionals.rb:40:8:40:12 | ... > ... | conditionals.rb:40:14:41:5 | then ... | (none) | +| conditionals.rb:45:1:49:3 | unless ... | UnlessExpr | conditionals.rb:45:8:45:13 | ... == ... | conditionals.rb:45:14:46:5 | then ... | else ... | +ifModifierExprs +| conditionals.rb:52:1:52:14 | ... if ... | IfModifierExpr | conditionals.rb:52:10:52:14 | ... > ... | conditionals.rb:52:1:52:5 | ... = ... | +unlessModifierExprs +| conditionals.rb:55:1:55:18 | ... unless ... | UnlessModifierExpr | conditionals.rb:55:14:55:18 | ... < ... | conditionals.rb:55:1:55:5 | ... = ... | +ternaryIfExprs +| conditionals.rb:58:5:58:25 | ... ? ... : ... | TernaryIfExpr | conditionals.rb:58:5:58:9 | ... > ... | conditionals.rb:58:13:58:17 | ... + ... | conditionals.rb:58:21:58:25 | ... - ... | diff --git a/ruby/ql/test/library-tests/ast/control/ConditionalExpr.ql b/ruby/ql/test/library-tests/ast/control/ConditionalExpr.ql new file mode 100644 index 00000000000..bd0e5fbe623 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/control/ConditionalExpr.ql @@ -0,0 +1,49 @@ +import ruby + +query predicate conditionalExprs( + ConditionalExpr e, string pClass, Expr cond, Expr branch, boolean branchCond +) { + pClass = e.getAPrimaryQlClass() and + cond = e.getCondition() and + branch = e.getBranch(branchCond) +} + +query predicate ifExprs( + IfExpr e, string pClass, Expr cond, StmtSequence thenExpr, string elseStr, boolean isElsif +) { + pClass = e.getAPrimaryQlClass() and + cond = e.getCondition() and + thenExpr = e.getThen() and + (if exists(e.getElse()) then elseStr = e.getElse().toString() else elseStr = "(none)") and + if e.isElsif() then isElsif = true else isElsif = false +} + +query predicate unlessExprs( + UnlessExpr e, string pClass, Expr cond, StmtSequence thenExpr, string elseStr +) { + pClass = e.getAPrimaryQlClass() and + cond = e.getCondition() and + thenExpr = e.getThen() and + if exists(e.getElse()) then elseStr = e.getElse().toString() else elseStr = "(none)" +} + +query predicate ifModifierExprs(IfModifierExpr e, string pClass, Expr cond, Expr expr) { + pClass = e.getAPrimaryQlClass() and + cond = e.getCondition() and + expr = e.getBody() +} + +query predicate unlessModifierExprs(UnlessModifierExpr e, string pClass, Expr cond, Expr expr) { + pClass = e.getAPrimaryQlClass() and + cond = e.getCondition() and + expr = e.getBody() +} + +query predicate ternaryIfExprs( + TernaryIfExpr e, string pClass, Expr cond, Expr thenExpr, Expr elseExpr +) { + pClass = e.getAPrimaryQlClass() and + cond = e.getCondition() and + thenExpr = e.getThen() and + elseExpr = e.getElse() +} diff --git a/ruby/ql/test/library-tests/ast/control/ControlExpr.expected b/ruby/ql/test/library-tests/ast/control/ControlExpr.expected new file mode 100644 index 00000000000..9088fd6ed14 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/control/ControlExpr.expected @@ -0,0 +1,27 @@ +| cases.rb:8:1:15:3 | case ... | CaseExpr | +| cases.rb:18:1:22:3 | case ... | CaseExpr | +| conditionals.rb:10:1:12:3 | if ... | IfExpr | +| conditionals.rb:15:1:19:3 | if ... | IfExpr | +| conditionals.rb:22:1:30:3 | if ... | IfExpr | +| conditionals.rb:24:1:29:5 | elsif ... | IfExpr | +| conditionals.rb:26:1:29:5 | elsif ... | IfExpr | +| conditionals.rb:33:1:37:3 | if ... | IfExpr | +| conditionals.rb:35:1:36:5 | elsif ... | IfExpr | +| conditionals.rb:40:1:42:3 | unless ... | UnlessExpr | +| conditionals.rb:45:1:49:3 | unless ... | UnlessExpr | +| conditionals.rb:52:1:52:14 | ... if ... | IfModifierExpr | +| conditionals.rb:55:1:55:18 | ... unless ... | UnlessModifierExpr | +| conditionals.rb:58:5:58:25 | ... ? ... : ... | TernaryIfExpr | +| conditionals.rb:61:1:64:3 | if ... | IfExpr | +| conditionals.rb:67:1:70:3 | if ... | IfExpr | +| loops.rb:9:1:12:3 | for ... in ... | ForExpr | +| loops.rb:16:1:19:3 | for ... in ... | ForExpr | +| loops.rb:22:1:25:3 | for ... in ... | ForExpr | +| loops.rb:28:1:32:3 | for ... in ... | ForExpr | +| loops.rb:35:1:39:3 | while ... | WhileExpr | +| loops.rb:42:1:45:3 | while ... | WhileExpr | +| loops.rb:48:1:48:19 | ... while ... | WhileModifierExpr | +| loops.rb:51:1:54:3 | until ... | UntilExpr | +| loops.rb:57:1:60:3 | until ... | UntilExpr | +| loops.rb:63:1:63:19 | ... until ... | UntilModifierExpr | +| loops.rb:66:1:67:3 | while ... | WhileExpr | diff --git a/ruby/ql/test/library-tests/ast/control/ControlExpr.ql b/ruby/ql/test/library-tests/ast/control/ControlExpr.ql new file mode 100644 index 00000000000..ad802180efb --- /dev/null +++ b/ruby/ql/test/library-tests/ast/control/ControlExpr.ql @@ -0,0 +1,3 @@ +import ruby + +query predicate controlExprs(ControlExpr c, string pClass) { pClass = c.getAPrimaryQlClass() } diff --git a/ruby/ql/test/library-tests/ast/control/Loop.expected b/ruby/ql/test/library-tests/ast/control/Loop.expected new file mode 100644 index 00000000000..02bde8f9e36 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/control/Loop.expected @@ -0,0 +1,50 @@ +loops +| loops.rb:9:1:12:3 | for ... in ... | ForExpr | loops.rb:9:15:12:3 | do ... | StmtSequence | +| loops.rb:16:1:19:3 | for ... in ... | ForExpr | loops.rb:16:15:19:3 | do ... | StmtSequence | +| loops.rb:22:1:25:3 | for ... in ... | ForExpr | loops.rb:22:35:25:3 | do ... | StmtSequence | +| loops.rb:28:1:32:3 | for ... in ... | ForExpr | loops.rb:28:37:32:3 | do ... | StmtSequence | +| loops.rb:35:1:39:3 | while ... | WhileExpr | loops.rb:35:12:39:3 | do ... | StmtSequence | +| loops.rb:42:1:45:3 | while ... | WhileExpr | loops.rb:42:13:45:3 | do ... | StmtSequence | +| loops.rb:48:1:48:19 | ... while ... | WhileModifierExpr | loops.rb:48:1:48:6 | ... += ... | AssignAddExpr | +| loops.rb:51:1:54:3 | until ... | UntilExpr | loops.rb:51:13:54:3 | do ... | StmtSequence | +| loops.rb:57:1:60:3 | until ... | UntilExpr | loops.rb:57:13:60:3 | do ... | StmtSequence | +| loops.rb:63:1:63:19 | ... until ... | UntilModifierExpr | loops.rb:63:1:63:6 | ... -= ... | AssignSubExpr | +| loops.rb:66:1:67:3 | while ... | WhileExpr | loops.rb:66:13:67:3 | do ... | StmtSequence | +conditionalLoops +| loops.rb:35:1:39:3 | while ... | WhileExpr | loops.rb:35:7:35:11 | ... < ... | loops.rb:35:12:39:3 | do ... | StmtSequence | +| loops.rb:42:1:45:3 | while ... | WhileExpr | loops.rb:42:7:42:11 | ... < ... | loops.rb:42:13:45:3 | do ... | StmtSequence | +| loops.rb:48:1:48:19 | ... while ... | WhileModifierExpr | loops.rb:48:14:48:19 | ... >= ... | loops.rb:48:1:48:6 | ... += ... | AssignAddExpr | +| loops.rb:51:1:54:3 | until ... | UntilExpr | loops.rb:51:7:51:12 | ... == ... | loops.rb:51:13:54:3 | do ... | StmtSequence | +| loops.rb:57:1:60:3 | until ... | UntilExpr | loops.rb:57:7:57:11 | ... > ... | loops.rb:57:13:60:3 | do ... | StmtSequence | +| loops.rb:63:1:63:19 | ... until ... | UntilModifierExpr | loops.rb:63:14:63:19 | ... == ... | loops.rb:63:1:63:6 | ... -= ... | AssignSubExpr | +| loops.rb:66:1:67:3 | while ... | WhileExpr | loops.rb:66:7:66:11 | ... < ... | loops.rb:66:13:67:3 | do ... | StmtSequence | +forExprs +| loops.rb:9:1:12:3 | for ... in ... | loops.rb:9:5:9:5 | n | loops.rb:9:15:12:3 | do ... | 0 | loops.rb:10:5:10:12 | ... += ... | +| loops.rb:9:1:12:3 | for ... in ... | loops.rb:9:5:9:5 | n | loops.rb:9:15:12:3 | do ... | 1 | loops.rb:11:5:11:11 | ... = ... | +| loops.rb:16:1:19:3 | for ... in ... | loops.rb:16:5:16:5 | n | loops.rb:16:15:19:3 | do ... | 0 | loops.rb:17:5:17:12 | ... += ... | +| loops.rb:16:1:19:3 | for ... in ... | loops.rb:16:5:16:5 | n | loops.rb:16:15:19:3 | do ... | 1 | loops.rb:18:5:18:12 | ... -= ... | +| loops.rb:22:1:25:3 | for ... in ... | loops.rb:22:5:22:14 | (..., ...) | loops.rb:22:35:25:3 | do ... | 0 | loops.rb:23:3:23:14 | ... += ... | +| loops.rb:22:1:25:3 | for ... in ... | loops.rb:22:5:22:14 | (..., ...) | loops.rb:22:35:25:3 | do ... | 1 | loops.rb:24:3:24:14 | ... *= ... | +| loops.rb:28:1:32:3 | for ... in ... | loops.rb:28:5:28:16 | (..., ...) | loops.rb:28:37:32:3 | do ... | 0 | loops.rb:29:3:29:14 | ... += ... | +| loops.rb:28:1:32:3 | for ... in ... | loops.rb:28:5:28:16 | (..., ...) | loops.rb:28:37:32:3 | do ... | 1 | loops.rb:30:3:30:14 | ... /= ... | +| loops.rb:28:1:32:3 | for ... in ... | loops.rb:28:5:28:16 | (..., ...) | loops.rb:28:37:32:3 | do ... | 2 | loops.rb:31:3:31:7 | break | +forExprsTuplePatterns +| loops.rb:22:1:25:3 | for ... in ... | loops.rb:22:5:22:14 | (..., ...) | 0 | loops.rb:22:5:22:7 | key | +| loops.rb:22:1:25:3 | for ... in ... | loops.rb:22:5:22:14 | (..., ...) | 1 | loops.rb:22:10:22:14 | value | +| loops.rb:28:1:32:3 | for ... in ... | loops.rb:28:5:28:16 | (..., ...) | 0 | loops.rb:28:6:28:8 | key | +| loops.rb:28:1:32:3 | for ... in ... | loops.rb:28:5:28:16 | (..., ...) | 1 | loops.rb:28:11:28:15 | value | +whileExprs +| loops.rb:35:1:39:3 | while ... | loops.rb:35:7:35:11 | ... < ... | loops.rb:35:12:39:3 | do ... | 0 | loops.rb:36:3:36:8 | ... += ... | +| loops.rb:35:1:39:3 | while ... | loops.rb:35:7:35:11 | ... < ... | loops.rb:35:12:39:3 | do ... | 1 | loops.rb:37:3:37:8 | ... += ... | +| loops.rb:35:1:39:3 | while ... | loops.rb:35:7:35:11 | ... < ... | loops.rb:35:12:39:3 | do ... | 2 | loops.rb:38:3:38:6 | next | +| loops.rb:42:1:45:3 | while ... | loops.rb:42:7:42:11 | ... < ... | loops.rb:42:13:45:3 | do ... | 0 | loops.rb:43:3:43:8 | ... += ... | +| loops.rb:42:1:45:3 | while ... | loops.rb:42:7:42:11 | ... < ... | loops.rb:42:13:45:3 | do ... | 1 | loops.rb:44:3:44:8 | ... += ... | +whileModifierExprs +| loops.rb:48:1:48:19 | ... while ... | loops.rb:48:14:48:19 | ... >= ... | loops.rb:48:1:48:6 | ... += ... | +untilExprs +| loops.rb:51:1:54:3 | until ... | loops.rb:51:7:51:12 | ... == ... | loops.rb:51:13:54:3 | do ... | 0 | loops.rb:52:3:52:8 | ... += ... | +| loops.rb:51:1:54:3 | until ... | loops.rb:51:7:51:12 | ... == ... | loops.rb:51:13:54:3 | do ... | 1 | loops.rb:53:3:53:8 | ... -= ... | +| loops.rb:57:1:60:3 | until ... | loops.rb:57:7:57:11 | ... > ... | loops.rb:57:13:60:3 | do ... | 0 | loops.rb:58:3:58:8 | ... += ... | +| loops.rb:57:1:60:3 | until ... | loops.rb:57:7:57:11 | ... > ... | loops.rb:57:13:60:3 | do ... | 1 | loops.rb:59:3:59:8 | ... -= ... | +untilModifierExprs +| loops.rb:63:1:63:19 | ... until ... | loops.rb:63:14:63:19 | ... == ... | loops.rb:63:1:63:6 | ... -= ... | diff --git a/ruby/ql/test/library-tests/ast/control/Loop.ql b/ruby/ql/test/library-tests/ast/control/Loop.ql new file mode 100644 index 00000000000..d5275b46e33 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/control/Loop.ql @@ -0,0 +1,47 @@ +import ruby + +query predicate loops(Loop l, string lClass, Expr body, string bodyClass) { + l.getBody() = body and lClass = l.getAPrimaryQlClass() and bodyClass = body.getAPrimaryQlClass() +} + +query predicate conditionalLoops( + ConditionalLoop l, string lClass, Expr cond, Expr body, string bodyClass +) { + l.getBody() = body and + lClass = l.getAPrimaryQlClass() and + bodyClass = body.getAPrimaryQlClass() and + cond = l.getCondition() +} + +query predicate forExprs(ForExpr f, Pattern p, StmtSequence body, int i, Stmt bodyChild) { + p = f.getPattern() and + body = f.getBody() and + bodyChild = body.getStmt(i) +} + +query predicate forExprsTuplePatterns(ForExpr f, TuplePattern tp, int i, Pattern cp) { + tp = f.getPattern() and + cp = tp.getElement(i) +} + +query predicate whileExprs(WhileExpr e, Expr cond, StmtSequence body, int i, Stmt bodyChild) { + cond = e.getCondition() and + body = e.getBody() and + bodyChild = body.getStmt(i) +} + +query predicate whileModifierExprs(WhileModifierExpr e, Expr cond, Expr body) { + cond = e.getCondition() and + body = e.getBody() +} + +query predicate untilExprs(UntilExpr e, Expr cond, StmtSequence body, int i, Stmt bodyChild) { + cond = e.getCondition() and + body = e.getBody() and + bodyChild = body.getStmt(i) +} + +query predicate untilModifierExprs(UntilModifierExpr e, Expr cond, Expr body) { + cond = e.getCondition() and + body = e.getBody() +} diff --git a/ruby/ql/test/library-tests/ast/control/cases.rb b/ruby/ql/test/library-tests/ast/control/cases.rb new file mode 100644 index 00000000000..38a74da81d6 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/control/cases.rb @@ -0,0 +1,22 @@ +# Define some variables used below +a = 0 +b = 0 +c = 0 +d = 0 + +# A case expr with a value and an else branch +case a +when b + 100 +when c, d + 200 +else + 300 +end + +# A case expr without a value or else branch +case +when a > b then 10 +when a == b then 20 +when a < b then 30 +end \ No newline at end of file diff --git a/ruby/ql/test/library-tests/ast/control/conditionals.rb b/ruby/ql/test/library-tests/ast/control/conditionals.rb new file mode 100644 index 00000000000..85e008f5c1d --- /dev/null +++ b/ruby/ql/test/library-tests/ast/control/conditionals.rb @@ -0,0 +1,70 @@ +# Define some variables used below +a = 0 +b = 0 +c = 0 +d = 0 +e = 0 +f = 0 + +# If expr with no else +if a > b then + c +end + +# If expr with single else +if a == b + c +else + d +end + +# If expr with multiple nested elsif branches +if a == 0 then + c +elsif a == 1 then + d +elsif a == 2 then + e +else + f +end + +# If expr with elsif and then no else +if a == 0 + b +elsif a == 1 + c +end + +# Unless expr with no else +unless a > b then + c +end + +# Unless expr with else +unless a == b + c +else + d +end + +# If-modified expr +a = b if c > d + +# Unless-modified expr +a = b unless c < d + +# Ternary if expr +a = b > c ? d + 1 : e - 2 + +# If expr with empty else (treated as no else) +if a > b then + c +else +end + +# If expr with empty then (treated as no then) +if a > b then +else + c +end \ No newline at end of file diff --git a/ruby/ql/test/library-tests/ast/control/loops.rb b/ruby/ql/test/library-tests/ast/control/loops.rb new file mode 100644 index 00000000000..b00a61e348b --- /dev/null +++ b/ruby/ql/test/library-tests/ast/control/loops.rb @@ -0,0 +1,67 @@ +# Define some variables used below. +foo = 0 +sum = 0 +x = 0 +y = 0 +z = 0 + +# For loop with a single variable as the iteration argument +for n in 1..10 + sum += n + foo = n +end + +# For loop with a single variable and a trailing comma as the iteration +# argument +for n in 1..10 + sum += n + foo -= n +end + +# For loop with a tuple pattern as the iteration argument +for key, value in {foo: 0, bar: 1} + sum += value + foo *= value +end + +# Same, but with parentheses around the pattern +for (key, value) in {foo: 0, bar: 1} + sum += value + foo /= value + break +end + +# While loop +while x < y + x += 1 + z += 1 + next +end + +# While loop with `do` keyword +while x < y do + x += 1 + z += 2 +end + +# While-modified expression +x += 1 while y >= x + +# Until loop +until x == y + x += 1 + z -= 1 +end + +# Until loop with `do` keyword +until x > y do + x += 1 + z -= 4 +end + +# Until-modified expression +x -= 1 until x == 0 + +# While loop with empty `do` block +while x < y do +end diff --git a/ruby/ql/test/library-tests/ast/erb/Erb.expected b/ruby/ql/test/library-tests/ast/erb/Erb.expected new file mode 100644 index 00000000000..089e9408e55 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/erb/Erb.expected @@ -0,0 +1,130 @@ +erbFiles +| template.html.erb:0:0:0:0 | template.html.erb | +erbAstNodes +| template.html.erb:1:1:1:9 | <%graphql | +| template.html.erb:1:1:17:2 | <%graphql\n fragment Foo on Bar {\n ...%> | +| template.html.erb:1:1:32:6 | erb template | +| template.html.erb:1:10:16:4 | \n fragment Foo on Bar {\n ... | +| template.html.erb:1:10:16:4 | \n fragment Foo on Bar {\n ... | +| template.html.erb:17:1:17:2 | %> | +| template.html.erb:17:3:19:3 | \n\n<%= | +| template.html.erb:17:3:19:20 | <%= "hello world" %> | +| template.html.erb:19:4:19:18 | "hello world" | +| template.html.erb:19:4:19:18 | "hello world" | +| template.html.erb:19:19:19:20 | %> | +| template.html.erb:19:21:21:3 | \n\n<%# | +| template.html.erb:19:21:21:31 | <%#= "this is commented out" %> | +| template.html.erb:21:4:21:29 | = "this is commented out" | +| template.html.erb:21:4:21:29 | = "this is commented out" | +| template.html.erb:21:30:21:31 | %> | +| template.html.erb:21:32:23:3 | \n\n<%# | +| template.html.erb:21:32:23:35 | <%# "this is also commented out" %> | +| template.html.erb:23:4:23:33 | "this is also commented out" | +| template.html.erb:23:4:23:33 | "this is also commented out" | +| template.html.erb:23:34:23:35 | %> | +| template.html.erb:23:36:25:2 | \n\n<% | +| template.html.erb:23:36:25:13 | <% xs = "" %> | +| template.html.erb:25:3:25:11 | xs = "" | +| template.html.erb:25:3:25:11 | xs = "" | +| template.html.erb:25:12:25:13 | %> | +| template.html.erb:25:14:27:2 | \n<ul>\n | +| template.html.erb:27:3:27:4 | <% | +| template.html.erb:27:3:27:41 | <% for x in ["foo", "bar", "baz...%> | +| template.html.erb:27:5:27:39 | for x in ["foo", "bar", "baz... | +| template.html.erb:27:5:27:39 | for x in ["foo", "bar", "baz... | +| template.html.erb:27:40:27:41 | %> | +| template.html.erb:27:42:28:6 | \n <li> | +| template.html.erb:28:7:28:9 | <%= | +| template.html.erb:28:7:30:12 | <%= xs += x\n xs\n %> | +| template.html.erb:28:10:30:10 | xs += x\n xs\n | +| template.html.erb:28:10:30:10 | xs += x\n xs\n | +| template.html.erb:30:11:30:12 | %> | +| template.html.erb:30:13:31:2 | </li>\n | +| template.html.erb:31:3:31:4 | <% | +| template.html.erb:31:3:31:11 | <% end %> | +| template.html.erb:31:5:31:9 | end | +| template.html.erb:31:5:31:9 | end | +| template.html.erb:31:10:31:11 | %> | +| template.html.erb:31:12:32:6 | \n</ul>\n | +erbTemplates +| template.html.erb:1:1:32:6 | erb template | +erbDirectives +| template.html.erb:1:1:17:2 | <%graphql\n fragment Foo on Bar {\n ...%> | +| template.html.erb:17:3:19:20 | <%= "hello world" %> | +| template.html.erb:19:21:21:31 | <%#= "this is commented out" %> | +| template.html.erb:21:32:23:35 | <%# "this is also commented out" %> | +| template.html.erb:23:36:25:13 | <% xs = "" %> | +| template.html.erb:27:3:27:41 | <% for x in ["foo", "bar", "baz...%> | +| template.html.erb:28:7:30:12 | <%= xs += x\n xs\n %> | +| template.html.erb:31:3:31:11 | <% end %> | +erbCommentDirectives +| template.html.erb:19:21:21:31 | <%#= "this is commented out" %> | +| template.html.erb:21:32:23:35 | <%# "this is also commented out" %> | +erbGraphqlDirectives +| template.html.erb:1:1:17:2 | <%graphql\n fragment Foo on Bar {\n ...%> | +erbOutputDirectives +| template.html.erb:17:3:19:20 | <%= "hello world" %> | +| template.html.erb:28:7:30:12 | <%= xs += x\n xs\n %> | +erbExecutionDirectives +| template.html.erb:23:36:25:13 | <% xs = "" %> | +| template.html.erb:27:3:27:41 | <% for x in ["foo", "bar", "baz...%> | +| template.html.erb:31:3:31:11 | <% end %> | +childStmts +| template.html.erb:17:3:19:20 | <%= "hello world" %> | template.html.erb:19:5:19:17 | "hello world" | +| template.html.erb:23:36:25:13 | <% xs = "" %> | template.html.erb:25:4:25:10 | ... = ... | +| template.html.erb:27:3:27:41 | <% for x in ["foo", "bar", "baz...%> | template.html.erb:27:6:31:8 | for ... in ... | +| template.html.erb:28:7:30:12 | <%= xs += x\n xs\n %> | template.html.erb:28:11:28:17 | ... += ... | +| template.html.erb:28:7:30:12 | <%= xs += x\n xs\n %> | template.html.erb:29:11:29:12 | xs | +terminalStatements +| template.html.erb:17:3:19:20 | <%= "hello world" %> | template.html.erb:19:5:19:17 | "hello world" | +| template.html.erb:23:36:25:13 | <% xs = "" %> | template.html.erb:25:4:25:10 | ... = ... | +| template.html.erb:27:3:27:41 | <% for x in ["foo", "bar", "baz...%> | template.html.erb:27:6:31:8 | for ... in ... | +| template.html.erb:28:7:30:12 | <%= xs += x\n xs\n %> | template.html.erb:29:11:29:12 | xs | +primaryQlClasses +| template.html.erb:1:1:1:9 | <%graphql | ErbToken | +| template.html.erb:1:1:17:2 | <%graphql\n fragment Foo on Bar {\n ...%> | ErbGraphqlDirective | +| template.html.erb:1:1:32:6 | erb template | ErbTemplate | +| template.html.erb:1:10:16:4 | \n fragment Foo on Bar {\n ... | ErbCode | +| template.html.erb:1:10:16:4 | \n fragment Foo on Bar {\n ... | ErbToken | +| template.html.erb:17:1:17:2 | %> | ErbToken | +| template.html.erb:17:3:19:3 | \n\n<%= | ErbToken | +| template.html.erb:17:3:19:20 | <%= "hello world" %> | ErbOutputDirective | +| template.html.erb:19:4:19:18 | "hello world" | ErbCode | +| template.html.erb:19:4:19:18 | "hello world" | ErbToken | +| template.html.erb:19:19:19:20 | %> | ErbToken | +| template.html.erb:19:21:21:3 | \n\n<%# | ErbToken | +| template.html.erb:19:21:21:31 | <%#= "this is commented out" %> | ErbCommentDirective | +| template.html.erb:21:4:21:29 | = "this is commented out" | ErbComment | +| template.html.erb:21:4:21:29 | = "this is commented out" | ErbToken | +| template.html.erb:21:30:21:31 | %> | ErbToken | +| template.html.erb:21:32:23:3 | \n\n<%# | ErbToken | +| template.html.erb:21:32:23:35 | <%# "this is also commented out" %> | ErbCommentDirective | +| template.html.erb:23:4:23:33 | "this is also commented out" | ErbComment | +| template.html.erb:23:4:23:33 | "this is also commented out" | ErbToken | +| template.html.erb:23:34:23:35 | %> | ErbToken | +| template.html.erb:23:36:25:2 | \n\n<% | ErbToken | +| template.html.erb:23:36:25:13 | <% xs = "" %> | ErbExecutionDirective | +| template.html.erb:25:3:25:11 | xs = "" | ErbCode | +| template.html.erb:25:3:25:11 | xs = "" | ErbToken | +| template.html.erb:25:12:25:13 | %> | ErbToken | +| template.html.erb:25:14:27:2 | \n<ul>\n | ErbToken | +| template.html.erb:27:3:27:4 | <% | ErbToken | +| template.html.erb:27:3:27:41 | <% for x in ["foo", "bar", "baz...%> | ErbExecutionDirective | +| template.html.erb:27:5:27:39 | for x in ["foo", "bar", "baz... | ErbCode | +| template.html.erb:27:5:27:39 | for x in ["foo", "bar", "baz... | ErbToken | +| template.html.erb:27:40:27:41 | %> | ErbToken | +| template.html.erb:27:42:28:6 | \n <li> | ErbToken | +| template.html.erb:28:7:28:9 | <%= | ErbToken | +| template.html.erb:28:7:30:12 | <%= xs += x\n xs\n %> | ErbOutputDirective | +| template.html.erb:28:10:30:10 | xs += x\n xs\n | ErbCode | +| template.html.erb:28:10:30:10 | xs += x\n xs\n | ErbToken | +| template.html.erb:30:11:30:12 | %> | ErbToken | +| template.html.erb:30:13:31:2 | </li>\n | ErbToken | +| template.html.erb:31:3:31:4 | <% | ErbToken | +| template.html.erb:31:3:31:11 | <% end %> | ErbExecutionDirective | +| template.html.erb:31:5:31:9 | end | ErbCode | +| template.html.erb:31:5:31:9 | end | ErbToken | +| template.html.erb:31:10:31:11 | %> | ErbToken | +| template.html.erb:31:12:32:6 | \n</ul>\n | ErbToken | +erbFileTemplates +| template.html.erb:0:0:0:0 | template.html.erb | template.html.erb:1:1:32:6 | erb template | diff --git a/ruby/ql/test/library-tests/ast/erb/Erb.ql b/ruby/ql/test/library-tests/ast/erb/Erb.ql new file mode 100644 index 00000000000..afdc1f2b70e --- /dev/null +++ b/ruby/ql/test/library-tests/ast/erb/Erb.ql @@ -0,0 +1,25 @@ +import ruby + +query predicate erbFiles(ErbFile f) { any() } + +query predicate erbAstNodes(ErbAstNode n) { any() } + +query predicate erbTemplates(ErbTemplate t) { any() } + +query predicate erbDirectives(ErbDirective d) { any() } + +query predicate erbCommentDirectives(ErbCommentDirective d) { any() } + +query predicate erbGraphqlDirectives(ErbGraphqlDirective d) { any() } + +query predicate erbOutputDirectives(ErbOutputDirective d) { any() } + +query predicate erbExecutionDirectives(ErbExecutionDirective d) { any() } + +query predicate childStmts(ErbDirective d, Stmt s) { s = d.getAChildStmt() } + +query predicate terminalStatements(ErbDirective d, Stmt s) { s = d.getTerminalStmt() } + +query predicate primaryQlClasses(ErbAstNode n, string cls) { cls = n.getAPrimaryQlClass() } + +query predicate erbFileTemplates(ErbFile f, ErbTemplate t) { t = f.getTemplate() } diff --git a/ruby/ql/test/library-tests/ast/erb/template.html.erb b/ruby/ql/test/library-tests/ast/erb/template.html.erb new file mode 100644 index 00000000000..f77d311a1b8 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/erb/template.html.erb @@ -0,0 +1,32 @@ +<%graphql + fragment Foo on Bar { + some { + queryText + moreProperties + } + } + + fragment AnotherFragment on SomethingElse { + other { + things + } + here { + etc + } + } +%> + +<%= "hello world" %> + +<%#= "this is commented out" %> + +<%# "this is also commented out" %> + +<% xs = "" %> +<ul> + <% for x in ["foo", "bar", "baz"] do %> + <li><%= xs += x + xs + %></li> + <% end %> +</ul> diff --git a/ruby/ql/test/library-tests/ast/gems/Gemfile b/ruby/ql/test/library-tests/ast/gems/Gemfile new file mode 100644 index 00000000000..c5adf783a39 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/gems/Gemfile @@ -0,0 +1,9 @@ +source 'https://rubygems.org' + +gem 'foo_gem', '~> 2.0' + +source 'https://gems.example.com' do + gem 'my_gem', '1.0' + gem 'another_gem', '3.1.4' +end + diff --git a/ruby/ql/test/library-tests/ast/gems/lib/test.rb b/ruby/ql/test/library-tests/ast/gems/lib/test.rb new file mode 100644 index 00000000000..9bc0deccf87 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/gems/lib/test.rb @@ -0,0 +1,5 @@ +class Foo + def self.greet + puts "Hello" + end +end diff --git a/ruby/ql/test/library-tests/ast/gems/test.expected b/ruby/ql/test/library-tests/ast/gems/test.expected new file mode 100644 index 00000000000..71ed228b3db --- /dev/null +++ b/ruby/ql/test/library-tests/ast/gems/test.expected @@ -0,0 +1,25 @@ +| Gemfile:1:1:1:29 | call to source | +| Gemfile:3:1:3:23 | call to gem | +| Gemfile:5:1:8:3 | call to source | +| Gemfile:6:3:6:21 | call to gem | +| Gemfile:7:3:7:28 | call to gem | +| lib/test.rb:3:5:3:16 | call to puts | +| test.gemspec:1:1:10:3 | call to new | +| test.gemspec:2:3:2:8 | call to name | +| test.gemspec:2:3:2:8 | call to name= | +| test.gemspec:3:3:3:11 | call to version | +| test.gemspec:3:3:3:11 | call to version= | +| test.gemspec:4:3:4:11 | call to summary | +| test.gemspec:4:3:4:11 | call to summary= | +| test.gemspec:5:3:5:15 | call to description | +| test.gemspec:5:3:5:15 | call to description= | +| test.gemspec:6:3:6:11 | call to authors | +| test.gemspec:6:3:6:11 | call to authors= | +| test.gemspec:6:19:6:31 | call to [] | +| test.gemspec:7:3:7:9 | call to email | +| test.gemspec:7:3:7:9 | call to email= | +| test.gemspec:8:3:8:9 | call to files | +| test.gemspec:8:3:8:9 | call to files= | +| test.gemspec:8:19:8:33 | call to [] | +| test.gemspec:9:3:9:12 | call to homepage | +| test.gemspec:9:3:9:12 | call to homepage= | diff --git a/ruby/ql/test/library-tests/ast/gems/test.gemspec b/ruby/ql/test/library-tests/ast/gems/test.gemspec new file mode 100644 index 00000000000..e96a530a2dc --- /dev/null +++ b/ruby/ql/test/library-tests/ast/gems/test.gemspec @@ -0,0 +1,10 @@ +Gem::Specification.new do |s| + s.name = 'test' + s.version = '0.0.0' + s.summary = "foo!" + s.description = "A test" + s.authors = ["Mona Lisa"] + s.email = 'mona@example.com' + s.files = ["lib/test.rb"] + s.homepage = 'https://github.com/github/codeql-ruby' +end diff --git a/ruby/ql/test/library-tests/ast/gems/test.ql b/ruby/ql/test/library-tests/ast/gems/test.ql new file mode 100644 index 00000000000..0d7277ed6bd --- /dev/null +++ b/ruby/ql/test/library-tests/ast/gems/test.ql @@ -0,0 +1,4 @@ +import ruby + +// Just enough to test that we extracted the Gemfile and the .gemspec file. +select any(Call c) diff --git a/ruby/ql/test/library-tests/ast/literals/literals.expected b/ruby/ql/test/library-tests/ast/literals/literals.expected new file mode 100644 index 00000000000..1dd2ffceb55 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/literals/literals.expected @@ -0,0 +1,791 @@ +allLiterals +| literals.rb:2:1:2:3 | nil | NilLiteral | nil | +| literals.rb:3:1:3:3 | NIL | NilLiteral | NIL | +| literals.rb:4:1:4:5 | false | BooleanLiteral | false | +| literals.rb:5:1:5:5 | FALSE | BooleanLiteral | FALSE | +| literals.rb:6:1:6:4 | true | BooleanLiteral | true | +| literals.rb:7:1:7:4 | TRUE | BooleanLiteral | TRUE | +| literals.rb:10:1:10:4 | 1234 | IntegerLiteral | 1234 | +| literals.rb:11:1:11:5 | 5_678 | IntegerLiteral | 5_678 | +| literals.rb:12:1:12:1 | 0 | IntegerLiteral | 0 | +| literals.rb:13:1:13:5 | 0d900 | IntegerLiteral | 0d900 | +| literals.rb:16:1:16:6 | 0x1234 | IntegerLiteral | 0x1234 | +| literals.rb:17:1:17:10 | 0xdeadbeef | IntegerLiteral | 0xdeadbeef | +| literals.rb:18:1:18:11 | 0xF00D_face | IntegerLiteral | 0xF00D_face | +| literals.rb:21:1:21:4 | 0123 | IntegerLiteral | 0123 | +| literals.rb:22:1:22:5 | 0o234 | IntegerLiteral | 0o234 | +| literals.rb:23:1:23:6 | 0O45_6 | IntegerLiteral | 0O45_6 | +| literals.rb:26:1:26:10 | 0b10010100 | IntegerLiteral | 0b10010100 | +| literals.rb:27:1:27:11 | 0B011_01101 | IntegerLiteral | 0B011_01101 | +| literals.rb:30:1:30:5 | 12.34 | FloatLiteral | 12.34 | +| literals.rb:31:1:31:7 | 1234e-2 | FloatLiteral | 1234e-2 | +| literals.rb:32:1:32:7 | 1.234E1 | FloatLiteral | 1.234E1 | +| literals.rb:35:1:35:3 | 23r | RationalLiteral | 23r | +| literals.rb:36:1:36:5 | 9.85r | RationalLiteral | 9.85r | +| literals.rb:39:1:39:2 | 2i | ComplexLiteral | 2i | +| literals.rb:46:1:46:2 | "" | StringLiteral | | +| literals.rb:47:1:47:2 | "" | StringLiteral | | +| literals.rb:48:1:48:7 | "hello" | StringLiteral | hello | +| literals.rb:49:1:49:9 | "goodbye" | StringLiteral | goodbye | +| literals.rb:50:1:50:30 | "string with escaped \\" quote" | StringLiteral | string with escaped \\" quote | +| literals.rb:51:1:51:21 | "string with " quote" | StringLiteral | string with " quote | +| literals.rb:52:1:52:14 | "foo bar baz" | StringLiteral | foo bar baz | +| literals.rb:53:1:53:15 | "foo bar baz" | StringLiteral | foo bar baz | +| literals.rb:54:1:54:20 | "foo ' bar " baz'" | StringLiteral | foo ' bar " baz' | +| literals.rb:55:1:55:20 | "FOO ' BAR " BAZ'" | StringLiteral | FOO ' BAR " BAZ' | +| literals.rb:56:1:56:12 | "foo\\ bar" | StringLiteral | foo\\ bar | +| literals.rb:57:1:57:12 | "foo\\ bar" | StringLiteral | foo\\ bar | +| literals.rb:58:1:58:20 | "2 + 2 = #{...}" | StringLiteral | <none> | +| literals.rb:58:13:58:13 | 2 | IntegerLiteral | 2 | +| literals.rb:58:17:58:17 | 2 | IntegerLiteral | 2 | +| literals.rb:59:1:59:22 | "3 + 4 = #{...}" | StringLiteral | <none> | +| literals.rb:59:15:59:15 | 3 | IntegerLiteral | 3 | +| literals.rb:59:19:59:19 | 4 | IntegerLiteral | 4 | +| literals.rb:60:1:60:20 | "2 + 2 = #{ 2 + 2 }" | StringLiteral | 2 + 2 = #{ 2 + 2 } | +| literals.rb:61:1:61:22 | "3 + 4 = #{ 3 + 4 }" | StringLiteral | 3 + 4 = #{ 3 + 4 } | +| literals.rb:62:1:62:5 | "foo" | StringLiteral | foo | +| literals.rb:62:7:62:11 | "bar" | StringLiteral | bar | +| literals.rb:62:13:62:17 | "baz" | StringLiteral | baz | +| literals.rb:63:1:63:7 | "foo" | StringLiteral | foo | +| literals.rb:63:9:63:13 | "bar" | StringLiteral | bar | +| literals.rb:63:15:63:19 | "baz" | StringLiteral | baz | +| literals.rb:64:1:64:5 | "foo" | StringLiteral | foo | +| literals.rb:64:7:64:21 | "bar#{...}" | StringLiteral | <none> | +| literals.rb:64:14:64:14 | 1 | IntegerLiteral | 1 | +| literals.rb:64:18:64:18 | 1 | IntegerLiteral | 1 | +| literals.rb:64:23:64:27 | "baz" | StringLiteral | baz | +| literals.rb:65:1:65:35 | "foo #{...} qux" | StringLiteral | <none> | +| literals.rb:65:9:65:28 | "bar #{...} baz" | StringLiteral | <none> | +| literals.rb:65:17:65:17 | 2 | IntegerLiteral | 2 | +| literals.rb:65:21:65:21 | 3 | IntegerLiteral | 3 | +| literals.rb:66:1:66:22 | "foo #{...}" | StringLiteral | <none> | +| literals.rb:66:17:66:17 | 1 | IntegerLiteral | 1 | +| literals.rb:66:19:66:19 | 9 | IntegerLiteral | 9 | +| literals.rb:69:1:69:2 | ?x | CharacterLiteral | ?x | +| literals.rb:70:1:70:3 | ?\\n | CharacterLiteral | ?\\n | +| literals.rb:71:1:71:3 | ?\\s | CharacterLiteral | ?\\s | +| literals.rb:72:1:72:3 | ?\\\\ | CharacterLiteral | ?\\\\ | +| literals.rb:73:1:73:7 | ?\\u{58} | CharacterLiteral | ?\\u{58} | +| literals.rb:74:1:74:5 | ?\\C-a | CharacterLiteral | ?\\C-a | +| literals.rb:75:1:75:5 | ?\\M-a | CharacterLiteral | ?\\M-a | +| literals.rb:76:1:76:8 | ?\\M-\\C-a | CharacterLiteral | ?\\M-\\C-a | +| literals.rb:77:1:77:8 | ?\\C-\\M-a | CharacterLiteral | ?\\C-\\M-a | +| literals.rb:80:1:80:3 | :"" | SymbolLiteral | | +| literals.rb:81:1:81:6 | :hello | SymbolLiteral | hello | +| literals.rb:82:1:82:10 | :"foo bar" | SymbolLiteral | foo bar | +| literals.rb:83:1:83:10 | :"bar baz" | SymbolLiteral | bar baz | +| literals.rb:84:1:84:14 | {...} | HashLiteral | <none> | +| literals.rb:84:3:84:5 | :foo | SymbolLiteral | foo | +| literals.rb:84:8:84:12 | "bar" | StringLiteral | bar | +| literals.rb:85:1:85:10 | :"wibble" | SymbolLiteral | wibble | +| literals.rb:86:1:86:17 | :"wibble wobble" | SymbolLiteral | wibble wobble | +| literals.rb:87:1:87:16 | :"foo_#{...}" | SymbolLiteral | <none> | +| literals.rb:87:10:87:10 | 2 | IntegerLiteral | 2 | +| literals.rb:87:14:87:14 | 2 | IntegerLiteral | 2 | +| literals.rb:88:1:88:17 | :"foo_#{ 1 + 1 }" | SymbolLiteral | foo_#{ 1 + 1 } | +| literals.rb:89:1:89:18 | :"foo_#{ 3 - 2 }" | SymbolLiteral | foo_#{ 3 - 2 } | +| literals.rb:92:1:92:2 | [...] | ArrayLiteral | <none> | +| literals.rb:93:1:93:9 | [...] | ArrayLiteral | <none> | +| literals.rb:93:2:93:2 | 1 | IntegerLiteral | 1 | +| literals.rb:93:5:93:5 | 2 | IntegerLiteral | 2 | +| literals.rb:93:8:93:8 | 3 | IntegerLiteral | 3 | +| literals.rb:94:1:94:14 | [...] | ArrayLiteral | <none> | +| literals.rb:94:2:94:2 | 4 | IntegerLiteral | 4 | +| literals.rb:94:5:94:5 | 5 | IntegerLiteral | 5 | +| literals.rb:94:8:94:9 | 12 | IntegerLiteral | 12 | +| literals.rb:94:13:94:13 | 2 | IntegerLiteral | 2 | +| literals.rb:95:1:95:11 | [...] | ArrayLiteral | <none> | +| literals.rb:95:2:95:2 | 7 | IntegerLiteral | 7 | +| literals.rb:95:5:95:10 | [...] | ArrayLiteral | <none> | +| literals.rb:95:6:95:6 | 8 | IntegerLiteral | 8 | +| literals.rb:95:9:95:9 | 9 | IntegerLiteral | 9 | +| literals.rb:98:1:98:4 | %w(...) | ArrayLiteral | <none> | +| literals.rb:99:1:99:15 | %w(...) | ArrayLiteral | <none> | +| literals.rb:99:4:99:6 | "foo" | StringLiteral | foo | +| literals.rb:99:8:99:10 | "bar" | StringLiteral | bar | +| literals.rb:99:12:99:14 | "baz" | StringLiteral | baz | +| literals.rb:100:1:100:15 | %w(...) | ArrayLiteral | <none> | +| literals.rb:100:4:100:6 | "foo" | StringLiteral | foo | +| literals.rb:100:8:100:10 | "bar" | StringLiteral | bar | +| literals.rb:100:12:100:14 | "baz" | StringLiteral | baz | +| literals.rb:101:1:101:21 | %w(...) | ArrayLiteral | <none> | +| literals.rb:101:4:101:6 | "foo" | StringLiteral | foo | +| literals.rb:101:8:101:16 | "bar#{...}" | StringLiteral | <none> | +| literals.rb:101:13:101:13 | 1 | IntegerLiteral | 1 | +| literals.rb:101:15:101:15 | 1 | IntegerLiteral | 1 | +| literals.rb:101:18:101:20 | "baz" | StringLiteral | baz | +| literals.rb:102:1:102:21 | %w(...) | ArrayLiteral | <none> | +| literals.rb:102:4:102:6 | "foo" | StringLiteral | foo | +| literals.rb:102:8:102:16 | "bar#{1+1}" | StringLiteral | bar#{1+1} | +| literals.rb:102:18:102:20 | "baz" | StringLiteral | baz | +| literals.rb:105:1:105:4 | %i(...) | ArrayLiteral | <none> | +| literals.rb:106:1:106:15 | %i(...) | ArrayLiteral | <none> | +| literals.rb:106:4:106:6 | :"foo" | SymbolLiteral | foo | +| literals.rb:106:8:106:10 | :"bar" | SymbolLiteral | bar | +| literals.rb:106:12:106:14 | :"baz" | SymbolLiteral | baz | +| literals.rb:107:1:107:15 | %i(...) | ArrayLiteral | <none> | +| literals.rb:107:4:107:6 | :"foo" | SymbolLiteral | foo | +| literals.rb:107:8:107:10 | :"bar" | SymbolLiteral | bar | +| literals.rb:107:12:107:14 | :"baz" | SymbolLiteral | baz | +| literals.rb:108:1:108:25 | %i(...) | ArrayLiteral | <none> | +| literals.rb:108:4:108:6 | :"foo" | SymbolLiteral | foo | +| literals.rb:108:8:108:20 | :"bar#{...}" | SymbolLiteral | <none> | +| literals.rb:108:14:108:14 | 2 | IntegerLiteral | 2 | +| literals.rb:108:18:108:18 | 4 | IntegerLiteral | 4 | +| literals.rb:108:22:108:24 | :"baz" | SymbolLiteral | baz | +| literals.rb:109:1:109:25 | %i(...) | ArrayLiteral | <none> | +| literals.rb:109:4:109:6 | :"foo" | SymbolLiteral | foo | +| literals.rb:109:8:109:12 | :"bar#{" | SymbolLiteral | bar#{ | +| literals.rb:109:14:109:14 | :"2" | SymbolLiteral | 2 | +| literals.rb:109:16:109:16 | :"+" | SymbolLiteral | + | +| literals.rb:109:18:109:18 | :"4" | SymbolLiteral | 4 | +| literals.rb:109:20:109:20 | :"}" | SymbolLiteral | } | +| literals.rb:109:22:109:24 | :"baz" | SymbolLiteral | baz | +| literals.rb:112:1:112:2 | {...} | HashLiteral | <none> | +| literals.rb:113:1:113:33 | {...} | HashLiteral | <none> | +| literals.rb:113:3:113:5 | :foo | SymbolLiteral | foo | +| literals.rb:113:8:113:8 | 1 | IntegerLiteral | 1 | +| literals.rb:113:11:113:14 | :bar | SymbolLiteral | bar | +| literals.rb:113:19:113:19 | 2 | IntegerLiteral | 2 | +| literals.rb:113:22:113:26 | "baz" | StringLiteral | baz | +| literals.rb:113:31:113:31 | 3 | IntegerLiteral | 3 | +| literals.rb:114:1:114:17 | {...} | HashLiteral | <none> | +| literals.rb:114:3:114:5 | :foo | SymbolLiteral | foo | +| literals.rb:114:8:114:8 | 7 | IntegerLiteral | 7 | +| literals.rb:117:2:117:2 | 1 | IntegerLiteral | 1 | +| literals.rb:117:2:117:6 | _ .. _ | RangeLiteral | <none> | +| literals.rb:117:5:117:6 | 10 | IntegerLiteral | 10 | +| literals.rb:118:2:118:2 | 1 | IntegerLiteral | 1 | +| literals.rb:118:2:118:7 | _ ... _ | RangeLiteral | <none> | +| literals.rb:118:6:118:7 | 10 | IntegerLiteral | 10 | +| literals.rb:119:2:119:2 | 1 | IntegerLiteral | 1 | +| literals.rb:119:2:119:7 | _ .. _ | RangeLiteral | <none> | +| literals.rb:119:7:119:7 | 0 | IntegerLiteral | 0 | +| literals.rb:120:2:120:11 | _ .. _ | RangeLiteral | <none> | +| literals.rb:120:9:120:9 | 2 | IntegerLiteral | 2 | +| literals.rb:120:11:120:11 | 3 | IntegerLiteral | 3 | +| literals.rb:121:2:121:2 | 1 | IntegerLiteral | 1 | +| literals.rb:121:2:121:4 | _ .. _ | RangeLiteral | <none> | +| literals.rb:122:2:122:4 | _ .. _ | RangeLiteral | <none> | +| literals.rb:122:4:122:4 | 1 | IntegerLiteral | 1 | +| literals.rb:123:2:123:2 | 0 | IntegerLiteral | 0 | +| literals.rb:123:2:123:4 | _ .. _ | RangeLiteral | <none> | +| literals.rb:123:6:123:6 | 1 | IntegerLiteral | 1 | +| literals.rb:126:1:126:7 | `ls -l` | SubshellLiteral | ls -l | +| literals.rb:127:1:127:9 | `ls -l` | SubshellLiteral | ls -l | +| literals.rb:128:1:128:18 | `du -d #{...}` | SubshellLiteral | <none> | +| literals.rb:128:11:128:11 | 1 | IntegerLiteral | 1 | +| literals.rb:128:15:128:15 | 1 | IntegerLiteral | 1 | +| literals.rb:129:1:129:20 | `du -d #{...}` | SubshellLiteral | <none> | +| literals.rb:129:13:129:13 | 5 | IntegerLiteral | 5 | +| literals.rb:129:17:129:17 | 4 | IntegerLiteral | 4 | +| literals.rb:132:1:132:2 | // | RegExpLiteral | | +| literals.rb:133:1:133:5 | /foo/ | RegExpLiteral | foo | +| literals.rb:134:1:134:6 | /foo/ | RegExpLiteral | foo | +| literals.rb:135:1:135:13 | /foo+\\sbar\\S/ | RegExpLiteral | foo+\\sbar\\S | +| literals.rb:136:1:136:18 | /foo#{...}bar/ | RegExpLiteral | <none> | +| literals.rb:136:8:136:8 | 1 | IntegerLiteral | 1 | +| literals.rb:136:12:136:12 | 1 | IntegerLiteral | 1 | +| literals.rb:137:1:137:8 | /foo/ | RegExpLiteral | foo | +| literals.rb:138:1:138:4 | // | RegExpLiteral | | +| literals.rb:139:1:139:7 | /foo/ | RegExpLiteral | foo | +| literals.rb:140:1:140:8 | /foo/ | RegExpLiteral | foo | +| literals.rb:141:1:141:15 | /foo+\\sbar\\S/ | RegExpLiteral | foo+\\sbar\\S | +| literals.rb:142:1:142:20 | /foo#{...}bar/ | RegExpLiteral | <none> | +| literals.rb:142:10:142:10 | 1 | IntegerLiteral | 1 | +| literals.rb:142:14:142:14 | 1 | IntegerLiteral | 1 | +| literals.rb:143:1:143:10 | /foo/ | RegExpLiteral | foo | +| literals.rb:146:1:146:34 | "abcdefghijklmnopqrstuvwxyzabcdef" | StringLiteral | abcdefghijklmnopqrstuvwxyzabcdef | +| literals.rb:147:1:147:35 | "foobarfoobarfoobarfoobarfooba..." | StringLiteral | foobarfoobarfoobarfoobarfoobarfoo | +| literals.rb:148:1:148:40 | "foobar\\\\foobar\\\\foobar\\\\fooba..." | StringLiteral | foobar\\\\foobar\\\\foobar\\\\foobar\\\\foobar | +| literals.rb:151:9:151:13 | <<SQL | HereDoc | \nselect * from table\n | +| literals.rb:151:16:151:20 | <<SQL | HereDoc | <none> | +| literals.rb:158:11:158:16 | <<-BLA | HereDoc | \nsome text\\nand some more\n | +| literals.rb:163:9:163:19 | <<~SQUIGGLY | HereDoc | \n indented stuff\n | +| literals.rb:167:9:167:15 | <<"DOC" | HereDoc | <none> | +| literals.rb:172:9:172:15 | <<'DOC' | HereDoc | <none> | +| literals.rb:176:10:176:19 | <<`SCRIPT` | HereDoc | \n cat file.txt\n | +stringlikeLiterals +| literals.rb:46:1:46:2 | "" | | +| literals.rb:47:1:47:2 | "" | | +| literals.rb:48:1:48:7 | "hello" | hello | +| literals.rb:49:1:49:9 | "goodbye" | goodbye | +| literals.rb:50:1:50:30 | "string with escaped \\" quote" | string with escaped \\" quote | +| literals.rb:51:1:51:21 | "string with " quote" | string with " quote | +| literals.rb:52:1:52:14 | "foo bar baz" | foo bar baz | +| literals.rb:53:1:53:15 | "foo bar baz" | foo bar baz | +| literals.rb:54:1:54:20 | "foo ' bar " baz'" | foo ' bar " baz' | +| literals.rb:55:1:55:20 | "FOO ' BAR " BAZ'" | FOO ' BAR " BAZ' | +| literals.rb:56:1:56:12 | "foo\\ bar" | foo\\ bar | +| literals.rb:57:1:57:12 | "foo\\ bar" | foo\\ bar | +| literals.rb:58:1:58:20 | "2 + 2 = #{...}" | <none> | +| literals.rb:59:1:59:22 | "3 + 4 = #{...}" | <none> | +| literals.rb:60:1:60:20 | "2 + 2 = #{ 2 + 2 }" | 2 + 2 = #{ 2 + 2 } | +| literals.rb:61:1:61:22 | "3 + 4 = #{ 3 + 4 }" | 3 + 4 = #{ 3 + 4 } | +| literals.rb:62:1:62:5 | "foo" | foo | +| literals.rb:62:7:62:11 | "bar" | bar | +| literals.rb:62:13:62:17 | "baz" | baz | +| literals.rb:63:1:63:7 | "foo" | foo | +| literals.rb:63:9:63:13 | "bar" | bar | +| literals.rb:63:15:63:19 | "baz" | baz | +| literals.rb:64:1:64:5 | "foo" | foo | +| literals.rb:64:7:64:21 | "bar#{...}" | <none> | +| literals.rb:64:23:64:27 | "baz" | baz | +| literals.rb:65:1:65:35 | "foo #{...} qux" | <none> | +| literals.rb:65:9:65:28 | "bar #{...} baz" | <none> | +| literals.rb:66:1:66:22 | "foo #{...}" | <none> | +| literals.rb:80:1:80:3 | :"" | | +| literals.rb:81:1:81:6 | :hello | hello | +| literals.rb:82:1:82:10 | :"foo bar" | foo bar | +| literals.rb:83:1:83:10 | :"bar baz" | bar baz | +| literals.rb:84:3:84:5 | :foo | foo | +| literals.rb:84:8:84:12 | "bar" | bar | +| literals.rb:85:1:85:10 | :"wibble" | wibble | +| literals.rb:86:1:86:17 | :"wibble wobble" | wibble wobble | +| literals.rb:87:1:87:16 | :"foo_#{...}" | <none> | +| literals.rb:88:1:88:17 | :"foo_#{ 1 + 1 }" | foo_#{ 1 + 1 } | +| literals.rb:89:1:89:18 | :"foo_#{ 3 - 2 }" | foo_#{ 3 - 2 } | +| literals.rb:99:4:99:6 | "foo" | foo | +| literals.rb:99:8:99:10 | "bar" | bar | +| literals.rb:99:12:99:14 | "baz" | baz | +| literals.rb:100:4:100:6 | "foo" | foo | +| literals.rb:100:8:100:10 | "bar" | bar | +| literals.rb:100:12:100:14 | "baz" | baz | +| literals.rb:101:4:101:6 | "foo" | foo | +| literals.rb:101:8:101:16 | "bar#{...}" | <none> | +| literals.rb:101:18:101:20 | "baz" | baz | +| literals.rb:102:4:102:6 | "foo" | foo | +| literals.rb:102:8:102:16 | "bar#{1+1}" | bar#{1+1} | +| literals.rb:102:18:102:20 | "baz" | baz | +| literals.rb:106:4:106:6 | :"foo" | foo | +| literals.rb:106:8:106:10 | :"bar" | bar | +| literals.rb:106:12:106:14 | :"baz" | baz | +| literals.rb:107:4:107:6 | :"foo" | foo | +| literals.rb:107:8:107:10 | :"bar" | bar | +| literals.rb:107:12:107:14 | :"baz" | baz | +| literals.rb:108:4:108:6 | :"foo" | foo | +| literals.rb:108:8:108:20 | :"bar#{...}" | <none> | +| literals.rb:108:22:108:24 | :"baz" | baz | +| literals.rb:109:4:109:6 | :"foo" | foo | +| literals.rb:109:8:109:12 | :"bar#{" | bar#{ | +| literals.rb:109:14:109:14 | :"2" | 2 | +| literals.rb:109:16:109:16 | :"+" | + | +| literals.rb:109:18:109:18 | :"4" | 4 | +| literals.rb:109:20:109:20 | :"}" | } | +| literals.rb:109:22:109:24 | :"baz" | baz | +| literals.rb:113:3:113:5 | :foo | foo | +| literals.rb:113:11:113:14 | :bar | bar | +| literals.rb:113:22:113:26 | "baz" | baz | +| literals.rb:114:3:114:5 | :foo | foo | +| literals.rb:126:1:126:7 | `ls -l` | ls -l | +| literals.rb:127:1:127:9 | `ls -l` | ls -l | +| literals.rb:128:1:128:18 | `du -d #{...}` | <none> | +| literals.rb:129:1:129:20 | `du -d #{...}` | <none> | +| literals.rb:132:1:132:2 | // | | +| literals.rb:133:1:133:5 | /foo/ | foo | +| literals.rb:134:1:134:6 | /foo/ | foo | +| literals.rb:135:1:135:13 | /foo+\\sbar\\S/ | foo+\\sbar\\S | +| literals.rb:136:1:136:18 | /foo#{...}bar/ | <none> | +| literals.rb:137:1:137:8 | /foo/ | foo | +| literals.rb:138:1:138:4 | // | | +| literals.rb:139:1:139:7 | /foo/ | foo | +| literals.rb:140:1:140:8 | /foo/ | foo | +| literals.rb:141:1:141:15 | /foo+\\sbar\\S/ | foo+\\sbar\\S | +| literals.rb:142:1:142:20 | /foo#{...}bar/ | <none> | +| literals.rb:143:1:143:10 | /foo/ | foo | +| literals.rb:146:1:146:34 | "abcdefghijklmnopqrstuvwxyzabcdef" | abcdefghijklmnopqrstuvwxyzabcdef | +| literals.rb:147:1:147:35 | "foobarfoobarfoobarfoobarfooba..." | foobarfoobarfoobarfoobarfoobarfoo | +| literals.rb:148:1:148:40 | "foobar\\\\foobar\\\\foobar\\\\fooba..." | foobar\\\\foobar\\\\foobar\\\\foobar\\\\foobar | +| literals.rb:151:9:151:13 | <<SQL | \nselect * from table\n | +| literals.rb:151:16:151:20 | <<SQL | <none> | +| literals.rb:158:11:158:16 | <<-BLA | \nsome text\\nand some more\n | +| literals.rb:163:9:163:19 | <<~SQUIGGLY | \n indented stuff\n | +| literals.rb:167:9:167:15 | <<"DOC" | <none> | +| literals.rb:172:9:172:15 | <<'DOC' | <none> | +| literals.rb:176:10:176:19 | <<`SCRIPT` | \n cat file.txt\n | +stringLiterals +| literals.rb:46:1:46:2 | "" | | +| literals.rb:47:1:47:2 | "" | | +| literals.rb:48:1:48:7 | "hello" | hello | +| literals.rb:49:1:49:9 | "goodbye" | goodbye | +| literals.rb:50:1:50:30 | "string with escaped \\" quote" | string with escaped \\" quote | +| literals.rb:51:1:51:21 | "string with " quote" | string with " quote | +| literals.rb:52:1:52:14 | "foo bar baz" | foo bar baz | +| literals.rb:53:1:53:15 | "foo bar baz" | foo bar baz | +| literals.rb:54:1:54:20 | "foo ' bar " baz'" | foo ' bar " baz' | +| literals.rb:55:1:55:20 | "FOO ' BAR " BAZ'" | FOO ' BAR " BAZ' | +| literals.rb:56:1:56:12 | "foo\\ bar" | foo\\ bar | +| literals.rb:57:1:57:12 | "foo\\ bar" | foo\\ bar | +| literals.rb:58:1:58:20 | "2 + 2 = #{...}" | <none> | +| literals.rb:59:1:59:22 | "3 + 4 = #{...}" | <none> | +| literals.rb:60:1:60:20 | "2 + 2 = #{ 2 + 2 }" | 2 + 2 = #{ 2 + 2 } | +| literals.rb:61:1:61:22 | "3 + 4 = #{ 3 + 4 }" | 3 + 4 = #{ 3 + 4 } | +| literals.rb:62:1:62:5 | "foo" | foo | +| literals.rb:62:7:62:11 | "bar" | bar | +| literals.rb:62:13:62:17 | "baz" | baz | +| literals.rb:63:1:63:7 | "foo" | foo | +| literals.rb:63:9:63:13 | "bar" | bar | +| literals.rb:63:15:63:19 | "baz" | baz | +| literals.rb:64:1:64:5 | "foo" | foo | +| literals.rb:64:7:64:21 | "bar#{...}" | <none> | +| literals.rb:64:23:64:27 | "baz" | baz | +| literals.rb:65:1:65:35 | "foo #{...} qux" | <none> | +| literals.rb:65:9:65:28 | "bar #{...} baz" | <none> | +| literals.rb:66:1:66:22 | "foo #{...}" | <none> | +| literals.rb:84:8:84:12 | "bar" | bar | +| literals.rb:99:4:99:6 | "foo" | foo | +| literals.rb:99:8:99:10 | "bar" | bar | +| literals.rb:99:12:99:14 | "baz" | baz | +| literals.rb:100:4:100:6 | "foo" | foo | +| literals.rb:100:8:100:10 | "bar" | bar | +| literals.rb:100:12:100:14 | "baz" | baz | +| literals.rb:101:4:101:6 | "foo" | foo | +| literals.rb:101:8:101:16 | "bar#{...}" | <none> | +| literals.rb:101:18:101:20 | "baz" | baz | +| literals.rb:102:4:102:6 | "foo" | foo | +| literals.rb:102:8:102:16 | "bar#{1+1}" | bar#{1+1} | +| literals.rb:102:18:102:20 | "baz" | baz | +| literals.rb:113:22:113:26 | "baz" | baz | +| literals.rb:146:1:146:34 | "abcdefghijklmnopqrstuvwxyzabcdef" | abcdefghijklmnopqrstuvwxyzabcdef | +| literals.rb:147:1:147:35 | "foobarfoobarfoobarfoobarfooba..." | foobarfoobarfoobarfoobarfoobarfoo | +| literals.rb:148:1:148:40 | "foobar\\\\foobar\\\\foobar\\\\fooba..." | foobar\\\\foobar\\\\foobar\\\\foobar\\\\foobar | +regExpLiterals +| literals.rb:132:1:132:2 | // | | | +| literals.rb:133:1:133:5 | /foo/ | foo | | +| literals.rb:134:1:134:6 | /foo/ | foo | i | +| literals.rb:135:1:135:13 | /foo+\\sbar\\S/ | foo+\\sbar\\S | | +| literals.rb:136:1:136:18 | /foo#{...}bar/ | <none> | | +| literals.rb:137:1:137:8 | /foo/ | foo | oxm | +| literals.rb:138:1:138:4 | // | | | +| literals.rb:139:1:139:7 | /foo/ | foo | | +| literals.rb:140:1:140:8 | /foo/ | foo | i | +| literals.rb:141:1:141:15 | /foo+\\sbar\\S/ | foo+\\sbar\\S | | +| literals.rb:142:1:142:20 | /foo#{...}bar/ | <none> | | +| literals.rb:143:1:143:10 | /foo/ | foo | mxo | +symbolLiterals +| literals.rb:80:1:80:3 | :"" | | +| literals.rb:81:1:81:6 | :hello | hello | +| literals.rb:82:1:82:10 | :"foo bar" | foo bar | +| literals.rb:83:1:83:10 | :"bar baz" | bar baz | +| literals.rb:84:3:84:5 | :foo | foo | +| literals.rb:85:1:85:10 | :"wibble" | wibble | +| literals.rb:86:1:86:17 | :"wibble wobble" | wibble wobble | +| literals.rb:87:1:87:16 | :"foo_#{...}" | <none> | +| literals.rb:88:1:88:17 | :"foo_#{ 1 + 1 }" | foo_#{ 1 + 1 } | +| literals.rb:89:1:89:18 | :"foo_#{ 3 - 2 }" | foo_#{ 3 - 2 } | +| literals.rb:106:4:106:6 | :"foo" | foo | +| literals.rb:106:8:106:10 | :"bar" | bar | +| literals.rb:106:12:106:14 | :"baz" | baz | +| literals.rb:107:4:107:6 | :"foo" | foo | +| literals.rb:107:8:107:10 | :"bar" | bar | +| literals.rb:107:12:107:14 | :"baz" | baz | +| literals.rb:108:4:108:6 | :"foo" | foo | +| literals.rb:108:8:108:20 | :"bar#{...}" | <none> | +| literals.rb:108:22:108:24 | :"baz" | baz | +| literals.rb:109:4:109:6 | :"foo" | foo | +| literals.rb:109:8:109:12 | :"bar#{" | bar#{ | +| literals.rb:109:14:109:14 | :"2" | 2 | +| literals.rb:109:16:109:16 | :"+" | + | +| literals.rb:109:18:109:18 | :"4" | 4 | +| literals.rb:109:20:109:20 | :"}" | } | +| literals.rb:109:22:109:24 | :"baz" | baz | +| literals.rb:113:3:113:5 | :foo | foo | +| literals.rb:113:11:113:14 | :bar | bar | +| literals.rb:114:3:114:5 | :foo | foo | +subshellLiterals +| literals.rb:126:1:126:7 | `ls -l` | ls -l | +| literals.rb:127:1:127:9 | `ls -l` | ls -l | +| literals.rb:128:1:128:18 | `du -d #{...}` | <none> | +| literals.rb:129:1:129:20 | `du -d #{...}` | <none> | +stringComponents +| literals.rb:48:1:48:7 | "hello" | StringLiteral | 0 | literals.rb:48:2:48:6 | hello | StringTextComponent | +| literals.rb:49:1:49:9 | "goodbye" | StringLiteral | 0 | literals.rb:49:2:49:8 | goodbye | StringTextComponent | +| literals.rb:50:1:50:30 | "string with escaped \\" quote" | StringLiteral | 0 | literals.rb:50:2:50:21 | string with escaped | StringTextComponent | +| literals.rb:50:1:50:30 | "string with escaped \\" quote" | StringLiteral | 1 | literals.rb:50:22:50:23 | \\" | StringEscapeSequenceComponent | +| literals.rb:50:1:50:30 | "string with escaped \\" quote" | StringLiteral | 2 | literals.rb:50:24:50:29 | quote | StringTextComponent | +| literals.rb:51:1:51:21 | "string with " quote" | StringLiteral | 0 | literals.rb:51:2:51:20 | string with " quote | StringTextComponent | +| literals.rb:52:1:52:14 | "foo bar baz" | StringLiteral | 0 | literals.rb:52:3:52:13 | foo bar baz | StringTextComponent | +| literals.rb:53:1:53:15 | "foo bar baz" | StringLiteral | 0 | literals.rb:53:4:53:14 | foo bar baz | StringTextComponent | +| literals.rb:54:1:54:20 | "foo ' bar " baz'" | StringLiteral | 0 | literals.rb:54:4:54:19 | foo ' bar " baz' | StringTextComponent | +| literals.rb:55:1:55:20 | "FOO ' BAR " BAZ'" | StringLiteral | 0 | literals.rb:55:4:55:19 | FOO ' BAR " BAZ' | StringTextComponent | +| literals.rb:56:1:56:12 | "foo\\ bar" | StringLiteral | 0 | literals.rb:56:4:56:11 | foo\\ bar | StringTextComponent | +| literals.rb:57:1:57:12 | "foo\\ bar" | StringLiteral | 0 | literals.rb:57:4:57:6 | foo | StringTextComponent | +| literals.rb:57:1:57:12 | "foo\\ bar" | StringLiteral | 1 | literals.rb:57:7:57:8 | \\ | StringEscapeSequenceComponent | +| literals.rb:57:1:57:12 | "foo\\ bar" | StringLiteral | 2 | literals.rb:57:9:57:11 | bar | StringTextComponent | +| literals.rb:58:1:58:20 | "2 + 2 = #{...}" | StringLiteral | 0 | literals.rb:58:2:58:9 | 2 + 2 = | StringTextComponent | +| literals.rb:58:1:58:20 | "2 + 2 = #{...}" | StringLiteral | 1 | literals.rb:58:10:58:19 | #{...} | StringInterpolationComponent | +| literals.rb:59:1:59:22 | "3 + 4 = #{...}" | StringLiteral | 0 | literals.rb:59:4:59:11 | 3 + 4 = | StringTextComponent | +| literals.rb:59:1:59:22 | "3 + 4 = #{...}" | StringLiteral | 1 | literals.rb:59:12:59:21 | #{...} | StringInterpolationComponent | +| literals.rb:60:1:60:20 | "2 + 2 = #{ 2 + 2 }" | StringLiteral | 0 | literals.rb:60:2:60:19 | 2 + 2 = #{ 2 + 2 } | StringTextComponent | +| literals.rb:61:1:61:22 | "3 + 4 = #{ 3 + 4 }" | StringLiteral | 0 | literals.rb:61:4:61:21 | 3 + 4 = #{ 3 + 4 } | StringTextComponent | +| literals.rb:62:1:62:5 | "foo" | StringLiteral | 0 | literals.rb:62:2:62:4 | foo | StringTextComponent | +| literals.rb:62:7:62:11 | "bar" | StringLiteral | 0 | literals.rb:62:8:62:10 | bar | StringTextComponent | +| literals.rb:62:13:62:17 | "baz" | StringLiteral | 0 | literals.rb:62:14:62:16 | baz | StringTextComponent | +| literals.rb:63:1:63:7 | "foo" | StringLiteral | 0 | literals.rb:63:4:63:6 | foo | StringTextComponent | +| literals.rb:63:9:63:13 | "bar" | StringLiteral | 0 | literals.rb:63:10:63:12 | bar | StringTextComponent | +| literals.rb:63:15:63:19 | "baz" | StringLiteral | 0 | literals.rb:63:16:63:18 | baz | StringTextComponent | +| literals.rb:64:1:64:5 | "foo" | StringLiteral | 0 | literals.rb:64:2:64:4 | foo | StringTextComponent | +| literals.rb:64:7:64:21 | "bar#{...}" | StringLiteral | 0 | literals.rb:64:8:64:10 | bar | StringTextComponent | +| literals.rb:64:7:64:21 | "bar#{...}" | StringLiteral | 1 | literals.rb:64:11:64:20 | #{...} | StringInterpolationComponent | +| literals.rb:64:23:64:27 | "baz" | StringLiteral | 0 | literals.rb:64:24:64:26 | baz | StringTextComponent | +| literals.rb:65:1:65:35 | "foo #{...} qux" | StringLiteral | 0 | literals.rb:65:2:65:5 | foo | StringTextComponent | +| literals.rb:65:1:65:35 | "foo #{...} qux" | StringLiteral | 1 | literals.rb:65:6:65:30 | #{...} | StringInterpolationComponent | +| literals.rb:65:1:65:35 | "foo #{...} qux" | StringLiteral | 2 | literals.rb:65:31:65:34 | qux | StringTextComponent | +| literals.rb:65:9:65:28 | "bar #{...} baz" | StringLiteral | 0 | literals.rb:65:10:65:13 | bar | StringTextComponent | +| literals.rb:65:9:65:28 | "bar #{...} baz" | StringLiteral | 1 | literals.rb:65:14:65:23 | #{...} | StringInterpolationComponent | +| literals.rb:65:9:65:28 | "bar #{...} baz" | StringLiteral | 2 | literals.rb:65:24:65:27 | baz | StringTextComponent | +| literals.rb:66:1:66:22 | "foo #{...}" | StringLiteral | 0 | literals.rb:66:2:66:5 | foo | StringTextComponent | +| literals.rb:66:1:66:22 | "foo #{...}" | StringLiteral | 1 | literals.rb:66:6:66:21 | #{...} | StringInterpolationComponent | +| literals.rb:82:1:82:10 | :"foo bar" | SymbolLiteral | 0 | literals.rb:82:3:82:9 | foo bar | StringTextComponent | +| literals.rb:83:1:83:10 | :"bar baz" | SymbolLiteral | 0 | literals.rb:83:3:83:9 | bar baz | StringTextComponent | +| literals.rb:84:8:84:12 | "bar" | StringLiteral | 0 | literals.rb:84:9:84:11 | bar | StringTextComponent | +| literals.rb:85:1:85:10 | :"wibble" | SymbolLiteral | 0 | literals.rb:85:4:85:9 | wibble | StringTextComponent | +| literals.rb:86:1:86:17 | :"wibble wobble" | SymbolLiteral | 0 | literals.rb:86:4:86:16 | wibble wobble | StringTextComponent | +| literals.rb:87:1:87:16 | :"foo_#{...}" | SymbolLiteral | 0 | literals.rb:87:3:87:6 | foo_ | StringTextComponent | +| literals.rb:87:1:87:16 | :"foo_#{...}" | SymbolLiteral | 1 | literals.rb:87:7:87:15 | #{...} | StringInterpolationComponent | +| literals.rb:88:1:88:17 | :"foo_#{ 1 + 1 }" | SymbolLiteral | 0 | literals.rb:88:3:88:16 | foo_#{ 1 + 1 } | StringTextComponent | +| literals.rb:89:1:89:18 | :"foo_#{ 3 - 2 }" | SymbolLiteral | 0 | literals.rb:89:4:89:17 | foo_#{ 3 - 2 } | StringTextComponent | +| literals.rb:99:4:99:6 | "foo" | StringLiteral | 0 | literals.rb:99:4:99:6 | foo | StringTextComponent | +| literals.rb:99:8:99:10 | "bar" | StringLiteral | 0 | literals.rb:99:8:99:10 | bar | StringTextComponent | +| literals.rb:99:12:99:14 | "baz" | StringLiteral | 0 | literals.rb:99:12:99:14 | baz | StringTextComponent | +| literals.rb:100:4:100:6 | "foo" | StringLiteral | 0 | literals.rb:100:4:100:6 | foo | StringTextComponent | +| literals.rb:100:8:100:10 | "bar" | StringLiteral | 0 | literals.rb:100:8:100:10 | bar | StringTextComponent | +| literals.rb:100:12:100:14 | "baz" | StringLiteral | 0 | literals.rb:100:12:100:14 | baz | StringTextComponent | +| literals.rb:101:4:101:6 | "foo" | StringLiteral | 0 | literals.rb:101:4:101:6 | foo | StringTextComponent | +| literals.rb:101:8:101:16 | "bar#{...}" | StringLiteral | 0 | literals.rb:101:8:101:10 | bar | StringTextComponent | +| literals.rb:101:8:101:16 | "bar#{...}" | StringLiteral | 1 | literals.rb:101:11:101:16 | #{...} | StringInterpolationComponent | +| literals.rb:101:18:101:20 | "baz" | StringLiteral | 0 | literals.rb:101:18:101:20 | baz | StringTextComponent | +| literals.rb:102:4:102:6 | "foo" | StringLiteral | 0 | literals.rb:102:4:102:6 | foo | StringTextComponent | +| literals.rb:102:8:102:16 | "bar#{1+1}" | StringLiteral | 0 | literals.rb:102:8:102:16 | bar#{1+1} | StringTextComponent | +| literals.rb:102:18:102:20 | "baz" | StringLiteral | 0 | literals.rb:102:18:102:20 | baz | StringTextComponent | +| literals.rb:106:4:106:6 | :"foo" | SymbolLiteral | 0 | literals.rb:106:4:106:6 | foo | StringTextComponent | +| literals.rb:106:8:106:10 | :"bar" | SymbolLiteral | 0 | literals.rb:106:8:106:10 | bar | StringTextComponent | +| literals.rb:106:12:106:14 | :"baz" | SymbolLiteral | 0 | literals.rb:106:12:106:14 | baz | StringTextComponent | +| literals.rb:107:4:107:6 | :"foo" | SymbolLiteral | 0 | literals.rb:107:4:107:6 | foo | StringTextComponent | +| literals.rb:107:8:107:10 | :"bar" | SymbolLiteral | 0 | literals.rb:107:8:107:10 | bar | StringTextComponent | +| literals.rb:107:12:107:14 | :"baz" | SymbolLiteral | 0 | literals.rb:107:12:107:14 | baz | StringTextComponent | +| literals.rb:108:4:108:6 | :"foo" | SymbolLiteral | 0 | literals.rb:108:4:108:6 | foo | StringTextComponent | +| literals.rb:108:8:108:20 | :"bar#{...}" | SymbolLiteral | 0 | literals.rb:108:8:108:10 | bar | StringTextComponent | +| literals.rb:108:8:108:20 | :"bar#{...}" | SymbolLiteral | 1 | literals.rb:108:11:108:20 | #{...} | StringInterpolationComponent | +| literals.rb:108:22:108:24 | :"baz" | SymbolLiteral | 0 | literals.rb:108:22:108:24 | baz | StringTextComponent | +| literals.rb:109:4:109:6 | :"foo" | SymbolLiteral | 0 | literals.rb:109:4:109:6 | foo | StringTextComponent | +| literals.rb:109:8:109:12 | :"bar#{" | SymbolLiteral | 0 | literals.rb:109:8:109:12 | bar#{ | StringTextComponent | +| literals.rb:109:14:109:14 | :"2" | SymbolLiteral | 0 | literals.rb:109:14:109:14 | 2 | StringTextComponent | +| literals.rb:109:16:109:16 | :"+" | SymbolLiteral | 0 | literals.rb:109:16:109:16 | + | StringTextComponent | +| literals.rb:109:18:109:18 | :"4" | SymbolLiteral | 0 | literals.rb:109:18:109:18 | 4 | StringTextComponent | +| literals.rb:109:20:109:20 | :"}" | SymbolLiteral | 0 | literals.rb:109:20:109:20 | } | StringTextComponent | +| literals.rb:109:22:109:24 | :"baz" | SymbolLiteral | 0 | literals.rb:109:22:109:24 | baz | StringTextComponent | +| literals.rb:113:22:113:26 | "baz" | StringLiteral | 0 | literals.rb:113:23:113:25 | baz | StringTextComponent | +| literals.rb:126:1:126:7 | `ls -l` | SubshellLiteral | 0 | literals.rb:126:2:126:6 | ls -l | StringTextComponent | +| literals.rb:127:1:127:9 | `ls -l` | SubshellLiteral | 0 | literals.rb:127:4:127:8 | ls -l | StringTextComponent | +| literals.rb:128:1:128:18 | `du -d #{...}` | SubshellLiteral | 0 | literals.rb:128:2:128:7 | du -d | StringTextComponent | +| literals.rb:128:1:128:18 | `du -d #{...}` | SubshellLiteral | 1 | literals.rb:128:8:128:17 | #{...} | StringInterpolationComponent | +| literals.rb:129:1:129:20 | `du -d #{...}` | SubshellLiteral | 0 | literals.rb:129:4:129:9 | du -d | StringTextComponent | +| literals.rb:129:1:129:20 | `du -d #{...}` | SubshellLiteral | 1 | literals.rb:129:10:129:19 | #{...} | StringInterpolationComponent | +| literals.rb:133:1:133:5 | /foo/ | RegExpLiteral | 0 | literals.rb:133:2:133:4 | foo | StringTextComponent | +| literals.rb:134:1:134:6 | /foo/ | RegExpLiteral | 0 | literals.rb:134:2:134:4 | foo | StringTextComponent | +| literals.rb:135:1:135:13 | /foo+\\sbar\\S/ | RegExpLiteral | 0 | literals.rb:135:2:135:5 | foo+ | StringTextComponent | +| literals.rb:135:1:135:13 | /foo+\\sbar\\S/ | RegExpLiteral | 1 | literals.rb:135:6:135:7 | \\s | StringEscapeSequenceComponent | +| literals.rb:135:1:135:13 | /foo+\\sbar\\S/ | RegExpLiteral | 2 | literals.rb:135:8:135:10 | bar | StringTextComponent | +| literals.rb:135:1:135:13 | /foo+\\sbar\\S/ | RegExpLiteral | 3 | literals.rb:135:11:135:12 | \\S | StringEscapeSequenceComponent | +| literals.rb:136:1:136:18 | /foo#{...}bar/ | RegExpLiteral | 0 | literals.rb:136:2:136:4 | foo | StringTextComponent | +| literals.rb:136:1:136:18 | /foo#{...}bar/ | RegExpLiteral | 1 | literals.rb:136:5:136:14 | #{...} | StringInterpolationComponent | +| literals.rb:136:1:136:18 | /foo#{...}bar/ | RegExpLiteral | 2 | literals.rb:136:15:136:17 | bar | StringTextComponent | +| literals.rb:137:1:137:8 | /foo/ | RegExpLiteral | 0 | literals.rb:137:2:137:4 | foo | StringTextComponent | +| literals.rb:139:1:139:7 | /foo/ | RegExpLiteral | 0 | literals.rb:139:4:139:6 | foo | StringTextComponent | +| literals.rb:140:1:140:8 | /foo/ | RegExpLiteral | 0 | literals.rb:140:4:140:6 | foo | StringTextComponent | +| literals.rb:141:1:141:15 | /foo+\\sbar\\S/ | RegExpLiteral | 0 | literals.rb:141:4:141:7 | foo+ | StringTextComponent | +| literals.rb:141:1:141:15 | /foo+\\sbar\\S/ | RegExpLiteral | 1 | literals.rb:141:8:141:9 | \\s | StringEscapeSequenceComponent | +| literals.rb:141:1:141:15 | /foo+\\sbar\\S/ | RegExpLiteral | 2 | literals.rb:141:10:141:12 | bar | StringTextComponent | +| literals.rb:141:1:141:15 | /foo+\\sbar\\S/ | RegExpLiteral | 3 | literals.rb:141:13:141:14 | \\S | StringEscapeSequenceComponent | +| literals.rb:142:1:142:20 | /foo#{...}bar/ | RegExpLiteral | 0 | literals.rb:142:4:142:6 | foo | StringTextComponent | +| literals.rb:142:1:142:20 | /foo#{...}bar/ | RegExpLiteral | 1 | literals.rb:142:7:142:16 | #{...} | StringInterpolationComponent | +| literals.rb:142:1:142:20 | /foo#{...}bar/ | RegExpLiteral | 2 | literals.rb:142:17:142:19 | bar | StringTextComponent | +| literals.rb:143:1:143:10 | /foo/ | RegExpLiteral | 0 | literals.rb:143:4:143:6 | foo | StringTextComponent | +| literals.rb:146:1:146:34 | "abcdefghijklmnopqrstuvwxyzabcdef" | StringLiteral | 0 | literals.rb:146:2:146:33 | abcdefghijklmnopqrstuvwxyzabcdef | StringTextComponent | +| literals.rb:147:1:147:35 | "foobarfoobarfoobarfoobarfooba..." | StringLiteral | 0 | literals.rb:147:2:147:34 | foobarfoobarfoobarfoobarfoobarfoo | StringTextComponent | +| literals.rb:148:1:148:40 | "foobar\\\\foobar\\\\foobar\\\\fooba..." | StringLiteral | 0 | literals.rb:148:2:148:7 | foobar | StringTextComponent | +| literals.rb:148:1:148:40 | "foobar\\\\foobar\\\\foobar\\\\fooba..." | StringLiteral | 1 | literals.rb:148:8:148:9 | \\\\ | StringEscapeSequenceComponent | +| literals.rb:148:1:148:40 | "foobar\\\\foobar\\\\foobar\\\\fooba..." | StringLiteral | 2 | literals.rb:148:10:148:15 | foobar | StringTextComponent | +| literals.rb:148:1:148:40 | "foobar\\\\foobar\\\\foobar\\\\fooba..." | StringLiteral | 3 | literals.rb:148:16:148:17 | \\\\ | StringEscapeSequenceComponent | +| literals.rb:148:1:148:40 | "foobar\\\\foobar\\\\foobar\\\\fooba..." | StringLiteral | 4 | literals.rb:148:18:148:23 | foobar | StringTextComponent | +| literals.rb:148:1:148:40 | "foobar\\\\foobar\\\\foobar\\\\fooba..." | StringLiteral | 5 | literals.rb:148:24:148:25 | \\\\ | StringEscapeSequenceComponent | +| literals.rb:148:1:148:40 | "foobar\\\\foobar\\\\foobar\\\\fooba..." | StringLiteral | 6 | literals.rb:148:26:148:31 | foobar | StringTextComponent | +| literals.rb:148:1:148:40 | "foobar\\\\foobar\\\\foobar\\\\fooba..." | StringLiteral | 7 | literals.rb:148:32:148:33 | \\\\ | StringEscapeSequenceComponent | +| literals.rb:148:1:148:40 | "foobar\\\\foobar\\\\foobar\\\\fooba..." | StringLiteral | 8 | literals.rb:148:34:148:39 | foobar | StringTextComponent | +| literals.rb:151:9:151:13 | <<SQL | HereDoc | 0 | literals.rb:151:22:152:20 | \nselect * from table\n | StringTextComponent | +| literals.rb:151:16:151:20 | <<SQL | HereDoc | 0 | literals.rb:153:4:154:13 | \nwhere name = | StringTextComponent | +| literals.rb:151:16:151:20 | <<SQL | HereDoc | 1 | literals.rb:154:14:154:22 | #{...} | StringInterpolationComponent | +| literals.rb:151:16:151:20 | <<SQL | HereDoc | 2 | literals.rb:154:23:154:23 | \n | StringTextComponent | +| literals.rb:158:11:158:16 | <<-BLA | HereDoc | 0 | literals.rb:158:17:159:9 | \nsome text | StringTextComponent | +| literals.rb:158:11:158:16 | <<-BLA | HereDoc | 1 | literals.rb:159:10:159:11 | \\n | StringEscapeSequenceComponent | +| literals.rb:158:11:158:16 | <<-BLA | HereDoc | 2 | literals.rb:159:12:160:2 | and some more\n | StringTextComponent | +| literals.rb:163:9:163:19 | <<~SQUIGGLY | HereDoc | 0 | literals.rb:163:20:164:18 | \n indented stuff\n | StringTextComponent | +| literals.rb:167:9:167:15 | <<"DOC" | HereDoc | 0 | literals.rb:167:16:168:11 | \n text with | StringTextComponent | +| literals.rb:167:9:167:15 | <<"DOC" | HereDoc | 1 | literals.rb:168:12:168:29 | #{...} | StringInterpolationComponent | +| literals.rb:167:9:167:15 | <<"DOC" | HereDoc | 2 | literals.rb:168:30:168:32 | !\n | StringTextComponent | +| literals.rb:172:9:172:15 | <<'DOC' | HereDoc | 0 | literals.rb:172:16:173:14 | \n text without | StringTextComponent | +| literals.rb:172:9:172:15 | <<'DOC' | HereDoc | 1 | literals.rb:173:15:173:32 | #{...} | StringInterpolationComponent | +| literals.rb:172:9:172:15 | <<'DOC' | HereDoc | 2 | literals.rb:173:33:173:35 | !\n | StringTextComponent | +| literals.rb:176:10:176:19 | <<`SCRIPT` | HereDoc | 0 | literals.rb:176:20:177:14 | \n cat file.txt\n | StringTextComponent | +stringInterpolations +| literals.rb:58:10:58:19 | #{...} | 0 | literals.rb:58:13:58:17 | ... + ... | AddExpr | +| literals.rb:59:12:59:21 | #{...} | 0 | literals.rb:59:15:59:19 | ... + ... | AddExpr | +| literals.rb:64:11:64:20 | #{...} | 0 | literals.rb:64:14:64:18 | ... * ... | MulExpr | +| literals.rb:65:6:65:30 | #{...} | 0 | literals.rb:65:9:65:28 | "bar #{...} baz" | StringLiteral | +| literals.rb:65:14:65:23 | #{...} | 0 | literals.rb:65:17:65:21 | ... + ... | AddExpr | +| literals.rb:66:6:66:21 | #{...} | 0 | literals.rb:66:9:66:14 | call to blah | MethodCall | +| literals.rb:66:6:66:21 | #{...} | 1 | literals.rb:66:17:66:19 | ... + ... | AddExpr | +| literals.rb:87:7:87:15 | #{...} | 0 | literals.rb:87:10:87:14 | ... + ... | AddExpr | +| literals.rb:101:11:101:16 | #{...} | 0 | literals.rb:101:13:101:15 | ... + ... | AddExpr | +| literals.rb:108:11:108:20 | #{...} | 0 | literals.rb:108:14:108:18 | ... + ... | AddExpr | +| literals.rb:128:8:128:17 | #{...} | 0 | literals.rb:128:11:128:15 | ... + ... | AddExpr | +| literals.rb:129:10:129:19 | #{...} | 0 | literals.rb:129:13:129:17 | ... - ... | SubExpr | +| literals.rb:136:5:136:14 | #{...} | 0 | literals.rb:136:8:136:12 | ... + ... | AddExpr | +| literals.rb:142:7:142:16 | #{...} | 0 | literals.rb:142:10:142:14 | ... + ... | AddExpr | +| literals.rb:154:14:154:22 | #{...} | 0 | literals.rb:154:17:154:20 | call to name | MethodCall | +| literals.rb:168:12:168:29 | #{...} | 0 | literals.rb:168:15:168:27 | call to interpolation | MethodCall | +| literals.rb:173:15:173:32 | #{...} | 0 | literals.rb:173:18:173:30 | call to interpolation | MethodCall | +concatenatedStrings +| literals.rb:62:1:62:17 | "..." "..." | foobarbaz | 0 | literals.rb:62:1:62:5 | "foo" | +| literals.rb:62:1:62:17 | "..." "..." | foobarbaz | 1 | literals.rb:62:7:62:11 | "bar" | +| literals.rb:62:1:62:17 | "..." "..." | foobarbaz | 2 | literals.rb:62:13:62:17 | "baz" | +| literals.rb:63:1:63:19 | "..." "..." | foobarbaz | 0 | literals.rb:63:1:63:7 | "foo" | +| literals.rb:63:1:63:19 | "..." "..." | foobarbaz | 1 | literals.rb:63:9:63:13 | "bar" | +| literals.rb:63:1:63:19 | "..." "..." | foobarbaz | 2 | literals.rb:63:15:63:19 | "baz" | +| literals.rb:64:1:64:27 | "..." "..." | <none> | 0 | literals.rb:64:1:64:5 | "foo" | +| literals.rb:64:1:64:27 | "..." "..." | <none> | 1 | literals.rb:64:7:64:21 | "bar#{...}" | +| literals.rb:64:1:64:27 | "..." "..." | <none> | 2 | literals.rb:64:23:64:27 | "baz" | +arrayLiterals +| literals.rb:92:1:92:2 | [...] | 0 | +| literals.rb:93:1:93:9 | [...] | 3 | +| literals.rb:94:1:94:14 | [...] | 3 | +| literals.rb:95:1:95:11 | [...] | 2 | +| literals.rb:95:5:95:10 | [...] | 2 | +| literals.rb:98:1:98:4 | %w(...) | 0 | +| literals.rb:99:1:99:15 | %w(...) | 3 | +| literals.rb:100:1:100:15 | %w(...) | 3 | +| literals.rb:101:1:101:21 | %w(...) | 3 | +| literals.rb:102:1:102:21 | %w(...) | 3 | +| literals.rb:105:1:105:4 | %i(...) | 0 | +| literals.rb:106:1:106:15 | %i(...) | 3 | +| literals.rb:107:1:107:15 | %i(...) | 3 | +| literals.rb:108:1:108:25 | %i(...) | 3 | +| literals.rb:109:1:109:25 | %i(...) | 7 | +arrayLiteralElements +| literals.rb:93:1:93:9 | [...] | 0 | literals.rb:93:2:93:2 | 1 | IntegerLiteral | +| literals.rb:93:1:93:9 | [...] | 1 | literals.rb:93:5:93:5 | 2 | IntegerLiteral | +| literals.rb:93:1:93:9 | [...] | 2 | literals.rb:93:8:93:8 | 3 | IntegerLiteral | +| literals.rb:94:1:94:14 | [...] | 0 | literals.rb:94:2:94:2 | 4 | IntegerLiteral | +| literals.rb:94:1:94:14 | [...] | 1 | literals.rb:94:5:94:5 | 5 | IntegerLiteral | +| literals.rb:94:1:94:14 | [...] | 2 | literals.rb:94:8:94:13 | ... / ... | DivExpr | +| literals.rb:95:1:95:11 | [...] | 0 | literals.rb:95:2:95:2 | 7 | IntegerLiteral | +| literals.rb:95:1:95:11 | [...] | 1 | literals.rb:95:5:95:10 | [...] | ArrayLiteral | +| literals.rb:95:5:95:10 | [...] | 0 | literals.rb:95:6:95:6 | 8 | IntegerLiteral | +| literals.rb:95:5:95:10 | [...] | 1 | literals.rb:95:9:95:9 | 9 | IntegerLiteral | +| literals.rb:99:1:99:15 | %w(...) | 0 | literals.rb:99:4:99:6 | "foo" | StringLiteral | +| literals.rb:99:1:99:15 | %w(...) | 1 | literals.rb:99:8:99:10 | "bar" | StringLiteral | +| literals.rb:99:1:99:15 | %w(...) | 2 | literals.rb:99:12:99:14 | "baz" | StringLiteral | +| literals.rb:100:1:100:15 | %w(...) | 0 | literals.rb:100:4:100:6 | "foo" | StringLiteral | +| literals.rb:100:1:100:15 | %w(...) | 1 | literals.rb:100:8:100:10 | "bar" | StringLiteral | +| literals.rb:100:1:100:15 | %w(...) | 2 | literals.rb:100:12:100:14 | "baz" | StringLiteral | +| literals.rb:101:1:101:21 | %w(...) | 0 | literals.rb:101:4:101:6 | "foo" | StringLiteral | +| literals.rb:101:1:101:21 | %w(...) | 1 | literals.rb:101:8:101:16 | "bar#{...}" | StringLiteral | +| literals.rb:101:1:101:21 | %w(...) | 2 | literals.rb:101:18:101:20 | "baz" | StringLiteral | +| literals.rb:102:1:102:21 | %w(...) | 0 | literals.rb:102:4:102:6 | "foo" | StringLiteral | +| literals.rb:102:1:102:21 | %w(...) | 1 | literals.rb:102:8:102:16 | "bar#{1+1}" | StringLiteral | +| literals.rb:102:1:102:21 | %w(...) | 2 | literals.rb:102:18:102:20 | "baz" | StringLiteral | +| literals.rb:106:1:106:15 | %i(...) | 0 | literals.rb:106:4:106:6 | :"foo" | SymbolLiteral | +| literals.rb:106:1:106:15 | %i(...) | 1 | literals.rb:106:8:106:10 | :"bar" | SymbolLiteral | +| literals.rb:106:1:106:15 | %i(...) | 2 | literals.rb:106:12:106:14 | :"baz" | SymbolLiteral | +| literals.rb:107:1:107:15 | %i(...) | 0 | literals.rb:107:4:107:6 | :"foo" | SymbolLiteral | +| literals.rb:107:1:107:15 | %i(...) | 1 | literals.rb:107:8:107:10 | :"bar" | SymbolLiteral | +| literals.rb:107:1:107:15 | %i(...) | 2 | literals.rb:107:12:107:14 | :"baz" | SymbolLiteral | +| literals.rb:108:1:108:25 | %i(...) | 0 | literals.rb:108:4:108:6 | :"foo" | SymbolLiteral | +| literals.rb:108:1:108:25 | %i(...) | 1 | literals.rb:108:8:108:20 | :"bar#{...}" | SymbolLiteral | +| literals.rb:108:1:108:25 | %i(...) | 2 | literals.rb:108:22:108:24 | :"baz" | SymbolLiteral | +| literals.rb:109:1:109:25 | %i(...) | 0 | literals.rb:109:4:109:6 | :"foo" | SymbolLiteral | +| literals.rb:109:1:109:25 | %i(...) | 1 | literals.rb:109:8:109:12 | :"bar#{" | SymbolLiteral | +| literals.rb:109:1:109:25 | %i(...) | 2 | literals.rb:109:14:109:14 | :"2" | SymbolLiteral | +| literals.rb:109:1:109:25 | %i(...) | 3 | literals.rb:109:16:109:16 | :"+" | SymbolLiteral | +| literals.rb:109:1:109:25 | %i(...) | 4 | literals.rb:109:18:109:18 | :"4" | SymbolLiteral | +| literals.rb:109:1:109:25 | %i(...) | 5 | literals.rb:109:20:109:20 | :"}" | SymbolLiteral | +| literals.rb:109:1:109:25 | %i(...) | 6 | literals.rb:109:22:109:24 | :"baz" | SymbolLiteral | +hashLiterals +| literals.rb:84:1:84:14 | {...} | 1 | +| literals.rb:112:1:112:2 | {...} | 0 | +| literals.rb:113:1:113:33 | {...} | 3 | +| literals.rb:114:1:114:17 | {...} | 2 | +hashLiteralElements +| literals.rb:84:1:84:14 | {...} | 0 | literals.rb:84:3:84:12 | Pair | Pair | +| literals.rb:113:1:113:33 | {...} | 0 | literals.rb:113:3:113:8 | Pair | Pair | +| literals.rb:113:1:113:33 | {...} | 1 | literals.rb:113:11:113:19 | Pair | Pair | +| literals.rb:113:1:113:33 | {...} | 2 | literals.rb:113:22:113:31 | Pair | Pair | +| literals.rb:114:1:114:17 | {...} | 0 | literals.rb:114:3:114:8 | Pair | Pair | +| literals.rb:114:1:114:17 | {...} | 1 | literals.rb:114:11:114:15 | ** ... | HashSplatExpr | +hashLiteralKeyValuePairs +| literals.rb:84:1:84:14 | {...} | literals.rb:84:3:84:12 | Pair | literals.rb:84:3:84:5 | :foo | literals.rb:84:8:84:12 | "bar" | +| literals.rb:113:1:113:33 | {...} | literals.rb:113:3:113:8 | Pair | literals.rb:113:3:113:5 | :foo | literals.rb:113:8:113:8 | 1 | +| literals.rb:113:1:113:33 | {...} | literals.rb:113:11:113:19 | Pair | literals.rb:113:11:113:14 | :bar | literals.rb:113:19:113:19 | 2 | +| literals.rb:113:1:113:33 | {...} | literals.rb:113:22:113:31 | Pair | literals.rb:113:22:113:26 | "baz" | literals.rb:113:31:113:31 | 3 | +| literals.rb:114:1:114:17 | {...} | literals.rb:114:3:114:8 | Pair | literals.rb:114:3:114:5 | :foo | literals.rb:114:8:114:8 | 7 | +finiteRangeLiterals +| literals.rb:117:2:117:6 | _ .. _ | literals.rb:117:2:117:2 | 1 | literals.rb:117:5:117:6 | 10 | +| literals.rb:118:2:118:7 | _ ... _ | literals.rb:118:2:118:2 | 1 | literals.rb:118:6:118:7 | 10 | +| literals.rb:119:2:119:7 | _ .. _ | literals.rb:119:2:119:2 | 1 | literals.rb:119:7:119:7 | 0 | +| literals.rb:120:2:120:11 | _ .. _ | literals.rb:120:2:120:6 | call to start | literals.rb:120:9:120:11 | ... + ... | +beginlessRangeLiterals +| literals.rb:122:2:122:4 | _ .. _ | literals.rb:122:4:122:4 | 1 | +endlessRangeLiterals +| literals.rb:121:2:121:4 | _ .. _ | literals.rb:121:2:121:2 | 1 | +| literals.rb:123:2:123:4 | _ .. _ | literals.rb:123:2:123:2 | 0 | +inclusiveRangeLiterals +| literals.rb:117:2:117:6 | _ .. _ | +| literals.rb:119:2:119:7 | _ .. _ | +| literals.rb:120:2:120:11 | _ .. _ | +| literals.rb:121:2:121:4 | _ .. _ | +| literals.rb:122:2:122:4 | _ .. _ | +| literals.rb:123:2:123:4 | _ .. _ | +exclusiveRangeLiterals +| literals.rb:118:2:118:7 | _ ... _ | +numericLiterals +| literals.rb:10:1:10:4 | 1234 | IntegerLiteral | 1234 | +| literals.rb:11:1:11:5 | 5_678 | IntegerLiteral | 5_678 | +| literals.rb:12:1:12:1 | 0 | IntegerLiteral | 0 | +| literals.rb:13:1:13:5 | 0d900 | IntegerLiteral | 0d900 | +| literals.rb:16:1:16:6 | 0x1234 | IntegerLiteral | 0x1234 | +| literals.rb:17:1:17:10 | 0xdeadbeef | IntegerLiteral | 0xdeadbeef | +| literals.rb:18:1:18:11 | 0xF00D_face | IntegerLiteral | 0xF00D_face | +| literals.rb:21:1:21:4 | 0123 | IntegerLiteral | 0123 | +| literals.rb:22:1:22:5 | 0o234 | IntegerLiteral | 0o234 | +| literals.rb:23:1:23:6 | 0O45_6 | IntegerLiteral | 0O45_6 | +| literals.rb:26:1:26:10 | 0b10010100 | IntegerLiteral | 0b10010100 | +| literals.rb:27:1:27:11 | 0B011_01101 | IntegerLiteral | 0B011_01101 | +| literals.rb:30:1:30:5 | 12.34 | FloatLiteral | 12.34 | +| literals.rb:31:1:31:7 | 1234e-2 | FloatLiteral | 1234e-2 | +| literals.rb:32:1:32:7 | 1.234E1 | FloatLiteral | 1.234E1 | +| literals.rb:35:1:35:3 | 23r | RationalLiteral | 23r | +| literals.rb:36:1:36:5 | 9.85r | RationalLiteral | 9.85r | +| literals.rb:39:1:39:2 | 2i | ComplexLiteral | 2i | +| literals.rb:58:13:58:13 | 2 | IntegerLiteral | 2 | +| literals.rb:58:17:58:17 | 2 | IntegerLiteral | 2 | +| literals.rb:59:15:59:15 | 3 | IntegerLiteral | 3 | +| literals.rb:59:19:59:19 | 4 | IntegerLiteral | 4 | +| literals.rb:64:14:64:14 | 1 | IntegerLiteral | 1 | +| literals.rb:64:18:64:18 | 1 | IntegerLiteral | 1 | +| literals.rb:65:17:65:17 | 2 | IntegerLiteral | 2 | +| literals.rb:65:21:65:21 | 3 | IntegerLiteral | 3 | +| literals.rb:66:17:66:17 | 1 | IntegerLiteral | 1 | +| literals.rb:66:19:66:19 | 9 | IntegerLiteral | 9 | +| literals.rb:87:10:87:10 | 2 | IntegerLiteral | 2 | +| literals.rb:87:14:87:14 | 2 | IntegerLiteral | 2 | +| literals.rb:93:2:93:2 | 1 | IntegerLiteral | 1 | +| literals.rb:93:5:93:5 | 2 | IntegerLiteral | 2 | +| literals.rb:93:8:93:8 | 3 | IntegerLiteral | 3 | +| literals.rb:94:2:94:2 | 4 | IntegerLiteral | 4 | +| literals.rb:94:5:94:5 | 5 | IntegerLiteral | 5 | +| literals.rb:94:8:94:9 | 12 | IntegerLiteral | 12 | +| literals.rb:94:13:94:13 | 2 | IntegerLiteral | 2 | +| literals.rb:95:2:95:2 | 7 | IntegerLiteral | 7 | +| literals.rb:95:6:95:6 | 8 | IntegerLiteral | 8 | +| literals.rb:95:9:95:9 | 9 | IntegerLiteral | 9 | +| literals.rb:101:13:101:13 | 1 | IntegerLiteral | 1 | +| literals.rb:101:15:101:15 | 1 | IntegerLiteral | 1 | +| literals.rb:108:14:108:14 | 2 | IntegerLiteral | 2 | +| literals.rb:108:18:108:18 | 4 | IntegerLiteral | 4 | +| literals.rb:113:8:113:8 | 1 | IntegerLiteral | 1 | +| literals.rb:113:19:113:19 | 2 | IntegerLiteral | 2 | +| literals.rb:113:31:113:31 | 3 | IntegerLiteral | 3 | +| literals.rb:114:8:114:8 | 7 | IntegerLiteral | 7 | +| literals.rb:117:2:117:2 | 1 | IntegerLiteral | 1 | +| literals.rb:117:5:117:6 | 10 | IntegerLiteral | 10 | +| literals.rb:118:2:118:2 | 1 | IntegerLiteral | 1 | +| literals.rb:118:6:118:7 | 10 | IntegerLiteral | 10 | +| literals.rb:119:2:119:2 | 1 | IntegerLiteral | 1 | +| literals.rb:119:7:119:7 | 0 | IntegerLiteral | 0 | +| literals.rb:120:9:120:9 | 2 | IntegerLiteral | 2 | +| literals.rb:120:11:120:11 | 3 | IntegerLiteral | 3 | +| literals.rb:121:2:121:2 | 1 | IntegerLiteral | 1 | +| literals.rb:122:4:122:4 | 1 | IntegerLiteral | 1 | +| literals.rb:123:2:123:2 | 0 | IntegerLiteral | 0 | +| literals.rb:123:6:123:6 | 1 | IntegerLiteral | 1 | +| literals.rb:128:11:128:11 | 1 | IntegerLiteral | 1 | +| literals.rb:128:15:128:15 | 1 | IntegerLiteral | 1 | +| literals.rb:129:13:129:13 | 5 | IntegerLiteral | 5 | +| literals.rb:129:17:129:17 | 4 | IntegerLiteral | 4 | +| literals.rb:136:8:136:8 | 1 | IntegerLiteral | 1 | +| literals.rb:136:12:136:12 | 1 | IntegerLiteral | 1 | +| literals.rb:142:10:142:10 | 1 | IntegerLiteral | 1 | +| literals.rb:142:14:142:14 | 1 | IntegerLiteral | 1 | +integerLiterals +| literals.rb:10:1:10:4 | 1234 | IntegerLiteral | 1234 | +| literals.rb:11:1:11:5 | 5_678 | IntegerLiteral | 5_678 | +| literals.rb:12:1:12:1 | 0 | IntegerLiteral | 0 | +| literals.rb:13:1:13:5 | 0d900 | IntegerLiteral | 0d900 | +| literals.rb:16:1:16:6 | 0x1234 | IntegerLiteral | 0x1234 | +| literals.rb:17:1:17:10 | 0xdeadbeef | IntegerLiteral | 0xdeadbeef | +| literals.rb:18:1:18:11 | 0xF00D_face | IntegerLiteral | 0xF00D_face | +| literals.rb:21:1:21:4 | 0123 | IntegerLiteral | 0123 | +| literals.rb:22:1:22:5 | 0o234 | IntegerLiteral | 0o234 | +| literals.rb:23:1:23:6 | 0O45_6 | IntegerLiteral | 0O45_6 | +| literals.rb:26:1:26:10 | 0b10010100 | IntegerLiteral | 0b10010100 | +| literals.rb:27:1:27:11 | 0B011_01101 | IntegerLiteral | 0B011_01101 | +| literals.rb:58:13:58:13 | 2 | IntegerLiteral | 2 | +| literals.rb:58:17:58:17 | 2 | IntegerLiteral | 2 | +| literals.rb:59:15:59:15 | 3 | IntegerLiteral | 3 | +| literals.rb:59:19:59:19 | 4 | IntegerLiteral | 4 | +| literals.rb:64:14:64:14 | 1 | IntegerLiteral | 1 | +| literals.rb:64:18:64:18 | 1 | IntegerLiteral | 1 | +| literals.rb:65:17:65:17 | 2 | IntegerLiteral | 2 | +| literals.rb:65:21:65:21 | 3 | IntegerLiteral | 3 | +| literals.rb:66:17:66:17 | 1 | IntegerLiteral | 1 | +| literals.rb:66:19:66:19 | 9 | IntegerLiteral | 9 | +| literals.rb:87:10:87:10 | 2 | IntegerLiteral | 2 | +| literals.rb:87:14:87:14 | 2 | IntegerLiteral | 2 | +| literals.rb:93:2:93:2 | 1 | IntegerLiteral | 1 | +| literals.rb:93:5:93:5 | 2 | IntegerLiteral | 2 | +| literals.rb:93:8:93:8 | 3 | IntegerLiteral | 3 | +| literals.rb:94:2:94:2 | 4 | IntegerLiteral | 4 | +| literals.rb:94:5:94:5 | 5 | IntegerLiteral | 5 | +| literals.rb:94:8:94:9 | 12 | IntegerLiteral | 12 | +| literals.rb:94:13:94:13 | 2 | IntegerLiteral | 2 | +| literals.rb:95:2:95:2 | 7 | IntegerLiteral | 7 | +| literals.rb:95:6:95:6 | 8 | IntegerLiteral | 8 | +| literals.rb:95:9:95:9 | 9 | IntegerLiteral | 9 | +| literals.rb:101:13:101:13 | 1 | IntegerLiteral | 1 | +| literals.rb:101:15:101:15 | 1 | IntegerLiteral | 1 | +| literals.rb:108:14:108:14 | 2 | IntegerLiteral | 2 | +| literals.rb:108:18:108:18 | 4 | IntegerLiteral | 4 | +| literals.rb:113:8:113:8 | 1 | IntegerLiteral | 1 | +| literals.rb:113:19:113:19 | 2 | IntegerLiteral | 2 | +| literals.rb:113:31:113:31 | 3 | IntegerLiteral | 3 | +| literals.rb:114:8:114:8 | 7 | IntegerLiteral | 7 | +| literals.rb:117:2:117:2 | 1 | IntegerLiteral | 1 | +| literals.rb:117:5:117:6 | 10 | IntegerLiteral | 10 | +| literals.rb:118:2:118:2 | 1 | IntegerLiteral | 1 | +| literals.rb:118:6:118:7 | 10 | IntegerLiteral | 10 | +| literals.rb:119:2:119:2 | 1 | IntegerLiteral | 1 | +| literals.rb:119:7:119:7 | 0 | IntegerLiteral | 0 | +| literals.rb:120:9:120:9 | 2 | IntegerLiteral | 2 | +| literals.rb:120:11:120:11 | 3 | IntegerLiteral | 3 | +| literals.rb:121:2:121:2 | 1 | IntegerLiteral | 1 | +| literals.rb:122:4:122:4 | 1 | IntegerLiteral | 1 | +| literals.rb:123:2:123:2 | 0 | IntegerLiteral | 0 | +| literals.rb:123:6:123:6 | 1 | IntegerLiteral | 1 | +| literals.rb:128:11:128:11 | 1 | IntegerLiteral | 1 | +| literals.rb:128:15:128:15 | 1 | IntegerLiteral | 1 | +| literals.rb:129:13:129:13 | 5 | IntegerLiteral | 5 | +| literals.rb:129:17:129:17 | 4 | IntegerLiteral | 4 | +| literals.rb:136:8:136:8 | 1 | IntegerLiteral | 1 | +| literals.rb:136:12:136:12 | 1 | IntegerLiteral | 1 | +| literals.rb:142:10:142:10 | 1 | IntegerLiteral | 1 | +| literals.rb:142:14:142:14 | 1 | IntegerLiteral | 1 | +floatLiterals +| literals.rb:30:1:30:5 | 12.34 | FloatLiteral | 12.34 | +| literals.rb:31:1:31:7 | 1234e-2 | FloatLiteral | 1234e-2 | +| literals.rb:32:1:32:7 | 1.234E1 | FloatLiteral | 1.234E1 | +rationalLiterals +| literals.rb:35:1:35:3 | 23r | RationalLiteral | 23r | +| literals.rb:36:1:36:5 | 9.85r | RationalLiteral | 9.85r | +complexLiterals +| literals.rb:39:1:39:2 | 2i | ComplexLiteral | 2i | diff --git a/ruby/ql/test/library-tests/ast/literals/literals.ql b/ruby/ql/test/library-tests/ast/literals/literals.ql new file mode 100644 index 00000000000..52469a35e50 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/literals/literals.ql @@ -0,0 +1,110 @@ +import ruby + +query predicate allLiterals(Literal l, string pClass, string valueText) { + pClass = l.getAPrimaryQlClass() and + ( + valueText = l.getValueText() + or + not exists(l.getValueText()) and valueText = "<none>" + ) +} + +query predicate stringlikeLiterals(StringlikeLiteral l, string valueText) { + valueText = l.getValueText() + or + not exists(l.getValueText()) and valueText = "<none>" +} + +query predicate stringLiterals(StringLiteral l, string valueText) { + stringlikeLiterals(l, valueText) +} + +query predicate regExpLiterals(RegExpLiteral l, string valueText, string flags) { + stringlikeLiterals(l, valueText) and flags = l.getFlagString() +} + +query predicate symbolLiterals(SymbolLiteral l, string valueText) { + stringlikeLiterals(l, valueText) +} + +query predicate subshellLiterals(SubshellLiteral l, string valueText) { + stringlikeLiterals(l, valueText) +} + +query predicate stringComponents( + StringlikeLiteral l, string lClass, int i, StringComponent c, string cClass +) { + lClass = l.getAPrimaryQlClass() and + c = l.getComponent(i) and + cClass = c.getAPrimaryQlClass() +} + +query predicate stringInterpolations(StringInterpolationComponent c, int i, Expr e, string eClass) { + e = c.getStmt(i) and eClass = e.getAPrimaryQlClass() +} + +query predicate concatenatedStrings(StringConcatenation sc, string valueText, int n, StringLiteral l) { + l = sc.getString(n) and + ( + valueText = sc.getConcatenatedValueText() + or + not exists(sc.getConcatenatedValueText()) and valueText = "<none>" + ) +} + +query predicate arrayLiterals(ArrayLiteral l, int numElements) { + numElements = l.getNumberOfElements() +} + +query predicate arrayLiteralElements(ArrayLiteral l, int n, Expr elementN, string pClass) { + elementN = l.getElement(n) and pClass = elementN.getAPrimaryQlClass() +} + +query predicate hashLiterals(HashLiteral l, int numElements) { + numElements = l.getNumberOfElements() +} + +query predicate hashLiteralElements(HashLiteral l, int n, Expr elementN, string pClass) { + elementN = l.getElement(n) and pClass = elementN.getAPrimaryQlClass() +} + +query predicate hashLiteralKeyValuePairs(HashLiteral l, Pair p, Expr k, Expr v) { + p = l.getAKeyValuePair() and k = p.getKey() and v = p.getValue() +} + +query predicate finiteRangeLiterals(RangeLiteral l, Expr begin, Expr end) { + begin = l.getBegin() and + end = l.getEnd() +} + +query predicate beginlessRangeLiterals(RangeLiteral l, Expr end) { + not exists(l.getBegin()) and end = l.getEnd() +} + +query predicate endlessRangeLiterals(RangeLiteral l, Expr begin) { + begin = l.getBegin() and not exists(l.getEnd()) +} + +query predicate inclusiveRangeLiterals(RangeLiteral l) { l.isInclusive() } + +query predicate exclusiveRangeLiterals(RangeLiteral l) { l.isExclusive() } + +query predicate numericLiterals(NumericLiteral l, string pClass, string valueText) { + allLiterals(l, pClass, valueText) +} + +query predicate integerLiterals(IntegerLiteral l, string pClass, string valueText) { + allLiterals(l, pClass, valueText) +} + +query predicate floatLiterals(FloatLiteral l, string pClass, string valueText) { + allLiterals(l, pClass, valueText) +} + +query predicate rationalLiterals(RationalLiteral l, string pClass, string valueText) { + allLiterals(l, pClass, valueText) +} + +query predicate complexLiterals(ComplexLiteral l, string pClass, string valueText) { + allLiterals(l, pClass, valueText) +} diff --git a/ruby/ql/test/library-tests/ast/literals/literals.rb b/ruby/ql/test/library-tests/ast/literals/literals.rb new file mode 100644 index 00000000000..a264a6d0003 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/literals/literals.rb @@ -0,0 +1,179 @@ +# boolean values and nil +nil +NIL +false +FALSE +true +TRUE + +# decimal integers +1234 +5_678 +0 +0d900 + +# hexadecimal integers +0x1234 +0xdeadbeef +0xF00D_face + +# octal integers +0123 +0o234 +0O45_6 + +# binary integers +0b10010100 +0B011_01101 + +# floating-point numbers +12.34 +1234e-2 +1.234E1 + +# rational numbers +23r +9.85r + +# imaginary/complex numbers +2i +#3.14i # BAD: parse error + +# imaginary & rational +#1.2ri # BAD: parse error + +# strings +"" +'' +"hello" +'goodbye' +"string with escaped \" quote" +'string with " quote' +%(foo bar baz) +%q<foo bar baz> +%q(foo ' bar " baz') +%Q(FOO ' BAR " BAZ') +%q(foo\ bar) # "foo\\ bar" +%Q(foo\ bar) # "foo bar" +"2 + 2 = #{ 2 + 2 }" # interpolation +%Q(3 + 4 = #{ 3 + 4 }) # interpolation +'2 + 2 = #{ 2 + 2 }' # no interpolation +%q(3 + 4 = #{ 3 + 4 }) # no interpolation +"foo" 'bar' "baz" # concatenated +%q{foo} "bar" 'baz' # concatenated +"foo" "bar#{ 1 * 1 }" 'baz' # concatenated, interpolation +"foo #{ "bar #{ 2 + 3 } baz" } qux" # interpolation containing string containing interpolation +"foo #{ blah(); 1+9 }" # multiple statements in interpolation + +# characters +?x +?\n +?\s +?\\ +?\u{58} +?\C-a +?\M-a +?\M-\C-a +?\C-\M-a + +# symbols +:"" +:hello +:"foo bar" +:'bar baz' +{ foo: "bar" } +%s(wibble) +%s[wibble wobble] +:"foo_#{ 2 + 2}" # interpolation +:'foo_#{ 1 + 1 }' # no interpolation +%s(foo_#{ 3 - 2 }) # no interpolation + +# arrays +[] +[1, 2, 3] +[4, 5, 12 / 2] +[7, [8, 9]] + +# arrays of strings +%w() +%w(foo bar baz) +%w!foo bar baz! +%W[foo bar#{1+1} baz] # interpolation +%w[foo bar#{1+1} baz] # no interpolation + +# arrays of symbols +%i() +%i(foo bar baz) +%i@foo bar baz@ +%I(foo bar#{ 2 + 4 } baz) # interpolation +%i(foo bar#{ 2 + 4 } baz) # no interpolation + +# hashes +{} +{ foo: 1, :bar => 2, 'baz' => 3 } +{ foo: 7, **bar } # hash-splat argument + +# ranges +(1..10) +(1...10) +(1 .. 0) +(start..2+3) +(1..) # 1 to infinity +(..1) # -infinity to 1 +(0..-1) # BAD: parsed as binary with minus endless range on the LHS + +# subshell +`ls -l` +%x(ls -l) +`du -d #{ 1 + 1 }` # interpolation +%x@du -d #{ 5 - 4 }@ # interpolation + +# regular expressions +// +/foo/ +/foo/i +/foo+\sbar\S/ +/foo#{ 1 + 1 }bar/ # interpolation +/foo/oxm +%r[] +%r(foo) +%r:foo:i +%r{foo+\sbar\S} +%r{foo#{ 1 + 1 }bar} # interpolation +%r:foo:mxo + +# long strings +'abcdefghijklmnopqrstuvwxyzabcdef' # 32 chars, should not be truncated +'foobarfoobarfoobarfoobarfoobarfoo' # 33 chars, should be truncated +"foobar\\foobar\\foobar\\foobar\\foobar" # several short components, but long enough overall to be truncated + +# here documents +run_sql(<<SQL, <<SQL) +select * from table +SQL +where name = #{ name } +SQL + +def m + query = <<-BLA +some text\nand some more + BLA +end + +query = <<~SQUIGGLY + indented stuff +SQUIGGLY + +query = <<"DOC" + text with #{ interpolation } ! +DOC + +# TODO: the parser currently does not handle single quoted heredocs correctly +query = <<'DOC' + text without #{ interpolation } ! +DOC + +output = <<`SCRIPT` + cat file.txt +SCRIPT + diff --git a/ruby/ql/test/library-tests/ast/misc/misc.erb b/ruby/ql/test/library-tests/ast/misc/misc.erb new file mode 100644 index 00000000000..3d15e9b59f9 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/misc/misc.erb @@ -0,0 +1,3 @@ +<% +require_asset("main_include_admin.js") +%> diff --git a/ruby/ql/test/library-tests/ast/misc/misc.expected b/ruby/ql/test/library-tests/ast/misc/misc.expected new file mode 100644 index 00000000000..45235cd5787 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/misc/misc.expected @@ -0,0 +1,21 @@ +undef +| misc.rb:3:1:3:30 | undef ... | 0 | misc.rb:3:7:3:9 | foo | foo | MethodName | +| misc.rb:3:1:3:30 | undef ... | 1 | misc.rb:3:12:3:15 | :foo | foo | MethodName | +| misc.rb:3:1:3:30 | undef ... | 2 | misc.rb:3:18:3:21 | foo= | foo= | MethodName | +| misc.rb:3:1:3:30 | undef ... | 3 | misc.rb:3:24:3:25 | [] | [] | MethodName | +| misc.rb:3:1:3:30 | undef ... | 4 | misc.rb:3:28:3:30 | []= | []= | MethodName | +| misc.rb:4:1:4:19 | undef ... | 0 | misc.rb:4:7:4:19 | :"foo_#{...}" | (none) | MethodName | +| misc.rb:5:1:5:35 | undef ... | 0 | misc.rb:5:7:5:9 | nil | nil | MethodName | +| misc.rb:5:1:5:35 | undef ... | 1 | misc.rb:5:12:5:15 | true | true | MethodName | +| misc.rb:5:1:5:35 | undef ... | 2 | misc.rb:5:18:5:22 | false | false | MethodName | +| misc.rb:5:1:5:35 | undef ... | 3 | misc.rb:5:25:5:29 | super | super | MethodName | +| misc.rb:5:1:5:35 | undef ... | 4 | misc.rb:5:32:5:35 | self | self | MethodName | +alias +| misc.rb:7:1:7:14 | alias ... | new | misc.rb:7:7:7:9 | new | new | MethodName | +| misc.rb:7:1:7:14 | alias ... | old | misc.rb:7:11:7:14 | :old | old | MethodName | +| misc.rb:8:1:8:14 | alias ... | new | misc.rb:8:7:8:10 | foo= | foo= | MethodName | +| misc.rb:8:1:8:14 | alias ... | old | misc.rb:8:12:8:14 | []= | []= | MethodName | +| misc.rb:9:1:9:16 | alias ... | new | misc.rb:9:7:9:11 | super | super | MethodName | +| misc.rb:9:1:9:16 | alias ... | old | misc.rb:9:13:9:16 | self | self | MethodName | +| misc.rb:10:1:10:24 | alias ... | new | misc.rb:10:7:10:17 | :"\\n#{...}" | (none) | MethodName | +| misc.rb:10:1:10:24 | alias ... | old | misc.rb:10:19:10:24 | :"foo" | foo | MethodName | diff --git a/ruby/ql/test/library-tests/ast/misc/misc.ql b/ruby/ql/test/library-tests/ast/misc/misc.ql new file mode 100644 index 00000000000..788a8593cdd --- /dev/null +++ b/ruby/ql/test/library-tests/ast/misc/misc.ql @@ -0,0 +1,23 @@ +import ruby + +private string getValueText(MethodName m) { + result = m.getValueText() + or + not exists(m.getValueText()) and result = "(none)" +} + +query predicate undef(UndefStmt u, int i, MethodName m, string name, string pClass) { + pClass = m.getAPrimaryQlClass() and + u.getMethodName(i) = m and + name = getValueText(m) +} + +query predicate alias(AliasStmt a, string prop, MethodName m, string name, string pClass) { + pClass = m.getAPrimaryQlClass() and + name = getValueText(m) and + ( + a.getOldName() = m and prop = "old" + or + a.getNewName() = m and prop = "new" + ) +} diff --git a/ruby/ql/test/library-tests/ast/misc/misc.rb b/ruby/ql/test/library-tests/ast/misc/misc.rb new file mode 100644 index 00000000000..6f7fabec245 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/misc/misc.rb @@ -0,0 +1,10 @@ +bar = "bar" + +undef foo, :foo, foo=, [], []= +undef :"foo_#{bar}" +undef nil, true, false, super, self + +alias new :old +alias foo= []= +alias super self +alias :"\n#{bar}" :"foo" diff --git a/ruby/ql/test/library-tests/ast/modules/classes.expected b/ruby/ql/test/library-tests/ast/modules/classes.expected new file mode 100644 index 00000000000..e81722ecfa2 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/modules/classes.expected @@ -0,0 +1,42 @@ +classes +| classes.rb:3:1:4:3 | Foo | ClassDeclaration | Foo | +| classes.rb:7:1:8:3 | Bar | ClassDeclaration | Bar | +| classes.rb:11:1:12:3 | Baz | ClassDeclaration | Baz | +| classes.rb:16:1:17:3 | MyClass | ClassDeclaration | MyClass | +| classes.rb:20:1:37:3 | Wibble | ClassDeclaration | Wibble | +| classes.rb:32:3:33:5 | ClassInWibble | ClassDeclaration | ClassInWibble | +| classes.rb:55:1:56:3 | MyClassInGlobalScope | ClassDeclaration | MyClassInGlobalScope | +| modules.rb:6:5:7:7 | ClassInFooBar | ClassDeclaration | ClassInFooBar | +| modules.rb:19:3:20:5 | ClassInFoo | ClassDeclaration | ClassInFoo | +| modules.rb:30:3:31:5 | ClassInAnotherDefinitionOfFoo | ClassDeclaration | ClassInAnotherDefinitionOfFoo | +| modules.rb:49:3:50:5 | ClassInAnotherDefinitionOfFooBar | ClassDeclaration | ClassInAnotherDefinitionOfFooBar | +| modules.rb:66:5:67:7 | Bar | ClassDeclaration | Bar | +| modules.rb:72:5:73:7 | Bar | ClassDeclaration | Bar | +| modules.rb:78:5:79:7 | Bar | ClassDeclaration | Bar | +| modules.rb:112:1:113:3 | YY | ClassDeclaration | YY | +| modules.rb:116:7:117:9 | YY | ClassDeclaration | YY | +classesWithNameScopeExprs +| classes.rb:16:1:17:3 | MyClass | classes.rb:16:7:16:14 | MyModule | +| modules.rb:66:5:67:7 | Bar | modules.rb:66:11:66:14 | Foo1 | +| modules.rb:72:5:73:7 | Bar | modules.rb:72:11:72:14 | Foo2 | +| modules.rb:78:5:79:7 | Bar | modules.rb:78:11:78:14 | Foo3 | +classesWithGlobalNameScopeExprs +| classes.rb:55:1:56:3 | MyClassInGlobalScope | +exprsInClasses +| classes.rb:20:1:37:3 | Wibble | 0 | classes.rb:21:3:23:5 | method_a | Method | +| classes.rb:20:1:37:3 | Wibble | 1 | classes.rb:25:3:27:5 | method_b | Method | +| classes.rb:20:1:37:3 | Wibble | 2 | classes.rb:29:3:29:20 | call to some_method_call | MethodCall | +| classes.rb:20:1:37:3 | Wibble | 3 | classes.rb:30:3:30:19 | ... = ... | AssignExpr | +| classes.rb:20:1:37:3 | Wibble | 4 | classes.rb:32:3:33:5 | ClassInWibble | ClassDeclaration | +| classes.rb:20:1:37:3 | Wibble | 5 | classes.rb:35:3:36:5 | ModuleInWibble | ModuleDeclaration | +methodsInClasses +| classes.rb:20:1:37:3 | Wibble | classes.rb:21:3:23:5 | method_a | method_a | +| classes.rb:20:1:37:3 | Wibble | classes.rb:25:3:27:5 | method_b | method_b | +classesInClasses +| classes.rb:20:1:37:3 | Wibble | classes.rb:32:3:33:5 | ClassInWibble | ClassInWibble | +modulesInClasses +| classes.rb:20:1:37:3 | Wibble | classes.rb:35:3:36:5 | ModuleInWibble | ModuleInWibble | +classesWithASuperclass +| classes.rb:7:1:8:3 | Bar | classes.rb:7:13:7:21 | BaseClass | +| classes.rb:11:1:12:3 | Baz | classes.rb:11:13:11:32 | call to superclass_for | +| modules.rb:116:7:117:9 | YY | modules.rb:116:18:116:19 | YY | diff --git a/ruby/ql/test/library-tests/ast/modules/classes.ql b/ruby/ql/test/library-tests/ast/modules/classes.ql new file mode 100644 index 00000000000..a4b392a32cb --- /dev/null +++ b/ruby/ql/test/library-tests/ast/modules/classes.ql @@ -0,0 +1,29 @@ +import ruby + +query predicate classes(ClassDeclaration c, string pClass, string name) { + pClass = c.getAPrimaryQlClass() and name = c.getName() +} + +query predicate classesWithNameScopeExprs(ClassDeclaration c, Expr se) { se = c.getScopeExpr() } + +query predicate classesWithGlobalNameScopeExprs(ClassDeclaration c) { c.hasGlobalScope() } + +query predicate exprsInClasses(ClassDeclaration c, int i, Expr e, string eClass) { + e = c.getStmt(i) and eClass = e.getAPrimaryQlClass() +} + +query predicate methodsInClasses(ClassDeclaration c, Method m, string name) { + m = c.getMethod(name) +} + +query predicate classesInClasses(ClassDeclaration c, ClassDeclaration child, string name) { + child = c.getClass(name) +} + +query predicate modulesInClasses(ClassDeclaration c, ModuleDeclaration m, string name) { + m = c.getModule(name) +} + +query predicate classesWithASuperclass(ClassDeclaration c, Expr scExpr) { + scExpr = c.getSuperclassExpr() +} diff --git a/ruby/ql/test/library-tests/ast/modules/classes.rb b/ruby/ql/test/library-tests/ast/modules/classes.rb new file mode 100644 index 00000000000..ce05212ada1 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/modules/classes.rb @@ -0,0 +1,56 @@ + +# a class with no superclass specified +class Foo +end + +# a class where the superclass is a constant +class Bar < BaseClass +end + +# a class where the superclass is a call expression +class Baz < superclass_for(:baz) +end + +# a class where the name is a scope resolution +module MyModule; end +class MyModule::MyClass +end + +# a class with various expressions +class Wibble + def method_a + puts 'a' + end + + def method_b + puts 'b' + end + + some_method_call() + $global_var = 123 + + class ClassInWibble + end + + module ModuleInWibble + end +end + +# a singleton class with some methods and some other arbitrary expressions +x = 'hello' +class << x + def length + 100 * super + end + + def wibble + puts 'wibble' + end + + another_method_call + $global_var2 = 456 +end + +# a class where the name is a scope resolution using the global scope +class ::MyClassInGlobalScope +end \ No newline at end of file diff --git a/ruby/ql/test/library-tests/ast/modules/module_base.expected b/ruby/ql/test/library-tests/ast/modules/module_base.expected new file mode 100644 index 00000000000..9119ccbc1de --- /dev/null +++ b/ruby/ql/test/library-tests/ast/modules/module_base.expected @@ -0,0 +1,102 @@ +moduleBases +| classes.rb:2:1:56:3 | classes.rb | Toplevel | +| classes.rb:3:1:4:3 | Foo | ClassDeclaration | +| classes.rb:7:1:8:3 | Bar | ClassDeclaration | +| classes.rb:11:1:12:3 | Baz | ClassDeclaration | +| classes.rb:15:1:15:20 | MyModule | ModuleDeclaration | +| classes.rb:16:1:17:3 | MyClass | ClassDeclaration | +| classes.rb:20:1:37:3 | Wibble | ClassDeclaration | +| classes.rb:32:3:33:5 | ClassInWibble | ClassDeclaration | +| classes.rb:35:3:36:5 | ModuleInWibble | ModuleDeclaration | +| classes.rb:41:1:52:3 | class << ... | SingletonClass | +| classes.rb:55:1:56:3 | MyClassInGlobalScope | ClassDeclaration | +| modules.rb:1:1:2:3 | Empty | ModuleDeclaration | +| modules.rb:1:1:122:1 | modules.rb | Toplevel | +| modules.rb:4:1:24:3 | Foo | ModuleDeclaration | +| modules.rb:5:3:14:5 | Bar | ModuleDeclaration | +| modules.rb:6:5:7:7 | ClassInFooBar | ClassDeclaration | +| modules.rb:19:3:20:5 | ClassInFoo | ClassDeclaration | +| modules.rb:26:1:35:3 | Foo | ModuleDeclaration | +| modules.rb:30:3:31:5 | ClassInAnotherDefinitionOfFoo | ClassDeclaration | +| modules.rb:37:1:46:3 | Bar | ModuleDeclaration | +| modules.rb:48:1:57:3 | Bar | ModuleDeclaration | +| modules.rb:49:3:50:5 | ClassInAnotherDefinitionOfFooBar | ClassDeclaration | +| modules.rb:60:1:61:3 | MyModuleInGlobalScope | ModuleDeclaration | +| modules.rb:63:1:81:3 | Test | ModuleDeclaration | +| modules.rb:65:3:68:5 | Foo1 | ModuleDeclaration | +| modules.rb:66:5:67:7 | Bar | ClassDeclaration | +| modules.rb:70:3:74:5 | Foo2 | ModuleDeclaration | +| modules.rb:71:5:71:19 | Foo2 | ModuleDeclaration | +| modules.rb:72:5:73:7 | Bar | ClassDeclaration | +| modules.rb:76:3:80:5 | Foo3 | ModuleDeclaration | +| modules.rb:78:5:79:7 | Bar | ClassDeclaration | +| modules.rb:83:1:86:3 | Other | ModuleDeclaration | +| modules.rb:84:3:85:5 | Foo1 | ModuleDeclaration | +| modules.rb:88:1:93:3 | IncludeTest | ModuleDeclaration | +| modules.rb:91:3:92:5 | Y | ModuleDeclaration | +| modules.rb:95:1:99:3 | IncludeTest2 | ModuleDeclaration | +| modules.rb:97:3:98:5 | Z | ModuleDeclaration | +| modules.rb:101:1:105:3 | PrependTest | ModuleDeclaration | +| modules.rb:103:3:104:5 | Y | ModuleDeclaration | +| modules.rb:107:1:110:3 | MM | ModuleDeclaration | +| modules.rb:108:3:109:5 | MM | ModuleDeclaration | +| modules.rb:112:1:113:3 | YY | ClassDeclaration | +| modules.rb:115:1:118:3 | XX | ModuleDeclaration | +| modules.rb:116:7:117:9 | YY | ClassDeclaration | +| modules.rb:120:1:121:3 | Baz | ModuleDeclaration | +| toplevel.rb:1:1:5:23 | toplevel.rb | Toplevel | +moduleBaseClasses +| classes.rb:2:1:56:3 | classes.rb | classes.rb:3:1:4:3 | Foo | +| classes.rb:2:1:56:3 | classes.rb | classes.rb:7:1:8:3 | Bar | +| classes.rb:2:1:56:3 | classes.rb | classes.rb:11:1:12:3 | Baz | +| classes.rb:2:1:56:3 | classes.rb | classes.rb:16:1:17:3 | MyClass | +| classes.rb:2:1:56:3 | classes.rb | classes.rb:20:1:37:3 | Wibble | +| classes.rb:2:1:56:3 | classes.rb | classes.rb:55:1:56:3 | MyClassInGlobalScope | +| classes.rb:20:1:37:3 | Wibble | classes.rb:32:3:33:5 | ClassInWibble | +| modules.rb:1:1:122:1 | modules.rb | modules.rb:112:1:113:3 | YY | +| modules.rb:4:1:24:3 | Foo | modules.rb:19:3:20:5 | ClassInFoo | +| modules.rb:5:3:14:5 | Bar | modules.rb:6:5:7:7 | ClassInFooBar | +| modules.rb:26:1:35:3 | Foo | modules.rb:30:3:31:5 | ClassInAnotherDefinitionOfFoo | +| modules.rb:48:1:57:3 | Bar | modules.rb:49:3:50:5 | ClassInAnotherDefinitionOfFooBar | +| modules.rb:65:3:68:5 | Foo1 | modules.rb:66:5:67:7 | Bar | +| modules.rb:70:3:74:5 | Foo2 | modules.rb:72:5:73:7 | Bar | +| modules.rb:76:3:80:5 | Foo3 | modules.rb:78:5:79:7 | Bar | +| modules.rb:115:1:118:3 | XX | modules.rb:116:7:117:9 | YY | +moduleBaseMethods +| classes.rb:20:1:37:3 | Wibble | classes.rb:21:3:23:5 | method_a | +| classes.rb:20:1:37:3 | Wibble | classes.rb:25:3:27:5 | method_b | +| classes.rb:41:1:52:3 | class << ... | classes.rb:42:3:44:5 | length | +| classes.rb:41:1:52:3 | class << ... | classes.rb:46:3:48:5 | wibble | +| modules.rb:4:1:24:3 | Foo | modules.rb:16:3:17:5 | method_in_foo | +| modules.rb:5:3:14:5 | Bar | modules.rb:9:5:10:7 | method_in_foo_bar | +| modules.rb:26:1:35:3 | Foo | modules.rb:27:3:28:5 | method_in_another_definition_of_foo | +| modules.rb:37:1:46:3 | Bar | modules.rb:38:3:39:5 | method_a | +| modules.rb:37:1:46:3 | Bar | modules.rb:41:3:42:5 | method_b | +| modules.rb:48:1:57:3 | Bar | modules.rb:52:3:53:5 | method_in_another_definition_of_foo_bar | +moduleBaseModules +| classes.rb:2:1:56:3 | classes.rb | classes.rb:15:1:15:20 | MyModule | +| classes.rb:20:1:37:3 | Wibble | classes.rb:35:3:36:5 | ModuleInWibble | +| modules.rb:1:1:122:1 | modules.rb | modules.rb:1:1:2:3 | Empty | +| modules.rb:1:1:122:1 | modules.rb | modules.rb:4:1:24:3 | Foo | +| modules.rb:1:1:122:1 | modules.rb | modules.rb:26:1:35:3 | Foo | +| modules.rb:1:1:122:1 | modules.rb | modules.rb:37:1:46:3 | Bar | +| modules.rb:1:1:122:1 | modules.rb | modules.rb:48:1:57:3 | Bar | +| modules.rb:1:1:122:1 | modules.rb | modules.rb:60:1:61:3 | MyModuleInGlobalScope | +| modules.rb:1:1:122:1 | modules.rb | modules.rb:63:1:81:3 | Test | +| modules.rb:1:1:122:1 | modules.rb | modules.rb:83:1:86:3 | Other | +| modules.rb:1:1:122:1 | modules.rb | modules.rb:88:1:93:3 | IncludeTest | +| modules.rb:1:1:122:1 | modules.rb | modules.rb:95:1:99:3 | IncludeTest2 | +| modules.rb:1:1:122:1 | modules.rb | modules.rb:101:1:105:3 | PrependTest | +| modules.rb:1:1:122:1 | modules.rb | modules.rb:107:1:110:3 | MM | +| modules.rb:1:1:122:1 | modules.rb | modules.rb:115:1:118:3 | XX | +| modules.rb:1:1:122:1 | modules.rb | modules.rb:120:1:121:3 | Baz | +| modules.rb:4:1:24:3 | Foo | modules.rb:5:3:14:5 | Bar | +| modules.rb:63:1:81:3 | Test | modules.rb:65:3:68:5 | Foo1 | +| modules.rb:63:1:81:3 | Test | modules.rb:70:3:74:5 | Foo2 | +| modules.rb:63:1:81:3 | Test | modules.rb:76:3:80:5 | Foo3 | +| modules.rb:70:3:74:5 | Foo2 | modules.rb:71:5:71:19 | Foo2 | +| modules.rb:83:1:86:3 | Other | modules.rb:84:3:85:5 | Foo1 | +| modules.rb:88:1:93:3 | IncludeTest | modules.rb:91:3:92:5 | Y | +| modules.rb:95:1:99:3 | IncludeTest2 | modules.rb:97:3:98:5 | Z | +| modules.rb:101:1:105:3 | PrependTest | modules.rb:103:3:104:5 | Y | +| modules.rb:107:1:110:3 | MM | modules.rb:108:3:109:5 | MM | diff --git a/ruby/ql/test/library-tests/ast/modules/module_base.ql b/ruby/ql/test/library-tests/ast/modules/module_base.ql new file mode 100644 index 00000000000..e95faa62969 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/modules/module_base.ql @@ -0,0 +1,9 @@ +import ruby + +query predicate moduleBases(ModuleBase mb, string pClass) { pClass = mb.getAPrimaryQlClass() } + +query predicate moduleBaseClasses(ModuleBase mb, ClassDeclaration c) { c = mb.getAClass() } + +query predicate moduleBaseMethods(ModuleBase mb, Method m) { m = mb.getAMethod() } + +query predicate moduleBaseModules(ModuleBase mb, ModuleDeclaration m) { m = mb.getAModule() } diff --git a/ruby/ql/test/library-tests/ast/modules/modules.expected b/ruby/ql/test/library-tests/ast/modules/modules.expected new file mode 100644 index 00000000000..61352f7c78e --- /dev/null +++ b/ruby/ql/test/library-tests/ast/modules/modules.expected @@ -0,0 +1,103 @@ +modules +| classes.rb:15:1:15:20 | MyModule | ModuleDeclaration | MyModule | +| classes.rb:35:3:36:5 | ModuleInWibble | ModuleDeclaration | ModuleInWibble | +| modules.rb:1:1:2:3 | Empty | ModuleDeclaration | Empty | +| modules.rb:4:1:24:3 | Foo | ModuleDeclaration | Foo | +| modules.rb:5:3:14:5 | Bar | ModuleDeclaration | Bar | +| modules.rb:26:1:35:3 | Foo | ModuleDeclaration | Foo | +| modules.rb:37:1:46:3 | Bar | ModuleDeclaration | Bar | +| modules.rb:48:1:57:3 | Bar | ModuleDeclaration | Bar | +| modules.rb:60:1:61:3 | MyModuleInGlobalScope | ModuleDeclaration | MyModuleInGlobalScope | +| modules.rb:63:1:81:3 | Test | ModuleDeclaration | Test | +| modules.rb:65:3:68:5 | Foo1 | ModuleDeclaration | Foo1 | +| modules.rb:70:3:74:5 | Foo2 | ModuleDeclaration | Foo2 | +| modules.rb:71:5:71:19 | Foo2 | ModuleDeclaration | Foo2 | +| modules.rb:76:3:80:5 | Foo3 | ModuleDeclaration | Foo3 | +| modules.rb:83:1:86:3 | Other | ModuleDeclaration | Other | +| modules.rb:84:3:85:5 | Foo1 | ModuleDeclaration | Foo1 | +| modules.rb:88:1:93:3 | IncludeTest | ModuleDeclaration | IncludeTest | +| modules.rb:91:3:92:5 | Y | ModuleDeclaration | Y | +| modules.rb:95:1:99:3 | IncludeTest2 | ModuleDeclaration | IncludeTest2 | +| modules.rb:97:3:98:5 | Z | ModuleDeclaration | Z | +| modules.rb:101:1:105:3 | PrependTest | ModuleDeclaration | PrependTest | +| modules.rb:103:3:104:5 | Y | ModuleDeclaration | Y | +| modules.rb:107:1:110:3 | MM | ModuleDeclaration | MM | +| modules.rb:108:3:109:5 | MM | ModuleDeclaration | MM | +| modules.rb:115:1:118:3 | XX | ModuleDeclaration | XX | +| modules.rb:120:1:121:3 | Baz | ModuleDeclaration | Baz | +modulesWithScopeExprs +| modules.rb:48:1:57:3 | Bar | modules.rb:48:8:48:10 | Foo | +| modules.rb:91:3:92:5 | Y | modules.rb:91:10:91:13 | Foo1 | +| modules.rb:97:3:98:5 | Z | modules.rb:97:10:97:13 | Foo1 | +| modules.rb:103:3:104:5 | Y | modules.rb:103:10:103:13 | Foo2 | +| modules.rb:108:3:109:5 | MM | modules.rb:108:10:108:11 | MM | +| modules.rb:120:1:121:3 | Baz | modules.rb:120:8:120:22 | Bar | +modulesWithGlobalNameScopeExprs +| modules.rb:60:1:61:3 | MyModuleInGlobalScope | +exprsInModules +| modules.rb:4:1:24:3 | Foo | 0 | modules.rb:5:3:14:5 | Bar | ModuleDeclaration | +| modules.rb:4:1:24:3 | Foo | 1 | modules.rb:16:3:17:5 | method_in_foo | Method | +| modules.rb:4:1:24:3 | Foo | 2 | modules.rb:19:3:20:5 | ClassInFoo | ClassDeclaration | +| modules.rb:4:1:24:3 | Foo | 3 | modules.rb:22:3:22:19 | call to puts | MethodCall | +| modules.rb:4:1:24:3 | Foo | 4 | modules.rb:23:3:23:17 | ... = ... | AssignExpr | +| modules.rb:5:3:14:5 | Bar | 0 | modules.rb:6:5:7:7 | ClassInFooBar | ClassDeclaration | +| modules.rb:5:3:14:5 | Bar | 1 | modules.rb:9:5:10:7 | method_in_foo_bar | Method | +| modules.rb:5:3:14:5 | Bar | 2 | modules.rb:12:5:12:26 | call to puts | MethodCall | +| modules.rb:5:3:14:5 | Bar | 3 | modules.rb:13:5:13:19 | ... = ... | AssignExpr | +| modules.rb:26:1:35:3 | Foo | 0 | modules.rb:27:3:28:5 | method_in_another_definition_of_foo | Method | +| modules.rb:26:1:35:3 | Foo | 1 | modules.rb:30:3:31:5 | ClassInAnotherDefinitionOfFoo | ClassDeclaration | +| modules.rb:26:1:35:3 | Foo | 2 | modules.rb:33:3:33:25 | call to puts | MethodCall | +| modules.rb:26:1:35:3 | Foo | 3 | modules.rb:34:3:34:17 | ... = ... | AssignExpr | +| modules.rb:37:1:46:3 | Bar | 0 | modules.rb:38:3:39:5 | method_a | Method | +| modules.rb:37:1:46:3 | Bar | 1 | modules.rb:41:3:42:5 | method_b | Method | +| modules.rb:37:1:46:3 | Bar | 2 | modules.rb:44:3:44:19 | call to puts | MethodCall | +| modules.rb:37:1:46:3 | Bar | 3 | modules.rb:45:3:45:17 | ... = ... | AssignExpr | +| modules.rb:48:1:57:3 | Bar | 0 | modules.rb:49:3:50:5 | ClassInAnotherDefinitionOfFooBar | ClassDeclaration | +| modules.rb:48:1:57:3 | Bar | 1 | modules.rb:52:3:53:5 | method_in_another_definition_of_foo_bar | Method | +| modules.rb:48:1:57:3 | Bar | 2 | modules.rb:55:3:55:30 | call to puts | MethodCall | +| modules.rb:48:1:57:3 | Bar | 3 | modules.rb:56:3:56:17 | ... = ... | AssignExpr | +| modules.rb:63:1:81:3 | Test | 0 | modules.rb:65:3:68:5 | Foo1 | ModuleDeclaration | +| modules.rb:63:1:81:3 | Test | 1 | modules.rb:70:3:74:5 | Foo2 | ModuleDeclaration | +| modules.rb:63:1:81:3 | Test | 2 | modules.rb:76:3:80:5 | Foo3 | ModuleDeclaration | +| modules.rb:65:3:68:5 | Foo1 | 0 | modules.rb:66:5:67:7 | Bar | ClassDeclaration | +| modules.rb:70:3:74:5 | Foo2 | 0 | modules.rb:71:5:71:19 | Foo2 | ModuleDeclaration | +| modules.rb:70:3:74:5 | Foo2 | 1 | modules.rb:72:5:73:7 | Bar | ClassDeclaration | +| modules.rb:76:3:80:5 | Foo3 | 0 | modules.rb:77:5:77:17 | ... = ... | AssignExpr | +| modules.rb:76:3:80:5 | Foo3 | 1 | modules.rb:78:5:79:7 | Bar | ClassDeclaration | +| modules.rb:83:1:86:3 | Other | 0 | modules.rb:84:3:85:5 | Foo1 | ModuleDeclaration | +| modules.rb:88:1:93:3 | IncludeTest | 0 | modules.rb:89:3:89:16 | call to include | MethodCall | +| modules.rb:88:1:93:3 | IncludeTest | 1 | modules.rb:90:3:90:38 | call to module_eval | MethodCall | +| modules.rb:88:1:93:3 | IncludeTest | 2 | modules.rb:91:3:92:5 | Y | ModuleDeclaration | +| modules.rb:95:1:99:3 | IncludeTest2 | 0 | modules.rb:96:3:96:14 | call to include | MethodCall | +| modules.rb:95:1:99:3 | IncludeTest2 | 1 | modules.rb:97:3:98:5 | Z | ModuleDeclaration | +| modules.rb:101:1:105:3 | PrependTest | 0 | modules.rb:102:3:102:16 | call to prepend | MethodCall | +| modules.rb:101:1:105:3 | PrependTest | 1 | modules.rb:103:3:104:5 | Y | ModuleDeclaration | +| modules.rb:107:1:110:3 | MM | 0 | modules.rb:108:3:109:5 | MM | ModuleDeclaration | +| modules.rb:115:1:118:3 | XX | 0 | modules.rb:116:7:117:9 | YY | ClassDeclaration | +methodsInModules +| modules.rb:4:1:24:3 | Foo | modules.rb:16:3:17:5 | method_in_foo | method_in_foo | +| modules.rb:5:3:14:5 | Bar | modules.rb:9:5:10:7 | method_in_foo_bar | method_in_foo_bar | +| modules.rb:26:1:35:3 | Foo | modules.rb:27:3:28:5 | method_in_another_definition_of_foo | method_in_another_definition_of_foo | +| modules.rb:37:1:46:3 | Bar | modules.rb:38:3:39:5 | method_a | method_a | +| modules.rb:37:1:46:3 | Bar | modules.rb:41:3:42:5 | method_b | method_b | +| modules.rb:48:1:57:3 | Bar | modules.rb:52:3:53:5 | method_in_another_definition_of_foo_bar | method_in_another_definition_of_foo_bar | +classesInModules +| modules.rb:4:1:24:3 | Foo | modules.rb:19:3:20:5 | ClassInFoo | ClassInFoo | +| modules.rb:5:3:14:5 | Bar | modules.rb:6:5:7:7 | ClassInFooBar | ClassInFooBar | +| modules.rb:26:1:35:3 | Foo | modules.rb:30:3:31:5 | ClassInAnotherDefinitionOfFoo | ClassInAnotherDefinitionOfFoo | +| modules.rb:48:1:57:3 | Bar | modules.rb:49:3:50:5 | ClassInAnotherDefinitionOfFooBar | ClassInAnotherDefinitionOfFooBar | +| modules.rb:65:3:68:5 | Foo1 | modules.rb:66:5:67:7 | Bar | Bar | +| modules.rb:70:3:74:5 | Foo2 | modules.rb:72:5:73:7 | Bar | Bar | +| modules.rb:76:3:80:5 | Foo3 | modules.rb:78:5:79:7 | Bar | Bar | +| modules.rb:115:1:118:3 | XX | modules.rb:116:7:117:9 | YY | YY | +modulesInModules +| modules.rb:4:1:24:3 | Foo | modules.rb:5:3:14:5 | Bar | Bar | +| modules.rb:63:1:81:3 | Test | modules.rb:65:3:68:5 | Foo1 | Foo1 | +| modules.rb:63:1:81:3 | Test | modules.rb:70:3:74:5 | Foo2 | Foo2 | +| modules.rb:63:1:81:3 | Test | modules.rb:76:3:80:5 | Foo3 | Foo3 | +| modules.rb:70:3:74:5 | Foo2 | modules.rb:71:5:71:19 | Foo2 | Foo2 | +| modules.rb:83:1:86:3 | Other | modules.rb:84:3:85:5 | Foo1 | Foo1 | +| modules.rb:88:1:93:3 | IncludeTest | modules.rb:91:3:92:5 | Y | Y | +| modules.rb:95:1:99:3 | IncludeTest2 | modules.rb:97:3:98:5 | Z | Z | +| modules.rb:101:1:105:3 | PrependTest | modules.rb:103:3:104:5 | Y | Y | +| modules.rb:107:1:110:3 | MM | modules.rb:108:3:109:5 | MM | MM | diff --git a/ruby/ql/test/library-tests/ast/modules/modules.ql b/ruby/ql/test/library-tests/ast/modules/modules.ql new file mode 100644 index 00000000000..9ed9c7b4fa2 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/modules/modules.ql @@ -0,0 +1,25 @@ +import ruby + +query predicate modules(ModuleDeclaration m, string pClass, string name) { + pClass = m.getAPrimaryQlClass() and name = m.getName() +} + +query predicate modulesWithScopeExprs(ModuleDeclaration m, Expr se) { se = m.getScopeExpr() } + +query predicate modulesWithGlobalNameScopeExprs(ModuleDeclaration m) { m.hasGlobalScope() } + +query predicate exprsInModules(ModuleDeclaration m, int i, Expr e, string eClass) { + e = m.getStmt(i) and eClass = e.getAPrimaryQlClass() +} + +query predicate methodsInModules(ModuleDeclaration mod, Method method, string name) { + method = mod.getMethod(name) +} + +query predicate classesInModules(ModuleDeclaration mod, ClassDeclaration klass, string name) { + klass = mod.getClass(name) +} + +query predicate modulesInModules(ModuleDeclaration mod, ModuleDeclaration child, string name) { + child = mod.getModule(name) +} diff --git a/ruby/ql/test/library-tests/ast/modules/modules.rb b/ruby/ql/test/library-tests/ast/modules/modules.rb new file mode 100644 index 00000000000..8287b0a1bc4 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/modules/modules.rb @@ -0,0 +1,122 @@ +module Empty +end + +module Foo + module Bar + class ClassInFooBar + end + + def method_in_foo_bar + end + + puts 'module Foo::Bar' + $global_var = 0 + end + + def method_in_foo + end + + class ClassInFoo + end + + puts 'module Foo' + $global_var = 1 +end + +module Foo + def method_in_another_definition_of_foo + end + + class ClassInAnotherDefinitionOfFoo + end + + puts 'module Foo again' + $global_var = 2 +end + +module Bar + def method_a + end + + def method_b + end + + puts 'module Bar' + $global_var = 3 +end + +module Foo::Bar + class ClassInAnotherDefinitionOfFooBar + end + + def method_in_another_definition_of_foo_bar + end + + puts 'module Foo::Bar again' + $global_var = 4 +end + +# a module where the name is a scope resolution using the global scope +module ::MyModuleInGlobalScope +end + +module Test + + module Foo1 + class Foo1::Bar + end + end + + module Foo2 + module Foo2 end + class Foo2::Bar + end + end + + module Foo3 + Foo3 = Object + class Foo3::Bar + end + end +end + +module Other + module Foo1 + end +end + +module IncludeTest + include ::Test + Object.module_eval { prepend Other } + module Foo1::Y + end +end + +module IncludeTest2 + include Test + module Foo1::Z + end +end + +module PrependTest + prepend ::Test + module Foo2::Y + end +end + +module MM + module MM::MM + end +end + +class YY +end + +module XX + class YY < YY + end +end + +module Test::Foo1::Bar::Baz +end + diff --git a/ruby/ql/test/library-tests/ast/modules/singleton_classes.expected b/ruby/ql/test/library-tests/ast/modules/singleton_classes.expected new file mode 100644 index 00000000000..75bb75b60b9 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/modules/singleton_classes.expected @@ -0,0 +1,10 @@ +singletonClasses +| classes.rb:41:1:52:3 | class << ... | SingletonClass | classes.rb:41:10:41:10 | x | +exprsInSingletonClasses +| classes.rb:41:1:52:3 | class << ... | 0 | classes.rb:42:3:44:5 | length | Method | +| classes.rb:41:1:52:3 | class << ... | 1 | classes.rb:46:3:48:5 | wibble | Method | +| classes.rb:41:1:52:3 | class << ... | 2 | classes.rb:50:3:50:21 | call to another_method_call | MethodCall | +| classes.rb:41:1:52:3 | class << ... | 3 | classes.rb:51:3:51:20 | ... = ... | AssignExpr | +methodsInSingletonClasses +| classes.rb:41:1:52:3 | class << ... | classes.rb:42:3:44:5 | length | +| classes.rb:41:1:52:3 | class << ... | classes.rb:46:3:48:5 | wibble | diff --git a/ruby/ql/test/library-tests/ast/modules/singleton_classes.ql b/ruby/ql/test/library-tests/ast/modules/singleton_classes.ql new file mode 100644 index 00000000000..d02dd8cb68c --- /dev/null +++ b/ruby/ql/test/library-tests/ast/modules/singleton_classes.ql @@ -0,0 +1,11 @@ +import ruby + +query predicate singletonClasses(SingletonClass sc, string pClass, Expr value) { + pClass = sc.getAPrimaryQlClass() and value = sc.getValue() +} + +query predicate exprsInSingletonClasses(SingletonClass sc, int i, Expr e, string eClass) { + e = sc.getStmt(i) and eClass = e.getAPrimaryQlClass() +} + +query predicate methodsInSingletonClasses(SingletonClass sc, Method m) { m = sc.getAMethod() } diff --git a/ruby/ql/test/library-tests/ast/modules/toplevel.expected b/ruby/ql/test/library-tests/ast/modules/toplevel.expected new file mode 100644 index 00000000000..c15df468299 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/modules/toplevel.expected @@ -0,0 +1,8 @@ +toplevel +| classes.rb:2:1:56:3 | classes.rb | Toplevel | +| modules.rb:1:1:122:1 | modules.rb | Toplevel | +| toplevel.rb:1:1:5:23 | toplevel.rb | Toplevel | +beginBlocks +| toplevel.rb:1:1:5:23 | toplevel.rb | 0 | toplevel.rb:5:1:5:22 | BEGIN { ... } | +endBlocks +| toplevel.rb:1:1:5:23 | toplevel.rb | toplevel.rb:3:1:3:18 | END { ... } | diff --git a/ruby/ql/test/library-tests/ast/modules/toplevel.ql b/ruby/ql/test/library-tests/ast/modules/toplevel.ql new file mode 100644 index 00000000000..90afb185ff6 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/modules/toplevel.ql @@ -0,0 +1,9 @@ +import ruby + +query predicate toplevel(Toplevel m, string pClass) { pClass = m.getAPrimaryQlClass() } + +query predicate beginBlocks(Toplevel m, int i, BeginBlock b) { b = m.getBeginBlock(i) } + +query predicate endBlocks(Toplevel m, EndBlock b) { + b.getLocation().getFile() = m.getLocation().getFile() +} diff --git a/ruby/ql/test/library-tests/ast/modules/toplevel.rb b/ruby/ql/test/library-tests/ast/modules/toplevel.rb new file mode 100644 index 00000000000..46105392531 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/modules/toplevel.rb @@ -0,0 +1,5 @@ +puts "world" + +END { puts "!!!" } + +BEGIN { puts "hello" } diff --git a/ruby/ql/test/library-tests/ast/operations/assignment.expected b/ruby/ql/test/library-tests/ast/operations/assignment.expected new file mode 100644 index 00000000000..7a536f183f7 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/operations/assignment.expected @@ -0,0 +1,90 @@ +assignments +| operations.rb:3:1:3:5 | ... = ... | = | operations.rb:3:1:3:1 | a | operations.rb:3:5:3:5 | 0 | AssignExpr | +| operations.rb:4:1:4:5 | ... = ... | = | operations.rb:4:1:4:1 | b | operations.rb:4:5:4:5 | 0 | AssignExpr | +| operations.rb:5:1:5:7 | ... = ... | = | operations.rb:5:1:5:3 | bar | operations.rb:5:7:5:7 | 0 | AssignExpr | +| operations.rb:6:1:6:8 | ... = ... | = | operations.rb:6:1:6:4 | base | operations.rb:6:8:6:8 | 0 | AssignExpr | +| operations.rb:7:1:7:7 | ... = ... | = | operations.rb:7:1:7:3 | baz | operations.rb:7:7:7:7 | 0 | AssignExpr | +| operations.rb:8:1:8:7 | ... = ... | = | operations.rb:8:1:8:3 | foo | operations.rb:8:7:8:7 | 0 | AssignExpr | +| operations.rb:9:1:9:10 | ... = ... | = | operations.rb:9:1:9:6 | handle | operations.rb:9:10:9:10 | 0 | AssignExpr | +| operations.rb:10:1:10:5 | ... = ... | = | operations.rb:10:1:10:1 | m | operations.rb:10:5:10:5 | 0 | AssignExpr | +| operations.rb:11:1:11:8 | ... = ... | = | operations.rb:11:1:11:4 | mask | operations.rb:11:8:11:8 | 0 | AssignExpr | +| operations.rb:12:1:12:5 | ... = ... | = | operations.rb:12:1:12:1 | n | operations.rb:12:5:12:5 | 0 | AssignExpr | +| operations.rb:13:1:13:8 | ... = ... | = | operations.rb:13:1:13:4 | name | operations.rb:13:8:13:8 | 0 | AssignExpr | +| operations.rb:14:1:14:7 | ... = ... | = | operations.rb:14:1:14:3 | num | operations.rb:14:7:14:7 | 0 | AssignExpr | +| operations.rb:15:1:15:9 | ... = ... | = | operations.rb:15:1:15:5 | power | operations.rb:15:9:15:9 | 0 | AssignExpr | +| operations.rb:16:1:16:7 | ... = ... | = | operations.rb:16:1:16:3 | qux | operations.rb:16:7:16:7 | 0 | AssignExpr | +| operations.rb:17:1:17:5 | ... = ... | = | operations.rb:17:1:17:1 | w | operations.rb:17:5:17:5 | 0 | AssignExpr | +| operations.rb:18:1:18:5 | ... = ... | = | operations.rb:18:1:18:1 | x | operations.rb:18:5:18:5 | 0 | AssignExpr | +| operations.rb:19:1:19:5 | ... = ... | = | operations.rb:19:1:19:1 | y | operations.rb:19:5:19:5 | 0 | AssignExpr | +| operations.rb:20:1:20:5 | ... = ... | = | operations.rb:20:1:20:1 | z | operations.rb:20:5:20:5 | 0 | AssignExpr | +| operations.rb:69:1:69:8 | ... += ... | += | operations.rb:69:1:69:1 | x | operations.rb:69:6:69:8 | 128 | AssignAddExpr | +| operations.rb:69:1:69:8 | ... = ... | = | operations.rb:69:1:69:1 | x | operations.rb:69:3:69:4 | ... + ... | AssignExpr | +| operations.rb:70:1:70:7 | ... -= ... | -= | operations.rb:70:1:70:1 | y | operations.rb:70:6:70:7 | 32 | AssignSubExpr | +| operations.rb:70:1:70:7 | ... = ... | = | operations.rb:70:1:70:1 | y | operations.rb:70:3:70:4 | ... - ... | AssignExpr | +| operations.rb:71:1:71:7 | ... *= ... | *= | operations.rb:71:1:71:1 | a | operations.rb:71:6:71:7 | 12 | AssignMulExpr | +| operations.rb:71:1:71:7 | ... = ... | = | operations.rb:71:1:71:1 | a | operations.rb:71:3:71:4 | ... * ... | AssignExpr | +| operations.rb:72:1:72:6 | ... /= ... | /= | operations.rb:72:1:72:1 | b | operations.rb:72:6:72:6 | 4 | AssignDivExpr | +| operations.rb:72:1:72:6 | ... = ... | = | operations.rb:72:1:72:1 | b | operations.rb:72:3:72:4 | ... / ... | AssignExpr | +| operations.rb:73:1:73:6 | ... %= ... | %= | operations.rb:73:1:73:1 | z | operations.rb:73:6:73:6 | 2 | AssignModuloExpr | +| operations.rb:73:1:73:6 | ... = ... | = | operations.rb:73:1:73:1 | z | operations.rb:73:3:73:4 | ... % ... | AssignExpr | +| operations.rb:74:1:74:11 | ... **= ... | **= | operations.rb:74:1:74:3 | foo | operations.rb:74:9:74:11 | bar | AssignExponentExpr | +| operations.rb:74:1:74:11 | ... = ... | = | operations.rb:74:1:74:3 | foo | operations.rb:74:5:74:7 | ... ** ... | AssignExpr | +| operations.rb:77:2:77:8 | ... &&= ... | &&= | operations.rb:77:2:77:2 | x | operations.rb:77:8:77:8 | y | AssignLogicalAndExpr | +| operations.rb:77:2:77:8 | ... = ... | = | operations.rb:77:2:77:2 | x | operations.rb:77:4:77:6 | ... && ... | AssignExpr | +| operations.rb:78:2:78:8 | ... = ... | = | operations.rb:78:2:78:2 | a | operations.rb:78:4:78:6 | ... \|\| ... | AssignExpr | +| operations.rb:78:2:78:8 | ... \|\|= ... | \|\|= | operations.rb:78:2:78:2 | a | operations.rb:78:8:78:8 | b | AssignLogicalOrExpr | +| operations.rb:81:2:81:8 | ... <<= ... | <<= | operations.rb:81:2:81:2 | x | operations.rb:81:8:81:8 | 2 | AssignLShiftExpr | +| operations.rb:81:2:81:8 | ... = ... | = | operations.rb:81:2:81:2 | x | operations.rb:81:4:81:6 | ... << ... | AssignExpr | +| operations.rb:82:2:82:8 | ... = ... | = | operations.rb:82:2:82:2 | y | operations.rb:82:4:82:6 | ... >> ... | AssignExpr | +| operations.rb:82:2:82:8 | ... >>= ... | >>= | operations.rb:82:2:82:2 | y | operations.rb:82:8:82:8 | 3 | AssignRShiftExpr | +| operations.rb:83:2:83:12 | ... &= ... | &= | operations.rb:83:2:83:4 | foo | operations.rb:83:9:83:12 | mask | AssignBitwiseAndExpr | +| operations.rb:83:2:83:12 | ... = ... | = | operations.rb:83:2:83:4 | foo | operations.rb:83:6:83:7 | ... & ... | AssignExpr | +| operations.rb:84:2:84:12 | ... = ... | = | operations.rb:84:2:84:4 | bar | operations.rb:84:6:84:7 | ... \| ... | AssignExpr | +| operations.rb:84:2:84:12 | ... \|= ... | \|= | operations.rb:84:2:84:4 | bar | operations.rb:84:9:84:12 | 0x01 | AssignBitwiseOrExpr | +| operations.rb:85:2:85:11 | ... = ... | = | operations.rb:85:2:85:4 | baz | operations.rb:85:6:85:7 | ... ^ ... | AssignExpr | +| operations.rb:85:2:85:11 | ... ^= ... | ^= | operations.rb:85:2:85:4 | baz | operations.rb:85:9:85:11 | qux | AssignBitwiseXorExpr | +| operations.rb:88:3:88:8 | ... = ... | = | operations.rb:88:3:88:4 | @x | operations.rb:88:8:88:8 | 1 | AssignExpr | +| operations.rb:89:3:89:9 | ... += ... | += | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AssignAddExpr | +| operations.rb:89:3:89:9 | ... = ... | = | operations.rb:89:3:89:4 | @x | operations.rb:89:6:89:7 | ... + ... | AssignExpr | +| operations.rb:91:3:91:9 | ... = ... | = | operations.rb:91:3:91:5 | @@y | operations.rb:91:9:91:9 | 3 | AssignExpr | +| operations.rb:92:3:92:10 | ... /= ... | /= | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | AssignDivExpr | +| operations.rb:92:3:92:10 | ... = ... | = | operations.rb:92:3:92:5 | @@y | operations.rb:92:7:92:8 | ... / ... | AssignExpr | +| operations.rb:95:1:95:15 | ... = ... | = | operations.rb:95:1:95:11 | $global_var | operations.rb:95:15:95:15 | 5 | AssignExpr | +| operations.rb:96:1:96:16 | ... *= ... | *= | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | AssignMulExpr | +| operations.rb:96:1:96:16 | ... = ... | = | operations.rb:96:1:96:11 | $global_var | operations.rb:96:13:96:14 | ... * ... | AssignExpr | +assignOperations +| operations.rb:69:1:69:8 | ... += ... | += | operations.rb:69:1:69:1 | x | operations.rb:69:6:69:8 | 128 | AssignAddExpr | +| operations.rb:70:1:70:7 | ... -= ... | -= | operations.rb:70:1:70:1 | y | operations.rb:70:6:70:7 | 32 | AssignSubExpr | +| operations.rb:71:1:71:7 | ... *= ... | *= | operations.rb:71:1:71:1 | a | operations.rb:71:6:71:7 | 12 | AssignMulExpr | +| operations.rb:72:1:72:6 | ... /= ... | /= | operations.rb:72:1:72:1 | b | operations.rb:72:6:72:6 | 4 | AssignDivExpr | +| operations.rb:73:1:73:6 | ... %= ... | %= | operations.rb:73:1:73:1 | z | operations.rb:73:6:73:6 | 2 | AssignModuloExpr | +| operations.rb:74:1:74:11 | ... **= ... | **= | operations.rb:74:1:74:3 | foo | operations.rb:74:9:74:11 | bar | AssignExponentExpr | +| operations.rb:77:2:77:8 | ... &&= ... | &&= | operations.rb:77:2:77:2 | x | operations.rb:77:8:77:8 | y | AssignLogicalAndExpr | +| operations.rb:78:2:78:8 | ... \|\|= ... | \|\|= | operations.rb:78:2:78:2 | a | operations.rb:78:8:78:8 | b | AssignLogicalOrExpr | +| operations.rb:81:2:81:8 | ... <<= ... | <<= | operations.rb:81:2:81:2 | x | operations.rb:81:8:81:8 | 2 | AssignLShiftExpr | +| operations.rb:82:2:82:8 | ... >>= ... | >>= | operations.rb:82:2:82:2 | y | operations.rb:82:8:82:8 | 3 | AssignRShiftExpr | +| operations.rb:83:2:83:12 | ... &= ... | &= | operations.rb:83:2:83:4 | foo | operations.rb:83:9:83:12 | mask | AssignBitwiseAndExpr | +| operations.rb:84:2:84:12 | ... \|= ... | \|= | operations.rb:84:2:84:4 | bar | operations.rb:84:9:84:12 | 0x01 | AssignBitwiseOrExpr | +| operations.rb:85:2:85:11 | ... ^= ... | ^= | operations.rb:85:2:85:4 | baz | operations.rb:85:9:85:11 | qux | AssignBitwiseXorExpr | +| operations.rb:89:3:89:9 | ... += ... | += | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AssignAddExpr | +| operations.rb:92:3:92:10 | ... /= ... | /= | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | AssignDivExpr | +| operations.rb:96:1:96:16 | ... *= ... | *= | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | AssignMulExpr | +assignArithmeticOperations +| operations.rb:69:1:69:8 | ... += ... | += | operations.rb:69:1:69:1 | x | operations.rb:69:6:69:8 | 128 | AssignAddExpr | +| operations.rb:70:1:70:7 | ... -= ... | -= | operations.rb:70:1:70:1 | y | operations.rb:70:6:70:7 | 32 | AssignSubExpr | +| operations.rb:71:1:71:7 | ... *= ... | *= | operations.rb:71:1:71:1 | a | operations.rb:71:6:71:7 | 12 | AssignMulExpr | +| operations.rb:72:1:72:6 | ... /= ... | /= | operations.rb:72:1:72:1 | b | operations.rb:72:6:72:6 | 4 | AssignDivExpr | +| operations.rb:73:1:73:6 | ... %= ... | %= | operations.rb:73:1:73:1 | z | operations.rb:73:6:73:6 | 2 | AssignModuloExpr | +| operations.rb:74:1:74:11 | ... **= ... | **= | operations.rb:74:1:74:3 | foo | operations.rb:74:9:74:11 | bar | AssignExponentExpr | +| operations.rb:89:3:89:9 | ... += ... | += | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AssignAddExpr | +| operations.rb:92:3:92:10 | ... /= ... | /= | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | AssignDivExpr | +| operations.rb:96:1:96:16 | ... *= ... | *= | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | AssignMulExpr | +assignLogicalOperations +| operations.rb:77:2:77:8 | ... &&= ... | &&= | operations.rb:77:2:77:2 | x | operations.rb:77:8:77:8 | y | AssignLogicalAndExpr | +| operations.rb:78:2:78:8 | ... \|\|= ... | \|\|= | operations.rb:78:2:78:2 | a | operations.rb:78:8:78:8 | b | AssignLogicalOrExpr | +assignBitwiseOperations +| operations.rb:81:2:81:8 | ... <<= ... | <<= | operations.rb:81:2:81:2 | x | operations.rb:81:8:81:8 | 2 | AssignLShiftExpr | +| operations.rb:82:2:82:8 | ... >>= ... | >>= | operations.rb:82:2:82:2 | y | operations.rb:82:8:82:8 | 3 | AssignRShiftExpr | +| operations.rb:83:2:83:12 | ... &= ... | &= | operations.rb:83:2:83:4 | foo | operations.rb:83:9:83:12 | mask | AssignBitwiseAndExpr | +| operations.rb:84:2:84:12 | ... \|= ... | \|= | operations.rb:84:2:84:4 | bar | operations.rb:84:9:84:12 | 0x01 | AssignBitwiseOrExpr | +| operations.rb:85:2:85:11 | ... ^= ... | ^= | operations.rb:85:2:85:4 | baz | operations.rb:85:9:85:11 | qux | AssignBitwiseXorExpr | diff --git a/ruby/ql/test/library-tests/ast/operations/assignment.ql b/ruby/ql/test/library-tests/ast/operations/assignment.ql new file mode 100644 index 00000000000..3ff3b3f7684 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/operations/assignment.ql @@ -0,0 +1,35 @@ +import ruby + +query predicate assignments(Assignment a, string operator, Expr left, Expr right, string pClass) { + operator = a.getOperator() and + left = a.getLeftOperand() and + right = a.getRightOperand() and + pClass = a.getAPrimaryQlClass() +} + +query predicate assignOperations( + AssignOperation o, string operator, Expr left, Expr right, string pClass +) { + operator = o.getOperator() and + left = o.getLeftOperand() and + right = o.getRightOperand() and + pClass = o.getAPrimaryQlClass() +} + +query predicate assignArithmeticOperations( + AssignArithmeticOperation o, string operator, Expr left, Expr right, string pClass +) { + assignOperations(o, operator, left, right, pClass) +} + +query predicate assignLogicalOperations( + AssignLogicalOperation o, string operator, Expr left, Expr right, string pClass +) { + assignOperations(o, operator, left, right, pClass) +} + +query predicate assignBitwiseOperations( + AssignBitwiseOperation o, string operator, Expr left, Expr right, string pClass +) { + assignOperations(o, operator, left, right, pClass) +} diff --git a/ruby/ql/test/library-tests/ast/operations/binary.expected b/ruby/ql/test/library-tests/ast/operations/binary.expected new file mode 100644 index 00000000000..236b0c3b574 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/operations/binary.expected @@ -0,0 +1,99 @@ +binaryOperations +| operations.rb:32:1:32:7 | ... + ... | + | operations.rb:32:1:32:1 | w | operations.rb:32:5:32:7 | 234 | AddExpr | +| operations.rb:33:1:33:6 | ... - ... | - | operations.rb:33:1:33:1 | x | operations.rb:33:5:33:6 | 17 | SubExpr | +| operations.rb:34:1:34:6 | ... * ... | * | operations.rb:34:1:34:1 | y | operations.rb:34:5:34:6 | 10 | MulExpr | +| operations.rb:35:1:35:5 | ... / ... | / | operations.rb:35:1:35:1 | z | operations.rb:35:5:35:5 | 2 | DivExpr | +| operations.rb:36:1:36:7 | ... % ... | % | operations.rb:36:1:36:3 | num | operations.rb:36:7:36:7 | 2 | ModuloExpr | +| operations.rb:37:1:37:13 | ... ** ... | ** | operations.rb:37:1:37:4 | base | operations.rb:37:9:37:13 | power | ExponentExpr | +| operations.rb:40:1:40:10 | ... && ... | && | operations.rb:40:1:40:3 | foo | operations.rb:40:8:40:10 | bar | LogicalAndExpr | +| operations.rb:41:1:41:11 | ... and ... | and | operations.rb:41:1:41:3 | baz | operations.rb:41:9:41:11 | qux | LogicalAndExpr | +| operations.rb:42:1:42:6 | ... or ... | or | operations.rb:42:1:42:1 | a | operations.rb:42:6:42:6 | b | LogicalOrExpr | +| operations.rb:43:1:43:6 | ... \|\| ... | \|\| | operations.rb:43:1:43:1 | x | operations.rb:43:6:43:6 | y | LogicalOrExpr | +| operations.rb:46:1:46:6 | ... << ... | << | operations.rb:46:1:46:1 | x | operations.rb:46:6:46:6 | 3 | LShiftExpr | +| operations.rb:47:1:47:7 | ... >> ... | >> | operations.rb:47:1:47:1 | y | operations.rb:47:6:47:7 | 16 | RShiftExpr | +| operations.rb:48:1:48:10 | ... & ... | & | operations.rb:48:1:48:3 | foo | operations.rb:48:7:48:10 | 0xff | BitwiseAndExpr | +| operations.rb:49:1:49:10 | ... \| ... | \| | operations.rb:49:1:49:3 | bar | operations.rb:49:7:49:10 | 0x02 | BitwiseOrExpr | +| operations.rb:50:1:50:9 | ... ^ ... | ^ | operations.rb:50:1:50:3 | baz | operations.rb:50:7:50:9 | qux | BitwiseXorExpr | +| operations.rb:53:1:53:6 | ... == ... | == | operations.rb:53:1:53:1 | x | operations.rb:53:6:53:6 | y | EqExpr | +| operations.rb:54:1:54:8 | ... != ... | != | operations.rb:54:1:54:1 | a | operations.rb:54:6:54:8 | 123 | NEExpr | +| operations.rb:55:1:55:7 | ... === ... | === | operations.rb:55:1:55:1 | m | operations.rb:55:7:55:7 | n | CaseEqExpr | +| operations.rb:58:1:58:5 | ... > ... | > | operations.rb:58:1:58:1 | x | operations.rb:58:5:58:5 | 0 | GTExpr | +| operations.rb:59:1:59:8 | ... >= ... | >= | operations.rb:59:1:59:1 | y | operations.rb:59:6:59:8 | 100 | GEExpr | +| operations.rb:60:1:60:5 | ... < ... | < | operations.rb:60:1:60:1 | a | operations.rb:60:5:60:5 | b | LTExpr | +| operations.rb:61:1:61:8 | ... <= ... | <= | operations.rb:61:1:61:1 | 7 | operations.rb:61:6:61:8 | foo | LEExpr | +| operations.rb:64:1:64:7 | ... <=> ... | <=> | operations.rb:64:1:64:1 | a | operations.rb:64:7:64:7 | b | SpaceshipExpr | +| operations.rb:65:1:65:15 | ... =~ ... | =~ | operations.rb:65:1:65:4 | name | operations.rb:65:9:65:15 | /foo.*/ | RegExpMatchExpr | +| operations.rb:66:1:66:17 | ... !~ ... | !~ | operations.rb:66:1:66:6 | handle | operations.rb:66:11:66:17 | /.*bar/ | NoRegExpMatchExpr | +| operations.rb:69:3:69:4 | ... + ... | + | operations.rb:69:1:69:1 | x | operations.rb:69:6:69:8 | 128 | AddExpr | +| operations.rb:70:3:70:4 | ... - ... | - | operations.rb:70:1:70:1 | y | operations.rb:70:6:70:7 | 32 | SubExpr | +| operations.rb:71:3:71:4 | ... * ... | * | operations.rb:71:1:71:1 | a | operations.rb:71:6:71:7 | 12 | MulExpr | +| operations.rb:72:3:72:4 | ... / ... | / | operations.rb:72:1:72:1 | b | operations.rb:72:6:72:6 | 4 | DivExpr | +| operations.rb:73:3:73:4 | ... % ... | % | operations.rb:73:1:73:1 | z | operations.rb:73:6:73:6 | 2 | ModuloExpr | +| operations.rb:74:5:74:7 | ... ** ... | ** | operations.rb:74:1:74:3 | foo | operations.rb:74:9:74:11 | bar | ExponentExpr | +| operations.rb:77:4:77:6 | ... && ... | && | operations.rb:77:2:77:2 | x | operations.rb:77:8:77:8 | y | LogicalAndExpr | +| operations.rb:78:4:78:6 | ... \|\| ... | \|\| | operations.rb:78:2:78:2 | a | operations.rb:78:8:78:8 | b | LogicalOrExpr | +| operations.rb:81:4:81:6 | ... << ... | << | operations.rb:81:2:81:2 | x | operations.rb:81:8:81:8 | 2 | LShiftExpr | +| operations.rb:82:4:82:6 | ... >> ... | >> | operations.rb:82:2:82:2 | y | operations.rb:82:8:82:8 | 3 | RShiftExpr | +| operations.rb:83:6:83:7 | ... & ... | & | operations.rb:83:2:83:4 | foo | operations.rb:83:9:83:12 | mask | BitwiseAndExpr | +| operations.rb:84:6:84:7 | ... \| ... | \| | operations.rb:84:2:84:4 | bar | operations.rb:84:9:84:12 | 0x01 | BitwiseOrExpr | +| operations.rb:85:6:85:7 | ... ^ ... | ^ | operations.rb:85:2:85:4 | baz | operations.rb:85:9:85:11 | qux | BitwiseXorExpr | +| operations.rb:89:6:89:7 | ... + ... | + | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AddExpr | +| operations.rb:92:7:92:8 | ... / ... | / | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | DivExpr | +| operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | MulExpr | +binaryArithmeticOperations +| operations.rb:32:1:32:7 | ... + ... | + | operations.rb:32:1:32:1 | w | operations.rb:32:5:32:7 | 234 | AddExpr | +| operations.rb:33:1:33:6 | ... - ... | - | operations.rb:33:1:33:1 | x | operations.rb:33:5:33:6 | 17 | SubExpr | +| operations.rb:34:1:34:6 | ... * ... | * | operations.rb:34:1:34:1 | y | operations.rb:34:5:34:6 | 10 | MulExpr | +| operations.rb:35:1:35:5 | ... / ... | / | operations.rb:35:1:35:1 | z | operations.rb:35:5:35:5 | 2 | DivExpr | +| operations.rb:36:1:36:7 | ... % ... | % | operations.rb:36:1:36:3 | num | operations.rb:36:7:36:7 | 2 | ModuloExpr | +| operations.rb:37:1:37:13 | ... ** ... | ** | operations.rb:37:1:37:4 | base | operations.rb:37:9:37:13 | power | ExponentExpr | +| operations.rb:69:3:69:4 | ... + ... | + | operations.rb:69:1:69:1 | x | operations.rb:69:6:69:8 | 128 | AddExpr | +| operations.rb:70:3:70:4 | ... - ... | - | operations.rb:70:1:70:1 | y | operations.rb:70:6:70:7 | 32 | SubExpr | +| operations.rb:71:3:71:4 | ... * ... | * | operations.rb:71:1:71:1 | a | operations.rb:71:6:71:7 | 12 | MulExpr | +| operations.rb:72:3:72:4 | ... / ... | / | operations.rb:72:1:72:1 | b | operations.rb:72:6:72:6 | 4 | DivExpr | +| operations.rb:73:3:73:4 | ... % ... | % | operations.rb:73:1:73:1 | z | operations.rb:73:6:73:6 | 2 | ModuloExpr | +| operations.rb:74:5:74:7 | ... ** ... | ** | operations.rb:74:1:74:3 | foo | operations.rb:74:9:74:11 | bar | ExponentExpr | +| operations.rb:89:6:89:7 | ... + ... | + | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AddExpr | +| operations.rb:92:7:92:8 | ... / ... | / | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | DivExpr | +| operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | MulExpr | +binaryLogicalOperations +| operations.rb:40:1:40:10 | ... && ... | && | operations.rb:40:1:40:3 | foo | operations.rb:40:8:40:10 | bar | LogicalAndExpr | +| operations.rb:41:1:41:11 | ... and ... | and | operations.rb:41:1:41:3 | baz | operations.rb:41:9:41:11 | qux | LogicalAndExpr | +| operations.rb:42:1:42:6 | ... or ... | or | operations.rb:42:1:42:1 | a | operations.rb:42:6:42:6 | b | LogicalOrExpr | +| operations.rb:43:1:43:6 | ... \|\| ... | \|\| | operations.rb:43:1:43:1 | x | operations.rb:43:6:43:6 | y | LogicalOrExpr | +| operations.rb:77:4:77:6 | ... && ... | && | operations.rb:77:2:77:2 | x | operations.rb:77:8:77:8 | y | LogicalAndExpr | +| operations.rb:78:4:78:6 | ... \|\| ... | \|\| | operations.rb:78:2:78:2 | a | operations.rb:78:8:78:8 | b | LogicalOrExpr | +binaryBitwiseOperations +| operations.rb:46:1:46:6 | ... << ... | << | operations.rb:46:1:46:1 | x | operations.rb:46:6:46:6 | 3 | LShiftExpr | +| operations.rb:47:1:47:7 | ... >> ... | >> | operations.rb:47:1:47:1 | y | operations.rb:47:6:47:7 | 16 | RShiftExpr | +| operations.rb:48:1:48:10 | ... & ... | & | operations.rb:48:1:48:3 | foo | operations.rb:48:7:48:10 | 0xff | BitwiseAndExpr | +| operations.rb:49:1:49:10 | ... \| ... | \| | operations.rb:49:1:49:3 | bar | operations.rb:49:7:49:10 | 0x02 | BitwiseOrExpr | +| operations.rb:50:1:50:9 | ... ^ ... | ^ | operations.rb:50:1:50:3 | baz | operations.rb:50:7:50:9 | qux | BitwiseXorExpr | +| operations.rb:81:4:81:6 | ... << ... | << | operations.rb:81:2:81:2 | x | operations.rb:81:8:81:8 | 2 | LShiftExpr | +| operations.rb:82:4:82:6 | ... >> ... | >> | operations.rb:82:2:82:2 | y | operations.rb:82:8:82:8 | 3 | RShiftExpr | +| operations.rb:83:6:83:7 | ... & ... | & | operations.rb:83:2:83:4 | foo | operations.rb:83:9:83:12 | mask | BitwiseAndExpr | +| operations.rb:84:6:84:7 | ... \| ... | \| | operations.rb:84:2:84:4 | bar | operations.rb:84:9:84:12 | 0x01 | BitwiseOrExpr | +| operations.rb:85:6:85:7 | ... ^ ... | ^ | operations.rb:85:2:85:4 | baz | operations.rb:85:9:85:11 | qux | BitwiseXorExpr | +comparisonOperations +| operations.rb:53:1:53:6 | ... == ... | == | operations.rb:53:1:53:1 | x | operations.rb:53:6:53:6 | y | EqExpr | +| operations.rb:54:1:54:8 | ... != ... | != | operations.rb:54:1:54:1 | a | operations.rb:54:6:54:8 | 123 | NEExpr | +| operations.rb:55:1:55:7 | ... === ... | === | operations.rb:55:1:55:1 | m | operations.rb:55:7:55:7 | n | CaseEqExpr | +| operations.rb:58:1:58:5 | ... > ... | > | operations.rb:58:1:58:1 | x | operations.rb:58:5:58:5 | 0 | GTExpr | +| operations.rb:59:1:59:8 | ... >= ... | >= | operations.rb:59:1:59:1 | y | operations.rb:59:6:59:8 | 100 | GEExpr | +| operations.rb:60:1:60:5 | ... < ... | < | operations.rb:60:1:60:1 | a | operations.rb:60:5:60:5 | b | LTExpr | +| operations.rb:61:1:61:8 | ... <= ... | <= | operations.rb:61:1:61:1 | 7 | operations.rb:61:6:61:8 | foo | LEExpr | +equalityOperations +| operations.rb:53:1:53:6 | ... == ... | == | operations.rb:53:1:53:1 | x | operations.rb:53:6:53:6 | y | EqExpr | +| operations.rb:54:1:54:8 | ... != ... | != | operations.rb:54:1:54:1 | a | operations.rb:54:6:54:8 | 123 | NEExpr | +| operations.rb:55:1:55:7 | ... === ... | === | operations.rb:55:1:55:1 | m | operations.rb:55:7:55:7 | n | CaseEqExpr | +relationalOperations +| operations.rb:58:1:58:5 | ... > ... | > | operations.rb:58:5:58:5 | 0 | operations.rb:58:1:58:1 | x | GTExpr | +| operations.rb:59:1:59:8 | ... >= ... | >= | operations.rb:59:6:59:8 | 100 | operations.rb:59:1:59:1 | y | GEExpr | +| operations.rb:60:1:60:5 | ... < ... | < | operations.rb:60:1:60:1 | a | operations.rb:60:5:60:5 | b | LTExpr | +| operations.rb:61:1:61:8 | ... <= ... | <= | operations.rb:61:1:61:1 | 7 | operations.rb:61:6:61:8 | foo | LEExpr | +spaceshipExprs +| operations.rb:64:1:64:7 | ... <=> ... | <=> | operations.rb:64:1:64:1 | a | operations.rb:64:7:64:7 | b | SpaceshipExpr | +regExpMatchExprs +| operations.rb:65:1:65:15 | ... =~ ... | =~ | operations.rb:65:1:65:4 | name | operations.rb:65:9:65:15 | /foo.*/ | RegExpMatchExpr | +noRegExpMatchExprs +| operations.rb:66:1:66:17 | ... !~ ... | !~ | operations.rb:66:1:66:6 | handle | operations.rb:66:11:66:17 | /.*bar/ | NoRegExpMatchExpr | diff --git a/ruby/ql/test/library-tests/ast/operations/binary.ql b/ruby/ql/test/library-tests/ast/operations/binary.ql new file mode 100644 index 00000000000..a2900adca95 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/operations/binary.ql @@ -0,0 +1,67 @@ +import ruby + +query predicate binaryOperations( + BinaryOperation o, string operator, Expr left, Expr right, string pClass +) { + operator = o.getOperator() and + left = o.getLeftOperand() and + right = o.getRightOperand() and + pClass = o.getAPrimaryQlClass() +} + +query predicate binaryArithmeticOperations( + BinaryArithmeticOperation o, string operator, Expr left, Expr right, string pClass +) { + binaryOperations(o, operator, left, right, pClass) +} + +query predicate binaryLogicalOperations( + BinaryLogicalOperation o, string operator, Expr left, Expr right, string pClass +) { + binaryOperations(o, operator, left, right, pClass) +} + +query predicate binaryBitwiseOperations( + BinaryBitwiseOperation o, string operator, Expr left, Expr right, string pClass +) { + binaryOperations(o, operator, left, right, pClass) +} + +query predicate comparisonOperations( + ComparisonOperation o, string operator, Expr left, Expr right, string pClass +) { + binaryOperations(o, operator, left, right, pClass) +} + +query predicate equalityOperations( + EqualityOperation o, string operator, Expr left, Expr right, string pClass +) { + binaryOperations(o, operator, left, right, pClass) +} + +query predicate relationalOperations( + RelationalOperation o, string operator, Expr lesser, Expr greater, string pClass +) { + operator = o.getOperator() and + lesser = o.getLesserOperand() and + greater = o.getGreaterOperand() and + pClass = o.getAPrimaryQlClass() +} + +query predicate spaceshipExprs( + SpaceshipExpr e, string operator, Expr left, Expr right, string pClass +) { + binaryOperations(e, operator, left, right, pClass) +} + +query predicate regExpMatchExprs( + RegExpMatchExpr e, string operator, Expr left, Expr right, string pClass +) { + binaryOperations(e, operator, left, right, pClass) +} + +query predicate noRegExpMatchExprs( + NoRegExpMatchExpr e, string operator, Expr left, Expr right, string pClass +) { + binaryOperations(e, operator, left, right, pClass) +} diff --git a/ruby/ql/test/library-tests/ast/operations/operation.expected b/ruby/ql/test/library-tests/ast/operations/operation.expected new file mode 100644 index 00000000000..76dbae694be --- /dev/null +++ b/ruby/ql/test/library-tests/ast/operations/operation.expected @@ -0,0 +1,196 @@ +| operations.rb:3:1:3:5 | ... = ... | = | operations.rb:3:1:3:1 | a | AssignExpr | +| operations.rb:3:1:3:5 | ... = ... | = | operations.rb:3:5:3:5 | 0 | AssignExpr | +| operations.rb:4:1:4:5 | ... = ... | = | operations.rb:4:1:4:1 | b | AssignExpr | +| operations.rb:4:1:4:5 | ... = ... | = | operations.rb:4:5:4:5 | 0 | AssignExpr | +| operations.rb:5:1:5:7 | ... = ... | = | operations.rb:5:1:5:3 | bar | AssignExpr | +| operations.rb:5:1:5:7 | ... = ... | = | operations.rb:5:7:5:7 | 0 | AssignExpr | +| operations.rb:6:1:6:8 | ... = ... | = | operations.rb:6:1:6:4 | base | AssignExpr | +| operations.rb:6:1:6:8 | ... = ... | = | operations.rb:6:8:6:8 | 0 | AssignExpr | +| operations.rb:7:1:7:7 | ... = ... | = | operations.rb:7:1:7:3 | baz | AssignExpr | +| operations.rb:7:1:7:7 | ... = ... | = | operations.rb:7:7:7:7 | 0 | AssignExpr | +| operations.rb:8:1:8:7 | ... = ... | = | operations.rb:8:1:8:3 | foo | AssignExpr | +| operations.rb:8:1:8:7 | ... = ... | = | operations.rb:8:7:8:7 | 0 | AssignExpr | +| operations.rb:9:1:9:10 | ... = ... | = | operations.rb:9:1:9:6 | handle | AssignExpr | +| operations.rb:9:1:9:10 | ... = ... | = | operations.rb:9:10:9:10 | 0 | AssignExpr | +| operations.rb:10:1:10:5 | ... = ... | = | operations.rb:10:1:10:1 | m | AssignExpr | +| operations.rb:10:1:10:5 | ... = ... | = | operations.rb:10:5:10:5 | 0 | AssignExpr | +| operations.rb:11:1:11:8 | ... = ... | = | operations.rb:11:1:11:4 | mask | AssignExpr | +| operations.rb:11:1:11:8 | ... = ... | = | operations.rb:11:8:11:8 | 0 | AssignExpr | +| operations.rb:12:1:12:5 | ... = ... | = | operations.rb:12:1:12:1 | n | AssignExpr | +| operations.rb:12:1:12:5 | ... = ... | = | operations.rb:12:5:12:5 | 0 | AssignExpr | +| operations.rb:13:1:13:8 | ... = ... | = | operations.rb:13:1:13:4 | name | AssignExpr | +| operations.rb:13:1:13:8 | ... = ... | = | operations.rb:13:8:13:8 | 0 | AssignExpr | +| operations.rb:14:1:14:7 | ... = ... | = | operations.rb:14:1:14:3 | num | AssignExpr | +| operations.rb:14:1:14:7 | ... = ... | = | operations.rb:14:7:14:7 | 0 | AssignExpr | +| operations.rb:15:1:15:9 | ... = ... | = | operations.rb:15:1:15:5 | power | AssignExpr | +| operations.rb:15:1:15:9 | ... = ... | = | operations.rb:15:9:15:9 | 0 | AssignExpr | +| operations.rb:16:1:16:7 | ... = ... | = | operations.rb:16:1:16:3 | qux | AssignExpr | +| operations.rb:16:1:16:7 | ... = ... | = | operations.rb:16:7:16:7 | 0 | AssignExpr | +| operations.rb:17:1:17:5 | ... = ... | = | operations.rb:17:1:17:1 | w | AssignExpr | +| operations.rb:17:1:17:5 | ... = ... | = | operations.rb:17:5:17:5 | 0 | AssignExpr | +| operations.rb:18:1:18:5 | ... = ... | = | operations.rb:18:1:18:1 | x | AssignExpr | +| operations.rb:18:1:18:5 | ... = ... | = | operations.rb:18:5:18:5 | 0 | AssignExpr | +| operations.rb:19:1:19:5 | ... = ... | = | operations.rb:19:1:19:1 | y | AssignExpr | +| operations.rb:19:1:19:5 | ... = ... | = | operations.rb:19:5:19:5 | 0 | AssignExpr | +| operations.rb:20:1:20:5 | ... = ... | = | operations.rb:20:1:20:1 | z | AssignExpr | +| operations.rb:20:1:20:5 | ... = ... | = | operations.rb:20:5:20:5 | 0 | AssignExpr | +| operations.rb:23:1:23:2 | ! ... | ! | operations.rb:23:2:23:2 | a | NotExpr | +| operations.rb:24:1:24:5 | not ... | not | operations.rb:24:5:24:5 | b | NotExpr | +| operations.rb:25:1:25:3 | + ... | + | operations.rb:25:2:25:3 | 14 | UnaryPlusExpr | +| operations.rb:26:1:26:2 | - ... | - | operations.rb:26:2:26:2 | 7 | UnaryMinusExpr | +| operations.rb:27:1:27:2 | ~ ... | ~ | operations.rb:27:2:27:2 | x | ComplementExpr | +| operations.rb:28:1:28:12 | defined? ... | defined? | operations.rb:28:10:28:12 | foo | DefinedExpr | +| operations.rb:29:20:29:23 | * ... | * | operations.rb:29:21:29:23 | [...] | SplatExpr | +| operations.rb:29:31:29:42 | ** ... | ** | operations.rb:29:33:29:42 | {...} | HashSplatExpr | +| operations.rb:32:1:32:7 | ... + ... | + | operations.rb:32:1:32:1 | w | AddExpr | +| operations.rb:32:1:32:7 | ... + ... | + | operations.rb:32:5:32:7 | 234 | AddExpr | +| operations.rb:33:1:33:6 | ... - ... | - | operations.rb:33:1:33:1 | x | SubExpr | +| operations.rb:33:1:33:6 | ... - ... | - | operations.rb:33:5:33:6 | 17 | SubExpr | +| operations.rb:34:1:34:6 | ... * ... | * | operations.rb:34:1:34:1 | y | MulExpr | +| operations.rb:34:1:34:6 | ... * ... | * | operations.rb:34:5:34:6 | 10 | MulExpr | +| operations.rb:35:1:35:5 | ... / ... | / | operations.rb:35:1:35:1 | z | DivExpr | +| operations.rb:35:1:35:5 | ... / ... | / | operations.rb:35:5:35:5 | 2 | DivExpr | +| operations.rb:36:1:36:7 | ... % ... | % | operations.rb:36:1:36:3 | num | ModuloExpr | +| operations.rb:36:1:36:7 | ... % ... | % | operations.rb:36:7:36:7 | 2 | ModuloExpr | +| operations.rb:37:1:37:13 | ... ** ... | ** | operations.rb:37:1:37:4 | base | ExponentExpr | +| operations.rb:37:1:37:13 | ... ** ... | ** | operations.rb:37:9:37:13 | power | ExponentExpr | +| operations.rb:40:1:40:10 | ... && ... | && | operations.rb:40:1:40:3 | foo | LogicalAndExpr | +| operations.rb:40:1:40:10 | ... && ... | && | operations.rb:40:8:40:10 | bar | LogicalAndExpr | +| operations.rb:41:1:41:11 | ... and ... | and | operations.rb:41:1:41:3 | baz | LogicalAndExpr | +| operations.rb:41:1:41:11 | ... and ... | and | operations.rb:41:9:41:11 | qux | LogicalAndExpr | +| operations.rb:42:1:42:6 | ... or ... | or | operations.rb:42:1:42:1 | a | LogicalOrExpr | +| operations.rb:42:1:42:6 | ... or ... | or | operations.rb:42:6:42:6 | b | LogicalOrExpr | +| operations.rb:43:1:43:6 | ... \|\| ... | \|\| | operations.rb:43:1:43:1 | x | LogicalOrExpr | +| operations.rb:43:1:43:6 | ... \|\| ... | \|\| | operations.rb:43:6:43:6 | y | LogicalOrExpr | +| operations.rb:46:1:46:6 | ... << ... | << | operations.rb:46:1:46:1 | x | LShiftExpr | +| operations.rb:46:1:46:6 | ... << ... | << | operations.rb:46:6:46:6 | 3 | LShiftExpr | +| operations.rb:47:1:47:7 | ... >> ... | >> | operations.rb:47:1:47:1 | y | RShiftExpr | +| operations.rb:47:1:47:7 | ... >> ... | >> | operations.rb:47:6:47:7 | 16 | RShiftExpr | +| operations.rb:48:1:48:10 | ... & ... | & | operations.rb:48:1:48:3 | foo | BitwiseAndExpr | +| operations.rb:48:1:48:10 | ... & ... | & | operations.rb:48:7:48:10 | 0xff | BitwiseAndExpr | +| operations.rb:49:1:49:10 | ... \| ... | \| | operations.rb:49:1:49:3 | bar | BitwiseOrExpr | +| operations.rb:49:1:49:10 | ... \| ... | \| | operations.rb:49:7:49:10 | 0x02 | BitwiseOrExpr | +| operations.rb:50:1:50:9 | ... ^ ... | ^ | operations.rb:50:1:50:3 | baz | BitwiseXorExpr | +| operations.rb:50:1:50:9 | ... ^ ... | ^ | operations.rb:50:7:50:9 | qux | BitwiseXorExpr | +| operations.rb:53:1:53:6 | ... == ... | == | operations.rb:53:1:53:1 | x | EqExpr | +| operations.rb:53:1:53:6 | ... == ... | == | operations.rb:53:6:53:6 | y | EqExpr | +| operations.rb:54:1:54:8 | ... != ... | != | operations.rb:54:1:54:1 | a | NEExpr | +| operations.rb:54:1:54:8 | ... != ... | != | operations.rb:54:6:54:8 | 123 | NEExpr | +| operations.rb:55:1:55:7 | ... === ... | === | operations.rb:55:1:55:1 | m | CaseEqExpr | +| operations.rb:55:1:55:7 | ... === ... | === | operations.rb:55:7:55:7 | n | CaseEqExpr | +| operations.rb:58:1:58:5 | ... > ... | > | operations.rb:58:1:58:1 | x | GTExpr | +| operations.rb:58:1:58:5 | ... > ... | > | operations.rb:58:5:58:5 | 0 | GTExpr | +| operations.rb:59:1:59:8 | ... >= ... | >= | operations.rb:59:1:59:1 | y | GEExpr | +| operations.rb:59:1:59:8 | ... >= ... | >= | operations.rb:59:6:59:8 | 100 | GEExpr | +| operations.rb:60:1:60:5 | ... < ... | < | operations.rb:60:1:60:1 | a | LTExpr | +| operations.rb:60:1:60:5 | ... < ... | < | operations.rb:60:5:60:5 | b | LTExpr | +| operations.rb:61:1:61:8 | ... <= ... | <= | operations.rb:61:1:61:1 | 7 | LEExpr | +| operations.rb:61:1:61:8 | ... <= ... | <= | operations.rb:61:6:61:8 | foo | LEExpr | +| operations.rb:64:1:64:7 | ... <=> ... | <=> | operations.rb:64:1:64:1 | a | SpaceshipExpr | +| operations.rb:64:1:64:7 | ... <=> ... | <=> | operations.rb:64:7:64:7 | b | SpaceshipExpr | +| operations.rb:65:1:65:15 | ... =~ ... | =~ | operations.rb:65:1:65:4 | name | RegExpMatchExpr | +| operations.rb:65:1:65:15 | ... =~ ... | =~ | operations.rb:65:9:65:15 | /foo.*/ | RegExpMatchExpr | +| operations.rb:66:1:66:17 | ... !~ ... | !~ | operations.rb:66:1:66:6 | handle | NoRegExpMatchExpr | +| operations.rb:66:1:66:17 | ... !~ ... | !~ | operations.rb:66:11:66:17 | /.*bar/ | NoRegExpMatchExpr | +| operations.rb:69:1:69:8 | ... += ... | += | operations.rb:69:1:69:1 | x | AssignAddExpr | +| operations.rb:69:1:69:8 | ... += ... | += | operations.rb:69:6:69:8 | 128 | AssignAddExpr | +| operations.rb:69:1:69:8 | ... = ... | = | operations.rb:69:1:69:1 | x | AssignExpr | +| operations.rb:69:1:69:8 | ... = ... | = | operations.rb:69:3:69:4 | ... + ... | AssignExpr | +| operations.rb:69:3:69:4 | ... + ... | + | operations.rb:69:1:69:1 | x | AddExpr | +| operations.rb:69:3:69:4 | ... + ... | + | operations.rb:69:6:69:8 | 128 | AddExpr | +| operations.rb:70:1:70:7 | ... -= ... | -= | operations.rb:70:1:70:1 | y | AssignSubExpr | +| operations.rb:70:1:70:7 | ... -= ... | -= | operations.rb:70:6:70:7 | 32 | AssignSubExpr | +| operations.rb:70:1:70:7 | ... = ... | = | operations.rb:70:1:70:1 | y | AssignExpr | +| operations.rb:70:1:70:7 | ... = ... | = | operations.rb:70:3:70:4 | ... - ... | AssignExpr | +| operations.rb:70:3:70:4 | ... - ... | - | operations.rb:70:1:70:1 | y | SubExpr | +| operations.rb:70:3:70:4 | ... - ... | - | operations.rb:70:6:70:7 | 32 | SubExpr | +| operations.rb:71:1:71:7 | ... *= ... | *= | operations.rb:71:1:71:1 | a | AssignMulExpr | +| operations.rb:71:1:71:7 | ... *= ... | *= | operations.rb:71:6:71:7 | 12 | AssignMulExpr | +| operations.rb:71:1:71:7 | ... = ... | = | operations.rb:71:1:71:1 | a | AssignExpr | +| operations.rb:71:1:71:7 | ... = ... | = | operations.rb:71:3:71:4 | ... * ... | AssignExpr | +| operations.rb:71:3:71:4 | ... * ... | * | operations.rb:71:1:71:1 | a | MulExpr | +| operations.rb:71:3:71:4 | ... * ... | * | operations.rb:71:6:71:7 | 12 | MulExpr | +| operations.rb:72:1:72:6 | ... /= ... | /= | operations.rb:72:1:72:1 | b | AssignDivExpr | +| operations.rb:72:1:72:6 | ... /= ... | /= | operations.rb:72:6:72:6 | 4 | AssignDivExpr | +| operations.rb:72:1:72:6 | ... = ... | = | operations.rb:72:1:72:1 | b | AssignExpr | +| operations.rb:72:1:72:6 | ... = ... | = | operations.rb:72:3:72:4 | ... / ... | AssignExpr | +| operations.rb:72:3:72:4 | ... / ... | / | operations.rb:72:1:72:1 | b | DivExpr | +| operations.rb:72:3:72:4 | ... / ... | / | operations.rb:72:6:72:6 | 4 | DivExpr | +| operations.rb:73:1:73:6 | ... %= ... | %= | operations.rb:73:1:73:1 | z | AssignModuloExpr | +| operations.rb:73:1:73:6 | ... %= ... | %= | operations.rb:73:6:73:6 | 2 | AssignModuloExpr | +| operations.rb:73:1:73:6 | ... = ... | = | operations.rb:73:1:73:1 | z | AssignExpr | +| operations.rb:73:1:73:6 | ... = ... | = | operations.rb:73:3:73:4 | ... % ... | AssignExpr | +| operations.rb:73:3:73:4 | ... % ... | % | operations.rb:73:1:73:1 | z | ModuloExpr | +| operations.rb:73:3:73:4 | ... % ... | % | operations.rb:73:6:73:6 | 2 | ModuloExpr | +| operations.rb:74:1:74:11 | ... **= ... | **= | operations.rb:74:1:74:3 | foo | AssignExponentExpr | +| operations.rb:74:1:74:11 | ... **= ... | **= | operations.rb:74:9:74:11 | bar | AssignExponentExpr | +| operations.rb:74:1:74:11 | ... = ... | = | operations.rb:74:1:74:3 | foo | AssignExpr | +| operations.rb:74:1:74:11 | ... = ... | = | operations.rb:74:5:74:7 | ... ** ... | AssignExpr | +| operations.rb:74:5:74:7 | ... ** ... | ** | operations.rb:74:1:74:3 | foo | ExponentExpr | +| operations.rb:74:5:74:7 | ... ** ... | ** | operations.rb:74:9:74:11 | bar | ExponentExpr | +| operations.rb:77:2:77:8 | ... &&= ... | &&= | operations.rb:77:2:77:2 | x | AssignLogicalAndExpr | +| operations.rb:77:2:77:8 | ... &&= ... | &&= | operations.rb:77:8:77:8 | y | AssignLogicalAndExpr | +| operations.rb:77:2:77:8 | ... = ... | = | operations.rb:77:2:77:2 | x | AssignExpr | +| operations.rb:77:2:77:8 | ... = ... | = | operations.rb:77:4:77:6 | ... && ... | AssignExpr | +| operations.rb:77:4:77:6 | ... && ... | && | operations.rb:77:2:77:2 | x | LogicalAndExpr | +| operations.rb:77:4:77:6 | ... && ... | && | operations.rb:77:8:77:8 | y | LogicalAndExpr | +| operations.rb:78:2:78:8 | ... = ... | = | operations.rb:78:2:78:2 | a | AssignExpr | +| operations.rb:78:2:78:8 | ... = ... | = | operations.rb:78:4:78:6 | ... \|\| ... | AssignExpr | +| operations.rb:78:2:78:8 | ... \|\|= ... | \|\|= | operations.rb:78:2:78:2 | a | AssignLogicalOrExpr | +| operations.rb:78:2:78:8 | ... \|\|= ... | \|\|= | operations.rb:78:8:78:8 | b | AssignLogicalOrExpr | +| operations.rb:78:4:78:6 | ... \|\| ... | \|\| | operations.rb:78:2:78:2 | a | LogicalOrExpr | +| operations.rb:78:4:78:6 | ... \|\| ... | \|\| | operations.rb:78:8:78:8 | b | LogicalOrExpr | +| operations.rb:81:2:81:8 | ... <<= ... | <<= | operations.rb:81:2:81:2 | x | AssignLShiftExpr | +| operations.rb:81:2:81:8 | ... <<= ... | <<= | operations.rb:81:8:81:8 | 2 | AssignLShiftExpr | +| operations.rb:81:2:81:8 | ... = ... | = | operations.rb:81:2:81:2 | x | AssignExpr | +| operations.rb:81:2:81:8 | ... = ... | = | operations.rb:81:4:81:6 | ... << ... | AssignExpr | +| operations.rb:81:4:81:6 | ... << ... | << | operations.rb:81:2:81:2 | x | LShiftExpr | +| operations.rb:81:4:81:6 | ... << ... | << | operations.rb:81:8:81:8 | 2 | LShiftExpr | +| operations.rb:82:2:82:8 | ... = ... | = | operations.rb:82:2:82:2 | y | AssignExpr | +| operations.rb:82:2:82:8 | ... = ... | = | operations.rb:82:4:82:6 | ... >> ... | AssignExpr | +| operations.rb:82:2:82:8 | ... >>= ... | >>= | operations.rb:82:2:82:2 | y | AssignRShiftExpr | +| operations.rb:82:2:82:8 | ... >>= ... | >>= | operations.rb:82:8:82:8 | 3 | AssignRShiftExpr | +| operations.rb:82:4:82:6 | ... >> ... | >> | operations.rb:82:2:82:2 | y | RShiftExpr | +| operations.rb:82:4:82:6 | ... >> ... | >> | operations.rb:82:8:82:8 | 3 | RShiftExpr | +| operations.rb:83:2:83:12 | ... &= ... | &= | operations.rb:83:2:83:4 | foo | AssignBitwiseAndExpr | +| operations.rb:83:2:83:12 | ... &= ... | &= | operations.rb:83:9:83:12 | mask | AssignBitwiseAndExpr | +| operations.rb:83:2:83:12 | ... = ... | = | operations.rb:83:2:83:4 | foo | AssignExpr | +| operations.rb:83:2:83:12 | ... = ... | = | operations.rb:83:6:83:7 | ... & ... | AssignExpr | +| operations.rb:83:6:83:7 | ... & ... | & | operations.rb:83:2:83:4 | foo | BitwiseAndExpr | +| operations.rb:83:6:83:7 | ... & ... | & | operations.rb:83:9:83:12 | mask | BitwiseAndExpr | +| operations.rb:84:2:84:12 | ... = ... | = | operations.rb:84:2:84:4 | bar | AssignExpr | +| operations.rb:84:2:84:12 | ... = ... | = | operations.rb:84:6:84:7 | ... \| ... | AssignExpr | +| operations.rb:84:2:84:12 | ... \|= ... | \|= | operations.rb:84:2:84:4 | bar | AssignBitwiseOrExpr | +| operations.rb:84:2:84:12 | ... \|= ... | \|= | operations.rb:84:9:84:12 | 0x01 | AssignBitwiseOrExpr | +| operations.rb:84:6:84:7 | ... \| ... | \| | operations.rb:84:2:84:4 | bar | BitwiseOrExpr | +| operations.rb:84:6:84:7 | ... \| ... | \| | operations.rb:84:9:84:12 | 0x01 | BitwiseOrExpr | +| operations.rb:85:2:85:11 | ... = ... | = | operations.rb:85:2:85:4 | baz | AssignExpr | +| operations.rb:85:2:85:11 | ... = ... | = | operations.rb:85:6:85:7 | ... ^ ... | AssignExpr | +| operations.rb:85:2:85:11 | ... ^= ... | ^= | operations.rb:85:2:85:4 | baz | AssignBitwiseXorExpr | +| operations.rb:85:2:85:11 | ... ^= ... | ^= | operations.rb:85:9:85:11 | qux | AssignBitwiseXorExpr | +| operations.rb:85:6:85:7 | ... ^ ... | ^ | operations.rb:85:2:85:4 | baz | BitwiseXorExpr | +| operations.rb:85:6:85:7 | ... ^ ... | ^ | operations.rb:85:9:85:11 | qux | BitwiseXorExpr | +| operations.rb:88:3:88:8 | ... = ... | = | operations.rb:88:3:88:4 | @x | AssignExpr | +| operations.rb:88:3:88:8 | ... = ... | = | operations.rb:88:8:88:8 | 1 | AssignExpr | +| operations.rb:89:3:89:9 | ... += ... | += | operations.rb:89:3:89:4 | @x | AssignAddExpr | +| operations.rb:89:3:89:9 | ... += ... | += | operations.rb:89:9:89:9 | 2 | AssignAddExpr | +| operations.rb:89:3:89:9 | ... = ... | = | operations.rb:89:3:89:4 | @x | AssignExpr | +| operations.rb:89:3:89:9 | ... = ... | = | operations.rb:89:6:89:7 | ... + ... | AssignExpr | +| operations.rb:89:6:89:7 | ... + ... | + | operations.rb:89:3:89:4 | @x | AddExpr | +| operations.rb:89:6:89:7 | ... + ... | + | operations.rb:89:9:89:9 | 2 | AddExpr | +| operations.rb:91:3:91:9 | ... = ... | = | operations.rb:91:3:91:5 | @@y | AssignExpr | +| operations.rb:91:3:91:9 | ... = ... | = | operations.rb:91:9:91:9 | 3 | AssignExpr | +| operations.rb:92:3:92:10 | ... /= ... | /= | operations.rb:92:3:92:5 | @@y | AssignDivExpr | +| operations.rb:92:3:92:10 | ... /= ... | /= | operations.rb:92:10:92:10 | 4 | AssignDivExpr | +| operations.rb:92:3:92:10 | ... = ... | = | operations.rb:92:3:92:5 | @@y | AssignExpr | +| operations.rb:92:3:92:10 | ... = ... | = | operations.rb:92:7:92:8 | ... / ... | AssignExpr | +| operations.rb:92:7:92:8 | ... / ... | / | operations.rb:92:3:92:5 | @@y | DivExpr | +| operations.rb:92:7:92:8 | ... / ... | / | operations.rb:92:10:92:10 | 4 | DivExpr | +| operations.rb:95:1:95:15 | ... = ... | = | operations.rb:95:1:95:11 | $global_var | AssignExpr | +| operations.rb:95:1:95:15 | ... = ... | = | operations.rb:95:15:95:15 | 5 | AssignExpr | +| operations.rb:96:1:96:16 | ... *= ... | *= | operations.rb:96:1:96:11 | $global_var | AssignMulExpr | +| operations.rb:96:1:96:16 | ... *= ... | *= | operations.rb:96:16:96:16 | 6 | AssignMulExpr | +| operations.rb:96:1:96:16 | ... = ... | = | operations.rb:96:1:96:11 | $global_var | AssignExpr | +| operations.rb:96:1:96:16 | ... = ... | = | operations.rb:96:13:96:14 | ... * ... | AssignExpr | +| operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:1:96:11 | $global_var | MulExpr | +| operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:16:96:16 | 6 | MulExpr | diff --git a/ruby/ql/test/library-tests/ast/operations/operation.ql b/ruby/ql/test/library-tests/ast/operations/operation.ql new file mode 100644 index 00000000000..2a1db9fd3df --- /dev/null +++ b/ruby/ql/test/library-tests/ast/operations/operation.ql @@ -0,0 +1,8 @@ +import ruby + +from Operation o, string operator, Expr operand, string pClass +where + operator = o.getOperator() and + operand = o.getAnOperand() and + pClass = o.getAPrimaryQlClass() +select o, operator, operand, pClass diff --git a/ruby/ql/test/library-tests/ast/operations/operations.rb b/ruby/ql/test/library-tests/ast/operations/operations.rb new file mode 100644 index 00000000000..fcbc4551bde --- /dev/null +++ b/ruby/ql/test/library-tests/ast/operations/operations.rb @@ -0,0 +1,96 @@ +# Start with assignments to all the identifiers used below, so that they are +# interpreted as variables. +a = 0 +b = 0 +bar = 0 +base = 0 +baz = 0 +foo = 0 +handle = 0 +m = 0 +mask = 0 +n = 0 +name = 0 +num = 0 +power = 0 +qux = 0 +w = 0 +x = 0 +y = 0 +z = 0 + +# Unary operations +!a +not b ++14 +-7 +~x +defined? foo +def foo; return 1, *[2], a:3, **{b:4, c:5} end + +# Binary arithmetic operations +w + 234 +x - 17 +y * 10 +z / 2 +num % 2 +base ** power + +# Binary logical operations +foo && bar +baz and qux +a or b +x || y + +# Binary bitwise operations +x << 3 +y >> 16 +foo & 0xff +bar | 0x02 +baz ^ qux + +# Equality operations +x == y +a != 123 +m === n + +# Relational operations +x > 0 +y >= 100 +a < b +7 <= foo + +# Misc binary operations +a <=> b +name =~ /foo.*/ +handle !~ /.*bar/ + +# Arithmetic assign operations +x += 128 +y -= 32 +a *= 12 +b /= 4 +z %= 2 +foo **= bar + +# Logical assign operations + x &&= y + a ||= b + + # Bitwise assign operations + x <<= 2 + y >>= 3 + foo &= mask + bar |= 0x01 + baz ^= qux + +class X + @x = 1 + @x += 2 + + @@y = 3 + @@y /= 4 +end + +$global_var = 5 +$global_var *= 6 diff --git a/ruby/ql/test/library-tests/ast/operations/unary.expected b/ruby/ql/test/library-tests/ast/operations/unary.expected new file mode 100644 index 00000000000..0190e9bcf56 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/operations/unary.expected @@ -0,0 +1,17 @@ +unaryOperations +| operations.rb:23:1:23:2 | ! ... | ! | operations.rb:23:2:23:2 | a | NotExpr | +| operations.rb:24:1:24:5 | not ... | not | operations.rb:24:5:24:5 | b | NotExpr | +| operations.rb:25:1:25:3 | + ... | + | operations.rb:25:2:25:3 | 14 | UnaryPlusExpr | +| operations.rb:26:1:26:2 | - ... | - | operations.rb:26:2:26:2 | 7 | UnaryMinusExpr | +| operations.rb:27:1:27:2 | ~ ... | ~ | operations.rb:27:2:27:2 | x | ComplementExpr | +| operations.rb:28:1:28:12 | defined? ... | defined? | operations.rb:28:10:28:12 | foo | DefinedExpr | +| operations.rb:29:20:29:23 | * ... | * | operations.rb:29:21:29:23 | [...] | SplatExpr | +| operations.rb:29:31:29:42 | ** ... | ** | operations.rb:29:33:29:42 | {...} | HashSplatExpr | +unaryLogicalOperations +| operations.rb:23:1:23:2 | ! ... | ! | operations.rb:23:2:23:2 | a | NotExpr | +| operations.rb:24:1:24:5 | not ... | not | operations.rb:24:5:24:5 | b | NotExpr | +unaryArithmeticOperations +| operations.rb:25:1:25:3 | + ... | + | operations.rb:25:2:25:3 | 14 | UnaryPlusExpr | +| operations.rb:26:1:26:2 | - ... | - | operations.rb:26:2:26:2 | 7 | UnaryMinusExpr | +unaryBitwiseOperations +| operations.rb:27:1:27:2 | ~ ... | ~ | operations.rb:27:2:27:2 | x | ComplementExpr | diff --git a/ruby/ql/test/library-tests/ast/operations/unary.ql b/ruby/ql/test/library-tests/ast/operations/unary.ql new file mode 100644 index 00000000000..f94e1def2d6 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/operations/unary.ql @@ -0,0 +1,25 @@ +import ruby + +query predicate unaryOperations(UnaryOperation o, string operator, Expr operand, string pClass) { + operator = o.getOperator() and + operand = o.getOperand() and + pClass = o.getAPrimaryQlClass() +} + +query predicate unaryLogicalOperations( + UnaryLogicalOperation o, string operator, Expr operand, string pClass +) { + unaryOperations(o, operator, operand, pClass) +} + +query predicate unaryArithmeticOperations( + UnaryArithmeticOperation o, string operator, Expr operand, string pClass +) { + unaryOperations(o, operator, operand, pClass) +} + +query predicate unaryBitwiseOperations( + UnaryBitwiseOperation o, string operator, Expr operand, string pClass +) { + unaryOperations(o, operator, operand, pClass) +} diff --git a/ruby/ql/test/library-tests/ast/params/params.expected b/ruby/ql/test/library-tests/ast/params/params.expected new file mode 100644 index 00000000000..5ae4fc32b38 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/params/params.expected @@ -0,0 +1,149 @@ +idParams +| params.rb:4:30:4:32 | foo | foo | +| params.rb:4:35:4:37 | bar | bar | +| params.rb:4:40:4:42 | baz | baz | +| params.rb:9:15:9:17 | key | key | +| params.rb:9:20:9:24 | value | value | +| params.rb:14:11:14:13 | foo | foo | +| params.rb:14:16:14:18 | bar | bar | +| params.rb:30:23:30:28 | wibble | wibble | +| params.rb:30:31:30:36 | *splat | splat | +| params.rb:30:39:30:52 | **double_splat | double_splat | +| params.rb:34:16:34:18 | val | val | +| params.rb:34:21:34:26 | *splat | splat | +| params.rb:34:29:34:42 | **double_splat | double_splat | +| params.rb:38:26:38:26 | x | x | +| params.rb:38:29:38:33 | *blah | blah | +| params.rb:38:36:38:43 | **wibble | wibble | +| params.rb:41:32:41:32 | x | x | +| params.rb:41:35:41:37 | foo | foo | +| params.rb:41:41:41:43 | bar | bar | +| params.rb:46:28:46:33 | &block | block | +| params.rb:49:28:49:29 | xx | xx | +| params.rb:49:33:49:34 | yy | yy | +| params.rb:53:34:53:34 | x | x | +| params.rb:53:37:53:37 | y | y | +| params.rb:53:41:53:41 | z | z | +| params.rb:58:33:58:36 | val1 | val1 | +| params.rb:58:39:58:42 | val2 | val2 | +| params.rb:58:49:58:52 | val3 | val3 | +| params.rb:62:29:62:34 | &block | block | +| params.rb:65:29:65:32 | name | name | +| params.rb:65:35:65:37 | age | age | +| params.rb:70:35:70:35 | a | a | +| params.rb:70:38:70:38 | b | b | +| params.rb:70:48:70:48 | c | c | +blockParams +| params.rb:46:28:46:33 | &block | block | +| params.rb:62:29:62:34 | &block | block | +patternParams +| params.rb:17:31:17:39 | (..., ...) | params.rb:17:32:17:32 | a | 0 | +| params.rb:17:31:17:39 | (..., ...) | params.rb:17:35:17:35 | b | 1 | +| params.rb:17:31:17:39 | (..., ...) | params.rb:17:38:17:38 | c | 2 | +| params.rb:22:15:22:20 | (..., ...) | params.rb:22:16:22:16 | a | 0 | +| params.rb:22:15:22:20 | (..., ...) | params.rb:22:19:22:19 | b | 1 | +| params.rb:25:23:25:37 | (..., ...) | params.rb:25:24:25:28 | first | 0 | +| params.rb:25:23:25:37 | (..., ...) | params.rb:25:31:25:36 | second | 1 | +| params.rb:25:40:25:54 | (..., ...) | params.rb:25:41:25:45 | third | 0 | +| params.rb:25:40:25:54 | (..., ...) | params.rb:25:48:25:53 | fourth | 1 | +splatParams +| params.rb:30:31:30:36 | *splat | splat | +| params.rb:34:21:34:26 | *splat | splat | +| params.rb:38:29:38:33 | *blah | blah | +hashSplatParams +| params.rb:30:39:30:52 | **double_splat | double_splat | +| params.rb:34:29:34:42 | **double_splat | double_splat | +| params.rb:38:36:38:43 | **wibble | wibble | +keywordParams +| params.rb:41:35:41:37 | foo | foo | (none) | +| params.rb:41:41:41:43 | bar | bar | 7 | +| params.rb:49:28:49:29 | xx | xx | (none) | +| params.rb:49:33:49:34 | yy | yy | 100 | +| params.rb:53:37:53:37 | y | y | (none) | +| params.rb:53:41:53:41 | z | z | 3 | +optionalParams +| params.rb:58:39:58:42 | val2 | val2 | params.rb:58:46:58:46 | 0 | +| params.rb:58:49:58:52 | val3 | val3 | params.rb:58:56:58:58 | 100 | +| params.rb:65:35:65:37 | age | age | params.rb:65:41:65:42 | 99 | +| params.rb:70:38:70:38 | b | b | params.rb:70:42:70:45 | 1000 | +| params.rb:70:48:70:48 | c | c | params.rb:70:52:70:53 | 20 | +paramsInMethods +| params.rb:4:1:5:3 | identifier_method_params | 0 | params.rb:4:30:4:32 | foo | SimpleParameter | +| params.rb:4:1:5:3 | identifier_method_params | 1 | params.rb:4:35:4:37 | bar | SimpleParameter | +| params.rb:4:1:5:3 | identifier_method_params | 2 | params.rb:4:40:4:42 | baz | SimpleParameter | +| params.rb:17:1:18:3 | destructured_method_param | 0 | params.rb:17:31:17:39 | (..., ...) | TuplePatternParameter | +| params.rb:30:1:31:3 | method_with_splat | 0 | params.rb:30:23:30:28 | wibble | SimpleParameter | +| params.rb:30:1:31:3 | method_with_splat | 1 | params.rb:30:31:30:36 | *splat | SplatParameter | +| params.rb:30:1:31:3 | method_with_splat | 2 | params.rb:30:39:30:52 | **double_splat | HashSplatParameter | +| params.rb:41:1:43:3 | method_with_keyword_params | 0 | params.rb:41:32:41:32 | x | SimpleParameter | +| params.rb:41:1:43:3 | method_with_keyword_params | 1 | params.rb:41:35:41:37 | foo | KeywordParameter | +| params.rb:41:1:43:3 | method_with_keyword_params | 2 | params.rb:41:41:41:43 | bar | KeywordParameter | +| params.rb:46:1:48:3 | use_block_with_keyword | 0 | params.rb:46:28:46:33 | &block | BlockParameter | +| params.rb:58:1:59:3 | method_with_optional_params | 0 | params.rb:58:33:58:36 | val1 | SimpleParameter | +| params.rb:58:1:59:3 | method_with_optional_params | 1 | params.rb:58:39:58:42 | val2 | OptionalParameter | +| params.rb:58:1:59:3 | method_with_optional_params | 2 | params.rb:58:49:58:52 | val3 | OptionalParameter | +| params.rb:62:1:64:3 | use_block_with_optional | 0 | params.rb:62:29:62:34 | &block | BlockParameter | +paramsInBlocks +| params.rb:9:11:11:3 | do ... end | 0 | params.rb:9:15:9:17 | key | SimpleParameter | +| params.rb:9:11:11:3 | do ... end | 1 | params.rb:9:20:9:24 | value | SimpleParameter | +| params.rb:22:12:22:32 | { ... } | 0 | params.rb:22:15:22:20 | (..., ...) | TuplePatternParameter | +| params.rb:34:12:35:3 | do ... end | 0 | params.rb:34:16:34:18 | val | SimpleParameter | +| params.rb:34:12:35:3 | do ... end | 1 | params.rb:34:21:34:26 | *splat | SplatParameter | +| params.rb:34:12:35:3 | do ... end | 2 | params.rb:34:29:34:42 | **double_splat | HashSplatParameter | +| params.rb:49:24:51:3 | do ... end | 0 | params.rb:49:28:49:29 | xx | KeywordParameter | +| params.rb:49:24:51:3 | do ... end | 1 | params.rb:49:33:49:34 | yy | KeywordParameter | +| params.rb:65:25:67:3 | do ... end | 0 | params.rb:65:29:65:32 | name | SimpleParameter | +| params.rb:65:25:67:3 | do ... end | 1 | params.rb:65:35:65:37 | age | OptionalParameter | +paramsInLambdas +| params.rb:14:7:14:33 | -> { ... } | 0 | params.rb:14:11:14:13 | foo | SimpleParameter | +| params.rb:14:7:14:33 | -> { ... } | 1 | params.rb:14:16:14:18 | bar | SimpleParameter | +| params.rb:25:19:27:1 | -> { ... } | 0 | params.rb:25:23:25:37 | (..., ...) | TuplePatternParameter | +| params.rb:25:19:27:1 | -> { ... } | 1 | params.rb:25:40:25:54 | (..., ...) | TuplePatternParameter | +| params.rb:38:22:38:47 | -> { ... } | 0 | params.rb:38:26:38:26 | x | SimpleParameter | +| params.rb:38:22:38:47 | -> { ... } | 1 | params.rb:38:29:38:33 | *blah | SplatParameter | +| params.rb:38:22:38:47 | -> { ... } | 2 | params.rb:38:36:38:43 | **wibble | HashSplatParameter | +| params.rb:53:30:55:1 | -> { ... } | 0 | params.rb:53:34:53:34 | x | SimpleParameter | +| params.rb:53:30:55:1 | -> { ... } | 1 | params.rb:53:37:53:37 | y | KeywordParameter | +| params.rb:53:30:55:1 | -> { ... } | 2 | params.rb:53:41:53:41 | z | KeywordParameter | +| params.rb:70:31:70:64 | -> { ... } | 0 | params.rb:70:35:70:35 | a | SimpleParameter | +| params.rb:70:31:70:64 | -> { ... } | 1 | params.rb:70:38:70:38 | b | OptionalParameter | +| params.rb:70:31:70:64 | -> { ... } | 2 | params.rb:70:48:70:48 | c | OptionalParameter | +params +| params.rb:4:30:4:32 | foo | 0 | SimpleParameter | +| params.rb:4:35:4:37 | bar | 1 | SimpleParameter | +| params.rb:4:40:4:42 | baz | 2 | SimpleParameter | +| params.rb:9:15:9:17 | key | 0 | SimpleParameter | +| params.rb:9:20:9:24 | value | 1 | SimpleParameter | +| params.rb:14:11:14:13 | foo | 0 | SimpleParameter | +| params.rb:14:16:14:18 | bar | 1 | SimpleParameter | +| params.rb:17:31:17:39 | (..., ...) | 0 | TuplePatternParameter | +| params.rb:22:15:22:20 | (..., ...) | 0 | TuplePatternParameter | +| params.rb:25:23:25:37 | (..., ...) | 0 | TuplePatternParameter | +| params.rb:25:40:25:54 | (..., ...) | 1 | TuplePatternParameter | +| params.rb:30:23:30:28 | wibble | 0 | SimpleParameter | +| params.rb:30:31:30:36 | *splat | 1 | SplatParameter | +| params.rb:30:39:30:52 | **double_splat | 2 | HashSplatParameter | +| params.rb:34:16:34:18 | val | 0 | SimpleParameter | +| params.rb:34:21:34:26 | *splat | 1 | SplatParameter | +| params.rb:34:29:34:42 | **double_splat | 2 | HashSplatParameter | +| params.rb:38:26:38:26 | x | 0 | SimpleParameter | +| params.rb:38:29:38:33 | *blah | 1 | SplatParameter | +| params.rb:38:36:38:43 | **wibble | 2 | HashSplatParameter | +| params.rb:41:32:41:32 | x | 0 | SimpleParameter | +| params.rb:41:35:41:37 | foo | 1 | KeywordParameter | +| params.rb:41:41:41:43 | bar | 2 | KeywordParameter | +| params.rb:46:28:46:33 | &block | 0 | BlockParameter | +| params.rb:49:28:49:29 | xx | 0 | KeywordParameter | +| params.rb:49:33:49:34 | yy | 1 | KeywordParameter | +| params.rb:53:34:53:34 | x | 0 | SimpleParameter | +| params.rb:53:37:53:37 | y | 1 | KeywordParameter | +| params.rb:53:41:53:41 | z | 2 | KeywordParameter | +| params.rb:58:33:58:36 | val1 | 0 | SimpleParameter | +| params.rb:58:39:58:42 | val2 | 1 | OptionalParameter | +| params.rb:58:49:58:52 | val3 | 2 | OptionalParameter | +| params.rb:62:29:62:34 | &block | 0 | BlockParameter | +| params.rb:65:29:65:32 | name | 0 | SimpleParameter | +| params.rb:65:35:65:37 | age | 1 | OptionalParameter | +| params.rb:70:35:70:35 | a | 0 | SimpleParameter | +| params.rb:70:38:70:38 | b | 1 | OptionalParameter | +| params.rb:70:48:70:48 | c | 2 | OptionalParameter | diff --git a/ruby/ql/test/library-tests/ast/params/params.ql b/ruby/ql/test/library-tests/ast/params/params.ql new file mode 100644 index 00000000000..eb77ac0e418 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/params/params.ql @@ -0,0 +1,48 @@ +import ruby + +//////////////////////////////////////////////////////////////////////////////// +// Query predicates for various types of parameter +query predicate idParams(NamedParameter np, string name) { name = np.getName() } + +query predicate blockParams(BlockParameter bp, string name) { name = bp.getName() } + +query predicate patternParams(TuplePatternParameter tpp, Pattern child, int childIndex) { + tpp.getElement(childIndex) = child +} + +query predicate splatParams(SplatParameter sp, string name) { name = sp.getName() } + +query predicate hashSplatParams(HashSplatParameter hsp, string name) { name = hsp.getName() } + +query predicate keywordParams(KeywordParameter kp, string name, string defaultValueStr) { + name = kp.getName() and + if kp.isOptional() + then defaultValueStr = kp.getDefaultValue().toString() + else defaultValueStr = "(none)" +} + +query predicate optionalParams(OptionalParameter op, string name, AstNode defaultValue) { + name = op.getName() and + defaultValue = op.getDefaultValue() +} + +//////////////////////////////////////////////////////////////////////////////// +// Query predicates for various contexts of parameters +query predicate paramsInMethods(Method m, int i, Parameter p, string pClass) { + p = m.getParameter(i) and pClass = p.getAPrimaryQlClass() +} + +query predicate paramsInBlocks(Block b, int i, Parameter p, string pClass) { + p = b.getParameter(i) and pClass = p.getAPrimaryQlClass() +} + +query predicate paramsInLambdas(Lambda l, int i, Parameter p, string pClass) { + p = l.getParameter(i) and pClass = p.getAPrimaryQlClass() +} + +//////////////////////////////////////////////////////////////////////////////// +// General query selecting all parameters +query predicate params(Parameter p, int i, string pClass) { + i = p.getPosition() and + pClass = p.getAPrimaryQlClass() +} diff --git a/ruby/ql/test/library-tests/ast/params/params.rb b/ruby/ql/test/library-tests/ast/params/params.rb new file mode 100644 index 00000000000..5946b265c84 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/params/params.rb @@ -0,0 +1,70 @@ +# Tests for the different kinds and contexts of parameters. + +# Method containing identifier parameters +def identifier_method_params(foo, bar, baz) +end + +# Block containing identifier parameters +hash = {} +hash.each do |key, value| + puts "#{key} -> #{value}" +end + +# Lambda containing identifier parameters +sum = -> (foo, bar) { foo + bar } + +# Method containing destructured parameters +def destructured_method_param((a, b, c)) +end + +# Block containing destructured parameters +array = [] +array.each { |(a, b)| puts a+b } + +# Lambda containing destructured parameters +sum_four_values = -> ((first, second), (third, fourth)) { + first + second + third + fourth +} + +# Method containing splat and hash-splat params +def method_with_splat(wibble, *splat, **double_splat) +end + +# Block with splat and hash-splat parameter +array.each do |val, *splat, **double_splat| +end + +# Lambda with splat and hash-splat +lambda_with_splats = -> (x, *blah, **wibble) {} + +# Method containing keyword parameters +def method_with_keyword_params(x, foo:, bar: 7) + x + foo + bar +end + +# Block with keyword parameters +def use_block_with_keyword(&block) + puts(block.call bar: 2, foo: 3) +end +use_block_with_keyword do |xx:, yy: 100| + xx + yy +end + +lambda_with_keyword_params = -> (x, y:, z: 3) { + x + y + z +} + +# Method containing optional parameters +def method_with_optional_params(val1, val2 = 0, val3 = 100) +end + +# Block containing optional parameter +def use_block_with_optional(&block) + block.call 'Zeus' +end +use_block_with_optional do |name, age = 99| + puts "#{name} is #{age} years old" +end + +# Lambda containing optional parameters +lambda_with_optional_params = -> (a, b = 1000, c = 20) { a+b+c } \ No newline at end of file diff --git a/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected b/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected new file mode 100644 index 00000000000..cff6c535405 --- /dev/null +++ b/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -0,0 +1,5160 @@ +break_ensure.rb: +# 1| enter m1 +#-----| -> elements + +# 1| enter break_ensure.rb +#-----| -> m1 + +# 1| m1 +#-----| -> m2 + +# 1| exit m1 + +# 1| exit break_ensure.rb + +# 1| exit m1 (normal) +#-----| -> exit m1 + +# 1| exit break_ensure.rb (normal) +#-----| -> exit break_ensure.rb + +# 1| elements +#-----| -> elements + +# 2| for ... in ... +#-----| -> elements + +# 2| element +#-----| -> element + +# 2| In +#-----| empty -> for ... in ... +#-----| non-empty -> element + +# 2| elements +#-----| -> In + +# 2| do ... +#-----| -> In + +# 3| if ... +#-----| -> do ... + +# 3| ... > ... +#-----| raise -> for ... in ... +#-----| true -> break +#-----| false -> if ... + +# 3| element +#-----| -> 0 + +# 3| 0 +#-----| -> ... > ... + +# 4| break +#-----| break -> for ... in ... + +# 7| ensure ... +#-----| -> exit m1 (normal) + +# 8| if ... +#-----| -> ensure ... + +# 8| call to nil? +#-----| false -> if ... +#-----| true -> self + +# 8| elements +#-----| -> call to nil? + +# 8| then ... +#-----| -> if ... + +# 9| call to puts +#-----| -> then ... + +# 9| self +#-----| -> "elements nil" + +# 9| "elements nil" +#-----| -> call to puts + +# 13| enter m2 +#-----| -> elements + +# 13| m2 +#-----| -> m3 + +# 13| exit m2 + +# 13| exit m2 (normal) +#-----| -> exit m2 + +# 13| elements +#-----| -> elements + +# 14| for ... in ... +#-----| -> exit m2 (normal) + +# 14| element +#-----| -> element + +# 14| In +#-----| empty -> for ... in ... +#-----| non-empty -> element + +# 14| elements +#-----| -> In + +# 14| do ... +#-----| -> In + +# 16| if ... +#-----| -> elements + +# 16| ... > ... +#-----| true -> break +#-----| false -> if ... +#-----| raise -> [ensure: raise] elements + +# 16| element +#-----| -> 0 + +# 16| 0 +#-----| -> ... > ... + +# 17| break +#-----| break -> [ensure: break] elements + +# 19| ensure ... +#-----| -> do ... + +# 19| [ensure: break] ensure ... +#-----| break -> for ... in ... + +# 19| [ensure: raise] ensure ... +#-----| raise -> for ... in ... + +# 20| if ... +#-----| -> ensure ... + +# 20| [ensure: break] if ... +#-----| -> [ensure: break] ensure ... + +# 20| [ensure: raise] if ... +#-----| -> [ensure: raise] ensure ... + +# 20| call to nil? +#-----| false -> if ... +#-----| true -> self + +# 20| [ensure: break] call to nil? +#-----| false -> [ensure: break] if ... +#-----| true -> [ensure: break] self + +# 20| [ensure: raise] call to nil? +#-----| false -> [ensure: raise] if ... +#-----| true -> [ensure: raise] self + +# 20| elements +#-----| -> call to nil? + +# 20| [ensure: break] elements +#-----| -> [ensure: break] call to nil? + +# 20| [ensure: raise] elements +#-----| -> [ensure: raise] call to nil? + +# 20| then ... +#-----| -> if ... + +# 20| [ensure: break] then ... +#-----| -> [ensure: break] if ... + +# 20| [ensure: raise] then ... +#-----| -> [ensure: raise] if ... + +# 21| call to puts +#-----| -> then ... + +# 21| [ensure: break] call to puts +#-----| -> [ensure: break] then ... + +# 21| [ensure: raise] call to puts +#-----| -> [ensure: raise] then ... + +# 21| self +#-----| -> "elements nil" + +# 21| [ensure: break] self +#-----| -> [ensure: break] "elements nil" + +# 21| [ensure: raise] self +#-----| -> [ensure: raise] "elements nil" + +# 21| "elements nil" +#-----| -> call to puts + +# 21| [ensure: break] "elements nil" +#-----| -> [ensure: break] call to puts + +# 21| [ensure: raise] "elements nil" +#-----| -> [ensure: raise] call to puts + +# 27| enter m3 +#-----| -> elements + +# 27| m3 +#-----| -> m4 + +# 27| exit m3 + +# 27| exit m3 (abnormal) +#-----| -> exit m3 + +# 27| exit m3 (normal) +#-----| -> exit m3 + +# 27| elements +#-----| -> elements + +# 29| if ... +#-----| -> elements + +# 29| call to nil? +#-----| false -> if ... +#-----| true -> return +#-----| raise -> [ensure: raise] elements + +# 29| elements +#-----| -> call to nil? + +# 30| return +#-----| return -> [ensure: return] elements + +# 32| ensure ... +#-----| -> self + +# 32| [ensure: raise] ensure ... +#-----| raise -> exit m3 (abnormal) + +# 32| [ensure: return] ensure ... +#-----| return -> exit m3 (normal) + +# 33| for ... in ... +#-----| -> ensure ... + +# 33| [ensure: raise] for ... in ... +#-----| -> [ensure: raise] ensure ... + +# 33| [ensure: return] for ... in ... +#-----| -> [ensure: return] ensure ... + +# 33| element +#-----| -> self + +# 33| [ensure: raise] element +#-----| -> [ensure: raise] self + +# 33| [ensure: return] element +#-----| -> [ensure: return] self + +# 33| In +#-----| empty -> for ... in ... +#-----| non-empty -> element + +# 33| [ensure: raise] In +#-----| empty -> [ensure: raise] for ... in ... +#-----| non-empty -> [ensure: raise] element + +# 33| [ensure: return] In +#-----| empty -> [ensure: return] for ... in ... +#-----| non-empty -> [ensure: return] element + +# 33| elements +#-----| -> In + +# 33| [ensure: raise] elements +#-----| -> [ensure: raise] In + +# 33| [ensure: return] elements +#-----| -> [ensure: return] In + +# 33| do ... +#-----| -> In + +# 33| [ensure: raise] do ... +#-----| -> [ensure: raise] In + +# 33| [ensure: return] do ... +#-----| -> [ensure: return] In + +# 35| if ... +#-----| -> do ... + +# 35| [ensure: raise] if ... +#-----| -> [ensure: raise] do ... + +# 35| [ensure: return] if ... +#-----| -> [ensure: return] do ... + +# 35| ... > ... +#-----| true -> break +#-----| false -> if ... + +# 35| [ensure: raise] ... > ... +#-----| true -> [ensure: raise] break +#-----| false -> [ensure: raise] if ... + +# 35| [ensure: return] ... > ... +#-----| true -> [ensure: return] break +#-----| false -> [ensure: return] if ... + +# 35| call to x +#-----| -> 0 + +# 35| [ensure: raise] call to x +#-----| -> [ensure: raise] 0 + +# 35| [ensure: return] call to x +#-----| -> [ensure: return] 0 + +# 35| self +#-----| -> call to x + +# 35| [ensure: raise] self +#-----| -> [ensure: raise] call to x + +# 35| [ensure: return] self +#-----| -> [ensure: return] call to x + +# 35| 0 +#-----| -> ... > ... + +# 35| [ensure: raise] 0 +#-----| -> [ensure: raise] ... > ... + +# 35| [ensure: return] 0 +#-----| -> [ensure: return] ... > ... + +# 36| break +#-----| break -> for ... in ... + +# 36| [ensure: raise] break +#-----| break -> [ensure: raise] for ... in ... + +# 36| [ensure: return] break +#-----| break -> [ensure: return] for ... in ... + +# 41| call to puts +#-----| -> exit m3 (normal) + +# 41| self +#-----| -> "Done" + +# 41| "Done" +#-----| -> call to puts + +# 44| enter m4 +#-----| -> elements + +# 44| m4 +#-----| -> exit break_ensure.rb (normal) + +# 44| exit m4 + +# 44| exit m4 (normal) +#-----| -> exit m4 + +# 44| elements +#-----| -> elements + +# 45| for ... in ... +#-----| -> exit m4 (normal) + +# 45| element +#-----| -> element + +# 45| In +#-----| empty -> for ... in ... +#-----| non-empty -> element + +# 45| elements +#-----| -> In + +# 45| do ... +#-----| -> In + +# 47| if ... +#-----| -> element + +# 47| ... > ... +#-----| false -> if ... +#-----| raise -> [ensure: raise] element +#-----| true -> self + +# 47| element +#-----| -> 1 + +# 47| 1 +#-----| -> ... > ... + +# 48| call to raise +#-----| raise -> [ensure: raise] element + +# 48| self +#-----| -> "" + +# 48| "" +#-----| -> call to raise + +# 50| ensure ... +#-----| -> do ... + +# 50| [ensure: raise] ensure ... +#-----| raise -> for ... in ... + +# 51| if ... +#-----| -> ensure ... + +# 51| [ensure: raise] if ... +#-----| -> [ensure: raise] ensure ... + +# 51| ... > ... +#-----| false -> if ... +#-----| true -> 10 + +# 51| [ensure: raise] ... > ... +#-----| false -> [ensure: raise] if ... +#-----| true -> [ensure: raise] 10 + +# 51| element +#-----| -> 0 + +# 51| [ensure: raise] element +#-----| -> [ensure: raise] 0 + +# 51| 0 +#-----| -> ... > ... + +# 51| [ensure: raise] 0 +#-----| -> [ensure: raise] ... > ... + +# 52| break +#-----| break -> for ... in ... + +# 52| [ensure: raise] break +#-----| break -> for ... in ... + +# 52| 10 +#-----| -> break + +# 52| [ensure: raise] 10 +#-----| -> [ensure: raise] break + +case.rb: +# 1| enter if_in_case +#-----| -> case ... + +# 1| enter case.rb +#-----| -> if_in_case + +# 1| if_in_case +#-----| -> exit case.rb (normal) + +# 1| exit if_in_case + +# 1| exit case.rb + +# 1| exit if_in_case (normal) +#-----| -> exit if_in_case + +# 1| exit case.rb (normal) +#-----| -> exit case.rb + +# 2| case ... +#-----| -> self + +# 2| call to x1 +#-----| -> when ... + +# 2| self +#-----| -> call to x1 + +# 3| when ... +#-----| -> 1 + +# 3| 1 +#-----| no-match -> when ... +#-----| match -> self + +# 3| then ... +#-----| -> exit if_in_case (normal) + +# 3| ( ... ) +#-----| -> then ... + +# 3| if ... +#-----| -> ( ... ) + +# 3| call to x2 +#-----| false -> if ... +#-----| true -> self + +# 3| self +#-----| -> call to x2 + +# 3| then ... +#-----| -> if ... + +# 3| call to puts +#-----| -> then ... + +# 3| self +#-----| -> "x2" + +# 3| "x2" +#-----| -> call to puts + +# 4| when ... +#-----| -> 2 + +# 4| 2 +#-----| match -> self +#-----| no-match -> exit if_in_case (normal) + +# 4| then ... +#-----| -> exit if_in_case (normal) + +# 4| call to puts +#-----| -> then ... + +# 4| self +#-----| -> "2" + +# 4| "2" +#-----| -> call to puts + +cfg.html.erb: +# 5| enter cfg.html.erb +#-----| -> @title + +# 5| @title +#-----| -> self + +# 5| exit cfg.html.erb + +# 5| exit cfg.html.erb (normal) +#-----| -> exit cfg.html.erb + +# 6| call to stylesheet_link_tag +#-----| -> self + +# 6| self +#-----| -> "application" + +# 6| "application" +#-----| -> :media + +# 6| Pair +#-----| -> call to stylesheet_link_tag + +# 6| :media +#-----| -> "all" + +# 6| "all" +#-----| -> Pair + +# 12| call to link_to +#-----| -> self + +# 12| self +#-----| -> "A" + +# 12| "A" +#-----| -> self + +# 12| call to a +#-----| -> :id + +# 12| self +#-----| -> call to a + +# 12| Pair +#-----| -> call to link_to + +# 12| :id +#-----| -> "a" + +# 12| "a" +#-----| -> Pair + +# 15| call to link_to +#-----| -> self + +# 15| self +#-----| -> "B" + +# 15| "B" +#-----| -> self + +# 15| call to a +#-----| -> call to link_to + +# 15| self +#-----| -> call to a + +# 16| call to link_to +#-----| -> self + +# 16| self +#-----| -> "C" + +# 16| "C" +#-----| -> self + +# 16| call to b +#-----| -> call to link_to + +# 16| self +#-----| -> call to b + +# 18| if ... +#-----| -> self + +# 18| call to admin? +#-----| true -> self +#-----| false -> self + +# 18| self +#-----| -> call to admin? + +# 18| then ... +#-----| -> if ... + +# 19| call to link_to +#-----| -> then ... + +# 19| self +#-----| -> "D" + +# 19| "D" +#-----| -> self + +# 19| call to d +#-----| -> call to link_to + +# 19| self +#-----| -> call to d + +# 20| else ... +#-----| -> if ... + +# 21| call to link_to +#-----| -> else ... + +# 21| self +#-----| -> "E" + +# 21| "E" +#-----| -> self + +# 21| call to e +#-----| -> call to link_to + +# 21| self +#-----| -> call to e + +# 29| call to each +#-----| -> exit cfg.html.erb (normal) + +# 29| call to collection +#-----| -> do ... end + +# 29| self +#-----| -> call to collection + +# 29| enter do ... end +#-----| -> key + +# 29| do ... end +#-----| -> call to each + +# 29| exit do ... end + +# 29| exit do ... end (normal) +#-----| -> exit do ... end + +# 29| key +#-----| -> value + +# 29| value +#-----| -> key + +# 30| key +#-----| -> value + +# 30| value +#-----| -> exit do ... end (normal) + +cfg.rb: +# 1| enter cfg.rb +#-----| -> self + +# 1| bar +#-----| -> alias ... + +# 1| exit cfg.rb + +# 1| exit cfg.rb (normal) +#-----| -> exit cfg.rb + +# 3| alias ... +#-----| -> foo + +# 3| foo +#-----| -> bar + +# 3| bar +#-----| -> b + +# 5| ... = ... +#-----| -> Array + +# 5| b +#-----| -> 42 + +# 5| 42 +#-----| -> ... = ... + +# 7| call to [] +#-----| -> Array + +# 7| Array +#-----| -> b + +# 7| :"one#{...}" +#-----| -> :"another" + +# 7| #{...} +#-----| -> :"one#{...}" + +# 7| b +#-----| -> #{...} + +# 7| :"another" +#-----| -> call to [] + +# 9| call to [] +#-----| -> self + +# 9| Array +#-----| -> b + +# 9| "one#{...}" +#-----| -> "another" + +# 9| #{...} +#-----| -> "one#{...}" + +# 9| b +#-----| -> #{...} + +# 9| "another" +#-----| -> call to [] + +# 12| call to puts +#-----| -> END { ... } + +# 12| self +#-----| -> 4 + +# 12| 4 +#-----| -> call to puts + +# 15| BEGIN { ... } +#-----| -> bar + +# 16| call to puts +#-----| -> BEGIN { ... } + +# 16| self +#-----| -> "hello" + +# 16| "hello" +#-----| -> call to puts + +# 19| enter END { ... } +#-----| -> self + +# 19| END { ... } +#-----| -> 41 + +# 19| exit END { ... } + +# 19| exit END { ... } (normal) +#-----| -> exit END { ... } + +# 20| call to puts +#-----| -> exit END { ... } (normal) + +# 20| self +#-----| -> "world" + +# 20| "world" +#-----| -> call to puts + +# 23| ... + ... +#-----| -> 2 + +# 23| 41 +#-----| -> 1 + +# 23| 1 +#-----| -> ... + ... + +# 25| 2 +#-----| -> { ... } + +# 25| call to times +#-----| -> self + +# 25| enter { ... } +#-----| -> x + +# 25| { ... } +#-----| -> call to times + +# 25| exit { ... } + +# 25| exit { ... } (normal) +#-----| -> exit { ... } + +# 25| x +#-----| -> self + +# 25| call to puts +#-----| -> exit { ... } (normal) + +# 25| self +#-----| -> x + +# 25| x +#-----| -> call to puts + +# 27| call to puts +#-----| -> Proc + +# 27| self +#-----| -> :puts + +# 27| &... +#-----| -> call to puts + +# 27| :puts +#-----| -> &... + +# 29| call to new +#-----| -> true + +# 29| Proc +#-----| -> { ... } + +# 29| enter { ... } +#-----| -> x + +# 29| { ... } +#-----| -> call to new + +# 29| exit { ... } + +# 29| exit { ... } (normal) +#-----| -> exit { ... } + +# 29| x +#-----| -> x + +# 29| call to call +#-----| -> exit { ... } (normal) + +# 29| x +#-----| -> call to call + +# 31| while ... +#-----| -> false + +# 31| true +#-----| true -> 1 + +# 32| break +#-----| break -> while ... + +# 32| 1 +#-----| -> break + +# 35| if ... +#-----| -> self + +# 35| false +#-----| false -> if ... + +# 39| call to puts +#-----| -> case ... + +# 39| self +#-----| -> 42 + +# 39| 42 +#-----| -> call to puts + +# 41| case ... +#-----| -> 10 + +# 41| 10 +#-----| -> when ... + +# 42| when ... +#-----| -> 1 + +# 42| 1 +#-----| no-match -> when ... +#-----| match -> self + +# 42| then ... +#-----| -> case ... + +# 42| call to puts +#-----| -> then ... + +# 42| self +#-----| -> "one" + +# 42| "one" +#-----| -> call to puts + +# 43| when ... +#-----| -> 2 + +# 43| 2 +#-----| no-match -> 3 +#-----| match -> self + +# 43| 3 +#-----| no-match -> 4 +#-----| match -> self + +# 43| 4 +#-----| match -> self +#-----| no-match -> self + +# 43| then ... +#-----| -> case ... + +# 43| call to puts +#-----| -> then ... + +# 43| self +#-----| -> "some" + +# 43| "some" +#-----| -> call to puts + +# 44| else ... +#-----| -> case ... + +# 44| call to puts +#-----| -> else ... + +# 44| self +#-----| -> "many" + +# 44| "many" +#-----| -> call to puts + +# 47| case ... +#-----| -> when ... + +# 48| when ... +#-----| -> b + +# 48| ... == ... +#-----| false -> when ... +#-----| true -> self + +# 48| b +#-----| -> 1 + +# 48| 1 +#-----| -> ... == ... + +# 48| then ... +#-----| -> chained + +# 48| call to puts +#-----| -> then ... + +# 48| self +#-----| -> "one" + +# 48| "one" +#-----| -> call to puts + +# 49| when ... +#-----| -> b + +# 49| ... == ... +#-----| false -> b +#-----| true -> self + +# 49| b +#-----| -> 0 + +# 49| 0 +#-----| -> ... == ... + +# 49| ... > ... +#-----| false -> chained +#-----| true -> self + +# 49| b +#-----| -> 1 + +# 49| 1 +#-----| -> ... > ... + +# 49| then ... +#-----| -> chained + +# 49| call to puts +#-----| -> then ... + +# 49| self +#-----| -> "some" + +# 49| "some" +#-----| -> call to puts + +# 52| ... = ... +#-----| -> character + +# 52| chained +#-----| -> "a" + +# 52| "a" +#-----| -> chained + +# 52| "#{...}" +#-----| -> "string" + +# 52| #{...} +#-----| -> "#{...}" + +# 52| chained +#-----| -> #{...} + +# 52| "string" +#-----| -> ... = ... + +# 54| ... = ... +#-----| -> Silly + +# 54| character +#-----| -> ?\x40 + +# 54| ?\x40 +#-----| -> ... = ... + +# 58| Silly +#-----| -> Object + +# 58| Object +#-----| -> complex + +# 59| ... = ... +#-----| -> conditional + +# 59| complex +#-----| -> 10-2i + +# 59| 10-2i +#-----| -> ... = ... + +# 60| ... = ... +#-----| -> C + +# 60| conditional +#-----| -> self + +# 60| ... < ... +#-----| true -> "hello" +#-----| false -> "bye" + +# 60| ... ? ... : ... +#-----| -> ... = ... + +# 60| call to b +#-----| -> 10 + +# 60| self +#-----| -> call to b + +# 60| 10 +#-----| -> ... < ... + +# 60| "hello" +#-----| -> ... ? ... : ... + +# 60| "bye" +#-----| -> ... ? ... : ... + +# 61| ... = ... +#-----| -> __synth__0 + +# 61| C +#-----| -> "constant" + +# 61| "constant" +#-----| -> ... = ... + +# 62| ... +#-----| -> pattern + +# 62| x +#-----| -> __synth__0 + +# 62| ... = ... +#-----| -> __synth__0__1 + +# 62| call to [] +#-----| -> ... = ... + +# 62| 0 +#-----| -> call to [] + +# 62| __synth__0 +#-----| -> 0 + +# 62| call to [] +#-----| -> * ... + +# 62| ... +#-----| -> ... + +# 62| 1 +#-----| -> call to [] + +# 62| __synth__0 +#-----| -> 1 + +# 62| ... = ... +#-----| -> y + +# 62| * ... +#-----| -> ... = ... + +# 62| __synth__0__1 +#-----| -> __synth__0 + +# 62| y +#-----| -> __synth__0__1 + +# 62| ... = ... +#-----| -> z + +# 62| call to [] +#-----| -> ... = ... + +# 62| 0 +#-----| -> call to [] + +# 62| __synth__0__1 +#-----| -> 0 + +# 62| z +#-----| -> __synth__0__1 + +# 62| ... = ... +#-----| -> ... + +# 62| call to [] +#-----| -> ... = ... + +# 62| 1 +#-----| -> call to [] + +# 62| __synth__0__1 +#-----| -> 1 + +# 62| call to [] +#-----| -> * ... + +# 62| ... = ... +#-----| -> x + +# 62| Array +#-----| -> 1 + +# 62| * ... +#-----| -> ... = ... + +# 62| __synth__0 +#-----| -> Array + +# 62| 1 +#-----| -> Array + +# 62| call to [] +#-----| -> call to [] + +# 62| Array +#-----| -> 2 + +# 62| 2 +#-----| -> 3 + +# 62| 3 +#-----| -> call to [] + +# 63| enter pattern +#-----| -> a + +# 63| pattern +#-----| -> items + +# 63| exit pattern + +# 63| exit pattern (normal) +#-----| -> exit pattern + +# 63| (..., ...) +#-----| -> self + +# 63| a +#-----| -> b + +# 63| b +#-----| -> (..., ...) + +# 64| call to puts +#-----| -> self + +# 64| self +#-----| -> a + +# 64| a +#-----| -> call to puts + +# 65| call to puts +#-----| -> exit pattern (normal) + +# 65| self +#-----| -> b + +# 65| b +#-----| -> call to puts + +# 67| ... = ... +#-----| -> self + +# 67| items +#-----| -> Array + +# 67| call to [] +#-----| -> ... = ... + +# 67| Array +#-----| -> 1 + +# 67| 1 +#-----| -> 2 + +# 67| 2 +#-----| -> 3 + +# 67| 3 +#-----| -> call to [] + +# 68| call to puts +#-----| -> print + +# 68| self +#-----| -> items + +# 68| ...[...] +#-----| -> call to puts + +# 68| items +#-----| -> 2 + +# 68| 2 +#-----| -> ...[...] + +# 69| enter print +#-----| -> self + +# 69| print +#-----| -> x + +# 69| exit print + +# 69| exit print (normal) +#-----| -> exit print + +# 70| call to puts +#-----| -> exit print (normal) + +# 70| self +#-----| -> "silly" + +# 70| "silly" +#-----| -> call to puts + +# 74| ... = ... +#-----| -> x + +# 74| x +#-----| -> 42 + +# 74| 42 +#-----| -> ... = ... + +# 75| if ... +#-----| -> ; + +# 75| ... < ... +#-----| true -> 0 +#-----| false -> x + +# 75| x +#-----| -> 0 + +# 75| 0 +#-----| -> ... < ... + +# 75| then ... +#-----| -> if ... + +# 75| 0 +#-----| -> then ... + +# 75| elsif ... +#-----| -> if ... + +# 75| ... > ... +#-----| true -> 10 +#-----| false -> x + +# 75| x +#-----| -> 10 + +# 75| 10 +#-----| -> ... > ... + +# 75| then ... +#-----| -> elsif ... + +# 75| 10 +#-----| -> then ... + +# 75| else ... +#-----| -> elsif ... + +# 75| x +#-----| -> else ... + +# 78| ; +#-----| -> self + +# 82| else ... +#-----| -> self + +# 83| call to puts +#-----| -> else ... + +# 83| self +#-----| -> "ok" + +# 83| "ok" +#-----| -> call to puts + +# 84| ensure ... +#-----| -> escape + +# 85| call to puts +#-----| -> ensure ... + +# 85| self +#-----| -> "end" + +# 85| "end" +#-----| -> call to puts + +# 88| ... = ... +#-----| -> Array + +# 88| escape +#-----| -> x + +# 88| "\u1234#{...}\n" +#-----| -> ... = ... + +# 88| #{...} +#-----| -> "\u1234#{...}\n" + +# 88| x +#-----| -> #{...} + +# 90| for ... in ... +#-----| -> $global + +# 90| x +#-----| -> x + +# 90| In +#-----| empty -> for ... in ... +#-----| non-empty -> x + +# 90| call to [] +#-----| -> In + +# 90| Array +#-----| -> 1.4 + +# 90| 1.4 +#-----| -> 2.5 + +# 90| 2.5 +#-----| -> 3.4e5 + +# 90| 3.4e5 +#-----| -> call to [] + +# 90| do ... +#-----| -> In + +# 91| if ... +#-----| -> self + +# 91| ... > ... +#-----| false -> if ... +#-----| true -> next + +# 91| x +#-----| -> 3 + +# 91| 3 +#-----| -> ... > ... + +# 91| next +#-----| next -> In + +# 92| call to puts +#-----| -> do ... + +# 92| self +#-----| -> x + +# 92| x +#-----| -> call to puts + +# 95| ... = ... +#-----| -> map1 + +# 95| $global +#-----| -> 42 + +# 95| 42 +#-----| -> ... = ... + +# 97| ... = ... +#-----| -> map2 + +# 97| map1 +#-----| -> "a" + +# 97| {...} +#-----| -> ... = ... + +# 97| Pair +#-----| -> "c" + +# 97| "a" +#-----| -> "b" + +# 97| "b" +#-----| -> Pair + +# 97| Pair +#-----| -> :e + +# 97| "c" +#-----| -> "d" + +# 97| "d" +#-----| -> Pair + +# 97| Pair +#-----| -> {...} + +# 97| :e +#-----| -> "f" + +# 97| "f" +#-----| -> Pair + +# 98| ... = ... +#-----| -> parameters + +# 98| map2 +#-----| -> map1 + +# 98| {...} +#-----| -> ... = ... + +# 98| ** ... +#-----| -> "x" + +# 98| map1 +#-----| -> ** ... + +# 98| Pair +#-----| -> map1 + +# 98| "x" +#-----| -> "y" + +# 98| "y" +#-----| -> Pair + +# 98| ** ... +#-----| -> {...} + +# 98| map1 +#-----| -> ** ... + +# 101| enter parameters +#-----| -> value + +# 101| parameters +#-----| -> type + +# 101| exit parameters + +# 101| exit parameters (normal) +#-----| -> exit parameters + +# 101| value +#-----| no-match -> 42 +#-----| match -> key + +# 101| 42 +#-----| -> key + +# 101| key +#-----| -> kwargs + +# 101| kwargs +#-----| -> self + +# 102| call to puts +#-----| -> kwargs + +# 102| self +#-----| -> value + +# 102| value +#-----| -> call to puts + +# 103| return +#-----| return -> exit parameters (normal) + +# 103| ...[...] +#-----| -> return + +# 103| kwargs +#-----| -> key + +# 103| key +#-----| -> ...[...] + +# 106| ... = ... +#-----| -> table + +# 106| type +#-----| -> "healthy" + +# 106| "healthy" +#-----| -> ... = ... + +# 107| ... = ... +#-----| -> self + +# 107| table +#-----| -> "food" + +# 107| "food" +#-----| -> ... = ... + +# 108| call to puts +#-----| -> b + +# 108| self +#-----| -> <<SQL + +# 108| ( ... ) +#-----| -> call to puts + +# 108| <<SQL +#-----| -> table + +# 109| #{...} +#-----| -> type + +# 109| table +#-----| -> #{...} + +# 110| #{...} +#-----| -> ( ... ) + +# 110| type +#-----| -> #{...} + +# 113| ... if ... +#-----| -> C + +# 113| call to puts +#-----| -> ... if ... + +# 113| self +#-----| -> "hi" + +# 113| "hi" +#-----| -> call to puts + +# 113| ... > ... +#-----| false -> ... if ... +#-----| true -> self + +# 113| b +#-----| -> 10 + +# 113| 10 +#-----| -> ... > ... + +# 115| C +#-----| -> @field + +# 116| ... = ... +#-----| -> @@static_field + +# 116| @field +#-----| -> 42 + +# 116| 42 +#-----| -> ... = ... + +# 117| ... = ... +#-----| -> swap + +# 117| @@static_field +#-----| -> 10 + +# 117| 10 +#-----| -> ... = ... + +# 120| ... = ... +#-----| -> M + +# 120| swap +#-----| -> -> { ... } + +# 120| enter -> { ... } +#-----| -> x + +# 120| -> { ... } +#-----| -> ... = ... + +# 120| exit -> { ... } + +# 120| exit -> { ... } (normal) +#-----| -> exit -> { ... } + +# 120| (..., ...) +#-----| -> Array + +# 120| x +#-----| -> y + +# 120| y +#-----| -> (..., ...) + +# 120| call to [] +#-----| -> exit -> { ... } (normal) + +# 120| Array +#-----| -> y + +# 120| y +#-----| -> x + +# 120| x +#-----| -> call to [] + +# 122| M +#-----| -> nothing + +# 123| ... = ... +#-----| -> some + +# 123| nothing +#-----| -> nil + +# 123| nil +#-----| -> ... = ... + +# 124| ... = ... +#-----| -> some + +# 124| some +#-----| -> 2 + +# 124| 2 +#-----| -> ... = ... + +# 125| some +#-----| -> some + +# 125| ... = ... +#-----| -> last + +# 125| some +#-----| -> 10 + +# 125| ... + ... +#-----| -> ... = ... + +# 125| 10 +#-----| -> ... + ... + +# 126| ... = ... +#-----| -> range + +# 126| last +#-----| -> 2 + +# 126| ( ... ) +#-----| -> ... = ... + +# 126| 2 +#-----| -> 4 + +# 126| 4 +#-----| -> 7 + +# 126| 7 +#-----| -> ( ... ) + +# 127| ... = ... +#-----| -> half + +# 127| range +#-----| -> 0 + +# 127| 0 +#-----| -> 9 + +# 127| _ .. _ +#-----| -> ... = ... + +# 127| 9 +#-----| -> _ .. _ + +# 128| ... = ... +#-----| -> regex + +# 128| half +#-----| -> 1 + +# 128| ... + ... +#-----| -> ... = ... + +# 128| ... / ... +#-----| -> 1 + +# 128| 1 +#-----| -> 3r + +# 128| 3r +#-----| -> ... / ... + +# 128| ... / ... +#-----| -> ... + ... + +# 128| 1 +#-----| -> 6r + +# 128| 6r +#-----| -> ... / ... + +# 129| ... = ... +#-----| -> Constant + +# 129| regex +#-----| -> range + +# 129| /hello\s+[#{...}]/ +#-----| -> ... = ... + +# 129| #{...} +#-----| -> /hello\s+[#{...}]/ + +# 129| range +#-----| -> #{...} + +# 130| ... = ... +#-----| -> EmptyClass + +# 130| Constant +#-----| -> 5 + +# 130| 5 +#-----| -> ... = ... + +# 133| EmptyClass +#-----| -> EmptyModule + +# 134| EmptyModule +#-----| -> ... rescue ... + +# 136| ... rescue ... +#-----| -> 1 + +# 136| ... / ... +#-----| raise -> self +#-----| -> __synth__0 + +# 136| 1 +#-----| -> 0 + +# 136| 0 +#-----| -> ... / ... + +# 136| call to puts +#-----| -> __synth__0 + +# 136| self +#-----| -> "div by zero" + +# 136| "div by zero" +#-----| -> call to puts + +# 138| ... +#-----| -> M + +# 138| init +#-----| -> __synth__0 + +# 138| ... = ... +#-----| -> last + +# 138| call to [] +#-----| -> ... = ... + +# 138| _ .. _ +#-----| -> call to [] + +# 138| __synth__0 +#-----| -> 0 + +# 138| 0 +#-----| -> -2 + +# 138| -2 +#-----| -> _ .. _ + +# 138| last +#-----| -> __synth__0 + +# 138| ... = ... +#-----| -> ... + +# 138| call to [] +#-----| -> ... = ... + +# 138| -1 +#-----| -> call to [] + +# 138| __synth__0 +#-----| -> -1 + +# 138| 1 +#-----| -> 2 + +# 138| ... = ... +#-----| -> init + +# 138| * ... +#-----| -> ... = ... + +# 138| __synth__0 +#-----| -> 1 + +# 138| 2 +#-----| -> 3 + +# 138| 3 +#-----| -> * ... + +# 140| M +#-----| -> Constant + +# 140| Constant +#-----| -> M + +# 141| call to itself +#-----| -> Constant + +# 141| M +#-----| -> call to itself + +# 141| Constant +#-----| -> Silly + +# 143| class << ... +#-----| -> silly + +# 143| call to itself +#-----| -> setter= + +# 143| Silly +#-----| -> call to itself + +# 144| setter= +#-----| -> print + +# 145| enter print +#-----| -> self + +# 145| print +#-----| -> class << ... + +# 145| exit print + +# 145| exit print (normal) +#-----| -> exit print + +# 146| call to puts +#-----| -> self + +# 146| self +#-----| -> "singleton" + +# 146| "singleton" +#-----| -> call to puts + +# 147| call to puts +#-----| -> exit print (normal) + +# 147| self +#-----| -> call to super + +# 147| call to print +#-----| -> call to puts + +# 147| call to super +#-----| -> call to print + +# 151| ... = ... +#-----| -> silly + +# 151| silly +#-----| -> Silly + +# 151| call to new +#-----| -> ... = ... + +# 151| Silly +#-----| -> call to new + +# 152| enter method +#-----| -> x + +# 152| method +#-----| -> two_parameters + +# 152| exit method + +# 152| exit method (normal) +#-----| -> exit method + +# 152| silly +#-----| -> method + +# 152| x +#-----| -> self + +# 153| call to puts +#-----| -> exit method (normal) + +# 153| self +#-----| -> x + +# 153| x +#-----| -> call to puts + +# 156| enter two_parameters +#-----| -> a + +# 156| two_parameters +#-----| -> self + +# 156| exit two_parameters + +# 156| exit two_parameters (normal) +#-----| -> exit two_parameters + +# 156| a +#-----| -> b + +# 156| b +#-----| -> exit two_parameters (normal) + +# 158| call to two_parameters +#-----| -> scriptfile + +# 158| self +#-----| -> Array + +# 158| * ... +#-----| -> call to two_parameters + +# 158| call to [] +#-----| -> * ... + +# 158| Array +#-----| -> 1 + +# 158| 1 +#-----| -> 2 + +# 158| 2 +#-----| -> call to [] + +# 160| ... = ... +#-----| -> symbol + +# 160| scriptfile +#-----| -> self + +# 160| `cat "#{...}"` +#-----| -> ... = ... + +# 160| #{...} +#-----| -> `cat "#{...}"` + +# 160| call to __FILE__ +#-----| -> #{...} + +# 160| self +#-----| -> call to __FILE__ + +# 162| ... = ... +#-----| -> delimited_symbol + +# 162| symbol +#-----| -> :hello + +# 162| :hello +#-----| -> ... = ... + +# 164| ... = ... +#-----| -> x + +# 164| delimited_symbol +#-----| -> 12 + +# 164| :"goodbye-#{...}" +#-----| -> ... = ... + +# 164| #{...} +#-----| -> :"goodbye-#{...}" + +# 164| ... + ... +#-----| -> #{...} + +# 164| 12 +#-----| -> 13 + +# 164| 13 +#-----| -> ... + ... + +# 166| ... = ... +#-----| -> x + +# 166| x +#-----| -> true + +# 166| true +#-----| -> ... = ... + +# 167| ... = ... +#-----| -> x + +# 167| x +#-----| -> true + +# 167| ! ... +#-----| -> ... = ... + +# 167| true +#-----| -> ! ... + +# 168| ... = ... +#-----| -> undef ... + +# 168| x +#-----| -> 42 + +# 168| - ... +#-----| -> ... = ... + +# 168| 42 +#-----| -> - ... + +# 170| undef ... +#-----| -> two_parameters + +# 170| two_parameters +#-----| -> x + +# 172| unless ... +#-----| -> x + +# 172| ... == ... +#-----| false -> self +#-----| true -> self + +# 172| x +#-----| -> 10 + +# 172| 10 +#-----| -> ... == ... + +# 172| then ... +#-----| -> unless ... + +# 172| call to puts +#-----| -> then ... + +# 172| self +#-----| -> "hi" + +# 172| "hi" +#-----| -> call to puts + +# 172| else ... +#-----| -> unless ... + +# 172| call to puts +#-----| -> else ... + +# 172| self +#-----| -> "bye" + +# 172| "bye" +#-----| -> call to puts + +# 174| call to puts +#-----| -> ... unless ... + +# 174| ... unless ... +#-----| -> x + +# 174| self +#-----| -> "hi" + +# 174| "hi" +#-----| -> call to puts + +# 174| ... == ... +#-----| true -> ... unless ... +#-----| false -> self + +# 174| x +#-----| -> 0 + +# 174| 0 +#-----| -> ... == ... + +# 176| until ... +#-----| -> i + +# 176| ... > ... +#-----| true -> until ... +#-----| false -> x + +# 176| x +#-----| -> 10 + +# 176| 10 +#-----| -> ... > ... + +# 176| do ... +#-----| -> x + +# 176| x +#-----| -> x + +# 176| ... = ... +#-----| -> self + +# 176| x +#-----| -> 10 + +# 176| ... + ... +#-----| -> ... = ... + +# 176| 10 +#-----| -> ... + ... + +# 176| call to puts +#-----| -> do ... + +# 176| self +#-----| -> "hello" + +# 176| "hello" +#-----| -> call to puts + +# 178| ... = ... +#-----| -> i + +# 178| i +#-----| -> 0 + +# 178| 0 +#-----| -> ... = ... + +# 179| ( ... ) +#-----| -> i + +# 179| ... until ... +#-----| -> x + +# 179| call to puts +#-----| -> i + +# 179| self +#-----| -> "hello" + +# 179| "hello" +#-----| -> call to puts + +# 179| i +#-----| -> i + +# 179| ... = ... +#-----| -> ( ... ) + +# 179| i +#-----| -> 1 + +# 179| ... + ... +#-----| -> ... = ... + +# 179| 1 +#-----| -> ... + ... + +# 179| ... == ... +#-----| true -> ... until ... +#-----| false -> self + +# 179| i +#-----| -> 10 + +# 179| 10 +#-----| -> ... == ... + +# 181| ... = ... +#-----| -> x + +# 181| x +#-----| -> 0 + +# 181| 0 +#-----| -> ... = ... + +# 182| while ... +#-----| -> i + +# 182| ... < ... +#-----| false -> while ... +#-----| true -> x + +# 182| x +#-----| -> 10 + +# 182| 10 +#-----| -> ... < ... + +# 182| do ... +#-----| -> x + +# 183| x +#-----| -> x + +# 183| ... = ... +#-----| -> x + +# 183| x +#-----| -> 1 + +# 183| ... + ... +#-----| -> ... = ... + +# 183| 1 +#-----| -> ... + ... + +# 184| if ... +#-----| -> self + +# 184| ... == ... +#-----| false -> if ... +#-----| true -> redo + +# 184| x +#-----| -> 5 + +# 184| 5 +#-----| -> ... == ... + +# 184| redo +#-----| redo -> x + +# 185| call to puts +#-----| -> do ... + +# 185| self +#-----| -> x + +# 185| x +#-----| -> call to puts + +# 188| ( ... ) +#-----| -> i + +# 188| ... while ... +#-----| -> run_block + +# 188| call to puts +#-----| -> i + +# 188| self +#-----| -> "hello" + +# 188| "hello" +#-----| -> call to puts + +# 188| i +#-----| -> i + +# 188| ... = ... +#-----| -> ( ... ) + +# 188| i +#-----| -> 1 + +# 188| ... - ... +#-----| -> ... = ... + +# 188| 1 +#-----| -> ... - ... + +# 188| ... != ... +#-----| false -> ... while ... +#-----| true -> self + +# 188| i +#-----| -> 0 + +# 188| 0 +#-----| -> ... != ... + +# 190| enter run_block +#-----| -> 42 + +# 190| run_block +#-----| -> self + +# 190| exit run_block + +# 190| exit run_block (normal) +#-----| -> exit run_block + +# 191| yield ... +#-----| -> exit run_block (normal) + +# 191| 42 +#-----| -> yield ... + +# 194| call to run_block +#-----| -> exit cfg.rb (normal) + +# 194| self +#-----| -> { ... } + +# 194| enter { ... } +#-----| -> x + +# 194| { ... } +#-----| -> call to run_block + +# 194| exit { ... } + +# 194| exit { ... } (normal) +#-----| -> exit { ... } + +# 194| x +#-----| -> self + +# 194| call to puts +#-----| -> exit { ... } (normal) + +# 194| self +#-----| -> x + +# 194| x +#-----| -> call to puts + +desugar.rb: +# 1| enter m1 +#-----| -> x + +# 1| enter desugar.rb +#-----| -> m1 + +# 1| m1 +#-----| -> m2 + +# 1| exit m1 + +# 1| exit desugar.rb + +# 1| exit m1 (normal) +#-----| -> exit m1 + +# 1| exit desugar.rb (normal) +#-----| -> exit desugar.rb + +# 1| x +#-----| -> x + +# 2| x +#-----| -> x + +# 2| ... = ... +#-----| -> exit m1 (normal) + +# 2| x +#-----| -> 1 + +# 2| ... + ... +#-----| -> ... = ... + +# 2| 1 +#-----| -> ... + ... + +# 5| enter m2 +#-----| -> x + +# 5| m2 +#-----| -> m3 + +# 5| exit m2 + +# 5| exit m2 (normal) +#-----| -> exit m2 + +# 5| x +#-----| -> x + +# 6| call to foo +#-----| -> __synth__0 + +# 6| x +#-----| -> call to foo + +# 6| ... +#-----| -> exit m2 (normal) + +# 6| call to count= +#-----| -> __synth__0 + +# 6| __synth__0 +#-----| -> ... + +# 6| ... = ... +#-----| -> call to count= + +# 6| __synth__0 +#-----| -> 1 + +# 6| 1 +#-----| -> ... = ... + +# 9| enter m3 +#-----| -> x + +# 9| m3 +#-----| -> m4 + +# 9| exit m3 + +# 9| exit m3 (normal) +#-----| -> exit m3 + +# 9| x +#-----| -> x + +# 10| call to foo +#-----| -> 0 + +# 10| x +#-----| -> call to foo + +# 10| ... +#-----| -> exit m3 (normal) + +# 10| call to []= +#-----| -> __synth__0 + +# 10| __synth__0 +#-----| -> ... + +# 10| ... = ... +#-----| -> call to []= + +# 10| __synth__0 +#-----| -> 1 + +# 10| 0 +#-----| -> __synth__0 + +# 10| 1 +#-----| -> ... = ... + +# 13| enter m4 +#-----| -> x + +# 13| m4 +#-----| -> m5 + +# 13| exit m4 + +# 13| exit m4 (normal) +#-----| -> exit m4 + +# 13| x +#-----| -> __synth__0 + +# 14| call to foo +#-----| -> ... = ... + +# 14| x +#-----| -> call to foo + +# 14| ... +#-----| -> exit m4 (normal) + +# 14| ... = ... +#-----| -> __synth__1 + +# 14| call to count= +#-----| -> __synth__1 + +# 14| __synth__0 +#-----| -> x + +# 14| __synth__0 +#-----| -> __synth__1 + +# 14| call to count +#-----| -> 1 + +# 14| __synth__0 +#-----| -> call to count + +# 14| ... = ... +#-----| -> __synth__0 + +# 14| __synth__1 +#-----| -> ... + +# 14| ... + ... +#-----| -> ... = ... + +# 14| __synth__1 +#-----| -> __synth__0 + +# 14| __synth__1 +#-----| -> call to count= + +# 14| 1 +#-----| -> ... + ... + +# 17| enter m5 +#-----| -> x + +# 17| m5 +#-----| -> m6 + +# 17| exit m5 + +# 17| exit m5 (normal) +#-----| -> exit m5 + +# 17| x +#-----| -> y + +# 17| y +#-----| -> __synth__0 + +# 18| call to foo +#-----| -> ... = ... + +# 18| x +#-----| -> call to foo + +# 18| ... +#-----| -> exit m5 (normal) + +# 18| ... = ... +#-----| -> __synth__1 + +# 18| call to []= +#-----| -> __synth__4 + +# 18| __synth__0 +#-----| -> x + +# 18| __synth__0 +#-----| -> __synth__1 + +# 18| call to [] +#-----| -> 1 + +# 18| __synth__0 +#-----| -> __synth__1 + +# 18| 0 +#-----| -> ... = ... + +# 18| ... = ... +#-----| -> __synth__2 + +# 18| __synth__1 +#-----| -> 0 + +# 18| __synth__1 +#-----| -> __synth__2 + +# 18| __synth__1 +#-----| -> __synth__2 + +# 18| call to bar +#-----| -> ... = ... + +# 18| y +#-----| -> call to bar + +# 18| ... = ... +#-----| -> __synth__3 + +# 18| __synth__2 +#-----| -> y + +# 18| __synth__2 +#-----| -> __synth__3 + +# 18| __synth__2 +#-----| -> __synth__3 + +# 18| ... + ... +#-----| -> ... = ... + +# 18| call to baz +#-----| -> 3 + +# 18| x +#-----| -> call to baz + +# 18| ... = ... +#-----| -> __synth__4 + +# 18| __synth__3 +#-----| -> x + +# 18| __synth__3 +#-----| -> __synth__4 + +# 18| __synth__3 +#-----| -> call to [] + +# 18| 3 +#-----| -> ... + ... + +# 18| ... = ... +#-----| -> __synth__0 + +# 18| __synth__4 +#-----| -> ... + +# 18| ... + ... +#-----| -> ... = ... + +# 18| __synth__4 +#-----| -> __synth__0 + +# 18| __synth__4 +#-----| -> call to []= + +# 18| 1 +#-----| -> ... + ... + +# 21| enter m6 +#-----| -> __synth__0 + +# 21| m6 +#-----| -> m7 + +# 21| exit m6 + +# 21| exit m6 (normal) +#-----| -> exit m6 + +# 22| x +#-----| -> __synth__0 + +# 22| ... +#-----| -> exit m6 (normal) + +# 22| ... = ... +#-----| -> y + +# 22| call to [] +#-----| -> ... = ... + +# 22| 0 +#-----| -> call to [] + +# 22| __synth__0 +#-----| -> 0 + +# 22| y +#-----| -> __synth__0 + +# 22| ... = ... +#-----| -> self + +# 22| call to [] +#-----| -> ... = ... + +# 22| _ .. _ +#-----| -> call to [] + +# 22| __synth__0 +#-----| -> 1 + +# 22| 1 +#-----| -> -2 + +# 22| -2 +#-----| -> _ .. _ + +# 22| call to z +#-----| -> __synth__0__1 + +# 22| self +#-----| -> call to z + +# 22| call to [] +#-----| -> ... = ... + +# 22| ... +#-----| -> ... + +# 22| -1 +#-----| -> call to [] + +# 22| __synth__0 +#-----| -> -1 + +# 22| call to bar= +#-----| -> __synth__0__1 + +# 22| __synth__0__1 +#-----| -> ... + +# 22| ... = ... +#-----| -> call to bar= + +# 22| __synth__0__1 +#-----| -> __synth__0 + +# 22| call to [] +#-----| -> * ... + +# 22| ... = ... +#-----| -> x + +# 22| Array +#-----| -> 1 + +# 22| * ... +#-----| -> ... = ... + +# 22| __synth__0 +#-----| -> Array + +# 22| 1 +#-----| -> 2 + +# 22| 2 +#-----| -> 3 + +# 22| 3 +#-----| -> 4 + +# 22| 4 +#-----| -> call to [] + +# 25| enter m7 +#-----| -> __synth__0 + +# 25| m7 +#-----| -> X + +# 25| exit m7 + +# 25| exit m7 (normal) +#-----| -> exit m7 + +# 26| x +#-----| -> __synth__0 + +# 26| ... +#-----| -> exit m7 (normal) + +# 26| ... = ... +#-----| -> __synth__0__1 + +# 26| call to [] +#-----| -> ... = ... + +# 26| 0 +#-----| -> call to [] + +# 26| __synth__0 +#-----| -> 0 + +# 26| call to [] +#-----| -> * ... + +# 26| ... +#-----| -> ... + +# 26| 1 +#-----| -> call to [] + +# 26| __synth__0 +#-----| -> 1 + +# 26| ... = ... +#-----| -> y + +# 26| * ... +#-----| -> ... = ... + +# 26| __synth__0__1 +#-----| -> __synth__0 + +# 26| y +#-----| -> __synth__0__1 + +# 26| ... = ... +#-----| -> z + +# 26| call to [] +#-----| -> ... = ... + +# 26| 0 +#-----| -> call to [] + +# 26| __synth__0__1 +#-----| -> 0 + +# 26| z +#-----| -> __synth__0__1 + +# 26| ... = ... +#-----| -> ... + +# 26| call to [] +#-----| -> ... = ... + +# 26| 1 +#-----| -> call to [] + +# 26| __synth__0__1 +#-----| -> 1 + +# 26| call to [] +#-----| -> * ... + +# 26| ... = ... +#-----| -> x + +# 26| Array +#-----| -> 1 + +# 26| * ... +#-----| -> ... = ... + +# 26| __synth__0 +#-----| -> Array + +# 26| 1 +#-----| -> Array + +# 26| call to [] +#-----| -> call to [] + +# 26| Array +#-----| -> 2 + +# 26| 2 +#-----| -> 3 + +# 26| 3 +#-----| -> call to [] + +# 29| X +#-----| -> @x + +# 30| ... = ... +#-----| -> @x + +# 30| @x +#-----| -> 1 + +# 30| 1 +#-----| -> ... = ... + +# 31| @x +#-----| -> @x + +# 31| ... = ... +#-----| -> @@y + +# 31| @x +#-----| -> 2 + +# 31| ... + ... +#-----| -> ... = ... + +# 31| 2 +#-----| -> ... + ... + +# 33| ... = ... +#-----| -> @@y + +# 33| @@y +#-----| -> 3 + +# 33| 3 +#-----| -> ... = ... + +# 34| @@y +#-----| -> @@y + +# 34| ... = ... +#-----| -> $global_var + +# 34| @@y +#-----| -> 4 + +# 34| ... / ... +#-----| -> ... = ... + +# 34| 4 +#-----| -> ... / ... + +# 37| ... = ... +#-----| -> $global_var + +# 37| $global_var +#-----| -> 5 + +# 37| 5 +#-----| -> ... = ... + +# 38| $global_var +#-----| -> $global_var + +# 38| ... = ... +#-----| -> exit desugar.rb (normal) + +# 38| $global_var +#-----| -> 6 + +# 38| ... * ... +#-----| -> ... = ... + +# 38| 6 +#-----| -> ... * ... + +exit.rb: +# 1| enter m1 +#-----| -> x + +# 1| enter exit.rb +#-----| -> m1 + +# 1| m1 +#-----| -> m2 + +# 1| exit m1 + +# 1| exit exit.rb + +# 1| exit m1 (abnormal) +#-----| -> exit m1 + +# 1| exit m1 (normal) +#-----| -> exit m1 + +# 1| exit exit.rb (normal) +#-----| -> exit exit.rb + +# 1| x +#-----| -> x + +# 2| if ... +#-----| -> self + +# 2| ... > ... +#-----| false -> if ... +#-----| true -> self + +# 2| x +#-----| -> 2 + +# 2| 2 +#-----| -> ... > ... + +# 3| call to exit +#-----| exit -> exit m1 (abnormal) + +# 3| self +#-----| -> 1 + +# 3| 1 +#-----| -> call to exit + +# 5| call to puts +#-----| -> exit m1 (normal) + +# 5| self +#-----| -> "x <= 2" + +# 5| "x <= 2" +#-----| -> call to puts + +# 8| enter m2 +#-----| -> x + +# 8| m2 +#-----| -> exit exit.rb (normal) + +# 8| exit m2 + +# 8| exit m2 (abnormal) +#-----| -> exit m2 + +# 8| exit m2 (normal) +#-----| -> exit m2 + +# 8| x +#-----| -> x + +# 9| if ... +#-----| -> self + +# 9| ... > ... +#-----| false -> if ... +#-----| true -> self + +# 9| x +#-----| -> 2 + +# 9| 2 +#-----| -> ... > ... + +# 10| call to abort +#-----| exit -> exit m2 (abnormal) + +# 10| self +#-----| -> "abort!" + +# 10| "abort!" +#-----| -> call to abort + +# 12| call to puts +#-----| -> exit m2 (normal) + +# 12| self +#-----| -> "x <= 2" + +# 12| "x <= 2" +#-----| -> call to puts + +heredoc.rb: +# 1| enter double_heredoc +#-----| -> self + +# 1| enter heredoc.rb +#-----| -> double_heredoc + +# 1| double_heredoc +#-----| -> exit heredoc.rb (normal) + +# 1| exit double_heredoc + +# 1| exit heredoc.rb + +# 1| exit double_heredoc (normal) +#-----| -> exit double_heredoc + +# 1| exit heredoc.rb (normal) +#-----| -> exit heredoc.rb + +# 2| call to puts +#-----| -> exit double_heredoc (normal) + +# 2| self +#-----| -> <<A + +# 2| <<A +#-----| -> <<A + +# 2| <<A +#-----| -> call to puts + +ifs.rb: +# 1| enter m1 +#-----| -> x + +# 1| enter ifs.rb +#-----| -> m1 + +# 1| m1 +#-----| -> m2 + +# 1| exit m1 + +# 1| exit ifs.rb + +# 1| exit m1 (normal) +#-----| -> exit m1 + +# 1| exit ifs.rb (normal) +#-----| -> exit ifs.rb + +# 1| x +#-----| -> x + +# 2| if ... +#-----| -> exit m1 (normal) + +# 2| ... > ... +#-----| false -> x +#-----| true -> self + +# 2| x +#-----| -> 2 + +# 2| 2 +#-----| -> ... > ... + +# 2| then ... +#-----| -> if ... + +# 3| call to puts +#-----| -> then ... + +# 3| self +#-----| -> "x is greater than 2" + +# 3| "x is greater than 2" +#-----| -> call to puts + +# 4| elsif ... +#-----| -> if ... + +# 4| ... <= ... +#-----| false -> [false] ... and ... +#-----| true -> x + +# 4| [false] ... and ... +#-----| false -> self + +# 4| [true] ... and ... +#-----| true -> self + +# 4| [false] ... and ... +#-----| false -> [false] ... and ... + +# 4| [true] ... and ... +#-----| true -> x + +# 4| x +#-----| -> 2 + +# 4| 2 +#-----| -> ... <= ... + +# 4| ... > ... +#-----| false -> [false] ... and ... +#-----| true -> [true] ... and ... + +# 4| x +#-----| -> 0 + +# 4| 0 +#-----| -> ... > ... + +# 4| [false] ! ... +#-----| false -> [false] ... and ... + +# 4| [true] ! ... +#-----| true -> [true] ... and ... + +# 4| [false] ( ... ) +#-----| false -> [true] ! ... + +# 4| [true] ( ... ) +#-----| true -> [false] ! ... + +# 4| ... == ... +#-----| false -> [false] ( ... ) +#-----| true -> [true] ( ... ) + +# 4| x +#-----| -> 5 + +# 4| 5 +#-----| -> ... == ... + +# 4| then ... +#-----| -> elsif ... + +# 5| call to puts +#-----| -> then ... + +# 5| self +#-----| -> "x is 1" + +# 5| "x is 1" +#-----| -> call to puts + +# 6| else ... +#-----| -> elsif ... + +# 7| call to puts +#-----| -> else ... + +# 7| self +#-----| -> "I can't guess the number" + +# 7| "I can't guess the number" +#-----| -> call to puts + +# 11| enter m2 +#-----| -> b + +# 11| m2 +#-----| -> m3 + +# 11| exit m2 + +# 11| exit m2 (normal) +#-----| -> exit m2 + +# 11| b +#-----| -> b + +# 12| if ... +#-----| -> 1 + +# 12| b +#-----| false -> if ... +#-----| true -> 0 + +# 13| return +#-----| return -> exit m2 (normal) + +# 13| 0 +#-----| -> return + +# 15| return +#-----| return -> exit m2 (normal) + +# 15| 1 +#-----| -> return + +# 18| enter m3 +#-----| -> x + +# 18| m3 +#-----| -> m4 + +# 18| exit m3 + +# 18| exit m3 (normal) +#-----| -> exit m3 + +# 18| x +#-----| -> x + +# 19| if ... +#-----| -> self + +# 19| ... < ... +#-----| false -> if ... +#-----| true -> x + +# 19| x +#-----| -> 0 + +# 19| 0 +#-----| -> ... < ... + +# 19| then ... +#-----| -> if ... + +# 20| ... = ... +#-----| -> x + +# 20| x +#-----| -> x + +# 20| - ... +#-----| -> ... = ... + +# 20| x +#-----| -> - ... + +# 21| if ... +#-----| -> then ... + +# 21| ... > ... +#-----| false -> if ... +#-----| true -> x + +# 21| x +#-----| -> 10 + +# 21| 10 +#-----| -> ... > ... + +# 21| then ... +#-----| -> if ... + +# 22| ... = ... +#-----| -> then ... + +# 22| x +#-----| -> x + +# 22| ... - ... +#-----| -> ... = ... + +# 22| x +#-----| -> 1 + +# 22| 1 +#-----| -> ... - ... + +# 25| call to puts +#-----| -> exit m3 (normal) + +# 25| self +#-----| -> x + +# 25| x +#-----| -> call to puts + +# 28| enter m4 +#-----| -> b1 + +# 28| m4 +#-----| -> m5 + +# 28| exit m4 + +# 28| exit m4 (normal) +#-----| -> exit m4 + +# 28| b1 +#-----| -> b2 + +# 28| b2 +#-----| -> b3 + +# 28| b3 +#-----| -> b1 + +# 29| return +#-----| return -> exit m4 (normal) + +# 29| [false] ( ... ) +#-----| false -> "!b2 || !b3" + +# 29| [true] ( ... ) +#-----| true -> "b2 || b3" + +# 29| ... ? ... : ... +#-----| -> return + +# 29| [false] ... ? ... : ... +#-----| false -> [false] ( ... ) + +# 29| [true] ... ? ... : ... +#-----| true -> [true] ( ... ) + +# 29| b1 +#-----| true -> b2 +#-----| false -> b3 + +# 29| b2 +#-----| false -> [false] ... ? ... : ... +#-----| true -> [true] ... ? ... : ... + +# 29| b3 +#-----| false -> [false] ... ? ... : ... +#-----| true -> [true] ... ? ... : ... + +# 29| "b2 || b3" +#-----| -> ... ? ... : ... + +# 29| "!b2 || !b3" +#-----| -> ... ? ... : ... + +# 32| enter m5 +#-----| -> b1 + +# 32| m5 +#-----| -> 1 + +# 32| exit m5 + +# 32| exit m5 (normal) +#-----| -> exit m5 + +# 32| b1 +#-----| -> b2 + +# 32| b2 +#-----| -> b3 + +# 32| b3 +#-----| -> b4 + +# 32| b4 +#-----| -> b5 + +# 32| b5 +#-----| -> b1 + +# 33| if ... +#-----| -> exit m5 (normal) + +# 33| [false] ( ... ) +#-----| false -> "!b2 || !b4 || !b5" + +# 33| [true] ( ... ) +#-----| true -> "b2 || b4 || b5" + +# 33| [false] if ... +#-----| false -> [false] ( ... ) + +# 33| [true] if ... +#-----| true -> [true] ( ... ) + +# 33| b1 +#-----| true -> b2 +#-----| false -> b3 + +# 33| [false] then ... +#-----| false -> [false] if ... + +# 33| [true] then ... +#-----| true -> [true] if ... + +# 33| b2 +#-----| false -> [false] then ... +#-----| true -> [true] then ... + +# 33| [false] elsif ... +#-----| false -> [false] if ... + +# 33| [true] elsif ... +#-----| true -> [true] if ... + +# 33| b3 +#-----| true -> b4 +#-----| false -> b5 + +# 33| [false] then ... +#-----| false -> [false] elsif ... + +# 33| [true] then ... +#-----| true -> [true] elsif ... + +# 33| b4 +#-----| false -> [false] then ... +#-----| true -> [true] then ... + +# 33| [false] else ... +#-----| false -> [false] elsif ... + +# 33| [true] else ... +#-----| true -> [true] elsif ... + +# 33| b5 +#-----| false -> [false] else ... +#-----| true -> [true] else ... + +# 33| then ... +#-----| -> if ... + +# 33| "b2 || b4 || b5" +#-----| -> then ... + +# 33| else ... +#-----| -> if ... + +# 33| "!b2 || !b4 || !b5" +#-----| -> else ... + +# 36| enter conditional_method_def +#-----| -> self + +# 36| conditional_method_def +#-----| -> ... unless ... + +# 36| ... unless ... +#-----| -> constant_condition + +# 36| exit conditional_method_def + +# 36| exit conditional_method_def (normal) +#-----| -> exit conditional_method_def + +# 37| call to puts +#-----| -> exit conditional_method_def (normal) + +# 37| self +#-----| -> "bla" + +# 37| "bla" +#-----| -> call to puts + +# 38| ... == ... +#-----| false -> conditional_method_def +#-----| true -> ... unless ... + +# 38| 1 +#-----| -> 2 + +# 38| 2 +#-----| -> ... == ... + +# 40| enter constant_condition +#-----| -> true + +# 40| constant_condition +#-----| -> empty_else + +# 40| exit constant_condition + +# 40| exit constant_condition (normal) +#-----| -> exit constant_condition + +# 41| if ... +#-----| -> exit constant_condition (normal) + +# 41| [false] ! ... +#-----| false -> if ... + +# 41| true +#-----| true -> [false] ! ... + +# 46| enter empty_else +#-----| -> b + +# 46| empty_else +#-----| -> exit ifs.rb (normal) + +# 46| exit empty_else + +# 46| exit empty_else (normal) +#-----| -> exit empty_else + +# 46| b +#-----| -> b + +# 47| if ... +#-----| -> self + +# 47| b +#-----| true -> self + +# 47| then ... +#-----| -> if ... + +# 48| call to puts +#-----| -> then ... + +# 48| self +#-----| -> "true" + +# 48| "true" +#-----| -> call to puts + +# 51| call to puts +#-----| -> exit empty_else (normal) + +# 51| self +#-----| -> "done" + +# 51| "done" +#-----| -> call to puts + +loops.rb: +# 1| enter m1 +#-----| -> x + +# 1| enter loops.rb +#-----| -> m1 + +# 1| m1 +#-----| -> m2 + +# 1| exit m1 + +# 1| exit loops.rb + +# 1| exit m1 (normal) +#-----| -> exit m1 + +# 1| exit loops.rb (normal) +#-----| -> exit loops.rb + +# 1| x +#-----| -> x + +# 2| while ... +#-----| -> exit m1 (normal) + +# 2| ... >= ... +#-----| false -> while ... +#-----| true -> self + +# 2| x +#-----| -> 0 + +# 2| 0 +#-----| -> ... >= ... + +# 2| do ... +#-----| -> x + +# 3| call to puts +#-----| -> x + +# 3| self +#-----| -> x + +# 3| x +#-----| -> call to puts + +# 4| x +#-----| -> x + +# 4| ... = ... +#-----| -> do ... + +# 4| x +#-----| -> 1 + +# 4| ... - ... +#-----| -> ... = ... + +# 4| 1 +#-----| -> ... - ... + +# 8| enter m2 +#-----| -> x + +# 8| m2 +#-----| -> m3 + +# 8| exit m2 + +# 8| exit m2 (normal) +#-----| -> exit m2 + +# 8| x +#-----| -> x + +# 9| while ... +#-----| -> self + +# 9| ... >= ... +#-----| false -> while ... +#-----| true -> self + +# 9| x +#-----| -> 0 + +# 9| 0 +#-----| -> ... >= ... + +# 9| do ... +#-----| -> x + +# 10| call to puts +#-----| -> x + +# 10| self +#-----| -> x + +# 10| x +#-----| -> call to puts + +# 11| x +#-----| -> x + +# 11| ... = ... +#-----| -> x + +# 11| x +#-----| -> 1 + +# 11| ... - ... +#-----| -> ... = ... + +# 11| 1 +#-----| -> ... - ... + +# 12| if ... +#-----| -> self + +# 12| ... > ... +#-----| true -> break +#-----| false -> x + +# 12| x +#-----| -> 100 + +# 12| 100 +#-----| -> ... > ... + +# 13| break +#-----| break -> while ... + +# 14| elsif ... +#-----| -> if ... + +# 14| ... > ... +#-----| true -> next +#-----| false -> x + +# 14| x +#-----| -> 50 + +# 14| 50 +#-----| -> ... > ... + +# 15| next +#-----| next -> x + +# 16| elsif ... +#-----| -> elsif ... + +# 16| ... > ... +#-----| false -> elsif ... +#-----| true -> redo + +# 16| x +#-----| -> 10 + +# 16| 10 +#-----| -> ... > ... + +# 17| redo +#-----| redo -> self + +# 19| call to puts +#-----| -> do ... + +# 19| self +#-----| -> "Iter" + +# 19| "Iter" +#-----| -> call to puts + +# 21| call to puts +#-----| -> exit m2 (normal) + +# 21| self +#-----| -> "Done" + +# 21| "Done" +#-----| -> call to puts + +# 24| enter m3 +#-----| -> Array + +# 24| m3 +#-----| -> m4 + +# 24| exit m3 + +# 24| exit m3 (normal) +#-----| -> exit m3 + +# 25| call to each +#-----| -> exit m3 (normal) + +# 25| call to [] +#-----| -> do ... end + +# 25| Array +#-----| -> 1 + +# 25| 1 +#-----| -> 2 + +# 25| 2 +#-----| -> 3 + +# 25| 3 +#-----| -> call to [] + +# 25| enter do ... end +#-----| -> x + +# 25| do ... end +#-----| -> call to each + +# 25| exit do ... end + +# 25| exit do ... end (normal) +#-----| -> exit do ... end + +# 25| x +#-----| -> self + +# 26| call to puts +#-----| -> exit do ... end (normal) + +# 26| self +#-----| -> x + +# 26| x +#-----| -> call to puts + +# 30| enter m4 +#-----| -> x + +# 30| m4 +#-----| -> exit loops.rb (normal) + +# 30| exit m4 + +# 30| exit m4 (normal) +#-----| -> exit m4 + +# 30| x +#-----| -> y + +# 30| y +#-----| -> x + +# 31| while ... +#-----| -> exit m4 (normal) + +# 31| ... < ... +#-----| false -> while ... + +# 31| x +#-----| -> y + +# 31| y +#-----| -> ... < ... + +raise.rb: +# 1| enter raise.rb +#-----| -> ExceptionA + +# 1| ExceptionA +#-----| -> Exception + +# 1| exit raise.rb + +# 1| exit raise.rb (normal) +#-----| -> exit raise.rb + +# 1| Exception +#-----| -> ExceptionB + +# 4| ExceptionB +#-----| -> Exception + +# 4| Exception +#-----| -> m1 + +# 7| enter m1 +#-----| -> x + +# 7| m1 +#-----| -> m2 + +# 7| exit m1 + +# 7| exit m1 (abnormal) +#-----| -> exit m1 + +# 7| exit m1 (normal) +#-----| -> exit m1 + +# 7| x +#-----| -> x + +# 8| if ... +#-----| -> self + +# 8| ... > ... +#-----| false -> if ... +#-----| true -> self + +# 8| x +#-----| -> 2 + +# 8| 2 +#-----| -> ... > ... + +# 9| call to raise +#-----| raise -> exit m1 (abnormal) + +# 9| self +#-----| -> "x > 2" + +# 9| "x > 2" +#-----| -> call to raise + +# 11| call to puts +#-----| -> exit m1 (normal) + +# 11| self +#-----| -> "x <= 2" + +# 11| "x <= 2" +#-----| -> call to puts + +# 14| enter m2 +#-----| -> b + +# 14| m2 +#-----| -> m3 + +# 14| exit m2 + +# 14| exit m2 (abnormal) +#-----| -> exit m2 + +# 14| exit m2 (normal) +#-----| -> exit m2 + +# 14| b +#-----| -> b + +# 16| if ... +#-----| -> self + +# 16| b +#-----| false -> if ... +#-----| true -> self + +# 17| call to raise +#-----| raise -> rescue ... + +# 17| self +#-----| -> ExceptionA + +# 17| ExceptionA +#-----| -> call to raise + +# 19| rescue ... +#-----| -> ExceptionA + +# 19| ExceptionA +#-----| match -> self +#-----| raise -> exit m2 (abnormal) + +# 19| then ... +#-----| -> self + +# 20| call to puts +#-----| -> then ... + +# 20| self +#-----| -> "Rescued" + +# 20| "Rescued" +#-----| -> call to puts + +# 22| call to puts +#-----| -> exit m2 (normal) + +# 22| self +#-----| -> "End m2" + +# 22| "End m2" +#-----| -> call to puts + +# 25| enter m3 +#-----| -> b + +# 25| m3 +#-----| -> m4 + +# 25| exit m3 + +# 25| exit m3 (normal) +#-----| -> exit m3 + +# 25| b +#-----| -> b + +# 27| if ... +#-----| -> self + +# 27| b +#-----| false -> if ... +#-----| true -> self + +# 28| call to raise +#-----| raise -> rescue ... + +# 28| self +#-----| -> ExceptionA + +# 28| ExceptionA +#-----| -> call to raise + +# 30| rescue ... +#-----| -> self + +# 30| then ... +#-----| -> self + +# 31| call to puts +#-----| -> then ... + +# 31| self +#-----| -> "Rescued" + +# 31| "Rescued" +#-----| -> call to puts + +# 33| call to puts +#-----| -> exit m3 (normal) + +# 33| self +#-----| -> "End m3" + +# 33| "End m3" +#-----| -> call to puts + +# 36| enter m4 +#-----| -> b + +# 36| m4 +#-----| -> m5 + +# 36| exit m4 + +# 36| exit m4 (normal) +#-----| -> exit m4 + +# 36| b +#-----| -> b + +# 38| if ... +#-----| -> self + +# 38| b +#-----| false -> if ... +#-----| true -> self + +# 39| call to raise +#-----| raise -> rescue ... + +# 39| self +#-----| -> ExceptionA + +# 39| ExceptionA +#-----| -> call to raise + +# 41| rescue ... +#-----| -> e + +# 41| e +#-----| -> self + +# 41| then ... +#-----| -> self + +# 42| call to puts +#-----| -> then ... + +# 42| self +#-----| -> "Rescued {e}" + +# 42| "Rescued {e}" +#-----| -> call to puts + +# 44| call to puts +#-----| -> exit m4 (normal) + +# 44| self +#-----| -> "End m4" + +# 44| "End m4" +#-----| -> call to puts + +# 47| enter m5 +#-----| -> b + +# 47| m5 +#-----| -> m6 + +# 47| exit m5 + +# 47| exit m5 (normal) +#-----| -> exit m5 + +# 47| b +#-----| -> b + +# 49| if ... +#-----| -> self + +# 49| b +#-----| false -> if ... +#-----| true -> self + +# 50| call to raise +#-----| raise -> rescue ... + +# 50| self +#-----| -> ExceptionA + +# 50| ExceptionA +#-----| -> call to raise + +# 52| rescue ... +#-----| -> e + +# 52| e +#-----| -> self + +# 54| call to puts +#-----| -> exit m5 (normal) + +# 54| self +#-----| -> "End m5" + +# 54| "End m5" +#-----| -> call to puts + +# 57| enter m6 +#-----| -> b + +# 57| m6 +#-----| -> m7 + +# 57| exit m6 + +# 57| exit m6 (abnormal) +#-----| -> exit m6 + +# 57| exit m6 (normal) +#-----| -> exit m6 + +# 57| b +#-----| -> b + +# 59| if ... +#-----| -> self + +# 59| b +#-----| false -> if ... +#-----| true -> self + +# 60| call to raise +#-----| raise -> rescue ... + +# 60| self +#-----| -> ExceptionA + +# 60| ExceptionA +#-----| -> call to raise + +# 62| rescue ... +#-----| -> ExceptionA + +# 62| ExceptionA +#-----| no-match -> ExceptionB +#-----| match -> e + +# 62| ExceptionB +#-----| match -> e +#-----| raise -> exit m6 (abnormal) + +# 62| e +#-----| -> self + +# 62| then ... +#-----| -> self + +# 63| call to puts +#-----| -> then ... + +# 63| self +#-----| -> "Rescued {e}" + +# 63| "Rescued {e}" +#-----| -> call to puts + +# 65| call to puts +#-----| -> exit m6 (normal) + +# 65| self +#-----| -> "End m6" + +# 65| "End m6" +#-----| -> call to puts + +# 68| enter m7 +#-----| -> x + +# 68| m7 +#-----| -> m8 + +# 68| exit m7 + +# 68| exit m7 (abnormal) +#-----| -> exit m7 + +# 68| exit m7 (normal) +#-----| -> exit m7 + +# 68| x +#-----| -> x + +# 69| if ... +#-----| -> self + +# 69| ... > ... +#-----| false -> x +#-----| true -> self +#-----| raise -> [ensure: raise] self + +# 69| x +#-----| -> 2 + +# 69| 2 +#-----| -> ... > ... + +# 70| call to raise +#-----| raise -> [ensure: raise] self + +# 70| self +#-----| -> "x > 2" + +# 70| "x > 2" +#-----| -> call to raise + +# 71| elsif ... +#-----| -> if ... + +# 71| ... < ... +#-----| false -> elsif ... +#-----| true -> "x < 0" +#-----| raise -> [ensure: raise] self + +# 71| x +#-----| -> 0 + +# 71| 0 +#-----| -> ... < ... + +# 72| return +#-----| return -> [ensure: return] self + +# 72| "x < 0" +#-----| -> return + +# 74| call to puts +#-----| -> self +#-----| raise -> [ensure: raise] self + +# 74| self +#-----| -> "0 <= x <= 2" + +# 74| "0 <= x <= 2" +#-----| -> call to puts + +# 75| ensure ... +#-----| -> exit m7 (normal) + +# 75| [ensure: raise] ensure ... +#-----| raise -> exit m7 (abnormal) + +# 75| [ensure: return] ensure ... +#-----| return -> exit m7 (normal) + +# 76| call to puts +#-----| -> ensure ... + +# 76| [ensure: raise] call to puts +#-----| -> [ensure: raise] ensure ... + +# 76| [ensure: return] call to puts +#-----| -> [ensure: return] ensure ... + +# 76| self +#-----| -> "ensure" + +# 76| [ensure: raise] self +#-----| -> [ensure: raise] "ensure" + +# 76| [ensure: return] self +#-----| -> [ensure: return] "ensure" + +# 76| "ensure" +#-----| -> call to puts + +# 76| [ensure: raise] "ensure" +#-----| -> [ensure: raise] call to puts + +# 76| [ensure: return] "ensure" +#-----| -> [ensure: return] call to puts + +# 79| enter m8 +#-----| -> x + +# 79| m8 +#-----| -> m9 + +# 79| exit m8 + +# 79| exit m8 (abnormal) +#-----| -> exit m8 + +# 79| exit m8 (normal) +#-----| -> exit m8 + +# 79| x +#-----| -> self + +# 80| call to puts +#-----| -> x + +# 80| self +#-----| -> "Begin m8" + +# 80| "Begin m8" +#-----| -> call to puts + +# 82| if ... +#-----| -> self + +# 82| ... > ... +#-----| false -> x +#-----| true -> self +#-----| raise -> [ensure: raise] self + +# 82| x +#-----| -> 2 + +# 82| 2 +#-----| -> ... > ... + +# 83| call to raise +#-----| raise -> [ensure: raise] self + +# 83| self +#-----| -> "x > 2" + +# 83| "x > 2" +#-----| -> call to raise + +# 84| elsif ... +#-----| -> if ... + +# 84| ... < ... +#-----| false -> elsif ... +#-----| true -> "x < 0" +#-----| raise -> [ensure: raise] self + +# 84| x +#-----| -> 0 + +# 84| 0 +#-----| -> ... < ... + +# 85| return +#-----| return -> [ensure: return] self + +# 85| "x < 0" +#-----| -> return + +# 87| call to puts +#-----| -> self +#-----| raise -> [ensure: raise] self + +# 87| self +#-----| -> "0 <= x <= 2" + +# 87| "0 <= x <= 2" +#-----| -> call to puts + +# 88| ensure ... +#-----| -> self + +# 88| [ensure: raise] ensure ... +#-----| raise -> exit m8 (abnormal) + +# 88| [ensure: return] ensure ... +#-----| return -> exit m8 (normal) + +# 89| call to puts +#-----| -> ensure ... + +# 89| [ensure: raise] call to puts +#-----| -> [ensure: raise] ensure ... + +# 89| [ensure: return] call to puts +#-----| -> [ensure: return] ensure ... + +# 89| self +#-----| -> "ensure" + +# 89| [ensure: raise] self +#-----| -> [ensure: raise] "ensure" + +# 89| [ensure: return] self +#-----| -> [ensure: return] "ensure" + +# 89| "ensure" +#-----| -> call to puts + +# 89| [ensure: raise] "ensure" +#-----| -> [ensure: raise] call to puts + +# 89| [ensure: return] "ensure" +#-----| -> [ensure: return] call to puts + +# 91| call to puts +#-----| -> exit m8 (normal) + +# 91| self +#-----| -> "End m8" + +# 91| "End m8" +#-----| -> call to puts + +# 94| enter m9 +#-----| -> x + +# 94| m9 +#-----| -> m10 + +# 94| exit m9 + +# 94| exit m9 (abnormal) +#-----| -> exit m9 + +# 94| exit m9 (normal) +#-----| -> exit m9 + +# 94| x +#-----| -> b1 + +# 94| b1 +#-----| -> b2 + +# 94| b2 +#-----| -> self + +# 95| call to puts +#-----| -> x +#-----| raise -> [ensure: raise] self + +# 95| self +#-----| -> "Begin m9" + +# 95| "Begin m9" +#-----| -> call to puts + +# 97| if ... +#-----| -> self + +# 97| ... > ... +#-----| false -> x +#-----| true -> self +#-----| raise -> [ensure: raise] self + +# 97| x +#-----| -> 2 + +# 97| 2 +#-----| -> ... > ... + +# 98| call to raise +#-----| raise -> [ensure: raise] self + +# 98| self +#-----| -> "x > 2" + +# 98| "x > 2" +#-----| -> call to raise + +# 99| elsif ... +#-----| -> if ... + +# 99| ... < ... +#-----| false -> elsif ... +#-----| true -> "x < 0" +#-----| raise -> [ensure: raise] self + +# 99| x +#-----| -> 0 + +# 99| 0 +#-----| -> ... < ... + +# 100| return +#-----| return -> [ensure: return] self + +# 100| "x < 0" +#-----| -> return + +# 102| call to puts +#-----| -> self +#-----| raise -> [ensure: raise] self + +# 102| self +#-----| -> "0 <= x <= 2" + +# 102| "0 <= x <= 2" +#-----| -> call to puts + +# 103| ensure ... +#-----| -> self + +# 103| [ensure: raise] ensure ... +#-----| raise -> [ensure: raise] self + +# 103| [ensure: return] ensure ... +#-----| return -> [ensure: return] self + +# 104| call to puts +#-----| -> b1 +#-----| raise -> [ensure: raise] self + +# 104| [ensure: raise] call to puts +#-----| -> [ensure: raise] b1 +#-----| raise -> [ensure: raise] self + +# 104| [ensure: return] call to puts +#-----| -> [ensure: return] b1 +#-----| raise -> [ensure: raise] self + +# 104| self +#-----| -> "outer ensure" + +# 104| [ensure: raise] self +#-----| -> [ensure: raise] "outer ensure" + +# 104| [ensure: return] self +#-----| -> [ensure: return] "outer ensure" + +# 104| "outer ensure" +#-----| -> call to puts + +# 104| [ensure: raise] "outer ensure" +#-----| -> [ensure: raise] call to puts + +# 104| [ensure: return] "outer ensure" +#-----| -> [ensure: return] call to puts + +# 106| if ... +#-----| -> self + +# 106| [ensure: raise] if ... +#-----| -> [ensure: raise] self + +# 106| [ensure: return] if ... +#-----| -> [ensure: return] self + +# 106| b1 +#-----| false -> if ... +#-----| true -> self + +# 106| [ensure: raise] b1 +#-----| false -> [ensure: raise] if ... +#-----| true -> [ensure: raise] self + +# 106| [ensure: return] b1 +#-----| false -> [ensure: return] if ... +#-----| true -> [ensure: return] self + +# 107| call to raise +#-----| raise -> [ensure(1): raise] self + +# 107| [ensure: raise] call to raise +#-----| raise -> [ensure: raise, ensure(1): raise] self + +# 107| [ensure: return] call to raise +#-----| raise -> [ensure: return, ensure(1): raise] self + +# 107| self +#-----| -> "b1 is true" + +# 107| [ensure: raise] self +#-----| -> [ensure: raise] "b1 is true" + +# 107| [ensure: return] self +#-----| -> [ensure: return] "b1 is true" + +# 107| "b1 is true" +#-----| -> call to raise + +# 107| [ensure: raise] "b1 is true" +#-----| -> [ensure: raise] call to raise + +# 107| [ensure: return] "b1 is true" +#-----| -> [ensure: return] call to raise + +# 109| ensure ... +#-----| -> ensure ... + +# 109| [ensure(1): raise] ensure ... +#-----| raise -> [ensure: raise] self + +# 109| [ensure: raise] ensure ... +#-----| -> [ensure: raise] ensure ... + +# 109| [ensure: raise, ensure(1): raise] ensure ... +#-----| raise -> [ensure: raise] self + +# 109| [ensure: return] ensure ... +#-----| -> [ensure: return] ensure ... + +# 109| [ensure: return, ensure(1): raise] ensure ... +#-----| raise -> [ensure: raise] self + +# 110| call to puts +#-----| -> ensure ... +#-----| raise -> [ensure: raise] self + +# 110| [ensure(1): raise] call to puts +#-----| -> [ensure(1): raise] ensure ... +#-----| raise -> [ensure: raise] self + +# 110| [ensure: raise] call to puts +#-----| -> [ensure: raise] ensure ... +#-----| raise -> [ensure: raise] self + +# 110| [ensure: raise, ensure(1): raise] call to puts +#-----| -> [ensure: raise, ensure(1): raise] ensure ... +#-----| raise -> [ensure: raise] self + +# 110| [ensure: return] call to puts +#-----| -> [ensure: return] ensure ... +#-----| raise -> [ensure: raise] self + +# 110| [ensure: return, ensure(1): raise] call to puts +#-----| -> [ensure: return, ensure(1): raise] ensure ... +#-----| raise -> [ensure: raise] self + +# 110| self +#-----| -> "inner ensure" + +# 110| [ensure(1): raise] self +#-----| -> [ensure(1): raise] "inner ensure" + +# 110| [ensure: raise] self +#-----| -> [ensure: raise] "inner ensure" + +# 110| [ensure: raise, ensure(1): raise] self +#-----| -> [ensure: raise, ensure(1): raise] "inner ensure" + +# 110| [ensure: return] self +#-----| -> [ensure: return] "inner ensure" + +# 110| [ensure: return, ensure(1): raise] self +#-----| -> [ensure: return, ensure(1): raise] "inner ensure" + +# 110| "inner ensure" +#-----| -> call to puts + +# 110| [ensure(1): raise] "inner ensure" +#-----| -> [ensure(1): raise] call to puts + +# 110| [ensure: raise] "inner ensure" +#-----| -> [ensure: raise] call to puts + +# 110| [ensure: raise, ensure(1): raise] "inner ensure" +#-----| -> [ensure: raise, ensure(1): raise] call to puts + +# 110| [ensure: return] "inner ensure" +#-----| -> [ensure: return] call to puts + +# 110| [ensure: return, ensure(1): raise] "inner ensure" +#-----| -> [ensure: return, ensure(1): raise] call to puts + +# 113| call to puts +#-----| -> self +#-----| raise -> [ensure: raise] self + +# 113| self +#-----| -> "End m9" + +# 113| "End m9" +#-----| -> call to puts + +# 114| ensure ... +#-----| -> exit m9 (normal) + +# 114| [ensure: raise] ensure ... +#-----| raise -> exit m9 (abnormal) + +# 114| [ensure: return] ensure ... +#-----| return -> exit m9 (normal) + +# 115| call to puts +#-----| -> b2 + +# 115| [ensure: raise] call to puts +#-----| -> [ensure: raise] b2 + +# 115| [ensure: return] call to puts +#-----| -> [ensure: return] b2 + +# 115| self +#-----| -> "method ensure" + +# 115| [ensure: raise] self +#-----| -> [ensure: raise] "method ensure" + +# 115| [ensure: return] self +#-----| -> [ensure: return] "method ensure" + +# 115| "method ensure" +#-----| -> call to puts + +# 115| [ensure: raise] "method ensure" +#-----| -> [ensure: raise] call to puts + +# 115| [ensure: return] "method ensure" +#-----| -> [ensure: return] call to puts + +# 116| if ... +#-----| -> ensure ... + +# 116| [ensure: raise] if ... +#-----| -> [ensure: raise] ensure ... + +# 116| [ensure: return] if ... +#-----| -> [ensure: return] ensure ... + +# 116| b2 +#-----| false -> if ... +#-----| true -> self + +# 116| [ensure: raise] b2 +#-----| false -> [ensure: raise] if ... +#-----| true -> [ensure: raise] self + +# 116| [ensure: return] b2 +#-----| false -> [ensure: return] if ... +#-----| true -> [ensure: return] self + +# 117| call to raise +#-----| raise -> exit m9 (abnormal) + +# 117| [ensure: raise] call to raise +#-----| raise -> exit m9 (abnormal) + +# 117| [ensure: return] call to raise +#-----| raise -> exit m9 (abnormal) + +# 117| self +#-----| -> "b2 is true" + +# 117| [ensure: raise] self +#-----| -> [ensure: raise] "b2 is true" + +# 117| [ensure: return] self +#-----| -> [ensure: return] "b2 is true" + +# 117| "b2 is true" +#-----| -> call to raise + +# 117| [ensure: raise] "b2 is true" +#-----| -> [ensure: raise] call to raise + +# 117| [ensure: return] "b2 is true" +#-----| -> [ensure: return] call to raise + +# 121| enter m10 +#-----| -> p + +# 121| m10 +#-----| -> m11 + +# 121| exit m10 + +# 121| exit m10 (abnormal) +#-----| -> exit m10 + +# 121| exit m10 (normal) +#-----| -> exit m10 + +# 121| p +#-----| no-match -> self +#-----| match -> self + +# 121| call to raise +#-----| raise -> exit m10 (abnormal) + +# 121| self +#-----| -> "Exception" + +# 121| "Exception" +#-----| -> call to raise + +# 124| ensure ... +#-----| -> exit m10 (normal) + +# 125| call to puts +#-----| -> ensure ... + +# 125| self +#-----| -> "Will not get executed if p is..." + +# 125| "Will not get executed if p is..." +#-----| -> call to puts + +# 128| enter m11 +#-----| -> b + +# 128| m11 +#-----| -> m12 + +# 128| exit m11 + +# 128| exit m11 (abnormal) +#-----| -> exit m11 + +# 128| exit m11 (normal) +#-----| -> exit m11 + +# 128| b +#-----| -> b + +# 130| if ... +#-----| -> self + +# 130| b +#-----| false -> if ... +#-----| true -> self + +# 131| call to raise +#-----| raise -> rescue ... + +# 131| self +#-----| -> ExceptionA + +# 131| ExceptionA +#-----| -> call to raise + +# 133| rescue ... +#-----| -> ExceptionA + +# 133| ExceptionA +#-----| no-match -> rescue ... +#-----| match -> self + +# 134| rescue ... +#-----| -> ExceptionB + +# 134| ExceptionB +#-----| match -> self +#-----| raise -> [ensure: raise] self + +# 134| then ... +#-----| -> self + +# 135| call to puts +#-----| -> then ... + +# 135| self +#-----| -> "ExceptionB" + +# 135| "ExceptionB" +#-----| -> call to puts + +# 136| ensure ... +#-----| -> self + +# 136| [ensure: raise] ensure ... +#-----| raise -> exit m11 (abnormal) + +# 137| call to puts +#-----| -> ensure ... + +# 137| [ensure: raise] call to puts +#-----| -> [ensure: raise] ensure ... + +# 137| self +#-----| -> "Ensure" + +# 137| [ensure: raise] self +#-----| -> [ensure: raise] "Ensure" + +# 137| "Ensure" +#-----| -> call to puts + +# 137| [ensure: raise] "Ensure" +#-----| -> [ensure: raise] call to puts + +# 139| call to puts +#-----| -> exit m11 (normal) + +# 139| self +#-----| -> "End m11" + +# 139| "End m11" +#-----| -> call to puts + +# 142| enter m12 +#-----| -> b + +# 142| m12 +#-----| -> m13 + +# 142| exit m12 + +# 142| exit m12 (normal) +#-----| -> exit m12 + +# 142| b +#-----| -> b + +# 143| if ... +#-----| -> 3 + +# 143| b +#-----| false -> if ... +#-----| true -> self + +# 144| call to raise +#-----| raise -> [ensure: raise] 3 + +# 144| self +#-----| -> "" + +# 144| "" +#-----| -> call to raise + +# 147| return +#-----| return -> exit m12 (normal) + +# 147| [ensure: raise] return +#-----| return -> exit m12 (normal) + +# 147| 3 +#-----| -> return + +# 147| [ensure: raise] 3 +#-----| -> [ensure: raise] return + +# 150| m13 +#-----| -> m14 + +# 154| enter m14 +#-----| -> element + +# 154| m14 +#-----| -> m15 + +# 154| exit m14 + +# 154| exit m14 (normal) +#-----| -> exit m14 + +# 154| element +#-----| -> element + +# 155| call to each +#-----| -> exit m14 (normal) + +# 155| element +#-----| -> { ... } + +# 155| enter { ... } +#-----| -> elem + +# 155| { ... } +#-----| -> call to each + +# 155| exit { ... } + +# 155| exit { ... } (abnormal) +#-----| -> exit { ... } + +# 155| exit { ... } (normal) +#-----| -> exit { ... } + +# 155| elem +#-----| -> element + +# 155| ... if ... +#-----| -> exit { ... } (normal) + +# 155| call to raise +#-----| raise -> exit { ... } (abnormal) + +# 155| self +#-----| -> "" + +# 155| "" +#-----| -> call to raise + +# 155| call to nil? +#-----| false -> ... if ... +#-----| true -> self + +# 155| element +#-----| -> call to nil? + +# 158| enter m15 +#-----| -> self + +# 158| m15 +#-----| -> C + +# 158| exit m15 + +# 158| exit m15 (normal) +#-----| -> exit m15 + +# 159| call to foo +#-----| -> exit m15 (normal) + +# 159| self +#-----| -> do ... end + +# 159| enter do ... end +#-----| -> self + +# 159| do ... end +#-----| -> call to foo + +# 159| exit do ... end + +# 159| exit do ... end (normal) +#-----| -> exit do ... end + +# 160| call to bar +#-----| -> exit do ... end (normal) + +# 160| self +#-----| -> -> { ... } + +# 160| enter -> { ... } +#-----| -> x + +# 160| -> { ... } +#-----| -> call to bar + +# 160| exit -> { ... } + +# 160| exit -> { ... } (abnormal) +#-----| -> exit -> { ... } + +# 160| exit -> { ... } (normal) +#-----| -> exit -> { ... } + +# 160| x +#-----| -> x + +# 161| call to raise +#-----| raise -> exit -> { ... } (abnormal) + +# 161| ... unless ... +#-----| -> exit -> { ... } (normal) + +# 161| self +#-----| -> "" + +# 161| "" +#-----| -> call to raise + +# 161| x +#-----| true -> ... unless ... +#-----| false -> self + +# 166| C +#-----| -> self + +# 167| enter m +#-----| -> self + +# 167| m +#-----| -> exit raise.rb (normal) + +# 167| exit m + +# 167| exit m (abnormal) +#-----| -> exit m + +# 167| self +#-----| -> m + +# 168| call to raise +#-----| raise -> exit m (abnormal) + +# 168| self +#-----| -> "" + +# 168| "" +#-----| -> call to raise diff --git a/ruby/ql/test/library-tests/controlflow/graph/Cfg.ql b/ruby/ql/test/library-tests/controlflow/graph/Cfg.ql new file mode 100644 index 00000000000..b4a5bbe946f --- /dev/null +++ b/ruby/ql/test/library-tests/controlflow/graph/Cfg.ql @@ -0,0 +1,10 @@ +/** + * @kind graph + */ + +import codeql.ruby.CFG +import codeql.ruby.controlflow.internal.ControlFlowGraphImpl::TestOutput + +class MyRelevantNode extends RelevantNode { + MyRelevantNode() { exists(this) } +} diff --git a/ruby/ql/test/library-tests/controlflow/graph/break_ensure.rb b/ruby/ql/test/library-tests/controlflow/graph/break_ensure.rb new file mode 100644 index 00000000000..4ee587049f3 --- /dev/null +++ b/ruby/ql/test/library-tests/controlflow/graph/break_ensure.rb @@ -0,0 +1,56 @@ +def m1 elements + for element in elements do + if element > 0 then + break + end + end +ensure + if elements.nil? then + puts "elements nil" + end +end + +def m2 elements + for element in elements do + begin + if element > 0 then + break + end + ensure + if elements.nil? then + puts "elements nil" + end + end + end +end + +def m3 elements + begin + if elements.nil? then + return + end + ensure + for element in elements do + begin + if x > 0 then + break + end + end + end + end + puts "Done" +end + +def m4 elements + for element in elements do + begin + if element > 1 then + raise "" + end + ensure + if element > 0 then + break 10; + end + end + end +end diff --git a/ruby/ql/test/library-tests/controlflow/graph/case.rb b/ruby/ql/test/library-tests/controlflow/graph/case.rb new file mode 100644 index 00000000000..97de48a9d42 --- /dev/null +++ b/ruby/ql/test/library-tests/controlflow/graph/case.rb @@ -0,0 +1,6 @@ +def if_in_case + case x1 + when 1 then (if x2 then puts "x2" end) + when 2 then puts "2" + end +end diff --git a/ruby/ql/test/library-tests/controlflow/graph/cfg.html.erb b/ruby/ql/test/library-tests/controlflow/graph/cfg.html.erb new file mode 100644 index 00000000000..aea9d7d73e3 --- /dev/null +++ b/ruby/ql/test/library-tests/controlflow/graph/cfg.html.erb @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title><%= @title %> + <%= stylesheet_link_tag "application", :media => "all" %> + + + +
    +
    + <%= link_to "A", a, id: "a" %> + +
    +
    + +
    + <% collection.each do |key, value| %> +
    <%= value %>
    + <% end %> +
    + + \ No newline at end of file diff --git a/ruby/ql/test/library-tests/controlflow/graph/cfg.rb b/ruby/ql/test/library-tests/controlflow/graph/cfg.rb new file mode 100644 index 00000000000..aa43ec6c9ae --- /dev/null +++ b/ruby/ql/test/library-tests/controlflow/graph/cfg.rb @@ -0,0 +1,199 @@ +def bar; end + +alias foo bar + +b = 42 + +%I(one#{ b } another) # bare symbol + +%W(one#{ b } another) # bare string + +begin + puts 4 +end + +BEGIN { + puts "hello" +} + +END { + puts "world" +} + +41 + 1 + +2.times { |x| puts x } + +puts &:puts + +Proc.new { |&x| x.call } + +while true + break 1 +end + +if false + puts "impossible" +end + +self.puts 42 + +case 10 + when 1 then puts "one" + when 2, 3, 4 then puts "some" + else puts "many" +end + +case + when b == 1 then puts "one" + when b == 0, b > 1 then puts "some" +end + +chained = "a" "#{chained}" "string" + +character = ?\x40 + + +# this is a class +class Silly < Object + complex = 10-2i + conditional = b < 10 ? "hello" : "bye" + C = "constant" + (x, (y, z)) = [1, [2, 3]] + def pattern( (a,b) ) + puts a + puts b + end + items = [1, 2, 3] + puts items[2] + def print() + puts "silly" + end +end + +x = 42 +if x < 0 then 0 elsif x > 10 then 10 else x end + +begin + ; # empty statement +rescue Exception, Exception2 => e + puts "oops" + retry +else + puts "ok" +ensure + puts "end" +end + +escape = "\u1234#{x}\n" + +for x in [1.4, 2.5, 3.4e5] do + if x > 3 then next end + puts x +end + +$global = 42 + +map1 = { 'a' => 'b', 'c': 'd', e: 'f' } +map2 = { **map1, 'x' => 'y', **map1} + + +def parameters(value = 42, key:, **kwargs) + puts value + return kwargs[key] +end + +type = "healthy" +table = "food" +puts (< 10 + +class C + @field = 42 + @@static_field = 10 +end + +swap = ->((x, y)) { [y, x] } + +module M + nothing = nil + some = 2 + some += 10 + last = (2; 4; 7) + range = 0..9 + half = 1/3r + 1/6r + regex = /hello\s+[#{range}]/ + Constant = 5 +end + +class EmptyClass; end +module EmptyModule; end + +1/0 rescue puts "div by zero" + +(*init, last) = 1, 2, 3 + +M::Constant +M.itself::Constant + +class << Silly.itself + def setter=() end + def print() + puts "singleton" + puts super.print() + end +end + +silly = Silly.new +def silly.method(*x) + puts x +end + +def two_parameters (a,b) end + +two_parameters(*[1,2]) + +scriptfile = `cat "#{__FILE__}"` + +symbol = :hello + +delimited_symbol = :"goodbye-#{ 12 + 13 }" + +x = true +x = ! true +x = - 42 + +undef two_parameters + +unless x == 10 then puts "hi" else puts "bye" end + +puts "hi" unless x == 0 + +until x > 10 do x += 10; puts "hello" end + +i = 0 +(puts "hello"; i += 1) until i == 10 + +x = 0 +while x < 10 do + x += 1 + if x == 5 then redo end + puts x +end + +(puts "hello"; i -= 1) while i != 0 + +def run_block + yield 42 +end + +run_block { |x|puts x } + +__END__ + +Some ignored nonsense + diff --git a/ruby/ql/test/library-tests/controlflow/graph/desugar.rb b/ruby/ql/test/library-tests/controlflow/graph/desugar.rb new file mode 100644 index 00000000000..68b36508d33 --- /dev/null +++ b/ruby/ql/test/library-tests/controlflow/graph/desugar.rb @@ -0,0 +1,38 @@ +def m1 x + x += 1 +end + +def m2 x + x.foo.count = 1 +end + +def m3 x + x.foo[0] = 1 +end + +def m4 x + x.foo.count += 1 +end + +def m5 x, y + x.foo[0, y.bar, x.baz + 3] += 1 +end + +def m6 + x, *y, z.bar = [1, 2, 3, 4] +end + +def m7 + x, (y, z) = [1, [2, 3]] +end + +class X + @x = 1 + @x += 2 + + @@y = 3 + @@y /= 4 +end + +$global_var = 5 +$global_var *= 6 diff --git a/ruby/ql/test/library-tests/controlflow/graph/exit.rb b/ruby/ql/test/library-tests/controlflow/graph/exit.rb new file mode 100644 index 00000000000..b67fa9e1532 --- /dev/null +++ b/ruby/ql/test/library-tests/controlflow/graph/exit.rb @@ -0,0 +1,13 @@ +def m1 x + if x > 2 + exit 1 + end + puts "x <= 2" +end + +def m2 x + if x > 2 + abort "abort!" + end + puts "x <= 2" +end diff --git a/ruby/ql/test/library-tests/controlflow/graph/heredoc.rb b/ruby/ql/test/library-tests/controlflow/graph/heredoc.rb new file mode 100644 index 00000000000..09e1b771ea8 --- /dev/null +++ b/ruby/ql/test/library-tests/controlflow/graph/heredoc.rb @@ -0,0 +1,7 @@ +def double_heredoc + puts(< 2 + puts "x is greater than 2" + elsif x <= 2 and x > 0 and !(x == 5) + puts "x is 1" + else + puts "I can't guess the number" + end +end + +def m2 b + if b + return 0 + end + return 1 +end + +def m3 x + if x < 0 + x = -x + if x > 10 + x = x - 1 + end + end + puts x +end + +def m4 (b1, b2, b3) + return (b1 ? b2 : b3) ? "b2 || b3" : "!b2 || !b3" +end + +def m5 (b1, b2, b3, b4, b5) + if (if b1 then b2 elsif b3 then b4 else b5 end) then "b2 || b4 || b5" else "!b2 || !b4 || !b5" end +end + +def conditional_method_def() + puts "bla" +end unless 1 == 2 + +def constant_condition() + if !true + puts "Impossible" + end +end + +def empty_else b + if b then + puts "true" + else + end + puts "done" +end \ No newline at end of file diff --git a/ruby/ql/test/library-tests/controlflow/graph/loops.rb b/ruby/ql/test/library-tests/controlflow/graph/loops.rb new file mode 100644 index 00000000000..b3f1c60557a --- /dev/null +++ b/ruby/ql/test/library-tests/controlflow/graph/loops.rb @@ -0,0 +1,33 @@ +def m1 x + while x >= 0 + puts x + x -= 1 + end +end + +def m2 x + while x >= 0 + puts x + x -= 1 + if x > 100 + break + elsif x > 50 + next + elsif x > 10 + redo + end + puts "Iter" + end + puts "Done" +end + +def m3 + [1,2,3].each do |x| + puts x + end +end + +def m4(x, y) + while x < y do + end +end diff --git a/ruby/ql/test/library-tests/controlflow/graph/raise.rb b/ruby/ql/test/library-tests/controlflow/graph/raise.rb new file mode 100644 index 00000000000..e5f0c0e50f5 --- /dev/null +++ b/ruby/ql/test/library-tests/controlflow/graph/raise.rb @@ -0,0 +1,170 @@ +class ExceptionA < Exception +end + +class ExceptionB < Exception +end + +def m1 x + if x > 2 + raise "x > 2" + end + puts "x <= 2" +end + +def m2 b + begin + if b + raise ExceptionA + end + rescue ExceptionA + puts "Rescued" + end + puts "End m2" +end + +def m3 b + begin + if b + raise ExceptionA + end + rescue + puts "Rescued" + end + puts "End m3" +end + +def m4 b + begin + if b + raise ExceptionA + end + rescue => e + puts "Rescued {e}" + end + puts "End m4" +end + +def m5 b + begin + if b + raise ExceptionA + end + rescue => e + end + puts "End m5" +end + +def m6 b + begin + if b + raise ExceptionA + end + rescue ExceptionA, ExceptionB => e + puts "Rescued {e}" + end + puts "End m6" +end + +def m7 x + if x > 2 + raise "x > 2" + elsif x < 0 + return "x < 0" + end + puts "0 <= x <= 2" +ensure + puts "ensure" +end + +def m8 x + puts "Begin m8" + begin + if x > 2 + raise "x > 2" + elsif x < 0 + return "x < 0" + end + puts "0 <= x <= 2" + ensure + puts "ensure" + end + puts "End m8" +end + +def m9(x, b1, b2) + puts "Begin m9" + begin + if x > 2 + raise "x > 2" + elsif x < 0 + return "x < 0" + end + puts "0 <= x <= 2" + ensure + puts "outer ensure" + begin + if b1 + raise "b1 is true" + end + ensure + puts "inner ensure" + end + end + puts "End m9" +ensure + puts "method ensure" + if b2 + raise "b2 is true" + end +end + +def m10(p = (raise "Exception")) +rescue + puts "Will not get executed if p is not supplied" +ensure + puts "Will not get executed if p is not supplied" +end + +def m11 b + begin + if b + raise ExceptionA + end + rescue ExceptionA + rescue ExceptionB + puts "ExceptionB" + ensure + puts "Ensure" + end + puts "End m11" +end + +def m12 b + if b + raise "" + end +ensure + return 3 +end + +def m13 +ensure +end + +def m14 element + element.each { |elem| raise "" if element.nil? } +end + +def m15 + foo do + bar ->(x) do + raise "" unless x + end + end +end + +class C + def self.m() + raise "" + end +end diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/test1.rb b/ruby/ql/test/library-tests/dataflow/api-graphs/test1.rb new file mode 100644 index 00000000000..752444ad2a7 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/api-graphs/test1.rb @@ -0,0 +1,31 @@ +MyModule #$ use=getMember("MyModule") +print MyModule.foo #$ use=getMember("MyModule").getReturn("foo") +Kernel.print(e) #$ use=getMember("Kernel").getReturn("print") +Object::Kernel #$ use=getMember("Kernel") +Object::Kernel.print(e) #$ use=getMember("Kernel").getReturn("print") +begin + print MyModule.bar #$ use=getMember("MyModule").getReturn("bar") + raise AttributeError #$ use=getMember("AttributeError") +rescue AttributeError => e #$ use=getMember("AttributeError") + Kernel.print(e) #$ use=getMember("Kernel").getReturn("print") +end +Unknown.new.run #$ use=getMember("Unknown").instance.getReturn("run") +Foo::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz") + +Const = [1, 2, 3] #$ use=getMember("Array").getReturn("[]") +Const.each do |c| #$ use=getMember("Const").getReturn("each") + puts c +end + +foo = Foo #$ use=getMember("Foo") +foo::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz") + +FooAlias = Foo #$ use=getMember("Foo") +FooAlias::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz") + +module Outer + module Inner + end +end + +Outer::Inner.foo #$ use=getMember("Outer").getMember("Inner").getReturn("foo") diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/use.expected b/ruby/ql/test/library-tests/dataflow/api-graphs/use.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/use.ql b/ruby/ql/test/library-tests/dataflow/api-graphs/use.ql new file mode 100644 index 00000000000..0ca7454109a --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/api-graphs/use.ql @@ -0,0 +1,35 @@ +import ruby +import codeql.ruby.DataFlow +import TestUtilities.InlineExpectationsTest +import codeql.ruby.ApiGraphs + +class ApiUseTest extends InlineExpectationsTest { + ApiUseTest() { this = "ApiUseTest" } + + override string getARelevantTag() { result = "use" } + + private predicate relevantNode(API::Node a, DataFlow::Node n, Location l) { + n = a.getAUse() and + l = n.getLocation() + } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + exists(API::Node a, DataFlow::Node n | relevantNode(a, n, location) | + tag = "use" and + // Only report the longest path on this line: + value = + max(API::Node a2, Location l2, DataFlow::Node n2 | + relevantNode(a2, n2, l2) and + l2.getFile() = location.getFile() and + l2.getStartLine() = location.getStartLine() + | + a2.getPath() + order by + size(n2.asExpr().getExpr()), a2.getPath().length() desc, a2.getPath() desc + ) and + element = n.toString() + ) + } +} + +private int size(AstNode n) { not n instanceof StmtSequence and result = count(n.getAChild*()) } 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 new file mode 100644 index 00000000000..e0940df3d03 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected @@ -0,0 +1,5 @@ +| barrier-guards.rb:3:4:3:15 | ... == ... | barrier-guards.rb:4:5:4:7 | foo | barrier-guards.rb:3:4:3:6 | foo | true | +| barrier-guards.rb:9:4:9:24 | call to include? | barrier-guards.rb:10:5:10:7 | foo | barrier-guards.rb:9:21:9:23 | foo | true | +| barrier-guards.rb:15:4:15:15 | ... != ... | barrier-guards.rb:18:5:18:7 | foo | barrier-guards.rb:15:4:15:6 | foo | false | +| barrier-guards.rb:21:8:21:19 | ... == ... | barrier-guards.rb:24:5:24:7 | foo | barrier-guards.rb:21:8:21:10 | foo | true | +| barrier-guards.rb:27:8:27:19 | ... != ... | barrier-guards.rb:28:5:28:7 | foo | barrier-guards.rb:27:8:27:10 | foo | false | diff --git a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.ql b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.ql new file mode 100644 index 00000000000..d56f6986c8d --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.ql @@ -0,0 +1,7 @@ +import codeql.ruby.dataflow.internal.DataFlowPublic +import codeql.ruby.dataflow.BarrierGuards +import codeql.ruby.controlflow.CfgNodes + +from BarrierGuard g, boolean branch, ExprCfgNode expr +where g.checks(expr, branch) +select g, g.getAGuardedNode(), expr, branch diff --git a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb new file mode 100644 index 00000000000..2a26bbb3ae0 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb @@ -0,0 +1,33 @@ +foo = "foo" + +if foo == "foo" + foo +else + foo +end + +if ["foo"].include?(foo) + foo +else + foo +end + +if foo != "foo" + foo +else + foo +end + +unless foo == "foo" + foo +else + foo +end + +unless foo != "foo" + foo +else + foo +end + +foo diff --git a/ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.expected b/ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.expected new file mode 100644 index 00000000000..ed1dcf79a3c --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.expected @@ -0,0 +1,36 @@ +edges +| call_sensitivity.rb:7:13:7:13 | x : | call_sensitivity.rb:8:11:8:11 | x : | +| call_sensitivity.rb:8:11:8:11 | x : | call_sensitivity.rb:15:20:15:20 | x : | +| call_sensitivity.rb:15:9:15:15 | "taint" : | call_sensitivity.rb:7:13:7:13 | x : | +| call_sensitivity.rb:15:20:15:20 | x : | call_sensitivity.rb:15:28:15:28 | x | +| call_sensitivity.rb:17:27:17:27 | x : | call_sensitivity.rb:18:17:18:17 | x : | +| call_sensitivity.rb:17:27:17:27 | x : | call_sensitivity.rb:18:17:18:17 | x : | +| call_sensitivity.rb:18:17:18:17 | x : | call_sensitivity.rb:27:17:27:17 | x : | +| call_sensitivity.rb:18:17:18:17 | x : | call_sensitivity.rb:36:23:36:23 | x : | +| call_sensitivity.rb:27:17:27:17 | x : | call_sensitivity.rb:27:27:27:27 | x | +| call_sensitivity.rb:28:25:28:31 | "taint" : | call_sensitivity.rb:17:27:17:27 | x : | +| call_sensitivity.rb:36:23:36:23 | x : | call_sensitivity.rb:36:31:36:31 | x | +| call_sensitivity.rb:37:25:37:31 | "taint" : | call_sensitivity.rb:17:27:17:27 | x : | +nodes +| call_sensitivity.rb:5:6:5:12 | "taint" | semmle.label | "taint" | +| call_sensitivity.rb:7:13:7:13 | x : | semmle.label | x : | +| call_sensitivity.rb:8:11:8:11 | x : | semmle.label | x : | +| call_sensitivity.rb:15:9:15:15 | "taint" : | semmle.label | "taint" : | +| call_sensitivity.rb:15:20:15:20 | x : | semmle.label | x : | +| call_sensitivity.rb:15:28:15:28 | x | semmle.label | x | +| call_sensitivity.rb:17:27:17:27 | x : | semmle.label | x : | +| call_sensitivity.rb:17:27:17:27 | x : | semmle.label | x : | +| call_sensitivity.rb:18:17:18:17 | x : | semmle.label | x : | +| call_sensitivity.rb:18:17:18:17 | x : | semmle.label | x : | +| call_sensitivity.rb:27:17:27:17 | x : | semmle.label | x : | +| call_sensitivity.rb:27:27:27:27 | x | semmle.label | x | +| call_sensitivity.rb:28:25:28:31 | "taint" : | semmle.label | "taint" : | +| call_sensitivity.rb:36:23:36:23 | x : | semmle.label | x : | +| call_sensitivity.rb:36:31:36:31 | x | semmle.label | x | +| call_sensitivity.rb:37:25:37:31 | "taint" : | semmle.label | "taint" : | +subpaths +#select +| call_sensitivity.rb:5:6:5:12 | "taint" | call_sensitivity.rb:5:6:5:12 | "taint" | call_sensitivity.rb:5:6:5:12 | "taint" | $@ | call_sensitivity.rb:5:6:5:12 | "taint" | "taint" | +| call_sensitivity.rb:15:28:15:28 | x | call_sensitivity.rb:15:9:15:15 | "taint" : | call_sensitivity.rb:15:28:15:28 | x | $@ | call_sensitivity.rb:15:9:15:15 | "taint" : | "taint" : | +| call_sensitivity.rb:27:27:27:27 | x | call_sensitivity.rb:28:25:28:31 | "taint" : | call_sensitivity.rb:27:27:27:27 | x | $@ | call_sensitivity.rb:28:25:28:31 | "taint" : | "taint" : | +| call_sensitivity.rb:36:31:36:31 | x | call_sensitivity.rb:37:25:37:31 | "taint" : | call_sensitivity.rb:36:31:36:31 | x | $@ | call_sensitivity.rb:37:25:37:31 | "taint" : | "taint" : | diff --git a/ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.ql b/ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.ql new file mode 100644 index 00000000000..f6974ed0fca --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.ql @@ -0,0 +1,26 @@ +/** + * @kind path-problem + */ + +import ruby +import codeql.ruby.DataFlow +import DataFlow::PathGraph + +class Conf extends DataFlow::Configuration { + Conf() { this = "Conf" } + + override predicate isSource(DataFlow::Node src) { + src.asExpr().getExpr().(StringLiteral).getValueText() = "taint" + } + + override predicate isSink(DataFlow::Node sink) { + exists(MethodCall mc | + mc.getMethodName() = "sink" and + mc.getAnArgument() = sink.asExpr().getExpr() + ) + } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, Conf conf +where conf.hasFlowPath(source, sink) +select sink, source, sink, "$@", source, source.toString() diff --git a/ruby/ql/test/library-tests/dataflow/call-sensitivity/call_sensitivity.rb b/ruby/ql/test/library-tests/dataflow/call-sensitivity/call_sensitivity.rb new file mode 100644 index 00000000000..e6c7117d859 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/call-sensitivity/call_sensitivity.rb @@ -0,0 +1,38 @@ +def sink s + puts s +end + +sink "taint" + +def yielder x + yield x +end + +yielder "no taint" { |x| sink x } # no flow + +yielder "taint" { |x| puts x } # no flow + +yielder "taint" { |x| sink x } # flow + +def apply_lambda (lambda, x) + lambda.call(x) +end + +my_lambda = -> (x) { sink x } +apply_lambda(my_lambda, "no taint") # no flow + +my_lambda = -> (x) { puts x } +apply_lambda(my_lambda, "taint") # no flow + +my_lambda = -> (x) { sink x } +apply_lambda(my_lambda, "taint") # flow + +my_lambda = lambda { |x| sink x } +apply_lambda(my_lambda, "no taint") # no flow + +my_lambda = lambda { |x| puts x } +apply_lambda(my_lambda, "taint") # no flow + +my_lambda = lambda { |x| sink x } +apply_lambda(my_lambda, "taint") # flow + diff --git a/ruby/ql/test/library-tests/dataflow/local/DataflowStep.expected b/ruby/ql/test/library-tests/dataflow/local/DataflowStep.expected new file mode 100644 index 00000000000..7496d7b5597 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/local/DataflowStep.expected @@ -0,0 +1,69 @@ +| local_dataflow.rb:1:1:7:3 | self (foo) | local_dataflow.rb:3:8:3:10 | self | +| local_dataflow.rb:1:1:7:3 | self (local_dataflow.rb) | local_dataflow.rb:11:1:11:2 | self | +| local_dataflow.rb:1:1:7:3 | self (local_dataflow.rb) | local_dataflow.rb:49:1:53:3 | self | +| local_dataflow.rb:1:1:7:3 | self in foo | local_dataflow.rb:3:8:3:10 | self | +| local_dataflow.rb:1:9:1:9 | a | local_dataflow.rb:1:9:1:9 | a | +| local_dataflow.rb:1:9:1:9 | a | local_dataflow.rb:2:7:2:7 | a | +| local_dataflow.rb:2:3:2:7 | ... = ... | local_dataflow.rb:3:13:3:13 | b | +| local_dataflow.rb:2:7:2:7 | a | local_dataflow.rb:2:3:2:7 | ... = ... | +| local_dataflow.rb:2:7:2:7 | a | local_dataflow.rb:2:3:2:7 | ... = ... | +| local_dataflow.rb:2:7:2:7 | a | local_dataflow.rb:3:10:3:10 | a | +| local_dataflow.rb:3:7:3:14 | ( ... ) | local_dataflow.rb:3:3:3:14 | ... = ... | +| local_dataflow.rb:3:10:3:10 | [post] a | local_dataflow.rb:4:11:4:11 | a | +| local_dataflow.rb:3:10:3:10 | a | local_dataflow.rb:4:11:4:11 | a | +| local_dataflow.rb:3:13:3:13 | b | local_dataflow.rb:3:7:3:14 | ( ... ) | +| local_dataflow.rb:3:13:3:13 | b | local_dataflow.rb:6:13:6:13 | b | +| local_dataflow.rb:4:7:4:11 | ... = ... | local_dataflow.rb:4:3:4:11 | ... = ... | +| local_dataflow.rb:4:11:4:11 | a | local_dataflow.rb:4:7:4:11 | ... = ... | +| local_dataflow.rb:4:11:4:11 | a | local_dataflow.rb:5:12:5:12 | a | +| local_dataflow.rb:5:7:5:13 | ( ... ) | local_dataflow.rb:5:3:5:13 | ... = ... | +| local_dataflow.rb:5:8:5:12 | ... = ... | local_dataflow.rb:5:7:5:13 | ( ... ) | +| local_dataflow.rb:5:12:5:12 | a | local_dataflow.rb:5:8:5:12 | ... = ... | +| local_dataflow.rb:5:12:5:12 | a | local_dataflow.rb:6:8:6:8 | a | +| local_dataflow.rb:6:7:6:14 | ( ... ) | local_dataflow.rb:6:3:6:14 | ... = ... | +| local_dataflow.rb:6:8:6:13 | ... = ... | local_dataflow.rb:6:7:6:14 | ( ... ) | +| local_dataflow.rb:6:10:6:11 | ... + ... | local_dataflow.rb:6:8:6:13 | ... = ... | +| local_dataflow.rb:9:1:9:15 | ... = ... | local_dataflow.rb:10:14:10:18 | array | +| local_dataflow.rb:9:9:9:15 | call to [] | local_dataflow.rb:9:1:9:15 | ... = ... | +| local_dataflow.rb:9:9:9:15 | call to [] | local_dataflow.rb:9:1:9:15 | ... = ... | +| local_dataflow.rb:10:5:13:3 | for ... in ... | local_dataflow.rb:10:1:13:3 | ... = ... | +| local_dataflow.rb:10:9:10:9 | x | local_dataflow.rb:12:5:12:5 | x | +| local_dataflow.rb:10:14:10:18 | array | local_dataflow.rb:10:5:13:3 | for ... in ... | +| local_dataflow.rb:10:14:10:18 | array | local_dataflow.rb:15:10:15:14 | array | +| local_dataflow.rb:11:1:11:2 | [post] self | local_dataflow.rb:12:3:12:5 | self | +| local_dataflow.rb:11:1:11:2 | self | local_dataflow.rb:12:3:12:5 | self | +| local_dataflow.rb:12:3:12:5 | [post] self | local_dataflow.rb:11:1:11:2 | self | +| local_dataflow.rb:12:3:12:5 | [post] self | local_dataflow.rb:49:1:53:3 | self | +| local_dataflow.rb:12:3:12:5 | call to p | local_dataflow.rb:10:19:13:3 | do ... | +| local_dataflow.rb:12:3:12:5 | self | local_dataflow.rb:11:1:11:2 | self | +| local_dataflow.rb:12:3:12:5 | self | local_dataflow.rb:49:1:53:3 | self | +| local_dataflow.rb:15:10:15:14 | array | local_dataflow.rb:15:1:17:3 | for ... in ... | +| local_dataflow.rb:15:10:15:14 | array | local_dataflow.rb:19:10:19:14 | array | +| local_dataflow.rb:16:3:16:10 | break | local_dataflow.rb:15:1:17:3 | for ... in ... | +| local_dataflow.rb:16:9:16:10 | 10 | local_dataflow.rb:16:3:16:10 | break | +| local_dataflow.rb:19:5:19:5 | x | local_dataflow.rb:20:6:20:6 | x | +| local_dataflow.rb:19:10:19:14 | array | local_dataflow.rb:19:1:21:3 | for ... in ... | +| local_dataflow.rb:20:3:20:25 | if ... | local_dataflow.rb:19:16:21:3 | do ... | +| local_dataflow.rb:20:17:20:21 | break | local_dataflow.rb:19:1:21:3 | for ... in ... | +| local_dataflow.rb:24:2:24:8 | break | local_dataflow.rb:23:1:25:3 | while ... | +| local_dataflow.rb:24:8:24:8 | 5 | local_dataflow.rb:24:2:24:8 | break | +| local_dataflow.rb:28:5:28:26 | M | local_dataflow.rb:28:1:28:26 | ... = ... | +| local_dataflow.rb:28:15:28:22 | "module" | local_dataflow.rb:28:5:28:26 | M | +| local_dataflow.rb:30:5:30:24 | C | local_dataflow.rb:30:1:30:24 | ... = ... | +| local_dataflow.rb:30:14:30:20 | "class" | local_dataflow.rb:30:5:30:24 | C | +| local_dataflow.rb:32:5:32:25 | bar | local_dataflow.rb:32:1:32:25 | ... = ... | +| local_dataflow.rb:32:5:32:25 | bar | local_dataflow.rb:32:1:32:25 | ... = ... | +| local_dataflow.rb:34:7:34:7 | x | local_dataflow.rb:34:7:34:7 | x | +| local_dataflow.rb:34:7:34:7 | x | local_dataflow.rb:35:6:35:6 | x | +| local_dataflow.rb:36:13:36:13 | 7 | local_dataflow.rb:36:6:36:13 | return | +| local_dataflow.rb:41:7:41:7 | x | local_dataflow.rb:41:7:41:7 | x | +| local_dataflow.rb:41:7:41:7 | x | local_dataflow.rb:42:6:42:6 | x | +| local_dataflow.rb:43:13:43:13 | 7 | local_dataflow.rb:43:6:43:13 | return | +| local_dataflow.rb:45:10:45:10 | 6 | local_dataflow.rb:45:3:45:10 | return | +| local_dataflow.rb:49:1:53:3 | [post] self | local_dataflow.rb:55:1:55:14 | self | +| local_dataflow.rb:49:1:53:3 | self | local_dataflow.rb:55:1:55:14 | self | +| local_dataflow.rb:49:3:53:3 | | local_dataflow.rb:50:18:50:18 | x | +| local_dataflow.rb:50:8:50:13 | "next" | local_dataflow.rb:50:3:50:13 | next | +| local_dataflow.rb:50:18:50:18 | [post] x | local_dataflow.rb:51:20:51:20 | x | +| local_dataflow.rb:50:18:50:18 | x | local_dataflow.rb:51:20:51:20 | x | +| local_dataflow.rb:51:9:51:15 | "break" | local_dataflow.rb:51:3:51:15 | break | diff --git a/ruby/ql/test/library-tests/dataflow/local/DataflowStep.ql b/ruby/ql/test/library-tests/dataflow/local/DataflowStep.ql new file mode 100644 index 00000000000..872625f39a7 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/local/DataflowStep.ql @@ -0,0 +1,6 @@ +import ruby +import codeql.ruby.DataFlow + +from DataFlow::Node pred, DataFlow::Node succ +where DataFlow::localFlowStep(pred, succ) +select pred, succ diff --git a/ruby/ql/test/library-tests/dataflow/local/Nodes.expected b/ruby/ql/test/library-tests/dataflow/local/Nodes.expected new file mode 100644 index 00000000000..6defc9d0e75 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/local/Nodes.expected @@ -0,0 +1,40 @@ +ret +| local_dataflow.rb:6:3:6:14 | ... = ... | +| local_dataflow.rb:32:14:32:21 | "method" | +| local_dataflow.rb:36:6:36:13 | return | +| local_dataflow.rb:38:3:38:13 | "reachable" | +| local_dataflow.rb:43:6:43:13 | return | +| local_dataflow.rb:45:3:45:10 | return | +| local_dataflow.rb:50:3:50:13 | next | +| local_dataflow.rb:51:3:51:15 | break | +| local_dataflow.rb:52:3:52:10 | "normal" | +arg +| local_dataflow.rb:3:8:3:10 | self | local_dataflow.rb:3:8:3:10 | call to p | -1 | +| local_dataflow.rb:3:10:3:10 | a | local_dataflow.rb:3:8:3:10 | call to p | 0 | +| local_dataflow.rb:6:8:6:8 | a | local_dataflow.rb:6:10:6:11 | ... + ... | -1 | +| local_dataflow.rb:6:13:6:13 | b | local_dataflow.rb:6:10:6:11 | ... + ... | 0 | +| local_dataflow.rb:9:9:9:15 | Array | local_dataflow.rb:9:9:9:15 | call to [] | -1 | +| local_dataflow.rb:9:10:9:10 | 1 | local_dataflow.rb:9:9:9:15 | call to [] | 0 | +| local_dataflow.rb:9:12:9:12 | 2 | local_dataflow.rb:9:9:9:15 | call to [] | 1 | +| local_dataflow.rb:9:14:9:14 | 3 | local_dataflow.rb:9:9:9:15 | call to [] | 2 | +| local_dataflow.rb:11:1:11:2 | self | local_dataflow.rb:11:1:11:2 | call to do | -1 | +| local_dataflow.rb:12:3:12:5 | self | local_dataflow.rb:12:3:12:5 | call to p | -1 | +| local_dataflow.rb:12:5:12:5 | x | local_dataflow.rb:12:3:12:5 | call to p | 0 | +| local_dataflow.rb:20:6:20:6 | x | local_dataflow.rb:20:6:20:10 | ... > ... | -1 | +| local_dataflow.rb:20:10:20:10 | 1 | local_dataflow.rb:20:6:20:10 | ... > ... | 0 | +| local_dataflow.rb:35:6:35:6 | x | local_dataflow.rb:35:6:35:11 | ... == ... | -1 | +| local_dataflow.rb:35:11:35:11 | 4 | local_dataflow.rb:35:6:35:11 | ... == ... | 0 | +| local_dataflow.rb:42:6:42:6 | x | local_dataflow.rb:42:6:42:11 | ... == ... | -1 | +| local_dataflow.rb:42:11:42:11 | 4 | local_dataflow.rb:42:6:42:11 | ... == ... | 0 | +| local_dataflow.rb:49:1:53:3 | self | local_dataflow.rb:49:1:53:3 | call to m | -1 | +| local_dataflow.rb:49:3:53:3 | do ... end | local_dataflow.rb:49:1:53:3 | call to m | -2 | +| local_dataflow.rb:50:18:50:18 | x | local_dataflow.rb:50:18:50:22 | ... < ... | -1 | +| local_dataflow.rb:50:22:50:22 | 4 | local_dataflow.rb:50:18:50:22 | ... < ... | 0 | +| local_dataflow.rb:51:20:51:20 | x | local_dataflow.rb:51:20:51:24 | ... < ... | -1 | +| local_dataflow.rb:51:24:51:24 | 9 | local_dataflow.rb:51:20:51:24 | ... < ... | 0 | +| local_dataflow.rb:55:1:55:14 | self | local_dataflow.rb:55:1:55:14 | call to foo | -1 | +| local_dataflow.rb:55:5:55:13 | Array | local_dataflow.rb:55:5:55:13 | call to [] | -1 | +| local_dataflow.rb:55:5:55:13 | call to [] | local_dataflow.rb:55:1:55:14 | call to foo | 0 | +| local_dataflow.rb:55:6:55:6 | 1 | local_dataflow.rb:55:5:55:13 | call to [] | 0 | +| local_dataflow.rb:55:9:55:9 | 2 | local_dataflow.rb:55:5:55:13 | call to [] | 1 | +| local_dataflow.rb:55:12:55:12 | 3 | local_dataflow.rb:55:5:55:13 | call to [] | 2 | diff --git a/ruby/ql/test/library-tests/dataflow/local/Nodes.ql b/ruby/ql/test/library-tests/dataflow/local/Nodes.ql new file mode 100644 index 00000000000..520c812b25d --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/local/Nodes.ql @@ -0,0 +1,7 @@ +import ruby +import codeql.ruby.dataflow.internal.DataFlowPrivate +import codeql.ruby.dataflow.internal.DataFlowDispatch + +query predicate ret(ReturningNode node) { any() } + +query predicate arg(ArgumentNode n, DataFlowCall call, int pos) { n.argumentOf(call, pos) } diff --git a/ruby/ql/test/library-tests/dataflow/local/local_dataflow.rb b/ruby/ql/test/library-tests/dataflow/local/local_dataflow.rb new file mode 100644 index 00000000000..2c413bfeadc --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/local/local_dataflow.rb @@ -0,0 +1,58 @@ +def foo(a) + b = a + c = (p a; b) + d = c = a + d = (c = a) + e = (a += b) +end + +array = [1,2,3] +y = for x in array +do + p x +end + +for x in array do + break 10 +end + +for x in array do + if x > 1 then break end +end + +while true + break 5 +end + +# string flows to x +x = module M; "module" end +# string flows to x +x = class C; "class" end +# string does not flow to x because "def" evaluates to a method symbol +x = def bar; "method" end + +def m x + if x == 4 + return 7 + end + "reachable" +end + +def m x + if x == 4 + return 7 + end + return 6 + "unreachable" +end + +m do + next "next" if x < 4 + break "break" if x < 9 + "normal" +end + +foo([1, 2, 3]) + +def foo x +end diff --git a/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected new file mode 100644 index 00000000000..a3536938432 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected @@ -0,0 +1,35 @@ +edges +| summaries.rb:1:11:1:26 | call to identity : | summaries.rb:2:6:2:12 | tainted | +| summaries.rb:1:11:1:26 | call to identity : | summaries.rb:4:24:4:30 | tainted : | +| summaries.rb:1:11:1:26 | call to identity : | summaries.rb:16:36:16:42 | tainted : | +| summaries.rb:1:20:1:26 | "taint" : | summaries.rb:1:11:1:26 | call to identity : | +| summaries.rb:4:12:7:3 | call to apply_block : | summaries.rb:9:6:9:13 | tainted2 | +| summaries.rb:4:24:4:30 | tainted : | summaries.rb:4:12:7:3 | call to apply_block : | +| summaries.rb:4:24:4:30 | tainted : | summaries.rb:4:36:4:36 | x : | +| summaries.rb:4:36:4:36 | x : | summaries.rb:5:8:5:8 | x | +| summaries.rb:11:17:11:17 | x : | summaries.rb:12:8:12:8 | x | +| summaries.rb:16:12:16:43 | call to apply_lambda : | summaries.rb:18:6:18:13 | tainted3 | +| summaries.rb:16:36:16:42 | tainted : | summaries.rb:11:17:11:17 | x : | +| summaries.rb:16:36:16:42 | tainted : | summaries.rb:16:12:16:43 | call to apply_lambda : | +nodes +| summaries.rb:1:11:1:26 | call to identity : | semmle.label | call to identity : | +| summaries.rb:1:20:1:26 | "taint" : | semmle.label | "taint" : | +| summaries.rb:2:6:2:12 | tainted | semmle.label | tainted | +| summaries.rb:4:12:7:3 | call to apply_block : | semmle.label | call to apply_block : | +| summaries.rb:4:24:4:30 | tainted : | semmle.label | tainted : | +| summaries.rb:4:36:4:36 | x : | semmle.label | x : | +| summaries.rb:5:8:5:8 | x | semmle.label | x | +| summaries.rb:9:6:9:13 | tainted2 | semmle.label | tainted2 | +| summaries.rb:11:17:11:17 | x : | semmle.label | x : | +| summaries.rb:12:8:12:8 | x | semmle.label | x | +| summaries.rb:16:12:16:43 | call to apply_lambda : | semmle.label | call to apply_lambda : | +| summaries.rb:16:36:16:42 | tainted : | semmle.label | tainted : | +| summaries.rb:18:6:18:13 | tainted3 | semmle.label | tainted3 | +subpaths +invalidSpecComponent +#select +| summaries.rb:2:6:2:12 | tainted | summaries.rb:1:20:1:26 | "taint" : | summaries.rb:2:6:2:12 | tainted | $@ | summaries.rb:1:20:1:26 | "taint" : | "taint" : | +| summaries.rb:5:8:5:8 | x | summaries.rb:1:20:1:26 | "taint" : | summaries.rb:5:8:5:8 | x | $@ | summaries.rb:1:20:1:26 | "taint" : | "taint" : | +| summaries.rb:9:6:9:13 | tainted2 | summaries.rb:1:20:1:26 | "taint" : | summaries.rb:9:6:9:13 | tainted2 | $@ | summaries.rb:1:20:1:26 | "taint" : | "taint" : | +| summaries.rb:12:8:12:8 | x | summaries.rb:1:20:1:26 | "taint" : | summaries.rb:12:8:12:8 | x | $@ | summaries.rb:1:20:1:26 | "taint" : | "taint" : | +| summaries.rb:18:6:18:13 | tainted3 | summaries.rb:1:20:1:26 | "taint" : | summaries.rb:18:6:18:13 | tainted3 | $@ | summaries.rb:1:20:1:26 | "taint" : | "taint" : | diff --git a/ruby/ql/test/library-tests/dataflow/summaries/Summaries.ql b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.ql new file mode 100644 index 00000000000..6d9db3f5c82 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.ql @@ -0,0 +1,77 @@ +/** + * @kind path-problem + */ + +import ruby +import codeql.ruby.dataflow.FlowSummary +import DataFlow::PathGraph +import codeql.ruby.TaintTracking +import codeql.ruby.dataflow.internal.FlowSummaryImpl + +query predicate invalidSpecComponent(SummarizedCallable sc, string s, string c) { + (sc.propagatesFlowExt(s, _, _) or sc.propagatesFlowExt(_, s, _)) and + Private::External::invalidSpecComponent(s, c) +} + +private class SummarizedCallableIdentity extends SummarizedCallable { + SummarizedCallableIdentity() { this = "identity" } + + override MethodCall getACall() { result.getMethodName() = this } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[0]" and + output = "ReturnValue" and + preservesValue = true + } +} + +private class SummarizedCallableApplyBlock extends SummarizedCallable { + SummarizedCallableApplyBlock() { this = "apply_block" } + + override MethodCall getACall() { result.getMethodName() = this } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[0]" and + output = "Parameter[0] of BlockArgument" and + preservesValue = true + or + input = "ReturnValue of BlockArgument" and + output = "ReturnValue" and + preservesValue = true + } +} + +private class SummarizedCallableApplyLambda extends SummarizedCallable { + SummarizedCallableApplyLambda() { this = "apply_lambda" } + + override MethodCall getACall() { result.getMethodName() = this } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[1]" and + output = "Parameter[0] of Argument[0]" and + preservesValue = true + or + input = "ReturnValue of Argument[0]" and + output = "ReturnValue" and + preservesValue = true + } +} + +class Conf extends TaintTracking::Configuration { + Conf() { this = "FlowSummaries" } + + override predicate isSource(DataFlow::Node src) { + src.asExpr().getExpr().(StringLiteral).getValueText() = "taint" + } + + override predicate isSink(DataFlow::Node sink) { + exists(MethodCall mc | + mc.getMethodName() = "sink" and + mc.getAnArgument() = sink.asExpr().getExpr() + ) + } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, Conf conf +where conf.hasFlowPath(source, sink) +select sink, source, sink, "$@", source, source.toString() diff --git a/ruby/ql/test/library-tests/dataflow/summaries/summaries.rb b/ruby/ql/test/library-tests/dataflow/summaries/summaries.rb new file mode 100644 index 00000000000..08851ae2936 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/summaries/summaries.rb @@ -0,0 +1,18 @@ +tainted = identity "taint" +sink tainted + +tainted2 = apply_block tainted do |x| + sink x + x +end + +sink tainted2 + +my_lambda = -> (x) { + sink x + x +} + +tainted3 = apply_lambda(my_lambda, tainted) + +sink(tainted3) diff --git a/ruby/ql/test/library-tests/frameworks/ActionController.expected b/ruby/ql/test/library-tests/frameworks/ActionController.expected new file mode 100644 index 00000000000..bbbcde90ee2 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/ActionController.expected @@ -0,0 +1,60 @@ +actionControllerControllerClasses +| ActiveRecordInjection.rb:27:1:58:3 | FooController | +| ActiveRecordInjection.rb:60:1:90:3 | BarController | +| ActiveRecordInjection.rb:92:1:96:3 | BazController | +| app/controllers/foo/bars_controller.rb:1:1:20:3 | BarsController | +actionControllerActionMethods +| ActiveRecordInjection.rb:32:3:57:5 | some_request_handler | +| ActiveRecordInjection.rb:61:3:69:5 | some_other_request_handler | +| ActiveRecordInjection.rb:71:3:89:5 | safe_paths | +| ActiveRecordInjection.rb:93:3:95:5 | yet_another_handler | +| app/controllers/foo/bars_controller.rb:3:3:5:5 | index | +| app/controllers/foo/bars_controller.rb:7:3:13:5 | show_debug | +| app/controllers/foo/bars_controller.rb:15:3:19:5 | show | +paramsCalls +| ActiveRecordInjection.rb:35:30:35:35 | call to params | +| ActiveRecordInjection.rb:39:29:39:34 | call to params | +| ActiveRecordInjection.rb:43:31:43:36 | call to params | +| ActiveRecordInjection.rb:48:21:48:26 | call to params | +| ActiveRecordInjection.rb:54:34:54:39 | call to params | +| ActiveRecordInjection.rb:56:23:56:28 | call to params | +| ActiveRecordInjection.rb:56:38:56:43 | call to params | +| ActiveRecordInjection.rb:62:10:62:15 | call to params | +| ActiveRecordInjection.rb:72:11:72:16 | call to params | +| ActiveRecordInjection.rb:77:12:77:17 | call to params | +| ActiveRecordInjection.rb:83:12:83:17 | call to params | +| ActiveRecordInjection.rb:88:15:88:20 | call to params | +| ActiveRecordInjection.rb:94:21:94:26 | call to params | +| app/controllers/foo/bars_controller.rb:8:21:8:26 | call to params | +| app/controllers/foo/bars_controller.rb:9:10:9:15 | call to params | +| app/controllers/foo/bars_controller.rb:16:21:16:26 | call to params | +| app/controllers/foo/bars_controller.rb:17:10:17:15 | call to params | +| app/views/foo/bars/show.html.erb:5:9:5:14 | call to params | +paramsSources +| ActiveRecordInjection.rb:35:30:35:35 | call to params | +| ActiveRecordInjection.rb:39:29:39:34 | call to params | +| ActiveRecordInjection.rb:43:31:43:36 | call to params | +| ActiveRecordInjection.rb:48:21:48:26 | call to params | +| ActiveRecordInjection.rb:54:34:54:39 | call to params | +| ActiveRecordInjection.rb:56:23:56:28 | call to params | +| ActiveRecordInjection.rb:56:38:56:43 | call to params | +| ActiveRecordInjection.rb:62:10:62:15 | call to params | +| ActiveRecordInjection.rb:72:11:72:16 | call to params | +| ActiveRecordInjection.rb:77:12:77:17 | call to params | +| ActiveRecordInjection.rb:83:12:83:17 | call to params | +| ActiveRecordInjection.rb:88:15:88:20 | call to params | +| ActiveRecordInjection.rb:94:21:94:26 | call to params | +| app/controllers/foo/bars_controller.rb:8:21:8:26 | call to params | +| app/controllers/foo/bars_controller.rb:9:10:9:15 | call to params | +| app/controllers/foo/bars_controller.rb:16:21:16:26 | call to params | +| app/controllers/foo/bars_controller.rb:17:10:17:15 | call to params | +| app/views/foo/bars/show.html.erb:5:9:5:14 | call to params | +redirectToCalls +| app/controllers/foo/bars_controller.rb:12:5:12:30 | call to redirect_to | +actionControllerHelperMethods +getAssociatedControllerClasses +| app/controllers/foo/bars_controller.rb:1:1:20:3 | BarsController | app/views/foo/bars/_widget.html.erb:0:0:0:0 | app/views/foo/bars/_widget.html.erb | +| app/controllers/foo/bars_controller.rb:1:1:20:3 | BarsController | app/views/foo/bars/show.html.erb:0:0:0:0 | app/views/foo/bars/show.html.erb | +controllerTemplateFiles +| app/controllers/foo/bars_controller.rb:1:1:20:3 | BarsController | app/views/foo/bars/_widget.html.erb:0:0:0:0 | app/views/foo/bars/_widget.html.erb | +| app/controllers/foo/bars_controller.rb:1:1:20:3 | BarsController | app/views/foo/bars/show.html.erb:0:0:0:0 | app/views/foo/bars/show.html.erb | diff --git a/ruby/ql/test/library-tests/frameworks/ActionController.ql b/ruby/ql/test/library-tests/frameworks/ActionController.ql new file mode 100644 index 00000000000..4ab93ee5d03 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/ActionController.ql @@ -0,0 +1,23 @@ +private import ruby +private import codeql.ruby.frameworks.ActionController +private import codeql.ruby.frameworks.ActionView + +query predicate actionControllerControllerClasses(ActionControllerControllerClass cls) { any() } + +query predicate actionControllerActionMethods(ActionControllerActionMethod m) { any() } + +query predicate paramsCalls(ParamsCall c) { any() } + +query predicate paramsSources(ParamsSource src) { any() } + +query predicate redirectToCalls(RedirectToCall c) { any() } + +query predicate actionControllerHelperMethods(ActionControllerHelperMethod m) { any() } + +query predicate getAssociatedControllerClasses(ActionControllerControllerClass cls, ErbFile f) { + cls = getAssociatedControllerClass(f) +} + +query predicate controllerTemplateFiles(ActionControllerControllerClass cls, ErbFile templateFile) { + controllerTemplateFile(cls, templateFile) +} diff --git a/ruby/ql/test/library-tests/frameworks/ActionView.expected b/ruby/ql/test/library-tests/frameworks/ActionView.expected new file mode 100644 index 00000000000..959c9cc0ab2 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/ActionView.expected @@ -0,0 +1,21 @@ +htmlSafeCalls +| app/views/foo/bars/show.html.erb:23:3:23:25 | call to html_safe | +| app/views/foo/bars/show.html.erb:27:3:27:25 | call to html_safe | +rawCalls +| app/views/foo/bars/_widget.html.erb:1:5:1:21 | call to raw | +| app/views/foo/bars/_widget.html.erb:2:5:2:20 | call to raw | +| app/views/foo/bars/_widget.html.erb:3:5:3:29 | call to raw | +| app/views/foo/bars/show.html.erb:1:14:1:29 | call to raw | +| app/views/foo/bars/show.html.erb:2:5:2:21 | call to raw | +| app/views/foo/bars/show.html.erb:3:5:3:20 | call to raw | +| app/views/foo/bars/show.html.erb:4:5:4:29 | call to raw | +| app/views/foo/bars/show.html.erb:5:5:5:21 | call to raw | +| app/views/foo/bars/show.html.erb:7:5:7:19 | call to raw | +renderCalls +| app/controllers/foo/bars_controller.rb:4:5:4:37 | call to render | +| app/controllers/foo/bars_controller.rb:18:5:18:76 | call to render | +| app/views/foo/bars/show.html.erb:31:5:31:89 | call to render | +renderToCalls +| app/controllers/foo/bars_controller.rb:10:16:10:97 | call to render_to_string | +linkToCalls +| app/views/foo/bars/show.html.erb:33:5:33:41 | call to link_to | diff --git a/ruby/ql/test/library-tests/frameworks/ActionView.ql b/ruby/ql/test/library-tests/frameworks/ActionView.ql new file mode 100644 index 00000000000..da48ed87e8a --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/ActionView.ql @@ -0,0 +1,12 @@ +import codeql.ruby.frameworks.ActionController +import codeql.ruby.frameworks.ActionView + +query predicate htmlSafeCalls(HtmlSafeCall c) { any() } + +query predicate rawCalls(RawCall c) { any() } + +query predicate renderCalls(RenderCall c) { any() } + +query predicate renderToCalls(RenderToCall c) { any() } + +query predicate linkToCalls(LinkToCall c) { any() } diff --git a/ruby/ql/test/library-tests/frameworks/ActiveRecord.expected b/ruby/ql/test/library-tests/frameworks/ActiveRecord.expected new file mode 100644 index 00000000000..183e45ea45e --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/ActiveRecord.expected @@ -0,0 +1,53 @@ +activeRecordModelClasses +| ActiveRecordInjection.rb:1:1:3:3 | UserGroup | +| ActiveRecordInjection.rb:5:1:17:3 | User | +| ActiveRecordInjection.rb:19:1:25:3 | Admin | +activeRecordSqlExecutionRanges +| ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | +| ActiveRecordInjection.rb:23:16:23:24 | condition | +| ActiveRecordInjection.rb:35:30:35:44 | ...[...] | +| ActiveRecordInjection.rb:39:20:39:42 | "id = '#{...}'" | +| ActiveRecordInjection.rb:43:22:43:44 | "id = '#{...}'" | +| ActiveRecordInjection.rb:47:16:47:21 | <<-SQL | +| ActiveRecordInjection.rb:54:20:54:47 | "user.id = '#{...}'" | +| ActiveRecordInjection.rb:68:20:68:32 | ... + ... | +| ActiveRecordInjection.rb:75:16:75:28 | "name #{...}" | +| ActiveRecordInjection.rb:80:20:80:39 | "username = #{...}" | +activeRecordModelClassMethodCalls +| ActiveRecordInjection.rb:2:3:2:17 | call to has_many | +| ActiveRecordInjection.rb:6:3:6:24 | call to belongs_to | +| ActiveRecordInjection.rb:10:5:10:68 | call to find | +| ActiveRecordInjection.rb:15:5:15:40 | call to find_by | +| ActiveRecordInjection.rb:15:5:15:46 | call to users | +| ActiveRecordInjection.rb:23:5:23:25 | call to destroy_by | +| ActiveRecordInjection.rb:35:5:35:45 | call to calculate | +| ActiveRecordInjection.rb:39:5:39:43 | call to delete_by | +| ActiveRecordInjection.rb:43:5:43:46 | call to destroy_by | +| ActiveRecordInjection.rb:47:5:47:35 | call to where | +| ActiveRecordInjection.rb:54:5:54:14 | call to where | +| ActiveRecordInjection.rb:54:5:54:48 | call to not | +| ActiveRecordInjection.rb:56:5:56:51 | call to authenticate | +| ActiveRecordInjection.rb:68:5:68:33 | call to delete_by | +| ActiveRecordInjection.rb:75:5:75:29 | call to order | +| ActiveRecordInjection.rb:80:7:80:40 | call to find_by | +| ActiveRecordInjection.rb:85:5:85:33 | call to find_by | +| ActiveRecordInjection.rb:88:5:88:34 | call to find | +| ActiveRecordInjection.rb:94:5:94:45 | call to delete_by | +potentiallyUnsafeSqlExecutingMethodCall +| ActiveRecordInjection.rb:10:5:10:68 | call to find | +| ActiveRecordInjection.rb:23:5:23:25 | call to destroy_by | +| ActiveRecordInjection.rb:35:5:35:45 | call to calculate | +| ActiveRecordInjection.rb:39:5:39:43 | call to delete_by | +| ActiveRecordInjection.rb:43:5:43:46 | call to destroy_by | +| ActiveRecordInjection.rb:47:5:47:35 | call to where | +| ActiveRecordInjection.rb:54:5:54:48 | call to not | +| ActiveRecordInjection.rb:68:5:68:33 | call to delete_by | +| ActiveRecordInjection.rb:75:5:75:29 | call to order | +| ActiveRecordInjection.rb:80:7:80:40 | call to find_by | +activeRecordModelInstantiations +| ActiveRecordInjection.rb:10:5:10:68 | self | ActiveRecordInjection.rb:5:1:17:3 | User | +| ActiveRecordInjection.rb:15:5:15:40 | call to find_by | ActiveRecordInjection.rb:1:1:3:3 | UserGroup | +| ActiveRecordInjection.rb:23:5:23:25 | self | ActiveRecordInjection.rb:19:1:25:3 | Admin | +| ActiveRecordInjection.rb:80:7:80:40 | call to find_by | ActiveRecordInjection.rb:5:1:17:3 | User | +| ActiveRecordInjection.rb:85:5:85:33 | call to find_by | ActiveRecordInjection.rb:5:1:17:3 | User | +| ActiveRecordInjection.rb:88:5:88:34 | call to find | ActiveRecordInjection.rb:5:1:17:3 | User | diff --git a/ruby/ql/test/library-tests/frameworks/ActiveRecord.ql b/ruby/ql/test/library-tests/frameworks/ActiveRecord.ql new file mode 100644 index 00000000000..f7ce327b95a --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/ActiveRecord.ql @@ -0,0 +1,18 @@ +import codeql.ruby.controlflow.CfgNodes +import codeql.ruby.frameworks.ActiveRecord + +query predicate activeRecordModelClasses(ActiveRecordModelClass cls) { any() } + +query predicate activeRecordSqlExecutionRanges(ActiveRecordSqlExecutionRange range) { any() } + +query predicate activeRecordModelClassMethodCalls(ActiveRecordModelClassMethodCall call) { any() } + +query predicate potentiallyUnsafeSqlExecutingMethodCall(PotentiallyUnsafeSqlExecutingMethodCall call) { + any() +} + +query predicate activeRecordModelInstantiations( + ActiveRecordModelInstantiation i, ActiveRecordModelClass cls +) { + i.getClass() = cls +} diff --git a/ruby/ql/test/library-tests/frameworks/ActiveRecordInjection.rb b/ruby/ql/test/library-tests/frameworks/ActiveRecordInjection.rb new file mode 100644 index 00000000000..848b4e423c3 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/ActiveRecordInjection.rb @@ -0,0 +1,96 @@ +class UserGroup < ActiveRecord::Base + has_many :users +end + +class User < ApplicationRecord + belongs_to :user_group + + def self.authenticate(name, pass) + # BAD: possible untrusted input interpolated into SQL fragment + find(:first, :conditions => "name='#{name}' and pass='#{pass}'") + end + + def self.from(user_group_id) + # GOOD: `find_by` with hash argument + UserGroup.find_by(id: user_group_id).users + end +end + +class Admin < User + def self.delete_by(condition = nil) + # BAD: `delete_by` overrides an ActiveRecord method, but doesn't perform + # any validation before passing its arguments on to another ActiveRecord method + destroy_by(condition) + end +end + +class FooController < ActionController::Base + + MAX_USER_ID = 100_000 + + # A string tainted by user input is inserted into an SQL query + def some_request_handler + # BAD: executes `SELECT AVG(#{params[:column]}) FROM "users"` + # where `params[:column]` is unsanitized + User.calculate(:average, params[:column]) + + # BAD: executes `DELETE FROM "users" WHERE (id = '#{params[:id]}')` + # where `params[:id]` is unsanitized + User.delete_by("id = '#{params[:id]}'") + + # BAD: executes `SELECT "users".* FROM "users" WHERE (id = '#{params[:id]}')` + # where `params[:id]` is unsanitized + User.destroy_by(["id = '#{params[:id]}'"]) + + # BAD: executes `SELECT "users".* FROM "users" WHERE id BETWEEN '#{params[:min_id]}' AND 100000` + # where `params[:min_id]` is unsanitized + User.where(<<-SQL, MAX_USER_ID) + id BETWEEN '#{params[:min_id]}' AND ? + SQL + + # BAD: chained method case + # executes `SELECT "users".* FROM "users" WHERE (NOT (user_id = 'params[:id]'))` + # where `params[:id]` is unsanitized + User.where.not("user.id = '#{params[:id]}'") + + User.authenticate(params[:name], params[:pass]) + end +end + +class BarController < ApplicationController + def some_other_request_handler + ps = params + uid = ps[:id] + uidEq = "= '#{uid}'" + + # BAD: executes `DELETE FROM "users" WHERE (id = #{uid})` + # where `uid` is unsantized + User.delete_by("id " + uidEq) + end + + def safe_paths + dir = params[:order] + # GOOD: barrier guard prevents taint flow + dir = "DESC" unless dir == "ASC" + User.order("name #{dir}") + + name = params[:user_name] + # GOOD: barrier guard prevents taint flow + if %w(alice bob charlie).include? name + User.find_by("username = #{name}") + end + + name = params[:user_name] + # GOOD: hash arguments are sanitized by ActiveRecord + User.find_by(user_name: name) + + # OK: `find` method is overridden in `User` + User.find(params[:user_group]) + end +end + +class BazController < BarController + def yet_another_handler + Admin.delete_by(params[:admin_condition]) + end +end diff --git a/ruby/ql/test/library-tests/frameworks/CommandExecution.rb b/ruby/ql/test/library-tests/frameworks/CommandExecution.rb new file mode 100644 index 00000000000..73b9944775e --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/CommandExecution.rb @@ -0,0 +1,90 @@ +`echo foo` +%x(echo foo) +%x{echo foo} +%x[echo foo] +%x/echo foo/ + +system("echo foo") +system("echo", "foo") +system(["echo", "echo"], "foo") + +system({"FOO" => "BAR"}, "echo foo") +system({"FOO" => "BAR"}, "echo", "foo") +system({"FOO" => "BAR"}, ["echo", "echo"], "foo") + +system("echo foo", unsetenv_others: true) +system("echo", "foo", unsetenv_others: true) +system(["echo", "echo"], "foo", unsetenv_others: true) + +system({"FOO" => "BAR"}, "echo foo", unsetenv_others: true) +system({"FOO" => "BAR"}, "echo", "foo", unsetenv_others: true) +system({"FOO" => "BAR"}, ["echo", "echo"], "foo", unsetenv_others: true) + +exec("echo foo") +exec("echo", "foo") +exec(["echo", "echo"], "foo") + +exec({"FOO" => "BAR"}, "echo foo") +exec({"FOO" => "BAR"}, "echo", "foo") +exec({"FOO" => "BAR"}, ["echo", "echo"], "foo") + +exec("echo foo", unsetenv_others: true) +exec("echo", "foo", unsetenv_others: true) +exec(["echo", "echo"], "foo", unsetenv_others: true) + +exec({"FOO" => "BAR"}, "echo foo", unsetenv_others: true) +exec({"FOO" => "BAR"}, "echo", "foo", unsetenv_others: true) +exec({"FOO" => "BAR"}, ["echo", "echo"], "foo", unsetenv_others: true) + +spawn("echo foo") +spawn("echo", "foo") +spawn(["echo", "echo"], "foo") + +spawn({"FOO" => "BAR"}, "echo foo") +spawn({"FOO" => "BAR"}, "echo", "foo") +spawn({"FOO" => "BAR"}, ["echo", "echo"], "foo") + +spawn("echo foo", unsetenv_others: true) +spawn("echo", "foo", unsetenv_others: true) +spawn(["echo", "echo"], "foo", unsetenv_others: true) + +spawn({"FOO" => "BAR"}, "echo foo", unsetenv_others: true) +spawn({"FOO" => "BAR"}, "echo", "foo", unsetenv_others: true) +spawn({"FOO" => "BAR"}, ["echo", "echo"], "foo", unsetenv_others: true) + +Open3.popen3("echo foo") +Open3.popen2("echo foo") +Open3.popen2e("echo foo") +Open3.capture3("echo foo") +Open3.capture2("echo foo") +Open3.capture2e("echo foo") +Open3.pipeline_rw("echo foo", "grep bar") +Open3.pipeline_r("echo foo", "grep bar") +Open3.pipeline_w("echo foo", "grep bar") +Open3.pipeline_start("echo foo", "grep bar") +Open3.pipeline("echo foo", "grep bar") + +<<`EOF` +echo foo +EOF + +module MockSystem + def system(*args) + args + end + + def self.system(*args) + args + end +end + +class Foo + include MockSystem + + def run + system("ls") + MockSystem.system("ls") + end +end + +UnknownModule.system("ls") diff --git a/ruby/ql/test/library-tests/frameworks/Eval.rb b/ruby/ql/test/library-tests/frameworks/Eval.rb new file mode 100644 index 00000000000..b75e57934bb --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/Eval.rb @@ -0,0 +1,26 @@ +# Uses of eval and send + +eval("raise \"error\"", binding, "file", 1) +send("raise", "error") + +a = [] +a.send("push", "1") + +class Foo + def eval(x) + x + 1 + end + + def send(*args) + 2 + end + + def run + eval("exit 1") + end +end + +Foo.new.send("exit", 1) +Foo.new.instance_eval("self.class", "file.rb", 3) +Foo.class_eval("def foo; 1; end", "file.rb", 1) +Foo.module_eval("def bar; 1; end", "other_file.rb", 2) \ No newline at end of file diff --git a/ruby/ql/test/library-tests/frameworks/StandardLibrary.expected b/ruby/ql/test/library-tests/frameworks/StandardLibrary.expected new file mode 100644 index 00000000000..594d4c299ad --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/StandardLibrary.expected @@ -0,0 +1,71 @@ +subshellLiteralExecutions +| CommandExecution.rb:1:1:1:10 | `echo foo` | +| CommandExecution.rb:2:1:2:12 | `echo foo` | +| CommandExecution.rb:3:1:3:12 | `echo foo` | +| CommandExecution.rb:4:1:4:12 | `echo foo` | +| CommandExecution.rb:5:1:5:12 | `echo foo` | +subshellHeredocExecutions +| CommandExecution.rb:67:1:67:7 | <<`EOF` | +kernelSystemCallExecutions +| CommandExecution.rb:7:1:7:18 | call to system | +| CommandExecution.rb:8:1:8:21 | call to system | +| CommandExecution.rb:9:1:9:31 | call to system | +| CommandExecution.rb:11:1:11:36 | call to system | +| CommandExecution.rb:12:1:12:39 | call to system | +| CommandExecution.rb:13:1:13:49 | call to system | +| CommandExecution.rb:15:1:15:41 | call to system | +| CommandExecution.rb:16:1:16:44 | call to system | +| CommandExecution.rb:17:1:17:54 | call to system | +| CommandExecution.rb:19:1:19:59 | call to system | +| CommandExecution.rb:20:1:20:62 | call to system | +| CommandExecution.rb:21:1:21:72 | call to system | +kernelExecCallExecutions +| CommandExecution.rb:23:1:23:16 | call to exec | +| CommandExecution.rb:24:1:24:19 | call to exec | +| CommandExecution.rb:25:1:25:29 | call to exec | +| CommandExecution.rb:27:1:27:34 | call to exec | +| CommandExecution.rb:28:1:28:37 | call to exec | +| CommandExecution.rb:29:1:29:47 | call to exec | +| CommandExecution.rb:31:1:31:39 | call to exec | +| CommandExecution.rb:32:1:32:42 | call to exec | +| CommandExecution.rb:33:1:33:52 | call to exec | +| CommandExecution.rb:35:1:35:57 | call to exec | +| CommandExecution.rb:36:1:36:60 | call to exec | +| CommandExecution.rb:37:1:37:70 | call to exec | +kernelSpawnCallExecutions +| CommandExecution.rb:39:1:39:17 | call to spawn | +| CommandExecution.rb:40:1:40:20 | call to spawn | +| CommandExecution.rb:41:1:41:30 | call to spawn | +| CommandExecution.rb:43:1:43:35 | call to spawn | +| CommandExecution.rb:44:1:44:38 | call to spawn | +| CommandExecution.rb:45:1:45:48 | call to spawn | +| CommandExecution.rb:47:1:47:40 | call to spawn | +| CommandExecution.rb:48:1:48:43 | call to spawn | +| CommandExecution.rb:49:1:49:53 | call to spawn | +| CommandExecution.rb:51:1:51:58 | call to spawn | +| CommandExecution.rb:52:1:52:61 | call to spawn | +| CommandExecution.rb:53:1:53:71 | call to spawn | +open3CallExecutions +| CommandExecution.rb:55:1:55:24 | call to popen3 | +| CommandExecution.rb:56:1:56:24 | call to popen2 | +| CommandExecution.rb:57:1:57:25 | call to popen2e | +| CommandExecution.rb:58:1:58:26 | call to capture3 | +| CommandExecution.rb:59:1:59:26 | call to capture2 | +| CommandExecution.rb:60:1:60:27 | call to capture2e | +open3PipelineCallExecutions +| CommandExecution.rb:61:1:61:41 | call to pipeline_rw | +| CommandExecution.rb:62:1:62:40 | call to pipeline_r | +| CommandExecution.rb:63:1:63:40 | call to pipeline_w | +| CommandExecution.rb:64:1:64:44 | call to pipeline_start | +| CommandExecution.rb:65:1:65:38 | call to pipeline | +evalCallCodeExecutions +| Eval.rb:3:1:3:43 | call to eval | Eval.rb:3:6:3:22 | "raise \\"error\\"" | +sendCallCodeExecutions +| Eval.rb:4:1:4:22 | call to send | Eval.rb:4:6:4:12 | "raise" | +| Eval.rb:7:1:7:19 | call to send | Eval.rb:7:8:7:13 | "push" | +instanceEvalCallCodeExecutions +| Eval.rb:24:1:24:49 | call to instance_eval | Eval.rb:24:23:24:34 | "self.class" | +classEvalCallCodeExecutions +| Eval.rb:25:1:25:47 | call to class_eval | Eval.rb:25:16:25:32 | "def foo; 1; end" | +moduleEvalCallCodeExecutions +| Eval.rb:26:1:26:54 | call to module_eval | Eval.rb:26:17:26:33 | "def bar; 1; end" | diff --git a/ruby/ql/test/library-tests/frameworks/StandardLibrary.ql b/ruby/ql/test/library-tests/frameworks/StandardLibrary.ql new file mode 100644 index 00000000000..791d55b0f8d --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/StandardLibrary.ql @@ -0,0 +1,32 @@ +import codeql.ruby.frameworks.StandardLibrary +import codeql.ruby.DataFlow + +query predicate subshellLiteralExecutions(SubshellLiteralExecution e) { any() } + +query predicate subshellHeredocExecutions(SubshellHeredocExecution e) { any() } + +query predicate kernelSystemCallExecutions(KernelSystemCall c) { any() } + +query predicate kernelExecCallExecutions(KernelExecCall c) { any() } + +query predicate kernelSpawnCallExecutions(KernelSpawnCall c) { any() } + +query predicate open3CallExecutions(Open3Call c) { any() } + +query predicate open3PipelineCallExecutions(Open3PipelineCall c) { any() } + +query DataFlow::Node evalCallCodeExecutions(EvalCallCodeExecution e) { result = e.getCode() } + +query DataFlow::Node sendCallCodeExecutions(SendCallCodeExecution e) { result = e.getCode() } + +query DataFlow::Node instanceEvalCallCodeExecutions(InstanceEvalCallCodeExecution e) { + result = e.getCode() +} + +query DataFlow::Node classEvalCallCodeExecutions(ClassEvalCallCodeExecution e) { + result = e.getCode() +} + +query DataFlow::Node moduleEvalCallCodeExecutions(ModuleEvalCallCodeExecution e) { + result = e.getCode() +} diff --git a/ruby/ql/test/library-tests/frameworks/app/components/DummyComponent.rb b/ruby/ql/test/library-tests/frameworks/app/components/DummyComponent.rb new file mode 100644 index 00000000000..80d6a602d68 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/app/components/DummyComponent.rb @@ -0,0 +1,2 @@ +class DummyComponent < ViewComponent::Base +end diff --git a/ruby/ql/test/library-tests/frameworks/app/controllers/foo/bars_controller.rb b/ruby/ql/test/library-tests/frameworks/app/controllers/foo/bars_controller.rb new file mode 100644 index 00000000000..6655cbcd4c7 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/app/controllers/foo/bars_controller.rb @@ -0,0 +1,20 @@ +class BarsController < ApplicationController + + def index + render template: "foo/bars/index" + end + + def show_debug + @user_website = params[:website] + dt = params[:text] + rendered = render_to_string "foo/bars/show", locals: { display_text: dt, safe_text: "hello" } + puts rendered + redirect_to action: "show" + end + + def show + @user_website = params[:website] + dt = params[:text] + render "foo/bars/show", locals: { display_text: dt, safe_text: "hello" } + end +end diff --git a/ruby/ql/test/library-tests/frameworks/app/views/foo/bars/_widget.html.erb b/ruby/ql/test/library-tests/frameworks/app/views/foo/bars/_widget.html.erb new file mode 100644 index 00000000000..dda3813363a --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/app/views/foo/bars/_widget.html.erb @@ -0,0 +1,4 @@ +<%= raw @display_text %> +<%= raw display_text %> +<%= raw locals[:display_text] %> +<%= @display_text %> diff --git a/ruby/ql/test/library-tests/frameworks/app/views/foo/bars/show.html.erb b/ruby/ql/test/library-tests/frameworks/app/views/foo/bars/show.html.erb new file mode 100644 index 00000000000..a86fabf719c --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/app/views/foo/bars/show.html.erb @@ -0,0 +1,33 @@ +website +<%= raw @display_text %> +<%= raw display_text %> +<%= raw locals[:display_text] %> +<%= raw params[:text] %> +<% key = :display_text %> +<%= raw locals[key] %> + +
      +<% key = [:display_text, :safe_text] do +
    • <%= raw locals[key] %>
    • +<% end %> +
    + +<%= @display_text %> + +<%= + full_text = prefix + locals[:display_text] + full_text +%> + +<%= + @display_text.html_safe +%> + +<%= + @display_text.html_safe + @display_text +%> + +<%= render partial: 'foo/bars/widget', locals: { display_text: "widget_" + display_text } %> + +<%= link_to "some website", @user_website %> diff --git a/ruby/ql/test/library-tests/frameworks/files/Files.expected b/ruby/ql/test/library-tests/frameworks/files/Files.expected new file mode 100644 index 00000000000..3c094b66626 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/files/Files.expected @@ -0,0 +1,60 @@ +fileInstances +| Files.rb:2:1:2:30 | ... = ... | +| Files.rb:2:1:2:30 | ... = ... | +| Files.rb:2:12:2:30 | call to new | +| Files.rb:3:1:3:21 | ... = ... | +| Files.rb:3:1:3:21 | ... = ... | +| Files.rb:3:14:3:21 | foo_file | +| Files.rb:4:1:4:8 | foo_file | +| Files.rb:7:13:7:22 | foo_file_2 | +| Files.rb:10:6:10:13 | foo_file | +| Files.rb:11:6:11:13 | foo_file | +| Files.rb:23:1:23:33 | ... = ... | +| Files.rb:23:19:23:33 | call to open | +| Files.rb:24:1:24:40 | ... = ... | +| Files.rb:24:19:24:40 | call to open | +ioInstances +| Files.rb:2:1:2:30 | ... = ... | +| Files.rb:2:1:2:30 | ... = ... | +| Files.rb:2:12:2:30 | call to new | +| Files.rb:3:1:3:21 | ... = ... | +| Files.rb:3:1:3:21 | ... = ... | +| Files.rb:3:14:3:21 | foo_file | +| Files.rb:4:1:4:8 | foo_file | +| Files.rb:7:13:7:22 | foo_file_2 | +| Files.rb:10:6:10:13 | foo_file | +| Files.rb:11:6:11:13 | foo_file | +| Files.rb:17:1:17:50 | ... = ... | +| Files.rb:17:1:17:50 | ... = ... | +| Files.rb:17:8:17:50 | call to new | +| Files.rb:18:1:18:13 | ... = ... | +| Files.rb:18:10:18:13 | rand | +| Files.rb:20:13:20:16 | rand | +| Files.rb:23:1:23:33 | ... = ... | +| Files.rb:23:19:23:33 | call to open | +| Files.rb:24:1:24:40 | ... = ... | +| Files.rb:24:19:24:40 | call to open | +| Files.rb:35:1:35:56 | ... = ... | +| Files.rb:35:13:35:56 | call to open | +fileReaders +| Files.rb:7:13:7:32 | call to readlines | +ioReaders +| Files.rb:7:13:7:32 | call to readlines | File | +| Files.rb:20:13:20:25 | call to read | IO | +| Files.rb:29:12:29:29 | call to read | IO | +| Files.rb:32:8:32:23 | call to read | IO | +ioFileReaders +| Files.rb:7:13:7:32 | call to readlines | File | +| Files.rb:29:12:29:29 | call to read | IO | +fileModuleFilenameSources +| Files.rb:10:6:10:18 | call to path | +| Files.rb:11:6:11:21 | call to to_path | +fileUtilsFilenameSources +| Files.rb:14:8:14:43 | call to makedirs | +fileSystemReadAccesses +| Files.rb:7:13:7:32 | call to readlines | +| Files.rb:29:12:29:29 | call to read | +fileNameSources +| Files.rb:10:6:10:18 | call to path | +| Files.rb:11:6:11:21 | call to to_path | +| Files.rb:14:8:14:43 | call to makedirs | diff --git a/ruby/ql/test/library-tests/frameworks/files/Files.ql b/ruby/ql/test/library-tests/frameworks/files/Files.ql new file mode 100644 index 00000000000..fc2fffe9216 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/files/Files.ql @@ -0,0 +1,21 @@ +private import ruby +private import codeql.ruby.frameworks.Files +private import codeql.ruby.Concepts + +query predicate fileInstances(File::FileInstance i) { any() } + +query predicate ioInstances(IO::IOInstance i) { any() } + +query predicate fileReaders(File::FileModuleReader r) { any() } + +query predicate ioReaders(IO::IOReader r, string api) { api = r.getAPI() } + +query predicate ioFileReaders(IO::IOFileReader r, string api) { api = r.getAPI() } + +query predicate fileModuleFilenameSources(File::FileModuleFilenameSource s) { any() } + +query predicate fileUtilsFilenameSources(FileUtils::FileUtilsFilenameSource s) { any() } + +query predicate fileSystemReadAccesses(FileSystemReadAccess a) { any() } + +query predicate fileNameSources(FileNameSource s) { any() } diff --git a/ruby/ql/test/library-tests/frameworks/files/Files.rb b/ruby/ql/test/library-tests/frameworks/files/Files.rb new file mode 100644 index 00000000000..8050a97cde6 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/files/Files.rb @@ -0,0 +1,35 @@ +# `foo_file` is a `File` instance +foo_file = File.new("foo.txt") +foo_file_2 = foo_file +foo_file + +# File read access +foo_lines = foo_file_2.readlines + +# `fp` is a file path +fp = foo_file.path +fp = foo_file.to_path + +# `FileUtils.makedirs` returns an array of file names +dirs = FileUtils.makedirs(["dir1", "dir2"]) + +# `rand` is an `IO` instance +rand = IO.new(IO.sysopen("/dev/random", "r"), "r") +rand_2 = rand + +rand_data = rand.read(32) + +# `foo_file_kernel` is a `File` instance +foo_file_kernel = open("foo.txt") +foo_file_kernel = Kernel.open("foo.txt") + +foo_command_kernel = open("|ls") + +# `IO.read("foo.txt")` reads from a file +foo_text = IO.read("foo.txt") + +# `IO.read("|date")` does not read from a file +date = IO.read("|date") + +# `rand_open` is an `IO` instance +rand_open = IO.open(IO.sysopen("/dev/random", "r"), "r") diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/Excon.expected b/ruby/ql/test/library-tests/frameworks/http_clients/Excon.expected new file mode 100644 index 00000000000..0275c675213 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/Excon.expected @@ -0,0 +1,12 @@ +| Excon.rb:3:9:3:40 | call to get | Excon.rb:4:1:4:10 | call to body | +| Excon.rb:6:9:6:60 | call to post | Excon.rb:7:1:7:10 | call to body | +| Excon.rb:9:9:9:59 | call to put | Excon.rb:10:1:10:10 | call to body | +| Excon.rb:12:9:12:61 | call to patch | Excon.rb:13:1:13:10 | call to body | +| Excon.rb:15:9:15:43 | call to delete | Excon.rb:16:1:16:10 | call to body | +| Excon.rb:18:9:18:41 | call to head | Excon.rb:19:1:19:10 | call to body | +| Excon.rb:21:9:21:44 | call to options | Excon.rb:22:1:22:10 | call to body | +| Excon.rb:24:9:24:42 | call to trace | Excon.rb:25:1:25:10 | call to body | +| Excon.rb:28:9:28:34 | call to get | Excon.rb:29:1:29:10 | call to body | +| Excon.rb:31:10:31:39 | call to post | Excon.rb:32:1:32:11 | call to body | +| Excon.rb:35:9:35:34 | call to get | Excon.rb:36:1:36:10 | call to body | +| Excon.rb:38:10:38:39 | call to post | Excon.rb:39:1:39:11 | call to body | diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/Excon.ql b/ruby/ql/test/library-tests/frameworks/http_clients/Excon.ql new file mode 100644 index 00000000000..9c6aded84c5 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/Excon.ql @@ -0,0 +1,4 @@ +import codeql.ruby.frameworks.http_clients.Excon +import codeql.ruby.DataFlow + +query DataFlow::Node exconHttpRequests(ExconHttpRequest e) { result = e.getResponseBody() } diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/Excon.rb b/ruby/ql/test/library-tests/frameworks/http_clients/Excon.rb new file mode 100644 index 00000000000..fb0bc7e75f1 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/Excon.rb @@ -0,0 +1,39 @@ +require "excon" + +resp1 = Excon.get("http://example.com/") +resp1.body + +resp2 = Excon.post("http://example.com/", body: "some_data") +resp2.body + +resp3 = Excon.put("http://example.com/", body: "some_data") +resp3.body + +resp4 = Excon.patch("http://example.com/", body: "some_data") +resp4.body + +resp5 = Excon.delete("http://example.com/") +resp5.body + +resp6 = Excon.head("http://example.com/") +resp6.body + +resp7 = Excon.options("http://example.com/") +resp7.body + +resp8 = Excon.trace("http://example.com/") +resp8.body + +connection1 = Excon.new("http://example.com") +resp9 = connection1.get(path: "/") +resp9.body + +resp10 = connection1.post(path: "/foo") +resp10.body + +connection2 = Excon::Connection.new("http://example.com") +resp9 = connection2.get(path: "/") +resp9.body + +resp10 = connection2.post(path: "/foo") +resp10.body \ No newline at end of file diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/Faraday.expected b/ruby/ql/test/library-tests/frameworks/http_clients/Faraday.expected new file mode 100644 index 00000000000..2013b49dd4a --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/Faraday.expected @@ -0,0 +1,9 @@ +| Faraday.rb:3:9:3:42 | call to get | Faraday.rb:4:1:4:10 | call to body | +| Faraday.rb:6:9:6:62 | call to post | Faraday.rb:7:1:7:10 | call to body | +| Faraday.rb:9:9:9:61 | call to put | Faraday.rb:10:1:10:10 | call to body | +| Faraday.rb:12:9:12:63 | call to patch | Faraday.rb:13:1:13:10 | call to body | +| Faraday.rb:15:9:15:45 | call to delete | Faraday.rb:16:1:16:10 | call to body | +| Faraday.rb:18:9:18:43 | call to head | Faraday.rb:19:1:19:10 | call to body | +| Faraday.rb:24:9:24:44 | call to trace | Faraday.rb:25:1:25:10 | call to body | +| Faraday.rb:28:9:28:27 | call to get | Faraday.rb:29:1:29:10 | call to body | +| Faraday.rb:31:10:31:46 | call to post | Faraday.rb:32:1:32:11 | call to body | diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/Faraday.ql b/ruby/ql/test/library-tests/frameworks/http_clients/Faraday.ql new file mode 100644 index 00000000000..b864bb44e53 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/Faraday.ql @@ -0,0 +1,4 @@ +import codeql.ruby.frameworks.http_clients.Faraday +import codeql.ruby.DataFlow + +query DataFlow::Node faradayHttpRequests(FaradayHttpRequest e) { result = e.getResponseBody() } diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/Faraday.rb b/ruby/ql/test/library-tests/frameworks/http_clients/Faraday.rb new file mode 100644 index 00000000000..ee8cccb44b0 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/Faraday.rb @@ -0,0 +1,32 @@ +require "faraday" + +resp1 = Faraday.get("http://example.com/") +resp1.body + +resp2 = Faraday.post("http://example.com/", body: "some_data") +resp2.body + +resp3 = Faraday.put("http://example.com/", body: "some_data") +resp3.body + +resp4 = Faraday.patch("http://example.com/", body: "some_data") +resp4.body + +resp5 = Faraday.delete("http://example.com/") +resp5.body + +resp6 = Faraday.head("http://example.com/") +resp6.body + +resp7 = Faraday.options("http://example.com/") +resp7.body + +resp8 = Faraday.trace("http://example.com/") +resp8.body + +connection = Faraday.new("http://example.com") +resp9 = connection.get("/") +resp9.body + +resp10 = connection.post("/foo", some: "data") +resp10.body \ No newline at end of file diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/HttpClient.expected b/ruby/ql/test/library-tests/frameworks/http_clients/HttpClient.expected new file mode 100644 index 00000000000..8c83a9e9fc6 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/HttpClient.expected @@ -0,0 +1,9 @@ +| HttpClient.rb:3:9:3:45 | call to get | HttpClient.rb:4:1:4:10 | call to body | +| HttpClient.rb:6:9:6:65 | call to post | HttpClient.rb:7:1:7:13 | call to content | +| HttpClient.rb:9:9:9:64 | call to put | HttpClient.rb:10:1:10:15 | call to http_body | +| HttpClient.rb:12:9:12:48 | call to delete | HttpClient.rb:13:1:13:10 | call to dump | +| HttpClient.rb:15:9:15:46 | call to head | HttpClient.rb:16:1:16:10 | call to body | +| HttpClient.rb:18:9:18:49 | call to options | HttpClient.rb:19:1:19:13 | call to content | +| HttpClient.rb:21:9:21:47 | call to trace | HttpClient.rb:22:1:22:15 | call to http_body | +| HttpClient.rb:24:9:24:53 | call to get_content | HttpClient.rb:24:9:24:53 | call to get_content | +| HttpClient.rb:26:10:26:74 | call to post_content | HttpClient.rb:26:10:26:74 | call to post_content | diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/HttpClient.ql b/ruby/ql/test/library-tests/frameworks/http_clients/HttpClient.ql new file mode 100644 index 00000000000..68cd21e19a1 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/HttpClient.ql @@ -0,0 +1,4 @@ +import codeql.ruby.frameworks.http_clients.HttpClient +import codeql.ruby.DataFlow + +query DataFlow::Node httpClientRequests(HttpClientRequest e) { result = e.getResponseBody() } diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/HttpClient.rb b/ruby/ql/test/library-tests/frameworks/http_clients/HttpClient.rb new file mode 100644 index 00000000000..7297c8432e0 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/HttpClient.rb @@ -0,0 +1,26 @@ +require "httpclient" + +resp1 = HTTPClient.get("http://example.com/") +resp1.body + +resp2 = HTTPClient.post("http://example.com/", body: "some_data") +resp2.content + +resp3 = HTTPClient.put("http://example.com/", body: "some_data") +resp3.http_body + +resp5 = HTTPClient.delete("http://example.com/") +resp5.dump + +resp6 = HTTPClient.head("http://example.com/") +resp6.body + +resp7 = HTTPClient.options("http://example.com/") +resp7.content + +resp8 = HTTPClient.trace("http://example.com/") +resp8.http_body + +resp9 = HTTPClient.get_content("http://example.com/") + +resp10 = HTTPClient.post_content("http://example.com/", body: "some_data") diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/Httparty.expected b/ruby/ql/test/library-tests/frameworks/http_clients/Httparty.expected new file mode 100644 index 00000000000..e44cf3c6f22 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/Httparty.expected @@ -0,0 +1,7 @@ +| Httparty.rb:5:1:5:35 | call to get | Httparty.rb:5:1:5:35 | call to get | +| Httparty.rb:7:1:7:55 | call to post | Httparty.rb:7:1:7:55 | call to post | +| Httparty.rb:9:1:9:54 | call to put | Httparty.rb:9:1:9:54 | call to put | +| Httparty.rb:11:1:11:56 | call to patch | Httparty.rb:11:1:11:56 | call to patch | +| Httparty.rb:15:9:15:46 | call to delete | Httparty.rb:16:1:16:10 | call to body | +| Httparty.rb:18:9:18:44 | call to head | Httparty.rb:19:1:19:10 | call to body | +| Httparty.rb:21:9:21:47 | call to options | Httparty.rb:22:1:22:10 | call to body | diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/Httparty.ql b/ruby/ql/test/library-tests/frameworks/http_clients/Httparty.ql new file mode 100644 index 00000000000..1ed1785be62 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/Httparty.ql @@ -0,0 +1,4 @@ +import codeql.ruby.frameworks.http_clients.Httparty +import codeql.ruby.DataFlow + +query DataFlow::Node httpartyRequests(HttpartyRequest e) { result = e.getResponseBody() } diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/Httparty.rb b/ruby/ql/test/library-tests/frameworks/http_clients/Httparty.rb new file mode 100644 index 00000000000..9316186ab94 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/Httparty.rb @@ -0,0 +1,31 @@ +require "httparty" + +# If the response body is not nil or an empty string, it will be parsed and returned directly. + +HTTParty.get("http://example.com/") + +HTTParty.post("http://example.com/", body: "some_data") + +HTTParty.put("http://example.com/", body: "some_data") + +HTTParty.patch("http://example.com/", body: "some_data") + +# Otherwise, `HTTParty::Response` will be returned, which has a `#body` method. + +resp5 = HTTParty.delete("http://example.com/") +resp5.body + +resp6 = HTTParty.head("http://example.com/") +resp6.body + +resp7 = HTTParty.options("http://example.com/") +resp7.body + +# HTTParty methods can also be included in other classes. +# This is not yet modelled. + +class MyClient + inlcude HTTParty +end + +MyClient.get("http://example.com") diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/NetHttp.expected b/ruby/ql/test/library-tests/frameworks/http_clients/NetHttp.expected new file mode 100644 index 00000000000..284d27eb197 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/NetHttp.expected @@ -0,0 +1,8 @@ +| NetHttp.rb:4:1:4:18 | call to get | NetHttp.rb:4:1:4:18 | call to get | +| NetHttp.rb:6:8:6:50 | call to post | NetHttp.rb:7:1:7:9 | call to body | +| NetHttp.rb:6:8:6:50 | call to post | NetHttp.rb:8:1:8:14 | call to read_body | +| NetHttp.rb:6:8:6:50 | call to post | NetHttp.rb:9:1:9:11 | call to entity | +| NetHttp.rb:13:6:13:17 | call to get | NetHttp.rb:18:1:18:7 | call to body | +| NetHttp.rb:14:6:14:18 | call to post | NetHttp.rb:19:1:19:12 | call to read_body | +| NetHttp.rb:15:6:15:17 | call to put | NetHttp.rb:20:1:20:9 | call to entity | +| NetHttp.rb:24:3:24:33 | call to get | NetHttp.rb:27:1:27:28 | call to body | diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/NetHttp.ql b/ruby/ql/test/library-tests/frameworks/http_clients/NetHttp.ql new file mode 100644 index 00000000000..d343fc36b8a --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/NetHttp.ql @@ -0,0 +1,4 @@ +import codeql.ruby.frameworks.http_clients.NetHttp +import codeql.ruby.DataFlow + +query DataFlow::Node netHttpRequests(NetHttpRequest e) { result = e.getResponseBody() } diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/NetHttp.rb b/ruby/ql/test/library-tests/frameworks/http_clients/NetHttp.rb new file mode 100644 index 00000000000..12333be9f4e --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/NetHttp.rb @@ -0,0 +1,27 @@ +require "net/http" + +uri = URI.parse("https://example.com") +Net::HTTP.get(uri) + +resp = Net::HTTP.post(URI.parse(uri), "some_body") +resp.body +resp.read_body +resp.entity + +req = Net::HTTP.new("https://example.com") + +r1 = req.get("/") +r2 = req.post("/") +r3 = req.put("/") +r4 = req.patch("/") + +r1.body +r2.read_body +r3.entity +r4.foo + +def get(domain, path) + Net::HTTP.new(domain).get(path) +end + +get("example.com", "/").body diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/OpenURI.expected b/ruby/ql/test/library-tests/frameworks/http_clients/OpenURI.expected new file mode 100644 index 00000000000..9da5401ba33 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/OpenURI.expected @@ -0,0 +1,6 @@ +openUriRequests +| OpenURI.rb:9:9:9:38 | call to open | OpenURI.rb:10:1:10:10 | call to read | +| OpenURI.rb:12:9:12:45 | call to open | OpenURI.rb:13:1:13:10 | call to read | +openUriKernelOpenRequests +| OpenURI.rb:3:9:3:41 | call to open | OpenURI.rb:4:1:4:10 | call to read | +| OpenURI.rb:6:9:6:34 | call to open | OpenURI.rb:7:1:7:15 | call to readlines | diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/OpenURI.ql b/ruby/ql/test/library-tests/frameworks/http_clients/OpenURI.ql new file mode 100644 index 00000000000..91d824f9965 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/OpenURI.ql @@ -0,0 +1,8 @@ +import codeql.ruby.frameworks.http_clients.OpenURI +import codeql.ruby.DataFlow + +query DataFlow::Node openUriRequests(OpenUriRequest e) { result = e.getResponseBody() } + +query DataFlow::Node openUriKernelOpenRequests(OpenUriKernelOpenRequest e) { + result = e.getResponseBody() +} diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/OpenURI.rb b/ruby/ql/test/library-tests/frameworks/http_clients/OpenURI.rb new file mode 100644 index 00000000000..4cffa581de8 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/OpenURI.rb @@ -0,0 +1,13 @@ +require "open-uri" + +resp1 = Kernel.open("http://example.com") +resp1.read + +resp2 = open("http://example.com") +resp2.readlines + +resp3 = URI.open("http://example.com") +resp3.read + +resp4 = URI.parse("https://example.com").open +resp4.read \ No newline at end of file diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/RestClient.expected b/ruby/ql/test/library-tests/frameworks/http_clients/RestClient.expected new file mode 100644 index 00000000000..4489baf81b5 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/RestClient.expected @@ -0,0 +1,8 @@ +| RestClient.rb:3:9:3:45 | call to get | RestClient.rb:4:1:4:10 | call to body | +| RestClient.rb:6:9:6:59 | call to post | RestClient.rb:7:1:7:10 | call to body | +| RestClient.rb:9:9:9:58 | call to put | RestClient.rb:10:1:10:10 | call to body | +| RestClient.rb:12:9:12:60 | call to patch | RestClient.rb:13:1:13:10 | call to body | +| RestClient.rb:15:9:15:47 | call to delete | RestClient.rb:16:1:16:10 | call to body | +| RestClient.rb:18:9:18:45 | call to head | RestClient.rb:19:1:19:10 | call to body | +| RestClient.rb:21:9:21:48 | call to options | RestClient.rb:22:1:22:10 | call to body | +| RestClient.rb:25:9:25:21 | call to get | RestClient.rb:26:1:26:10 | call to body | diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/RestClient.ql b/ruby/ql/test/library-tests/frameworks/http_clients/RestClient.ql new file mode 100644 index 00000000000..7a0f25a3533 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/RestClient.ql @@ -0,0 +1,6 @@ +import codeql.ruby.frameworks.http_clients.RestClient +import codeql.ruby.DataFlow + +query DataFlow::Node restClientHttpRequests(RestClientHttpRequest e) { + result = e.getResponseBody() +} diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/RestClient.rb b/ruby/ql/test/library-tests/frameworks/http_clients/RestClient.rb new file mode 100644 index 00000000000..875aab62fc3 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/RestClient.rb @@ -0,0 +1,26 @@ +require "rest-client" + +resp1 = RestClient.get("http://example.com/") +resp1.body + +resp2 = RestClient.post("http://example.com", some: "data") +resp2.body + +resp3 = RestClient.put("http://example.com", some: "data") +resp3.body + +resp4 = RestClient.patch("http://example.com", some: "data") +resp4.body + +resp5 = RestClient.delete("http://example.com") +resp5.body + +resp6 = RestClient.head("http://example.com") +resp6.body + +resp7 = RestClient.options("http://example.com") +resp7.body + +resource8 = RestClient::Resource.new "http://example.com" +resp8 = resource8.get +resp8.body \ No newline at end of file diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/Typhoeus.expected b/ruby/ql/test/library-tests/frameworks/http_clients/Typhoeus.expected new file mode 100644 index 00000000000..c96db3a445a --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/Typhoeus.expected @@ -0,0 +1,7 @@ +| Typhoeus.rb:3:9:3:43 | call to get | Typhoeus.rb:4:1:4:10 | call to body | +| Typhoeus.rb:6:9:6:63 | call to post | Typhoeus.rb:7:1:7:10 | call to body | +| Typhoeus.rb:9:9:9:62 | call to put | Typhoeus.rb:10:1:10:10 | call to body | +| Typhoeus.rb:12:9:12:64 | call to patch | Typhoeus.rb:13:1:13:10 | call to body | +| Typhoeus.rb:15:9:15:46 | call to delete | Typhoeus.rb:16:1:16:10 | call to body | +| Typhoeus.rb:18:9:18:44 | call to head | Typhoeus.rb:19:1:19:10 | call to body | +| Typhoeus.rb:21:9:21:47 | call to options | Typhoeus.rb:22:1:22:10 | call to body | diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/Typhoeus.ql b/ruby/ql/test/library-tests/frameworks/http_clients/Typhoeus.ql new file mode 100644 index 00000000000..3e04d647937 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/Typhoeus.ql @@ -0,0 +1,4 @@ +import codeql.ruby.frameworks.http_clients.Typhoeus +import codeql.ruby.DataFlow + +query DataFlow::Node typhoeusHttpRequests(TyphoeusHttpRequest e) { result = e.getResponseBody() } diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/Typhoeus.rb b/ruby/ql/test/library-tests/frameworks/http_clients/Typhoeus.rb new file mode 100644 index 00000000000..743081478c8 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/http_clients/Typhoeus.rb @@ -0,0 +1,22 @@ +require "typhoeus" + +resp1 = Typhoeus.get("http://example.com/") +resp1.body + +resp2 = Typhoeus.post("http://example.com/", body: "some_data") +resp2.body + +resp3 = Typhoeus.put("http://example.com/", body: "some_data") +resp3.body + +resp4 = Typhoeus.patch("http://example.com/", body: "some_data") +resp4.body + +resp5 = Typhoeus.delete("http://example.com/") +resp5.body + +resp6 = Typhoeus.head("http://example.com/") +resp6.body + +resp7 = Typhoeus.options("http://example.com/") +resp7.body \ No newline at end of file diff --git a/ruby/ql/test/library-tests/modules/ancestors.expected b/ruby/ql/test/library-tests/modules/ancestors.expected new file mode 100644 index 00000000000..a9c235619fd --- /dev/null +++ b/ruby/ql/test/library-tests/modules/ancestors.expected @@ -0,0 +1,154 @@ +calls.rb: +# 102| Hash +#-----| super -> Object + +# 77| Integer +#-----| super -> Numeric + +# 86| Kernel + +# 90| Module +#-----| super -> Object + +# 97| Object +#-----| include -> Kernel +#-----| super -> BasicObject + +# 106| Array +#-----| super -> Object + +#-----| BasicObject + +#-----| Class +#-----| super -> Module + +#-----| Complex +#-----| super -> Numeric + +#-----| FalseClass +#-----| super -> Object + +#-----| Float +#-----| super -> Numeric + +#-----| NilClass +#-----| super -> Object + +#-----| Numeric +#-----| super -> Object + +#-----| Proc + +#-----| Rational +#-----| super -> Numeric + +#-----| Symbol + +#-----| TrueClass +#-----| super -> Object + +# 15| M + +private.rb: +# 1| C +#-----| super -> Object +#-----| include -> M + +calls.rb: +# 51| D +#-----| super -> C + +# 82| String +#-----| super -> Object + +# 144| S +#-----| super -> Object + +# 150| A +#-----| super -> S + +# 155| B +#-----| super -> S + +hello.rb: +# 1| EnglishWords + +# 11| Greeting +#-----| super -> Object +#-----| include -> EnglishWords + +# 18| HelloWorld +#-----| super -> Greeting + +modules.rb: +# 1| Empty + +# 4| Foo + +# 37| Bar +#-----| super -> Object + +# 60| MyModuleInGlobalScope + +# 63| Test + +# 83| Other + +# 88| IncludeTest +#-----| include -> Test + +# 95| IncludeTest2 +#-----| include -> Test + +# 101| PrependTest +#-----| prepend -> Test + +# 107| MM + +# 112| YY +#-----| super -> Object + +# 115| XX + +# 5| Foo::Bar + +# 19| Foo::ClassInFoo +#-----| super -> Object + +# 30| Foo::ClassInAnotherDefinitionOfFoo +#-----| super -> Object + +# 116| XX::YY +#-----| super -> YY + +# 65| Test::Foo1 + +# 70| Test::Foo2 + +# 76| Test::Foo3 + +# 84| Other::Foo1 + +# 6| Foo::Bar::ClassInFooBar +#-----| super -> Object + +# 71| Test::Foo2::Foo2 + +# 108| MM::MM + +# 49| Foo::Bar::ClassInAnotherDefinitionOfFooBar +#-----| super -> Object + +# 66| Test::Foo1::Bar +#-----| super -> Object + +# 91| Test::Foo1::Y + +# 97| Test::Foo1::Z + +# 103| Test::Foo2::Y + +# 72| Test::Foo2::Foo2::Bar +#-----| super -> Object + +# 120| Test::Foo1::Bar::Baz diff --git a/ruby/ql/test/library-tests/modules/ancestors.ql b/ruby/ql/test/library-tests/modules/ancestors.ql new file mode 100644 index 00000000000..897ddf94f35 --- /dev/null +++ b/ruby/ql/test/library-tests/modules/ancestors.ql @@ -0,0 +1,21 @@ +/** + * @kind graph + * @id rb/test/ancestors + */ + +import ruby + +query predicate nodes(Module node, string key, string value) { + key = "semmle.label" and value = node.toString() +} + +query predicate edges(Module source, Module target, string key, string value) { + key = "semmle.label" and + ( + target = source.getSuperClass() and value = "super" + or + target = source.getAPrependedModule() and value = "prepend" + or + target = source.getAnIncludedModule() and value = "include" + ) +} diff --git a/ruby/ql/test/library-tests/modules/callgraph.expected b/ruby/ql/test/library-tests/modules/callgraph.expected new file mode 100644 index 00000000000..4d5a29cce35 --- /dev/null +++ b/ruby/ql/test/library-tests/modules/callgraph.expected @@ -0,0 +1,129 @@ +getTarget +| calls.rb:2:5:2:14 | call to puts | calls.rb:87:5:87:17 | puts | +| calls.rb:5:1:5:3 | call to foo | calls.rb:1:1:3:3 | foo | +| calls.rb:5:1:5:3 | call to foo | calls.rb:71:1:75:3 | foo | +| calls.rb:8:5:8:14 | call to puts | calls.rb:87:5:87:17 | puts | +| calls.rb:11:1:11:8 | call to bar | calls.rb:7:1:9:3 | bar | +| calls.rb:13:1:13:8 | call to foo | calls.rb:1:1:3:3 | foo | +| calls.rb:13:1:13:8 | call to foo | calls.rb:71:1:75:3 | foo | +| calls.rb:22:5:22:15 | call to singleton_m | calls.rb:17:5:17:29 | singleton_m | +| calls.rb:23:5:23:20 | call to singleton_m | calls.rb:17:5:17:29 | singleton_m | +| calls.rb:27:1:27:13 | call to singleton_m | calls.rb:17:5:17:29 | singleton_m | +| calls.rb:30:5:30:13 | call to include | calls.rb:92:5:92:20 | include | +| calls.rb:38:9:38:18 | call to instance_m | calls.rb:16:5:16:23 | instance_m | +| calls.rb:39:9:39:23 | call to instance_m | calls.rb:16:5:16:23 | instance_m | +| calls.rb:46:5:46:9 | call to new | calls.rb:99:5:99:16 | new | +| calls.rb:47:1:47:5 | call to baz | calls.rb:37:5:43:7 | baz | +| calls.rb:49:1:49:12 | call to instance_m | calls.rb:16:5:16:23 | instance_m | +| calls.rb:53:9:53:13 | call to super | calls.rb:37:5:43:7 | baz | +| calls.rb:57:5:57:9 | call to new | calls.rb:99:5:99:16 | new | +| calls.rb:58:1:58:5 | call to baz | calls.rb:52:5:54:7 | baz | +| calls.rb:60:1:60:12 | call to instance_m | calls.rb:16:5:16:23 | instance_m | +| calls.rb:63:5:63:16 | call to bit_length | calls.rb:78:5:78:23 | bit_length | +| calls.rb:64:5:64:16 | call to bit_length | calls.rb:78:5:78:23 | bit_length | +| calls.rb:68:5:68:11 | yield ... | calls.rb:74:16:74:29 | { ... } | +| calls.rb:68:5:68:11 | yield ... | calls.rb:141:10:141:28 | { ... } | +| calls.rb:72:11:72:18 | call to new | calls.rb:99:5:99:16 | new | +| calls.rb:73:5:73:10 | ...[...] | calls.rb:103:5:103:15 | [] | +| calls.rb:74:5:74:29 | call to call_block | calls.rb:67:1:69:3 | call_block | +| calls.rb:74:22:74:27 | ...[...] | calls.rb:103:5:103:15 | [] | +| calls.rb:98:5:98:18 | call to include | calls.rb:92:5:92:20 | include | +| calls.rb:112:15:112:25 | call to length | calls.rb:108:3:108:17 | length | +| calls.rb:113:9:113:24 | yield ... | calls.rb:129:23:129:62 | { ... } | +| calls.rb:113:9:113:24 | yield ... | calls.rb:131:17:131:35 | { ... } | +| calls.rb:113:9:113:24 | yield ... | calls.rb:133:17:133:40 | { ... } | +| calls.rb:113:9:113:24 | yield ... | calls.rb:135:18:135:37 | { ... } | +| calls.rb:113:18:113:24 | ...[...] | calls.rb:107:3:107:13 | [] | +| calls.rb:120:5:120:20 | yield ... | calls.rb:123:7:123:30 | { ... } | +| calls.rb:123:1:123:30 | call to funny | calls.rb:119:1:121:3 | funny | +| calls.rb:123:13:123:29 | call to puts | calls.rb:87:5:87:17 | puts | +| calls.rb:123:18:123:29 | call to capitalize | calls.rb:83:5:83:23 | capitalize | +| calls.rb:125:1:125:14 | call to capitalize | calls.rb:83:5:83:23 | capitalize | +| calls.rb:126:1:126:12 | call to bit_length | calls.rb:78:5:78:23 | bit_length | +| calls.rb:127:1:127:5 | call to abs | calls.rb:79:5:79:16 | abs | +| calls.rb:129:1:129:62 | call to foreach | calls.rb:110:3:116:5 | foreach | +| calls.rb:129:32:129:61 | call to puts | calls.rb:87:5:87:17 | puts | +| calls.rb:131:1:131:35 | call to foreach | calls.rb:110:3:116:5 | foreach | +| calls.rb:131:23:131:34 | call to bit_length | calls.rb:78:5:78:23 | bit_length | +| calls.rb:133:1:133:40 | call to foreach | calls.rb:110:3:116:5 | foreach | +| calls.rb:133:23:133:39 | call to puts | calls.rb:87:5:87:17 | puts | +| calls.rb:135:1:135:37 | call to foreach | calls.rb:110:3:116:5 | foreach | +| calls.rb:135:27:135:36 | call to puts | calls.rb:87:5:87:17 | puts | +| calls.rb:138:5:138:17 | call to call_block | calls.rb:67:1:69:3 | call_block | +| calls.rb:141:1:141:28 | call to indirect | calls.rb:137:1:139:3 | indirect | +| calls.rb:141:16:141:27 | call to bit_length | calls.rb:78:5:78:23 | bit_length | +| calls.rb:146:9:146:17 | call to to_s | calls.rb:151:5:152:7 | to_s | +| calls.rb:146:9:146:17 | call to to_s | calls.rb:156:5:157:7 | to_s | +| calls.rb:160:1:160:5 | call to new | calls.rb:99:5:99:16 | new | +| calls.rb:160:1:160:14 | call to s_method | calls.rb:145:5:147:7 | s_method | +| calls.rb:161:1:161:5 | call to new | calls.rb:99:5:99:16 | new | +| calls.rb:161:1:161:14 | call to s_method | calls.rb:145:5:147:7 | s_method | +| calls.rb:162:1:162:5 | call to new | calls.rb:99:5:99:16 | new | +| calls.rb:162:1:162:14 | call to s_method | calls.rb:145:5:147:7 | s_method | +| calls.rb:167:1:167:15 | call to private_on_main | calls.rb:164:1:165:3 | private_on_main | +| hello.rb:12:5:12:24 | call to include | calls.rb:92:5:92:20 | include | +| hello.rb:14:16:14:20 | call to hello | hello.rb:2:5:4:7 | hello | +| hello.rb:20:16:20:20 | call to super | hello.rb:13:5:15:7 | message | +| hello.rb:20:30:20:34 | call to world | hello.rb:5:5:7:7 | world | +| modules.rb:12:5:12:26 | call to puts | calls.rb:87:5:87:17 | puts | +| modules.rb:22:3:22:19 | call to puts | calls.rb:87:5:87:17 | puts | +| modules.rb:33:3:33:25 | call to puts | calls.rb:87:5:87:17 | puts | +| modules.rb:44:3:44:19 | call to puts | calls.rb:87:5:87:17 | puts | +| modules.rb:55:3:55:30 | call to puts | calls.rb:87:5:87:17 | puts | +| modules.rb:89:3:89:16 | call to include | calls.rb:92:5:92:20 | include | +| modules.rb:90:3:90:38 | call to module_eval | calls.rb:91:5:91:24 | module_eval | +| modules.rb:90:24:90:36 | call to prepend | calls.rb:93:5:93:20 | prepend | +| modules.rb:96:3:96:14 | call to include | calls.rb:92:5:92:20 | include | +| modules.rb:102:3:102:16 | call to prepend | calls.rb:93:5:93:20 | prepend | +| private.rb:2:3:3:5 | call to private | calls.rb:94:5:94:20 | private | +| private.rb:10:3:10:19 | call to private | calls.rb:94:5:94:20 | private | +| private.rb:12:3:12:9 | call to private | calls.rb:94:5:94:20 | private | +| private.rb:24:1:24:5 | call to new | calls.rb:99:5:99:16 | new | +| private.rb:25:1:25:5 | call to new | calls.rb:99:5:99:16 | new | +| private.rb:26:1:26:5 | call to new | calls.rb:99:5:99:16 | new | +| private.rb:27:1:27:5 | call to new | calls.rb:99:5:99:16 | new | +| private.rb:28:1:28:5 | call to new | calls.rb:99:5:99:16 | new | +| private.rb:28:1:28:12 | call to public | private.rb:5:3:6:5 | public | +| private.rb:30:1:30:15 | call to private_on_main | private.rb:21:1:22:3 | private_on_main | +unresolvedCall +| calls.rb:19:5:19:14 | call to instance_m | +| calls.rb:20:5:20:19 | call to instance_m | +| calls.rb:26:1:26:12 | call to instance_m | +| calls.rb:31:5:31:14 | call to instance_m | +| calls.rb:32:5:32:19 | call to instance_m | +| calls.rb:34:5:34:15 | call to singleton_m | +| calls.rb:35:5:35:20 | call to singleton_m | +| calls.rb:41:9:41:19 | call to singleton_m | +| calls.rb:42:9:42:24 | call to singleton_m | +| calls.rb:48:1:48:13 | call to singleton_m | +| calls.rb:59:1:59:13 | call to singleton_m | +| calls.rb:112:11:112:25 | ... < ... | +| calls.rb:114:11:114:12 | ... + ... | +| calls.rb:129:1:129:13 | call to [] | +| calls.rb:129:48:129:59 | call to capitalize | +| calls.rb:131:1:131:7 | call to [] | +| calls.rb:133:1:133:7 | call to [] | +| calls.rb:133:28:133:39 | call to capitalize | +| calls.rb:135:1:135:8 | call to [] | +| calls.rb:135:4:135:5 | - ... | +| calls.rb:135:32:135:36 | call to abs | +| hello.rb:20:16:20:26 | ... + ... | +| hello.rb:20:16:20:34 | ... + ... | +| hello.rb:20:16:20:40 | ... + ... | +| private.rb:24:1:24:14 | call to private1 | +| private.rb:25:1:25:14 | call to private2 | +| private.rb:26:1:26:14 | call to private3 | +| private.rb:27:1:27:14 | call to private4 | +privateMethod +| calls.rb:1:1:3:3 | foo | +| calls.rb:62:1:65:3 | optional_arg | +| calls.rb:67:1:69:3 | call_block | +| calls.rb:71:1:75:3 | foo | +| calls.rb:119:1:121:3 | funny | +| calls.rb:137:1:139:3 | indirect | +| calls.rb:164:1:165:3 | private_on_main | +| private.rb:2:11:3:5 | private1 | +| private.rb:8:3:9:5 | private2 | +| private.rb:14:3:15:5 | private3 | +| private.rb:17:3:18:5 | private4 | +| private.rb:21:1:22:3 | private_on_main | diff --git a/ruby/ql/test/library-tests/modules/callgraph.ql b/ruby/ql/test/library-tests/modules/callgraph.ql new file mode 100644 index 00000000000..bc8a9d6f9f9 --- /dev/null +++ b/ruby/ql/test/library-tests/modules/callgraph.ql @@ -0,0 +1,7 @@ +import ruby + +query Callable getTarget(Call call) { result = call.getATarget() } + +query predicate unresolvedCall(Call call) { not exists(call.getATarget()) } + +query predicate privateMethod(Method m) { m.isPrivate() } diff --git a/ruby/ql/test/library-tests/modules/calls.rb b/ruby/ql/test/library-tests/modules/calls.rb new file mode 100644 index 00000000000..d5c9c774f06 --- /dev/null +++ b/ruby/ql/test/library-tests/modules/calls.rb @@ -0,0 +1,167 @@ +def foo + puts "foo" +end + +foo + +def self.bar + puts "bar" +end + +self.bar + +self.foo + +module M + def instance_m; end + def self.singleton_m; end + + instance_m # NoMethodError + self.instance_m # NoMethodError + + singleton_m + self.singleton_m +end + +M.instance_m # NoMethodError +M.singleton_m + +class C + include M + instance_m # NoMethodError + self.instance_m # NoMethodError + + singleton_m # NoMethodError + self.singleton_m # NoMethodError + + def baz + instance_m + self.instance_m + + singleton_m # NoMethodError + self.singleton_m # NoMethodError + end +end + +c = C.new +c.baz +c.singleton_m # NoMethodError +c.instance_m + +class D < C + def baz + super + end +end + +d = D.new +d.baz +d.singleton_m # NoMethodError +d.instance_m + +def optional_arg(a = 4, b: 5) + a.bit_length + b.bit_length +end + +def call_block + yield 1 +end + +def foo() + var = Hash.new + var[1] + call_block { |x| var[x] } +end + +class Integer + def bit_length; end + def abs; end +end + +class String + def capitalize; end +end + +module Kernel + def puts; end +end + +class Module + def module_eval; end + def include; end + def prepend; end + def private; end +end + +class Object < Module + include Kernel + def new; end +end + +class Hash + def []; end +end + +class Array + def []; end + def length; end + + def foreach &body + x = 0 + while x < self.length + yield x, self[x] + x += 1 + end + end +end + +def funny + yield "prefix: " +end + +funny { |i| puts i.capitalize} + +"a".capitalize +1.bit_length +1.abs + +["a","b","c"].foreach { |i, v| puts "#{i} -> #{v.capitalize}"} # TODO should resolve to String.capitalize + +[1,2,3].foreach { |i| i.bit_length} + +[1,2,3].foreach { |i| puts i.capitalize} # NoMethodError + +[1,-2,3].foreach { |_, v| puts v.abs} # TODO should resolve to Integer.abs + +def indirect &b + call_block &b +end + +indirect { |i| i.bit_length} + + +class S + def s_method + self.to_s + end +end + +class A < S + def to_s + end +end + +class B < S + def to_s + end +end + +S.new.s_method +A.new.s_method +B.new.s_method + +def private_on_main +end + +private_on_main diff --git a/ruby/ql/test/library-tests/modules/hello.rb b/ruby/ql/test/library-tests/modules/hello.rb new file mode 100644 index 00000000000..1cfa0f7e4f3 --- /dev/null +++ b/ruby/ql/test/library-tests/modules/hello.rb @@ -0,0 +1,22 @@ +module EnglishWords + def hello + return "hello" + end + def world + return "world" + end +end + + +class Greeting + include EnglishWords + def message + return hello + end +end + +class HelloWorld < Greeting + def message + return super + " " + world + "!" + end +end \ No newline at end of file diff --git a/ruby/ql/test/library-tests/modules/methods.expected b/ruby/ql/test/library-tests/modules/methods.expected new file mode 100644 index 00000000000..20e2b1ee242 --- /dev/null +++ b/ruby/ql/test/library-tests/modules/methods.expected @@ -0,0 +1,223 @@ +getMethod +| calls.rb:15:1:24:3 | M | instance_m | calls.rb:16:5:16:23 | instance_m | +| calls.rb:51:1:55:3 | D | baz | calls.rb:52:5:54:7 | baz | +| calls.rb:77:1:80:3 | Integer | abs | calls.rb:79:5:79:16 | abs | +| calls.rb:77:1:80:3 | Integer | bit_length | calls.rb:78:5:78:23 | bit_length | +| calls.rb:82:1:84:3 | String | capitalize | calls.rb:83:5:83:23 | capitalize | +| calls.rb:86:1:88:3 | Kernel | puts | calls.rb:87:5:87:17 | puts | +| calls.rb:90:1:95:3 | Module | include | calls.rb:92:5:92:20 | include | +| calls.rb:90:1:95:3 | Module | module_eval | calls.rb:91:5:91:24 | module_eval | +| calls.rb:90:1:95:3 | Module | prepend | calls.rb:93:5:93:20 | prepend | +| calls.rb:90:1:95:3 | Module | private | calls.rb:94:5:94:20 | private | +| calls.rb:97:1:100:3 | Object | call_block | calls.rb:67:1:69:3 | call_block | +| calls.rb:97:1:100:3 | Object | foo | calls.rb:1:1:3:3 | foo | +| calls.rb:97:1:100:3 | Object | foo | calls.rb:71:1:75:3 | foo | +| calls.rb:97:1:100:3 | Object | funny | calls.rb:119:1:121:3 | funny | +| calls.rb:97:1:100:3 | Object | indirect | calls.rb:137:1:139:3 | indirect | +| calls.rb:97:1:100:3 | Object | new | calls.rb:99:5:99:16 | new | +| calls.rb:97:1:100:3 | Object | optional_arg | calls.rb:62:1:65:3 | optional_arg | +| calls.rb:97:1:100:3 | Object | private_on_main | calls.rb:164:1:165:3 | private_on_main | +| calls.rb:97:1:100:3 | Object | private_on_main | private.rb:21:1:22:3 | private_on_main | +| calls.rb:102:1:104:3 | Hash | [] | calls.rb:103:5:103:15 | [] | +| calls.rb:106:1:117:3 | Array | [] | calls.rb:107:3:107:13 | [] | +| calls.rb:106:1:117:3 | Array | foreach | calls.rb:110:3:116:5 | foreach | +| calls.rb:106:1:117:3 | Array | length | calls.rb:108:3:108:17 | length | +| calls.rb:144:1:148:3 | S | s_method | calls.rb:145:5:147:7 | s_method | +| calls.rb:150:1:153:3 | A | to_s | calls.rb:151:5:152:7 | to_s | +| calls.rb:155:1:158:3 | B | to_s | calls.rb:156:5:157:7 | to_s | +| hello.rb:1:1:8:3 | EnglishWords | hello | hello.rb:2:5:4:7 | hello | +| hello.rb:1:1:8:3 | EnglishWords | world | hello.rb:5:5:7:7 | world | +| hello.rb:11:1:16:3 | Greeting | message | hello.rb:13:5:15:7 | message | +| hello.rb:18:1:22:3 | HelloWorld | message | hello.rb:19:5:21:7 | message | +| modules.rb:4:1:24:3 | Foo | method_in_another_definition_of_foo | modules.rb:27:3:28:5 | method_in_another_definition_of_foo | +| modules.rb:4:1:24:3 | Foo | method_in_foo | modules.rb:16:3:17:5 | method_in_foo | +| modules.rb:5:3:14:5 | Foo::Bar | method_in_another_definition_of_foo_bar | modules.rb:52:3:53:5 | method_in_another_definition_of_foo_bar | +| modules.rb:5:3:14:5 | Foo::Bar | method_in_foo_bar | modules.rb:9:5:10:7 | method_in_foo_bar | +| modules.rb:37:1:46:3 | Bar | method_a | modules.rb:38:3:39:5 | method_a | +| modules.rb:37:1:46:3 | Bar | method_b | modules.rb:41:3:42:5 | method_b | +| private.rb:1:1:19:3 | C | baz | calls.rb:37:5:43:7 | baz | +| private.rb:1:1:19:3 | C | private2 | private.rb:8:3:9:5 | private2 | +| private.rb:1:1:19:3 | C | private3 | private.rb:14:3:15:5 | private3 | +| private.rb:1:1:19:3 | C | private4 | private.rb:17:3:18:5 | private4 | +| private.rb:1:1:19:3 | C | public | private.rb:5:3:6:5 | public | +lookupMethod +| calls.rb:15:1:24:3 | M | instance_m | calls.rb:16:5:16:23 | instance_m | +| calls.rb:51:1:55:3 | D | baz | calls.rb:52:5:54:7 | baz | +| calls.rb:51:1:55:3 | D | call_block | calls.rb:67:1:69:3 | call_block | +| calls.rb:51:1:55:3 | D | foo | calls.rb:1:1:3:3 | foo | +| calls.rb:51:1:55:3 | D | foo | calls.rb:71:1:75:3 | foo | +| calls.rb:51:1:55:3 | D | funny | calls.rb:119:1:121:3 | funny | +| calls.rb:51:1:55:3 | D | indirect | calls.rb:137:1:139:3 | indirect | +| calls.rb:51:1:55:3 | D | instance_m | calls.rb:16:5:16:23 | instance_m | +| calls.rb:51:1:55:3 | D | new | calls.rb:99:5:99:16 | new | +| calls.rb:51:1:55:3 | D | optional_arg | calls.rb:62:1:65:3 | optional_arg | +| calls.rb:51:1:55:3 | D | private2 | private.rb:8:3:9:5 | private2 | +| calls.rb:51:1:55:3 | D | private3 | private.rb:14:3:15:5 | private3 | +| calls.rb:51:1:55:3 | D | private4 | private.rb:17:3:18:5 | private4 | +| calls.rb:51:1:55:3 | D | private_on_main | calls.rb:164:1:165:3 | private_on_main | +| calls.rb:51:1:55:3 | D | public | private.rb:5:3:6:5 | public | +| calls.rb:51:1:55:3 | D | puts | calls.rb:87:5:87:17 | puts | +| calls.rb:77:1:80:3 | Integer | abs | calls.rb:79:5:79:16 | abs | +| calls.rb:77:1:80:3 | Integer | bit_length | calls.rb:78:5:78:23 | bit_length | +| calls.rb:77:1:80:3 | Integer | new | calls.rb:99:5:99:16 | new | +| calls.rb:77:1:80:3 | Integer | puts | calls.rb:87:5:87:17 | puts | +| calls.rb:82:1:84:3 | String | call_block | calls.rb:67:1:69:3 | call_block | +| calls.rb:82:1:84:3 | String | capitalize | calls.rb:83:5:83:23 | capitalize | +| calls.rb:82:1:84:3 | String | foo | calls.rb:1:1:3:3 | foo | +| calls.rb:82:1:84:3 | String | foo | calls.rb:71:1:75:3 | foo | +| calls.rb:82:1:84:3 | String | funny | calls.rb:119:1:121:3 | funny | +| calls.rb:82:1:84:3 | String | indirect | calls.rb:137:1:139:3 | indirect | +| calls.rb:82:1:84:3 | String | new | calls.rb:99:5:99:16 | new | +| calls.rb:82:1:84:3 | String | optional_arg | calls.rb:62:1:65:3 | optional_arg | +| calls.rb:82:1:84:3 | String | private_on_main | calls.rb:164:1:165:3 | private_on_main | +| calls.rb:82:1:84:3 | String | puts | calls.rb:87:5:87:17 | puts | +| calls.rb:86:1:88:3 | Kernel | puts | calls.rb:87:5:87:17 | puts | +| calls.rb:90:1:95:3 | Module | call_block | calls.rb:67:1:69:3 | call_block | +| calls.rb:90:1:95:3 | Module | foo | calls.rb:1:1:3:3 | foo | +| calls.rb:90:1:95:3 | Module | foo | calls.rb:71:1:75:3 | foo | +| calls.rb:90:1:95:3 | Module | funny | calls.rb:119:1:121:3 | funny | +| calls.rb:90:1:95:3 | Module | include | calls.rb:92:5:92:20 | include | +| calls.rb:90:1:95:3 | Module | indirect | calls.rb:137:1:139:3 | indirect | +| calls.rb:90:1:95:3 | Module | module_eval | calls.rb:91:5:91:24 | module_eval | +| calls.rb:90:1:95:3 | Module | new | calls.rb:99:5:99:16 | new | +| calls.rb:90:1:95:3 | Module | optional_arg | calls.rb:62:1:65:3 | optional_arg | +| calls.rb:90:1:95:3 | Module | prepend | calls.rb:93:5:93:20 | prepend | +| calls.rb:90:1:95:3 | Module | private | calls.rb:94:5:94:20 | private | +| calls.rb:90:1:95:3 | Module | private_on_main | calls.rb:164:1:165:3 | private_on_main | +| calls.rb:90:1:95:3 | Module | puts | calls.rb:87:5:87:17 | puts | +| calls.rb:97:1:100:3 | Object | call_block | calls.rb:67:1:69:3 | call_block | +| calls.rb:97:1:100:3 | Object | foo | calls.rb:1:1:3:3 | foo | +| calls.rb:97:1:100:3 | Object | foo | calls.rb:71:1:75:3 | foo | +| calls.rb:97:1:100:3 | Object | funny | calls.rb:119:1:121:3 | funny | +| calls.rb:97:1:100:3 | Object | indirect | calls.rb:137:1:139:3 | indirect | +| calls.rb:97:1:100:3 | Object | new | calls.rb:99:5:99:16 | new | +| calls.rb:97:1:100:3 | Object | optional_arg | calls.rb:62:1:65:3 | optional_arg | +| calls.rb:97:1:100:3 | Object | private_on_main | calls.rb:164:1:165:3 | private_on_main | +| calls.rb:97:1:100:3 | Object | private_on_main | private.rb:21:1:22:3 | private_on_main | +| calls.rb:97:1:100:3 | Object | puts | calls.rb:87:5:87:17 | puts | +| calls.rb:102:1:104:3 | Hash | [] | calls.rb:103:5:103:15 | [] | +| calls.rb:102:1:104:3 | Hash | call_block | calls.rb:67:1:69:3 | call_block | +| calls.rb:102:1:104:3 | Hash | foo | calls.rb:1:1:3:3 | foo | +| calls.rb:102:1:104:3 | Hash | foo | calls.rb:71:1:75:3 | foo | +| calls.rb:102:1:104:3 | Hash | funny | calls.rb:119:1:121:3 | funny | +| calls.rb:102:1:104:3 | Hash | indirect | calls.rb:137:1:139:3 | indirect | +| calls.rb:102:1:104:3 | Hash | new | calls.rb:99:5:99:16 | new | +| calls.rb:102:1:104:3 | Hash | optional_arg | calls.rb:62:1:65:3 | optional_arg | +| calls.rb:102:1:104:3 | Hash | private_on_main | calls.rb:164:1:165:3 | private_on_main | +| calls.rb:102:1:104:3 | Hash | puts | calls.rb:87:5:87:17 | puts | +| calls.rb:106:1:117:3 | Array | [] | calls.rb:107:3:107:13 | [] | +| calls.rb:106:1:117:3 | Array | call_block | calls.rb:67:1:69:3 | call_block | +| calls.rb:106:1:117:3 | Array | foo | calls.rb:1:1:3:3 | foo | +| calls.rb:106:1:117:3 | Array | foo | calls.rb:71:1:75:3 | foo | +| calls.rb:106:1:117:3 | Array | foreach | calls.rb:110:3:116:5 | foreach | +| calls.rb:106:1:117:3 | Array | funny | calls.rb:119:1:121:3 | funny | +| calls.rb:106:1:117:3 | Array | indirect | calls.rb:137:1:139:3 | indirect | +| calls.rb:106:1:117:3 | Array | length | calls.rb:108:3:108:17 | length | +| calls.rb:106:1:117:3 | Array | new | calls.rb:99:5:99:16 | new | +| calls.rb:106:1:117:3 | Array | optional_arg | calls.rb:62:1:65:3 | optional_arg | +| calls.rb:106:1:117:3 | Array | private_on_main | calls.rb:164:1:165:3 | private_on_main | +| calls.rb:106:1:117:3 | Array | puts | calls.rb:87:5:87:17 | puts | +| calls.rb:144:1:148:3 | S | call_block | calls.rb:67:1:69:3 | call_block | +| calls.rb:144:1:148:3 | S | foo | calls.rb:1:1:3:3 | foo | +| calls.rb:144:1:148:3 | S | foo | calls.rb:71:1:75:3 | foo | +| calls.rb:144:1:148:3 | S | funny | calls.rb:119:1:121:3 | funny | +| calls.rb:144:1:148:3 | S | indirect | calls.rb:137:1:139:3 | indirect | +| calls.rb:144:1:148:3 | S | new | calls.rb:99:5:99:16 | new | +| calls.rb:144:1:148:3 | S | optional_arg | calls.rb:62:1:65:3 | optional_arg | +| calls.rb:144:1:148:3 | S | private_on_main | calls.rb:164:1:165:3 | private_on_main | +| calls.rb:144:1:148:3 | S | puts | calls.rb:87:5:87:17 | puts | +| calls.rb:144:1:148:3 | S | s_method | calls.rb:145:5:147:7 | s_method | +| calls.rb:150:1:153:3 | A | call_block | calls.rb:67:1:69:3 | call_block | +| calls.rb:150:1:153:3 | A | foo | calls.rb:1:1:3:3 | foo | +| calls.rb:150:1:153:3 | A | foo | calls.rb:71:1:75:3 | foo | +| calls.rb:150:1:153:3 | A | funny | calls.rb:119:1:121:3 | funny | +| calls.rb:150:1:153:3 | A | indirect | calls.rb:137:1:139:3 | indirect | +| calls.rb:150:1:153:3 | A | new | calls.rb:99:5:99:16 | new | +| calls.rb:150:1:153:3 | A | optional_arg | calls.rb:62:1:65:3 | optional_arg | +| calls.rb:150:1:153:3 | A | private_on_main | calls.rb:164:1:165:3 | private_on_main | +| calls.rb:150:1:153:3 | A | puts | calls.rb:87:5:87:17 | puts | +| calls.rb:150:1:153:3 | A | s_method | calls.rb:145:5:147:7 | s_method | +| calls.rb:150:1:153:3 | A | to_s | calls.rb:151:5:152:7 | to_s | +| calls.rb:155:1:158:3 | B | call_block | calls.rb:67:1:69:3 | call_block | +| calls.rb:155:1:158:3 | B | foo | calls.rb:1:1:3:3 | foo | +| calls.rb:155:1:158:3 | B | foo | calls.rb:71:1:75:3 | foo | +| calls.rb:155:1:158:3 | B | funny | calls.rb:119:1:121:3 | funny | +| calls.rb:155:1:158:3 | B | indirect | calls.rb:137:1:139:3 | indirect | +| calls.rb:155:1:158:3 | B | new | calls.rb:99:5:99:16 | new | +| calls.rb:155:1:158:3 | B | optional_arg | calls.rb:62:1:65:3 | optional_arg | +| calls.rb:155:1:158:3 | B | private_on_main | calls.rb:164:1:165:3 | private_on_main | +| calls.rb:155:1:158:3 | B | puts | calls.rb:87:5:87:17 | puts | +| calls.rb:155:1:158:3 | B | s_method | calls.rb:145:5:147:7 | s_method | +| calls.rb:155:1:158:3 | B | to_s | calls.rb:156:5:157:7 | to_s | +| file://:0:0:0:0 | Class | include | calls.rb:92:5:92:20 | include | +| file://:0:0:0:0 | Class | module_eval | calls.rb:91:5:91:24 | module_eval | +| file://:0:0:0:0 | Class | new | calls.rb:99:5:99:16 | new | +| file://:0:0:0:0 | Class | prepend | calls.rb:93:5:93:20 | prepend | +| file://:0:0:0:0 | Class | private | calls.rb:94:5:94:20 | private | +| file://:0:0:0:0 | Class | puts | calls.rb:87:5:87:17 | puts | +| file://:0:0:0:0 | Complex | new | calls.rb:99:5:99:16 | new | +| file://:0:0:0:0 | Complex | puts | calls.rb:87:5:87:17 | puts | +| file://:0:0:0:0 | FalseClass | new | calls.rb:99:5:99:16 | new | +| file://:0:0:0:0 | FalseClass | puts | calls.rb:87:5:87:17 | puts | +| file://:0:0:0:0 | Float | new | calls.rb:99:5:99:16 | new | +| file://:0:0:0:0 | Float | puts | calls.rb:87:5:87:17 | puts | +| file://:0:0:0:0 | NilClass | new | calls.rb:99:5:99:16 | new | +| file://:0:0:0:0 | NilClass | puts | calls.rb:87:5:87:17 | puts | +| file://:0:0:0:0 | Numeric | new | calls.rb:99:5:99:16 | new | +| file://:0:0:0:0 | Numeric | puts | calls.rb:87:5:87:17 | puts | +| file://:0:0:0:0 | Rational | new | calls.rb:99:5:99:16 | new | +| file://:0:0:0:0 | Rational | puts | calls.rb:87:5:87:17 | puts | +| file://:0:0:0:0 | TrueClass | new | calls.rb:99:5:99:16 | new | +| file://:0:0:0:0 | TrueClass | puts | calls.rb:87:5:87:17 | puts | +| hello.rb:1:1:8:3 | EnglishWords | hello | hello.rb:2:5:4:7 | hello | +| hello.rb:1:1:8:3 | EnglishWords | world | hello.rb:5:5:7:7 | world | +| hello.rb:11:1:16:3 | Greeting | hello | hello.rb:2:5:4:7 | hello | +| hello.rb:11:1:16:3 | Greeting | message | hello.rb:13:5:15:7 | message | +| hello.rb:11:1:16:3 | Greeting | new | calls.rb:99:5:99:16 | new | +| hello.rb:11:1:16:3 | Greeting | puts | calls.rb:87:5:87:17 | puts | +| hello.rb:11:1:16:3 | Greeting | world | hello.rb:5:5:7:7 | world | +| hello.rb:18:1:22:3 | HelloWorld | hello | hello.rb:2:5:4:7 | hello | +| hello.rb:18:1:22:3 | HelloWorld | message | hello.rb:19:5:21:7 | message | +| hello.rb:18:1:22:3 | HelloWorld | new | calls.rb:99:5:99:16 | new | +| hello.rb:18:1:22:3 | HelloWorld | puts | calls.rb:87:5:87:17 | puts | +| hello.rb:18:1:22:3 | HelloWorld | world | hello.rb:5:5:7:7 | world | +| modules.rb:4:1:24:3 | Foo | method_in_another_definition_of_foo | modules.rb:27:3:28:5 | method_in_another_definition_of_foo | +| modules.rb:4:1:24:3 | Foo | method_in_foo | modules.rb:16:3:17:5 | method_in_foo | +| modules.rb:5:3:14:5 | Foo::Bar | method_in_another_definition_of_foo_bar | modules.rb:52:3:53:5 | method_in_another_definition_of_foo_bar | +| modules.rb:5:3:14:5 | Foo::Bar | method_in_foo_bar | modules.rb:9:5:10:7 | method_in_foo_bar | +| modules.rb:6:5:7:7 | Foo::Bar::ClassInFooBar | new | calls.rb:99:5:99:16 | new | +| modules.rb:6:5:7:7 | Foo::Bar::ClassInFooBar | puts | calls.rb:87:5:87:17 | puts | +| modules.rb:19:3:20:5 | Foo::ClassInFoo | new | calls.rb:99:5:99:16 | new | +| modules.rb:19:3:20:5 | Foo::ClassInFoo | puts | calls.rb:87:5:87:17 | puts | +| modules.rb:30:3:31:5 | Foo::ClassInAnotherDefinitionOfFoo | new | calls.rb:99:5:99:16 | new | +| modules.rb:30:3:31:5 | Foo::ClassInAnotherDefinitionOfFoo | puts | calls.rb:87:5:87:17 | puts | +| modules.rb:37:1:46:3 | Bar | method_a | modules.rb:38:3:39:5 | method_a | +| modules.rb:37:1:46:3 | Bar | method_b | modules.rb:41:3:42:5 | method_b | +| modules.rb:37:1:46:3 | Bar | new | calls.rb:99:5:99:16 | new | +| modules.rb:37:1:46:3 | Bar | puts | calls.rb:87:5:87:17 | puts | +| modules.rb:49:3:50:5 | Foo::Bar::ClassInAnotherDefinitionOfFooBar | new | calls.rb:99:5:99:16 | new | +| modules.rb:49:3:50:5 | Foo::Bar::ClassInAnotherDefinitionOfFooBar | puts | calls.rb:87:5:87:17 | puts | +| modules.rb:66:5:67:7 | Test::Foo1::Bar | new | calls.rb:99:5:99:16 | new | +| modules.rb:66:5:67:7 | Test::Foo1::Bar | puts | calls.rb:87:5:87:17 | puts | +| modules.rb:72:5:73:7 | Test::Foo2::Foo2::Bar | new | calls.rb:99:5:99:16 | new | +| modules.rb:72:5:73:7 | Test::Foo2::Foo2::Bar | puts | calls.rb:87:5:87:17 | puts | +| modules.rb:112:1:113:3 | YY | new | calls.rb:99:5:99:16 | new | +| modules.rb:112:1:113:3 | YY | puts | calls.rb:87:5:87:17 | puts | +| modules.rb:116:7:117:9 | XX::YY | new | calls.rb:99:5:99:16 | new | +| modules.rb:116:7:117:9 | XX::YY | puts | calls.rb:87:5:87:17 | puts | +| private.rb:1:1:19:3 | C | baz | calls.rb:37:5:43:7 | baz | +| private.rb:1:1:19:3 | C | call_block | calls.rb:67:1:69:3 | call_block | +| private.rb:1:1:19:3 | C | foo | calls.rb:1:1:3:3 | foo | +| private.rb:1:1:19:3 | C | foo | calls.rb:71:1:75:3 | foo | +| private.rb:1:1:19:3 | C | funny | calls.rb:119:1:121:3 | funny | +| private.rb:1:1:19:3 | C | indirect | calls.rb:137:1:139:3 | indirect | +| private.rb:1:1:19:3 | C | instance_m | calls.rb:16:5:16:23 | instance_m | +| private.rb:1:1:19:3 | C | new | calls.rb:99:5:99:16 | new | +| private.rb:1:1:19:3 | C | optional_arg | calls.rb:62:1:65:3 | optional_arg | +| private.rb:1:1:19:3 | C | private2 | private.rb:8:3:9:5 | private2 | +| private.rb:1:1:19:3 | C | private3 | private.rb:14:3:15:5 | private3 | +| private.rb:1:1:19:3 | C | private4 | private.rb:17:3:18:5 | private4 | +| private.rb:1:1:19:3 | C | private_on_main | calls.rb:164:1:165:3 | private_on_main | +| private.rb:1:1:19:3 | C | private_on_main | private.rb:21:1:22:3 | private_on_main | +| private.rb:1:1:19:3 | C | public | private.rb:5:3:6:5 | public | +| private.rb:1:1:19:3 | C | puts | calls.rb:87:5:87:17 | puts | diff --git a/ruby/ql/test/library-tests/modules/methods.ql b/ruby/ql/test/library-tests/modules/methods.ql new file mode 100644 index 00000000000..e92ce65e684 --- /dev/null +++ b/ruby/ql/test/library-tests/modules/methods.ql @@ -0,0 +1,8 @@ +import ruby +import codeql.ruby.ast.internal.Module as M + +query MethodBase getMethod(Module m, string name) { + result = M::ExposedForTestingOnly::getMethod(m, name) +} + +query MethodBase lookupMethod(Module m, string name) { result = M::lookupMethod(m, name) } diff --git a/ruby/ql/test/library-tests/modules/modules.expected b/ruby/ql/test/library-tests/modules/modules.expected new file mode 100644 index 00000000000..b7402d83003 --- /dev/null +++ b/ruby/ql/test/library-tests/modules/modules.expected @@ -0,0 +1,152 @@ +getModule +| calls.rb:15:1:24:3 | M | +| calls.rb:51:1:55:3 | D | +| calls.rb:77:1:80:3 | Integer | +| calls.rb:82:1:84:3 | String | +| calls.rb:86:1:88:3 | Kernel | +| calls.rb:90:1:95:3 | Module | +| calls.rb:97:1:100:3 | Object | +| calls.rb:102:1:104:3 | Hash | +| calls.rb:106:1:117:3 | Array | +| calls.rb:144:1:148:3 | S | +| calls.rb:150:1:153:3 | A | +| calls.rb:155:1:158:3 | B | +| file://:0:0:0:0 | BasicObject | +| file://:0:0:0:0 | Class | +| file://:0:0:0:0 | Complex | +| file://:0:0:0:0 | FalseClass | +| file://:0:0:0:0 | Float | +| file://:0:0:0:0 | NilClass | +| file://:0:0:0:0 | Numeric | +| file://:0:0:0:0 | Proc | +| file://:0:0:0:0 | Rational | +| file://:0:0:0:0 | Symbol | +| file://:0:0:0:0 | TrueClass | +| hello.rb:1:1:8:3 | EnglishWords | +| hello.rb:11:1:16:3 | Greeting | +| hello.rb:18:1:22:3 | HelloWorld | +| modules.rb:1:1:2:3 | Empty | +| modules.rb:4:1:24:3 | Foo | +| modules.rb:5:3:14:5 | Foo::Bar | +| modules.rb:6:5:7:7 | Foo::Bar::ClassInFooBar | +| modules.rb:19:3:20:5 | Foo::ClassInFoo | +| modules.rb:30:3:31:5 | Foo::ClassInAnotherDefinitionOfFoo | +| modules.rb:37:1:46:3 | Bar | +| modules.rb:49:3:50:5 | Foo::Bar::ClassInAnotherDefinitionOfFooBar | +| modules.rb:60:1:61:3 | MyModuleInGlobalScope | +| modules.rb:63:1:81:3 | Test | +| modules.rb:65:3:68:5 | Test::Foo1 | +| modules.rb:66:5:67:7 | Test::Foo1::Bar | +| modules.rb:70:3:74:5 | Test::Foo2 | +| modules.rb:71:5:71:19 | Test::Foo2::Foo2 | +| modules.rb:72:5:73:7 | Test::Foo2::Foo2::Bar | +| modules.rb:76:3:80:5 | Test::Foo3 | +| modules.rb:83:1:86:3 | Other | +| modules.rb:84:3:85:5 | Other::Foo1 | +| modules.rb:88:1:93:3 | IncludeTest | +| modules.rb:91:3:92:5 | Test::Foo1::Y | +| modules.rb:95:1:99:3 | IncludeTest2 | +| modules.rb:97:3:98:5 | Test::Foo1::Z | +| modules.rb:101:1:105:3 | PrependTest | +| modules.rb:103:3:104:5 | Test::Foo2::Y | +| modules.rb:107:1:110:3 | MM | +| modules.rb:108:3:109:5 | MM::MM | +| modules.rb:112:1:113:3 | YY | +| modules.rb:115:1:118:3 | XX | +| modules.rb:116:7:117:9 | XX::YY | +| modules.rb:120:1:121:3 | Test::Foo1::Bar::Baz | +| private.rb:1:1:19:3 | C | +getADeclaration +| calls.rb:15:1:24:3 | M | calls.rb:15:1:24:3 | M | +| calls.rb:51:1:55:3 | D | calls.rb:51:1:55:3 | D | +| calls.rb:77:1:80:3 | Integer | calls.rb:77:1:80:3 | Integer | +| calls.rb:82:1:84:3 | String | calls.rb:82:1:84:3 | String | +| calls.rb:86:1:88:3 | Kernel | calls.rb:86:1:88:3 | Kernel | +| calls.rb:90:1:95:3 | Module | calls.rb:90:1:95:3 | Module | +| calls.rb:97:1:100:3 | Object | calls.rb:1:1:167:16 | calls.rb | +| calls.rb:97:1:100:3 | Object | calls.rb:97:1:100:3 | Object | +| calls.rb:97:1:100:3 | Object | hello.rb:1:1:22:3 | hello.rb | +| calls.rb:97:1:100:3 | Object | modules.rb:1:1:122:1 | modules.rb | +| calls.rb:97:1:100:3 | Object | private.rb:1:1:30:15 | private.rb | +| calls.rb:102:1:104:3 | Hash | calls.rb:102:1:104:3 | Hash | +| calls.rb:106:1:117:3 | Array | calls.rb:106:1:117:3 | Array | +| calls.rb:144:1:148:3 | S | calls.rb:144:1:148:3 | S | +| calls.rb:150:1:153:3 | A | calls.rb:150:1:153:3 | A | +| calls.rb:155:1:158:3 | B | calls.rb:155:1:158:3 | B | +| hello.rb:1:1:8:3 | EnglishWords | hello.rb:1:1:8:3 | EnglishWords | +| hello.rb:11:1:16:3 | Greeting | hello.rb:11:1:16:3 | Greeting | +| hello.rb:18:1:22:3 | HelloWorld | hello.rb:18:1:22:3 | HelloWorld | +| modules.rb:1:1:2:3 | Empty | modules.rb:1:1:2:3 | Empty | +| modules.rb:4:1:24:3 | Foo | modules.rb:4:1:24:3 | Foo | +| modules.rb:4:1:24:3 | Foo | modules.rb:26:1:35:3 | Foo | +| modules.rb:5:3:14:5 | Foo::Bar | modules.rb:5:3:14:5 | Bar | +| modules.rb:5:3:14:5 | Foo::Bar | modules.rb:48:1:57:3 | Bar | +| modules.rb:6:5:7:7 | Foo::Bar::ClassInFooBar | modules.rb:6:5:7:7 | ClassInFooBar | +| modules.rb:19:3:20:5 | Foo::ClassInFoo | modules.rb:19:3:20:5 | ClassInFoo | +| modules.rb:30:3:31:5 | Foo::ClassInAnotherDefinitionOfFoo | modules.rb:30:3:31:5 | ClassInAnotherDefinitionOfFoo | +| modules.rb:37:1:46:3 | Bar | modules.rb:37:1:46:3 | Bar | +| modules.rb:37:1:46:3 | Bar | modules.rb:78:5:79:7 | Bar | +| modules.rb:49:3:50:5 | Foo::Bar::ClassInAnotherDefinitionOfFooBar | modules.rb:49:3:50:5 | ClassInAnotherDefinitionOfFooBar | +| modules.rb:60:1:61:3 | MyModuleInGlobalScope | modules.rb:60:1:61:3 | MyModuleInGlobalScope | +| modules.rb:63:1:81:3 | Test | modules.rb:63:1:81:3 | Test | +| modules.rb:65:3:68:5 | Test::Foo1 | modules.rb:65:3:68:5 | Foo1 | +| modules.rb:66:5:67:7 | Test::Foo1::Bar | modules.rb:66:5:67:7 | Bar | +| modules.rb:70:3:74:5 | Test::Foo2 | modules.rb:70:3:74:5 | Foo2 | +| modules.rb:71:5:71:19 | Test::Foo2::Foo2 | modules.rb:71:5:71:19 | Foo2 | +| modules.rb:72:5:73:7 | Test::Foo2::Foo2::Bar | modules.rb:72:5:73:7 | Bar | +| modules.rb:76:3:80:5 | Test::Foo3 | modules.rb:76:3:80:5 | Foo3 | +| modules.rb:83:1:86:3 | Other | modules.rb:83:1:86:3 | Other | +| modules.rb:84:3:85:5 | Other::Foo1 | modules.rb:84:3:85:5 | Foo1 | +| modules.rb:88:1:93:3 | IncludeTest | modules.rb:88:1:93:3 | IncludeTest | +| modules.rb:91:3:92:5 | Test::Foo1::Y | modules.rb:91:3:92:5 | Y | +| modules.rb:95:1:99:3 | IncludeTest2 | modules.rb:95:1:99:3 | IncludeTest2 | +| modules.rb:97:3:98:5 | Test::Foo1::Z | modules.rb:97:3:98:5 | Z | +| modules.rb:101:1:105:3 | PrependTest | modules.rb:101:1:105:3 | PrependTest | +| modules.rb:103:3:104:5 | Test::Foo2::Y | modules.rb:103:3:104:5 | Y | +| modules.rb:107:1:110:3 | MM | modules.rb:107:1:110:3 | MM | +| modules.rb:108:3:109:5 | MM::MM | modules.rb:108:3:109:5 | MM | +| modules.rb:112:1:113:3 | YY | modules.rb:112:1:113:3 | YY | +| modules.rb:115:1:118:3 | XX | modules.rb:115:1:118:3 | XX | +| modules.rb:116:7:117:9 | XX::YY | modules.rb:116:7:117:9 | YY | +| modules.rb:120:1:121:3 | Test::Foo1::Bar::Baz | modules.rb:120:1:121:3 | Baz | +| private.rb:1:1:19:3 | C | calls.rb:29:1:44:3 | C | +| private.rb:1:1:19:3 | C | private.rb:1:1:19:3 | C | +getSuperClass +| calls.rb:51:1:55:3 | D | private.rb:1:1:19:3 | C | +| calls.rb:77:1:80:3 | Integer | file://:0:0:0:0 | Numeric | +| calls.rb:82:1:84:3 | String | calls.rb:97:1:100:3 | Object | +| calls.rb:90:1:95:3 | Module | calls.rb:97:1:100:3 | Object | +| calls.rb:97:1:100:3 | Object | file://:0:0:0:0 | BasicObject | +| calls.rb:102:1:104:3 | Hash | calls.rb:97:1:100:3 | Object | +| calls.rb:106:1:117:3 | Array | calls.rb:97:1:100:3 | Object | +| calls.rb:144:1:148:3 | S | calls.rb:97:1:100:3 | Object | +| calls.rb:150:1:153:3 | A | calls.rb:144:1:148:3 | S | +| calls.rb:155:1:158:3 | B | calls.rb:144:1:148:3 | S | +| file://:0:0:0:0 | Class | calls.rb:90:1:95:3 | Module | +| file://:0:0:0:0 | Complex | file://:0:0:0:0 | Numeric | +| file://:0:0:0:0 | FalseClass | calls.rb:97:1:100:3 | Object | +| file://:0:0:0:0 | Float | file://:0:0:0:0 | Numeric | +| file://:0:0:0:0 | NilClass | calls.rb:97:1:100:3 | Object | +| file://:0:0:0:0 | Numeric | calls.rb:97:1:100:3 | Object | +| file://:0:0:0:0 | Rational | file://:0:0:0:0 | Numeric | +| file://:0:0:0:0 | TrueClass | calls.rb:97:1:100:3 | Object | +| hello.rb:11:1:16:3 | Greeting | calls.rb:97:1:100:3 | Object | +| hello.rb:18:1:22:3 | HelloWorld | hello.rb:11:1:16:3 | Greeting | +| modules.rb:6:5:7:7 | Foo::Bar::ClassInFooBar | calls.rb:97:1:100:3 | Object | +| modules.rb:19:3:20:5 | Foo::ClassInFoo | calls.rb:97:1:100:3 | Object | +| modules.rb:30:3:31:5 | Foo::ClassInAnotherDefinitionOfFoo | calls.rb:97:1:100:3 | Object | +| modules.rb:37:1:46:3 | Bar | calls.rb:97:1:100:3 | Object | +| modules.rb:49:3:50:5 | Foo::Bar::ClassInAnotherDefinitionOfFooBar | calls.rb:97:1:100:3 | Object | +| modules.rb:66:5:67:7 | Test::Foo1::Bar | calls.rb:97:1:100:3 | Object | +| modules.rb:72:5:73:7 | Test::Foo2::Foo2::Bar | calls.rb:97:1:100:3 | Object | +| modules.rb:112:1:113:3 | YY | calls.rb:97:1:100:3 | Object | +| modules.rb:116:7:117:9 | XX::YY | modules.rb:112:1:113:3 | YY | +| private.rb:1:1:19:3 | C | calls.rb:97:1:100:3 | Object | +getAPrependedModule +| modules.rb:101:1:105:3 | PrependTest | modules.rb:63:1:81:3 | Test | +getAnIncludedModule +| calls.rb:97:1:100:3 | Object | calls.rb:86:1:88:3 | Kernel | +| hello.rb:11:1:16:3 | Greeting | hello.rb:1:1:8:3 | EnglishWords | +| modules.rb:88:1:93:3 | IncludeTest | modules.rb:63:1:81:3 | Test | +| modules.rb:95:1:99:3 | IncludeTest2 | modules.rb:63:1:81:3 | Test | +| private.rb:1:1:19:3 | C | calls.rb:15:1:24:3 | M | diff --git a/ruby/ql/test/library-tests/modules/modules.ql b/ruby/ql/test/library-tests/modules/modules.ql new file mode 100644 index 00000000000..a29d2e93756 --- /dev/null +++ b/ruby/ql/test/library-tests/modules/modules.ql @@ -0,0 +1,11 @@ +import ruby + +query Module getModule() { any() } + +query ModuleBase getADeclaration(Module m) { result = m.getADeclaration() } + +query Module getSuperClass(Module m) { result = m.getSuperClass() } + +query Module getAPrependedModule(Module m) { result = m.getAPrependedModule() } + +query Module getAnIncludedModule(Module m) { result = m.getAnIncludedModule() } diff --git a/ruby/ql/test/library-tests/modules/modules.rb b/ruby/ql/test/library-tests/modules/modules.rb new file mode 100644 index 00000000000..8287b0a1bc4 --- /dev/null +++ b/ruby/ql/test/library-tests/modules/modules.rb @@ -0,0 +1,122 @@ +module Empty +end + +module Foo + module Bar + class ClassInFooBar + end + + def method_in_foo_bar + end + + puts 'module Foo::Bar' + $global_var = 0 + end + + def method_in_foo + end + + class ClassInFoo + end + + puts 'module Foo' + $global_var = 1 +end + +module Foo + def method_in_another_definition_of_foo + end + + class ClassInAnotherDefinitionOfFoo + end + + puts 'module Foo again' + $global_var = 2 +end + +module Bar + def method_a + end + + def method_b + end + + puts 'module Bar' + $global_var = 3 +end + +module Foo::Bar + class ClassInAnotherDefinitionOfFooBar + end + + def method_in_another_definition_of_foo_bar + end + + puts 'module Foo::Bar again' + $global_var = 4 +end + +# a module where the name is a scope resolution using the global scope +module ::MyModuleInGlobalScope +end + +module Test + + module Foo1 + class Foo1::Bar + end + end + + module Foo2 + module Foo2 end + class Foo2::Bar + end + end + + module Foo3 + Foo3 = Object + class Foo3::Bar + end + end +end + +module Other + module Foo1 + end +end + +module IncludeTest + include ::Test + Object.module_eval { prepend Other } + module Foo1::Y + end +end + +module IncludeTest2 + include Test + module Foo1::Z + end +end + +module PrependTest + prepend ::Test + module Foo2::Y + end +end + +module MM + module MM::MM + end +end + +class YY +end + +module XX + class YY < YY + end +end + +module Test::Foo1::Bar::Baz +end + diff --git a/ruby/ql/test/library-tests/modules/private.rb b/ruby/ql/test/library-tests/modules/private.rb new file mode 100644 index 00000000000..5c1d666be3a --- /dev/null +++ b/ruby/ql/test/library-tests/modules/private.rb @@ -0,0 +1,30 @@ +class C + private def private1 + end + + def public + end + + def private2 + end + private :private2 + + private + + def private3 + end + + def private4 + end +end + +def private_on_main +end + +C.new.private1 +C.new.private2 +C.new.private3 +C.new.private4 +C.new.public + +private_on_main \ No newline at end of file diff --git a/ruby/ql/test/library-tests/modules/superclasses.expected b/ruby/ql/test/library-tests/modules/superclasses.expected new file mode 100644 index 00000000000..b1948a9976e --- /dev/null +++ b/ruby/ql/test/library-tests/modules/superclasses.expected @@ -0,0 +1,148 @@ +calls.rb: +# 102| Hash +#-----| -> Object + +# 77| Integer +#-----| -> Numeric + +# 86| Kernel + +# 90| Module +#-----| -> Object + +# 97| Object +#-----| -> BasicObject + +# 106| Array +#-----| -> Object + +#-----| BasicObject + +#-----| Class +#-----| -> Module + +#-----| Complex +#-----| -> Numeric + +#-----| FalseClass +#-----| -> Object + +#-----| Float +#-----| -> Numeric + +#-----| NilClass +#-----| -> Object + +#-----| Numeric +#-----| -> Object + +#-----| Proc + +#-----| Rational +#-----| -> Numeric + +#-----| Symbol + +#-----| TrueClass +#-----| -> Object + +# 15| M + +private.rb: +# 1| C +#-----| -> Object + +calls.rb: +# 51| D +#-----| -> C + +# 82| String +#-----| -> Object + +# 144| S +#-----| -> Object + +# 150| A +#-----| -> S + +# 155| B +#-----| -> S + +hello.rb: +# 1| EnglishWords + +# 11| Greeting +#-----| -> Object + +# 18| HelloWorld +#-----| -> Greeting + +modules.rb: +# 1| Empty + +# 4| Foo + +# 37| Bar +#-----| -> Object + +# 60| MyModuleInGlobalScope + +# 63| Test + +# 83| Other + +# 88| IncludeTest + +# 95| IncludeTest2 + +# 101| PrependTest + +# 107| MM + +# 112| YY +#-----| -> Object + +# 115| XX + +# 5| Foo::Bar + +# 19| Foo::ClassInFoo +#-----| -> Object + +# 30| Foo::ClassInAnotherDefinitionOfFoo +#-----| -> Object + +# 116| XX::YY +#-----| -> YY + +# 65| Test::Foo1 + +# 70| Test::Foo2 + +# 76| Test::Foo3 + +# 84| Other::Foo1 + +# 6| Foo::Bar::ClassInFooBar +#-----| -> Object + +# 71| Test::Foo2::Foo2 + +# 108| MM::MM + +# 49| Foo::Bar::ClassInAnotherDefinitionOfFooBar +#-----| -> Object + +# 66| Test::Foo1::Bar +#-----| -> Object + +# 91| Test::Foo1::Y + +# 97| Test::Foo1::Z + +# 103| Test::Foo2::Y + +# 72| Test::Foo2::Foo2::Bar +#-----| -> Object + +# 120| Test::Foo1::Bar::Baz diff --git a/ruby/ql/test/library-tests/modules/superclasses.ql b/ruby/ql/test/library-tests/modules/superclasses.ql new file mode 100644 index 00000000000..d2141aa38ff --- /dev/null +++ b/ruby/ql/test/library-tests/modules/superclasses.ql @@ -0,0 +1,12 @@ +/** + * @kind graph + * @id rb/test/supertypes + */ + +import ruby + +query predicate nodes(Module node, string key, string value) { + key = "semmle.label" and value = node.toString() +} + +query predicate edges(Module source, Module target) { target = source.getSuperClass() } diff --git a/ruby/ql/test/library-tests/regexp/parse.expected b/ruby/ql/test/library-tests/regexp/parse.expected new file mode 100644 index 00000000000..130d828a5d5 --- /dev/null +++ b/ruby/ql/test/library-tests/regexp/parse.expected @@ -0,0 +1,556 @@ +regexp.rb: +# 5| [RegExpConstant, RegExpNormalChar] a + +# 5| [RegExpSequence] abc +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 5| [RegExpConstant, RegExpNormalChar] b + +# 5| [RegExpConstant, RegExpNormalChar] c + +# 8| [RegExpStar] a* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 8| [RegExpConstant, RegExpNormalChar] a + +# 8| [RegExpSequence] a*b+c?d +#-----| 0 -> [RegExpStar] a* +#-----| 1 -> [RegExpPlus] b+ +#-----| 2 -> [RegExpOpt] c? +#-----| 3 -> [RegExpConstant, RegExpNormalChar] d + +# 8| [RegExpPlus] b+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] b + +# 8| [RegExpConstant, RegExpNormalChar] b + +# 8| [RegExpOpt] c? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] c + +# 8| [RegExpConstant, RegExpNormalChar] c + +# 8| [RegExpConstant, RegExpNormalChar] d + +# 9| [RegExpRange] a{4,8} +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 9| [RegExpConstant, RegExpNormalChar] a + +# 9| [RegExpNormalChar] 4 + +# 9| [RegExpNormalChar] , + +# 9| [RegExpNormalChar] 8 + +# 9| [RegExpNormalChar] } + +# 10| [RegExpRange] a{,8} +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 10| [RegExpConstant, RegExpNormalChar] a + +# 10| [RegExpNormalChar] , + +# 10| [RegExpNormalChar] 8 + +# 10| [RegExpNormalChar] } + +# 11| [InfiniteRepetitionQuantifier, RegExpRange] a{3,} +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 11| [RegExpConstant, RegExpNormalChar] a + +# 11| [RegExpNormalChar] 3 + +# 11| [RegExpNormalChar] , + +# 11| [RegExpNormalChar] } + +# 12| [RegExpRange] a{7} +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 12| [RegExpConstant, RegExpNormalChar] a + +# 12| [RegExpNormalChar] 7 + +# 12| [RegExpNormalChar] } + +# 15| [RegExpAlt] foo|bar +#-----| 0 -> [RegExpSequence] foo +#-----| 1 -> [RegExpSequence] bar + +# 15| [RegExpConstant, RegExpNormalChar] f + +# 15| [RegExpSequence] foo +#-----| 0 -> [RegExpConstant, RegExpNormalChar] f +#-----| 1 -> [RegExpConstant, RegExpNormalChar] o +#-----| 2 -> [RegExpConstant, RegExpNormalChar] o + +# 15| [RegExpConstant, RegExpNormalChar] o + +# 15| [RegExpConstant, RegExpNormalChar] o + +# 15| [RegExpConstant, RegExpNormalChar] b + +# 15| [RegExpSequence] bar +#-----| 0 -> [RegExpConstant, RegExpNormalChar] b +#-----| 1 -> [RegExpConstant, RegExpNormalChar] a +#-----| 2 -> [RegExpConstant, RegExpNormalChar] r + +# 15| [RegExpConstant, RegExpNormalChar] a + +# 15| [RegExpConstant, RegExpNormalChar] r + +# 18| [RegExpCharacterClass] [abc] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 18| [RegExpConstant, RegExpNormalChar] a + +# 18| [RegExpConstant, RegExpNormalChar] b + +# 18| [RegExpConstant, RegExpNormalChar] c + +# 19| [RegExpCharacterClass] [a-fA-F0-9_] +#-----| 0 -> [RegExpCharacterRange] a-f +#-----| 1 -> [RegExpCharacterRange] A-F +#-----| 2 -> [RegExpCharacterRange] 0-9 +#-----| 3 -> [RegExpConstant, RegExpNormalChar] _ + +# 19| [RegExpCharacterRange] a-f +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] f + +# 19| [RegExpConstant, RegExpNormalChar] a + +# 19| [RegExpConstant, RegExpNormalChar] f + +# 19| [RegExpCharacterRange] A-F +#-----| 0 -> [RegExpConstant, RegExpNormalChar] A +#-----| 1 -> [RegExpConstant, RegExpNormalChar] F + +# 19| [RegExpConstant, RegExpNormalChar] A + +# 19| [RegExpConstant, RegExpNormalChar] F + +# 19| [RegExpCharacterRange] 0-9 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 9 + +# 19| [RegExpConstant, RegExpNormalChar] 0 + +# 19| [RegExpConstant, RegExpNormalChar] 9 + +# 19| [RegExpConstant, RegExpNormalChar] _ + +# 20| [RegExpCaret] \A + +# 20| [RegExpSequence] \A[+-]?\d+ +#-----| 0 -> [RegExpCaret] \A +#-----| 1 -> [RegExpOpt] [+-]? +#-----| 2 -> [RegExpPlus] \d+ + +# 20| [RegExpCharacterClass] [+-] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] + +#-----| 1 -> [RegExpConstant, RegExpNormalChar] - + +# 20| [RegExpOpt] [+-]? +#-----| 0 -> [RegExpCharacterClass] [+-] + +# 20| [RegExpConstant, RegExpNormalChar] + + +# 20| [RegExpConstant, RegExpNormalChar] - + +# 20| [RegExpPlus] \d+ +#-----| 0 -> [RegExpCharacterClassEscape] \d + +# 20| [RegExpCharacterClassEscape] \d + +# 21| [RegExpCharacterClass] [\w] +#-----| 0 -> [RegExpCharacterClassEscape] \w + +# 21| [RegExpPlus] [\w]+ +#-----| 0 -> [RegExpCharacterClass] [\w] + +# 21| [RegExpCharacterClassEscape] \w + +# 22| [RegExpConstant, RegExpEscape] \[ + +# 22| [RegExpSequence] \[\][123] +#-----| 0 -> [RegExpConstant, RegExpEscape] \[ +#-----| 1 -> [RegExpConstant, RegExpEscape] \] +#-----| 2 -> [RegExpCharacterClass] [123] + +# 22| [RegExpConstant, RegExpEscape] \] + +# 22| [RegExpCharacterClass] [123] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 1 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 2 +#-----| 2 -> [RegExpConstant, RegExpNormalChar] 3 + +# 22| [RegExpConstant, RegExpNormalChar] 1 + +# 22| [RegExpConstant, RegExpNormalChar] 2 + +# 22| [RegExpConstant, RegExpNormalChar] 3 + +# 23| [RegExpCharacterClass] [^A-Z] +#-----| 0 -> [RegExpCharacterRange] A-Z + +# 23| [RegExpCharacterRange] A-Z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] A +#-----| 1 -> [RegExpConstant, RegExpNormalChar] Z + +# 23| [RegExpConstant, RegExpNormalChar] A + +# 23| [RegExpConstant, RegExpNormalChar] Z + +# 24| [RegExpCharacterClass] []] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ] + +# 24| [RegExpConstant, RegExpNormalChar] ] + +# 25| [RegExpCharacterClass] [^]] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ] + +# 25| [RegExpConstant, RegExpNormalChar] ] + +# 26| [RegExpCharacterClass] [^-] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] - + +# 26| [RegExpConstant, RegExpNormalChar] - + +# 29| [RegExpCharacterClass] [[a-f] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] [ +#-----| 1 -> [RegExpCharacterRange] a-f + +# 29| [RegExpSequence] [[a-f]A-F] +#-----| 0 -> [RegExpCharacterClass] [[a-f] +#-----| 1 -> [RegExpConstant, RegExpNormalChar] A +#-----| 2 -> [RegExpConstant, RegExpNormalChar] - +#-----| 3 -> [RegExpConstant, RegExpNormalChar] F +#-----| 4 -> [RegExpConstant, RegExpNormalChar] ] + +# 29| [RegExpConstant, RegExpNormalChar] [ + +# 29| [RegExpCharacterRange] a-f +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] f + +# 29| [RegExpConstant, RegExpNormalChar] a + +# 29| [RegExpConstant, RegExpNormalChar] f + +# 29| [RegExpConstant, RegExpNormalChar] A + +# 29| [RegExpConstant, RegExpNormalChar] - + +# 29| [RegExpConstant, RegExpNormalChar] F + +# 29| [RegExpConstant, RegExpNormalChar] ] + +# 32| [RegExpStar] .* +#-----| 0 -> [RegExpDot] . + +# 32| [RegExpDot] . + +# 33| [RegExpStar] .* +#-----| 0 -> [RegExpDot] . + +# 33| [RegExpDot] . + +# 34| [RegExpPlus] \w+ +#-----| 0 -> [RegExpCharacterClassEscape] \w + +# 34| [RegExpCharacterClassEscape] \w + +# 34| [RegExpSequence] \w+\W +#-----| 0 -> [RegExpPlus] \w+ +#-----| 1 -> [RegExpCharacterClassEscape] \W + +# 34| [RegExpCharacterClassEscape] \W + +# 35| [RegExpCharacterClassEscape] \s + +# 35| [RegExpSequence] \s\S +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpCharacterClassEscape] \S + +# 35| [RegExpCharacterClassEscape] \S + +# 36| [RegExpCharacterClassEscape] \d + +# 36| [RegExpSequence] \d\D +#-----| 0 -> [RegExpCharacterClassEscape] \d +#-----| 1 -> [RegExpCharacterClassEscape] \D + +# 36| [RegExpCharacterClassEscape] \D + +# 37| [RegExpCharacterClassEscape] \h + +# 37| [RegExpSequence] \h\H +#-----| 0 -> [RegExpCharacterClassEscape] \h +#-----| 1 -> [RegExpCharacterClassEscape] \H + +# 37| [RegExpCharacterClassEscape] \H + +# 38| [RegExpConstant, RegExpEscape] \n + +# 38| [RegExpSequence] \n\r\t +#-----| 0 -> [RegExpConstant, RegExpEscape] \n +#-----| 1 -> [RegExpConstant, RegExpEscape] \r +#-----| 2 -> [RegExpConstant, RegExpEscape] \t + +# 38| [RegExpConstant, RegExpEscape] \r + +# 38| [RegExpConstant, RegExpEscape] \t + +# 41| [RegExpStar] (foo)* +#-----| 0 -> [RegExpGroup] (foo) + +# 41| [RegExpGroup] (foo) +#-----| 0 -> [RegExpSequence] foo + +# 41| [RegExpSequence] (foo)*bar +#-----| 0 -> [RegExpStar] (foo)* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b +#-----| 2 -> [RegExpConstant, RegExpNormalChar] a +#-----| 3 -> [RegExpConstant, RegExpNormalChar] r + +# 41| [RegExpConstant, RegExpNormalChar] f + +# 41| [RegExpSequence] foo +#-----| 0 -> [RegExpConstant, RegExpNormalChar] f +#-----| 1 -> [RegExpConstant, RegExpNormalChar] o +#-----| 2 -> [RegExpConstant, RegExpNormalChar] o + +# 41| [RegExpConstant, RegExpNormalChar] o + +# 41| [RegExpConstant, RegExpNormalChar] o + +# 41| [RegExpConstant, RegExpNormalChar] b + +# 41| [RegExpConstant, RegExpNormalChar] a + +# 41| [RegExpConstant, RegExpNormalChar] r + +# 42| [RegExpConstant, RegExpNormalChar] f + +# 42| [RegExpSequence] fo(o|b)ar +#-----| 0 -> [RegExpConstant, RegExpNormalChar] f +#-----| 1 -> [RegExpConstant, RegExpNormalChar] o +#-----| 2 -> [RegExpGroup] (o|b) +#-----| 3 -> [RegExpConstant, RegExpNormalChar] a +#-----| 4 -> [RegExpConstant, RegExpNormalChar] r + +# 42| [RegExpConstant, RegExpNormalChar] o + +# 42| [RegExpGroup] (o|b) +#-----| 0 -> [RegExpAlt] o|b + +# 42| [RegExpAlt] o|b +#-----| 0 -> [RegExpConstant, RegExpNormalChar] o +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 42| [RegExpConstant, RegExpNormalChar] o + +# 42| [RegExpConstant, RegExpNormalChar] b + +# 42| [RegExpConstant, RegExpNormalChar] a + +# 42| [RegExpConstant, RegExpNormalChar] r + +# 43| [RegExpGroup] (a|b|cd) +#-----| 0 -> [RegExpAlt] a|b|cd + +# 43| [RegExpSequence] (a|b|cd)e +#-----| 0 -> [RegExpGroup] (a|b|cd) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] e + +# 43| [RegExpAlt] a|b|cd +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b +#-----| 2 -> [RegExpSequence] cd + +# 43| [RegExpConstant, RegExpNormalChar] a + +# 43| [RegExpConstant, RegExpNormalChar] b + +# 43| [RegExpConstant, RegExpNormalChar] c + +# 43| [RegExpSequence] cd +#-----| 0 -> [RegExpConstant, RegExpNormalChar] c +#-----| 1 -> [RegExpConstant, RegExpNormalChar] d + +# 43| [RegExpConstant, RegExpNormalChar] d + +# 43| [RegExpConstant, RegExpNormalChar] e + +# 44| [RegExpGroup] (?::+) +#-----| 0 -> [RegExpPlus] :+ + +# 44| [RegExpSequence] (?::+)\w +#-----| 0 -> [RegExpGroup] (?::+) +#-----| 1 -> [RegExpCharacterClassEscape] \w + +# 44| [RegExpPlus] :+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] : + +# 44| [RegExpConstant, RegExpNormalChar] : + +# 44| [RegExpCharacterClassEscape] \w + +# 47| [RegExpGroup] (?\w+) +#-----| 0 -> [RegExpPlus] \w+ + +# 47| [RegExpPlus] \w+ +#-----| 0 -> [RegExpCharacterClassEscape] \w + +# 47| [RegExpCharacterClassEscape] \w + +# 48| [RegExpGroup] (?'foo'fo+) +#-----| 0 -> [RegExpSequence] fo+ + +# 48| [RegExpConstant, RegExpNormalChar] f + +# 48| [RegExpSequence] fo+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] f +#-----| 1 -> [RegExpPlus] o+ + +# 48| [RegExpPlus] o+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] o + +# 48| [RegExpConstant, RegExpNormalChar] o + +# 51| [RegExpGroup] (a+) +#-----| 0 -> [RegExpPlus] a+ + +# 51| [RegExpSequence] (a+)b+\1 +#-----| 0 -> [RegExpGroup] (a+) +#-----| 1 -> [RegExpPlus] b+ +#-----| 2 -> [RegExpBackRef] \1 + +# 51| [RegExpPlus] a+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 51| [RegExpConstant, RegExpNormalChar] a + +# 51| [RegExpPlus] b+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] b + +# 51| [RegExpConstant, RegExpNormalChar] b + +# 51| [RegExpBackRef] \1 + +# 52| [RegExpGroup] (?q+) +#-----| 0 -> [RegExpPlus] q+ + +# 52| [RegExpSequence] (?q+)\s+\k+ +#-----| 0 -> [RegExpGroup] (?q+) +#-----| 1 -> [RegExpPlus] \s+ +#-----| 2 -> [RegExpPlus] \k+ + +# 52| [RegExpPlus] q+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] q + +# 52| [RegExpConstant, RegExpNormalChar] q + +# 52| [RegExpPlus] \s+ +#-----| 0 -> [RegExpCharacterClassEscape] \s + +# 52| [RegExpCharacterClassEscape] \s + +# 52| [RegExpBackRef] \k + +# 52| [RegExpPlus] \k+ +#-----| 0 -> [RegExpBackRef] \k + +# 55| [RegExpNamedCharacterProperty] \p{Word} + +# 55| [RegExpStar] \p{Word}* +#-----| 0 -> [RegExpNamedCharacterProperty] \p{Word} + +# 56| [RegExpNamedCharacterProperty] \P{Digit} + +# 56| [RegExpPlus] \P{Digit}+ +#-----| 0 -> [RegExpNamedCharacterProperty] \P{Digit} + +# 57| [RegExpNamedCharacterProperty] \p{^Alnum} + +# 57| [RegExpRange] \p{^Alnum}{2,3} +#-----| 0 -> [RegExpNamedCharacterProperty] \p{^Alnum} + +# 57| [RegExpNormalChar] 2 + +# 57| [RegExpNormalChar] , + +# 57| [RegExpNormalChar] 3 + +# 57| [RegExpNormalChar] } + +# 58| [RegExpCharacterClass] [a-f\p{Digit}] +#-----| 0 -> [RegExpCharacterRange] a-f +#-----| 1 -> [RegExpNamedCharacterProperty] \p{Digit} + +# 58| [RegExpPlus] [a-f\p{Digit}]+ +#-----| 0 -> [RegExpCharacterClass] [a-f\p{Digit}] + +# 58| [RegExpCharacterRange] a-f +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] f + +# 58| [RegExpConstant, RegExpNormalChar] a + +# 58| [RegExpConstant, RegExpNormalChar] f + +# 58| [RegExpNamedCharacterProperty] \p{Digit} + +# 61| [RegExpCharacterClass] [[:alpha:]] +#-----| 0 -> [RegExpNamedCharacterProperty] [:alpha:] + +# 61| [RegExpSequence] [[:alpha:]][[:digit:]] +#-----| 0 -> [RegExpCharacterClass] [[:alpha:]] +#-----| 1 -> [RegExpCharacterClass] [[:digit:]] + +# 61| [RegExpNamedCharacterProperty] [:alpha:] + +# 61| [RegExpCharacterClass] [[:digit:]] +#-----| 0 -> [RegExpNamedCharacterProperty] [:digit:] + +# 61| [RegExpNamedCharacterProperty] [:digit:] + +# 64| [RegExpCharacterClass] [[:alpha:][:digit:]] +#-----| 0 -> [RegExpNamedCharacterProperty] [:alpha:] +#-----| 1 -> [RegExpNamedCharacterProperty] [:digit:] + +# 64| [RegExpNamedCharacterProperty] [:alpha:] + +# 64| [RegExpNamedCharacterProperty] [:digit:] + +# 67| [RegExpCharacterClass] [A-F[:digit:]a-f] +#-----| 0 -> [RegExpCharacterRange] A-F +#-----| 1 -> [RegExpNamedCharacterProperty] [:digit:] +#-----| 2 -> [RegExpCharacterRange] a-f + +# 67| [RegExpCharacterRange] A-F +#-----| 0 -> [RegExpConstant, RegExpNormalChar] A +#-----| 1 -> [RegExpConstant, RegExpNormalChar] F + +# 67| [RegExpConstant, RegExpNormalChar] A + +# 67| [RegExpConstant, RegExpNormalChar] F + +# 67| [RegExpNamedCharacterProperty] [:digit:] + +# 67| [RegExpCharacterRange] a-f +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] f + +# 67| [RegExpConstant, RegExpNormalChar] a + +# 67| [RegExpConstant, RegExpNormalChar] f + +# 70| [RegExpNamedCharacterProperty] [:digit:] diff --git a/ruby/ql/test/library-tests/regexp/parse.ql b/ruby/ql/test/library-tests/regexp/parse.ql new file mode 100644 index 00000000000..e18c383c4d8 --- /dev/null +++ b/ruby/ql/test/library-tests/regexp/parse.ql @@ -0,0 +1,27 @@ +/** + * @kind graph + */ + +import codeql.Locations +import codeql.ruby.regexp.RegExpTreeView as RETV + +query predicate nodes(RETV::RegExpTerm n, string attr, string val) { + attr = "semmle.label" and + val = "[" + concat(n.getAPrimaryQlClass(), ", ") + "] " + n.toString() + or + attr = "semmle.order" and + val = + any(int i | + n = + rank[i](RETV::RegExpTerm t, string fp, int sl, int sc | + t.hasLocationInfo(fp, sl, sc, _, _) + | + t order by fp, sl, sc + ) + ).toString() +} + +query predicate edges(RETV::RegExpTerm pred, RETV::RegExpTerm succ, string attr, string val) { + attr in ["semmle.label", "semmle.order"] and + val = any(int i | succ = pred.getChild(i)).toString() +} diff --git a/ruby/ql/test/library-tests/regexp/regexp.rb b/ruby/ql/test/library-tests/regexp/regexp.rb new file mode 100644 index 00000000000..469813207d9 --- /dev/null +++ b/ruby/ql/test/library-tests/regexp/regexp.rb @@ -0,0 +1,70 @@ +# Empty +// + +# Basic sequence +/abc/ + +# Repetition +/a*b+c?d/ +/a{4,8}/ +/a{,8}/ +/a{3,}/ +/a{7}/ + +# Alternation +/foo|bar/ + +# Character classes +/[abc]/ +/[a-fA-F0-9_]/ +/\A[+-]?\d+/ +/[\w]+/ +/\[\][123]/ +/[^A-Z]/ +/[]]/ # MRI gives a warning, but accepts this as matching ']' +/[^]]/ # MRI gives a warning, but accepts this as matching anything except ']' +/[^-]/ + +# Nested character classes +/[[a-f]A-F]/ # BAD - not parsed correctly + +# Meta-character classes +/.*/ +/.*/m +/\w+\W/ +/\s\S/ +/\d\D/ +/\h\H/ +/\n\r\t/ + +# Groups +/(foo)*bar/ +/fo(o|b)ar/ +/(a|b|cd)e/ +/(?::+)\w/ # Non-capturing group matching colons + +# Named groups +/(?\w+)/ +/(?'foo'fo+)/ + +# Backreferences +/(a+)b+\1/ +/(?q+)\s+\k+/ + +# Named character properties using the p-style syntax +/\p{Word}*/ +/\P{Digit}+/ +/\p{^Alnum}{2,3}/ +/[a-f\p{Digit}]+/ # Also valid inside character classes + +# Two separate character classes, each containing a single POSIX bracket expression +/[[:alpha:]][[:digit:]]/ + +# A single character class containing two POSIX bracket expressions +/[[:alpha:][:digit:]]/ + +# A single character class containing two ranges and one POSIX bracket expression +/[A-F[:digit:]a-f]/ + +# *Not* a POSIX bracket expression; just a regular character class. +/[:digit:]/ diff --git a/ruby/ql/test/library-tests/variables/class_variables.rb b/ruby/ql/test/library-tests/variables/class_variables.rb new file mode 100644 index 00000000000..dd32df7ea14 --- /dev/null +++ b/ruby/ql/test/library-tests/variables/class_variables.rb @@ -0,0 +1,29 @@ +@@x = 42 + +p @@x + +def print + p @@x +end + +class X + def b + p @@x + end + def self.s + p @@x + end +end + +class Y < BasicObject + @@x = 10 +end + +module M + @@x = 12 +end + +module N + include M + p @@x +end diff --git a/ruby/ql/test/library-tests/variables/instance_variables.rb b/ruby/ql/test/library-tests/variables/instance_variables.rb new file mode 100644 index 00000000000..d35b6b15af9 --- /dev/null +++ b/ruby/ql/test/library-tests/variables/instance_variables.rb @@ -0,0 +1,44 @@ +@top = 1 + +def foo + @foo = 10 +end + +def print_foo + puts @foo +end + +puts @top + +class X + @x = 10 + def m() + @y = 7 + end +end + +module M + @m = 10 + def n() + @n = 7 + end +end + +puts { + @x = 100 +} + +def bar + 1.times { @x = 200 } +end + +class C + @x = 42 + def x + def y + @x = 10 + end + y + p @x + end + end \ No newline at end of file diff --git a/ruby/ql/test/library-tests/variables/nested_scopes.rb b/ruby/ql/test/library-tests/variables/nested_scopes.rb new file mode 100644 index 00000000000..db0311fe7d2 --- /dev/null +++ b/ruby/ql/test/library-tests/variables/nested_scopes.rb @@ -0,0 +1,42 @@ +def a + puts "method a" +end +class C + a = 5 + module M + a = 4 + module N + a = 3 + class D + a = 2 + def show_a + a = 1 + puts a + a.times do |a| + a.times do | x; a| + a = 6 + a.times { |x| puts a } + end + end + end + def show_a2 a + puts a + end + puts a + end + def self.show + puts a # not a variable, but a call to a() + end + class << self + a = 10 + puts a + end + puts a + end + puts a + end + puts a +end +d = C::M::N::D.new +d.show_a + diff --git a/ruby/ql/test/library-tests/variables/parameter.expected b/ruby/ql/test/library-tests/variables/parameter.expected new file mode 100644 index 00000000000..6e21d46b212 --- /dev/null +++ b/ruby/ql/test/library-tests/variables/parameter.expected @@ -0,0 +1,44 @@ +parameterVariable +| nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:23:15:23 | a | +| nested_scopes.rb:16:26:16:26 | x | nested_scopes.rb:16:26:16:26 | x | +| nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:16:29:16:29 | a | +| nested_scopes.rb:18:26:18:26 | x | nested_scopes.rb:18:26:18:26 | x | +| nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:21:22:21 | a | +| parameters.rb:1:14:1:14 | x | parameters.rb:1:14:1:14 | x | +| parameters.rb:1:18:1:18 | y | parameters.rb:1:18:1:18 | y | +| parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | +| parameters.rb:7:25:7:31 | *pizzas | parameters.rb:7:26:7:31 | pizzas | +| parameters.rb:15:15:15:19 | **map | parameters.rb:15:17:15:19 | map | +| parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key | +| parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value | +| parameters.rb:21:16:21:21 | &block | parameters.rb:21:17:21:21 | block | +| parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | +| parameters.rb:25:33:25:36 | size | parameters.rb:25:33:25:36 | size | +| parameters.rb:30:15:30:19 | first | parameters.rb:30:15:30:19 | first | +| parameters.rb:30:24:30:29 | middle | parameters.rb:30:24:30:29 | middle | +| parameters.rb:30:36:30:39 | last | parameters.rb:30:36:30:39 | last | +| parameters.rb:35:11:35:11 | a | parameters.rb:35:11:35:11 | a | +| parameters.rb:40:12:40:12 | d | parameters.rb:40:12:40:12 | d | +| parameters.rb:45:20:45:20 | _ | parameters.rb:45:20:45:20 | _ | +| parameters.rb:49:12:49:16 | (..., ...) | parameters.rb:49:13:49:13 | a | +| parameters.rb:49:12:49:16 | (..., ...) | parameters.rb:49:15:49:15 | b | +| parameters.rb:54:14:54:14 | y | parameters.rb:54:14:54:14 | y | +| scopes.rb:2:14:2:14 | x | scopes.rb:2:14:2:14 | x | +| scopes.rb:9:14:9:14 | x | scopes.rb:9:14:9:14 | x | +| ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b | +| ssa.rb:18:8:18:8 | x | ssa.rb:18:8:18:8 | x | +| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | +| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x | +| ssa.rb:44:8:44:8 | b | ssa.rb:44:8:44:8 | b | +| ssa.rb:49:9:49:9 | x | ssa.rb:49:9:49:9 | x | +| ssa.rb:53:8:53:10 | foo | ssa.rb:53:8:53:10 | foo | +| ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a | +| ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a | +parameterNoVariable +| parameters.rb:45:22:45:22 | _ | +parameterVariableNoAccess +| nested_scopes.rb:16:26:16:26 | x | nested_scopes.rb:16:26:16:26 | x | +| nested_scopes.rb:18:26:18:26 | x | nested_scopes.rb:18:26:18:26 | x | +| scopes.rb:2:14:2:14 | x | scopes.rb:2:14:2:14 | x | +| scopes.rb:9:14:9:14 | x | scopes.rb:9:14:9:14 | x | +| ssa.rb:49:9:49:9 | x | ssa.rb:49:9:49:9 | x | diff --git a/ruby/ql/test/library-tests/variables/parameter.ql b/ruby/ql/test/library-tests/variables/parameter.ql new file mode 100644 index 00000000000..93c3972c391 --- /dev/null +++ b/ruby/ql/test/library-tests/variables/parameter.ql @@ -0,0 +1,10 @@ +import codeql.ruby.ast.Variable +import codeql.ruby.ast.Parameter + +query predicate parameterVariable(Parameter p, Variable v) { v = p.getAVariable() } + +query predicate parameterNoVariable(Parameter p) { not exists(p.getAVariable()) } + +query predicate parameterVariableNoAccess(Parameter p, Variable v) { + v = p.getAVariable() and strictcount(v.getAnAccess()) = 1 +} diff --git a/ruby/ql/test/library-tests/variables/parameters.rb b/ruby/ql/test/library-tests/variables/parameters.rb new file mode 100644 index 00000000000..99aff4df5ae --- /dev/null +++ b/ruby/ql/test/library-tests/variables/parameters.rb @@ -0,0 +1,58 @@ +1.times do | x ; y| + y = 5 + puts x + puts y +end + +def order_pizza(client, *pizzas) + if pizzas.count == 1 + puts "1 pizza for #{client}!" + else + puts "#{ pizzas.count} pizzas for #{client}!" + end +end + +def print_map(**map) + map.each do |key, value| + puts "#{key} = #{value}" + end +end + +def call_block(&block) + block.call +end + +def opt_param(name = 'unknown', size = name.length) + puts name + puts size +end + +def key_param(first: , middle: '', last:) + puts "#{first} #{middle} #{last}" +end + +b = 2 +def multi(a = (b = 5)) + # `a` is a parameter and `b` is a new variable + puts "#{a} #{b}" +end + +def multi2(d: e = 4) + # `d` is a parameter and `e` is a local variable + puts "#{d} #{e}" +end + +def dup_underscore(_,_) + puts _ # binds to the first _ +end + +def tuples((a,b)) + puts "#{a} #{b}" +end + +x = 10 +1.times do | y = (x = 1)| + puts x + puts y +end + diff --git a/ruby/ql/test/library-tests/variables/scopes.rb b/ruby/ql/test/library-tests/variables/scopes.rb new file mode 100644 index 00000000000..db55da3b77f --- /dev/null +++ b/ruby/ql/test/library-tests/variables/scopes.rb @@ -0,0 +1,49 @@ +def a ; "x" end +1.times do | x | + puts a # not a local variable + a = 3 + puts a # local variable +end +a = 6 +puts a +1.times do | x | + puts a # local variable from top-level + a += 3 + puts a # local variable from top-level + a, b, (c, d) = [4, 5, [6, 7]] + puts a # local variable from top-level + puts b # new local variable + puts c # new local variable + puts d # new local variable +end + +# new global variable +$global = 42 + +# use of a pre-defined global variable +script = $0 + +class A; end +x = A +module x::B + x = 1 +end +class << x + x = 2 +end +class x::C < x + x = 3 +end +def x.foo + x = 4 +end + +module M + var = 1 + foo = <<-EOF + #{var} + #{fun} + #{var2 = 10} + #{var2} + EOF +end diff --git a/ruby/ql/test/library-tests/variables/ssa.expected b/ruby/ql/test/library-tests/variables/ssa.expected new file mode 100644 index 00000000000..b17ef242f27 --- /dev/null +++ b/ruby/ql/test/library-tests/variables/ssa.expected @@ -0,0 +1,616 @@ +definition +| class_variables.rb:1:1:1:3 | self (class_variables.rb) | class_variables.rb:1:1:29:4 | self | +| class_variables.rb:5:1:7:3 | self (print) | class_variables.rb:5:1:7:3 | self | +| class_variables.rb:9:1:16:3 | self (X) | class_variables.rb:9:1:16:3 | self | +| class_variables.rb:10:3:12:5 | self (b) | class_variables.rb:10:3:12:5 | self | +| class_variables.rb:13:3:15:5 | self (s) | class_variables.rb:13:3:15:5 | self | +| class_variables.rb:26:1:29:3 | self (N) | class_variables.rb:26:1:29:3 | self | +| instance_variables.rb:1:1:1:4 | self (instance_variables.rb) | instance_variables.rb:1:1:44:4 | self | +| instance_variables.rb:7:1:9:3 | self (print_foo) | instance_variables.rb:7:1:9:3 | self | +| instance_variables.rb:37:3:43:5 | self (x) | instance_variables.rb:37:3:43:5 | self | +| nested_scopes.rb:1:1:3:3 | self (a) | nested_scopes.rb:1:1:3:3 | self | +| nested_scopes.rb:4:1:39:3 | self (C) | nested_scopes.rb:4:1:39:3 | self | +| nested_scopes.rb:5:3:5:7 | ... = ... | nested_scopes.rb:5:3:5:3 | a | +| nested_scopes.rb:6:3:37:5 | self (M) | nested_scopes.rb:6:3:37:5 | self | +| nested_scopes.rb:7:5:7:9 | ... = ... | nested_scopes.rb:7:5:7:5 | a | +| nested_scopes.rb:8:5:35:7 | self (N) | nested_scopes.rb:8:5:35:7 | self | +| nested_scopes.rb:9:7:9:11 | ... = ... | nested_scopes.rb:9:7:9:7 | a | +| nested_scopes.rb:10:7:26:9 | self (D) | nested_scopes.rb:10:7:26:9 | self | +| nested_scopes.rb:11:9:11:13 | ... = ... | nested_scopes.rb:11:9:11:9 | a | +| nested_scopes.rb:12:9:21:11 | self (show_a) | nested_scopes.rb:12:9:21:11 | self | +| nested_scopes.rb:13:11:13:15 | ... = ... | nested_scopes.rb:13:11:13:11 | a | +| nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:23:15:23 | a | +| nested_scopes.rb:17:15:17:19 | ... = ... | nested_scopes.rb:16:29:16:29 | a | +| nested_scopes.rb:18:23:18:36 | | nested_scopes.rb:12:9:21:11 | self | +| nested_scopes.rb:18:23:18:36 | | nested_scopes.rb:16:29:16:29 | a | +| nested_scopes.rb:22:9:24:11 | self (show_a2) | nested_scopes.rb:22:9:24:11 | self | +| nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:21:22:21 | a | +| nested_scopes.rb:27:7:29:9 | self (show) | nested_scopes.rb:27:7:29:9 | self | +| nested_scopes.rb:30:16:30:19 | self (class << ...) | nested_scopes.rb:30:7:33:9 | self | +| nested_scopes.rb:31:11:31:16 | ... = ... | nested_scopes.rb:31:11:31:11 | a | +| nested_scopes.rb:40:1:40:18 | ... = ... | nested_scopes.rb:40:1:40:1 | d | +| parameters.rb:1:9:5:3 | | parameters.rb:1:1:58:1 | self | +| parameters.rb:1:14:1:14 | x | parameters.rb:1:14:1:14 | x | +| parameters.rb:2:4:2:8 | ... = ... | parameters.rb:1:18:1:18 | y | +| parameters.rb:7:1:13:3 | self (order_pizza) | parameters.rb:7:1:13:3 | self | +| parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | +| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | +| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map | +| parameters.rb:16:12:18:5 | | parameters.rb:15:1:19:3 | self | +| parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key | +| parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value | +| parameters.rb:21:17:21:21 | block | parameters.rb:21:17:21:21 | block | +| parameters.rb:25:1:28:3 | self (opt_param) | parameters.rb:25:1:28:3 | self | +| parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | +| parameters.rb:25:33:25:36 | size | parameters.rb:25:33:25:36 | size | +| parameters.rb:30:1:32:3 | self (key_param) | parameters.rb:30:1:32:3 | self | +| parameters.rb:30:15:30:19 | first | parameters.rb:30:15:30:19 | first | +| parameters.rb:30:24:30:29 | middle | parameters.rb:30:24:30:29 | middle | +| parameters.rb:30:36:30:39 | last | parameters.rb:30:36:30:39 | last | +| parameters.rb:35:1:38:3 | | parameters.rb:35:16:35:16 | b | +| parameters.rb:35:1:38:3 | self (multi) | parameters.rb:35:1:38:3 | self | +| parameters.rb:35:11:35:11 | a | parameters.rb:35:11:35:11 | a | +| parameters.rb:35:16:35:20 | ... = ... | parameters.rb:35:16:35:16 | b | +| parameters.rb:37:3:37:18 | phi | parameters.rb:35:16:35:16 | b | +| parameters.rb:40:1:43:3 | | parameters.rb:40:15:40:15 | e | +| parameters.rb:40:1:43:3 | self (multi2) | parameters.rb:40:1:43:3 | self | +| parameters.rb:40:12:40:12 | d | parameters.rb:40:12:40:12 | d | +| parameters.rb:40:15:40:19 | ... = ... | parameters.rb:40:15:40:15 | e | +| parameters.rb:42:3:42:18 | phi | parameters.rb:40:15:40:15 | e | +| parameters.rb:45:1:47:3 | self (dup_underscore) | parameters.rb:45:1:47:3 | self | +| parameters.rb:45:20:45:20 | _ | parameters.rb:45:20:45:20 | _ | +| parameters.rb:49:1:51:3 | self (tuples) | parameters.rb:49:1:51:3 | self | +| parameters.rb:49:13:49:13 | a | parameters.rb:49:13:49:13 | a | +| parameters.rb:49:15:49:15 | b | parameters.rb:49:15:49:15 | b | +| parameters.rb:53:1:53:6 | ... = ... | parameters.rb:53:1:53:1 | x | +| parameters.rb:54:9:57:3 | | parameters.rb:1:1:58:1 | self | +| parameters.rb:54:9:57:3 | | parameters.rb:53:1:53:1 | x | +| parameters.rb:54:14:54:14 | y | parameters.rb:54:14:54:14 | y | +| parameters.rb:54:19:54:23 | ... = ... | parameters.rb:53:1:53:1 | x | +| parameters.rb:55:4:55:9 | phi | parameters.rb:53:1:53:1 | x | +| scopes.rb:1:1:1:15 | self (scopes.rb) | scopes.rb:1:1:49:4 | self | +| scopes.rb:2:9:6:3 | | scopes.rb:1:1:49:4 | self | +| scopes.rb:4:4:4:8 | ... = ... | scopes.rb:4:4:4:4 | a | +| scopes.rb:7:1:7:5 | ... = ... | scopes.rb:7:1:7:1 | a | +| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self | +| scopes.rb:9:9:18:3 | | scopes.rb:7:1:7:1 | a | +| scopes.rb:11:4:11:9 | ... = ... | scopes.rb:7:1:7:1 | a | +| scopes.rb:13:4:13:4 | ... = ... | scopes.rb:7:1:7:1 | a | +| scopes.rb:13:7:13:7 | ... = ... | scopes.rb:13:7:13:7 | b | +| scopes.rb:13:10:13:15 | ... = ... | scopes.rb:13:10:13:15 | __synth__0__1 | +| scopes.rb:13:11:13:11 | ... = ... | scopes.rb:13:11:13:11 | c | +| scopes.rb:13:14:13:14 | ... = ... | scopes.rb:13:14:13:14 | d | +| scopes.rb:13:19:13:32 | ... = ... | scopes.rb:13:4:13:32 | __synth__0 | +| scopes.rb:27:1:27:5 | ... = ... | scopes.rb:27:1:27:1 | x | +| scopes.rb:41:1:49:3 | self (M) | scopes.rb:41:1:49:3 | self | +| scopes.rb:42:2:42:9 | ... = ... | scopes.rb:42:2:42:4 | var | +| scopes.rb:46:5:46:13 | ... = ... | scopes.rb:46:5:46:8 | var2 | +| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | +| ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b | +| ssa.rb:2:3:2:7 | ... = ... | ssa.rb:2:3:2:3 | i | +| ssa.rb:5:3:13:5 | phi | ssa.rb:2:3:2:3 | i | +| ssa.rb:6:5:6:9 | ... = ... | ssa.rb:2:3:2:3 | i | +| ssa.rb:10:5:10:9 | ... = ... | ssa.rb:2:3:2:3 | i | +| ssa.rb:18:1:23:3 | self (m1) | ssa.rb:18:1:23:3 | self | +| ssa.rb:18:8:18:8 | x | ssa.rb:18:8:18:8 | x | +| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | +| ssa.rb:21:5:21:10 | ... = ... | ssa.rb:18:8:18:8 | x | +| ssa.rb:25:1:30:3 | | ssa.rb:26:7:26:10 | elem | +| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | +| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | +| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem | +| ssa.rb:26:12:26:22 | phi | ssa.rb:26:7:26:10 | elem | +| ssa.rb:33:16:35:5 | | ssa.rb:32:1:36:3 | self | +| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x | +| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | +| ssa.rb:40:3:40:9 | ... = ... | ssa.rb:40:3:40:4 | m3 | +| ssa.rb:44:1:47:3 | | ssa.rb:45:3:45:3 | x | +| ssa.rb:44:1:47:3 | self (m5) | ssa.rb:44:1:47:3 | self | +| ssa.rb:44:8:44:8 | b | ssa.rb:44:8:44:8 | b | +| ssa.rb:45:3:45:7 | ... = ... | ssa.rb:45:3:45:3 | x | +| ssa.rb:45:3:45:12 | phi | ssa.rb:45:3:45:3 | x | +| ssa.rb:49:1:51:3 | | ssa.rb:49:14:49:14 | y | +| ssa.rb:49:1:51:3 | self (m6) | ssa.rb:49:1:51:3 | self | +| ssa.rb:49:14:49:19 | ... = ... | ssa.rb:49:14:49:14 | y | +| ssa.rb:50:3:50:8 | phi | ssa.rb:49:14:49:14 | y | +| ssa.rb:53:1:56:3 | self (m7) | ssa.rb:53:1:56:3 | self | +| ssa.rb:53:8:53:10 | foo | ssa.rb:53:8:53:10 | foo | +| ssa.rb:54:3:54:11 | ... = ... | ssa.rb:54:3:54:3 | x | +| ssa.rb:58:1:62:3 | self (m8) | ssa.rb:58:1:62:3 | self | +| ssa.rb:59:3:59:8 | ... = ... | ssa.rb:59:3:59:3 | x | +| ssa.rb:60:3:60:9 | ... = ... | ssa.rb:59:3:59:3 | x | +| ssa.rb:64:1:72:3 | self (m9) | ssa.rb:64:1:72:3 | self | +| ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a | +| ssa.rb:65:3:65:15 | ... = ... | ssa.rb:65:3:65:10 | captured | +| ssa.rb:66:3:70:5 | call to times | ssa.rb:65:3:65:10 | captured | +| ssa.rb:66:11:70:5 | | ssa.rb:64:1:72:3 | self | +| ssa.rb:66:11:70:5 | | ssa.rb:65:3:65:10 | captured | +| ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a | +| ssa.rb:69:5:69:17 | ... = ... | ssa.rb:65:3:65:10 | captured | +| ssa.rb:74:1:79:3 | self (m10) | ssa.rb:74:1:79:3 | self | +| ssa.rb:75:3:75:14 | ... = ... | ssa.rb:75:3:75:10 | captured | +| ssa.rb:76:7:78:5 | | ssa.rb:74:1:79:3 | self | +| ssa.rb:76:7:78:5 | | ssa.rb:75:3:75:10 | captured | +| ssa.rb:81:1:88:3 | self (m11) | ssa.rb:81:1:88:3 | self | +| ssa.rb:82:3:82:14 | ... = ... | ssa.rb:82:3:82:10 | captured | +| ssa.rb:83:7:87:5 | | ssa.rb:81:1:88:3 | self | +| ssa.rb:84:10:86:8 | | ssa.rb:81:1:88:3 | self | +| ssa.rb:84:10:86:8 | | ssa.rb:82:3:82:10 | captured | +read +| class_variables.rb:1:1:1:3 | self (class_variables.rb) | class_variables.rb:1:1:29:4 | self | class_variables.rb:3:1:3:5 | self | +| class_variables.rb:5:1:7:3 | self (print) | class_variables.rb:5:1:7:3 | self | class_variables.rb:6:2:6:6 | self | +| class_variables.rb:9:1:16:3 | self (X) | class_variables.rb:9:1:16:3 | self | class_variables.rb:13:7:13:10 | self | +| class_variables.rb:10:3:12:5 | self (b) | class_variables.rb:10:3:12:5 | self | class_variables.rb:11:5:11:9 | self | +| class_variables.rb:13:3:15:5 | self (s) | class_variables.rb:13:3:15:5 | self | class_variables.rb:14:4:14:8 | self | +| class_variables.rb:26:1:29:3 | self (N) | class_variables.rb:26:1:29:3 | self | class_variables.rb:27:3:27:11 | self | +| class_variables.rb:26:1:29:3 | self (N) | class_variables.rb:26:1:29:3 | self | class_variables.rb:28:3:28:7 | self | +| instance_variables.rb:1:1:1:4 | self (instance_variables.rb) | instance_variables.rb:1:1:44:4 | self | instance_variables.rb:11:1:11:9 | self | +| instance_variables.rb:1:1:1:4 | self (instance_variables.rb) | instance_variables.rb:1:1:44:4 | self | instance_variables.rb:27:1:29:1 | self | +| instance_variables.rb:7:1:9:3 | self (print_foo) | instance_variables.rb:7:1:9:3 | self | instance_variables.rb:8:3:8:11 | self | +| instance_variables.rb:37:3:43:5 | self (x) | instance_variables.rb:37:3:43:5 | self | instance_variables.rb:41:4:41:4 | self | +| instance_variables.rb:37:3:43:5 | self (x) | instance_variables.rb:37:3:43:5 | self | instance_variables.rb:42:4:42:7 | self | +| nested_scopes.rb:1:1:3:3 | self (a) | nested_scopes.rb:1:1:3:3 | self | nested_scopes.rb:2:3:2:17 | self | +| nested_scopes.rb:4:1:39:3 | self (C) | nested_scopes.rb:4:1:39:3 | self | nested_scopes.rb:38:3:38:8 | self | +| nested_scopes.rb:5:3:5:7 | ... = ... | nested_scopes.rb:5:3:5:3 | a | nested_scopes.rb:38:8:38:8 | a | +| nested_scopes.rb:6:3:37:5 | self (M) | nested_scopes.rb:6:3:37:5 | self | nested_scopes.rb:36:5:36:10 | self | +| nested_scopes.rb:7:5:7:9 | ... = ... | nested_scopes.rb:7:5:7:5 | a | nested_scopes.rb:36:10:36:10 | a | +| nested_scopes.rb:8:5:35:7 | self (N) | nested_scopes.rb:8:5:35:7 | self | nested_scopes.rb:27:11:27:14 | self | +| nested_scopes.rb:8:5:35:7 | self (N) | nested_scopes.rb:8:5:35:7 | self | nested_scopes.rb:30:16:30:19 | self | +| nested_scopes.rb:8:5:35:7 | self (N) | nested_scopes.rb:8:5:35:7 | self | nested_scopes.rb:34:7:34:12 | self | +| nested_scopes.rb:9:7:9:11 | ... = ... | nested_scopes.rb:9:7:9:7 | a | nested_scopes.rb:34:12:34:12 | a | +| nested_scopes.rb:10:7:26:9 | self (D) | nested_scopes.rb:10:7:26:9 | self | nested_scopes.rb:25:9:25:14 | self | +| nested_scopes.rb:11:9:11:13 | ... = ... | nested_scopes.rb:11:9:11:9 | a | nested_scopes.rb:25:14:25:14 | a | +| nested_scopes.rb:12:9:21:11 | self (show_a) | nested_scopes.rb:12:9:21:11 | self | nested_scopes.rb:14:11:14:16 | self | +| nested_scopes.rb:13:11:13:15 | ... = ... | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:14:16:14:16 | a | +| nested_scopes.rb:13:11:13:15 | ... = ... | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:15:11:15:11 | a | +| nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:16:13:16:13 | a | +| nested_scopes.rb:17:15:17:19 | ... = ... | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:15:18:15 | a | +| nested_scopes.rb:18:23:18:36 | | nested_scopes.rb:12:9:21:11 | self | nested_scopes.rb:18:29:18:34 | self | +| nested_scopes.rb:18:23:18:36 | | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:34:18:34 | a | +| nested_scopes.rb:22:9:24:11 | self (show_a2) | nested_scopes.rb:22:9:24:11 | self | nested_scopes.rb:23:11:23:16 | self | +| nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:23:16:23:16 | a | +| nested_scopes.rb:27:7:29:9 | self (show) | nested_scopes.rb:27:7:29:9 | self | nested_scopes.rb:28:11:28:16 | self | +| nested_scopes.rb:27:7:29:9 | self (show) | nested_scopes.rb:27:7:29:9 | self | nested_scopes.rb:28:16:28:16 | self | +| nested_scopes.rb:30:16:30:19 | self (class << ...) | nested_scopes.rb:30:7:33:9 | self | nested_scopes.rb:32:11:32:16 | self | +| nested_scopes.rb:31:11:31:16 | ... = ... | nested_scopes.rb:31:11:31:11 | a | nested_scopes.rb:32:16:32:16 | a | +| nested_scopes.rb:40:1:40:18 | ... = ... | nested_scopes.rb:40:1:40:1 | d | nested_scopes.rb:41:1:41:1 | d | +| parameters.rb:1:9:5:3 | | parameters.rb:1:1:58:1 | self | parameters.rb:3:4:3:9 | self | +| parameters.rb:1:9:5:3 | | parameters.rb:1:1:58:1 | self | parameters.rb:4:4:4:9 | self | +| parameters.rb:1:14:1:14 | x | parameters.rb:1:14:1:14 | x | parameters.rb:3:9:3:9 | x | +| parameters.rb:2:4:2:8 | ... = ... | parameters.rb:1:18:1:18 | y | parameters.rb:4:9:4:9 | y | +| parameters.rb:7:1:13:3 | self (order_pizza) | parameters.rb:7:1:13:3 | self | parameters.rb:9:5:9:33 | self | +| parameters.rb:7:1:13:3 | self (order_pizza) | parameters.rb:7:1:13:3 | self | parameters.rb:11:5:11:49 | self | +| parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:9:25:9:30 | client | +| parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:11:41:11:46 | client | +| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas | +| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:11:14:11:19 | pizzas | +| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map | parameters.rb:16:3:16:5 | map | +| parameters.rb:16:12:18:5 | | parameters.rb:15:1:19:3 | self | parameters.rb:17:5:17:28 | self | +| parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key | parameters.rb:17:13:17:15 | key | +| parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value | parameters.rb:17:22:17:26 | value | +| parameters.rb:21:17:21:21 | block | parameters.rb:21:17:21:21 | block | parameters.rb:22:3:22:7 | block | +| parameters.rb:25:1:28:3 | self (opt_param) | parameters.rb:25:1:28:3 | self | parameters.rb:26:3:26:11 | self | +| parameters.rb:25:1:28:3 | self (opt_param) | parameters.rb:25:1:28:3 | self | parameters.rb:27:3:27:11 | self | +| parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:25:40:25:43 | name | +| parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:26:8:26:11 | name | +| parameters.rb:25:33:25:36 | size | parameters.rb:25:33:25:36 | size | parameters.rb:27:8:27:11 | size | +| parameters.rb:30:1:32:3 | self (key_param) | parameters.rb:30:1:32:3 | self | parameters.rb:31:3:31:35 | self | +| parameters.rb:30:15:30:19 | first | parameters.rb:30:15:30:19 | first | parameters.rb:31:11:31:15 | first | +| parameters.rb:30:24:30:29 | middle | parameters.rb:30:24:30:29 | middle | parameters.rb:31:20:31:25 | middle | +| parameters.rb:30:36:30:39 | last | parameters.rb:30:36:30:39 | last | parameters.rb:31:30:31:33 | last | +| parameters.rb:35:1:38:3 | self (multi) | parameters.rb:35:1:38:3 | self | parameters.rb:37:3:37:18 | self | +| parameters.rb:35:11:35:11 | a | parameters.rb:35:11:35:11 | a | parameters.rb:37:11:37:11 | a | +| parameters.rb:37:3:37:18 | phi | parameters.rb:35:16:35:16 | b | parameters.rb:37:16:37:16 | b | +| parameters.rb:40:1:43:3 | self (multi2) | parameters.rb:40:1:43:3 | self | parameters.rb:42:3:42:18 | self | +| parameters.rb:40:12:40:12 | d | parameters.rb:40:12:40:12 | d | parameters.rb:42:11:42:11 | d | +| parameters.rb:42:3:42:18 | phi | parameters.rb:40:15:40:15 | e | parameters.rb:42:16:42:16 | e | +| parameters.rb:45:1:47:3 | self (dup_underscore) | parameters.rb:45:1:47:3 | self | parameters.rb:46:3:46:8 | self | +| parameters.rb:45:20:45:20 | _ | parameters.rb:45:20:45:20 | _ | parameters.rb:46:8:46:8 | _ | +| parameters.rb:49:1:51:3 | self (tuples) | parameters.rb:49:1:51:3 | self | parameters.rb:50:3:50:18 | self | +| parameters.rb:49:13:49:13 | a | parameters.rb:49:13:49:13 | a | parameters.rb:50:11:50:11 | a | +| parameters.rb:49:15:49:15 | b | parameters.rb:49:15:49:15 | b | parameters.rb:50:16:50:16 | b | +| parameters.rb:54:9:57:3 | | parameters.rb:1:1:58:1 | self | parameters.rb:55:4:55:9 | self | +| parameters.rb:54:9:57:3 | | parameters.rb:1:1:58:1 | self | parameters.rb:56:4:56:9 | self | +| parameters.rb:54:14:54:14 | y | parameters.rb:54:14:54:14 | y | parameters.rb:56:9:56:9 | y | +| parameters.rb:55:4:55:9 | phi | parameters.rb:53:1:53:1 | x | parameters.rb:55:9:55:9 | x | +| scopes.rb:1:1:1:15 | self (scopes.rb) | scopes.rb:1:1:49:4 | self | scopes.rb:8:1:8:6 | self | +| scopes.rb:2:9:6:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:3:4:3:9 | self | +| scopes.rb:2:9:6:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:3:9:3:9 | self | +| scopes.rb:2:9:6:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:5:4:5:9 | self | +| scopes.rb:4:4:4:8 | ... = ... | scopes.rb:4:4:4:4 | a | scopes.rb:5:9:5:9 | a | +| scopes.rb:7:1:7:5 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:8:6:8:6 | a | +| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:10:4:10:9 | self | +| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:12:4:12:9 | self | +| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:14:4:14:9 | self | +| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:15:4:15:9 | self | +| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:16:4:16:9 | self | +| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:17:4:17:9 | self | +| scopes.rb:9:9:18:3 | | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a | +| scopes.rb:9:9:18:3 | | scopes.rb:7:1:7:1 | a | scopes.rb:11:4:11:4 | a | +| scopes.rb:11:4:11:9 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:12:9:12:9 | a | +| scopes.rb:13:4:13:4 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:14:9:14:9 | a | +| scopes.rb:13:7:13:7 | ... = ... | scopes.rb:13:7:13:7 | b | scopes.rb:15:9:15:9 | b | +| scopes.rb:13:10:13:15 | ... = ... | scopes.rb:13:10:13:15 | __synth__0__1 | scopes.rb:13:11:13:11 | __synth__0__1 | +| scopes.rb:13:10:13:15 | ... = ... | scopes.rb:13:10:13:15 | __synth__0__1 | scopes.rb:13:14:13:14 | __synth__0__1 | +| scopes.rb:13:11:13:11 | ... = ... | scopes.rb:13:11:13:11 | c | scopes.rb:16:9:16:9 | c | +| scopes.rb:13:14:13:14 | ... = ... | scopes.rb:13:14:13:14 | d | scopes.rb:17:9:17:9 | d | +| scopes.rb:13:19:13:32 | ... = ... | scopes.rb:13:4:13:32 | __synth__0 | scopes.rb:13:4:13:4 | __synth__0 | +| scopes.rb:13:19:13:32 | ... = ... | scopes.rb:13:4:13:32 | __synth__0 | scopes.rb:13:7:13:7 | __synth__0 | +| scopes.rb:13:19:13:32 | ... = ... | scopes.rb:13:4:13:32 | __synth__0 | scopes.rb:13:10:13:15 | __synth__0 | +| scopes.rb:27:1:27:5 | ... = ... | scopes.rb:27:1:27:1 | x | scopes.rb:28:8:28:8 | x | +| scopes.rb:27:1:27:5 | ... = ... | scopes.rb:27:1:27:1 | x | scopes.rb:31:10:31:10 | x | +| scopes.rb:27:1:27:5 | ... = ... | scopes.rb:27:1:27:1 | x | scopes.rb:34:7:34:7 | x | +| scopes.rb:27:1:27:5 | ... = ... | scopes.rb:27:1:27:1 | x | scopes.rb:34:14:34:14 | x | +| scopes.rb:27:1:27:5 | ... = ... | scopes.rb:27:1:27:1 | x | scopes.rb:37:5:37:5 | x | +| scopes.rb:41:1:49:3 | self (M) | scopes.rb:41:1:49:3 | self | scopes.rb:45:5:45:7 | self | +| scopes.rb:42:2:42:9 | ... = ... | scopes.rb:42:2:42:4 | var | scopes.rb:44:5:44:7 | var | +| scopes.rb:46:5:46:13 | ... = ... | scopes.rb:46:5:46:8 | var2 | scopes.rb:47:5:47:8 | var2 | +| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:3:3:3:8 | self | +| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:4:3:4:12 | self | +| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:7:5:7:10 | self | +| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:8:5:8:14 | self | +| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:11:5:11:10 | self | +| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:12:5:12:14 | self | +| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:15:3:15:8 | self | +| ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b | ssa.rb:5:6:5:6 | b | +| ssa.rb:2:3:2:7 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:3:8:3:8 | i | +| ssa.rb:2:3:2:7 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:4:8:4:8 | i | +| ssa.rb:5:3:13:5 | phi | ssa.rb:2:3:2:3 | i | ssa.rb:15:8:15:8 | i | +| ssa.rb:6:5:6:9 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:7:10:7:10 | i | +| ssa.rb:6:5:6:9 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:8:10:8:10 | i | +| ssa.rb:10:5:10:9 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:11:10:11:10 | i | +| ssa.rb:10:5:10:9 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:12:10:12:10 | i | +| ssa.rb:18:1:23:3 | self (m1) | ssa.rb:18:1:23:3 | self | ssa.rb:20:5:20:10 | self | +| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:19:9:19:9 | x | +| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:20:10:20:10 | x | +| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:21:5:21:5 | x | +| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self | +| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:29:3:29:11 | self | +| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:26:15:26:22 | elements | +| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:27:10:27:13 | elem | +| ssa.rb:26:12:26:22 | phi | ssa.rb:26:7:26:10 | elem | ssa.rb:29:8:29:11 | elem | +| ssa.rb:33:16:35:5 | | ssa.rb:32:1:36:3 | self | ssa.rb:34:5:34:10 | self | +| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x | ssa.rb:34:10:34:10 | x | +| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:39:3:39:9 | self | +| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:39:8:39:9 | self | +| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:41:3:41:13 | self | +| ssa.rb:40:3:40:9 | ... = ... | ssa.rb:40:3:40:4 | m3 | ssa.rb:41:8:41:9 | m3 | +| ssa.rb:44:1:47:3 | self (m5) | ssa.rb:44:1:47:3 | self | ssa.rb:46:3:46:8 | self | +| ssa.rb:44:8:44:8 | b | ssa.rb:44:8:44:8 | b | ssa.rb:45:12:45:12 | b | +| ssa.rb:45:3:45:12 | phi | ssa.rb:45:3:45:3 | x | ssa.rb:46:8:46:8 | x | +| ssa.rb:49:1:51:3 | self (m6) | ssa.rb:49:1:51:3 | self | ssa.rb:50:3:50:8 | self | +| ssa.rb:50:3:50:8 | phi | ssa.rb:49:14:49:14 | y | ssa.rb:50:8:50:8 | y | +| ssa.rb:53:1:56:3 | self (m7) | ssa.rb:53:1:56:3 | self | ssa.rb:55:3:55:8 | self | +| ssa.rb:53:8:53:10 | foo | ssa.rb:53:8:53:10 | foo | ssa.rb:54:7:54:9 | foo | +| ssa.rb:54:3:54:11 | ... = ... | ssa.rb:54:3:54:3 | x | ssa.rb:55:8:55:8 | x | +| ssa.rb:58:1:62:3 | self (m8) | ssa.rb:58:1:62:3 | self | ssa.rb:61:3:61:8 | self | +| ssa.rb:59:3:59:8 | ... = ... | ssa.rb:59:3:59:3 | x | ssa.rb:60:3:60:3 | x | +| ssa.rb:60:3:60:9 | ... = ... | ssa.rb:59:3:59:3 | x | ssa.rb:61:8:61:8 | x | +| ssa.rb:64:1:72:3 | self (m9) | ssa.rb:64:1:72:3 | self | ssa.rb:71:3:71:15 | self | +| ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a | ssa.rb:66:3:66:3 | a | +| ssa.rb:66:3:70:5 | call to times | ssa.rb:65:3:65:10 | captured | ssa.rb:71:8:71:15 | captured | +| ssa.rb:66:11:70:5 | | ssa.rb:64:1:72:3 | self | ssa.rb:67:5:67:10 | self | +| ssa.rb:66:11:70:5 | | ssa.rb:64:1:72:3 | self | ssa.rb:68:5:68:17 | self | +| ssa.rb:66:11:70:5 | | ssa.rb:65:3:65:10 | captured | ssa.rb:68:10:68:17 | captured | +| ssa.rb:66:11:70:5 | | ssa.rb:65:3:65:10 | captured | ssa.rb:69:5:69:12 | captured | +| ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a | ssa.rb:67:10:67:10 | a | +| ssa.rb:74:1:79:3 | self (m10) | ssa.rb:74:1:79:3 | self | ssa.rb:76:3:78:5 | self | +| ssa.rb:76:7:78:5 | | ssa.rb:74:1:79:3 | self | ssa.rb:77:6:77:23 | self | +| ssa.rb:76:7:78:5 | | ssa.rb:75:3:75:10 | captured | ssa.rb:77:15:77:22 | captured | +| ssa.rb:81:1:88:3 | self (m11) | ssa.rb:81:1:88:3 | self | ssa.rb:83:3:87:5 | self | +| ssa.rb:83:7:87:5 | | ssa.rb:81:1:88:3 | self | ssa.rb:84:6:86:8 | self | +| ssa.rb:84:10:86:8 | | ssa.rb:81:1:88:3 | self | ssa.rb:85:10:85:22 | self | +| ssa.rb:84:10:86:8 | | ssa.rb:82:3:82:10 | captured | ssa.rb:85:15:85:22 | captured | +firstRead +| class_variables.rb:1:1:1:3 | self (class_variables.rb) | class_variables.rb:1:1:29:4 | self | class_variables.rb:3:1:3:5 | self | +| class_variables.rb:5:1:7:3 | self (print) | class_variables.rb:5:1:7:3 | self | class_variables.rb:6:2:6:6 | self | +| class_variables.rb:9:1:16:3 | self (X) | class_variables.rb:9:1:16:3 | self | class_variables.rb:13:7:13:10 | self | +| class_variables.rb:10:3:12:5 | self (b) | class_variables.rb:10:3:12:5 | self | class_variables.rb:11:5:11:9 | self | +| class_variables.rb:13:3:15:5 | self (s) | class_variables.rb:13:3:15:5 | self | class_variables.rb:14:4:14:8 | self | +| class_variables.rb:26:1:29:3 | self (N) | class_variables.rb:26:1:29:3 | self | class_variables.rb:27:3:27:11 | self | +| instance_variables.rb:1:1:1:4 | self (instance_variables.rb) | instance_variables.rb:1:1:44:4 | self | instance_variables.rb:11:1:11:9 | self | +| instance_variables.rb:7:1:9:3 | self (print_foo) | instance_variables.rb:7:1:9:3 | self | instance_variables.rb:8:3:8:11 | self | +| instance_variables.rb:37:3:43:5 | self (x) | instance_variables.rb:37:3:43:5 | self | instance_variables.rb:41:4:41:4 | self | +| nested_scopes.rb:1:1:3:3 | self (a) | nested_scopes.rb:1:1:3:3 | self | nested_scopes.rb:2:3:2:17 | self | +| nested_scopes.rb:4:1:39:3 | self (C) | nested_scopes.rb:4:1:39:3 | self | nested_scopes.rb:38:3:38:8 | self | +| nested_scopes.rb:5:3:5:7 | ... = ... | nested_scopes.rb:5:3:5:3 | a | nested_scopes.rb:38:8:38:8 | a | +| nested_scopes.rb:6:3:37:5 | self (M) | nested_scopes.rb:6:3:37:5 | self | nested_scopes.rb:36:5:36:10 | self | +| nested_scopes.rb:7:5:7:9 | ... = ... | nested_scopes.rb:7:5:7:5 | a | nested_scopes.rb:36:10:36:10 | a | +| nested_scopes.rb:8:5:35:7 | self (N) | nested_scopes.rb:8:5:35:7 | self | nested_scopes.rb:27:11:27:14 | self | +| nested_scopes.rb:9:7:9:11 | ... = ... | nested_scopes.rb:9:7:9:7 | a | nested_scopes.rb:34:12:34:12 | a | +| nested_scopes.rb:10:7:26:9 | self (D) | nested_scopes.rb:10:7:26:9 | self | nested_scopes.rb:25:9:25:14 | self | +| nested_scopes.rb:11:9:11:13 | ... = ... | nested_scopes.rb:11:9:11:9 | a | nested_scopes.rb:25:14:25:14 | a | +| nested_scopes.rb:12:9:21:11 | self (show_a) | nested_scopes.rb:12:9:21:11 | self | nested_scopes.rb:14:11:14:16 | self | +| nested_scopes.rb:13:11:13:15 | ... = ... | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:14:16:14:16 | a | +| nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:16:13:16:13 | a | +| nested_scopes.rb:17:15:17:19 | ... = ... | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:15:18:15 | a | +| nested_scopes.rb:18:23:18:36 | | nested_scopes.rb:12:9:21:11 | self | nested_scopes.rb:18:29:18:34 | self | +| nested_scopes.rb:18:23:18:36 | | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:34:18:34 | a | +| nested_scopes.rb:22:9:24:11 | self (show_a2) | nested_scopes.rb:22:9:24:11 | self | nested_scopes.rb:23:11:23:16 | self | +| nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:23:16:23:16 | a | +| nested_scopes.rb:27:7:29:9 | self (show) | nested_scopes.rb:27:7:29:9 | self | nested_scopes.rb:28:11:28:16 | self | +| nested_scopes.rb:30:16:30:19 | self (class << ...) | nested_scopes.rb:30:7:33:9 | self | nested_scopes.rb:32:11:32:16 | self | +| nested_scopes.rb:31:11:31:16 | ... = ... | nested_scopes.rb:31:11:31:11 | a | nested_scopes.rb:32:16:32:16 | a | +| nested_scopes.rb:40:1:40:18 | ... = ... | nested_scopes.rb:40:1:40:1 | d | nested_scopes.rb:41:1:41:1 | d | +| parameters.rb:1:9:5:3 | | parameters.rb:1:1:58:1 | self | parameters.rb:3:4:3:9 | self | +| parameters.rb:1:14:1:14 | x | parameters.rb:1:14:1:14 | x | parameters.rb:3:9:3:9 | x | +| parameters.rb:2:4:2:8 | ... = ... | parameters.rb:1:18:1:18 | y | parameters.rb:4:9:4:9 | y | +| parameters.rb:7:1:13:3 | self (order_pizza) | parameters.rb:7:1:13:3 | self | parameters.rb:9:5:9:33 | self | +| parameters.rb:7:1:13:3 | self (order_pizza) | parameters.rb:7:1:13:3 | self | parameters.rb:11:5:11:49 | self | +| parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:9:25:9:30 | client | +| parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:11:41:11:46 | client | +| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas | +| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map | parameters.rb:16:3:16:5 | map | +| parameters.rb:16:12:18:5 | | parameters.rb:15:1:19:3 | self | parameters.rb:17:5:17:28 | self | +| parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key | parameters.rb:17:13:17:15 | key | +| parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value | parameters.rb:17:22:17:26 | value | +| parameters.rb:21:17:21:21 | block | parameters.rb:21:17:21:21 | block | parameters.rb:22:3:22:7 | block | +| parameters.rb:25:1:28:3 | self (opt_param) | parameters.rb:25:1:28:3 | self | parameters.rb:26:3:26:11 | self | +| parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:25:40:25:43 | name | +| parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:26:8:26:11 | name | +| parameters.rb:25:33:25:36 | size | parameters.rb:25:33:25:36 | size | parameters.rb:27:8:27:11 | size | +| parameters.rb:30:1:32:3 | self (key_param) | parameters.rb:30:1:32:3 | self | parameters.rb:31:3:31:35 | self | +| parameters.rb:30:15:30:19 | first | parameters.rb:30:15:30:19 | first | parameters.rb:31:11:31:15 | first | +| parameters.rb:30:24:30:29 | middle | parameters.rb:30:24:30:29 | middle | parameters.rb:31:20:31:25 | middle | +| parameters.rb:30:36:30:39 | last | parameters.rb:30:36:30:39 | last | parameters.rb:31:30:31:33 | last | +| parameters.rb:35:1:38:3 | self (multi) | parameters.rb:35:1:38:3 | self | parameters.rb:37:3:37:18 | self | +| parameters.rb:35:11:35:11 | a | parameters.rb:35:11:35:11 | a | parameters.rb:37:11:37:11 | a | +| parameters.rb:37:3:37:18 | phi | parameters.rb:35:16:35:16 | b | parameters.rb:37:16:37:16 | b | +| parameters.rb:40:1:43:3 | self (multi2) | parameters.rb:40:1:43:3 | self | parameters.rb:42:3:42:18 | self | +| parameters.rb:40:12:40:12 | d | parameters.rb:40:12:40:12 | d | parameters.rb:42:11:42:11 | d | +| parameters.rb:42:3:42:18 | phi | parameters.rb:40:15:40:15 | e | parameters.rb:42:16:42:16 | e | +| parameters.rb:45:1:47:3 | self (dup_underscore) | parameters.rb:45:1:47:3 | self | parameters.rb:46:3:46:8 | self | +| parameters.rb:45:20:45:20 | _ | parameters.rb:45:20:45:20 | _ | parameters.rb:46:8:46:8 | _ | +| parameters.rb:49:1:51:3 | self (tuples) | parameters.rb:49:1:51:3 | self | parameters.rb:50:3:50:18 | self | +| parameters.rb:49:13:49:13 | a | parameters.rb:49:13:49:13 | a | parameters.rb:50:11:50:11 | a | +| parameters.rb:49:15:49:15 | b | parameters.rb:49:15:49:15 | b | parameters.rb:50:16:50:16 | b | +| parameters.rb:54:9:57:3 | | parameters.rb:1:1:58:1 | self | parameters.rb:55:4:55:9 | self | +| parameters.rb:54:14:54:14 | y | parameters.rb:54:14:54:14 | y | parameters.rb:56:9:56:9 | y | +| parameters.rb:55:4:55:9 | phi | parameters.rb:53:1:53:1 | x | parameters.rb:55:9:55:9 | x | +| scopes.rb:1:1:1:15 | self (scopes.rb) | scopes.rb:1:1:49:4 | self | scopes.rb:8:1:8:6 | self | +| scopes.rb:2:9:6:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:3:4:3:9 | self | +| scopes.rb:4:4:4:8 | ... = ... | scopes.rb:4:4:4:4 | a | scopes.rb:5:9:5:9 | a | +| scopes.rb:7:1:7:5 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:8:6:8:6 | a | +| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:10:4:10:9 | self | +| scopes.rb:9:9:18:3 | | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a | +| scopes.rb:11:4:11:9 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:12:9:12:9 | a | +| scopes.rb:13:4:13:4 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:14:9:14:9 | a | +| scopes.rb:13:7:13:7 | ... = ... | scopes.rb:13:7:13:7 | b | scopes.rb:15:9:15:9 | b | +| scopes.rb:13:10:13:15 | ... = ... | scopes.rb:13:10:13:15 | __synth__0__1 | scopes.rb:13:11:13:11 | __synth__0__1 | +| scopes.rb:13:11:13:11 | ... = ... | scopes.rb:13:11:13:11 | c | scopes.rb:16:9:16:9 | c | +| scopes.rb:13:14:13:14 | ... = ... | scopes.rb:13:14:13:14 | d | scopes.rb:17:9:17:9 | d | +| scopes.rb:13:19:13:32 | ... = ... | scopes.rb:13:4:13:32 | __synth__0 | scopes.rb:13:4:13:4 | __synth__0 | +| scopes.rb:27:1:27:5 | ... = ... | scopes.rb:27:1:27:1 | x | scopes.rb:28:8:28:8 | x | +| scopes.rb:41:1:49:3 | self (M) | scopes.rb:41:1:49:3 | self | scopes.rb:45:5:45:7 | self | +| scopes.rb:42:2:42:9 | ... = ... | scopes.rb:42:2:42:4 | var | scopes.rb:44:5:44:7 | var | +| scopes.rb:46:5:46:13 | ... = ... | scopes.rb:46:5:46:8 | var2 | scopes.rb:47:5:47:8 | var2 | +| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:3:3:3:8 | self | +| ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b | ssa.rb:5:6:5:6 | b | +| ssa.rb:2:3:2:7 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:3:8:3:8 | i | +| ssa.rb:5:3:13:5 | phi | ssa.rb:2:3:2:3 | i | ssa.rb:15:8:15:8 | i | +| ssa.rb:6:5:6:9 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:7:10:7:10 | i | +| ssa.rb:10:5:10:9 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:11:10:11:10 | i | +| ssa.rb:18:1:23:3 | self (m1) | ssa.rb:18:1:23:3 | self | ssa.rb:20:5:20:10 | self | +| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:19:9:19:9 | x | +| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self | +| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:29:3:29:11 | self | +| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:26:15:26:22 | elements | +| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:27:10:27:13 | elem | +| ssa.rb:26:12:26:22 | phi | ssa.rb:26:7:26:10 | elem | ssa.rb:29:8:29:11 | elem | +| ssa.rb:33:16:35:5 | | ssa.rb:32:1:36:3 | self | ssa.rb:34:5:34:10 | self | +| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x | ssa.rb:34:10:34:10 | x | +| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:39:3:39:9 | self | +| ssa.rb:40:3:40:9 | ... = ... | ssa.rb:40:3:40:4 | m3 | ssa.rb:41:8:41:9 | m3 | +| ssa.rb:44:1:47:3 | self (m5) | ssa.rb:44:1:47:3 | self | ssa.rb:46:3:46:8 | self | +| ssa.rb:44:8:44:8 | b | ssa.rb:44:8:44:8 | b | ssa.rb:45:12:45:12 | b | +| ssa.rb:45:3:45:12 | phi | ssa.rb:45:3:45:3 | x | ssa.rb:46:8:46:8 | x | +| ssa.rb:49:1:51:3 | self (m6) | ssa.rb:49:1:51:3 | self | ssa.rb:50:3:50:8 | self | +| ssa.rb:50:3:50:8 | phi | ssa.rb:49:14:49:14 | y | ssa.rb:50:8:50:8 | y | +| ssa.rb:53:1:56:3 | self (m7) | ssa.rb:53:1:56:3 | self | ssa.rb:55:3:55:8 | self | +| ssa.rb:53:8:53:10 | foo | ssa.rb:53:8:53:10 | foo | ssa.rb:54:7:54:9 | foo | +| ssa.rb:54:3:54:11 | ... = ... | ssa.rb:54:3:54:3 | x | ssa.rb:55:8:55:8 | x | +| ssa.rb:58:1:62:3 | self (m8) | ssa.rb:58:1:62:3 | self | ssa.rb:61:3:61:8 | self | +| ssa.rb:59:3:59:8 | ... = ... | ssa.rb:59:3:59:3 | x | ssa.rb:60:3:60:3 | x | +| ssa.rb:60:3:60:9 | ... = ... | ssa.rb:59:3:59:3 | x | ssa.rb:61:8:61:8 | x | +| ssa.rb:64:1:72:3 | self (m9) | ssa.rb:64:1:72:3 | self | ssa.rb:71:3:71:15 | self | +| ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a | ssa.rb:66:3:66:3 | a | +| ssa.rb:66:3:70:5 | call to times | ssa.rb:65:3:65:10 | captured | ssa.rb:71:8:71:15 | captured | +| ssa.rb:66:11:70:5 | | ssa.rb:64:1:72:3 | self | ssa.rb:67:5:67:10 | self | +| ssa.rb:66:11:70:5 | | ssa.rb:65:3:65:10 | captured | ssa.rb:68:10:68:17 | captured | +| ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a | ssa.rb:67:10:67:10 | a | +| ssa.rb:74:1:79:3 | self (m10) | ssa.rb:74:1:79:3 | self | ssa.rb:76:3:78:5 | self | +| ssa.rb:76:7:78:5 | | ssa.rb:74:1:79:3 | self | ssa.rb:77:6:77:23 | self | +| ssa.rb:76:7:78:5 | | ssa.rb:75:3:75:10 | captured | ssa.rb:77:15:77:22 | captured | +| ssa.rb:81:1:88:3 | self (m11) | ssa.rb:81:1:88:3 | self | ssa.rb:83:3:87:5 | self | +| ssa.rb:83:7:87:5 | | ssa.rb:81:1:88:3 | self | ssa.rb:84:6:86:8 | self | +| ssa.rb:84:10:86:8 | | ssa.rb:81:1:88:3 | self | ssa.rb:85:10:85:22 | self | +| ssa.rb:84:10:86:8 | | ssa.rb:82:3:82:10 | captured | ssa.rb:85:15:85:22 | captured | +lastRead +| class_variables.rb:1:1:1:3 | self (class_variables.rb) | class_variables.rb:1:1:29:4 | self | class_variables.rb:3:1:3:5 | self | +| class_variables.rb:5:1:7:3 | self (print) | class_variables.rb:5:1:7:3 | self | class_variables.rb:6:2:6:6 | self | +| class_variables.rb:9:1:16:3 | self (X) | class_variables.rb:9:1:16:3 | self | class_variables.rb:13:7:13:10 | self | +| class_variables.rb:10:3:12:5 | self (b) | class_variables.rb:10:3:12:5 | self | class_variables.rb:11:5:11:9 | self | +| class_variables.rb:13:3:15:5 | self (s) | class_variables.rb:13:3:15:5 | self | class_variables.rb:14:4:14:8 | self | +| class_variables.rb:26:1:29:3 | self (N) | class_variables.rb:26:1:29:3 | self | class_variables.rb:28:3:28:7 | self | +| instance_variables.rb:1:1:1:4 | self (instance_variables.rb) | instance_variables.rb:1:1:44:4 | self | instance_variables.rb:27:1:29:1 | self | +| instance_variables.rb:7:1:9:3 | self (print_foo) | instance_variables.rb:7:1:9:3 | self | instance_variables.rb:8:3:8:11 | self | +| instance_variables.rb:37:3:43:5 | self (x) | instance_variables.rb:37:3:43:5 | self | instance_variables.rb:42:4:42:7 | self | +| nested_scopes.rb:1:1:3:3 | self (a) | nested_scopes.rb:1:1:3:3 | self | nested_scopes.rb:2:3:2:17 | self | +| nested_scopes.rb:4:1:39:3 | self (C) | nested_scopes.rb:4:1:39:3 | self | nested_scopes.rb:38:3:38:8 | self | +| nested_scopes.rb:5:3:5:7 | ... = ... | nested_scopes.rb:5:3:5:3 | a | nested_scopes.rb:38:8:38:8 | a | +| nested_scopes.rb:6:3:37:5 | self (M) | nested_scopes.rb:6:3:37:5 | self | nested_scopes.rb:36:5:36:10 | self | +| nested_scopes.rb:7:5:7:9 | ... = ... | nested_scopes.rb:7:5:7:5 | a | nested_scopes.rb:36:10:36:10 | a | +| nested_scopes.rb:8:5:35:7 | self (N) | nested_scopes.rb:8:5:35:7 | self | nested_scopes.rb:34:7:34:12 | self | +| nested_scopes.rb:9:7:9:11 | ... = ... | nested_scopes.rb:9:7:9:7 | a | nested_scopes.rb:34:12:34:12 | a | +| nested_scopes.rb:10:7:26:9 | self (D) | nested_scopes.rb:10:7:26:9 | self | nested_scopes.rb:25:9:25:14 | self | +| nested_scopes.rb:11:9:11:13 | ... = ... | nested_scopes.rb:11:9:11:9 | a | nested_scopes.rb:25:14:25:14 | a | +| nested_scopes.rb:12:9:21:11 | self (show_a) | nested_scopes.rb:12:9:21:11 | self | nested_scopes.rb:14:11:14:16 | self | +| nested_scopes.rb:13:11:13:15 | ... = ... | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:15:11:15:11 | a | +| nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:16:13:16:13 | a | +| nested_scopes.rb:17:15:17:19 | ... = ... | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:15:18:15 | a | +| nested_scopes.rb:18:23:18:36 | | nested_scopes.rb:12:9:21:11 | self | nested_scopes.rb:18:29:18:34 | self | +| nested_scopes.rb:18:23:18:36 | | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:34:18:34 | a | +| nested_scopes.rb:22:9:24:11 | self (show_a2) | nested_scopes.rb:22:9:24:11 | self | nested_scopes.rb:23:11:23:16 | self | +| nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:23:16:23:16 | a | +| nested_scopes.rb:27:7:29:9 | self (show) | nested_scopes.rb:27:7:29:9 | self | nested_scopes.rb:28:16:28:16 | self | +| nested_scopes.rb:30:16:30:19 | self (class << ...) | nested_scopes.rb:30:7:33:9 | self | nested_scopes.rb:32:11:32:16 | self | +| nested_scopes.rb:31:11:31:16 | ... = ... | nested_scopes.rb:31:11:31:11 | a | nested_scopes.rb:32:16:32:16 | a | +| nested_scopes.rb:40:1:40:18 | ... = ... | nested_scopes.rb:40:1:40:1 | d | nested_scopes.rb:41:1:41:1 | d | +| parameters.rb:1:9:5:3 | | parameters.rb:1:1:58:1 | self | parameters.rb:4:4:4:9 | self | +| parameters.rb:1:14:1:14 | x | parameters.rb:1:14:1:14 | x | parameters.rb:3:9:3:9 | x | +| parameters.rb:2:4:2:8 | ... = ... | parameters.rb:1:18:1:18 | y | parameters.rb:4:9:4:9 | y | +| parameters.rb:7:1:13:3 | self (order_pizza) | parameters.rb:7:1:13:3 | self | parameters.rb:9:5:9:33 | self | +| parameters.rb:7:1:13:3 | self (order_pizza) | parameters.rb:7:1:13:3 | self | parameters.rb:11:5:11:49 | self | +| parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:9:25:9:30 | client | +| parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:11:41:11:46 | client | +| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas | +| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:11:14:11:19 | pizzas | +| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map | parameters.rb:16:3:16:5 | map | +| parameters.rb:16:12:18:5 | | parameters.rb:15:1:19:3 | self | parameters.rb:17:5:17:28 | self | +| parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key | parameters.rb:17:13:17:15 | key | +| parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value | parameters.rb:17:22:17:26 | value | +| parameters.rb:21:17:21:21 | block | parameters.rb:21:17:21:21 | block | parameters.rb:22:3:22:7 | block | +| parameters.rb:25:1:28:3 | self (opt_param) | parameters.rb:25:1:28:3 | self | parameters.rb:27:3:27:11 | self | +| parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:26:8:26:11 | name | +| parameters.rb:25:33:25:36 | size | parameters.rb:25:33:25:36 | size | parameters.rb:27:8:27:11 | size | +| parameters.rb:30:1:32:3 | self (key_param) | parameters.rb:30:1:32:3 | self | parameters.rb:31:3:31:35 | self | +| parameters.rb:30:15:30:19 | first | parameters.rb:30:15:30:19 | first | parameters.rb:31:11:31:15 | first | +| parameters.rb:30:24:30:29 | middle | parameters.rb:30:24:30:29 | middle | parameters.rb:31:20:31:25 | middle | +| parameters.rb:30:36:30:39 | last | parameters.rb:30:36:30:39 | last | parameters.rb:31:30:31:33 | last | +| parameters.rb:35:1:38:3 | self (multi) | parameters.rb:35:1:38:3 | self | parameters.rb:37:3:37:18 | self | +| parameters.rb:35:11:35:11 | a | parameters.rb:35:11:35:11 | a | parameters.rb:37:11:37:11 | a | +| parameters.rb:37:3:37:18 | phi | parameters.rb:35:16:35:16 | b | parameters.rb:37:16:37:16 | b | +| parameters.rb:40:1:43:3 | self (multi2) | parameters.rb:40:1:43:3 | self | parameters.rb:42:3:42:18 | self | +| parameters.rb:40:12:40:12 | d | parameters.rb:40:12:40:12 | d | parameters.rb:42:11:42:11 | d | +| parameters.rb:42:3:42:18 | phi | parameters.rb:40:15:40:15 | e | parameters.rb:42:16:42:16 | e | +| parameters.rb:45:1:47:3 | self (dup_underscore) | parameters.rb:45:1:47:3 | self | parameters.rb:46:3:46:8 | self | +| parameters.rb:45:20:45:20 | _ | parameters.rb:45:20:45:20 | _ | parameters.rb:46:8:46:8 | _ | +| parameters.rb:49:1:51:3 | self (tuples) | parameters.rb:49:1:51:3 | self | parameters.rb:50:3:50:18 | self | +| parameters.rb:49:13:49:13 | a | parameters.rb:49:13:49:13 | a | parameters.rb:50:11:50:11 | a | +| parameters.rb:49:15:49:15 | b | parameters.rb:49:15:49:15 | b | parameters.rb:50:16:50:16 | b | +| parameters.rb:54:9:57:3 | | parameters.rb:1:1:58:1 | self | parameters.rb:56:4:56:9 | self | +| parameters.rb:54:14:54:14 | y | parameters.rb:54:14:54:14 | y | parameters.rb:56:9:56:9 | y | +| parameters.rb:55:4:55:9 | phi | parameters.rb:53:1:53:1 | x | parameters.rb:55:9:55:9 | x | +| scopes.rb:1:1:1:15 | self (scopes.rb) | scopes.rb:1:1:49:4 | self | scopes.rb:8:1:8:6 | self | +| scopes.rb:2:9:6:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:5:4:5:9 | self | +| scopes.rb:4:4:4:8 | ... = ... | scopes.rb:4:4:4:4 | a | scopes.rb:5:9:5:9 | a | +| scopes.rb:7:1:7:5 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:8:6:8:6 | a | +| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:17:4:17:9 | self | +| scopes.rb:9:9:18:3 | | scopes.rb:7:1:7:1 | a | scopes.rb:11:4:11:4 | a | +| scopes.rb:11:4:11:9 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:12:9:12:9 | a | +| scopes.rb:13:4:13:4 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:14:9:14:9 | a | +| scopes.rb:13:7:13:7 | ... = ... | scopes.rb:13:7:13:7 | b | scopes.rb:15:9:15:9 | b | +| scopes.rb:13:10:13:15 | ... = ... | scopes.rb:13:10:13:15 | __synth__0__1 | scopes.rb:13:14:13:14 | __synth__0__1 | +| scopes.rb:13:11:13:11 | ... = ... | scopes.rb:13:11:13:11 | c | scopes.rb:16:9:16:9 | c | +| scopes.rb:13:14:13:14 | ... = ... | scopes.rb:13:14:13:14 | d | scopes.rb:17:9:17:9 | d | +| scopes.rb:13:19:13:32 | ... = ... | scopes.rb:13:4:13:32 | __synth__0 | scopes.rb:13:10:13:15 | __synth__0 | +| scopes.rb:27:1:27:5 | ... = ... | scopes.rb:27:1:27:1 | x | scopes.rb:37:5:37:5 | x | +| scopes.rb:41:1:49:3 | self (M) | scopes.rb:41:1:49:3 | self | scopes.rb:45:5:45:7 | self | +| scopes.rb:42:2:42:9 | ... = ... | scopes.rb:42:2:42:4 | var | scopes.rb:44:5:44:7 | var | +| scopes.rb:46:5:46:13 | ... = ... | scopes.rb:46:5:46:8 | var2 | scopes.rb:47:5:47:8 | var2 | +| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:15:3:15:8 | self | +| ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b | ssa.rb:5:6:5:6 | b | +| ssa.rb:2:3:2:7 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:4:8:4:8 | i | +| ssa.rb:5:3:13:5 | phi | ssa.rb:2:3:2:3 | i | ssa.rb:15:8:15:8 | i | +| ssa.rb:6:5:6:9 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:8:10:8:10 | i | +| ssa.rb:10:5:10:9 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:12:10:12:10 | i | +| ssa.rb:18:1:23:3 | self (m1) | ssa.rb:18:1:23:3 | self | ssa.rb:20:5:20:10 | self | +| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:19:9:19:9 | x | +| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:21:5:21:5 | x | +| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:29:3:29:11 | self | +| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:26:15:26:22 | elements | +| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:27:10:27:13 | elem | +| ssa.rb:26:12:26:22 | phi | ssa.rb:26:7:26:10 | elem | ssa.rb:29:8:29:11 | elem | +| ssa.rb:33:16:35:5 | | ssa.rb:32:1:36:3 | self | ssa.rb:34:5:34:10 | self | +| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x | ssa.rb:34:10:34:10 | x | +| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:41:3:41:13 | self | +| ssa.rb:40:3:40:9 | ... = ... | ssa.rb:40:3:40:4 | m3 | ssa.rb:41:8:41:9 | m3 | +| ssa.rb:44:1:47:3 | self (m5) | ssa.rb:44:1:47:3 | self | ssa.rb:46:3:46:8 | self | +| ssa.rb:44:8:44:8 | b | ssa.rb:44:8:44:8 | b | ssa.rb:45:12:45:12 | b | +| ssa.rb:45:3:45:12 | phi | ssa.rb:45:3:45:3 | x | ssa.rb:46:8:46:8 | x | +| ssa.rb:49:1:51:3 | self (m6) | ssa.rb:49:1:51:3 | self | ssa.rb:50:3:50:8 | self | +| ssa.rb:50:3:50:8 | phi | ssa.rb:49:14:49:14 | y | ssa.rb:50:8:50:8 | y | +| ssa.rb:53:1:56:3 | self (m7) | ssa.rb:53:1:56:3 | self | ssa.rb:55:3:55:8 | self | +| ssa.rb:53:8:53:10 | foo | ssa.rb:53:8:53:10 | foo | ssa.rb:54:7:54:9 | foo | +| ssa.rb:54:3:54:11 | ... = ... | ssa.rb:54:3:54:3 | x | ssa.rb:55:8:55:8 | x | +| ssa.rb:58:1:62:3 | self (m8) | ssa.rb:58:1:62:3 | self | ssa.rb:61:3:61:8 | self | +| ssa.rb:59:3:59:8 | ... = ... | ssa.rb:59:3:59:3 | x | ssa.rb:60:3:60:3 | x | +| ssa.rb:60:3:60:9 | ... = ... | ssa.rb:59:3:59:3 | x | ssa.rb:61:8:61:8 | x | +| ssa.rb:64:1:72:3 | self (m9) | ssa.rb:64:1:72:3 | self | ssa.rb:71:3:71:15 | self | +| ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a | ssa.rb:66:3:66:3 | a | +| ssa.rb:66:3:70:5 | call to times | ssa.rb:65:3:65:10 | captured | ssa.rb:71:8:71:15 | captured | +| ssa.rb:66:11:70:5 | | ssa.rb:64:1:72:3 | self | ssa.rb:68:5:68:17 | self | +| ssa.rb:66:11:70:5 | | ssa.rb:65:3:65:10 | captured | ssa.rb:69:5:69:12 | captured | +| ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a | ssa.rb:67:10:67:10 | a | +| ssa.rb:74:1:79:3 | self (m10) | ssa.rb:74:1:79:3 | self | ssa.rb:76:3:78:5 | self | +| ssa.rb:76:7:78:5 | | ssa.rb:74:1:79:3 | self | ssa.rb:77:6:77:23 | self | +| ssa.rb:76:7:78:5 | | ssa.rb:75:3:75:10 | captured | ssa.rb:77:15:77:22 | captured | +| ssa.rb:81:1:88:3 | self (m11) | ssa.rb:81:1:88:3 | self | ssa.rb:83:3:87:5 | self | +| ssa.rb:83:7:87:5 | | ssa.rb:81:1:88:3 | self | ssa.rb:84:6:86:8 | self | +| ssa.rb:84:10:86:8 | | ssa.rb:81:1:88:3 | self | ssa.rb:85:10:85:22 | self | +| ssa.rb:84:10:86:8 | | ssa.rb:82:3:82:10 | captured | ssa.rb:85:15:85:22 | captured | +adjacentReads +| class_variables.rb:26:1:29:3 | self (N) | class_variables.rb:26:1:29:3 | self | class_variables.rb:27:3:27:11 | self | class_variables.rb:28:3:28:7 | self | +| instance_variables.rb:1:1:1:4 | self (instance_variables.rb) | instance_variables.rb:1:1:44:4 | self | instance_variables.rb:11:1:11:9 | self | instance_variables.rb:27:1:29:1 | self | +| instance_variables.rb:37:3:43:5 | self (x) | instance_variables.rb:37:3:43:5 | self | instance_variables.rb:41:4:41:4 | self | instance_variables.rb:42:4:42:7 | self | +| nested_scopes.rb:8:5:35:7 | self (N) | nested_scopes.rb:8:5:35:7 | self | nested_scopes.rb:27:11:27:14 | self | nested_scopes.rb:30:16:30:19 | self | +| nested_scopes.rb:8:5:35:7 | self (N) | nested_scopes.rb:8:5:35:7 | self | nested_scopes.rb:30:16:30:19 | self | nested_scopes.rb:34:7:34:12 | self | +| nested_scopes.rb:13:11:13:15 | ... = ... | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:14:16:14:16 | a | nested_scopes.rb:15:11:15:11 | a | +| nested_scopes.rb:27:7:29:9 | self (show) | nested_scopes.rb:27:7:29:9 | self | nested_scopes.rb:28:11:28:16 | self | nested_scopes.rb:28:16:28:16 | self | +| nested_scopes.rb:30:16:30:19 | self (class << ...) | nested_scopes.rb:30:7:33:9 | self | nested_scopes.rb:30:16:30:19 | self | nested_scopes.rb:32:11:32:16 | self | +| parameters.rb:1:9:5:3 | | parameters.rb:1:1:58:1 | self | parameters.rb:3:4:3:9 | self | parameters.rb:4:4:4:9 | self | +| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas | parameters.rb:11:14:11:19 | pizzas | +| parameters.rb:25:1:28:3 | self (opt_param) | parameters.rb:25:1:28:3 | self | parameters.rb:26:3:26:11 | self | parameters.rb:27:3:27:11 | self | +| parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:25:40:25:43 | name | parameters.rb:26:8:26:11 | name | +| parameters.rb:54:9:57:3 | | parameters.rb:1:1:58:1 | self | parameters.rb:55:4:55:9 | self | parameters.rb:56:4:56:9 | self | +| scopes.rb:2:9:6:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:3:4:3:9 | self | scopes.rb:3:9:3:9 | self | +| scopes.rb:2:9:6:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:3:9:3:9 | self | scopes.rb:5:4:5:9 | self | +| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:10:4:10:9 | self | scopes.rb:12:4:12:9 | self | +| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:12:4:12:9 | self | scopes.rb:14:4:14:9 | self | +| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:14:4:14:9 | self | scopes.rb:15:4:15:9 | self | +| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:15:4:15:9 | self | scopes.rb:16:4:16:9 | self | +| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:16:4:16:9 | self | scopes.rb:17:4:17:9 | self | +| scopes.rb:9:9:18:3 | | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a | scopes.rb:11:4:11:4 | a | +| scopes.rb:13:10:13:15 | ... = ... | scopes.rb:13:10:13:15 | __synth__0__1 | scopes.rb:13:11:13:11 | __synth__0__1 | scopes.rb:13:14:13:14 | __synth__0__1 | +| scopes.rb:13:19:13:32 | ... = ... | scopes.rb:13:4:13:32 | __synth__0 | scopes.rb:13:4:13:4 | __synth__0 | scopes.rb:13:7:13:7 | __synth__0 | +| scopes.rb:13:19:13:32 | ... = ... | scopes.rb:13:4:13:32 | __synth__0 | scopes.rb:13:7:13:7 | __synth__0 | scopes.rb:13:10:13:15 | __synth__0 | +| scopes.rb:27:1:27:5 | ... = ... | scopes.rb:27:1:27:1 | x | scopes.rb:28:8:28:8 | x | scopes.rb:31:10:31:10 | x | +| scopes.rb:27:1:27:5 | ... = ... | scopes.rb:27:1:27:1 | x | scopes.rb:31:10:31:10 | x | scopes.rb:34:7:34:7 | x | +| scopes.rb:27:1:27:5 | ... = ... | scopes.rb:27:1:27:1 | x | scopes.rb:34:7:34:7 | x | scopes.rb:34:14:34:14 | x | +| scopes.rb:27:1:27:5 | ... = ... | scopes.rb:27:1:27:1 | x | scopes.rb:34:14:34:14 | x | scopes.rb:37:5:37:5 | x | +| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:3:3:3:8 | self | ssa.rb:4:3:4:12 | self | +| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:4:3:4:12 | self | ssa.rb:7:5:7:10 | self | +| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:4:3:4:12 | self | ssa.rb:11:5:11:10 | self | +| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:7:5:7:10 | self | ssa.rb:8:5:8:14 | self | +| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:8:5:8:14 | self | ssa.rb:15:3:15:8 | self | +| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:11:5:11:10 | self | ssa.rb:12:5:12:14 | self | +| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:12:5:12:14 | self | ssa.rb:15:3:15:8 | self | +| ssa.rb:2:3:2:7 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:3:8:3:8 | i | ssa.rb:4:8:4:8 | i | +| ssa.rb:6:5:6:9 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:7:10:7:10 | i | ssa.rb:8:10:8:10 | i | +| ssa.rb:10:5:10:9 | ... = ... | ssa.rb:2:3:2:3 | i | ssa.rb:11:10:11:10 | i | ssa.rb:12:10:12:10 | i | +| ssa.rb:18:1:23:3 | self (m1) | ssa.rb:18:1:23:3 | self | ssa.rb:20:5:20:10 | self | ssa.rb:20:5:20:10 | self | +| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:19:9:19:9 | x | ssa.rb:20:10:20:10 | x | +| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:20:10:20:10 | x | ssa.rb:21:5:21:5 | x | +| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self | ssa.rb:27:5:27:13 | self | +| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self | ssa.rb:29:3:29:11 | self | +| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:39:3:39:9 | self | ssa.rb:39:8:39:9 | self | +| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:39:8:39:9 | self | ssa.rb:41:3:41:13 | self | +| ssa.rb:66:11:70:5 | | ssa.rb:64:1:72:3 | self | ssa.rb:67:5:67:10 | self | ssa.rb:68:5:68:17 | self | +| ssa.rb:66:11:70:5 | | ssa.rb:65:3:65:10 | captured | ssa.rb:68:10:68:17 | captured | ssa.rb:69:5:69:12 | captured | +phi +| parameters.rb:37:3:37:18 | phi | parameters.rb:35:16:35:16 | b | parameters.rb:35:1:38:3 | | +| parameters.rb:37:3:37:18 | phi | parameters.rb:35:16:35:16 | b | parameters.rb:35:16:35:20 | ... = ... | +| parameters.rb:42:3:42:18 | phi | parameters.rb:40:15:40:15 | e | parameters.rb:40:1:43:3 | | +| parameters.rb:42:3:42:18 | phi | parameters.rb:40:15:40:15 | e | parameters.rb:40:15:40:19 | ... = ... | +| parameters.rb:55:4:55:9 | phi | parameters.rb:53:1:53:1 | x | parameters.rb:54:9:57:3 | | +| parameters.rb:55:4:55:9 | phi | parameters.rb:53:1:53:1 | x | parameters.rb:54:19:54:23 | ... = ... | +| ssa.rb:5:3:13:5 | phi | ssa.rb:2:3:2:3 | i | ssa.rb:6:5:6:9 | ... = ... | +| ssa.rb:5:3:13:5 | phi | ssa.rb:2:3:2:3 | i | ssa.rb:10:5:10:9 | ... = ... | +| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:18:8:18:8 | x | +| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:21:5:21:10 | ... = ... | +| ssa.rb:26:12:26:22 | phi | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | | +| ssa.rb:26:12:26:22 | phi | ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem | +| ssa.rb:45:3:45:12 | phi | ssa.rb:45:3:45:3 | x | ssa.rb:44:1:47:3 | | +| ssa.rb:45:3:45:12 | phi | ssa.rb:45:3:45:3 | x | ssa.rb:45:3:45:7 | ... = ... | +| ssa.rb:50:3:50:8 | phi | ssa.rb:49:14:49:14 | y | ssa.rb:49:1:51:3 | | +| ssa.rb:50:3:50:8 | phi | ssa.rb:49:14:49:14 | y | ssa.rb:49:14:49:19 | ... = ... | diff --git a/ruby/ql/test/library-tests/variables/ssa.ql b/ruby/ql/test/library-tests/variables/ssa.ql new file mode 100644 index 00000000000..df2f9cd91e5 --- /dev/null +++ b/ruby/ql/test/library-tests/variables/ssa.ql @@ -0,0 +1,27 @@ +import codeql.ruby.AST +import codeql.ruby.CFG +import codeql.ruby.ast.Variable +import codeql.ruby.dataflow.SSA + +query predicate definition(Ssa::Definition def, Variable v) { def.getSourceVariable() = v } + +query predicate read(Ssa::Definition def, Variable v, CfgNode read) { + def.getSourceVariable() = v and read = def.getARead() +} + +query predicate firstRead(Ssa::Definition def, Variable v, CfgNode read) { + def.getSourceVariable() = v and read = def.getAFirstRead() +} + +query predicate lastRead(Ssa::Definition def, Variable v, CfgNode read) { + def.getSourceVariable() = v and read = def.getALastRead() +} + +query predicate adjacentReads(Ssa::Definition def, Variable v, CfgNode read1, CfgNode read2) { + def.getSourceVariable() = v and + def.hasAdjacentReads(read1, read2) +} + +query predicate phi(Ssa::PhiNode phi, Variable v, Ssa::Definition input) { + phi.getSourceVariable() = v and input = phi.getAnInput() +} diff --git a/ruby/ql/test/library-tests/variables/ssa.rb b/ruby/ql/test/library-tests/variables/ssa.rb new file mode 100644 index 00000000000..bb13dd6c4c9 --- /dev/null +++ b/ruby/ql/test/library-tests/variables/ssa.rb @@ -0,0 +1,88 @@ +def m b # defines b_0 + i = 0 # defines i_0 + puts i # reads i_0 (first read) + puts i + 1 # reads i_0 (last read) + if b # reads b_0 + i = 1 # defines i_1 + puts i # reads i_1 (first read) + puts i + 1 # reads i_1 (last read) + else + i = 2 # defines i_2 + puts i # reads i_2 (first read) + puts i + 1 # reads i_2 (last read) + end + # defines i_3 = phi(i_1, i_2) + puts i # reads i3 (first read and last read) +end + +def m1 x + while x >= 0 + puts x + x -= 1 + end +end + +def m2 elements + for elem in elements do + puts elem + end + puts elem +end + +def m3 + [1,2,3].each do |x| + puts x + end +end + +def m4 + puts m3 + m3 = 10 + puts m3 + 1 +end + +def m5 b + x = 0 if b + puts x +end + +def m6 (x = (y = 10)) + puts y +end + +def m7 foo + x = foo.x + puts x +end + +def m8 + x = 10 + x += 10 + puts x +end + +def m9 a + captured = 10 + a.times do |a| + puts a + puts captured + captured += 1 + end + puts captured +end + +def m10 + captured = 0 + foo do + bar(baz: captured) + end +end + +def m11 + captured = 0 + foo do + bar do + puts captured + end + end +end \ No newline at end of file diff --git a/ruby/ql/test/library-tests/variables/varaccess.expected b/ruby/ql/test/library-tests/variables/varaccess.expected new file mode 100644 index 00000000000..332f3fa8af9 --- /dev/null +++ b/ruby/ql/test/library-tests/variables/varaccess.expected @@ -0,0 +1,541 @@ +variableAccess +| class_variables.rb:1:1:1:3 | @@x | class_variables.rb:1:1:1:3 | @@x | class_variables.rb:1:1:29:4 | class_variables.rb | +| class_variables.rb:3:1:3:5 | self | class_variables.rb:1:1:29:4 | self | class_variables.rb:1:1:29:4 | class_variables.rb | +| class_variables.rb:3:3:3:5 | @@x | class_variables.rb:1:1:1:3 | @@x | class_variables.rb:1:1:29:4 | class_variables.rb | +| class_variables.rb:6:2:6:6 | self | class_variables.rb:5:1:7:3 | self | class_variables.rb:5:1:7:3 | print | +| class_variables.rb:6:4:6:6 | @@x | class_variables.rb:1:1:1:3 | @@x | class_variables.rb:1:1:29:4 | class_variables.rb | +| class_variables.rb:11:5:11:9 | self | class_variables.rb:10:3:12:5 | self | class_variables.rb:10:3:12:5 | b | +| class_variables.rb:11:7:11:9 | @@x | class_variables.rb:11:7:11:9 | @@x | class_variables.rb:9:1:16:3 | X | +| class_variables.rb:13:7:13:10 | self | class_variables.rb:9:1:16:3 | self | class_variables.rb:9:1:16:3 | X | +| class_variables.rb:14:4:14:8 | self | class_variables.rb:13:3:15:5 | self | class_variables.rb:13:3:15:5 | s | +| class_variables.rb:14:6:14:8 | @@x | class_variables.rb:11:7:11:9 | @@x | class_variables.rb:9:1:16:3 | X | +| class_variables.rb:19:3:19:5 | @@x | class_variables.rb:19:3:19:5 | @@x | class_variables.rb:18:1:20:3 | Y | +| class_variables.rb:23:3:23:5 | @@x | class_variables.rb:23:3:23:5 | @@x | class_variables.rb:22:1:24:3 | M | +| class_variables.rb:27:3:27:11 | self | class_variables.rb:26:1:29:3 | self | class_variables.rb:26:1:29:3 | N | +| class_variables.rb:28:3:28:7 | self | class_variables.rb:26:1:29:3 | self | class_variables.rb:26:1:29:3 | N | +| class_variables.rb:28:5:28:7 | @@x | class_variables.rb:28:5:28:7 | @@x | class_variables.rb:26:1:29:3 | N | +| instance_variables.rb:1:1:1:4 | @top | instance_variables.rb:1:1:1:4 | @top | instance_variables.rb:1:1:44:4 | instance_variables.rb | +| instance_variables.rb:4:3:4:6 | @foo | instance_variables.rb:4:3:4:6 | @foo | instance_variables.rb:1:1:44:4 | instance_variables.rb | +| instance_variables.rb:8:3:8:11 | self | instance_variables.rb:7:1:9:3 | self | instance_variables.rb:7:1:9:3 | print_foo | +| instance_variables.rb:8:8:8:11 | @foo | instance_variables.rb:4:3:4:6 | @foo | instance_variables.rb:1:1:44:4 | instance_variables.rb | +| instance_variables.rb:11:1:11:9 | self | instance_variables.rb:1:1:44:4 | self | instance_variables.rb:1:1:44:4 | instance_variables.rb | +| instance_variables.rb:11:6:11:9 | @top | instance_variables.rb:1:1:1:4 | @top | instance_variables.rb:1:1:44:4 | instance_variables.rb | +| instance_variables.rb:14:3:14:4 | @x | instance_variables.rb:14:3:14:4 | @x | instance_variables.rb:13:1:18:3 | X | +| instance_variables.rb:16:5:16:6 | @y | instance_variables.rb:16:5:16:6 | @y | instance_variables.rb:13:1:18:3 | X | +| instance_variables.rb:21:2:21:3 | @m | instance_variables.rb:21:2:21:3 | @m | instance_variables.rb:20:1:25:3 | M | +| instance_variables.rb:23:4:23:5 | @n | instance_variables.rb:23:4:23:5 | @n | instance_variables.rb:20:1:25:3 | M | +| instance_variables.rb:27:1:29:1 | self | instance_variables.rb:1:1:44:4 | self | instance_variables.rb:1:1:44:4 | instance_variables.rb | +| instance_variables.rb:28:3:28:4 | @x | instance_variables.rb:28:3:28:4 | @x | instance_variables.rb:1:1:44:4 | instance_variables.rb | +| instance_variables.rb:32:12:32:13 | @x | instance_variables.rb:32:12:32:13 | @x | instance_variables.rb:1:1:44:4 | instance_variables.rb | +| instance_variables.rb:36:3:36:4 | @x | instance_variables.rb:36:3:36:4 | @x | instance_variables.rb:35:1:44:4 | C | +| instance_variables.rb:39:6:39:7 | @x | instance_variables.rb:39:6:39:7 | @x | instance_variables.rb:35:1:44:4 | C | +| instance_variables.rb:41:4:41:4 | self | instance_variables.rb:37:3:43:5 | self | instance_variables.rb:37:3:43:5 | x | +| instance_variables.rb:42:4:42:7 | self | instance_variables.rb:37:3:43:5 | self | instance_variables.rb:37:3:43:5 | x | +| instance_variables.rb:42:6:42:7 | @x | instance_variables.rb:39:6:39:7 | @x | instance_variables.rb:35:1:44:4 | C | +| nested_scopes.rb:2:3:2:17 | self | nested_scopes.rb:1:1:3:3 | self | nested_scopes.rb:1:1:3:3 | a | +| nested_scopes.rb:5:3:5:3 | a | nested_scopes.rb:5:3:5:3 | a | nested_scopes.rb:4:1:39:3 | C | +| nested_scopes.rb:7:5:7:5 | a | nested_scopes.rb:7:5:7:5 | a | nested_scopes.rb:6:3:37:5 | M | +| nested_scopes.rb:9:7:9:7 | a | nested_scopes.rb:9:7:9:7 | a | nested_scopes.rb:8:5:35:7 | N | +| nested_scopes.rb:11:9:11:9 | a | nested_scopes.rb:11:9:11:9 | a | nested_scopes.rb:10:7:26:9 | D | +| nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:12:9:21:11 | show_a | +| nested_scopes.rb:14:11:14:16 | self | nested_scopes.rb:12:9:21:11 | self | nested_scopes.rb:12:9:21:11 | show_a | +| nested_scopes.rb:14:16:14:16 | a | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:12:9:21:11 | show_a | +| nested_scopes.rb:15:11:15:11 | a | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:12:9:21:11 | show_a | +| nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:19:20:13 | do ... end | +| nested_scopes.rb:16:13:16:13 | a | nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:19:20:13 | do ... end | +| nested_scopes.rb:16:26:16:26 | x | nested_scopes.rb:16:26:16:26 | x | nested_scopes.rb:16:21:19:15 | do ... end | +| nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:16:21:19:15 | do ... end | +| nested_scopes.rb:17:15:17:15 | a | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:16:21:19:15 | do ... end | +| nested_scopes.rb:18:15:18:15 | a | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:16:21:19:15 | do ... end | +| nested_scopes.rb:18:26:18:26 | x | nested_scopes.rb:18:26:18:26 | x | nested_scopes.rb:18:23:18:36 | { ... } | +| nested_scopes.rb:18:29:18:34 | self | nested_scopes.rb:12:9:21:11 | self | nested_scopes.rb:12:9:21:11 | show_a | +| nested_scopes.rb:18:34:18:34 | a | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:16:21:19:15 | do ... end | +| nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:9:24:11 | show_a2 | +| nested_scopes.rb:23:11:23:16 | self | nested_scopes.rb:22:9:24:11 | self | nested_scopes.rb:22:9:24:11 | show_a2 | +| nested_scopes.rb:23:16:23:16 | a | nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:9:24:11 | show_a2 | +| nested_scopes.rb:25:9:25:14 | self | nested_scopes.rb:10:7:26:9 | self | nested_scopes.rb:10:7:26:9 | D | +| nested_scopes.rb:25:14:25:14 | a | nested_scopes.rb:11:9:11:9 | a | nested_scopes.rb:10:7:26:9 | D | +| nested_scopes.rb:27:11:27:14 | self | nested_scopes.rb:8:5:35:7 | self | nested_scopes.rb:8:5:35:7 | N | +| nested_scopes.rb:28:11:28:16 | self | nested_scopes.rb:27:7:29:9 | self | nested_scopes.rb:27:7:29:9 | show | +| nested_scopes.rb:28:16:28:16 | self | nested_scopes.rb:27:7:29:9 | self | nested_scopes.rb:27:7:29:9 | show | +| nested_scopes.rb:30:16:30:19 | self | nested_scopes.rb:8:5:35:7 | self | nested_scopes.rb:8:5:35:7 | N | +| nested_scopes.rb:31:11:31:11 | a | nested_scopes.rb:31:11:31:11 | a | nested_scopes.rb:30:7:33:9 | class << ... | +| nested_scopes.rb:32:11:32:16 | self | nested_scopes.rb:30:7:33:9 | self | nested_scopes.rb:30:7:33:9 | class << ... | +| nested_scopes.rb:32:16:32:16 | a | nested_scopes.rb:31:11:31:11 | a | nested_scopes.rb:30:7:33:9 | class << ... | +| nested_scopes.rb:34:7:34:12 | self | nested_scopes.rb:8:5:35:7 | self | nested_scopes.rb:8:5:35:7 | N | +| nested_scopes.rb:34:12:34:12 | a | nested_scopes.rb:9:7:9:7 | a | nested_scopes.rb:8:5:35:7 | N | +| nested_scopes.rb:36:5:36:10 | self | nested_scopes.rb:6:3:37:5 | self | nested_scopes.rb:6:3:37:5 | M | +| nested_scopes.rb:36:10:36:10 | a | nested_scopes.rb:7:5:7:5 | a | nested_scopes.rb:6:3:37:5 | M | +| nested_scopes.rb:38:3:38:8 | self | nested_scopes.rb:4:1:39:3 | self | nested_scopes.rb:4:1:39:3 | C | +| nested_scopes.rb:38:8:38:8 | a | nested_scopes.rb:5:3:5:3 | a | nested_scopes.rb:4:1:39:3 | C | +| nested_scopes.rb:40:1:40:1 | d | nested_scopes.rb:40:1:40:1 | d | nested_scopes.rb:1:1:42:1 | nested_scopes.rb | +| nested_scopes.rb:41:1:41:1 | d | nested_scopes.rb:40:1:40:1 | d | nested_scopes.rb:1:1:42:1 | nested_scopes.rb | +| parameters.rb:1:14:1:14 | x | parameters.rb:1:14:1:14 | x | parameters.rb:1:9:5:3 | do ... end | +| parameters.rb:1:18:1:18 | y | parameters.rb:1:18:1:18 | y | parameters.rb:1:9:5:3 | do ... end | +| parameters.rb:2:4:2:4 | y | parameters.rb:1:18:1:18 | y | parameters.rb:1:9:5:3 | do ... end | +| parameters.rb:3:4:3:9 | self | parameters.rb:1:1:58:1 | self | parameters.rb:1:1:58:1 | parameters.rb | +| parameters.rb:3:9:3:9 | x | parameters.rb:1:14:1:14 | x | parameters.rb:1:9:5:3 | do ... end | +| parameters.rb:4:4:4:9 | self | parameters.rb:1:1:58:1 | self | parameters.rb:1:1:58:1 | parameters.rb | +| parameters.rb:4:9:4:9 | y | parameters.rb:1:18:1:18 | y | parameters.rb:1:9:5:3 | do ... end | +| parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:7:1:13:3 | order_pizza | +| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:1:13:3 | order_pizza | +| parameters.rb:8:6:8:11 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:1:13:3 | order_pizza | +| parameters.rb:9:5:9:33 | self | parameters.rb:7:1:13:3 | self | parameters.rb:7:1:13:3 | order_pizza | +| parameters.rb:9:25:9:30 | client | parameters.rb:7:17:7:22 | client | parameters.rb:7:1:13:3 | order_pizza | +| parameters.rb:11:5:11:49 | self | parameters.rb:7:1:13:3 | self | parameters.rb:7:1:13:3 | order_pizza | +| parameters.rb:11:14:11:19 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:1:13:3 | order_pizza | +| parameters.rb:11:41:11:46 | client | parameters.rb:7:17:7:22 | client | parameters.rb:7:1:13:3 | order_pizza | +| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map | parameters.rb:15:1:19:3 | print_map | +| parameters.rb:16:3:16:5 | map | parameters.rb:15:17:15:19 | map | parameters.rb:15:1:19:3 | print_map | +| parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key | parameters.rb:16:12:18:5 | do ... end | +| parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value | parameters.rb:16:12:18:5 | do ... end | +| parameters.rb:17:5:17:28 | self | parameters.rb:15:1:19:3 | self | parameters.rb:15:1:19:3 | print_map | +| parameters.rb:17:13:17:15 | key | parameters.rb:16:16:16:18 | key | parameters.rb:16:12:18:5 | do ... end | +| parameters.rb:17:22:17:26 | value | parameters.rb:16:21:16:25 | value | parameters.rb:16:12:18:5 | do ... end | +| parameters.rb:21:17:21:21 | block | parameters.rb:21:17:21:21 | block | parameters.rb:21:1:23:3 | call_block | +| parameters.rb:22:3:22:7 | block | parameters.rb:21:17:21:21 | block | parameters.rb:21:1:23:3 | call_block | +| parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:25:1:28:3 | opt_param | +| parameters.rb:25:33:25:36 | size | parameters.rb:25:33:25:36 | size | parameters.rb:25:1:28:3 | opt_param | +| parameters.rb:25:40:25:43 | name | parameters.rb:25:15:25:18 | name | parameters.rb:25:1:28:3 | opt_param | +| parameters.rb:26:3:26:11 | self | parameters.rb:25:1:28:3 | self | parameters.rb:25:1:28:3 | opt_param | +| parameters.rb:26:8:26:11 | name | parameters.rb:25:15:25:18 | name | parameters.rb:25:1:28:3 | opt_param | +| parameters.rb:27:3:27:11 | self | parameters.rb:25:1:28:3 | self | parameters.rb:25:1:28:3 | opt_param | +| parameters.rb:27:8:27:11 | size | parameters.rb:25:33:25:36 | size | parameters.rb:25:1:28:3 | opt_param | +| parameters.rb:30:15:30:19 | first | parameters.rb:30:15:30:19 | first | parameters.rb:30:1:32:3 | key_param | +| parameters.rb:30:24:30:29 | middle | parameters.rb:30:24:30:29 | middle | parameters.rb:30:1:32:3 | key_param | +| parameters.rb:30:36:30:39 | last | parameters.rb:30:36:30:39 | last | parameters.rb:30:1:32:3 | key_param | +| parameters.rb:31:3:31:35 | self | parameters.rb:30:1:32:3 | self | parameters.rb:30:1:32:3 | key_param | +| parameters.rb:31:11:31:15 | first | parameters.rb:30:15:30:19 | first | parameters.rb:30:1:32:3 | key_param | +| parameters.rb:31:20:31:25 | middle | parameters.rb:30:24:30:29 | middle | parameters.rb:30:1:32:3 | key_param | +| parameters.rb:31:30:31:33 | last | parameters.rb:30:36:30:39 | last | parameters.rb:30:1:32:3 | key_param | +| parameters.rb:34:1:34:1 | b | parameters.rb:34:1:34:1 | b | parameters.rb:1:1:58:1 | parameters.rb | +| parameters.rb:35:11:35:11 | a | parameters.rb:35:11:35:11 | a | parameters.rb:35:1:38:3 | multi | +| parameters.rb:35:16:35:16 | b | parameters.rb:35:16:35:16 | b | parameters.rb:35:1:38:3 | multi | +| parameters.rb:37:3:37:18 | self | parameters.rb:35:1:38:3 | self | parameters.rb:35:1:38:3 | multi | +| parameters.rb:37:11:37:11 | a | parameters.rb:35:11:35:11 | a | parameters.rb:35:1:38:3 | multi | +| parameters.rb:37:16:37:16 | b | parameters.rb:35:16:35:16 | b | parameters.rb:35:1:38:3 | multi | +| parameters.rb:40:12:40:12 | d | parameters.rb:40:12:40:12 | d | parameters.rb:40:1:43:3 | multi2 | +| parameters.rb:40:15:40:15 | e | parameters.rb:40:15:40:15 | e | parameters.rb:40:1:43:3 | multi2 | +| parameters.rb:42:3:42:18 | self | parameters.rb:40:1:43:3 | self | parameters.rb:40:1:43:3 | multi2 | +| parameters.rb:42:11:42:11 | d | parameters.rb:40:12:40:12 | d | parameters.rb:40:1:43:3 | multi2 | +| parameters.rb:42:16:42:16 | e | parameters.rb:40:15:40:15 | e | parameters.rb:40:1:43:3 | multi2 | +| parameters.rb:45:20:45:20 | _ | parameters.rb:45:20:45:20 | _ | parameters.rb:45:1:47:3 | dup_underscore | +| parameters.rb:46:3:46:8 | self | parameters.rb:45:1:47:3 | self | parameters.rb:45:1:47:3 | dup_underscore | +| parameters.rb:46:8:46:8 | _ | parameters.rb:45:20:45:20 | _ | parameters.rb:45:1:47:3 | dup_underscore | +| parameters.rb:49:13:49:13 | a | parameters.rb:49:13:49:13 | a | parameters.rb:49:1:51:3 | tuples | +| parameters.rb:49:15:49:15 | b | parameters.rb:49:15:49:15 | b | parameters.rb:49:1:51:3 | tuples | +| parameters.rb:50:3:50:18 | self | parameters.rb:49:1:51:3 | self | parameters.rb:49:1:51:3 | tuples | +| parameters.rb:50:11:50:11 | a | parameters.rb:49:13:49:13 | a | parameters.rb:49:1:51:3 | tuples | +| parameters.rb:50:16:50:16 | b | parameters.rb:49:15:49:15 | b | parameters.rb:49:1:51:3 | tuples | +| parameters.rb:53:1:53:1 | x | parameters.rb:53:1:53:1 | x | parameters.rb:1:1:58:1 | parameters.rb | +| parameters.rb:54:14:54:14 | y | parameters.rb:54:14:54:14 | y | parameters.rb:54:9:57:3 | do ... end | +| parameters.rb:54:19:54:19 | x | parameters.rb:53:1:53:1 | x | parameters.rb:1:1:58:1 | parameters.rb | +| parameters.rb:55:4:55:9 | self | parameters.rb:1:1:58:1 | self | parameters.rb:1:1:58:1 | parameters.rb | +| parameters.rb:55:9:55:9 | x | parameters.rb:53:1:53:1 | x | parameters.rb:1:1:58:1 | parameters.rb | +| parameters.rb:56:4:56:9 | self | parameters.rb:1:1:58:1 | self | parameters.rb:1:1:58:1 | parameters.rb | +| parameters.rb:56:9:56:9 | y | parameters.rb:54:14:54:14 | y | parameters.rb:54:9:57:3 | do ... end | +| scopes.rb:2:14:2:14 | x | scopes.rb:2:14:2:14 | x | scopes.rb:2:9:6:3 | do ... end | +| scopes.rb:3:4:3:9 | self | scopes.rb:1:1:49:4 | self | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:3:9:3:9 | self | scopes.rb:1:1:49:4 | self | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:4:4:4:4 | a | scopes.rb:4:4:4:4 | a | scopes.rb:2:9:6:3 | do ... end | +| scopes.rb:5:4:5:9 | self | scopes.rb:1:1:49:4 | self | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:5:9:5:9 | a | scopes.rb:4:4:4:4 | a | scopes.rb:2:9:6:3 | do ... end | +| scopes.rb:7:1:7:1 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:8:1:8:6 | self | scopes.rb:1:1:49:4 | self | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:8:6:8:6 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:9:14:9:14 | x | scopes.rb:9:14:9:14 | x | scopes.rb:9:9:18:3 | do ... end | +| scopes.rb:10:4:10:9 | self | scopes.rb:1:1:49:4 | self | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:10:9:10:9 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:11:4:11:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:11:4:11:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:12:4:12:9 | self | scopes.rb:1:1:49:4 | self | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:12:9:12:9 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:13:4:13:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:13:7:13:7 | b | scopes.rb:13:7:13:7 | b | scopes.rb:9:9:18:3 | do ... end | +| scopes.rb:13:11:13:11 | c | scopes.rb:13:11:13:11 | c | scopes.rb:9:9:18:3 | do ... end | +| scopes.rb:13:14:13:14 | d | scopes.rb:13:14:13:14 | d | scopes.rb:9:9:18:3 | do ... end | +| scopes.rb:14:4:14:9 | self | scopes.rb:1:1:49:4 | self | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:14:9:14:9 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:15:4:15:9 | self | scopes.rb:1:1:49:4 | self | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:15:9:15:9 | b | scopes.rb:13:7:13:7 | b | scopes.rb:9:9:18:3 | do ... end | +| scopes.rb:16:4:16:9 | self | scopes.rb:1:1:49:4 | self | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:16:9:16:9 | c | scopes.rb:13:11:13:11 | c | scopes.rb:9:9:18:3 | do ... end | +| scopes.rb:17:4:17:9 | self | scopes.rb:1:1:49:4 | self | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:17:9:17:9 | d | scopes.rb:13:14:13:14 | d | scopes.rb:9:9:18:3 | do ... end | +| scopes.rb:24:1:24:6 | script | scopes.rb:24:1:24:6 | script | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:27:1:27:1 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:28:8:28:8 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:29:3:29:3 | x | scopes.rb:29:3:29:3 | x | scopes.rb:28:1:30:3 | B | +| scopes.rb:31:10:31:10 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:32:3:32:3 | x | scopes.rb:32:3:32:3 | x | scopes.rb:31:1:33:3 | class << ... | +| scopes.rb:34:7:34:7 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:34:14:34:14 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:35:3:35:3 | x | scopes.rb:35:3:35:3 | x | scopes.rb:34:1:36:3 | C | +| scopes.rb:37:5:37:5 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:38:3:38:3 | x | scopes.rb:38:3:38:3 | x | scopes.rb:37:1:39:3 | foo | +| scopes.rb:42:2:42:4 | var | scopes.rb:42:2:42:4 | var | scopes.rb:41:1:49:3 | M | +| scopes.rb:43:2:43:4 | foo | scopes.rb:43:2:43:4 | foo | scopes.rb:41:1:49:3 | M | +| scopes.rb:44:5:44:7 | var | scopes.rb:42:2:42:4 | var | scopes.rb:41:1:49:3 | M | +| scopes.rb:45:5:45:7 | self | scopes.rb:41:1:49:3 | self | scopes.rb:41:1:49:3 | M | +| scopes.rb:46:5:46:8 | var2 | scopes.rb:46:5:46:8 | var2 | scopes.rb:41:1:49:3 | M | +| scopes.rb:47:5:47:8 | var2 | scopes.rb:46:5:46:8 | var2 | scopes.rb:41:1:49:3 | M | +| ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b | ssa.rb:1:1:16:3 | m | +| ssa.rb:2:3:2:3 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m | +| ssa.rb:3:3:3:8 | self | ssa.rb:1:1:16:3 | self | ssa.rb:1:1:16:3 | m | +| ssa.rb:3:8:3:8 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m | +| ssa.rb:4:3:4:12 | self | ssa.rb:1:1:16:3 | self | ssa.rb:1:1:16:3 | m | +| ssa.rb:4:8:4:8 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m | +| ssa.rb:5:6:5:6 | b | ssa.rb:1:7:1:7 | b | ssa.rb:1:1:16:3 | m | +| ssa.rb:6:5:6:5 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m | +| ssa.rb:7:5:7:10 | self | ssa.rb:1:1:16:3 | self | ssa.rb:1:1:16:3 | m | +| ssa.rb:7:10:7:10 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m | +| ssa.rb:8:5:8:14 | self | ssa.rb:1:1:16:3 | self | ssa.rb:1:1:16:3 | m | +| ssa.rb:8:10:8:10 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m | +| ssa.rb:10:5:10:5 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m | +| ssa.rb:11:5:11:10 | self | ssa.rb:1:1:16:3 | self | ssa.rb:1:1:16:3 | m | +| ssa.rb:11:10:11:10 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m | +| ssa.rb:12:5:12:14 | self | ssa.rb:1:1:16:3 | self | ssa.rb:1:1:16:3 | m | +| ssa.rb:12:10:12:10 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m | +| ssa.rb:15:3:15:8 | self | ssa.rb:1:1:16:3 | self | ssa.rb:1:1:16:3 | m | +| ssa.rb:15:8:15:8 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m | +| ssa.rb:18:8:18:8 | x | ssa.rb:18:8:18:8 | x | ssa.rb:18:1:23:3 | m1 | +| ssa.rb:19:9:19:9 | x | ssa.rb:18:8:18:8 | x | ssa.rb:18:1:23:3 | m1 | +| ssa.rb:20:5:20:10 | self | ssa.rb:18:1:23:3 | self | ssa.rb:18:1:23:3 | m1 | +| ssa.rb:20:10:20:10 | x | ssa.rb:18:8:18:8 | x | ssa.rb:18:1:23:3 | m1 | +| ssa.rb:21:5:21:5 | x | ssa.rb:18:8:18:8 | x | ssa.rb:18:1:23:3 | m1 | +| ssa.rb:21:5:21:5 | x | ssa.rb:18:8:18:8 | x | ssa.rb:18:1:23:3 | m1 | +| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:25:1:30:3 | m2 | +| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | m2 | +| ssa.rb:26:15:26:22 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:25:1:30:3 | m2 | +| ssa.rb:27:5:27:13 | self | ssa.rb:25:1:30:3 | self | ssa.rb:25:1:30:3 | m2 | +| ssa.rb:27:10:27:13 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | m2 | +| ssa.rb:29:3:29:11 | self | ssa.rb:25:1:30:3 | self | ssa.rb:25:1:30:3 | m2 | +| ssa.rb:29:8:29:11 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | m2 | +| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x | ssa.rb:33:16:35:5 | do ... end | +| ssa.rb:34:5:34:10 | self | ssa.rb:32:1:36:3 | self | ssa.rb:32:1:36:3 | m3 | +| ssa.rb:34:10:34:10 | x | ssa.rb:33:20:33:20 | x | ssa.rb:33:16:35:5 | do ... end | +| ssa.rb:39:3:39:9 | self | ssa.rb:38:1:42:3 | self | ssa.rb:38:1:42:3 | m4 | +| ssa.rb:39:8:39:9 | self | ssa.rb:38:1:42:3 | self | ssa.rb:38:1:42:3 | m4 | +| ssa.rb:40:3:40:4 | m3 | ssa.rb:40:3:40:4 | m3 | ssa.rb:38:1:42:3 | m4 | +| ssa.rb:41:3:41:13 | self | ssa.rb:38:1:42:3 | self | ssa.rb:38:1:42:3 | m4 | +| ssa.rb:41:8:41:9 | m3 | ssa.rb:40:3:40:4 | m3 | ssa.rb:38:1:42:3 | m4 | +| ssa.rb:44:8:44:8 | b | ssa.rb:44:8:44:8 | b | ssa.rb:44:1:47:3 | m5 | +| ssa.rb:45:3:45:3 | x | ssa.rb:45:3:45:3 | x | ssa.rb:44:1:47:3 | m5 | +| ssa.rb:45:12:45:12 | b | ssa.rb:44:8:44:8 | b | ssa.rb:44:1:47:3 | m5 | +| ssa.rb:46:3:46:8 | self | ssa.rb:44:1:47:3 | self | ssa.rb:44:1:47:3 | m5 | +| ssa.rb:46:8:46:8 | x | ssa.rb:45:3:45:3 | x | ssa.rb:44:1:47:3 | m5 | +| ssa.rb:49:9:49:9 | x | ssa.rb:49:9:49:9 | x | ssa.rb:49:1:51:3 | m6 | +| ssa.rb:49:14:49:14 | y | ssa.rb:49:14:49:14 | y | ssa.rb:49:1:51:3 | m6 | +| ssa.rb:50:3:50:8 | self | ssa.rb:49:1:51:3 | self | ssa.rb:49:1:51:3 | m6 | +| ssa.rb:50:8:50:8 | y | ssa.rb:49:14:49:14 | y | ssa.rb:49:1:51:3 | m6 | +| ssa.rb:53:8:53:10 | foo | ssa.rb:53:8:53:10 | foo | ssa.rb:53:1:56:3 | m7 | +| ssa.rb:54:3:54:3 | x | ssa.rb:54:3:54:3 | x | ssa.rb:53:1:56:3 | m7 | +| ssa.rb:54:7:54:9 | foo | ssa.rb:53:8:53:10 | foo | ssa.rb:53:1:56:3 | m7 | +| ssa.rb:55:3:55:8 | self | ssa.rb:53:1:56:3 | self | ssa.rb:53:1:56:3 | m7 | +| ssa.rb:55:8:55:8 | x | ssa.rb:54:3:54:3 | x | ssa.rb:53:1:56:3 | m7 | +| ssa.rb:59:3:59:3 | x | ssa.rb:59:3:59:3 | x | ssa.rb:58:1:62:3 | m8 | +| ssa.rb:60:3:60:3 | x | ssa.rb:59:3:59:3 | x | ssa.rb:58:1:62:3 | m8 | +| ssa.rb:60:3:60:3 | x | ssa.rb:59:3:59:3 | x | ssa.rb:58:1:62:3 | m8 | +| ssa.rb:61:3:61:8 | self | ssa.rb:58:1:62:3 | self | ssa.rb:58:1:62:3 | m8 | +| ssa.rb:61:8:61:8 | x | ssa.rb:59:3:59:3 | x | ssa.rb:58:1:62:3 | m8 | +| ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a | ssa.rb:64:1:72:3 | m9 | +| ssa.rb:65:3:65:10 | captured | ssa.rb:65:3:65:10 | captured | ssa.rb:64:1:72:3 | m9 | +| ssa.rb:66:3:66:3 | a | ssa.rb:64:8:64:8 | a | ssa.rb:64:1:72:3 | m9 | +| ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a | ssa.rb:66:11:70:5 | do ... end | +| ssa.rb:67:5:67:10 | self | ssa.rb:64:1:72:3 | self | ssa.rb:64:1:72:3 | m9 | +| ssa.rb:67:10:67:10 | a | ssa.rb:66:15:66:15 | a | ssa.rb:66:11:70:5 | do ... end | +| ssa.rb:68:5:68:17 | self | ssa.rb:64:1:72:3 | self | ssa.rb:64:1:72:3 | m9 | +| ssa.rb:68:10:68:17 | captured | ssa.rb:65:3:65:10 | captured | ssa.rb:64:1:72:3 | m9 | +| ssa.rb:69:5:69:12 | captured | ssa.rb:65:3:65:10 | captured | ssa.rb:64:1:72:3 | m9 | +| ssa.rb:69:5:69:12 | captured | ssa.rb:65:3:65:10 | captured | ssa.rb:64:1:72:3 | m9 | +| ssa.rb:71:3:71:15 | self | ssa.rb:64:1:72:3 | self | ssa.rb:64:1:72:3 | m9 | +| ssa.rb:71:8:71:15 | captured | ssa.rb:65:3:65:10 | captured | ssa.rb:64:1:72:3 | m9 | +| ssa.rb:75:3:75:10 | captured | ssa.rb:75:3:75:10 | captured | ssa.rb:74:1:79:3 | m10 | +| ssa.rb:76:3:78:5 | self | ssa.rb:74:1:79:3 | self | ssa.rb:74:1:79:3 | m10 | +| ssa.rb:77:6:77:23 | self | ssa.rb:74:1:79:3 | self | ssa.rb:74:1:79:3 | m10 | +| ssa.rb:77:15:77:22 | captured | ssa.rb:75:3:75:10 | captured | ssa.rb:74:1:79:3 | m10 | +| ssa.rb:82:3:82:10 | captured | ssa.rb:82:3:82:10 | captured | ssa.rb:81:1:88:3 | m11 | +| ssa.rb:83:3:87:5 | self | ssa.rb:81:1:88:3 | self | ssa.rb:81:1:88:3 | m11 | +| ssa.rb:84:6:86:8 | self | ssa.rb:81:1:88:3 | self | ssa.rb:81:1:88:3 | m11 | +| ssa.rb:85:10:85:22 | self | ssa.rb:81:1:88:3 | self | ssa.rb:81:1:88:3 | m11 | +| ssa.rb:85:15:85:22 | captured | ssa.rb:82:3:82:10 | captured | ssa.rb:81:1:88:3 | m11 | +explicitWrite +| class_variables.rb:1:1:1:3 | @@x | class_variables.rb:1:1:1:8 | ... = ... | +| class_variables.rb:19:3:19:5 | @@x | class_variables.rb:19:3:19:10 | ... = ... | +| class_variables.rb:23:3:23:5 | @@x | class_variables.rb:23:3:23:10 | ... = ... | +| instance_variables.rb:1:1:1:4 | @top | instance_variables.rb:1:1:1:8 | ... = ... | +| instance_variables.rb:4:3:4:6 | @foo | instance_variables.rb:4:3:4:11 | ... = ... | +| instance_variables.rb:14:3:14:4 | @x | instance_variables.rb:14:3:14:9 | ... = ... | +| instance_variables.rb:16:5:16:6 | @y | instance_variables.rb:16:5:16:10 | ... = ... | +| instance_variables.rb:21:2:21:3 | @m | instance_variables.rb:21:2:21:8 | ... = ... | +| instance_variables.rb:23:4:23:5 | @n | instance_variables.rb:23:4:23:9 | ... = ... | +| instance_variables.rb:28:3:28:4 | @x | instance_variables.rb:28:3:28:10 | ... = ... | +| instance_variables.rb:32:12:32:13 | @x | instance_variables.rb:32:12:32:19 | ... = ... | +| instance_variables.rb:36:3:36:4 | @x | instance_variables.rb:36:3:36:9 | ... = ... | +| instance_variables.rb:39:6:39:7 | @x | instance_variables.rb:39:6:39:12 | ... = ... | +| nested_scopes.rb:5:3:5:3 | a | nested_scopes.rb:5:3:5:7 | ... = ... | +| nested_scopes.rb:7:5:7:5 | a | nested_scopes.rb:7:5:7:9 | ... = ... | +| nested_scopes.rb:9:7:9:7 | a | nested_scopes.rb:9:7:9:11 | ... = ... | +| nested_scopes.rb:11:9:11:9 | a | nested_scopes.rb:11:9:11:13 | ... = ... | +| nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:13:11:13:15 | ... = ... | +| nested_scopes.rb:17:15:17:15 | a | nested_scopes.rb:17:15:17:19 | ... = ... | +| nested_scopes.rb:31:11:31:11 | a | nested_scopes.rb:31:11:31:16 | ... = ... | +| nested_scopes.rb:40:1:40:1 | d | nested_scopes.rb:40:1:40:18 | ... = ... | +| parameters.rb:2:4:2:4 | y | parameters.rb:2:4:2:8 | ... = ... | +| parameters.rb:34:1:34:1 | b | parameters.rb:34:1:34:5 | ... = ... | +| parameters.rb:35:16:35:16 | b | parameters.rb:35:16:35:20 | ... = ... | +| parameters.rb:40:15:40:15 | e | parameters.rb:40:15:40:19 | ... = ... | +| parameters.rb:53:1:53:1 | x | parameters.rb:53:1:53:6 | ... = ... | +| parameters.rb:54:19:54:19 | x | parameters.rb:54:19:54:23 | ... = ... | +| scopes.rb:4:4:4:4 | a | scopes.rb:4:4:4:8 | ... = ... | +| scopes.rb:7:1:7:1 | a | scopes.rb:7:1:7:5 | ... = ... | +| scopes.rb:11:4:11:4 | a | scopes.rb:11:4:11:9 | ... += ... | +| scopes.rb:11:4:11:4 | a | scopes.rb:11:4:11:9 | ... = ... | +| scopes.rb:13:4:13:4 | a | scopes.rb:13:4:13:4 | ... = ... | +| scopes.rb:13:4:13:4 | a | scopes.rb:13:4:13:32 | ... = ... | +| scopes.rb:13:7:13:7 | b | scopes.rb:13:4:13:32 | ... = ... | +| scopes.rb:13:7:13:7 | b | scopes.rb:13:7:13:7 | ... = ... | +| scopes.rb:13:10:13:15 | __synth__0__1 | scopes.rb:13:10:13:15 | ... = ... | +| scopes.rb:13:11:13:11 | c | scopes.rb:13:4:13:32 | ... = ... | +| scopes.rb:13:11:13:11 | c | scopes.rb:13:11:13:11 | ... = ... | +| scopes.rb:13:14:13:14 | d | scopes.rb:13:4:13:32 | ... = ... | +| scopes.rb:13:14:13:14 | d | scopes.rb:13:14:13:14 | ... = ... | +| scopes.rb:13:19:13:32 | __synth__0 | scopes.rb:13:19:13:32 | ... = ... | +| scopes.rb:21:1:21:7 | $global | scopes.rb:21:1:21:12 | ... = ... | +| scopes.rb:24:1:24:6 | script | scopes.rb:24:1:24:11 | ... = ... | +| scopes.rb:27:1:27:1 | x | scopes.rb:27:1:27:5 | ... = ... | +| scopes.rb:29:3:29:3 | x | scopes.rb:29:3:29:7 | ... = ... | +| scopes.rb:32:3:32:3 | x | scopes.rb:32:3:32:7 | ... = ... | +| scopes.rb:35:3:35:3 | x | scopes.rb:35:3:35:7 | ... = ... | +| scopes.rb:38:3:38:3 | x | scopes.rb:38:3:38:7 | ... = ... | +| scopes.rb:42:2:42:4 | var | scopes.rb:42:2:42:9 | ... = ... | +| scopes.rb:43:2:43:4 | foo | scopes.rb:43:2:43:13 | ... = ... | +| scopes.rb:46:5:46:8 | var2 | scopes.rb:46:5:46:13 | ... = ... | +| ssa.rb:2:3:2:3 | i | ssa.rb:2:3:2:7 | ... = ... | +| ssa.rb:6:5:6:5 | i | ssa.rb:6:5:6:9 | ... = ... | +| ssa.rb:10:5:10:5 | i | ssa.rb:10:5:10:9 | ... = ... | +| ssa.rb:21:5:21:5 | x | ssa.rb:21:5:21:10 | ... -= ... | +| ssa.rb:21:5:21:5 | x | ssa.rb:21:5:21:10 | ... = ... | +| ssa.rb:40:3:40:4 | m3 | ssa.rb:40:3:40:9 | ... = ... | +| ssa.rb:45:3:45:3 | x | ssa.rb:45:3:45:7 | ... = ... | +| ssa.rb:49:14:49:14 | y | ssa.rb:49:14:49:19 | ... = ... | +| ssa.rb:54:3:54:3 | x | ssa.rb:54:3:54:11 | ... = ... | +| ssa.rb:59:3:59:3 | x | ssa.rb:59:3:59:8 | ... = ... | +| ssa.rb:60:3:60:3 | x | ssa.rb:60:3:60:9 | ... += ... | +| ssa.rb:60:3:60:3 | x | ssa.rb:60:3:60:9 | ... = ... | +| ssa.rb:65:3:65:10 | captured | ssa.rb:65:3:65:15 | ... = ... | +| ssa.rb:69:5:69:12 | captured | ssa.rb:69:5:69:17 | ... += ... | +| ssa.rb:69:5:69:12 | captured | ssa.rb:69:5:69:17 | ... = ... | +| ssa.rb:75:3:75:10 | captured | ssa.rb:75:3:75:14 | ... = ... | +| ssa.rb:82:3:82:10 | captured | ssa.rb:82:3:82:14 | ... = ... | +implicitWrite +| nested_scopes.rb:15:23:15:23 | a | +| nested_scopes.rb:16:26:16:26 | x | +| nested_scopes.rb:16:29:16:29 | a | +| nested_scopes.rb:18:26:18:26 | x | +| nested_scopes.rb:22:21:22:21 | a | +| parameters.rb:1:14:1:14 | x | +| parameters.rb:1:18:1:18 | y | +| parameters.rb:7:17:7:22 | client | +| parameters.rb:7:26:7:31 | pizzas | +| parameters.rb:15:17:15:19 | map | +| parameters.rb:16:16:16:18 | key | +| parameters.rb:16:21:16:25 | value | +| parameters.rb:21:17:21:21 | block | +| parameters.rb:25:15:25:18 | name | +| parameters.rb:25:33:25:36 | size | +| parameters.rb:30:15:30:19 | first | +| parameters.rb:30:24:30:29 | middle | +| parameters.rb:30:36:30:39 | last | +| parameters.rb:35:11:35:11 | a | +| parameters.rb:40:12:40:12 | d | +| parameters.rb:45:20:45:20 | _ | +| parameters.rb:49:13:49:13 | a | +| parameters.rb:49:15:49:15 | b | +| parameters.rb:54:14:54:14 | y | +| scopes.rb:2:14:2:14 | x | +| scopes.rb:9:14:9:14 | x | +| ssa.rb:1:7:1:7 | b | +| ssa.rb:18:8:18:8 | x | +| ssa.rb:25:8:25:15 | elements | +| ssa.rb:26:7:26:10 | elem | +| ssa.rb:33:20:33:20 | x | +| ssa.rb:44:8:44:8 | b | +| ssa.rb:49:9:49:9 | x | +| ssa.rb:53:8:53:10 | foo | +| ssa.rb:64:8:64:8 | a | +| ssa.rb:66:15:66:15 | a | +readAccess +| class_variables.rb:3:1:3:5 | self | +| class_variables.rb:3:3:3:5 | @@x | +| class_variables.rb:6:2:6:6 | self | +| class_variables.rb:6:4:6:6 | @@x | +| class_variables.rb:11:5:11:9 | self | +| class_variables.rb:11:7:11:9 | @@x | +| class_variables.rb:13:7:13:10 | self | +| class_variables.rb:14:4:14:8 | self | +| class_variables.rb:14:6:14:8 | @@x | +| class_variables.rb:27:3:27:11 | self | +| class_variables.rb:28:3:28:7 | self | +| class_variables.rb:28:5:28:7 | @@x | +| instance_variables.rb:8:3:8:11 | self | +| instance_variables.rb:8:8:8:11 | @foo | +| instance_variables.rb:11:1:11:9 | self | +| instance_variables.rb:11:6:11:9 | @top | +| instance_variables.rb:27:1:29:1 | self | +| instance_variables.rb:41:4:41:4 | self | +| instance_variables.rb:42:4:42:7 | self | +| instance_variables.rb:42:6:42:7 | @x | +| nested_scopes.rb:2:3:2:17 | self | +| nested_scopes.rb:14:11:14:16 | self | +| nested_scopes.rb:14:16:14:16 | a | +| nested_scopes.rb:15:11:15:11 | a | +| nested_scopes.rb:16:13:16:13 | a | +| nested_scopes.rb:18:15:18:15 | a | +| nested_scopes.rb:18:29:18:34 | self | +| nested_scopes.rb:18:34:18:34 | a | +| nested_scopes.rb:23:11:23:16 | self | +| nested_scopes.rb:23:16:23:16 | a | +| nested_scopes.rb:25:9:25:14 | self | +| nested_scopes.rb:25:14:25:14 | a | +| nested_scopes.rb:27:11:27:14 | self | +| nested_scopes.rb:28:11:28:16 | self | +| nested_scopes.rb:28:16:28:16 | self | +| nested_scopes.rb:30:16:30:19 | self | +| nested_scopes.rb:32:11:32:16 | self | +| nested_scopes.rb:32:16:32:16 | a | +| nested_scopes.rb:34:7:34:12 | self | +| nested_scopes.rb:34:12:34:12 | a | +| nested_scopes.rb:36:5:36:10 | self | +| nested_scopes.rb:36:10:36:10 | a | +| nested_scopes.rb:38:3:38:8 | self | +| nested_scopes.rb:38:8:38:8 | a | +| nested_scopes.rb:41:1:41:1 | d | +| parameters.rb:3:4:3:9 | self | +| parameters.rb:3:9:3:9 | x | +| parameters.rb:4:4:4:9 | self | +| parameters.rb:4:9:4:9 | y | +| parameters.rb:8:6:8:11 | pizzas | +| parameters.rb:9:5:9:33 | self | +| parameters.rb:9:25:9:30 | client | +| parameters.rb:11:5:11:49 | self | +| parameters.rb:11:14:11:19 | pizzas | +| parameters.rb:11:41:11:46 | client | +| parameters.rb:16:3:16:5 | map | +| parameters.rb:17:5:17:28 | self | +| parameters.rb:17:13:17:15 | key | +| parameters.rb:17:22:17:26 | value | +| parameters.rb:22:3:22:7 | block | +| parameters.rb:25:40:25:43 | name | +| parameters.rb:26:3:26:11 | self | +| parameters.rb:26:8:26:11 | name | +| parameters.rb:27:3:27:11 | self | +| parameters.rb:27:8:27:11 | size | +| parameters.rb:31:3:31:35 | self | +| parameters.rb:31:11:31:15 | first | +| parameters.rb:31:20:31:25 | middle | +| parameters.rb:31:30:31:33 | last | +| parameters.rb:37:3:37:18 | self | +| parameters.rb:37:11:37:11 | a | +| parameters.rb:37:16:37:16 | b | +| parameters.rb:42:3:42:18 | self | +| parameters.rb:42:11:42:11 | d | +| parameters.rb:42:16:42:16 | e | +| parameters.rb:46:3:46:8 | self | +| parameters.rb:46:8:46:8 | _ | +| parameters.rb:50:3:50:18 | self | +| parameters.rb:50:11:50:11 | a | +| parameters.rb:50:16:50:16 | b | +| parameters.rb:55:4:55:9 | self | +| parameters.rb:55:9:55:9 | x | +| parameters.rb:56:4:56:9 | self | +| parameters.rb:56:9:56:9 | y | +| scopes.rb:3:4:3:9 | self | +| scopes.rb:3:9:3:9 | self | +| scopes.rb:5:4:5:9 | self | +| scopes.rb:5:9:5:9 | a | +| scopes.rb:8:1:8:6 | self | +| scopes.rb:8:6:8:6 | a | +| scopes.rb:10:4:10:9 | self | +| scopes.rb:10:9:10:9 | a | +| scopes.rb:11:4:11:4 | a | +| scopes.rb:12:4:12:9 | self | +| scopes.rb:12:9:12:9 | a | +| scopes.rb:13:4:13:4 | __synth__0 | +| scopes.rb:13:7:13:7 | __synth__0 | +| scopes.rb:13:10:13:15 | __synth__0 | +| scopes.rb:13:11:13:11 | __synth__0__1 | +| scopes.rb:13:14:13:14 | __synth__0__1 | +| scopes.rb:14:4:14:9 | self | +| scopes.rb:14:9:14:9 | a | +| scopes.rb:15:4:15:9 | self | +| scopes.rb:15:9:15:9 | b | +| scopes.rb:16:4:16:9 | self | +| scopes.rb:16:9:16:9 | c | +| scopes.rb:17:4:17:9 | self | +| scopes.rb:17:9:17:9 | d | +| scopes.rb:24:10:24:11 | $0 | +| scopes.rb:28:8:28:8 | x | +| scopes.rb:31:10:31:10 | x | +| scopes.rb:34:7:34:7 | x | +| scopes.rb:34:14:34:14 | x | +| scopes.rb:37:5:37:5 | x | +| scopes.rb:44:5:44:7 | var | +| scopes.rb:45:5:45:7 | self | +| scopes.rb:47:5:47:8 | var2 | +| ssa.rb:3:3:3:8 | self | +| ssa.rb:3:8:3:8 | i | +| ssa.rb:4:3:4:12 | self | +| ssa.rb:4:8:4:8 | i | +| ssa.rb:5:6:5:6 | b | +| ssa.rb:7:5:7:10 | self | +| ssa.rb:7:10:7:10 | i | +| ssa.rb:8:5:8:14 | self | +| ssa.rb:8:10:8:10 | i | +| ssa.rb:11:5:11:10 | self | +| ssa.rb:11:10:11:10 | i | +| ssa.rb:12:5:12:14 | self | +| ssa.rb:12:10:12:10 | i | +| ssa.rb:15:3:15:8 | self | +| ssa.rb:15:8:15:8 | i | +| ssa.rb:19:9:19:9 | x | +| ssa.rb:20:5:20:10 | self | +| ssa.rb:20:10:20:10 | x | +| ssa.rb:21:5:21:5 | x | +| ssa.rb:26:15:26:22 | elements | +| ssa.rb:27:5:27:13 | self | +| ssa.rb:27:10:27:13 | elem | +| ssa.rb:29:3:29:11 | self | +| ssa.rb:29:8:29:11 | elem | +| ssa.rb:34:5:34:10 | self | +| ssa.rb:34:10:34:10 | x | +| ssa.rb:39:3:39:9 | self | +| ssa.rb:39:8:39:9 | self | +| ssa.rb:41:3:41:13 | self | +| ssa.rb:41:8:41:9 | m3 | +| ssa.rb:45:12:45:12 | b | +| ssa.rb:46:3:46:8 | self | +| ssa.rb:46:8:46:8 | x | +| ssa.rb:50:3:50:8 | self | +| ssa.rb:50:8:50:8 | y | +| ssa.rb:54:7:54:9 | foo | +| ssa.rb:55:3:55:8 | self | +| ssa.rb:55:8:55:8 | x | +| ssa.rb:60:3:60:3 | x | +| ssa.rb:61:3:61:8 | self | +| ssa.rb:61:8:61:8 | x | +| ssa.rb:66:3:66:3 | a | +| ssa.rb:67:5:67:10 | self | +| ssa.rb:67:10:67:10 | a | +| ssa.rb:68:5:68:17 | self | +| ssa.rb:68:10:68:17 | captured | +| ssa.rb:69:5:69:12 | captured | +| ssa.rb:71:3:71:15 | self | +| ssa.rb:71:8:71:15 | captured | +| ssa.rb:76:3:78:5 | self | +| ssa.rb:77:6:77:23 | self | +| ssa.rb:77:15:77:22 | captured | +| ssa.rb:83:3:87:5 | self | +| ssa.rb:84:6:86:8 | self | +| ssa.rb:85:10:85:22 | self | +| ssa.rb:85:15:85:22 | captured | diff --git a/ruby/ql/test/library-tests/variables/varaccess.ql b/ruby/ql/test/library-tests/variables/varaccess.ql new file mode 100644 index 00000000000..18cc26c1d24 --- /dev/null +++ b/ruby/ql/test/library-tests/variables/varaccess.ql @@ -0,0 +1,15 @@ +import codeql.ruby.AST +import codeql.ruby.ast.Variable + +query predicate variableAccess(VariableAccess access, Variable variable, Scope scope) { + variable = access.getVariable() and + scope = variable.getDeclaringScope() +} + +query predicate explicitWrite(VariableWriteAccess write, AstNode assignment) { + write.isExplicitWrite(assignment) +} + +query predicate implicitWrite(VariableWriteAccess write) { write.isImplicitWrite() } + +query predicate readAccess(VariableReadAccess read) { any() } diff --git a/ruby/ql/test/library-tests/variables/variable.expected b/ruby/ql/test/library-tests/variables/variable.expected new file mode 100644 index 00000000000..4076efdeb88 --- /dev/null +++ b/ruby/ql/test/library-tests/variables/variable.expected @@ -0,0 +1,149 @@ +| class_variables.rb:1:1:1:3 | @@x | +| class_variables.rb:1:1:29:4 | self | +| class_variables.rb:5:1:7:3 | self | +| class_variables.rb:9:1:16:3 | self | +| class_variables.rb:10:3:12:5 | self | +| class_variables.rb:11:7:11:9 | @@x | +| class_variables.rb:13:3:15:5 | self | +| class_variables.rb:18:1:20:3 | self | +| class_variables.rb:19:3:19:5 | @@x | +| class_variables.rb:22:1:24:3 | self | +| class_variables.rb:23:3:23:5 | @@x | +| class_variables.rb:26:1:29:3 | self | +| class_variables.rb:28:5:28:7 | @@x | +| file://:0:0:0:0 | $0 | +| file://:0:0:0:0 | $global | +| instance_variables.rb:1:1:1:4 | @top | +| instance_variables.rb:1:1:44:4 | self | +| instance_variables.rb:3:1:5:3 | self | +| instance_variables.rb:4:3:4:6 | @foo | +| instance_variables.rb:7:1:9:3 | self | +| instance_variables.rb:13:1:18:3 | self | +| instance_variables.rb:14:3:14:4 | @x | +| instance_variables.rb:15:3:17:5 | self | +| instance_variables.rb:16:5:16:6 | @y | +| instance_variables.rb:20:1:25:3 | self | +| instance_variables.rb:21:2:21:3 | @m | +| instance_variables.rb:22:2:24:4 | self | +| instance_variables.rb:23:4:23:5 | @n | +| instance_variables.rb:28:3:28:4 | @x | +| instance_variables.rb:31:1:33:3 | self | +| instance_variables.rb:32:12:32:13 | @x | +| instance_variables.rb:35:1:44:4 | self | +| instance_variables.rb:36:3:36:4 | @x | +| instance_variables.rb:37:3:43:5 | self | +| instance_variables.rb:38:4:40:6 | self | +| instance_variables.rb:39:6:39:7 | @x | +| nested_scopes.rb:1:1:3:3 | self | +| nested_scopes.rb:1:1:42:1 | self | +| nested_scopes.rb:4:1:39:3 | self | +| nested_scopes.rb:5:3:5:3 | a | +| nested_scopes.rb:6:3:37:5 | self | +| nested_scopes.rb:7:5:7:5 | a | +| nested_scopes.rb:8:5:35:7 | self | +| nested_scopes.rb:9:7:9:7 | a | +| nested_scopes.rb:10:7:26:9 | self | +| nested_scopes.rb:11:9:11:9 | a | +| nested_scopes.rb:12:9:21:11 | self | +| nested_scopes.rb:13:11:13:11 | a | +| nested_scopes.rb:15:23:15:23 | a | +| nested_scopes.rb:16:26:16:26 | x | +| nested_scopes.rb:16:29:16:29 | a | +| nested_scopes.rb:18:26:18:26 | x | +| nested_scopes.rb:22:9:24:11 | self | +| nested_scopes.rb:22:21:22:21 | a | +| nested_scopes.rb:27:7:29:9 | self | +| nested_scopes.rb:30:7:33:9 | self | +| nested_scopes.rb:31:11:31:11 | a | +| nested_scopes.rb:40:1:40:1 | d | +| parameters.rb:1:1:58:1 | self | +| parameters.rb:1:14:1:14 | x | +| parameters.rb:1:18:1:18 | y | +| parameters.rb:7:1:13:3 | self | +| parameters.rb:7:17:7:22 | client | +| parameters.rb:7:26:7:31 | pizzas | +| parameters.rb:15:1:19:3 | self | +| parameters.rb:15:17:15:19 | map | +| parameters.rb:16:16:16:18 | key | +| parameters.rb:16:21:16:25 | value | +| parameters.rb:21:1:23:3 | self | +| parameters.rb:21:17:21:21 | block | +| parameters.rb:25:1:28:3 | self | +| parameters.rb:25:15:25:18 | name | +| parameters.rb:25:33:25:36 | size | +| parameters.rb:30:1:32:3 | self | +| parameters.rb:30:15:30:19 | first | +| parameters.rb:30:24:30:29 | middle | +| parameters.rb:30:36:30:39 | last | +| parameters.rb:34:1:34:1 | b | +| parameters.rb:35:1:38:3 | self | +| parameters.rb:35:11:35:11 | a | +| parameters.rb:35:16:35:16 | b | +| parameters.rb:40:1:43:3 | self | +| parameters.rb:40:12:40:12 | d | +| parameters.rb:40:15:40:15 | e | +| parameters.rb:45:1:47:3 | self | +| parameters.rb:45:20:45:20 | _ | +| parameters.rb:49:1:51:3 | self | +| parameters.rb:49:13:49:13 | a | +| parameters.rb:49:15:49:15 | b | +| parameters.rb:53:1:53:1 | x | +| parameters.rb:54:14:54:14 | y | +| scopes.rb:1:1:1:15 | self | +| scopes.rb:1:1:49:4 | self | +| scopes.rb:2:14:2:14 | x | +| scopes.rb:4:4:4:4 | a | +| scopes.rb:7:1:7:1 | a | +| scopes.rb:9:14:9:14 | x | +| scopes.rb:13:4:13:32 | __synth__0 | +| scopes.rb:13:7:13:7 | b | +| scopes.rb:13:10:13:15 | __synth__0__1 | +| scopes.rb:13:11:13:11 | c | +| scopes.rb:13:14:13:14 | d | +| scopes.rb:24:1:24:6 | script | +| scopes.rb:26:1:26:12 | self | +| scopes.rb:27:1:27:1 | x | +| scopes.rb:28:1:30:3 | self | +| scopes.rb:29:3:29:3 | x | +| scopes.rb:31:1:33:3 | self | +| scopes.rb:32:3:32:3 | x | +| scopes.rb:34:1:36:3 | self | +| scopes.rb:35:3:35:3 | x | +| scopes.rb:37:1:39:3 | self | +| scopes.rb:38:3:38:3 | x | +| scopes.rb:41:1:49:3 | self | +| scopes.rb:42:2:42:4 | var | +| scopes.rb:43:2:43:4 | foo | +| scopes.rb:46:5:46:8 | var2 | +| ssa.rb:1:1:16:3 | self | +| ssa.rb:1:1:88:3 | self | +| ssa.rb:1:7:1:7 | b | +| ssa.rb:2:3:2:3 | i | +| ssa.rb:18:1:23:3 | self | +| ssa.rb:18:8:18:8 | x | +| ssa.rb:25:1:30:3 | self | +| ssa.rb:25:8:25:15 | elements | +| ssa.rb:26:7:26:10 | elem | +| ssa.rb:32:1:36:3 | self | +| ssa.rb:33:20:33:20 | x | +| ssa.rb:38:1:42:3 | self | +| ssa.rb:40:3:40:4 | m3 | +| ssa.rb:44:1:47:3 | self | +| ssa.rb:44:8:44:8 | b | +| ssa.rb:45:3:45:3 | x | +| ssa.rb:49:1:51:3 | self | +| ssa.rb:49:9:49:9 | x | +| ssa.rb:49:14:49:14 | y | +| ssa.rb:53:1:56:3 | self | +| ssa.rb:53:8:53:10 | foo | +| ssa.rb:54:3:54:3 | x | +| ssa.rb:58:1:62:3 | self | +| ssa.rb:59:3:59:3 | x | +| ssa.rb:64:1:72:3 | self | +| ssa.rb:64:8:64:8 | a | +| ssa.rb:65:3:65:10 | captured | +| ssa.rb:66:15:66:15 | a | +| ssa.rb:74:1:79:3 | self | +| ssa.rb:75:3:75:10 | captured | +| ssa.rb:81:1:88:3 | self | +| ssa.rb:82:3:82:10 | captured | diff --git a/ruby/ql/test/library-tests/variables/variable.ql b/ruby/ql/test/library-tests/variables/variable.ql new file mode 100644 index 00000000000..7a87b119bec --- /dev/null +++ b/ruby/ql/test/library-tests/variables/variable.ql @@ -0,0 +1,3 @@ +import codeql.ruby.ast.Variable + +query predicate variable(Variable v) { any() } diff --git a/ruby/ql/test/library-tests/variables/varscopes.expected b/ruby/ql/test/library-tests/variables/varscopes.expected new file mode 100644 index 00000000000..090243e57a8 --- /dev/null +++ b/ruby/ql/test/library-tests/variables/varscopes.expected @@ -0,0 +1,75 @@ +| class_variables.rb:1:1:29:4 | class_variables.rb | +| class_variables.rb:5:1:7:3 | print | +| class_variables.rb:9:1:16:3 | X | +| class_variables.rb:10:3:12:5 | b | +| class_variables.rb:13:3:15:5 | s | +| class_variables.rb:18:1:20:3 | Y | +| class_variables.rb:22:1:24:3 | M | +| class_variables.rb:26:1:29:3 | N | +| instance_variables.rb:1:1:44:4 | instance_variables.rb | +| instance_variables.rb:3:1:5:3 | foo | +| instance_variables.rb:7:1:9:3 | print_foo | +| instance_variables.rb:13:1:18:3 | X | +| instance_variables.rb:15:3:17:5 | m | +| instance_variables.rb:20:1:25:3 | M | +| instance_variables.rb:22:2:24:4 | n | +| instance_variables.rb:27:6:29:1 | { ... } | +| instance_variables.rb:31:1:33:3 | bar | +| instance_variables.rb:32:10:32:21 | { ... } | +| instance_variables.rb:35:1:44:4 | C | +| instance_variables.rb:37:3:43:5 | x | +| instance_variables.rb:38:4:40:6 | y | +| nested_scopes.rb:1:1:3:3 | a | +| nested_scopes.rb:1:1:42:1 | nested_scopes.rb | +| nested_scopes.rb:4:1:39:3 | C | +| nested_scopes.rb:6:3:37:5 | M | +| nested_scopes.rb:8:5:35:7 | N | +| nested_scopes.rb:10:7:26:9 | D | +| nested_scopes.rb:12:9:21:11 | show_a | +| nested_scopes.rb:15:19:20:13 | do ... end | +| nested_scopes.rb:16:21:19:15 | do ... end | +| nested_scopes.rb:18:23:18:36 | { ... } | +| nested_scopes.rb:22:9:24:11 | show_a2 | +| nested_scopes.rb:27:7:29:9 | show | +| nested_scopes.rb:30:7:33:9 | class << ... | +| parameters.rb:1:1:58:1 | parameters.rb | +| parameters.rb:1:9:5:3 | do ... end | +| parameters.rb:7:1:13:3 | order_pizza | +| parameters.rb:15:1:19:3 | print_map | +| parameters.rb:16:12:18:5 | do ... end | +| parameters.rb:21:1:23:3 | call_block | +| parameters.rb:25:1:28:3 | opt_param | +| parameters.rb:30:1:32:3 | key_param | +| parameters.rb:35:1:38:3 | multi | +| parameters.rb:40:1:43:3 | multi2 | +| parameters.rb:45:1:47:3 | dup_underscore | +| parameters.rb:49:1:51:3 | tuples | +| parameters.rb:54:9:57:3 | do ... end | +| scopes.rb:1:1:1:15 | a | +| scopes.rb:1:1:49:4 | scopes.rb | +| scopes.rb:2:9:6:3 | do ... end | +| scopes.rb:9:9:18:3 | do ... end | +| scopes.rb:26:1:26:12 | A | +| scopes.rb:28:1:30:3 | B | +| scopes.rb:31:1:33:3 | class << ... | +| scopes.rb:34:1:36:3 | C | +| scopes.rb:37:1:39:3 | foo | +| scopes.rb:41:1:49:3 | M | +| ssa.rb:1:1:16:3 | m | +| ssa.rb:1:1:88:3 | ssa.rb | +| ssa.rb:18:1:23:3 | m1 | +| ssa.rb:25:1:30:3 | m2 | +| ssa.rb:32:1:36:3 | m3 | +| ssa.rb:33:16:35:5 | do ... end | +| ssa.rb:38:1:42:3 | m4 | +| ssa.rb:44:1:47:3 | m5 | +| ssa.rb:49:1:51:3 | m6 | +| ssa.rb:53:1:56:3 | m7 | +| ssa.rb:58:1:62:3 | m8 | +| ssa.rb:64:1:72:3 | m9 | +| ssa.rb:66:11:70:5 | do ... end | +| ssa.rb:74:1:79:3 | m10 | +| ssa.rb:76:7:78:5 | do ... end | +| ssa.rb:81:1:88:3 | m11 | +| ssa.rb:83:7:87:5 | do ... end | +| ssa.rb:84:10:86:8 | do ... end | diff --git a/ruby/ql/test/library-tests/variables/varscopes.ql b/ruby/ql/test/library-tests/variables/varscopes.ql new file mode 100644 index 00000000000..2f3aa1b2e6c --- /dev/null +++ b/ruby/ql/test/library-tests/variables/varscopes.ql @@ -0,0 +1,3 @@ +import codeql.ruby.ast.Scope + +select any(Scope x) diff --git a/ruby/ql/test/qlpack.lock.yml b/ruby/ql/test/qlpack.lock.yml new file mode 100644 index 00000000000..0bef0f691a9 --- /dev/null +++ b/ruby/ql/test/qlpack.lock.yml @@ -0,0 +1,6 @@ +--- +dependencies: + codeql/suite-helpers: + version: 0.0.2 +compiled: false +lockVersion: 1.0.0 diff --git a/ruby/ql/test/qlpack.yml b/ruby/ql/test/qlpack.yml new file mode 100644 index 00000000000..91111a5ffa3 --- /dev/null +++ b/ruby/ql/test/qlpack.yml @@ -0,0 +1,8 @@ +name: codeql/ruby-tests +version: 0.0.2 +dependencies: + codeql/ruby-queries: ^0.0.2 + codeql/ruby-examples: ^0.0.2 + codeql/ruby-all: ^0.0.2 +extractor: ruby +tests: . diff --git a/ruby/ql/test/query-tests/AlertSuppression/.gitattributes b/ruby/ql/test/query-tests/AlertSuppression/.gitattributes new file mode 100644 index 00000000000..7ed66a396cf --- /dev/null +++ b/ruby/ql/test/query-tests/AlertSuppression/.gitattributes @@ -0,0 +1 @@ +TestWindows.java eol=crlf diff --git a/ruby/ql/test/query-tests/AlertSuppression/AlertSuppression.expected b/ruby/ql/test/query-tests/AlertSuppression/AlertSuppression.expected new file mode 100644 index 00000000000..840b13f61bc --- /dev/null +++ b/ruby/ql/test/query-tests/AlertSuppression/AlertSuppression.expected @@ -0,0 +1,48 @@ +| Test.rb:1:16:1:21 | # lgtm | lgtm | lgtm | Test.rb:1:1:1:21 | suppression range | +| Test.rb:2:1:2:32 | # lgtm[rb/confusing-method-name] | lgtm[rb/confusing-method-name] | lgtm[rb/confusing-method-name] | Test.rb:2:1:2:32 | suppression range | +| Test.rb:3:1:3:65 | # lgtm[rb/confusing-method-name, rb/non-short-circuit-evaluation] | lgtm[rb/confusing-method-name, rb/non-short-circuit-evaluation] | lgtm[rb/confusing-method-name, rb/non-short-circuit-evaluation] | Test.rb:3:1:3:65 | suppression range | +| Test.rb:4:1:4:23 | # lgtm[@tag:exceptions] | lgtm[@tag:exceptions] | lgtm[@tag:exceptions] | Test.rb:4:1:4:23 | suppression range | +| Test.rb:5:1:5:48 | # lgtm[@tag:exceptions,rb/confusing-method-name] | lgtm[@tag:exceptions,rb/confusing-method-name] | lgtm[@tag:exceptions,rb/confusing-method-name] | Test.rb:5:1:5:48 | suppression range | +| Test.rb:6:1:6:27 | # lgtm[@expires:2017-06-11] | lgtm[@expires:2017-06-11] | lgtm[@expires:2017-06-11] | Test.rb:6:1:6:27 | suppression range | +| Test.rb:7:1:7:78 | # lgtm[rb/confusing-method-name] does not seem confusing despite alert by lgtm | lgtm[rb/confusing-method-name] does not seem confusing despite alert by lgtm | lgtm[rb/confusing-method-name] | Test.rb:7:1:7:78 | suppression range | +| Test.rb:8:1:8:17 | # lgtm: blah blah | lgtm: blah blah | lgtm | Test.rb:8:1:8:17 | suppression range | +| Test.rb:9:1:9:31 | # lgtm blah blah #falsepositive | lgtm blah blah #falsepositive | lgtm | Test.rb:9:1:9:31 | suppression range | +| Test.rb:10:1:10:33 | #lgtm [rb/confusing-method-name] | lgtm [rb/confusing-method-name] | lgtm [rb/confusing-method-name] | Test.rb:10:1:10:33 | suppression range | +| Test.rb:11:1:11:8 | # lgtm[] | lgtm[] | lgtm[] | Test.rb:11:1:11:8 | suppression range | +| Test.rb:13:1:13:5 | #lgtm | lgtm | lgtm | Test.rb:13:1:13:5 | suppression range | +| Test.rb:14:1:14:6 | #\tlgtm | \tlgtm | lgtm | Test.rb:14:1:14:6 | suppression range | +| Test.rb:15:1:15:33 | # lgtm\t[rb/confusing-method-name] | lgtm\t[rb/confusing-method-name] | lgtm\t[rb/confusing-method-name] | Test.rb:15:1:15:33 | suppression range | +| Test.rb:18:1:18:11 | # foo; lgtm | foo; lgtm | lgtm | Test.rb:18:1:18:11 | suppression range | +| Test.rb:19:1:19:37 | # foo; lgtm[rb/confusing-method-name] | foo; lgtm[rb/confusing-method-name] | lgtm[rb/confusing-method-name] | Test.rb:19:1:19:37 | suppression range | +| Test.rb:21:1:21:36 | # foo lgtm[rb/confusing-method-name] | foo lgtm[rb/confusing-method-name] | lgtm[rb/confusing-method-name] | Test.rb:21:1:21:36 | suppression range | +| Test.rb:23:1:23:40 | # foo lgtm[rb/confusing-method-name] bar | foo lgtm[rb/confusing-method-name] bar | lgtm[rb/confusing-method-name] | Test.rb:23:1:23:40 | suppression range | +| Test.rb:24:1:24:7 | # LGTM! | LGTM! | LGTM | Test.rb:24:1:24:7 | suppression range | +| Test.rb:25:1:25:32 | # LGTM[rb/confusing-method-name] | LGTM[rb/confusing-method-name] | LGTM[rb/confusing-method-name] | Test.rb:25:1:25:32 | suppression range | +| Test.rb:26:1:26:73 | #lgtm[rb/confusing-method-name] and lgtm[rb/non-short-circuit-evaluation] | lgtm[rb/confusing-method-name] and lgtm[rb/non-short-circuit-evaluation] | lgtm[rb/confusing-method-name] | Test.rb:26:1:26:73 | suppression range | +| Test.rb:26:1:26:73 | #lgtm[rb/confusing-method-name] and lgtm[rb/non-short-circuit-evaluation] | lgtm[rb/confusing-method-name] and lgtm[rb/non-short-circuit-evaluation] | lgtm[rb/non-short-circuit-evaluation] | Test.rb:26:1:26:73 | suppression range | +| Test.rb:27:1:27:37 | #lgtm[rb/confusing-method-name]; lgtm | lgtm[rb/confusing-method-name]; lgtm | lgtm | Test.rb:27:1:27:37 | suppression range | +| Test.rb:27:1:27:37 | #lgtm[rb/confusing-method-name]; lgtm | lgtm[rb/confusing-method-name]; lgtm | lgtm[rb/confusing-method-name] | Test.rb:27:1:27:37 | suppression range | +| TestWindows.rb:1:23:1:29 | # lgtm\r | lgtm\r | lgtm | TestWindows.rb:1:1:1:29 | suppression range | +| TestWindows.rb:2:1:2:33 | # lgtm[rb/confusing-method-name]\r | lgtm[rb/confusing-method-name]\r | lgtm[rb/confusing-method-name] | TestWindows.rb:2:1:2:33 | suppression range | +| TestWindows.rb:3:1:3:66 | # lgtm[rb/confusing-method-name, rb/non-short-circuit-evaluation]\r | lgtm[rb/confusing-method-name, rb/non-short-circuit-evaluation]\r | lgtm[rb/confusing-method-name, rb/non-short-circuit-evaluation] | TestWindows.rb:3:1:3:66 | suppression range | +| TestWindows.rb:4:1:4:24 | # lgtm[@tag:exceptions]\r | lgtm[@tag:exceptions]\r | lgtm[@tag:exceptions] | TestWindows.rb:4:1:4:24 | suppression range | +| TestWindows.rb:5:1:5:49 | # lgtm[@tag:exceptions,rb/confusing-method-name]\r | lgtm[@tag:exceptions,rb/confusing-method-name]\r | lgtm[@tag:exceptions,rb/confusing-method-name] | TestWindows.rb:5:1:5:49 | suppression range | +| TestWindows.rb:6:1:6:28 | # lgtm[@expires:2017-06-11]\r | lgtm[@expires:2017-06-11]\r | lgtm[@expires:2017-06-11] | TestWindows.rb:6:1:6:28 | suppression range | +| TestWindows.rb:7:1:7:79 | # lgtm[rb/confusing-method-name] does not seem confusing despite alert by lgtm\r | lgtm[rb/confusing-method-name] does not seem confusing despite alert by lgtm\r | lgtm[rb/confusing-method-name] | TestWindows.rb:7:1:7:79 | suppression range | +| TestWindows.rb:8:1:8:18 | # lgtm: blah blah\r | lgtm: blah blah\r | lgtm | TestWindows.rb:8:1:8:18 | suppression range | +| TestWindows.rb:9:1:9:32 | # lgtm blah blah #falsepositive\r | lgtm blah blah #falsepositive\r | lgtm | TestWindows.rb:9:1:9:32 | suppression range | +| TestWindows.rb:10:1:10:34 | #lgtm [rb/confusing-method-name]\r | lgtm [rb/confusing-method-name]\r | lgtm [rb/confusing-method-name] | TestWindows.rb:10:1:10:34 | suppression range | +| TestWindows.rb:11:1:11:9 | # lgtm[]\r | lgtm[]\r | lgtm[] | TestWindows.rb:11:1:11:9 | suppression range | +| TestWindows.rb:13:1:13:6 | #lgtm\r | lgtm\r | lgtm | TestWindows.rb:13:1:13:6 | suppression range | +| TestWindows.rb:14:1:14:7 | #\tlgtm\r | \tlgtm\r | lgtm | TestWindows.rb:14:1:14:7 | suppression range | +| TestWindows.rb:15:1:15:34 | # lgtm\t[rb/confusing-method-name]\r | lgtm\t[rb/confusing-method-name]\r | lgtm\t[rb/confusing-method-name] | TestWindows.rb:15:1:15:34 | suppression range | +| TestWindows.rb:18:1:18:12 | # foo; lgtm\r | foo; lgtm\r | lgtm | TestWindows.rb:18:1:18:12 | suppression range | +| TestWindows.rb:19:1:19:38 | # foo; lgtm[rb/confusing-method-name]\r | foo; lgtm[rb/confusing-method-name]\r | lgtm[rb/confusing-method-name] | TestWindows.rb:19:1:19:38 | suppression range | +| TestWindows.rb:21:1:21:37 | # foo lgtm[rb/confusing-method-name]\r | foo lgtm[rb/confusing-method-name]\r | lgtm[rb/confusing-method-name] | TestWindows.rb:21:1:21:37 | suppression range | +| TestWindows.rb:23:1:23:41 | # foo lgtm[rb/confusing-method-name] bar\r | foo lgtm[rb/confusing-method-name] bar\r | lgtm[rb/confusing-method-name] | TestWindows.rb:23:1:23:41 | suppression range | +| TestWindows.rb:24:1:24:8 | # LGTM!\r | LGTM!\r | LGTM | TestWindows.rb:24:1:24:8 | suppression range | +| TestWindows.rb:25:1:25:33 | # LGTM[rb/confusing-method-name]\r | LGTM[rb/confusing-method-name]\r | LGTM[rb/confusing-method-name] | TestWindows.rb:25:1:25:33 | suppression range | +| TestWindows.rb:26:1:26:74 | #lgtm[rb/confusing-method-name] and lgtm[rb/non-short-circuit-evaluation]\r | lgtm[rb/confusing-method-name] and lgtm[rb/non-short-circuit-evaluation]\r | lgtm[rb/confusing-method-name] | TestWindows.rb:26:1:26:74 | suppression range | +| TestWindows.rb:26:1:26:74 | #lgtm[rb/confusing-method-name] and lgtm[rb/non-short-circuit-evaluation]\r | lgtm[rb/confusing-method-name] and lgtm[rb/non-short-circuit-evaluation]\r | lgtm[rb/non-short-circuit-evaluation] | TestWindows.rb:26:1:26:74 | suppression range | +| TestWindows.rb:27:1:27:38 | #lgtm[rb/confusing-method-name]; lgtm\r | lgtm[rb/confusing-method-name]; lgtm\r | lgtm | TestWindows.rb:27:1:27:38 | suppression range | +| TestWindows.rb:27:1:27:38 | #lgtm[rb/confusing-method-name]; lgtm\r | lgtm[rb/confusing-method-name]; lgtm\r | lgtm[rb/confusing-method-name] | TestWindows.rb:27:1:27:38 | suppression range | diff --git a/ruby/ql/test/query-tests/AlertSuppression/AlertSuppression.qlref b/ruby/ql/test/query-tests/AlertSuppression/AlertSuppression.qlref new file mode 100644 index 00000000000..9d7833eccae --- /dev/null +++ b/ruby/ql/test/query-tests/AlertSuppression/AlertSuppression.qlref @@ -0,0 +1 @@ +AlertSuppression.ql diff --git a/ruby/ql/test/query-tests/AlertSuppression/Test.rb b/ruby/ql/test/query-tests/AlertSuppression/Test.rb new file mode 100644 index 00000000000..028d5b87aa7 --- /dev/null +++ b/ruby/ql/test/query-tests/AlertSuppression/Test.rb @@ -0,0 +1,28 @@ +class Test end # lgtm +# lgtm[rb/confusing-method-name] +# lgtm[rb/confusing-method-name, rb/non-short-circuit-evaluation] +# lgtm[@tag:exceptions] +# lgtm[@tag:exceptions,rb/confusing-method-name] +# lgtm[@expires:2017-06-11] +# lgtm[rb/confusing-method-name] does not seem confusing despite alert by lgtm +# lgtm: blah blah +# lgtm blah blah #falsepositive +#lgtm [rb/confusing-method-name] +# lgtm[] +# lgtmfoo +#lgtm +# lgtm +# lgtm [rb/confusing-method-name] +# foolgtm[rb/confusing-method-name] +# foolgtm +# foo; lgtm +# foo; lgtm[rb/confusing-method-name] +# foo lgtm +# foo lgtm[rb/confusing-method-name] +# foo lgtm bar +# foo lgtm[rb/confusing-method-name] bar +# LGTM! +# LGTM[rb/confusing-method-name] +#lgtm[rb/confusing-method-name] and lgtm[rb/non-short-circuit-evaluation] +#lgtm[rb/confusing-method-name]; lgtm + diff --git a/ruby/ql/test/query-tests/AlertSuppression/TestWindows.rb b/ruby/ql/test/query-tests/AlertSuppression/TestWindows.rb new file mode 100644 index 00000000000..1e69a66ee11 --- /dev/null +++ b/ruby/ql/test/query-tests/AlertSuppression/TestWindows.rb @@ -0,0 +1,28 @@ +class TestWindows end # lgtm +# lgtm[rb/confusing-method-name] +# lgtm[rb/confusing-method-name, rb/non-short-circuit-evaluation] +# lgtm[@tag:exceptions] +# lgtm[@tag:exceptions,rb/confusing-method-name] +# lgtm[@expires:2017-06-11] +# lgtm[rb/confusing-method-name] does not seem confusing despite alert by lgtm +# lgtm: blah blah +# lgtm blah blah #falsepositive +#lgtm [rb/confusing-method-name] +# lgtm[] +# lgtmfoo +#lgtm +# lgtm +# lgtm [rb/confusing-method-name] +# foolgtm[rb/confusing-method-name] +# foolgtm +# foo; lgtm +# foo; lgtm[rb/confusing-method-name] +# foo lgtm +# foo lgtm[rb/confusing-method-name] +# foo lgtm bar +# foo lgtm[rb/confusing-method-name] bar +# LGTM! +# LGTM[rb/confusing-method-name] +#lgtm[rb/confusing-method-name] and lgtm[rb/non-short-circuit-evaluation] +#lgtm[rb/confusing-method-name]; lgtm + diff --git a/ruby/ql/test/query-tests/analysis/Definitions.expected b/ruby/ql/test/query-tests/analysis/Definitions.expected new file mode 100644 index 00000000000..47f0b049d08 --- /dev/null +++ b/ruby/ql/test/query-tests/analysis/Definitions.expected @@ -0,0 +1,14 @@ +| Definitions.rb:6:7:6:21 | call to g | Definitions.rb:9:5:11:7 | g | method | +| Definitions.rb:6:9:6:21 | SOME_CONSTANT | Definitions.rb:2:3:2:15 | SOME_CONSTANT | constant | +| Definitions.rb:10:7:10:7 | x | Definitions.rb:9:11:9:11 | x | variable | +| Definitions.rb:14:7:14:7 | call to f | Definitions.rb:5:5:7:7 | f | method | +| Definitions.rb:23:5:23:7 | @@a | Definitions.rb:20:3:20:5 | @@a | class variable | +| Definitions.rb:32:7:32:7 | y | Definitions.rb:31:10:31:10 | y | variable | +| Definitions.rb:36:7:36:7 | A | Definitions.rb:1:1:17:3 | A | constant | +| Definitions.rb:36:7:36:10 | B | Definitions.rb:4:3:16:5 | B | constant | +| Definitions.rb:36:7:36:18 | call to g | Definitions.rb:9:5:11:7 | g | method | +| Definitions.rb:36:18:36:18 | y | Definitions.rb:35:11:35:11 | y | variable | +| Definitions.rb:39:7:39:8 | @e | Definitions.rb:30:7:30:8 | @e | instance variable | +| Definitions.rb:41:7:41:9 | @@b | Definitions.rb:27:5:27:7 | @@b | class variable | +| Definitions.rb:46:1:46:1 | C | Definitions.rb:19:1:44:3 | C | constant | +| Definitions.rb:46:1:46:4 | D | Definitions.rb:26:3:43:5 | D | constant | diff --git a/ruby/ql/test/query-tests/analysis/Definitions.qlref b/ruby/ql/test/query-tests/analysis/Definitions.qlref new file mode 100644 index 00000000000..a8620aaeec6 --- /dev/null +++ b/ruby/ql/test/query-tests/analysis/Definitions.qlref @@ -0,0 +1 @@ +queries/analysis/Definitions.ql diff --git a/ruby/ql/test/query-tests/analysis/Definitions.rb b/ruby/ql/test/query-tests/analysis/Definitions.rb new file mode 100644 index 00000000000..90b59be5acf --- /dev/null +++ b/ruby/ql/test/query-tests/analysis/Definitions.rb @@ -0,0 +1,46 @@ +module A + SOME_CONSTANT = 1 + + class B + def f + g SOME_CONSTANT + end + + def g x + x + end + + def h + f + end + end +end + +module C + @@a = 1 + + def self.a + @@a + end + + class D + @@b = 2 + + def initialize + @e = 1 + x, y = [1, 2] + y + end + + def h y + A::B.new.g y + UnknownClass.some_method + @f = 2 + @e + @f + @@b + end + end +end + +C::D.new diff --git a/ruby/ql/test/query-tests/diagnostics/ExtractionErrors.expected b/ruby/ql/test/query-tests/diagnostics/ExtractionErrors.expected new file mode 100644 index 00000000000..749d8b347bb --- /dev/null +++ b/ruby/ql/test/query-tests/diagnostics/ExtractionErrors.expected @@ -0,0 +1,2 @@ +| src/not_ruby.rb:5:25:5:26 | parse error | Extraction failed in src/not_ruby.rb with error parse error | 2 | +| src/unsupported_feature.rb:1:6:2:11 | parse error | Extraction failed in src/unsupported_feature.rb with error parse error | 2 | diff --git a/ruby/ql/test/query-tests/diagnostics/ExtractionErrors.qlref b/ruby/ql/test/query-tests/diagnostics/ExtractionErrors.qlref new file mode 100644 index 00000000000..ffbdb0a7b1b --- /dev/null +++ b/ruby/ql/test/query-tests/diagnostics/ExtractionErrors.qlref @@ -0,0 +1 @@ +queries/diagnostics/ExtractionErrors.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/diagnostics/NumberOfFilesExtractedWithErrors.expected b/ruby/ql/test/query-tests/diagnostics/NumberOfFilesExtractedWithErrors.expected new file mode 100644 index 00000000000..a882f18620b --- /dev/null +++ b/ruby/ql/test/query-tests/diagnostics/NumberOfFilesExtractedWithErrors.expected @@ -0,0 +1 @@ +| 2 | diff --git a/ruby/ql/test/query-tests/diagnostics/NumberOfFilesExtractedWithErrors.qlref b/ruby/ql/test/query-tests/diagnostics/NumberOfFilesExtractedWithErrors.qlref new file mode 100644 index 00000000000..17823cc8837 --- /dev/null +++ b/ruby/ql/test/query-tests/diagnostics/NumberOfFilesExtractedWithErrors.qlref @@ -0,0 +1 @@ +queries/summary/NumberOfFilesExtractedWithErrors.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/diagnostics/NumberOfSuccessfullyExtractedFiles.expected b/ruby/ql/test/query-tests/diagnostics/NumberOfSuccessfullyExtractedFiles.expected new file mode 100644 index 00000000000..7621cebdd5f --- /dev/null +++ b/ruby/ql/test/query-tests/diagnostics/NumberOfSuccessfullyExtractedFiles.expected @@ -0,0 +1 @@ +| 3 | diff --git a/ruby/ql/test/query-tests/diagnostics/NumberOfSuccessfullyExtractedFiles.qlref b/ruby/ql/test/query-tests/diagnostics/NumberOfSuccessfullyExtractedFiles.qlref new file mode 100644 index 00000000000..5f6eda05206 --- /dev/null +++ b/ruby/ql/test/query-tests/diagnostics/NumberOfSuccessfullyExtractedFiles.qlref @@ -0,0 +1 @@ +queries/summary/NumberOfSuccessfullyExtractedFiles.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/diagnostics/SuccessfullyExtractedFiles.expected b/ruby/ql/test/query-tests/diagnostics/SuccessfullyExtractedFiles.expected new file mode 100644 index 00000000000..ccb442a930a --- /dev/null +++ b/ruby/ql/test/query-tests/diagnostics/SuccessfullyExtractedFiles.expected @@ -0,0 +1,3 @@ +| src/bar.erb:0:0:0:0 | src/bar.erb | | +| src/foo.rb:0:0:0:0 | src/foo.rb | | +| src/vendor/cache/lib.rb:0:0:0:0 | src/vendor/cache/lib.rb | | diff --git a/ruby/ql/test/query-tests/diagnostics/SuccessfullyExtractedFiles.qlref b/ruby/ql/test/query-tests/diagnostics/SuccessfullyExtractedFiles.qlref new file mode 100644 index 00000000000..6dc0952ba3a --- /dev/null +++ b/ruby/ql/test/query-tests/diagnostics/SuccessfullyExtractedFiles.qlref @@ -0,0 +1 @@ +queries/diagnostics/SuccessfullyExtractedFiles.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/diagnostics/src/bar.erb b/ruby/ql/test/query-tests/diagnostics/src/bar.erb new file mode 100644 index 00000000000..5bd37fdef28 --- /dev/null +++ b/ruby/ql/test/query-tests/diagnostics/src/bar.erb @@ -0,0 +1,2 @@ +<%# comment -%> +blah diff --git a/ruby/ql/test/query-tests/diagnostics/src/foo.rb b/ruby/ql/test/query-tests/diagnostics/src/foo.rb new file mode 100644 index 00000000000..7b831d230ed --- /dev/null +++ b/ruby/ql/test/query-tests/diagnostics/src/foo.rb @@ -0,0 +1,9 @@ +# comment + +def hello + p "hello world" +end + +# another one + +hello diff --git a/ruby/ql/test/query-tests/diagnostics/src/not_ruby.rb b/ruby/ql/test/query-tests/diagnostics/src/not_ruby.rb new file mode 100755 index 00000000000..51128f4f7fe --- /dev/null +++ b/ruby/ql/test/query-tests/diagnostics/src/not_ruby.rb @@ -0,0 +1,5 @@ +#!/bin/bash + +# This is a bash script +export FOO="$(whereis ls)" +exec "$FOO" "$(dirname "$0")" diff --git a/ruby/ql/test/query-tests/diagnostics/src/unsupported_feature.rb b/ruby/ql/test/query-tests/diagnostics/src/unsupported_feature.rb new file mode 100644 index 00000000000..44ae09f6a5e --- /dev/null +++ b/ruby/ql/test/query-tests/diagnostics/src/unsupported_feature.rb @@ -0,0 +1,3 @@ +case foo + in 3 then 5 +end \ No newline at end of file diff --git a/ruby/ql/test/query-tests/diagnostics/src/vendor/cache/lib.rb b/ruby/ql/test/query-tests/diagnostics/src/vendor/cache/lib.rb new file mode 100644 index 00000000000..f75db7941db --- /dev/null +++ b/ruby/ql/test/query-tests/diagnostics/src/vendor/cache/lib.rb @@ -0,0 +1,9 @@ +# comment + +def hello + p "hello lib" +end + +# another one + +hello diff --git a/ruby/ql/test/query-tests/metrics/FLines/Empty.rb b/ruby/ql/test/query-tests/metrics/FLines/Empty.rb new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ruby/ql/test/query-tests/metrics/FLines/FLines.expected b/ruby/ql/test/query-tests/metrics/FLines/FLines.expected new file mode 100644 index 00000000000..e988ecc8ff0 --- /dev/null +++ b/ruby/ql/test/query-tests/metrics/FLines/FLines.expected @@ -0,0 +1,2 @@ +| FLines.rb:0:0:0:0 | FLines.rb | 34 | +| Empty.rb:0:0:0:0 | Empty.rb | 0 | diff --git a/ruby/ql/test/query-tests/metrics/FLines/FLines.qlref b/ruby/ql/test/query-tests/metrics/FLines/FLines.qlref new file mode 100644 index 00000000000..31546437598 --- /dev/null +++ b/ruby/ql/test/query-tests/metrics/FLines/FLines.qlref @@ -0,0 +1 @@ +queries/metrics/FLines.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/metrics/FLines/FLines.rb b/ruby/ql/test/query-tests/metrics/FLines/FLines.rb new file mode 100644 index 00000000000..5383f87951b --- /dev/null +++ b/ruby/ql/test/query-tests/metrics/FLines/FLines.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +=begin +some preprocessing here +and here +=end + +class FLinesTest + + def foo(bar) + # This is a comment + # and another + + some_string = <<-ESCAPE +hello world +multiple +lines + +how many lines of code in this heredoc? +# 9 lines total + +ESCAPE + + some_other_string = "line 1 + line" + bar + + p some_string + p some_other_string + + some_string + some_other_string + end + + +end diff --git a/ruby/ql/test/query-tests/metrics/FLines/FLinesOfCode.expected b/ruby/ql/test/query-tests/metrics/FLines/FLinesOfCode.expected new file mode 100644 index 00000000000..b5500519091 --- /dev/null +++ b/ruby/ql/test/query-tests/metrics/FLines/FLinesOfCode.expected @@ -0,0 +1,2 @@ +| FLines.rb:0:0:0:0 | FLines.rb | 18 | +| Empty.rb:0:0:0:0 | Empty.rb | 0 | diff --git a/ruby/ql/test/query-tests/metrics/FLines/FLinesOfCode.qlref b/ruby/ql/test/query-tests/metrics/FLines/FLinesOfCode.qlref new file mode 100644 index 00000000000..cb4dd2b8767 --- /dev/null +++ b/ruby/ql/test/query-tests/metrics/FLines/FLinesOfCode.qlref @@ -0,0 +1 @@ +queries/metrics/FLinesOfCode.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/metrics/FLines/FLinesOfComments.expected b/ruby/ql/test/query-tests/metrics/FLines/FLinesOfComments.expected new file mode 100644 index 00000000000..cf90251a4f5 --- /dev/null +++ b/ruby/ql/test/query-tests/metrics/FLines/FLinesOfComments.expected @@ -0,0 +1,2 @@ +| FLines.rb:0:0:0:0 | FLines.rb | 7 | +| Empty.rb:0:0:0:0 | Empty.rb | 0 | diff --git a/ruby/ql/test/query-tests/metrics/FLines/FLinesOfComments.qlref b/ruby/ql/test/query-tests/metrics/FLines/FLinesOfComments.qlref new file mode 100644 index 00000000000..766ae4bcc59 --- /dev/null +++ b/ruby/ql/test/query-tests/metrics/FLines/FLinesOfComments.qlref @@ -0,0 +1 @@ +queries/metrics/FLinesOfComments.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/performance/UseDetect/UseDetect.expected b/ruby/ql/test/query-tests/performance/UseDetect/UseDetect.expected new file mode 100644 index 00000000000..d8a5ec8dd41 --- /dev/null +++ b/ruby/ql/test/query-tests/performance/UseDetect/UseDetect.expected @@ -0,0 +1,7 @@ +| UseDetect.rb:5:9:5:36 | call to first | Replace this call and $@ with 'detect'. | UseDetect.rb:5:9:5:30 | call to select | 'select' call | +| UseDetect.rb:6:9:6:35 | call to last | Replace this call and $@ with 'reverse_detect'. | UseDetect.rb:6:9:6:30 | call to select | 'select' call | +| UseDetect.rb:7:9:7:33 | ...[...] | Replace this call and $@ with 'detect'. | UseDetect.rb:7:9:7:30 | call to select | 'select' call | +| UseDetect.rb:8:9:8:34 | ...[...] | Replace this call and $@ with 'reverse_detect'. | UseDetect.rb:8:9:8:30 | call to select | 'select' call | +| UseDetect.rb:9:9:9:36 | call to first | Replace this call and $@ with 'detect'. | UseDetect.rb:9:9:9:30 | call to filter | 'select' call | +| UseDetect.rb:10:9:10:37 | call to last | Replace this call and $@ with 'reverse_detect'. | UseDetect.rb:10:9:10:32 | call to find_all | 'select' call | +| UseDetect.rb:12:9:12:24 | call to first | Replace this call and $@ with 'detect'. | UseDetect.rb:11:22:11:43 | call to select | 'select' call | diff --git a/ruby/ql/test/query-tests/performance/UseDetect/UseDetect.qlref b/ruby/ql/test/query-tests/performance/UseDetect/UseDetect.qlref new file mode 100644 index 00000000000..f2a94b28c40 --- /dev/null +++ b/ruby/ql/test/query-tests/performance/UseDetect/UseDetect.qlref @@ -0,0 +1 @@ +experimental/performance/UseDetect.ql diff --git a/ruby/ql/test/query-tests/performance/UseDetect/UseDetect.rb b/ruby/ql/test/query-tests/performance/UseDetect/UseDetect.rb new file mode 100644 index 00000000000..e1d2d9b91ba --- /dev/null +++ b/ruby/ql/test/query-tests/performance/UseDetect/UseDetect.rb @@ -0,0 +1,21 @@ + +class DetectTest + def test + # These are bad + [].select { |i| true }.first + [].select { |i| true }.last + [].select { |i| true }[0] + [].select { |i| true }[-1] + [].filter { |i| true }.first + [].find_all { |i| true }.last + selection1 = [].select { |i| true } + selection1.first + + # These are good + [].select("").first # Selecting a string + [].select { |i| true }[1] # Selecting the second element + selection2 = [].select { |i| true } + selection2.first # Selection used elsewhere + selection3 = selection2 + end +end diff --git a/ruby/ql/test/query-tests/security/cwe-022/PathInjection.expected b/ruby/ql/test/query-tests/security/cwe-022/PathInjection.expected new file mode 100644 index 00000000000..5ac1c88d6f4 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-022/PathInjection.expected @@ -0,0 +1,64 @@ +edges +| tainted_path.rb:4:12:4:17 | call to params : | tainted_path.rb:5:26:5:29 | path | +| tainted_path.rb:10:12:10:43 | call to absolute_path : | tainted_path.rb:11:26:11:29 | path | +| tainted_path.rb:10:31:10:36 | call to params : | tainted_path.rb:10:31:10:43 | ...[...] : | +| tainted_path.rb:10:31:10:43 | ...[...] : | tainted_path.rb:10:12:10:43 | call to absolute_path : | +| tainted_path.rb:16:15:16:41 | call to dirname : | tainted_path.rb:17:26:17:29 | path | +| tainted_path.rb:16:28:16:33 | call to params : | tainted_path.rb:16:28:16:40 | ...[...] : | +| tainted_path.rb:16:28:16:40 | ...[...] : | tainted_path.rb:16:15:16:41 | call to dirname : | +| tainted_path.rb:22:12:22:41 | call to expand_path : | tainted_path.rb:23:26:23:29 | path | +| tainted_path.rb:22:29:22:34 | call to params : | tainted_path.rb:22:29:22:41 | ...[...] : | +| tainted_path.rb:22:29:22:41 | ...[...] : | tainted_path.rb:22:12:22:41 | call to expand_path : | +| tainted_path.rb:28:12:28:34 | call to path : | tainted_path.rb:29:26:29:29 | path | +| tainted_path.rb:28:22:28:27 | call to params : | tainted_path.rb:28:22:28:34 | ...[...] : | +| tainted_path.rb:28:22:28:34 | ...[...] : | tainted_path.rb:28:12:28:34 | call to path : | +| tainted_path.rb:34:12:34:41 | call to realdirpath : | tainted_path.rb:35:26:35:29 | path | +| tainted_path.rb:34:29:34:34 | call to params : | tainted_path.rb:34:29:34:41 | ...[...] : | +| tainted_path.rb:34:29:34:41 | ...[...] : | tainted_path.rb:34:12:34:41 | call to realdirpath : | +| tainted_path.rb:40:12:40:38 | call to realpath : | tainted_path.rb:41:26:41:29 | path | +| tainted_path.rb:40:26:40:31 | call to params : | tainted_path.rb:40:26:40:38 | ...[...] : | +| tainted_path.rb:40:26:40:38 | ...[...] : | tainted_path.rb:40:12:40:38 | call to realpath : | +| tainted_path.rb:47:12:47:63 | call to join : | tainted_path.rb:48:26:48:29 | path | +| tainted_path.rb:47:43:47:48 | call to params : | tainted_path.rb:47:43:47:55 | ...[...] : | +| tainted_path.rb:47:43:47:55 | ...[...] : | tainted_path.rb:47:12:47:63 | call to join : | +nodes +| tainted_path.rb:4:12:4:17 | call to params : | semmle.label | call to params : | +| tainted_path.rb:5:26:5:29 | path | semmle.label | path | +| tainted_path.rb:10:12:10:43 | call to absolute_path : | semmle.label | call to absolute_path : | +| tainted_path.rb:10:31:10:36 | call to params : | semmle.label | call to params : | +| tainted_path.rb:10:31:10:43 | ...[...] : | semmle.label | ...[...] : | +| tainted_path.rb:11:26:11:29 | path | semmle.label | path | +| tainted_path.rb:16:15:16:41 | call to dirname : | semmle.label | call to dirname : | +| tainted_path.rb:16:28:16:33 | call to params : | semmle.label | call to params : | +| tainted_path.rb:16:28:16:40 | ...[...] : | semmle.label | ...[...] : | +| tainted_path.rb:17:26:17:29 | path | semmle.label | path | +| tainted_path.rb:22:12:22:41 | call to expand_path : | semmle.label | call to expand_path : | +| tainted_path.rb:22:29:22:34 | call to params : | semmle.label | call to params : | +| tainted_path.rb:22:29:22:41 | ...[...] : | semmle.label | ...[...] : | +| tainted_path.rb:23:26:23:29 | path | semmle.label | path | +| tainted_path.rb:28:12:28:34 | call to path : | semmle.label | call to path : | +| tainted_path.rb:28:22:28:27 | call to params : | semmle.label | call to params : | +| tainted_path.rb:28:22:28:34 | ...[...] : | semmle.label | ...[...] : | +| tainted_path.rb:29:26:29:29 | path | semmle.label | path | +| tainted_path.rb:34:12:34:41 | call to realdirpath : | semmle.label | call to realdirpath : | +| tainted_path.rb:34:29:34:34 | call to params : | semmle.label | call to params : | +| tainted_path.rb:34:29:34:41 | ...[...] : | semmle.label | ...[...] : | +| tainted_path.rb:35:26:35:29 | path | semmle.label | path | +| tainted_path.rb:40:12:40:38 | call to realpath : | semmle.label | call to realpath : | +| tainted_path.rb:40:26:40:31 | call to params : | semmle.label | call to params : | +| tainted_path.rb:40:26:40:38 | ...[...] : | semmle.label | ...[...] : | +| tainted_path.rb:41:26:41:29 | path | semmle.label | path | +| tainted_path.rb:47:12:47:63 | call to join : | semmle.label | call to join : | +| tainted_path.rb:47:43:47:48 | call to params : | semmle.label | call to params : | +| tainted_path.rb:47:43:47:55 | ...[...] : | semmle.label | ...[...] : | +| tainted_path.rb:48:26:48:29 | path | semmle.label | path | +subpaths +#select +| tainted_path.rb:5:26:5:29 | path | tainted_path.rb:4:12:4:17 | call to params : | tainted_path.rb:5:26:5:29 | path | This path depends on $@. | tainted_path.rb:4:12:4:17 | call to params | a user-provided value | +| tainted_path.rb:11:26:11:29 | path | tainted_path.rb:10:31:10:36 | call to params : | tainted_path.rb:11:26:11:29 | path | This path depends on $@. | tainted_path.rb:10:31:10:36 | call to params | a user-provided value | +| tainted_path.rb:17:26:17:29 | path | tainted_path.rb:16:28:16:33 | call to params : | tainted_path.rb:17:26:17:29 | path | This path depends on $@. | tainted_path.rb:16:28:16:33 | call to params | a user-provided value | +| tainted_path.rb:23:26:23:29 | path | tainted_path.rb:22:29:22:34 | call to params : | tainted_path.rb:23:26:23:29 | path | This path depends on $@. | tainted_path.rb:22:29:22:34 | call to params | a user-provided value | +| tainted_path.rb:29:26:29:29 | path | tainted_path.rb:28:22:28:27 | call to params : | tainted_path.rb:29:26:29:29 | path | This path depends on $@. | tainted_path.rb:28:22:28:27 | call to params | a user-provided value | +| tainted_path.rb:35:26:35:29 | path | tainted_path.rb:34:29:34:34 | call to params : | tainted_path.rb:35:26:35:29 | path | This path depends on $@. | tainted_path.rb:34:29:34:34 | call to params | a user-provided value | +| tainted_path.rb:41:26:41:29 | path | tainted_path.rb:40:26:40:31 | call to params : | tainted_path.rb:41:26:41:29 | path | This path depends on $@. | tainted_path.rb:40:26:40:31 | call to params | a user-provided value | +| tainted_path.rb:48:26:48:29 | path | tainted_path.rb:47:43:47:48 | call to params : | tainted_path.rb:48:26:48:29 | path | This path depends on $@. | tainted_path.rb:47:43:47:48 | call to params | a user-provided value | diff --git a/ruby/ql/test/query-tests/security/cwe-022/PathInjection.qlref b/ruby/ql/test/query-tests/security/cwe-022/PathInjection.qlref new file mode 100644 index 00000000000..7b98278f7a7 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-022/PathInjection.qlref @@ -0,0 +1 @@ +queries/security/cwe-022/PathInjection.ql diff --git a/ruby/ql/test/query-tests/security/cwe-022/tainted_path.rb b/ruby/ql/test/query-tests/security/cwe-022/tainted_path.rb new file mode 100644 index 00000000000..dc852803ff9 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-022/tainted_path.rb @@ -0,0 +1,62 @@ +class FooController < ActionController::Base + # BAD + def route0 + path = params[:path] + @content = File.read path + end + + # BAD - File.absolute_path preserves taint + def route1 + path = File.absolute_path params[:path] + @content = File.read path + end + + # BAD - File.dirname preserves taint + def route2 + path = "#{File.dirname(params[:path])}/foo" + @content = File.read path + end + + # BAD - File.expand_path preserves taint + def route3 + path = File.expand_path params[:path] + @content = File.read path + end + + # BAD - File.path preserves taint + def route4 + path = File.path params[:path] + @content = File.read path + end + + # BAD - File.realdirpath preserves taint + def route5 + path = File.realdirpath params[:path] + @content = File.read path + end + + # BAD - File.realpath preserves taint + def route6 + path = File.realpath params[:path] + @content = File.read path + end + + # BAD - tainted arguments in any position propagate to the return value of + # File.join + def route7 + path = File.join("foo", "bar", "baz", params[:path], "qux") + @content = File.read path + end + + # GOOD - File.basename does not preserve taint + def route8 + path = File.basename params[:path] + @content = File.read path + end + + # GOOD - explicitly sanitized + def route9 + path = ActiveStorage::Filename.new(params[:path]).sanitized + @content = File.read path + end +end diff --git a/ruby/ql/test/query-tests/security/cwe-078/CommandInjection.expected b/ruby/ql/test/query-tests/security/cwe-078/CommandInjection.expected new file mode 100644 index 00000000000..d3338f6cd56 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-078/CommandInjection.expected @@ -0,0 +1,33 @@ +edges +| CommandInjection.rb:6:15:6:20 | call to params : | CommandInjection.rb:7:10:7:15 | #{...} | +| CommandInjection.rb:6:15:6:20 | call to params : | CommandInjection.rb:8:16:8:18 | cmd | +| CommandInjection.rb:6:15:6:20 | call to params : | CommandInjection.rb:10:14:10:16 | cmd | +| CommandInjection.rb:6:15:6:20 | call to params : | CommandInjection.rb:11:17:11:22 | #{...} | +| CommandInjection.rb:6:15:6:20 | call to params : | CommandInjection.rb:13:9:13:14 | #{...} | +| CommandInjection.rb:6:15:6:20 | call to params : | CommandInjection.rb:29:19:29:24 | #{...} | +| CommandInjection.rb:6:15:6:20 | call to params : | CommandInjection.rb:33:24:33:36 | "echo #{...}" | +| CommandInjection.rb:6:15:6:20 | call to params : | CommandInjection.rb:34:39:34:51 | "grep #{...}" | +| CommandInjection.rb:46:15:46:20 | call to params : | CommandInjection.rb:50:24:50:36 | "echo #{...}" | +nodes +| CommandInjection.rb:6:15:6:20 | call to params : | semmle.label | call to params : | +| CommandInjection.rb:7:10:7:15 | #{...} | semmle.label | #{...} | +| CommandInjection.rb:8:16:8:18 | cmd | semmle.label | cmd | +| CommandInjection.rb:10:14:10:16 | cmd | semmle.label | cmd | +| CommandInjection.rb:11:17:11:22 | #{...} | semmle.label | #{...} | +| CommandInjection.rb:13:9:13:14 | #{...} | semmle.label | #{...} | +| CommandInjection.rb:29:19:29:24 | #{...} | semmle.label | #{...} | +| CommandInjection.rb:33:24:33:36 | "echo #{...}" | semmle.label | "echo #{...}" | +| CommandInjection.rb:34:39:34:51 | "grep #{...}" | semmle.label | "grep #{...}" | +| CommandInjection.rb:46:15:46:20 | call to params : | semmle.label | call to params : | +| CommandInjection.rb:50:24:50:36 | "echo #{...}" | semmle.label | "echo #{...}" | +subpaths +#select +| CommandInjection.rb:7:10:7:15 | #{...} | CommandInjection.rb:6:15:6:20 | call to params : | CommandInjection.rb:7:10:7:15 | #{...} | This command depends on $@. | CommandInjection.rb:6:15:6:20 | call to params | a user-provided value | +| CommandInjection.rb:8:16:8:18 | cmd | CommandInjection.rb:6:15:6:20 | call to params : | CommandInjection.rb:8:16:8:18 | cmd | This command depends on $@. | CommandInjection.rb:6:15:6:20 | call to params | a user-provided value | +| CommandInjection.rb:10:14:10:16 | cmd | CommandInjection.rb:6:15:6:20 | call to params : | CommandInjection.rb:10:14:10:16 | cmd | This command depends on $@. | CommandInjection.rb:6:15:6:20 | call to params | a user-provided value | +| CommandInjection.rb:11:17:11:22 | #{...} | CommandInjection.rb:6:15:6:20 | call to params : | CommandInjection.rb:11:17:11:22 | #{...} | This command depends on $@. | CommandInjection.rb:6:15:6:20 | call to params | a user-provided value | +| CommandInjection.rb:13:9:13:14 | #{...} | CommandInjection.rb:6:15:6:20 | call to params : | CommandInjection.rb:13:9:13:14 | #{...} | This command depends on $@. | CommandInjection.rb:6:15:6:20 | call to params | a user-provided value | +| CommandInjection.rb:29:19:29:24 | #{...} | CommandInjection.rb:6:15:6:20 | call to params : | CommandInjection.rb:29:19:29:24 | #{...} | This command depends on $@. | CommandInjection.rb:6:15:6:20 | call to params | a user-provided value | +| CommandInjection.rb:33:24:33:36 | "echo #{...}" | CommandInjection.rb:6:15:6:20 | call to params : | CommandInjection.rb:33:24:33:36 | "echo #{...}" | This command depends on $@. | CommandInjection.rb:6:15:6:20 | call to params | a user-provided value | +| CommandInjection.rb:34:39:34:51 | "grep #{...}" | CommandInjection.rb:6:15:6:20 | call to params : | CommandInjection.rb:34:39:34:51 | "grep #{...}" | This command depends on $@. | CommandInjection.rb:6:15:6:20 | call to params | a user-provided value | +| CommandInjection.rb:50:24:50:36 | "echo #{...}" | CommandInjection.rb:46:15:46:20 | call to params : | CommandInjection.rb:50:24:50:36 | "echo #{...}" | This command depends on $@. | CommandInjection.rb:46:15:46:20 | call to params | a user-provided value | diff --git a/ruby/ql/test/query-tests/security/cwe-078/CommandInjection.qlref b/ruby/ql/test/query-tests/security/cwe-078/CommandInjection.qlref new file mode 100644 index 00000000000..b9c6fe1b90a --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-078/CommandInjection.qlref @@ -0,0 +1 @@ +queries/security/cwe-078/CommandInjection.ql diff --git a/ruby/ql/test/query-tests/security/cwe-078/CommandInjection.rb b/ruby/ql/test/query-tests/security/cwe-078/CommandInjection.rb new file mode 100644 index 00000000000..f91d36f71bd --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-078/CommandInjection.rb @@ -0,0 +1,52 @@ +require "shellwords" +require "open3" + +class UsersController < ActionController::Base + def create + cmd = params[:cmd] + `#{cmd}` + system(cmd) + system("echo", cmd) # OK, because cmd is not shell interpreted + exec(cmd) + %x(echo #{cmd}) + result = <<`EOF` + #{cmd} +EOF + + safe_cmd_1 = Shellwords.escape(cmd) + `echo #{safe_cmd_1}` + + safe_cmd_2 = Shellwords.shellescape(cmd) + `echo #{safe_cmd_2}` + + if cmd == "some constant" + `echo #{cmd}` + end + + if %w(foo bar).include? cmd + `echo #{cmd}` + else + `echo #{cmd}` + end + + # Open3 methods + Open3.capture2("echo #{cmd}") + Open3.pipeline("cat foo.txt", "grep #{cmd}") + Open3.pipeline(["echo", cmd], "tail") # OK, because cmd is not shell interpreted + end + + def show + `ls` + system("ls") + exec("ls") + %x(ls) + end + + def index + cmd = params[:key] + if %w(foo bar).include? cmd + `echo #{cmd}` + end + Open3.capture2("echo #{cmd}") + end +end diff --git a/ruby/ql/test/query-tests/security/cwe-078/KernelOpen.expected b/ruby/ql/test/query-tests/security/cwe-078/KernelOpen.expected new file mode 100644 index 00000000000..ccdd73f58c7 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-078/KernelOpen.expected @@ -0,0 +1,11 @@ +edges +| KernelOpen.rb:3:12:3:17 | call to params : | KernelOpen.rb:4:10:4:13 | file | +| KernelOpen.rb:3:12:3:17 | call to params : | KernelOpen.rb:5:13:5:16 | file | +nodes +| KernelOpen.rb:3:12:3:17 | call to params : | semmle.label | call to params : | +| KernelOpen.rb:4:10:4:13 | file | semmle.label | file | +| KernelOpen.rb:5:13:5:16 | file | semmle.label | file | +subpaths +#select +| KernelOpen.rb:4:10:4:13 | file | KernelOpen.rb:3:12:3:17 | call to params : | KernelOpen.rb:4:10:4:13 | file | This call to Kernel.open depends on a user-provided value. Replace it with File.open. | +| KernelOpen.rb:5:13:5:16 | file | KernelOpen.rb:3:12:3:17 | call to params : | KernelOpen.rb:5:13:5:16 | file | This call to IO.read depends on a user-provided value. Replace it with File.read. | diff --git a/ruby/ql/test/query-tests/security/cwe-078/KernelOpen.qlref b/ruby/ql/test/query-tests/security/cwe-078/KernelOpen.qlref new file mode 100644 index 00000000000..aea01648c78 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-078/KernelOpen.qlref @@ -0,0 +1 @@ +queries/security/cwe-078/KernelOpen.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/cwe-078/KernelOpen.rb b/ruby/ql/test/query-tests/security/cwe-078/KernelOpen.rb new file mode 100644 index 00000000000..2c6e303f398 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-078/KernelOpen.rb @@ -0,0 +1,17 @@ +class UsersController < ActionController::Base + def create + file = params[:file] + open(file) # BAD + IO.read(file) # BAD + + File.open(file).read # GOOD + + if file == "some/const/path.txt" + open(file) # GOOD - file path is sanitised by guard + end + + if %w(some/const/1.txt some/const/2.txt).include? file + IO.read(file) # GOOD - file path is sanitised by guard + end + end +end diff --git a/ruby/ql/test/query-tests/security/cwe-079/ReflectedXSS.expected b/ruby/ql/test/query-tests/security/cwe-079/ReflectedXSS.expected new file mode 100644 index 00000000000..9f9f21e346d --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-079/ReflectedXSS.expected @@ -0,0 +1,60 @@ +edges +| app/controllers/foo/bars_controller.rb:9:12:9:17 | call to params : | app/controllers/foo/bars_controller.rb:9:12:9:29 | ...[...] : | +| app/controllers/foo/bars_controller.rb:9:12:9:29 | ...[...] : | app/views/foo/bars/show.html.erb:47:5:47:13 | call to user_name | +| app/controllers/foo/bars_controller.rb:13:5:13:37 | ... = ... : | app/views/foo/bars/show.html.erb:51:5:51:18 | call to user_name_memo | +| app/controllers/foo/bars_controller.rb:13:20:13:25 | call to params : | app/controllers/foo/bars_controller.rb:13:5:13:37 | ... = ... : | +| app/controllers/foo/bars_controller.rb:17:21:17:26 | call to params : | app/controllers/foo/bars_controller.rb:17:21:17:36 | ...[...] : | +| app/controllers/foo/bars_controller.rb:17:21:17:36 | ...[...] : | app/views/foo/bars/show.html.erb:2:18:2:30 | @user_website | +| app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params : | app/controllers/foo/bars_controller.rb:19:22:19:23 | dt : | +| app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params : | app/controllers/foo/bars_controller.rb:23:53:23:54 | dt : | +| app/controllers/foo/bars_controller.rb:19:22:19:23 | dt : | app/views/foo/bars/show.html.erb:41:3:41:16 | @instance_text | +| app/controllers/foo/bars_controller.rb:23:53:23:54 | dt : | app/views/foo/bars/show.html.erb:5:9:5:20 | call to display_text | +| app/controllers/foo/bars_controller.rb:23:53:23:54 | dt : | app/views/foo/bars/show.html.erb:8:9:8:36 | ...[...] | +| app/controllers/foo/bars_controller.rb:23:53:23:54 | dt : | app/views/foo/bars/show.html.erb:12:9:12:26 | ...[...] | +| app/controllers/foo/bars_controller.rb:23:53:23:54 | dt : | app/views/foo/bars/show.html.erb:36:3:36:14 | call to display_text | +| app/controllers/foo/bars_controller.rb:23:53:23:54 | dt : | app/views/foo/bars/show.html.erb:44:76:44:87 | call to display_text : | +| app/views/foo/bars/show.html.erb:44:64:44:87 | ... + ... : | app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text | +| app/views/foo/bars/show.html.erb:44:64:44:87 | ... + ... : | app/views/foo/bars/_widget.html.erb:8:9:8:36 | ...[...] | +| app/views/foo/bars/show.html.erb:44:76:44:87 | call to display_text : | app/views/foo/bars/show.html.erb:44:64:44:87 | ... + ... : | +| app/views/foo/bars/show.html.erb:54:29:54:34 | call to params : | app/views/foo/bars/show.html.erb:54:29:54:44 | ...[...] | +| app/views/foo/bars/show.html.erb:57:13:57:18 | call to params : | app/views/foo/bars/show.html.erb:57:13:57:28 | ...[...] | +nodes +| app/controllers/foo/bars_controller.rb:9:12:9:17 | call to params : | semmle.label | call to params : | +| app/controllers/foo/bars_controller.rb:9:12:9:29 | ...[...] : | semmle.label | ...[...] : | +| app/controllers/foo/bars_controller.rb:13:5:13:37 | ... = ... : | semmle.label | ... = ... : | +| app/controllers/foo/bars_controller.rb:13:20:13:25 | call to params : | semmle.label | call to params : | +| app/controllers/foo/bars_controller.rb:17:21:17:26 | call to params : | semmle.label | call to params : | +| app/controllers/foo/bars_controller.rb:17:21:17:36 | ...[...] : | semmle.label | ...[...] : | +| app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params : | semmle.label | call to params : | +| app/controllers/foo/bars_controller.rb:19:22:19:23 | dt : | semmle.label | dt : | +| app/controllers/foo/bars_controller.rb:23:53:23:54 | dt : | semmle.label | dt : | +| app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text | semmle.label | call to display_text | +| app/views/foo/bars/_widget.html.erb:8:9:8:36 | ...[...] | semmle.label | ...[...] | +| app/views/foo/bars/show.html.erb:2:18:2:30 | @user_website | semmle.label | @user_website | +| app/views/foo/bars/show.html.erb:5:9:5:20 | call to display_text | semmle.label | call to display_text | +| app/views/foo/bars/show.html.erb:8:9:8:36 | ...[...] | semmle.label | ...[...] | +| app/views/foo/bars/show.html.erb:12:9:12:26 | ...[...] | semmle.label | ...[...] | +| app/views/foo/bars/show.html.erb:36:3:36:14 | call to display_text | semmle.label | call to display_text | +| app/views/foo/bars/show.html.erb:41:3:41:16 | @instance_text | semmle.label | @instance_text | +| app/views/foo/bars/show.html.erb:44:64:44:87 | ... + ... : | semmle.label | ... + ... : | +| app/views/foo/bars/show.html.erb:44:76:44:87 | call to display_text : | semmle.label | call to display_text : | +| app/views/foo/bars/show.html.erb:47:5:47:13 | call to user_name | semmle.label | call to user_name | +| app/views/foo/bars/show.html.erb:51:5:51:18 | call to user_name_memo | semmle.label | call to user_name_memo | +| app/views/foo/bars/show.html.erb:54:29:54:34 | call to params : | semmle.label | call to params : | +| app/views/foo/bars/show.html.erb:54:29:54:44 | ...[...] | semmle.label | ...[...] | +| app/views/foo/bars/show.html.erb:57:13:57:18 | call to params : | semmle.label | call to params : | +| app/views/foo/bars/show.html.erb:57:13:57:28 | ...[...] | semmle.label | ...[...] | +subpaths +#select +| app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params : | app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text | Cross-site scripting vulnerability due to $@. | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params | a user-provided value | +| app/views/foo/bars/_widget.html.erb:8:9:8:36 | ...[...] | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params : | app/views/foo/bars/_widget.html.erb:8:9:8:36 | ...[...] | Cross-site scripting vulnerability due to $@. | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params | a user-provided value | +| app/views/foo/bars/show.html.erb:2:18:2:30 | @user_website | app/controllers/foo/bars_controller.rb:17:21:17:26 | call to params : | app/views/foo/bars/show.html.erb:2:18:2:30 | @user_website | Cross-site scripting vulnerability due to $@. | app/controllers/foo/bars_controller.rb:17:21:17:26 | call to params | a user-provided value | +| app/views/foo/bars/show.html.erb:5:9:5:20 | call to display_text | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params : | app/views/foo/bars/show.html.erb:5:9:5:20 | call to display_text | Cross-site scripting vulnerability due to $@. | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params | a user-provided value | +| app/views/foo/bars/show.html.erb:8:9:8:36 | ...[...] | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params : | app/views/foo/bars/show.html.erb:8:9:8:36 | ...[...] | Cross-site scripting vulnerability due to $@. | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params | a user-provided value | +| app/views/foo/bars/show.html.erb:12:9:12:26 | ...[...] | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params : | app/views/foo/bars/show.html.erb:12:9:12:26 | ...[...] | Cross-site scripting vulnerability due to $@. | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params | a user-provided value | +| app/views/foo/bars/show.html.erb:36:3:36:14 | call to display_text | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params : | app/views/foo/bars/show.html.erb:36:3:36:14 | call to display_text | Cross-site scripting vulnerability due to $@. | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params | a user-provided value | +| app/views/foo/bars/show.html.erb:41:3:41:16 | @instance_text | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params : | app/views/foo/bars/show.html.erb:41:3:41:16 | @instance_text | Cross-site scripting vulnerability due to $@. | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params | a user-provided value | +| app/views/foo/bars/show.html.erb:47:5:47:13 | call to user_name | app/controllers/foo/bars_controller.rb:9:12:9:17 | call to params : | app/views/foo/bars/show.html.erb:47:5:47:13 | call to user_name | Cross-site scripting vulnerability due to $@. | app/controllers/foo/bars_controller.rb:9:12:9:17 | call to params | a user-provided value | +| app/views/foo/bars/show.html.erb:51:5:51:18 | call to user_name_memo | app/controllers/foo/bars_controller.rb:13:20:13:25 | call to params : | app/views/foo/bars/show.html.erb:51:5:51:18 | call to user_name_memo | Cross-site scripting vulnerability due to $@. | app/controllers/foo/bars_controller.rb:13:20:13:25 | call to params | a user-provided value | +| app/views/foo/bars/show.html.erb:54:29:54:44 | ...[...] | app/views/foo/bars/show.html.erb:54:29:54:34 | call to params : | app/views/foo/bars/show.html.erb:54:29:54:44 | ...[...] | Cross-site scripting vulnerability due to $@. | app/views/foo/bars/show.html.erb:54:29:54:34 | call to params | a user-provided value | +| app/views/foo/bars/show.html.erb:57:13:57:28 | ...[...] | app/views/foo/bars/show.html.erb:57:13:57:18 | call to params : | app/views/foo/bars/show.html.erb:57:13:57:28 | ...[...] | Cross-site scripting vulnerability due to $@. | app/views/foo/bars/show.html.erb:57:13:57:18 | call to params | a user-provided value | diff --git a/ruby/ql/test/query-tests/security/cwe-079/ReflectedXSS.qlref b/ruby/ql/test/query-tests/security/cwe-079/ReflectedXSS.qlref new file mode 100644 index 00000000000..af140959abb --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-079/ReflectedXSS.qlref @@ -0,0 +1 @@ +queries/security/cwe-079/ReflectedXSS.ql diff --git a/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.expected b/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.expected new file mode 100644 index 00000000000..5d578d84776 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.expected @@ -0,0 +1,46 @@ +edges +| app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read : | app/controllers/foo/stores_controller.rb:9:22:9:23 | dt : | +| app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read : | app/controllers/foo/stores_controller.rb:13:55:13:56 | dt : | +| app/controllers/foo/stores_controller.rb:9:22:9:23 | dt : | app/views/foo/stores/show.html.erb:38:3:38:16 | @instance_text | +| app/controllers/foo/stores_controller.rb:12:28:12:48 | call to raw_name : | app/views/foo/stores/show.html.erb:84:5:84:24 | @other_user_raw_name | +| app/controllers/foo/stores_controller.rb:13:55:13:56 | dt : | app/views/foo/stores/show.html.erb:2:9:2:20 | call to display_text | +| app/controllers/foo/stores_controller.rb:13:55:13:56 | dt : | app/views/foo/stores/show.html.erb:5:9:5:36 | ...[...] | +| app/controllers/foo/stores_controller.rb:13:55:13:56 | dt : | app/views/foo/stores/show.html.erb:9:9:9:26 | ...[...] | +| app/controllers/foo/stores_controller.rb:13:55:13:56 | dt : | app/views/foo/stores/show.html.erb:33:3:33:14 | call to display_text | +| app/controllers/foo/stores_controller.rb:13:55:13:56 | dt : | app/views/foo/stores/show.html.erb:41:76:41:87 | call to display_text : | +| app/views/foo/stores/show.html.erb:41:64:41:87 | ... + ... : | app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text | +| app/views/foo/stores/show.html.erb:41:64:41:87 | ... + ... : | app/views/foo/bars/_widget.html.erb:8:9:8:36 | ...[...] | +| app/views/foo/stores/show.html.erb:41:76:41:87 | call to display_text : | app/views/foo/stores/show.html.erb:41:64:41:87 | ... + ... : | +nodes +| app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read : | semmle.label | call to read : | +| app/controllers/foo/stores_controller.rb:9:22:9:23 | dt : | semmle.label | dt : | +| app/controllers/foo/stores_controller.rb:12:28:12:48 | call to raw_name : | semmle.label | call to raw_name : | +| app/controllers/foo/stores_controller.rb:13:55:13:56 | dt : | semmle.label | dt : | +| app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text | semmle.label | call to display_text | +| app/views/foo/bars/_widget.html.erb:8:9:8:36 | ...[...] | semmle.label | ...[...] | +| app/views/foo/stores/show.html.erb:2:9:2:20 | call to display_text | semmle.label | call to display_text | +| app/views/foo/stores/show.html.erb:5:9:5:36 | ...[...] | semmle.label | ...[...] | +| app/views/foo/stores/show.html.erb:9:9:9:26 | ...[...] | semmle.label | ...[...] | +| app/views/foo/stores/show.html.erb:33:3:33:14 | call to display_text | semmle.label | call to display_text | +| app/views/foo/stores/show.html.erb:38:3:38:16 | @instance_text | semmle.label | @instance_text | +| app/views/foo/stores/show.html.erb:41:64:41:87 | ... + ... : | semmle.label | ... + ... : | +| app/views/foo/stores/show.html.erb:41:76:41:87 | call to display_text : | semmle.label | call to display_text : | +| app/views/foo/stores/show.html.erb:47:5:47:16 | call to handle | semmle.label | call to handle | +| app/views/foo/stores/show.html.erb:50:5:50:18 | call to raw_name | semmle.label | call to raw_name | +| app/views/foo/stores/show.html.erb:64:3:64:18 | call to handle | semmle.label | call to handle | +| app/views/foo/stores/show.html.erb:70:3:70:20 | call to raw_name | semmle.label | call to raw_name | +| app/views/foo/stores/show.html.erb:84:5:84:24 | @other_user_raw_name | semmle.label | @other_user_raw_name | +subpaths +#select +| app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read : | app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text | Cross-site scripting vulnerability due to $@ | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | stored value | +| app/views/foo/bars/_widget.html.erb:8:9:8:36 | ...[...] | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read : | app/views/foo/bars/_widget.html.erb:8:9:8:36 | ...[...] | Cross-site scripting vulnerability due to $@ | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | stored value | +| app/views/foo/stores/show.html.erb:2:9:2:20 | call to display_text | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read : | app/views/foo/stores/show.html.erb:2:9:2:20 | call to display_text | Cross-site scripting vulnerability due to $@ | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | stored value | +| app/views/foo/stores/show.html.erb:5:9:5:36 | ...[...] | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read : | app/views/foo/stores/show.html.erb:5:9:5:36 | ...[...] | Cross-site scripting vulnerability due to $@ | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | stored value | +| app/views/foo/stores/show.html.erb:9:9:9:26 | ...[...] | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read : | app/views/foo/stores/show.html.erb:9:9:9:26 | ...[...] | Cross-site scripting vulnerability due to $@ | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | stored value | +| app/views/foo/stores/show.html.erb:33:3:33:14 | call to display_text | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read : | app/views/foo/stores/show.html.erb:33:3:33:14 | call to display_text | Cross-site scripting vulnerability due to $@ | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | stored value | +| app/views/foo/stores/show.html.erb:38:3:38:16 | @instance_text | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read : | app/views/foo/stores/show.html.erb:38:3:38:16 | @instance_text | Cross-site scripting vulnerability due to $@ | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | stored value | +| app/views/foo/stores/show.html.erb:47:5:47:16 | call to handle | app/views/foo/stores/show.html.erb:47:5:47:16 | call to handle | app/views/foo/stores/show.html.erb:47:5:47:16 | call to handle | Cross-site scripting vulnerability due to $@ | app/views/foo/stores/show.html.erb:47:5:47:16 | call to handle | stored value | +| app/views/foo/stores/show.html.erb:50:5:50:18 | call to raw_name | app/views/foo/stores/show.html.erb:50:5:50:18 | call to raw_name | app/views/foo/stores/show.html.erb:50:5:50:18 | call to raw_name | Cross-site scripting vulnerability due to $@ | app/views/foo/stores/show.html.erb:50:5:50:18 | call to raw_name | stored value | +| app/views/foo/stores/show.html.erb:64:3:64:18 | call to handle | app/views/foo/stores/show.html.erb:64:3:64:18 | call to handle | app/views/foo/stores/show.html.erb:64:3:64:18 | call to handle | Cross-site scripting vulnerability due to $@ | app/views/foo/stores/show.html.erb:64:3:64:18 | call to handle | stored value | +| app/views/foo/stores/show.html.erb:70:3:70:20 | call to raw_name | app/views/foo/stores/show.html.erb:70:3:70:20 | call to raw_name | app/views/foo/stores/show.html.erb:70:3:70:20 | call to raw_name | Cross-site scripting vulnerability due to $@ | app/views/foo/stores/show.html.erb:70:3:70:20 | call to raw_name | stored value | +| app/views/foo/stores/show.html.erb:84:5:84:24 | @other_user_raw_name | app/controllers/foo/stores_controller.rb:12:28:12:48 | call to raw_name : | app/views/foo/stores/show.html.erb:84:5:84:24 | @other_user_raw_name | Cross-site scripting vulnerability due to $@ | app/controllers/foo/stores_controller.rb:12:28:12:48 | call to raw_name | stored value | diff --git a/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.qlref b/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.qlref new file mode 100644 index 00000000000..78de28cb282 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.qlref @@ -0,0 +1 @@ +queries/security/cwe-079/StoredXSS.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/cwe-079/app/controllers/foo/bars_controller.rb b/ruby/ql/test/query-tests/security/cwe-079/app/controllers/foo/bars_controller.rb new file mode 100644 index 00000000000..56df7b4d3dd --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-079/app/controllers/foo/bars_controller.rb @@ -0,0 +1,25 @@ +class BarsController < ApplicationController + helper_method :user_name, :user_name_memo + + def index + render template: "foo/bars/index" + end + + def user_name + return params[:user_name] + end + + def user_name_memo + @user_name ||= params[:user_name] + end + + def show + @user_website = params[:website] + dt = params[:text] + @instance_text = dt + @safe_foo = params[:text] + @safe_foo = "safe_foo" + @html_escaped = ERB::Util.html_escape(params[:text]) + render "foo/bars/show", locals: { display_text: dt, safe_text: "hello" } + end +end diff --git a/ruby/ql/test/query-tests/security/cwe-079/app/controllers/foo/stores_controller.rb b/ruby/ql/test/query-tests/security/cwe-079/app/controllers/foo/stores_controller.rb new file mode 100644 index 00000000000..6dbb2508353 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-079/app/controllers/foo/stores_controller.rb @@ -0,0 +1,15 @@ +class StoresController < ApplicationController + helper_method :user_handle + def user_handle + User.find(1).handle + end + + def show + dt = File.read("foo.txt") + @instance_text = dt + @user = User.find 1 + @safe_user_handle = ERB::Util.html_escape(@user.handle) + @other_user_raw_name = User.find(2).raw_name + render "foo/stores/show", locals: { display_text: dt, safe_text: "hello" } + end +end diff --git a/ruby/ql/test/query-tests/security/cwe-079/app/models/user.rb b/ruby/ql/test/query-tests/security/cwe-079/app/models/user.rb new file mode 100644 index 00000000000..1235ccb9e52 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-079/app/models/user.rb @@ -0,0 +1,14 @@ +class User < ActiveRecord::Base + def is_dummy_user? + self.user_id == 0 + end + + def raw_name + me = self + me.handle + end + + def display_name + self.real_name || self.handle + end +end diff --git a/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/bars/_widget.html.erb b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/bars/_widget.html.erb new file mode 100644 index 00000000000..54b52afe8c7 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/bars/_widget.html.erb @@ -0,0 +1,11 @@ +<%# BAD: A local rendered raw as an instance variable %> +<%= raw @display_text %> + +<%# BAD: A local rendered raw as a local variable %> +<%= raw display_text %> + +<%# BAD: A local rendered raw via the local_assigns hash %> +<%= raw local_assigns[:display_text] %> + +<%# GOOD: A local rendered with default escaping via the local_assigns hash %> +<%= local_assigns[:display_text] %> diff --git a/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/bars/show.html.erb b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/bars/show.html.erb new file mode 100644 index 00000000000..0df7af11039 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/bars/show.html.erb @@ -0,0 +1,71 @@ +<%# BAD: An instance variable rendered without escaping %> +website + +<%# BAD: A local rendered raw as a local variable %> +<%= raw display_text %> + +<%# BAD: A local rendered raw via the local_assigns hash %> +<%= raw local_assigns[:display_text] %> + +<% key = :display_text %> +<%# BAD: A local rendered raw via the locals_assigns hash %> +<%= raw local_assigns[key] %> + +
      +<% for key in [:display_text, :safe_text] do %> + <%# BAD: A local rendered raw via the locals hash %> + <%# TODO: we miss that `key` can take `:display_text` as a value here %> +
    • <%= raw local_assigns[key] %>
    • +<% end %> +
    + +<%# GOOD: A local rendered with default escaping via the local_assigns hash %> +<%= local_assigns[display_text] %> + +<%# GOOD: default escaping of rendered text %> +<%= + full_text = prefix + local_assigns[:display_text] + full_text +%> + +<%# GOOD: default escaping of rendered text (from instance var) %> +<%= @instance_text %> + +<%# BAD: html_safe marks string as not requiring HTML escaping %> +<%= + display_text.html_safe +%> + +<%# BAD: html_safe marks string as not requiring HTML escaping %> +<%= + @instance_text.html_safe +%> + +<%= render partial: 'foo/bars/widget', locals: { display_text: "widget_" + display_text } %> + +<%# BAD: user_name is a helper method that returns unsanitized user-input %> +<%= user_name.html_safe %> + +<%# BAD: user_name_memo is a helper method that returns unsanitized user-input %> +<%# TODO: we miss this because the return value from user_name_memo is not properly linked to this call %> +<%= user_name_memo.html_safe %> + +<%# BAD: unsanitized user-input should not be passed to link_to as the URL %> +<%= link_to "user website", params[:website] %> + +<%# BAD: unsanitized user-input should not be passed to link_to as the URL %> +<%= link_to params[:website], class: "user-link" do %> + user website +<% end %> + +<%# GOOD: @safe_foo is a hardcoded string here at runtime %> +<%= @safe_foo.html_safe %> + +<%# GOOD: @html_escaped is manually escaped in the controller %> +<%= @html_escaped.html_safe %> + +<%# GOOD: @html_escaped is manually escaped in the controller %> +<%= + html_escaped_in_template = h params[:text] + html_escaped_in_template.html_safe +%> diff --git a/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/bars/show_unused.html.erb b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/bars/show_unused.html.erb new file mode 100644 index 00000000000..35ad37da173 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/bars/show_unused.html.erb @@ -0,0 +1,5 @@ +<%# This file is not rendered. There should be no flow into it. %> + +<%= display_text.html_safe %> +<%= local_assigns[:display_text].html_safe %> +<%= @instance_text.html_safe %> diff --git a/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show.html.erb b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show.html.erb new file mode 100644 index 00000000000..01049437529 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show.html.erb @@ -0,0 +1,84 @@ +<%# BAD: A local rendered raw as a local variable %> +<%= raw display_text %> + +<%# BAD: A local rendered raw via the local_assigns hash %> +<%= raw local_assigns[:display_text] %> + +<% key = :display_text %> +<%# BAD: A local rendered raw via the locals_assigns hash %> +<%= raw local_assigns[key] %> + +
      +<% for key in [:display_text, :safe_text] do %> + <%# BAD: A local rendered raw via the locals hash %> + <%# TODO: we miss that `key` can take `:display_text` as a value here %> +
    • <%= raw local_assigns[key] %>
    • +<% end %> +
    + +<%# GOOD: A local rendered with default escaping via the local_assigns hash %> +<%= local_assigns[display_text] %> + +<%# GOOD: default escaping of rendered text %> +<%= + full_text = prefix + local_assigns[:display_text] + full_text +%> + +<%# GOOD: default escaping of rendered text (from instance var) %> +<%= @instance_text %> + +<%# BAD: html_safe marks string as not requiring HTML escaping %> +<%= + display_text.html_safe +%> + +<%# BAD: html_safe marks string as not requiring HTML escaping %> +<%= + @instance_text.html_safe +%> + +<%= render partial: 'foo/bars/widget', locals: { display_text: "widget_" + display_text } %> + +<%# BAD: user_name_handle is a helper method that returns unsanitized database content %> +<%= user_name_handle.html_safe %> + +<%# BAD: Direct to a database value without escaping %> +<%= @user.handle.html_safe %> + +<%# BAD: Indirect to a database value without escaping %> +<%= @user.raw_name.html_safe %> + +<%# GOOD: Direct to a database value with escaping %> +<%= @user.handle %> + +<%# GOOD: @safe_user_handle is manually escaped in the controller %> +<%= @safe_user_handle %> + +<%# GOOD: object_id is a built-in method, not an ORM access method %> +<%= @user.object_id.html_safe %> + +<%# BAD: Direct to a database value without escaping %> +<%= + some_user = User.find 1 + some_user.handle.html_safe +%> + +<%# BAD: Indirect to a database value without escaping %> +<%= + some_user = User.find 1 + some_user.raw_name.html_safe +%> + +<%# GOOD: Direct to a database value with escaping %> +<%= + some_user = User.find 1 + some_user.handle +%> + +<%# BAD: Indirect to a database value without escaping %> +<%# TODO: we do not detect that `display_name` can return a DB field %> +<%= @user.display_name.html_safe %> + +<%# BAD: Indirect to a database value without escaping %> +<%= @other_user_raw_name.html_safe %> diff --git a/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show_unused.html.erb b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show_unused.html.erb new file mode 100644 index 00000000000..35ad37da173 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show_unused.html.erb @@ -0,0 +1,5 @@ +<%# This file is not rendered. There should be no flow into it. %> + +<%= display_text.html_safe %> +<%= local_assigns[:display_text].html_safe %> +<%= @instance_text.html_safe %> diff --git a/ruby/ql/test/query-tests/security/cwe-089/ActiveRecordInjection.rb b/ruby/ql/test/query-tests/security/cwe-089/ActiveRecordInjection.rb new file mode 100644 index 00000000000..9c314a82b34 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-089/ActiveRecordInjection.rb @@ -0,0 +1,139 @@ +class UserGroup < ActiveRecord::Base + has_many :users +end + +class User < ApplicationRecord + belongs_to :user_group + + def self.authenticate(name, pass) + # BAD: possible untrusted input interpolated into SQL fragment + find(:first, :conditions => "name='#{name}' and pass='#{pass}'") + end + + def self.from(user_group_id) + # GOOD: `find_by` with hash argument + UserGroup.find_by(id: user_group_id).users + end +end + +class Admin < User + def self.delete_by(condition = nil) + # BAD: `delete_by overrides an ActiveRecord method, but doesn't perform + # any validation before passing its arguments on to another ActiveRecord method + destroy_by(condition) + end +end + +class FooController < ActionController::Base + + MAX_USER_ID = 100_000 + + # A string tainted by user input is inserted into an SQL query + def some_request_handler + # BAD: executes `SELECT AVG(#{params[:column]}) FROM "users"` + # where `params[:column]` is unsanitized + User.calculate(:average, params[:column]) + + # BAD: executes `SELECT MAX(#{params[:column]}) FROM "users"` + # where `params[:column]` is unsanitized + User.maximum(params[:column]) + + # BAD: executes `DELETE FROM "users" WHERE (id = '#{params[:id]}')` + # where `params[:id]` is unsanitized + User.delete_by("id = '#{params[:id]}'") + + # BAD: executes `DELETE FROM "users" WHERE (id = '#{params[:id]}')` + # where `params[:id]` is unsanitized + # (in Rails < 4.0) + User.delete_all("id = '#{params[:id]}'") + + # BAD: executes `SELECT "users".* FROM "users" WHERE (id = '#{params[:id]}')` + # where `params[:id]` is unsanitized + User.destroy_by(["id = '#{params[:id]}'"]) + + # BAD: executes `SELECT "users".* FROM "users" WHERE (id = '#{params[:id]}')` + # where `params[:id]` is unsanitized + # (in Rails < 4.0) + User.destroy_all(["id = '#{params[:id]}'"]) + + # BAD: executes `SELECT "users".* FROM "users" WHERE id BETWEEN '#{params[:min_id]}' AND 100000` + # where `params[:min_id]` is unsanitized + User.where(<<-SQL, MAX_USER_ID) + id BETWEEN '#{params[:min_id]}' AND ? + SQL + + # BAD: chained method case + # executes `SELECT "users".* FROM "users" WHERE (NOT (user_id = 'params[:id]'))` + # where `params[:id]` is unsanitized + User.where.not("user.id = '#{params[:id]}'") + + User.authenticate(params[:name], params[:pass]) + + # BAD: executes `SELECT "users".* FROM "users" WHERE (id = '#{params[:id]}')` LIMIT 1 + # where `params[:id]` is unsanitized + User.find_or_initialize_by("id = '#{params[:id]}'") + + user = User.first + # BAD: executes `SELECT "users".* FROM "users" WHERE id = 1 LIMIT 1 #{params[:lock]}` + # where `params[:lock]` is unsanitized + user.reload(lock: params[:lock]) + + # BAD: executes `SELECT #{params[:column]} FROM "users"` + # where `params[:column]` is unsanitized + User.select(params[:column]) + User.reselect(params[:column]) + + # BAD: executes `SELECT "users".* FROM "users" WHERE (#{params[:condition]})` + # where `params[:condition]` is unsanitized + User.rewhere(params[:condition]) + + # BAD: executes `UPDATE "users" SET #{params[:fields]}` + # where `params[:fields]` is unsanitized + User.update_all(params[:fields]) + end +end + +class BarController < ApplicationController + def some_other_request_handler + ps = params + uid = ps[:id] + uidEq = "= '#{uid}'" + + # BAD: executes `DELETE FROM "users" WHERE (id = #{uid})` + # where `uid` is unsantized + User.delete_by("id " + uidEq) + end + + def safe_paths + dir = params[:order] + # GOOD: barrier guard prevents taint flow + if dir == "ASC" + User.order("name #{dir}") + else + dir = "DESC" + User.order("name #{dir}") + end + # TODO: a more idiomatic form of this guard is the following: + # dir = "DESC" unless dir == "ASC" + # but our taint tracking can't (yet) handle that properly + + name = params[:user_name] + # GOOD: barrier guard prevents taint flow + if %w(alice bob charlie).include? name + User.find_by("username = #{name}") + end + + name = params[:user_name] + # GOOD: hash arguments are sanitized by ActiveRecord + User.find_by(user_name: name) + + # OK: `find` method is overridden in `User` + User.find(params[:user_group]) + end +end + +class BazController < BarController + def yet_another_handler + Admin.delete_by(params[:admin_condition]) + end +end diff --git a/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected new file mode 100644 index 00000000000..aca755ba998 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected @@ -0,0 +1,83 @@ +edges +| ActiveRecordInjection.rb:8:25:8:28 | name : | ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | +| ActiveRecordInjection.rb:8:31:8:34 | pass : | ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | +| ActiveRecordInjection.rb:20:22:20:30 | condition : | ActiveRecordInjection.rb:23:16:23:24 | condition | +| ActiveRecordInjection.rb:35:30:35:35 | call to params : | ActiveRecordInjection.rb:35:30:35:44 | ...[...] | +| ActiveRecordInjection.rb:39:18:39:23 | call to params : | ActiveRecordInjection.rb:39:18:39:32 | ...[...] | +| ActiveRecordInjection.rb:43:29:43:34 | call to params : | ActiveRecordInjection.rb:43:20:43:42 | "id = '#{...}'" | +| ActiveRecordInjection.rb:48:30:48:35 | call to params : | ActiveRecordInjection.rb:48:21:48:43 | "id = '#{...}'" | +| ActiveRecordInjection.rb:52:31:52:36 | call to params : | ActiveRecordInjection.rb:52:22:52:44 | "id = '#{...}'" | +| ActiveRecordInjection.rb:57:32:57:37 | call to params : | ActiveRecordInjection.rb:57:23:57:45 | "id = '#{...}'" | +| ActiveRecordInjection.rb:62:21:62:26 | call to params : | ActiveRecordInjection.rb:61:16:61:21 | <<-SQL | +| ActiveRecordInjection.rb:68:34:68:39 | call to params : | ActiveRecordInjection.rb:68:20:68:47 | "user.id = '#{...}'" | +| ActiveRecordInjection.rb:70:23:70:28 | call to params : | ActiveRecordInjection.rb:70:23:70:35 | ...[...] : | +| ActiveRecordInjection.rb:70:23:70:35 | ...[...] : | ActiveRecordInjection.rb:8:25:8:28 | name : | +| ActiveRecordInjection.rb:70:38:70:43 | call to params : | ActiveRecordInjection.rb:70:38:70:50 | ...[...] : | +| ActiveRecordInjection.rb:70:38:70:50 | ...[...] : | ActiveRecordInjection.rb:8:31:8:34 | pass : | +| ActiveRecordInjection.rb:74:41:74:46 | call to params : | ActiveRecordInjection.rb:74:32:74:54 | "id = '#{...}'" | +| ActiveRecordInjection.rb:83:17:83:22 | call to params : | ActiveRecordInjection.rb:83:17:83:31 | ...[...] | +| ActiveRecordInjection.rb:84:19:84:24 | call to params : | ActiveRecordInjection.rb:84:19:84:33 | ...[...] | +| ActiveRecordInjection.rb:88:18:88:23 | call to params : | ActiveRecordInjection.rb:88:18:88:35 | ...[...] | +| ActiveRecordInjection.rb:92:21:92:26 | call to params : | ActiveRecordInjection.rb:92:21:92:35 | ...[...] | +| ActiveRecordInjection.rb:98:10:98:15 | call to params : | ActiveRecordInjection.rb:104:20:104:32 | ... + ... | +| ActiveRecordInjection.rb:137:21:137:26 | call to params : | ActiveRecordInjection.rb:137:21:137:44 | ...[...] : | +| ActiveRecordInjection.rb:137:21:137:44 | ...[...] : | ActiveRecordInjection.rb:20:22:20:30 | condition : | +nodes +| ActiveRecordInjection.rb:8:25:8:28 | name : | semmle.label | name : | +| ActiveRecordInjection.rb:8:31:8:34 | pass : | semmle.label | pass : | +| ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | semmle.label | "name='#{...}' and pass='#{...}'" | +| ActiveRecordInjection.rb:20:22:20:30 | condition : | semmle.label | condition : | +| ActiveRecordInjection.rb:23:16:23:24 | condition | semmle.label | condition | +| ActiveRecordInjection.rb:35:30:35:35 | call to params : | semmle.label | call to params : | +| ActiveRecordInjection.rb:35:30:35:44 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:39:18:39:23 | call to params : | semmle.label | call to params : | +| ActiveRecordInjection.rb:39:18:39:32 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:43:20:43:42 | "id = '#{...}'" | semmle.label | "id = '#{...}'" | +| ActiveRecordInjection.rb:43:29:43:34 | call to params : | semmle.label | call to params : | +| ActiveRecordInjection.rb:48:21:48:43 | "id = '#{...}'" | semmle.label | "id = '#{...}'" | +| ActiveRecordInjection.rb:48:30:48:35 | call to params : | semmle.label | call to params : | +| ActiveRecordInjection.rb:52:22:52:44 | "id = '#{...}'" | semmle.label | "id = '#{...}'" | +| ActiveRecordInjection.rb:52:31:52:36 | call to params : | semmle.label | call to params : | +| ActiveRecordInjection.rb:57:23:57:45 | "id = '#{...}'" | semmle.label | "id = '#{...}'" | +| ActiveRecordInjection.rb:57:32:57:37 | call to params : | semmle.label | call to params : | +| ActiveRecordInjection.rb:61:16:61:21 | <<-SQL | semmle.label | <<-SQL | +| ActiveRecordInjection.rb:62:21:62:26 | call to params : | semmle.label | call to params : | +| ActiveRecordInjection.rb:68:20:68:47 | "user.id = '#{...}'" | semmle.label | "user.id = '#{...}'" | +| ActiveRecordInjection.rb:68:34:68:39 | call to params : | semmle.label | call to params : | +| ActiveRecordInjection.rb:70:23:70:28 | call to params : | semmle.label | call to params : | +| ActiveRecordInjection.rb:70:23:70:35 | ...[...] : | semmle.label | ...[...] : | +| ActiveRecordInjection.rb:70:38:70:43 | call to params : | semmle.label | call to params : | +| ActiveRecordInjection.rb:70:38:70:50 | ...[...] : | semmle.label | ...[...] : | +| ActiveRecordInjection.rb:74:32:74:54 | "id = '#{...}'" | semmle.label | "id = '#{...}'" | +| ActiveRecordInjection.rb:74:41:74:46 | call to params : | semmle.label | call to params : | +| ActiveRecordInjection.rb:83:17:83:22 | call to params : | semmle.label | call to params : | +| ActiveRecordInjection.rb:83:17:83:31 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:84:19:84:24 | call to params : | semmle.label | call to params : | +| ActiveRecordInjection.rb:84:19:84:33 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:88:18:88:23 | call to params : | semmle.label | call to params : | +| ActiveRecordInjection.rb:88:18:88:35 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:92:21:92:26 | call to params : | semmle.label | call to params : | +| ActiveRecordInjection.rb:92:21:92:35 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:98:10:98:15 | call to params : | semmle.label | call to params : | +| ActiveRecordInjection.rb:104:20:104:32 | ... + ... | semmle.label | ... + ... | +| ActiveRecordInjection.rb:137:21:137:26 | call to params : | semmle.label | call to params : | +| ActiveRecordInjection.rb:137:21:137:44 | ...[...] : | semmle.label | ...[...] : | +subpaths +#select +| ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | ActiveRecordInjection.rb:70:23:70:28 | call to params : | ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | This SQL query depends on $@. | ActiveRecordInjection.rb:70:23:70:28 | call to params | a user-provided value | +| ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | ActiveRecordInjection.rb:70:38:70:43 | call to params : | ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | This SQL query depends on $@. | ActiveRecordInjection.rb:70:38:70:43 | call to params | a user-provided value | +| ActiveRecordInjection.rb:23:16:23:24 | condition | ActiveRecordInjection.rb:137:21:137:26 | call to params : | ActiveRecordInjection.rb:23:16:23:24 | condition | This SQL query depends on $@. | ActiveRecordInjection.rb:137:21:137:26 | call to params | a user-provided value | +| ActiveRecordInjection.rb:35:30:35:44 | ...[...] | ActiveRecordInjection.rb:35:30:35:35 | call to params : | ActiveRecordInjection.rb:35:30:35:44 | ...[...] | This SQL query depends on $@. | ActiveRecordInjection.rb:35:30:35:35 | call to params | a user-provided value | +| ActiveRecordInjection.rb:39:18:39:32 | ...[...] | ActiveRecordInjection.rb:39:18:39:23 | call to params : | ActiveRecordInjection.rb:39:18:39:32 | ...[...] | This SQL query depends on $@. | ActiveRecordInjection.rb:39:18:39:23 | call to params | a user-provided value | +| ActiveRecordInjection.rb:43:20:43:42 | "id = '#{...}'" | ActiveRecordInjection.rb:43:29:43:34 | call to params : | ActiveRecordInjection.rb:43:20:43:42 | "id = '#{...}'" | This SQL query depends on $@. | ActiveRecordInjection.rb:43:29:43:34 | call to params | a user-provided value | +| ActiveRecordInjection.rb:48:21:48:43 | "id = '#{...}'" | ActiveRecordInjection.rb:48:30:48:35 | call to params : | ActiveRecordInjection.rb:48:21:48:43 | "id = '#{...}'" | This SQL query depends on $@. | ActiveRecordInjection.rb:48:30:48:35 | call to params | a user-provided value | +| ActiveRecordInjection.rb:52:22:52:44 | "id = '#{...}'" | ActiveRecordInjection.rb:52:31:52:36 | call to params : | ActiveRecordInjection.rb:52:22:52:44 | "id = '#{...}'" | This SQL query depends on $@. | ActiveRecordInjection.rb:52:31:52:36 | call to params | a user-provided value | +| ActiveRecordInjection.rb:57:23:57:45 | "id = '#{...}'" | ActiveRecordInjection.rb:57:32:57:37 | call to params : | ActiveRecordInjection.rb:57:23:57:45 | "id = '#{...}'" | This SQL query depends on $@. | ActiveRecordInjection.rb:57:32:57:37 | call to params | a user-provided value | +| ActiveRecordInjection.rb:61:16:61:21 | <<-SQL | ActiveRecordInjection.rb:62:21:62:26 | call to params : | ActiveRecordInjection.rb:61:16:61:21 | <<-SQL | This SQL query depends on $@. | ActiveRecordInjection.rb:62:21:62:26 | call to params | a user-provided value | +| ActiveRecordInjection.rb:68:20:68:47 | "user.id = '#{...}'" | ActiveRecordInjection.rb:68:34:68:39 | call to params : | ActiveRecordInjection.rb:68:20:68:47 | "user.id = '#{...}'" | This SQL query depends on $@. | ActiveRecordInjection.rb:68:34:68:39 | call to params | a user-provided value | +| ActiveRecordInjection.rb:74:32:74:54 | "id = '#{...}'" | ActiveRecordInjection.rb:74:41:74:46 | call to params : | ActiveRecordInjection.rb:74:32:74:54 | "id = '#{...}'" | This SQL query depends on $@. | ActiveRecordInjection.rb:74:41:74:46 | call to params | a user-provided value | +| ActiveRecordInjection.rb:83:17:83:31 | ...[...] | ActiveRecordInjection.rb:83:17:83:22 | call to params : | ActiveRecordInjection.rb:83:17:83:31 | ...[...] | This SQL query depends on $@. | ActiveRecordInjection.rb:83:17:83:22 | call to params | a user-provided value | +| ActiveRecordInjection.rb:84:19:84:33 | ...[...] | ActiveRecordInjection.rb:84:19:84:24 | call to params : | ActiveRecordInjection.rb:84:19:84:33 | ...[...] | This SQL query depends on $@. | ActiveRecordInjection.rb:84:19:84:24 | call to params | a user-provided value | +| ActiveRecordInjection.rb:88:18:88:35 | ...[...] | ActiveRecordInjection.rb:88:18:88:23 | call to params : | ActiveRecordInjection.rb:88:18:88:35 | ...[...] | This SQL query depends on $@. | ActiveRecordInjection.rb:88:18:88:23 | call to params | a user-provided value | +| ActiveRecordInjection.rb:92:21:92:35 | ...[...] | ActiveRecordInjection.rb:92:21:92:26 | call to params : | ActiveRecordInjection.rb:92:21:92:35 | ...[...] | This SQL query depends on $@. | ActiveRecordInjection.rb:92:21:92:26 | call to params | a user-provided value | +| ActiveRecordInjection.rb:104:20:104:32 | ... + ... | ActiveRecordInjection.rb:98:10:98:15 | call to params : | ActiveRecordInjection.rb:104:20:104:32 | ... + ... | This SQL query depends on $@. | ActiveRecordInjection.rb:98:10:98:15 | call to params | a user-provided value | diff --git a/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.qlref b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.qlref new file mode 100644 index 00000000000..bcb55c8510f --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.qlref @@ -0,0 +1 @@ +queries/security/cwe-089/SqlInjection.ql diff --git a/ruby/ql/test/query-tests/security/cwe-094/CodeInjection.expected b/ruby/ql/test/query-tests/security/cwe-094/CodeInjection.expected new file mode 100644 index 00000000000..834fd1d1db7 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-094/CodeInjection.expected @@ -0,0 +1,16 @@ +edges +| CodeInjection.rb:3:12:3:17 | call to params : | CodeInjection.rb:6:10:6:13 | code | +| CodeInjection.rb:3:12:3:17 | call to params : | CodeInjection.rb:18:20:18:23 | code | +| CodeInjection.rb:3:12:3:17 | call to params : | CodeInjection.rb:21:21:21:24 | code | +nodes +| CodeInjection.rb:3:12:3:17 | call to params : | semmle.label | call to params : | +| CodeInjection.rb:6:10:6:13 | code | semmle.label | code | +| CodeInjection.rb:9:10:9:15 | call to params | semmle.label | call to params | +| CodeInjection.rb:18:20:18:23 | code | semmle.label | code | +| CodeInjection.rb:21:21:21:24 | code | semmle.label | code | +subpaths +#select +| CodeInjection.rb:6:10:6:13 | code | CodeInjection.rb:3:12:3:17 | call to params : | CodeInjection.rb:6:10:6:13 | code | This code execution depends on $@. | CodeInjection.rb:3:12:3:17 | call to params | a user-provided value | +| CodeInjection.rb:9:10:9:15 | call to params | CodeInjection.rb:9:10:9:15 | call to params | CodeInjection.rb:9:10:9:15 | call to params | This code execution depends on $@. | CodeInjection.rb:9:10:9:15 | call to params | a user-provided value | +| CodeInjection.rb:18:20:18:23 | code | CodeInjection.rb:3:12:3:17 | call to params : | CodeInjection.rb:18:20:18:23 | code | This code execution depends on $@. | CodeInjection.rb:3:12:3:17 | call to params | a user-provided value | +| CodeInjection.rb:21:21:21:24 | code | CodeInjection.rb:3:12:3:17 | call to params : | CodeInjection.rb:21:21:21:24 | code | This code execution depends on $@. | CodeInjection.rb:3:12:3:17 | call to params | a user-provided value | diff --git a/ruby/ql/test/query-tests/security/cwe-094/CodeInjection.qlref b/ruby/ql/test/query-tests/security/cwe-094/CodeInjection.qlref new file mode 100644 index 00000000000..6dcbcb4448e --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-094/CodeInjection.qlref @@ -0,0 +1 @@ +queries/security/cwe-094/CodeInjection.ql diff --git a/ruby/ql/test/query-tests/security/cwe-094/CodeInjection.rb b/ruby/ql/test/query-tests/security/cwe-094/CodeInjection.rb new file mode 100644 index 00000000000..80ee5bee133 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-094/CodeInjection.rb @@ -0,0 +1,53 @@ +class UsersController < ActionController::Base + def create + code = params[:code] + + # BAD + eval(code) + + # BAD + eval(params) + + # GOOD - user input is in second argument, which is not evaluated as Ruby code + send(:sanitize, params[:code]) + + # GOOD + Foo.new.bar(code) + + # BAD + Foo.class_eval(code) + + # BAD + Foo.module_eval(code) + + # GOOD + Bar.class_eval(code) + end + + def update + # GOOD + eval("foo") + end + + private + + def sanitize(code) + true + end +end + +class Foo + def eval(x) + true + end + + def bar(x) + eval(x) + end +end + +class Bar + def self.class_eval(x) + true + end +end \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/ANodeBlog-LICENSE b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/ANodeBlog-LICENSE new file mode 100644 index 00000000000..8dada3edaf5 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/ANodeBlog-LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/CodeMirror-LICENSE b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/CodeMirror-LICENSE new file mode 100644 index 00000000000..ff7db4b99f5 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/CodeMirror-LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (C) 2017 by Marijn Haverbeke and others + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/Prism-LICENSE b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/Prism-LICENSE new file mode 100644 index 00000000000..528949f42d9 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/Prism-LICENSE @@ -0,0 +1,21 @@ +MIT LICENSE + +Copyright (c) 2012 Lea Verou + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/Prototype.js-LICENSE b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/Prototype.js-LICENSE new file mode 100644 index 00000000000..05789cf0190 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/Prototype.js-LICENSE @@ -0,0 +1,16 @@ +Copyright (c) 2005-2010 Sam Stephenson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/ReDoS.expected b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/ReDoS.expected new file mode 100644 index 00000000000..7cb5759e733 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/ReDoS.expected @@ -0,0 +1,93 @@ +| tst.rb:4:14:4:28 | (?:__\|[\\s\\S])+? | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '__'. | +| tst.rb:4:38:4:54 | (?:\\*\\*\|[\\s\\S])+? | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '**'. | +| tst.rb:19:20:19:39 | (?:[^"\\\\]\|\\\\\\\\\|\\\\.)+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\\\\\\\'. | +| tst.rb:19:43:19:62 | (?:[^'\\\\]\|\\\\\\\\\|\\\\.)+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\\\\\\\'. | +| tst.rb:19:67:19:86 | (?:[^)\\\\]\|\\\\\\\\\|\\\\.)+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\\\\\\\'. | +| tst.rb:31:50:31:51 | .* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\|\|\\n'. | +| tst.rb:36:19:36:28 | (\\\\\\/\|.)*? | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\\\/'. | +| tst.rb:41:23:41:24 | .* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '#'. | +| tst.rb:47:27:47:29 | .*? | This part of the regular expression may cause exponential backtracking on strings starting with '"' and containing many repetitions of '""'. | +| tst.rb:47:33:47:35 | .*? | This part of the regular expression may cause exponential backtracking on strings starting with ''' and containing many repetitions of ''''. | +| tst.rb:52:33:52:35 | .*? | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of ']['. | +| tst.rb:52:66:52:68 | .*? | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of ']['. | +| tst.rb:58:11:58:16 | [a-z]+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| tst.rb:59:11:59:16 | [a-z]* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| tst.rb:60:39:60:50 | [a-zA-Z0-9]+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. | +| tst.rb:61:12:61:19 | ([a-z])+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'aa'. | +| tst.rb:66:12:66:27 | [\\w#:.~>+()\\s-]+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\t'. | +| tst.rb:66:34:66:36 | .*? | This part of the regular expression may cause exponential backtracking on strings starting with '[' and containing many repetitions of ']['. | +| tst.rb:71:15:71:22 | (\\\\?.)*? | This part of the regular expression may cause exponential backtracking on strings starting with '"' and containing many repetitions of '\\\\a'. | +| tst.rb:74:10:74:17 | (b\|a?b)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'b'. | +| tst.rb:77:10:77:17 | (a\|aa?)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| tst.rb:83:10:83:16 | (.\|\\n)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\n'. | +| tst.rb:95:11:95:24 | ([\\S\\s]\|[^a])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '`'. | +| tst.rb:101:11:101:19 | (.\|[^a])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '`'. | +| tst.rb:107:11:107:19 | (b\|[^a])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'b'. | +| tst.rb:110:11:110:19 | (G\|[^a])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'G'. | +| tst.rb:113:11:113:23 | ([0-9]\|[^a])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. | +| tst.rb:125:11:125:24 | ([a-z]\|[d-h])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'd'. | +| tst.rb:128:11:128:26 | ([^a-z]\|[^0-9])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '/'. | +| tst.rb:131:11:131:21 | (\\d\|[0-9])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. | +| tst.rb:134:11:134:18 | (\\s\|\\s)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of ' '. | +| tst.rb:137:11:137:17 | (\\w\|G)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'G'. | +| tst.rb:143:11:143:18 | (\\d\|\\w)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. | +| tst.rb:146:11:146:17 | (\\d\|5)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '5'. | +| tst.rb:155:11:155:20 | (\\f\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'f'. | +| tst.rb:158:11:158:18 | (\\W\|\\D)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of ' '. | +| tst.rb:161:11:161:18 | (\\S\|\\w)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. | +| tst.rb:164:11:164:20 | (\\S\|[\\w])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. | +| tst.rb:167:11:167:23 | (1s\|[\\da-z])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '1s'. | +| tst.rb:170:11:170:19 | (0\|[\\d])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. | +| tst.rb:173:12:173:16 | [\\d]+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. | +| tst.rb:185:12:185:17 | [^>a]+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '='. | +| tst.rb:188:13:188:15 | \\s* | This part of the regular expression may cause exponential backtracking on strings starting with '\\n' and containing many repetitions of '\\n'. | +| tst.rb:191:14:191:16 | \\s+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of ' '. | +| tst.rb:194:64:194:75 | [ a-zA-Z{}]+ | This part of the regular expression may cause exponential backtracking on strings starting with '{[A(A)A:' and containing many repetitions of ' A:'. | +| tst.rb:194:77:194:78 | ,? | This part of the regular expression may cause exponential backtracking on strings starting with '{[A(A)A: ' and containing many repetitions of ',A: '. | +| tst.rb:197:11:197:12 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| tst.rb:197:14:197:15 | b+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'b'. | +| tst.rb:200:12:200:18 | (a+a?)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| tst.rb:200:13:200:14 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| tst.rb:203:11:203:12 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| tst.rb:209:11:209:12 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| tst.rb:215:11:215:13 | \\n+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\n'. | +| tst.rb:218:11:218:15 | [^X]+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'W'. | +| tst.rb:221:16:221:16 | b | This part of the regular expression may cause exponential backtracking on strings starting with 'W' and containing many repetitions of 'bW'. | +| tst.rb:227:16:227:16 | b | This part of the regular expression may cause exponential backtracking on strings starting with 'W' and containing many repetitions of 'bW'. | +| tst.rb:239:13:239:13 | b | This part of the regular expression may cause exponential backtracking on strings starting with 'a' and containing many repetitions of 'ba'. | +| tst.rb:245:11:245:17 | [\\n\\s]+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\n'. | +| tst.rb:254:11:254:13 | \\w* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'foobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbaz'. | +| tst.rb:254:23:254:25 | \\w* | This part of the regular expression may cause exponential backtracking on strings starting with 'foobarbaz' and containing many repetitions of 'foobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbaz'. | +| tst.rb:254:35:254:37 | \\w* | This part of the regular expression may cause exponential backtracking on strings starting with 'foobarbazfoobarbaz' and containing many repetitions of 'foobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbaz'. | +| tst.rb:254:47:254:49 | \\w* | This part of the regular expression may cause exponential backtracking on strings starting with 'foobarbazfoobarbazfoobarbaz' and containing many repetitions of 'foobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbaz'. | +| tst.rb:257:10:257:112 | (.thisisagoddamnlongstringforstresstestingthequery\|\\sthisisagoddamnlongstringforstresstestingthequery)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of ' thisisagoddamnlongstringforstresstestingthequery'. | +| tst.rb:260:10:260:73 | (thisisagoddamnlongstringforstresstestingthequery\|this\\w+query)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'thisisagoddamnlongstringforstresstestingthequery'. | +| tst.rb:260:64:260:66 | \\w+ | This part of the regular expression may cause exponential backtracking on strings starting with 'this' and containing many repetitions of 'aquerythis'. | +| tst.rb:272:17:272:18 | b+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'b'. | +| tst.rb:275:34:275:36 | \\s* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '"" a='. | +| tst.rb:281:12:281:13 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| tst.rb:284:12:284:13 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| tst.rb:290:12:290:13 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| tst.rb:293:21:293:22 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| tst.rb:299:86:299:87 | e+ | This part of the regular expression may cause exponential backtracking on strings starting with ';00000000000000' and containing many repetitions of 'e'. | +| tst.rb:302:14:302:15 | c+ | This part of the regular expression may cause exponential backtracking on strings starting with 'ab' and containing many repetitions of 'c'. | +| tst.rb:305:14:305:16 | \\s+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of ' '. | +| tst.rb:308:12:308:21 | ([^\\/]\|X)+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'X'. | +| tst.rb:311:16:311:20 | [^Y]+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'Xx'. | +| tst.rb:314:11:314:12 | a* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| tst.rb:317:14:317:19 | [\\w-]* | This part of the regular expression may cause exponential backtracking on strings starting with 'foo' and containing many repetitions of '-'. | +| tst.rb:320:11:320:15 | (ab)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'ab'. | +| tst.rb:323:10:323:16 | (a?a?)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| tst.rb:332:10:332:18 | (?:a\|a?)+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| tst.rb:338:13:338:41 | (([a-c]\|[c-d])T(e?e?e?e?\|X))+ | This part of the regular expression may cause exponential backtracking on strings starting with 'PRE' and containing many repetitions of 'cTX'. | +| tst.rb:341:12:341:15 | (a)+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'aa'. | +| tst.rb:344:12:344:13 | b+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'bb'. | +| tst.rb:350:11:350:12 | a* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| tst.rb:351:11:351:12 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| tst.rb:352:11:352:12 | a* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| tst.rb:353:11:353:12 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| tst.rb:360:11:360:26 | ((?:a{\|-)\|\\w\\{)+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a{'. | +| tst.rb:361:11:361:29 | ((?:a{0\|-)\|\\w\\{\\d)+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a{0'. | +| tst.rb:362:11:362:31 | ((?:a{0,\|-)\|\\w\\{\\d,)+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a{0,'. | +| tst.rb:363:11:363:34 | ((?:a{0,2\|-)\|\\w\\{\\d,\\d)+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a{0,2'. | +| tst.rb:369:12:369:22 | (\\u0061\|a)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | diff --git a/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/ReDoS.qlref b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/ReDoS.qlref new file mode 100644 index 00000000000..7f4557181d7 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/ReDoS.qlref @@ -0,0 +1 @@ +queries/security/cwe-1333/ReDoS.ql diff --git a/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/brace-expansion-LICENSE b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/brace-expansion-LICENSE new file mode 100644 index 00000000000..de3226673c3 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/brace-expansion-LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2013 Julian Gruber + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/jest-LICENSE b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/jest-LICENSE new file mode 100644 index 00000000000..10e779c44ac --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/jest-LICENSE @@ -0,0 +1,23 @@ +MIT License + +For Jest software + +Copyright (c) 2014-present, Facebook, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/knockout-LICENSE b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/knockout-LICENSE new file mode 100644 index 00000000000..08a07b1ae8d --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/knockout-LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) - http://www.opensource.org/licenses/mit-license.php + +Copyright (c) Steven Sanderson, the Knockout.js team, and other contributors +http://knockoutjs.com/ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/marked-LICENSE b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/marked-LICENSE new file mode 100644 index 00000000000..64b41a0e463 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/marked-LICENSE @@ -0,0 +1,43 @@ +# License information + +## Contribution License Agreement + +If you contribute code to this project, you are implicitly allowing your code +to be distributed under the MIT license. You are also implicitly verifying that +all code is your original work. `` + +## Marked + +Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +## Markdown + +Copyright © 2004, John Gruber +http://daringfireball.net/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name “Markdown” nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +This software is provided by the copyright holders and contributors “as is” and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage. diff --git a/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/tst.rb b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/tst.rb new file mode 100644 index 00000000000..01f8e8a83bc --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-1333-exponential-redos/tst.rb @@ -0,0 +1,372 @@ +# NOT GOOD; attack: "_" + "__".repeat(100) +# Adapted from marked (https://github.com/markedjs/marked), which is licensed +# under the MIT license; see file marked-LICENSE. +bad1 = /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/ + +# GOOD +# Adapted from marked (https://github.com/markedjs/marked), which is licensed +# under the MIT license; see file marked-LICENSE. +good1 = /^\b_((?:__|[^_])+?)_\b|^\*((?:\*\*|[^*])+?)\*(?!\*)/ + +# GOOD - there is no witness in the end that could cause the regexp to not match +# Adapted from brace-expansion (https://github.com/juliangruber/brace-expansion), +# which is licensed under the MIT license; see file brace-expansion-LICENSE. +good2 = /(.*,)+.+/ + +# NOT GOOD; attack: " '" + "\\\\".repeat(100) +# Adapted from CodeMirror (https://github.com/codemirror/codemirror), +# which is licensed under the MIT license; see file CodeMirror-LICENSE. +bad2 = /^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/ + +# GOOD +# Adapted from lulucms2 (https://github.com/yiifans/lulucms2). +good2 = /\(\*(?:[\s\S]*?\(\*[\s\S]*?\*\))*[\s\S]*?\*\)/ + +# GOOD +# Adapted from jest (https://github.com/facebook/jest), which is licensed +# under the MIT license; see file jest-LICENSE. +good3 = /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/ + +# NOT GOOD, variant of good3; attack: "a|\n:|\n" + "||\n".repeat(100) +bad4 = /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)a/ + +# NOT GOOD; attack: "/" + "\\/a".repeat(100) +# Adapted from ANodeBlog (https://github.com/gefangshuai/ANodeBlog), +# which is licensed under the Apache License 2.0; see file ANodeBlog-LICENSE. +bad5 = /\/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$)/ + +# NOT GOOD; attack: "##".repeat(100) + "\na" +# Adapted from CodeMirror (https://github.com/codemirror/codemirror), +# which is licensed under the MIT license; see file CodeMirror-LICENSE. +bad6 = /^([\s\[\{\(]|#.*)*$/ + +# GOOD +good4 = /(\r\n|\r|\n)+/ + +# BAD - PoC: `node -e "/((?:[^\"\']|\".*?\"|\'.*?\')*?)([(,)]|$)/.test(\"'''''''''''''''''''''''''''''''''''''''''''''\\\"\");"`. It's complicated though, because the regexp still matches something, it just matches the empty-string after the attack string. +actuallyBad = /((?:[^"']|".*?"|'.*?')*?)([(,)]|$)/ + +# NOT GOOD; attack: "a" + "[]".repeat(100) + ".b\n" +# Adapted from Knockout (https://github.com/knockout/knockout), which is +# licensed under the MIT license; see file knockout-LICENSE +bad6 = /^[\_$a-z][\_$a-z0-9]*(\[.*?\])*(\.[\_$a-z][\_$a-z0-9]*(\[.*?\])*)*$/i + +# GOOD +good6 = /(a|.)*/ + +# Testing the NFA - only some of the below are detected. +bad7 = /^([a-z]+)+$/ +bad8 = /^([a-z]*)*$/ +bad9 = /^([a-zA-Z0-9])(([\\.-]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$/ +bad10 = /^(([a-z])+.)+[A-Z]([a-z])+$/ + +# NOT GOOD; attack: "[" + "][".repeat(100) + "]!" +# Adapted from Prototype.js (https://github.com/prototypejs/prototype), which +# is licensed under the MIT license; see file Prototype.js-LICENSE. +bad11 = /(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/ + +# NOT GOOD; attack: "'" + "\\a".repeat(100) + '"' +# Adapted from Prism (https://github.com/PrismJS/prism), which is licensed +# under the MIT license; see file Prism-LICENSE. +bad12 = /("|')(\\?.)*?\1/ + +# NOT GOOD +bad13 = /(b|a?b)*c/ + +# NOT GOOD +bad15 = /(a|aa?)*b/ + +# GOOD +good7 = /(.|\n)*!/ + +# NOT GOOD; attack: "\n".repeat(100) + "." +bad16 = /(.|\n)*!/m + +# GOOD +good8 = /([\w.]+)*/ + +# BAD - we don't yet parse regexps constructed from strings +bad17 = Regexp.new '(a|aa?)*b' + +# GOOD - not used as regexp +good9 = '(a|aa?)*b' + +# NOT GOOD +bad18 = /(([\S\s]|[^a])*)"/ + +# GOOD - there is no witness in the end that could cause the regexp to not match +good10 = /([^"']+)*/ + +# NOT GOOD +bad20 = /((.|[^a])*)"/ + +# GOOD +good10 = /((a|[^a])*)"/ + +# NOT GOOD +bad21 = /((b|[^a])*)"/ + +# NOT GOOD +bad22 = /((G|[^a])*)"/ + +# NOT GOOD +bad23 = /(([0-9]|[^a])*)"/ + +# BAD - missing result +bad24 = /(?:=(?:([!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+)|"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)"))?/ + +# BAD - missing result +bad25 = /"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)"/ + +# GOOD +bad26 = /"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"\\])*)"/ + +# NOT GOOD +bad27 = /(([a-z]|[d-h])*)"/ + +# NOT GOOD +bad27 = /(([^a-z]|[^0-9])*)"/ + +# NOT GOOD +bad28 = /((\d|[0-9])*)"/ + +# NOT GOOD +bad29 = /((\s|\s)*)"/ + +# NOT GOOD +bad30 = /((\w|G)*)"/ + +# GOOD +good11 = /((\s|\d)*)"/ + +# NOT GOOD +bad31 = /((\d|\w)*)"/ + +# NOT GOOD +bad32 = /((\d|5)*)"/ + +# BAD - \f is not handled correctly +bad33 = /((\s|[\f])*)"/ + +# BAD - \v is not handled correctly +bad34 = /((\s|[\v]|\\v)*)"/ + +# NOT GOOD +bad35 = /((\f|[\f])*)"/ + +# NOT GOOD +bad36 = /((\W|\D)*)"/ + +# NOT GOOD +bad37 = /((\S|\w)*)"/ + +# NOT GOOD +bad38 = /((\S|[\w])*)"/ + +# NOT GOOD +bad39 = /((1s|[\da-z])*)"/ + +# NOT GOOD +bad40 = /((0|[\d])*)"/ + +# NOT GOOD +bad41 = /(([\d]+)*)"/ + +# GOOD - there is no witness in the end that could cause the regexp to not match +good12 = /(\d+(X\d+)?)+/ + +# GOOD - there is no witness in the end that could cause the regexp to not match +good13 = /([0-9]+(X[0-9]*)?)*/ + +# GOOD +good15 = /^([^>]+)*(>|$)/ + +# NOT GOOD +bad43 = /^([^>a]+)*(>|$)/ + +# NOT GOOD +bad44 = /(\n\s*)+$/ + +# NOT GOOD +bad45 = /^(?:\s+|#.*|\(\?#[^)]*\))*(?:[?*+]|{\d+(?:,\d*)?})/ + +# NOT GOOD +bad46 = /\{\[\s*([a-zA-Z]+)\(([a-zA-Z]+)\)((\s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),?)+)*\s*\]\}/ + +# NOT GOOD +bad47 = /(a+|b+|c+)*c/ + +# NOT GOOD +bad48 = /(((a+a?)*)+b+)/ + +# NOT GOOD +bad49 = /(a+)+bbbb/ + +# GOOD +good16 = /(a+)+aaaaa*a+/ + +# NOT GOOD +bad50 = /(a+)+aaaaa$/ + +# GOOD +good17 = /(\n+)+\n\n/ + +# NOT GOOD +bad51 = /(\n+)+\n\n$/ + +# NOT GOOD +bad52 = /([^X]+)*$/ + +# NOT GOOD +bad53 = /(([^X]b)+)*$/ + +# GOOD +good18 = /(([^X]b)+)*($|[^X]b)/ + +# NOT GOOD +bad54 = /(([^X]b)+)*($|[^X]c)/ + +# GOOD +good20 = /((ab)+)*ababab/ + +# GOOD +good21 = /((ab)+)*abab(ab)*(ab)+/ + +# GOOD +good22 = /((ab)+)*/ + +# NOT GOOD +bad55 = /((ab)+)*$/ + +# GOOD +good23 = /((ab)+)*[a1][b1][a2][b2][a3][b3]/ + +# NOT GOOD +bad56 = /([\n\s]+)*(.)/ + +# GOOD - any witness passes through the accept state. +good24 = /(A*A*X)*/ + +# GOOD +good26 = /([^\\\]]+)*/ + +# NOT GOOD +bad59 = /(\w*foobarbaz\w*foobarbaz\w*foobarbaz\w*foobarbaz\s*foobarbaz\d*foobarbaz\w*)+-/ + +# NOT GOOD +bad60 = /(.thisisagoddamnlongstringforstresstestingthequery|\sthisisagoddamnlongstringforstresstestingthequery)*-/ + +# NOT GOOD +bad61 = /(thisisagoddamnlongstringforstresstestingthequery|this\w+query)*-/ + +# GOOD +good27 = /(thisisagoddamnlongstringforstresstestingthequery|imanotherbutunrelatedstringcomparedtotheotherstring)*-/ + +# GOOD +#good28 = /foo([\uDC66\uDC67]|[\uDC68\uDC69])*foo/ + +# GOOD +#good29 = /foo((\uDC66|\uDC67)|(\uDC68|\uDC69))*foo/ + +# NOT GOOD (but cannot currently construct a prefix) +bad62 = /a{2,3}(b+)+X/ + +# NOT GOOD (and a good prefix test) +bad63 = /^<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/ + +# GOOD +good30 = /(a+)*[\S\s][\S\s][\S\s]?/ + +# GOOD - but we fail to see that repeating the attack string ends in the "accept any" state (due to not parsing the range `[^]{2,3}`). +good31 = /(a+)*[\S\s]{2,3}/ + +# GOOD - but we spuriously conclude that a rejecting suffix exists (due to not parsing the range `[^]{2,}` when constructing the NFA). +good32 = /(a+)*([\S\s]{2,}|X)$/ + +# GOOD +good33 = /(a+)*([\S\s]*|X)$/ + +# NOT GOOD +bad64 = /((a+)*$|[\S\s]+)/ + +# GOOD - but still flagged. The only change compared to the above is the order of alternatives, which we don't model. +good34 = /([\S\s]+|(a+)*$)/ + +# GOOD +good35 = /((;|^)a+)+$/ + +# NOT GOOD (a good prefix test) +bad65 = /(^|;)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(e+)+f/ + +# NOT GOOD +bad66 = /^ab(c+)+$/ + +# NOT GOOD +bad67 = /(\d(\s+)*){20}/ + +# GOOD - but we spuriously conclude that a rejecting suffix exists. +good36 = /(([^\/]|X)+)(\/[\S\s]*)*$/ + +# GOOD - but we spuriously conclude that a rejecting suffix exists. +good37 = /^((x([^Y]+)?)*(Y|$))/ + +# NOT GOOD +bad68 = /(a*)+b/ + +# NOT GOOD +bad69 = /foo([\w-]*)+bar/ + +# NOT GOOD +bad70 = /((ab)*)+c/ + +# NOT GOOD +bad71 = /(a?a?)*b/ + +# GOOD +good38 = /(a?)*b/ + +# NOT GOOD - but not detected +bad72 = /(c?a?)*b/ + +# NOT GOOD +bad73 = /(?:a|a?)+b/ + +# NOT GOOD - but not detected. +bad74 = /(a?b?)*$/ + +# NOT GOOD +bad76 = /PRE(([a-c]|[c-d])T(e?e?e?e?|X))+(cTcT|cTXcTX$)/ + +# NOT GOOD - but not detected +bad77 = /^((a)+\w)+$/ + +# NOT GOOD +bad78 = /^(b+.)+$/ + +# GOOD +good39 = /a*b/ + +# All 4 bad combinations of nested * and + +bad79 = /(a*)*b/ +bad80 = /(a+)*b/ +bad81 = /(a*)+b/ +bad82 = /(a+)+b/ + +# GOOD +good40 = /(a|b)+/ +good41 = /(?:[\s;,"'<>(){}|\[\]@=+*]|:(?![\/\\]))+/ + +# NOT GOOD +bad83 = /^((?:a{|-)|\w\{)+X$/ +bad84 = /^((?:a{0|-)|\w\{\d)+X$/ +bad85 = /^((?:a{0,|-)|\w\{\d,)+X$/ +bad86 = /^((?:a{0,2|-)|\w\{\d,\d)+X$/ + +# GOOD: +good42 = /^((?:a{0,2}|-)|\w\{\d,\d\})+X$/ + +# NOT GOOD +bad87 = /^X(\u0061|a)*Y$/ + +# GOOD +good43 = /^X(\u0061|b)+Y$/ \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.expected b/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.expected new file mode 100644 index 00000000000..938758f9db1 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.expected @@ -0,0 +1,62 @@ +edges +| PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:10:5:10:8 | name | +| PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:11:5:11:8 | name | +| PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:12:5:12:8 | name | +| PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:13:5:13:8 | name | +| PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:14:5:14:8 | name | +| PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:15:5:15:8 | name | +| PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:16:5:16:8 | name | +| PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:17:5:17:8 | name | +| PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:18:5:18:8 | name | +| PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:19:5:19:8 | name | +| PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:20:5:20:8 | name | +| PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:21:5:21:8 | name | +| PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:22:5:22:8 | name | +| PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:23:17:23:20 | name | +| PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:24:18:24:21 | name | +| PolynomialReDoS.rb:27:9:27:14 | call to params : | PolynomialReDoS.rb:28:5:28:5 | a | +| PolynomialReDoS.rb:29:9:29:14 | call to params : | PolynomialReDoS.rb:30:5:30:5 | b | +| PolynomialReDoS.rb:31:9:31:14 | call to params : | PolynomialReDoS.rb:32:5:32:5 | c | +nodes +| PolynomialReDoS.rb:4:12:4:17 | call to params : | semmle.label | call to params : | +| PolynomialReDoS.rb:10:5:10:8 | name | semmle.label | name | +| PolynomialReDoS.rb:11:5:11:8 | name | semmle.label | name | +| PolynomialReDoS.rb:12:5:12:8 | name | semmle.label | name | +| PolynomialReDoS.rb:13:5:13:8 | name | semmle.label | name | +| PolynomialReDoS.rb:14:5:14:8 | name | semmle.label | name | +| PolynomialReDoS.rb:15:5:15:8 | name | semmle.label | name | +| PolynomialReDoS.rb:16:5:16:8 | name | semmle.label | name | +| PolynomialReDoS.rb:17:5:17:8 | name | semmle.label | name | +| PolynomialReDoS.rb:18:5:18:8 | name | semmle.label | name | +| PolynomialReDoS.rb:19:5:19:8 | name | semmle.label | name | +| PolynomialReDoS.rb:20:5:20:8 | name | semmle.label | name | +| PolynomialReDoS.rb:21:5:21:8 | name | semmle.label | name | +| PolynomialReDoS.rb:22:5:22:8 | name | semmle.label | name | +| PolynomialReDoS.rb:23:17:23:20 | name | semmle.label | name | +| PolynomialReDoS.rb:24:18:24:21 | name | semmle.label | name | +| PolynomialReDoS.rb:27:9:27:14 | call to params : | semmle.label | call to params : | +| PolynomialReDoS.rb:28:5:28:5 | a | semmle.label | a | +| PolynomialReDoS.rb:29:9:29:14 | call to params : | semmle.label | call to params : | +| PolynomialReDoS.rb:30:5:30:5 | b | semmle.label | b | +| PolynomialReDoS.rb:31:9:31:14 | call to params : | semmle.label | call to params : | +| PolynomialReDoS.rb:32:5:32:5 | c | semmle.label | c | +subpaths +#select +| PolynomialReDoS.rb:10:5:10:17 | ... =~ ... | PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:10:5:10:8 | name | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | PolynomialReDoS.rb:7:19:7:21 | \\s+ | regular expression | PolynomialReDoS.rb:4:12:4:17 | call to params | a user-provided value | +| PolynomialReDoS.rb:11:5:11:17 | ... !~ ... | PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:11:5:11:8 | name | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | PolynomialReDoS.rb:7:19:7:21 | \\s+ | regular expression | PolynomialReDoS.rb:4:12:4:17 | call to params | a user-provided value | +| PolynomialReDoS.rb:12:5:12:15 | ...[...] | PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:12:5:12:8 | name | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | PolynomialReDoS.rb:7:19:7:21 | \\s+ | regular expression | PolynomialReDoS.rb:4:12:4:17 | call to params | a user-provided value | +| PolynomialReDoS.rb:13:5:13:23 | call to gsub | PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:13:5:13:8 | name | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | PolynomialReDoS.rb:7:19:7:21 | \\s+ | regular expression | PolynomialReDoS.rb:4:12:4:17 | call to params | a user-provided value | +| PolynomialReDoS.rb:14:5:14:20 | call to index | PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:14:5:14:8 | name | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | PolynomialReDoS.rb:7:19:7:21 | \\s+ | regular expression | PolynomialReDoS.rb:4:12:4:17 | call to params | a user-provided value | +| PolynomialReDoS.rb:15:5:15:20 | call to match | PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:15:5:15:8 | name | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | PolynomialReDoS.rb:7:19:7:21 | \\s+ | regular expression | PolynomialReDoS.rb:4:12:4:17 | call to params | a user-provided value | +| PolynomialReDoS.rb:16:5:16:21 | call to match? | PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:16:5:16:8 | name | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | PolynomialReDoS.rb:7:19:7:21 | \\s+ | regular expression | PolynomialReDoS.rb:4:12:4:17 | call to params | a user-provided value | +| PolynomialReDoS.rb:17:5:17:24 | call to partition | PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:17:5:17:8 | name | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | PolynomialReDoS.rb:7:19:7:21 | \\s+ | regular expression | PolynomialReDoS.rb:4:12:4:17 | call to params | a user-provided value | +| PolynomialReDoS.rb:18:5:18:21 | call to rindex | PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:18:5:18:8 | name | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | PolynomialReDoS.rb:7:19:7:21 | \\s+ | regular expression | PolynomialReDoS.rb:4:12:4:17 | call to params | a user-provided value | +| PolynomialReDoS.rb:19:5:19:25 | call to rpartition | PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:19:5:19:8 | name | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | PolynomialReDoS.rb:7:19:7:21 | \\s+ | regular expression | PolynomialReDoS.rb:4:12:4:17 | call to params | a user-provided value | +| PolynomialReDoS.rb:20:5:20:19 | call to scan | PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:20:5:20:8 | name | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | PolynomialReDoS.rb:7:19:7:21 | \\s+ | regular expression | PolynomialReDoS.rb:4:12:4:17 | call to params | a user-provided value | +| PolynomialReDoS.rb:21:5:21:20 | call to split | PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:21:5:21:8 | name | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | PolynomialReDoS.rb:7:19:7:21 | \\s+ | regular expression | PolynomialReDoS.rb:4:12:4:17 | call to params | a user-provided value | +| PolynomialReDoS.rb:22:5:22:22 | call to sub | PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:22:5:22:8 | name | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | PolynomialReDoS.rb:7:19:7:21 | \\s+ | regular expression | PolynomialReDoS.rb:4:12:4:17 | call to params | a user-provided value | +| PolynomialReDoS.rb:23:5:23:20 | call to match | PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:23:17:23:20 | name | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | PolynomialReDoS.rb:7:19:7:21 | \\s+ | regular expression | PolynomialReDoS.rb:4:12:4:17 | call to params | a user-provided value | +| PolynomialReDoS.rb:24:5:24:21 | call to match? | PolynomialReDoS.rb:4:12:4:17 | call to params : | PolynomialReDoS.rb:24:18:24:21 | name | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | PolynomialReDoS.rb:7:19:7:21 | \\s+ | regular expression | PolynomialReDoS.rb:4:12:4:17 | call to params | a user-provided value | +| PolynomialReDoS.rb:28:5:28:21 | call to gsub! | PolynomialReDoS.rb:27:9:27:14 | call to params : | PolynomialReDoS.rb:28:5:28:5 | a | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | PolynomialReDoS.rb:7:19:7:21 | \\s+ | regular expression | PolynomialReDoS.rb:27:9:27:14 | call to params | a user-provided value | +| PolynomialReDoS.rb:30:5:30:18 | call to slice! | PolynomialReDoS.rb:29:9:29:14 | call to params : | PolynomialReDoS.rb:30:5:30:5 | b | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | PolynomialReDoS.rb:7:19:7:21 | \\s+ | regular expression | PolynomialReDoS.rb:29:9:29:14 | call to params | a user-provided value | +| PolynomialReDoS.rb:32:5:32:20 | call to sub! | PolynomialReDoS.rb:31:9:31:14 | call to params : | PolynomialReDoS.rb:32:5:32:5 | c | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | PolynomialReDoS.rb:7:19:7:21 | \\s+ | regular expression | PolynomialReDoS.rb:31:9:31:14 | call to params | a user-provided value | diff --git a/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.qlref b/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.qlref new file mode 100644 index 00000000000..5807dc56fa0 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.qlref @@ -0,0 +1 @@ +queries/security/cwe-1333/PolynomialReDoS.ql diff --git a/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.rb b/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.rb new file mode 100644 index 00000000000..55db9555584 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-1333-polynomial-redos/PolynomialReDoS.rb @@ -0,0 +1,42 @@ +class FooController < ActionController::Base + def some_request_handler + # A source for the data-flow query (i.e. a remote flow source) + name = params[:name] + + # A vulnerable regex + regex = /^\s+|\s+$/ + + # Various sinks that match the source against the regex + name =~ regex # NOT GOOD + name !~ regex # NOT GOOD + name[regex] # NOT GOOD + name.gsub regex, '' # NOT GOOD + name.index regex # NOT GOOD + name.match regex # NOT GOOD + name.match? regex # NOT GOOD + name.partition regex # NOT GOOD + name.rindex regex # NOT GOOD + name.rpartition regex # NOT GOOD + name.scan regex # NOT GOOD + name.split regex # NOT GOOD + name.sub regex, '' # NOT GOOD + regex.match name # NOT GOOD + regex.match? name # NOT GOOD + + # Destructive variants + a = params[:b] + a.gsub! regex, '' # NOT GOOD + b = params[:a] + b.slice! regex # NOT GOOD + c = params[:c] + c.sub! regex, '' # NOT GOOD + + # GOOD - guarded by a string length check + if name.length < 1024 + name.gsub regex, '' + end + + # GOOD - regex does not suffer from polynomial backtracking (regression test) + params[:foo] =~ /\A[bc].*\Z/ + end +end diff --git a/ruby/ql/test/query-tests/security/cwe-295/Excon.rb b/ruby/ql/test/query-tests/security/cwe-295/Excon.rb new file mode 100644 index 00000000000..e08f9672ef5 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-295/Excon.rb @@ -0,0 +1,49 @@ +require "excon" + +def method1 + # BAD + Excon.defaults[:ssl_verify_peer] = false + Excon.get("http://example.com/") +end + +def method2 + # BAD + Excon.ssl_verify_peer = false + Excon.get("http://example.com/") +end + +def method3(secure) + # BAD + Excon.defaults[:ssl_verify_peer] = (secure ? true : false) + Excon.get("http://example.com/") +end + +def method4 + # BAD + conn = Excon::Connection.new("http://example.com/", ssl_verify_peer: false) + conn.get +end + +def method5 + # BAD + Excon.ssl_verify_peer = true + Excon.new("http://example.com/", ssl_verify_peer: false).get +end + +def method6 + # GOOD + Excon.defaults[:ssl_verify_peer] = true + Excon.get("http://example.com/") +end + +def method7 + # GOOD + Excon.ssl_verify_peer = true + Excon.get("http://example.com/") +end + +def method8 + # GOOD + Excon.defaults[:ssl_verify_peer] = false + Excon.new("http://example.com/", ssl_verify_peer: true) +end \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/cwe-295/Faraday.rb b/ruby/ql/test/query-tests/security/cwe-295/Faraday.rb new file mode 100644 index 00000000000..fb66122ed6a --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-295/Faraday.rb @@ -0,0 +1,28 @@ +require "faraday" + +# BAD +connection = Faraday.new("http://example.com", ssl: { verify: false }) +response = connection.get("/") + +# BAD +connection = Faraday.new("http://example.com", ssl: { verify_mode: OpenSSL::SSL::VERIFY_NONE }) +response = connection.get("/") + +# GOOD +connection = Faraday.new("http://example.com") +response = connection.get("/") + +# GOOD +response = Faraday.get("http://example.com") + +# GOOD +connection = Faraday.new("http://example.com", ssl: { version: :TLSv1 }) +response = connection.get("/") + +# GOOD +connection = Faraday.new("http://example.com", ssl: { verify: true }) +response = connection.get("/") + +# GOOD +connection = Faraday.new("http://example.com", ssl: { verify_mode: OpenSSL::SSL::VERIFY_PEER }) +response = connection.get("/") diff --git a/ruby/ql/test/query-tests/security/cwe-295/HttpClient.rb b/ruby/ql/test/query-tests/security/cwe-295/HttpClient.rb new file mode 100644 index 00000000000..902950e5be9 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-295/HttpClient.rb @@ -0,0 +1,18 @@ +require "httpclient" + +# BAD +client = HTTPClient.new +client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE +client.get("https://example.com") + +# GOOD +client = HTTPClient.new +client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_PEER +client.get("https://example.com") + +# GOOD +client = HTTPClient.new +client.get("https://example.com") + +# GOOD +HTTPClient.get("https://example.com/") \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/cwe-295/Httparty.rb b/ruby/ql/test/query-tests/security/cwe-295/Httparty.rb new file mode 100644 index 00000000000..562cbbc1f43 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-295/Httparty.rb @@ -0,0 +1,37 @@ +require "httparty" + +# BAD +HTTParty.get("http://example.com/", verify: false) + +# BAD +HTTParty.get("http://example.com/", verify_peer: false) + +# BAD +HTTParty.get("http://example.com/", { verify_peer: false }) + +# BAD +HTTParty.post("http://example.com/", body: "some_data", verify: false) + +# BAD +HTTParty.post("http://example.com/", { body: "some_data", verify: false }) + +# GOOD +HTTParty.get("http://example.com/") + +# GOOD +HTTParty.get("http://example.com/", verify: true) + +# GOOD +HTTParty.get("http://example.com/", verify_peer: true) + +# GOOD +HTTParty.post("http://example.com/", body: "some_data") + +# GOOD +HTTParty.post("http://example.com/", body: "some_data", verify: true) + +# GOOD +HTTParty.post("http://example.com/", { body: "some_data" }) + +# GOOD +HTTParty.post("http://example.com/", { body: "some_data", verify: true }) \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/cwe-295/NetHttp.rb b/ruby/ql/test/query-tests/security/cwe-295/NetHttp.rb new file mode 100644 index 00000000000..9269eeae531 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-295/NetHttp.rb @@ -0,0 +1,10 @@ +require "net/https" +require "uri" + +uri = URI.parse "https://example.com/" +http = Net::HTTP.new uri.host, uri.port +http.use_ssl = true +http.verify_mode = OpenSSL::SSL::VERIFY_NONE +request = Net::HTTP::Get.new uri.request_uri +response = http.request request +puts response.body diff --git a/ruby/ql/test/query-tests/security/cwe-295/OpenURI.rb b/ruby/ql/test/query-tests/security/cwe-295/OpenURI.rb new file mode 100644 index 00000000000..a825791c823 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-295/OpenURI.rb @@ -0,0 +1,47 @@ +require "open-uri" + +# BAD +Kernel.open("https://example.com", ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE) + +# BAD +Kernel.open("https://example.com", { ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE }) + +# BAD +options = { ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE } +Kernel.open("https://example.com", options) + +# BAD +URI.parse("https://example.com").open(ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE) + +# BAD +URI.parse("https://example.com").open({ ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE }) + +# BAD +options = { ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE } +URI.parse("https://example.com").open(options) + +# GOOD +Kernel.open("https://example.com") + +# GOOD +Kernel.open("https://example.com", ssl_verify_mode: OpenSSL::SSL::VERIFY_PEER) + +# GOOD +Kernel.open("https://example.com", { ssl_verify_mode: OpenSSL::SSL::VERIFY_PEER }) + +# GOOD +options = { ssl_verify_mode: OpenSSL::SSL::VERIFY_PEER } +Kernel.open("https://example.com", options) + +# GOOD +URI.parse("https://example.com").open + +# GOOD +URI.parse("https://example.com").open(ssl_verify_mode: OpenSSL::SSL::VERIFY_PEER) + +# GOOD +URI.parse("https://example.com").open({ ssl_verify_mode: OpenSSL::SSL::VERIFY_PEER }) + +# GOOD +options = { ssl_verify_mode: OpenSSL::SSL::VERIFY_PEER } +URI.parse("https://example.com").open(options) \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/cwe-295/RequestWithoutValidation.expected b/ruby/ql/test/query-tests/security/cwe-295/RequestWithoutValidation.expected new file mode 100644 index 00000000000..e3fc81e1038 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-295/RequestWithoutValidation.expected @@ -0,0 +1,25 @@ +| Excon.rb:6:3:6:34 | call to get | This request may run with $@. | Excon.rb:5:3:5:34 | call to []= | certificate validation disabled | +| Excon.rb:12:3:12:34 | call to get | This request may run with $@. | Excon.rb:11:3:11:23 | call to ssl_verify_peer= | certificate validation disabled | +| Excon.rb:18:3:18:34 | call to get | This request may run with $@. | Excon.rb:17:3:17:34 | call to []= | certificate validation disabled | +| Excon.rb:24:3:24:10 | call to get | This request may run with $@. | Excon.rb:23:55:23:76 | Pair | certificate validation disabled | +| Excon.rb:30:3:30:62 | call to get | This request may run with $@. | Excon.rb:30:36:30:57 | Pair | certificate validation disabled | +| Faraday.rb:5:12:5:30 | call to get | This request may run with $@. | Faraday.rb:4:48:4:69 | Pair | certificate validation disabled | +| Faraday.rb:9:12:9:30 | call to get | This request may run with $@. | Faraday.rb:8:48:8:94 | Pair | certificate validation disabled | +| HttpClient.rb:6:1:6:33 | call to get | This request may run with $@. | HttpClient.rb:5:1:5:29 | call to verify_mode= | certificate validation disabled | +| Httparty.rb:4:1:4:50 | call to get | This request may run with $@. | Httparty.rb:4:37:4:49 | Pair | certificate validation disabled | +| Httparty.rb:7:1:7:55 | call to get | This request may run with $@. | Httparty.rb:7:37:7:54 | Pair | certificate validation disabled | +| Httparty.rb:10:1:10:59 | call to get | This request may run with $@. | Httparty.rb:10:39:10:56 | Pair | certificate validation disabled | +| Httparty.rb:13:1:13:70 | call to post | This request may run with $@. | Httparty.rb:13:57:13:69 | Pair | certificate validation disabled | +| Httparty.rb:16:1:16:74 | call to post | This request may run with $@. | Httparty.rb:16:59:16:71 | Pair | certificate validation disabled | +| NetHttp.rb:9:12:9:31 | call to request | This request may run with $@. | NetHttp.rb:7:1:7:16 | ... = ... | certificate validation disabled | +| OpenURI.rb:4:1:4:78 | call to open | This request may run with $@. | OpenURI.rb:4:36:4:77 | Pair | certificate validation disabled | +| OpenURI.rb:7:1:7:82 | call to open | This request may run with $@. | OpenURI.rb:7:38:7:79 | Pair | certificate validation disabled | +| OpenURI.rb:11:1:11:43 | call to open | This request may run with $@. | OpenURI.rb:10:13:10:54 | Pair | certificate validation disabled | +| OpenURI.rb:14:1:14:81 | call to open | This request may run with $@. | OpenURI.rb:14:39:14:80 | Pair | certificate validation disabled | +| OpenURI.rb:17:1:17:85 | call to open | This request may run with $@. | OpenURI.rb:17:41:17:82 | Pair | certificate validation disabled | +| OpenURI.rb:21:1:21:46 | call to open | This request may run with $@. | OpenURI.rb:20:13:20:54 | Pair | certificate validation disabled | +| RestClient.rb:5:12:5:23 | call to get | This request may run with $@. | RestClient.rb:4:60:4:96 | Pair | certificate validation disabled | +| RestClient.rb:9:12:9:23 | call to get | This request may run with $@. | RestClient.rb:8:62:8:98 | Pair | certificate validation disabled | +| RestClient.rb:14:12:14:23 | call to get | This request may run with $@. | RestClient.rb:12:13:12:49 | Pair | certificate validation disabled | +| Typhoeus.rb:4:1:4:62 | call to get | This request may run with $@. | Typhoeus.rb:4:41:4:61 | Pair | certificate validation disabled | +| Typhoeus.rb:8:1:8:54 | call to post | This request may run with $@. | Typhoeus.rb:7:37:7:57 | Pair | certificate validation disabled | diff --git a/ruby/ql/test/query-tests/security/cwe-295/RequestWithoutValidation.qlref b/ruby/ql/test/query-tests/security/cwe-295/RequestWithoutValidation.qlref new file mode 100644 index 00000000000..e2caf232ddb --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-295/RequestWithoutValidation.qlref @@ -0,0 +1 @@ +queries/security/cwe-295/RequestWithoutValidation.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/cwe-295/RestClient.rb b/ruby/ql/test/query-tests/security/cwe-295/RestClient.rb new file mode 100644 index 00000000000..4615c0acb80 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-295/RestClient.rb @@ -0,0 +1,33 @@ +require "rest-client" + +# BAD +resource = RestClient::Resource.new("https://example.com", verify_ssl: OpenSSL::SSL::VERIFY_NONE) +response = resource.get + +# BAD +resource = RestClient::Resource.new("https://example.com", { verify_ssl: OpenSSL::SSL::VERIFY_NONE }) +response = resource.get + +# BAD +options = { verify_ssl: OpenSSL::SSL::VERIFY_NONE } +resource = RestClient::Resource.new("https://example.com", options) +response = resource.get + +# GOOD +RestClient.get("https://example.com") + +# GOOD +resource = RestClient::Resource.new("https://example.com") +response = resource.get + +# GOOD +resource = RestClient::Resource.new("https://example.com", verify_ssl: OpenSSL::SSL::VERIFY_PEER) +response = resource.get +# BAD +resource = RestClient::Resource.new("https://example.com", { verify_ssl: OpenSSL::SSL::VERIFY_PEER }) +response = resource.get + +# GOOD +options = { verify_ssl: OpenSSL::SSL::VERIFY_PEER } +resource = RestClient::Resource.new("https://example.com", options) +response = resource.get \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/cwe-295/Typhoeus.rb b/ruby/ql/test/query-tests/security/cwe-295/Typhoeus.rb new file mode 100644 index 00000000000..aed601cf888 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-295/Typhoeus.rb @@ -0,0 +1,11 @@ +require "typhoeus" + +# BAD +Typhoeus.get("https://www.example.com", ssl_verifypeer: false) + +# BAD +post_options = { body: "some data", ssl_verifypeer: false } +Typhoeus.post("https://www.example.com", post_options) + +# GOOD +Typhoeus.get("https://www.example.com") \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/cwe-502/oj-global-options/OjGlobalOptions.rb b/ruby/ql/test/query-tests/security/cwe-502/oj-global-options/OjGlobalOptions.rb new file mode 100644 index 00000000000..3ec21d778c1 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-502/oj-global-options/OjGlobalOptions.rb @@ -0,0 +1,16 @@ +require "oj" + +class UsersController < ActionController::Base + # GOOD - Oj.load is safe when any mode other than :object is set globally + def route0 + json_data = params[:key] + object = Oj.load json_data + end + + # BAD - the safe mode set globally is overridden with an unsafe mode passed as + # a call argument + def route1 + json_data = params[:key] + object = Oj.load json_data, mode: :object + end +end diff --git a/ruby/ql/test/query-tests/security/cwe-502/oj-global-options/Startup.rb b/ruby/ql/test/query-tests/security/cwe-502/oj-global-options/Startup.rb new file mode 100644 index 00000000000..9a4e3f610a6 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-502/oj-global-options/Startup.rb @@ -0,0 +1,5 @@ +require "oj" + +# Set the default mode for the Oj library to use the :compat mode, which makes +# Oj.load safe for untrusted data. +Oj.default_options = { :mode => :compat } \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/cwe-502/oj-global-options/UnsafeDeserialization.expected b/ruby/ql/test/query-tests/security/cwe-502/oj-global-options/UnsafeDeserialization.expected new file mode 100644 index 00000000000..8e4e62d4476 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-502/oj-global-options/UnsafeDeserialization.expected @@ -0,0 +1,8 @@ +edges +| OjGlobalOptions.rb:13:17:13:22 | call to params : | OjGlobalOptions.rb:14:22:14:30 | json_data | +nodes +| OjGlobalOptions.rb:13:17:13:22 | call to params : | semmle.label | call to params : | +| OjGlobalOptions.rb:14:22:14:30 | json_data | semmle.label | json_data | +subpaths +#select +| OjGlobalOptions.rb:14:22:14:30 | json_data | OjGlobalOptions.rb:13:17:13:22 | call to params : | OjGlobalOptions.rb:14:22:14:30 | json_data | Unsafe deserialization of $@. | OjGlobalOptions.rb:13:17:13:22 | call to params | user input | diff --git a/ruby/ql/test/query-tests/security/cwe-502/oj-global-options/UnsafeDeserialization.qlref b/ruby/ql/test/query-tests/security/cwe-502/oj-global-options/UnsafeDeserialization.qlref new file mode 100644 index 00000000000..55f7c440b46 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-502/oj-global-options/UnsafeDeserialization.qlref @@ -0,0 +1 @@ +queries/security/cwe-502/UnsafeDeserialization.ql diff --git a/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.expected b/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.expected new file mode 100644 index 00000000000..b2fde305145 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.expected @@ -0,0 +1,35 @@ +edges +| UnsafeDeserialization.rb:9:39:9:44 | call to params : | UnsafeDeserialization.rb:10:27:10:41 | serialized_data | +| UnsafeDeserialization.rb:15:39:15:44 | call to params : | UnsafeDeserialization.rb:16:30:16:44 | serialized_data | +| UnsafeDeserialization.rb:21:17:21:22 | call to params : | UnsafeDeserialization.rb:22:24:22:32 | json_data | +| UnsafeDeserialization.rb:27:17:27:22 | call to params : | UnsafeDeserialization.rb:28:27:28:35 | json_data | +| UnsafeDeserialization.rb:39:17:39:22 | call to params : | UnsafeDeserialization.rb:40:24:40:32 | yaml_data | +| UnsafeDeserialization.rb:51:17:51:22 | call to params : | UnsafeDeserialization.rb:52:22:52:30 | json_data | +| UnsafeDeserialization.rb:51:17:51:22 | call to params : | UnsafeDeserialization.rb:53:22:53:30 | json_data | +| UnsafeDeserialization.rb:58:17:58:22 | call to params : | UnsafeDeserialization.rb:68:23:68:31 | json_data | +nodes +| UnsafeDeserialization.rb:9:39:9:44 | call to params : | semmle.label | call to params : | +| UnsafeDeserialization.rb:10:27:10:41 | serialized_data | semmle.label | serialized_data | +| UnsafeDeserialization.rb:15:39:15:44 | call to params : | semmle.label | call to params : | +| UnsafeDeserialization.rb:16:30:16:44 | serialized_data | semmle.label | serialized_data | +| UnsafeDeserialization.rb:21:17:21:22 | call to params : | semmle.label | call to params : | +| UnsafeDeserialization.rb:22:24:22:32 | json_data | semmle.label | json_data | +| UnsafeDeserialization.rb:27:17:27:22 | call to params : | semmle.label | call to params : | +| UnsafeDeserialization.rb:28:27:28:35 | json_data | semmle.label | json_data | +| UnsafeDeserialization.rb:39:17:39:22 | call to params : | semmle.label | call to params : | +| UnsafeDeserialization.rb:40:24:40:32 | yaml_data | semmle.label | yaml_data | +| UnsafeDeserialization.rb:51:17:51:22 | call to params : | semmle.label | call to params : | +| UnsafeDeserialization.rb:52:22:52:30 | json_data | semmle.label | json_data | +| UnsafeDeserialization.rb:53:22:53:30 | json_data | semmle.label | json_data | +| UnsafeDeserialization.rb:58:17:58:22 | call to params : | semmle.label | call to params : | +| UnsafeDeserialization.rb:68:23:68:31 | json_data | semmle.label | json_data | +subpaths +#select +| UnsafeDeserialization.rb:10:27:10:41 | serialized_data | UnsafeDeserialization.rb:9:39:9:44 | call to params : | UnsafeDeserialization.rb:10:27:10:41 | serialized_data | Unsafe deserialization of $@. | UnsafeDeserialization.rb:9:39:9:44 | call to params | user input | +| UnsafeDeserialization.rb:16:30:16:44 | serialized_data | UnsafeDeserialization.rb:15:39:15:44 | call to params : | UnsafeDeserialization.rb:16:30:16:44 | serialized_data | Unsafe deserialization of $@. | UnsafeDeserialization.rb:15:39:15:44 | call to params | user input | +| UnsafeDeserialization.rb:22:24:22:32 | json_data | UnsafeDeserialization.rb:21:17:21:22 | call to params : | UnsafeDeserialization.rb:22:24:22:32 | json_data | Unsafe deserialization of $@. | UnsafeDeserialization.rb:21:17:21:22 | call to params | user input | +| UnsafeDeserialization.rb:28:27:28:35 | json_data | UnsafeDeserialization.rb:27:17:27:22 | call to params : | UnsafeDeserialization.rb:28:27:28:35 | json_data | Unsafe deserialization of $@. | UnsafeDeserialization.rb:27:17:27:22 | call to params | user input | +| UnsafeDeserialization.rb:40:24:40:32 | yaml_data | UnsafeDeserialization.rb:39:17:39:22 | call to params : | UnsafeDeserialization.rb:40:24:40:32 | yaml_data | Unsafe deserialization of $@. | UnsafeDeserialization.rb:39:17:39:22 | call to params | user input | +| UnsafeDeserialization.rb:52:22:52:30 | json_data | UnsafeDeserialization.rb:51:17:51:22 | call to params : | UnsafeDeserialization.rb:52:22:52:30 | json_data | Unsafe deserialization of $@. | UnsafeDeserialization.rb:51:17:51:22 | call to params | user input | +| UnsafeDeserialization.rb:53:22:53:30 | json_data | UnsafeDeserialization.rb:51:17:51:22 | call to params : | UnsafeDeserialization.rb:53:22:53:30 | json_data | Unsafe deserialization of $@. | UnsafeDeserialization.rb:51:17:51:22 | call to params | user input | +| UnsafeDeserialization.rb:68:23:68:31 | json_data | UnsafeDeserialization.rb:58:17:58:22 | call to params : | UnsafeDeserialization.rb:68:23:68:31 | json_data | Unsafe deserialization of $@. | UnsafeDeserialization.rb:58:17:58:22 | call to params | user input | diff --git a/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.qlref b/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.qlref new file mode 100644 index 00000000000..55f7c440b46 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.qlref @@ -0,0 +1 @@ +queries/security/cwe-502/UnsafeDeserialization.ql diff --git a/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.rb b/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.rb new file mode 100644 index 00000000000..63f6171ea99 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.rb @@ -0,0 +1,76 @@ +require "base64" +require "json" +require "oj" +require "yaml" + +class UsersController < ActionController::Base + # BAD + def route0 + serialized_data = Base64.decode64 params[:key] + object = Marshal.load serialized_data + end + + # BAD + def route1 + serialized_data = Base64.decode64 params[:key] + object = Marshal.restore serialized_data + end + + # BAD + def route2 + json_data = params[:key] + object = JSON.load json_data + end + + # BAD + def route3 + json_data = params[:key] + object = JSON.restore json_data + end + + # GOOD - JSON.parse is safe to use on untrusted data + def route4 + json_data = params[:key] + object = JSON.parse json_data + end + + # BAD + def route5 + yaml_data = params[:key] + object = YAML.load yaml_data + end + + # GOOD + def route6 + yaml_data = params[:key] + object = YAML.safe_load yaml_data + end + + # BAD - Oj.load is unsafe in its default :object mode + def route7 + json_data = params[:key] + object = Oj.load json_data + object = Oj.load json_data, mode: :object + end + + # GOOD - Oj.load is safe in any other mode + def route8 + json_data = params[:key] + # Test the different ways the options hash can be passed + options = { allow_blank: true, mode: :rails } + object1 = Oj.load json_data, options + object2 = Oj.load json_data, mode: :strict + object3 = Oj.load json_data, :allow_blank => true, :mode => :compat + + # TODO: false positive; we aren't detecting flow from `:json` to the call argument. + more_options = { allow_blank: true } + more_options[:mode] = :json + object4 = Oj.load json_data, more_options + end + + # GOOD + def route9 + json_data = params[:key] + object = Oj.safe_load json_data + end +end diff --git a/ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.expected b/ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.expected new file mode 100644 index 00000000000..8ebdd388081 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.expected @@ -0,0 +1,26 @@ +edges +| UrlRedirect.rb:9:17:9:22 | call to params : | UrlRedirect.rb:9:17:9:28 | ...[...] | +| UrlRedirect.rb:14:17:14:22 | call to params : | UrlRedirect.rb:14:17:14:43 | call to fetch | +| UrlRedirect.rb:19:17:19:22 | call to params : | UrlRedirect.rb:19:17:19:37 | call to to_unsafe_hash | +| UrlRedirect.rb:24:31:24:36 | call to params : | UrlRedirect.rb:24:17:24:37 | call to filter_params | +| UrlRedirect.rb:34:20:34:25 | call to params : | UrlRedirect.rb:34:17:34:37 | "#{...}/foo" | +nodes +| UrlRedirect.rb:4:17:4:22 | call to params | semmle.label | call to params | +| UrlRedirect.rb:9:17:9:22 | call to params : | semmle.label | call to params : | +| UrlRedirect.rb:9:17:9:28 | ...[...] | semmle.label | ...[...] | +| UrlRedirect.rb:14:17:14:22 | call to params : | semmle.label | call to params : | +| UrlRedirect.rb:14:17:14:43 | call to fetch | semmle.label | call to fetch | +| UrlRedirect.rb:19:17:19:22 | call to params : | semmle.label | call to params : | +| UrlRedirect.rb:19:17:19:37 | call to to_unsafe_hash | semmle.label | call to to_unsafe_hash | +| UrlRedirect.rb:24:17:24:37 | call to filter_params | semmle.label | call to filter_params | +| UrlRedirect.rb:24:31:24:36 | call to params : | semmle.label | call to params : | +| UrlRedirect.rb:34:17:34:37 | "#{...}/foo" | semmle.label | "#{...}/foo" | +| UrlRedirect.rb:34:20:34:25 | call to params : | semmle.label | call to params : | +subpaths +#select +| UrlRedirect.rb:4:17:4:22 | call to params | UrlRedirect.rb:4:17:4:22 | call to params | UrlRedirect.rb:4:17:4:22 | call to params | Untrusted URL redirection due to $@. | UrlRedirect.rb:4:17:4:22 | call to params | a user-provided value | +| UrlRedirect.rb:9:17:9:28 | ...[...] | UrlRedirect.rb:9:17:9:22 | call to params : | UrlRedirect.rb:9:17:9:28 | ...[...] | Untrusted URL redirection due to $@. | UrlRedirect.rb:9:17:9:22 | call to params | a user-provided value | +| UrlRedirect.rb:14:17:14:43 | call to fetch | UrlRedirect.rb:14:17:14:22 | call to params : | UrlRedirect.rb:14:17:14:43 | call to fetch | Untrusted URL redirection due to $@. | UrlRedirect.rb:14:17:14:22 | call to params | a user-provided value | +| UrlRedirect.rb:19:17:19:37 | call to to_unsafe_hash | UrlRedirect.rb:19:17:19:22 | call to params : | UrlRedirect.rb:19:17:19:37 | call to to_unsafe_hash | Untrusted URL redirection due to $@. | UrlRedirect.rb:19:17:19:22 | call to params | a user-provided value | +| UrlRedirect.rb:24:17:24:37 | call to filter_params | UrlRedirect.rb:24:31:24:36 | call to params : | UrlRedirect.rb:24:17:24:37 | call to filter_params | Untrusted URL redirection due to $@. | UrlRedirect.rb:24:31:24:36 | call to params | a user-provided value | +| UrlRedirect.rb:34:17:34:37 | "#{...}/foo" | UrlRedirect.rb:34:20:34:25 | call to params : | UrlRedirect.rb:34:17:34:37 | "#{...}/foo" | Untrusted URL redirection due to $@. | UrlRedirect.rb:34:20:34:25 | call to params | a user-provided value | diff --git a/ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.qlref b/ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.qlref new file mode 100644 index 00000000000..422dc00837a --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.qlref @@ -0,0 +1 @@ +queries/security/cwe-601/UrlRedirect.ql diff --git a/ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.rb b/ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.rb new file mode 100644 index 00000000000..c5bfc6d0093 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-601/UrlRedirect.rb @@ -0,0 +1,59 @@ +class UsersController < ActionController::Base + # BAD + def route1 + redirect_to params + end + + # BAD + def route2 + redirect_to params[:key] + end + + # BAD + def route3 + redirect_to params.fetch(:specific_arg) + end + + # BAD + def route4 + redirect_to params.to_unsafe_hash + end + + # BAD + def route5 + redirect_to filter_params(params) + end + + # GOOD + def route6 + redirect_to "/foo/#{params[:key]}" + end + + # BAD + def route7 + redirect_to "#{params[:key]}/foo" + end + + # GOOD + def route8 + key = params[:key] + if key == "foo" + redirect_to key + else + redirect_to "/default" + end + end + + # GOOD + # Technically vulnerable, but we assume this is a handler for a POST request, + # so can't be triggered by following a link. + def create + redirect_to params[:key] + end + + private + + def filter_params(input_params) + input_params.permit(:key) + end +end diff --git a/ruby/ql/test/query-tests/security/cwe-611/LibXmlRuby.rb b/ruby/ql/test/query-tests/security/cwe-611/LibXmlRuby.rb new file mode 100644 index 00000000000..33844bdc2c9 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-611/LibXmlRuby.rb @@ -0,0 +1,16 @@ +class LibXmlRubyXXE < ApplicationController + + content = params[:xml] + LibXML::XML::Document.string(content, { options: 2 | 2048, encoding: 'utf-8' }) + LibXML::XML::Document.file(content, { options: LibXML::XML::Parser::Options::NOENT | 2048}) + LibXML::XML::Document.io(content, { options: XML::Parser::Options::NOENT | 2048 }) + LibXML::XML::Parser.string(content, { options: 2 | 2048 }) + LibXML::XML::Parser.file(content, { options: 3 | 2048 }) + LibXML::XML::Parser.io(content, { options: 2 | 2048}) + + XML::Document.string(content, { options: 2 | 2048 }) + XML::Parser.string(content, { options: 2 | 2048 }) + + LibXML::XML::Parser.file(content, { options: 2048 }) # OK + +end diff --git a/ruby/ql/test/query-tests/security/cwe-611/Nokogiri.rb b/ruby/ql/test/query-tests/security/cwe-611/Nokogiri.rb new file mode 100644 index 00000000000..76f37cfb751 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-611/Nokogiri.rb @@ -0,0 +1,30 @@ +class NokogiriXXE < ApplicationController + + content = params[:xml] + + Nokogiri::XML::parse(content, nil, nil, 2) + Nokogiri::XML::parse(content, nil, nil, 1 | 2) + Nokogiri::XML::parse(content, nil, nil, 1 & ~Nokogiri::XML::ParseOptions::NONET) + Nokogiri::XML::parse(content, nil, nil, Nokogiri::XML::ParseOptions::NOENT) + Nokogiri::XML::parse(content, nil, nil, Nokogiri::XML::ParseOptions::DTDLOAD) + Nokogiri::XML::parse(content, nil, nil, ~Nokogiri::XML::ParseOptions::NOENT) #OK + Nokogiri::XML::parse(content, nil, nil, ~Nokogiri::XML::ParseOptions::NONET) + Nokogiri::XML::parse(content, nil, nil, Nokogiri::XML::ParseOptions.new 2) + options = Nokogiri::XML::ParseOptions.new 2048 + options.noent + Nokogiri::XML::parse(content, nil, nil, options) + Nokogiri::XML::parse(content, nil, nil, (Nokogiri::XML::ParseOptions.new 0).noent) + + Nokogiri::XML::parse(content) { |x| x.noent } + Nokogiri::XML::parse(content) { |x| x.nononet } #FAIL + Nokogiri::XML::parse(content) { |x| x.nodtdload } # OK + + Nokogiri::XML::parse(content) { |x| x.nonet.noent.nodtdload } + + Nokogiri::XML::parse(content, nil, nil, 2048) # OK + Nokogiri::XML::parse(content, nil, nil, 3) + Nokogiri::XML::parse(content) { |x| x.nonet.nodtdload } # OK + Nokogiri::XML::parse(content, nil, nil, Nokogiri::XML::ParseOptions::NOENT & ~Nokogiri::XML::ParseOptions::NOBLANKS) + Nokogiri::XML::parse(content, nil, nil, ~Nokogiri::XML::ParseOptions::NONET | Nokogiri::XML::ParseOptions::NOBLANKS) + +end diff --git a/ruby/ql/test/query-tests/security/cwe-611/Xxe.expected b/ruby/ql/test/query-tests/security/cwe-611/Xxe.expected new file mode 100644 index 00000000000..0db8bb55da4 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-611/Xxe.expected @@ -0,0 +1,75 @@ +edges +| LibXmlRuby.rb:3:15:3:20 | call to params : | LibXmlRuby.rb:4:34:4:40 | content | +| LibXmlRuby.rb:3:15:3:20 | call to params : | LibXmlRuby.rb:5:32:5:38 | content | +| LibXmlRuby.rb:3:15:3:20 | call to params : | LibXmlRuby.rb:6:30:6:36 | content | +| LibXmlRuby.rb:3:15:3:20 | call to params : | LibXmlRuby.rb:7:32:7:38 | content | +| LibXmlRuby.rb:3:15:3:20 | call to params : | LibXmlRuby.rb:8:30:8:36 | content | +| LibXmlRuby.rb:3:15:3:20 | call to params : | LibXmlRuby.rb:9:28:9:34 | content | +| LibXmlRuby.rb:3:15:3:20 | call to params : | LibXmlRuby.rb:11:26:11:32 | content | +| LibXmlRuby.rb:3:15:3:20 | call to params : | LibXmlRuby.rb:12:24:12:30 | content | +| Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:5:26:5:32 | content | +| Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:6:26:6:32 | content | +| Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:7:26:7:32 | content | +| Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:8:26:8:32 | content | +| Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:9:26:9:32 | content | +| Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:11:26:11:32 | content | +| Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:12:26:12:32 | content | +| Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:15:26:15:32 | content | +| Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:16:26:16:32 | content | +| Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:18:26:18:32 | content | +| Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:19:26:19:32 | content | +| Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:22:26:22:32 | content | +| Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:25:26:25:32 | content | +| Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:27:26:27:32 | content | +| Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:28:26:28:32 | content | +nodes +| LibXmlRuby.rb:3:15:3:20 | call to params : | semmle.label | call to params : | +| LibXmlRuby.rb:4:34:4:40 | content | semmle.label | content | +| LibXmlRuby.rb:5:32:5:38 | content | semmle.label | content | +| LibXmlRuby.rb:6:30:6:36 | content | semmle.label | content | +| LibXmlRuby.rb:7:32:7:38 | content | semmle.label | content | +| LibXmlRuby.rb:8:30:8:36 | content | semmle.label | content | +| LibXmlRuby.rb:9:28:9:34 | content | semmle.label | content | +| LibXmlRuby.rb:11:26:11:32 | content | semmle.label | content | +| LibXmlRuby.rb:12:24:12:30 | content | semmle.label | content | +| Nokogiri.rb:3:15:3:20 | call to params : | semmle.label | call to params : | +| Nokogiri.rb:5:26:5:32 | content | semmle.label | content | +| Nokogiri.rb:6:26:6:32 | content | semmle.label | content | +| Nokogiri.rb:7:26:7:32 | content | semmle.label | content | +| Nokogiri.rb:8:26:8:32 | content | semmle.label | content | +| Nokogiri.rb:9:26:9:32 | content | semmle.label | content | +| Nokogiri.rb:11:26:11:32 | content | semmle.label | content | +| Nokogiri.rb:12:26:12:32 | content | semmle.label | content | +| Nokogiri.rb:15:26:15:32 | content | semmle.label | content | +| Nokogiri.rb:16:26:16:32 | content | semmle.label | content | +| Nokogiri.rb:18:26:18:32 | content | semmle.label | content | +| Nokogiri.rb:19:26:19:32 | content | semmle.label | content | +| Nokogiri.rb:22:26:22:32 | content | semmle.label | content | +| Nokogiri.rb:25:26:25:32 | content | semmle.label | content | +| Nokogiri.rb:27:26:27:32 | content | semmle.label | content | +| Nokogiri.rb:28:26:28:32 | content | semmle.label | content | +subpaths +#select +| LibXmlRuby.rb:4:34:4:40 | content | LibXmlRuby.rb:3:15:3:20 | call to params : | LibXmlRuby.rb:4:34:4:40 | content | Unsafe parsing of XML file from $@. | LibXmlRuby.rb:3:15:3:20 | call to params | user input | +| LibXmlRuby.rb:5:32:5:38 | content | LibXmlRuby.rb:3:15:3:20 | call to params : | LibXmlRuby.rb:5:32:5:38 | content | Unsafe parsing of XML file from $@. | LibXmlRuby.rb:3:15:3:20 | call to params | user input | +| LibXmlRuby.rb:6:30:6:36 | content | LibXmlRuby.rb:3:15:3:20 | call to params : | LibXmlRuby.rb:6:30:6:36 | content | Unsafe parsing of XML file from $@. | LibXmlRuby.rb:3:15:3:20 | call to params | user input | +| LibXmlRuby.rb:7:32:7:38 | content | LibXmlRuby.rb:3:15:3:20 | call to params : | LibXmlRuby.rb:7:32:7:38 | content | Unsafe parsing of XML file from $@. | LibXmlRuby.rb:3:15:3:20 | call to params | user input | +| LibXmlRuby.rb:8:30:8:36 | content | LibXmlRuby.rb:3:15:3:20 | call to params : | LibXmlRuby.rb:8:30:8:36 | content | Unsafe parsing of XML file from $@. | LibXmlRuby.rb:3:15:3:20 | call to params | user input | +| LibXmlRuby.rb:9:28:9:34 | content | LibXmlRuby.rb:3:15:3:20 | call to params : | LibXmlRuby.rb:9:28:9:34 | content | Unsafe parsing of XML file from $@. | LibXmlRuby.rb:3:15:3:20 | call to params | user input | +| LibXmlRuby.rb:11:26:11:32 | content | LibXmlRuby.rb:3:15:3:20 | call to params : | LibXmlRuby.rb:11:26:11:32 | content | Unsafe parsing of XML file from $@. | LibXmlRuby.rb:3:15:3:20 | call to params | user input | +| LibXmlRuby.rb:12:24:12:30 | content | LibXmlRuby.rb:3:15:3:20 | call to params : | LibXmlRuby.rb:12:24:12:30 | content | Unsafe parsing of XML file from $@. | LibXmlRuby.rb:3:15:3:20 | call to params | user input | +| Nokogiri.rb:5:26:5:32 | content | Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:5:26:5:32 | content | Unsafe parsing of XML file from $@. | Nokogiri.rb:3:15:3:20 | call to params | user input | +| Nokogiri.rb:6:26:6:32 | content | Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:6:26:6:32 | content | Unsafe parsing of XML file from $@. | Nokogiri.rb:3:15:3:20 | call to params | user input | +| Nokogiri.rb:7:26:7:32 | content | Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:7:26:7:32 | content | Unsafe parsing of XML file from $@. | Nokogiri.rb:3:15:3:20 | call to params | user input | +| Nokogiri.rb:8:26:8:32 | content | Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:8:26:8:32 | content | Unsafe parsing of XML file from $@. | Nokogiri.rb:3:15:3:20 | call to params | user input | +| Nokogiri.rb:9:26:9:32 | content | Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:9:26:9:32 | content | Unsafe parsing of XML file from $@. | Nokogiri.rb:3:15:3:20 | call to params | user input | +| Nokogiri.rb:11:26:11:32 | content | Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:11:26:11:32 | content | Unsafe parsing of XML file from $@. | Nokogiri.rb:3:15:3:20 | call to params | user input | +| Nokogiri.rb:12:26:12:32 | content | Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:12:26:12:32 | content | Unsafe parsing of XML file from $@. | Nokogiri.rb:3:15:3:20 | call to params | user input | +| Nokogiri.rb:15:26:15:32 | content | Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:15:26:15:32 | content | Unsafe parsing of XML file from $@. | Nokogiri.rb:3:15:3:20 | call to params | user input | +| Nokogiri.rb:16:26:16:32 | content | Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:16:26:16:32 | content | Unsafe parsing of XML file from $@. | Nokogiri.rb:3:15:3:20 | call to params | user input | +| Nokogiri.rb:18:26:18:32 | content | Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:18:26:18:32 | content | Unsafe parsing of XML file from $@. | Nokogiri.rb:3:15:3:20 | call to params | user input | +| Nokogiri.rb:19:26:19:32 | content | Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:19:26:19:32 | content | Unsafe parsing of XML file from $@. | Nokogiri.rb:3:15:3:20 | call to params | user input | +| Nokogiri.rb:22:26:22:32 | content | Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:22:26:22:32 | content | Unsafe parsing of XML file from $@. | Nokogiri.rb:3:15:3:20 | call to params | user input | +| Nokogiri.rb:25:26:25:32 | content | Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:25:26:25:32 | content | Unsafe parsing of XML file from $@. | Nokogiri.rb:3:15:3:20 | call to params | user input | +| Nokogiri.rb:27:26:27:32 | content | Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:27:26:27:32 | content | Unsafe parsing of XML file from $@. | Nokogiri.rb:3:15:3:20 | call to params | user input | +| Nokogiri.rb:28:26:28:32 | content | Nokogiri.rb:3:15:3:20 | call to params : | Nokogiri.rb:28:26:28:32 | content | Unsafe parsing of XML file from $@. | Nokogiri.rb:3:15:3:20 | call to params | user input | diff --git a/ruby/ql/test/query-tests/security/cwe-611/Xxe.qlref b/ruby/ql/test/query-tests/security/cwe-611/Xxe.qlref new file mode 100644 index 00000000000..8ed653a4869 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-611/Xxe.qlref @@ -0,0 +1 @@ +queries/security/cwe-611/Xxe.ql diff --git a/ruby/ql/test/query-tests/security/cwe-732/FilePermissions.rb b/ruby/ql/test/query-tests/security/cwe-732/FilePermissions.rb new file mode 100644 index 00000000000..305bdb2d147 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-732/FilePermissions.rb @@ -0,0 +1,73 @@ +require "fileutils" + +def run_chmod_1(filename) + # BAD: sets file as world writable + FileUtils.chmod 0222, filename + # BAD: sets file as world writable + FileUtils.chmod 0622, filename + # BAD: sets file as world readable + FileUtils.chmod 0755, filename + # BAD: sets file as world readable + writable + FileUtils.chmod 0777, filename +end + +module DummyModule + def chmod(mode, list, options = {} ) + list + end +end + +def run_chmod_2(filename) + foo = File + bar = foo + baz = DummyModule + # GOOD: DummyModule is not a known class that performs file permission modifications + baz.chmod 0755, filename + baz = bar + # BAD: sets file as world readable + baz.chmod 0755, filename +end + +def run_chmod_3(filename) + # TODO: we currently miss this + foo = FileUtils + bar, baz = foo, 7 + # BAD: sets file as world readable + bar.chmod 0755, filename +end + +def run_chmod_4(filename) + # GOOD: no group/world access + FileUtils.chmod 0700, filename + # GOOD: group/world execute bit only + FileUtils.chmod 0711, filename + # GOOD: world execute bit only + FileUtils.chmod 0701, filename + # GOOD: group execute bit only + FileUtils.chmod 0710, filename +end + +def run_chmod_5(filename) + perm = 0777 + # BAD: sets world rwx + FileUtils.chmod perm, filename + perm2 = perm + # BAD: sets world rwx + FileUtils.chmod perm2, filename + + perm = "u=wrx,g=rwx,o=x" + perm2 = perm + # BAD: sets group rwx + FileUtils.chmod perm2, filename + # BAD: sets file as world readable + FileUtils.chmod "u=rwx,o+r", filename + # GOOD: sets file as group/world unreadable + FileUtils.chmod "u=rwx,go-r", filename + # BAD: sets group/world as +rw + FileUtils.chmod "a+rw", filename +end + +def run_chmod_R(filename) + # BAD: sets file as world readable + FileUtils.chmod_R 0755, filename +end diff --git a/ruby/ql/test/query-tests/security/cwe-732/WeakFilePermissions.expected b/ruby/ql/test/query-tests/security/cwe-732/WeakFilePermissions.expected new file mode 100644 index 00000000000..f7335edd5e0 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-732/WeakFilePermissions.expected @@ -0,0 +1,31 @@ +edges +| FilePermissions.rb:51:10:51:13 | 0777 : | FilePermissions.rb:53:19:53:22 | perm | +| FilePermissions.rb:51:10:51:13 | 0777 : | FilePermissions.rb:56:19:56:23 | perm2 | +| FilePermissions.rb:58:10:58:26 | "u=wrx,g=rwx,o=x" : | FilePermissions.rb:61:19:61:23 | perm2 | +nodes +| FilePermissions.rb:5:19:5:22 | 0222 | semmle.label | 0222 | +| FilePermissions.rb:7:19:7:22 | 0622 | semmle.label | 0622 | +| FilePermissions.rb:9:19:9:22 | 0755 | semmle.label | 0755 | +| FilePermissions.rb:11:19:11:22 | 0777 | semmle.label | 0777 | +| FilePermissions.rb:28:13:28:16 | 0755 | semmle.label | 0755 | +| FilePermissions.rb:51:10:51:13 | 0777 : | semmle.label | 0777 : | +| FilePermissions.rb:53:19:53:22 | perm | semmle.label | perm | +| FilePermissions.rb:56:19:56:23 | perm2 | semmle.label | perm2 | +| FilePermissions.rb:58:10:58:26 | "u=wrx,g=rwx,o=x" : | semmle.label | "u=wrx,g=rwx,o=x" : | +| FilePermissions.rb:61:19:61:23 | perm2 | semmle.label | perm2 | +| FilePermissions.rb:63:19:63:29 | "u=rwx,o+r" | semmle.label | "u=rwx,o+r" | +| FilePermissions.rb:67:19:67:24 | "a+rw" | semmle.label | "a+rw" | +| FilePermissions.rb:72:21:72:24 | 0755 | semmle.label | 0755 | +subpaths +#select +| FilePermissions.rb:5:19:5:22 | 0222 | FilePermissions.rb:5:19:5:22 | 0222 | FilePermissions.rb:5:19:5:22 | 0222 | Overly permissive mask in $@ sets file to $@. | FilePermissions.rb:5:3:5:32 | call to chmod | call to chmod | FilePermissions.rb:5:19:5:22 | 0222 | 0222 | +| FilePermissions.rb:7:19:7:22 | 0622 | FilePermissions.rb:7:19:7:22 | 0622 | FilePermissions.rb:7:19:7:22 | 0622 | Overly permissive mask in $@ sets file to $@. | FilePermissions.rb:7:3:7:32 | call to chmod | call to chmod | FilePermissions.rb:7:19:7:22 | 0622 | 0622 | +| FilePermissions.rb:9:19:9:22 | 0755 | FilePermissions.rb:9:19:9:22 | 0755 | FilePermissions.rb:9:19:9:22 | 0755 | Overly permissive mask in $@ sets file to $@. | FilePermissions.rb:9:3:9:32 | call to chmod | call to chmod | FilePermissions.rb:9:19:9:22 | 0755 | 0755 | +| FilePermissions.rb:11:19:11:22 | 0777 | FilePermissions.rb:11:19:11:22 | 0777 | FilePermissions.rb:11:19:11:22 | 0777 | Overly permissive mask in $@ sets file to $@. | FilePermissions.rb:11:3:11:32 | call to chmod | call to chmod | FilePermissions.rb:11:19:11:22 | 0777 | 0777 | +| FilePermissions.rb:28:13:28:16 | 0755 | FilePermissions.rb:28:13:28:16 | 0755 | FilePermissions.rb:28:13:28:16 | 0755 | Overly permissive mask in $@ sets file to $@. | FilePermissions.rb:28:3:28:26 | call to chmod | call to chmod | FilePermissions.rb:28:13:28:16 | 0755 | 0755 | +| FilePermissions.rb:51:10:51:13 | 0777 | FilePermissions.rb:51:10:51:13 | 0777 : | FilePermissions.rb:53:19:53:22 | perm | Overly permissive mask in $@ sets file to $@. | FilePermissions.rb:53:3:53:32 | call to chmod | call to chmod | FilePermissions.rb:51:10:51:13 | 0777 | 0777 | +| FilePermissions.rb:51:10:51:13 | 0777 | FilePermissions.rb:51:10:51:13 | 0777 : | FilePermissions.rb:56:19:56:23 | perm2 | Overly permissive mask in $@ sets file to $@. | FilePermissions.rb:56:3:56:33 | call to chmod | call to chmod | FilePermissions.rb:51:10:51:13 | 0777 | 0777 | +| FilePermissions.rb:58:10:58:26 | "u=wrx,g=rwx,o=x" | FilePermissions.rb:58:10:58:26 | "u=wrx,g=rwx,o=x" : | FilePermissions.rb:61:19:61:23 | perm2 | Overly permissive mask in $@ sets file to $@. | FilePermissions.rb:61:3:61:33 | call to chmod | call to chmod | FilePermissions.rb:58:10:58:26 | "u=wrx,g=rwx,o=x" | "u=wrx,g=rwx,o=x" | +| FilePermissions.rb:63:19:63:29 | "u=rwx,o+r" | FilePermissions.rb:63:19:63:29 | "u=rwx,o+r" | FilePermissions.rb:63:19:63:29 | "u=rwx,o+r" | Overly permissive mask in $@ sets file to $@. | FilePermissions.rb:63:3:63:39 | call to chmod | call to chmod | FilePermissions.rb:63:19:63:29 | "u=rwx,o+r" | "u=rwx,o+r" | +| FilePermissions.rb:67:19:67:24 | "a+rw" | FilePermissions.rb:67:19:67:24 | "a+rw" | FilePermissions.rb:67:19:67:24 | "a+rw" | Overly permissive mask in $@ sets file to $@. | FilePermissions.rb:67:3:67:34 | call to chmod | call to chmod | FilePermissions.rb:67:19:67:24 | "a+rw" | "a+rw" | +| FilePermissions.rb:72:21:72:24 | 0755 | FilePermissions.rb:72:21:72:24 | 0755 | FilePermissions.rb:72:21:72:24 | 0755 | Overly permissive mask in $@ sets file to $@. | FilePermissions.rb:72:3:72:34 | call to chmod_R | call to chmod_R | FilePermissions.rb:72:21:72:24 | 0755 | 0755 | diff --git a/ruby/ql/test/query-tests/security/cwe-732/WeakFilePermissions.qlref b/ruby/ql/test/query-tests/security/cwe-732/WeakFilePermissions.qlref new file mode 100644 index 00000000000..bf19b31509d --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-732/WeakFilePermissions.qlref @@ -0,0 +1 @@ +queries/security/cwe-732/WeakFilePermissions.ql diff --git a/ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.expected b/ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.expected new file mode 100644 index 00000000000..71c50be564f --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.expected @@ -0,0 +1,39 @@ +edges +| HardcodedCredentials.rb:12:19:12:64 | "4NQX/CqB5Ae98zFUmwj1DMpF7azsh..." : | HardcodedCredentials.rb:1:23:1:30 | password | +| HardcodedCredentials.rb:18:19:18:72 | ... + ... : | HardcodedCredentials.rb:1:23:1:30 | password | +| HardcodedCredentials.rb:18:27:18:72 | "ogH6qSYWGdbR/2WOGYa7eZ/tObL+G..." : | HardcodedCredentials.rb:18:19:18:72 | ... + ... : | +| HardcodedCredentials.rb:20:11:20:76 | "3jOe7sXKX6Tx52qHWUVqh2t9LNsE+..." : | HardcodedCredentials.rb:23:19:23:20 | pw : | +| HardcodedCredentials.rb:21:12:21:37 | "4fQuzXef4f2yow8KWvIJTA==" : | HardcodedCredentials.rb:23:19:23:20 | pw : | +| HardcodedCredentials.rb:23:19:23:20 | pw : | HardcodedCredentials.rb:1:23:1:30 | password | +| HardcodedCredentials.rb:38:40:38:85 | "kdW/xVhiv6y1fQQNevDpUaq+2rfPK..." : | HardcodedCredentials.rb:31:18:31:23 | passwd | +| HardcodedCredentials.rb:43:29:43:43 | "user@test.com" : | HardcodedCredentials.rb:43:18:43:25 | username | +| HardcodedCredentials.rb:43:57:43:70 | "abcdef123456" : | HardcodedCredentials.rb:43:46:43:53 | password | +nodes +| HardcodedCredentials.rb:1:23:1:30 | password | semmle.label | password | +| HardcodedCredentials.rb:4:20:4:65 | "xwjVWdfzfRlbcgKkbSfG/xSrUeHYq..." | semmle.label | "xwjVWdfzfRlbcgKkbSfG/xSrUeHYq..." | +| HardcodedCredentials.rb:8:30:8:75 | "X6BLgRWSAtAWG/GaHS+WGGW2K7zZF..." | semmle.label | "X6BLgRWSAtAWG/GaHS+WGGW2K7zZF..." | +| HardcodedCredentials.rb:12:19:12:64 | "4NQX/CqB5Ae98zFUmwj1DMpF7azsh..." : | semmle.label | "4NQX/CqB5Ae98zFUmwj1DMpF7azsh..." : | +| HardcodedCredentials.rb:15:30:15:75 | "WLC17dLQ9P8YlQvqm77qplOMm5pd1..." | semmle.label | "WLC17dLQ9P8YlQvqm77qplOMm5pd1..." | +| HardcodedCredentials.rb:18:19:18:72 | ... + ... : | semmle.label | ... + ... : | +| HardcodedCredentials.rb:18:27:18:72 | "ogH6qSYWGdbR/2WOGYa7eZ/tObL+G..." : | semmle.label | "ogH6qSYWGdbR/2WOGYa7eZ/tObL+G..." : | +| HardcodedCredentials.rb:20:11:20:76 | "3jOe7sXKX6Tx52qHWUVqh2t9LNsE+..." : | semmle.label | "3jOe7sXKX6Tx52qHWUVqh2t9LNsE+..." : | +| HardcodedCredentials.rb:21:12:21:37 | "4fQuzXef4f2yow8KWvIJTA==" : | semmle.label | "4fQuzXef4f2yow8KWvIJTA==" : | +| HardcodedCredentials.rb:23:19:23:20 | pw : | semmle.label | pw : | +| HardcodedCredentials.rb:31:18:31:23 | passwd | semmle.label | passwd | +| HardcodedCredentials.rb:38:40:38:85 | "kdW/xVhiv6y1fQQNevDpUaq+2rfPK..." : | semmle.label | "kdW/xVhiv6y1fQQNevDpUaq+2rfPK..." : | +| HardcodedCredentials.rb:43:18:43:25 | username | semmle.label | username | +| HardcodedCredentials.rb:43:29:43:43 | "user@test.com" : | semmle.label | "user@test.com" : | +| HardcodedCredentials.rb:43:46:43:53 | password | semmle.label | password | +| HardcodedCredentials.rb:43:57:43:70 | "abcdef123456" : | semmle.label | "abcdef123456" : | +subpaths +#select +| HardcodedCredentials.rb:4:20:4:65 | "xwjVWdfzfRlbcgKkbSfG/xSrUeHYq..." | HardcodedCredentials.rb:4:20:4:65 | "xwjVWdfzfRlbcgKkbSfG/xSrUeHYq..." | HardcodedCredentials.rb:4:20:4:65 | "xwjVWdfzfRlbcgKkbSfG/xSrUeHYq..." | Use of $@. | HardcodedCredentials.rb:4:20:4:65 | "xwjVWdfzfRlbcgKkbSfG/xSrUeHYq..." | hardcoded credentials | +| HardcodedCredentials.rb:8:30:8:75 | "X6BLgRWSAtAWG/GaHS+WGGW2K7zZF..." | HardcodedCredentials.rb:8:30:8:75 | "X6BLgRWSAtAWG/GaHS+WGGW2K7zZF..." | HardcodedCredentials.rb:8:30:8:75 | "X6BLgRWSAtAWG/GaHS+WGGW2K7zZF..." | Use of $@. | HardcodedCredentials.rb:8:30:8:75 | "X6BLgRWSAtAWG/GaHS+WGGW2K7zZF..." | hardcoded credentials | +| HardcodedCredentials.rb:12:19:12:64 | "4NQX/CqB5Ae98zFUmwj1DMpF7azsh..." | HardcodedCredentials.rb:12:19:12:64 | "4NQX/CqB5Ae98zFUmwj1DMpF7azsh..." : | HardcodedCredentials.rb:1:23:1:30 | password | Use of $@. | HardcodedCredentials.rb:12:19:12:64 | "4NQX/CqB5Ae98zFUmwj1DMpF7azsh..." | hardcoded credentials | +| HardcodedCredentials.rb:15:30:15:75 | "WLC17dLQ9P8YlQvqm77qplOMm5pd1..." | HardcodedCredentials.rb:15:30:15:75 | "WLC17dLQ9P8YlQvqm77qplOMm5pd1..." | HardcodedCredentials.rb:15:30:15:75 | "WLC17dLQ9P8YlQvqm77qplOMm5pd1..." | Use of $@. | HardcodedCredentials.rb:15:30:15:75 | "WLC17dLQ9P8YlQvqm77qplOMm5pd1..." | hardcoded credentials | +| HardcodedCredentials.rb:18:27:18:72 | "ogH6qSYWGdbR/2WOGYa7eZ/tObL+G..." | HardcodedCredentials.rb:18:27:18:72 | "ogH6qSYWGdbR/2WOGYa7eZ/tObL+G..." : | HardcodedCredentials.rb:1:23:1:30 | password | Use of $@. | HardcodedCredentials.rb:18:27:18:72 | "ogH6qSYWGdbR/2WOGYa7eZ/tObL+G..." | hardcoded credentials | +| HardcodedCredentials.rb:20:11:20:76 | "3jOe7sXKX6Tx52qHWUVqh2t9LNsE+..." | HardcodedCredentials.rb:20:11:20:76 | "3jOe7sXKX6Tx52qHWUVqh2t9LNsE+..." : | HardcodedCredentials.rb:1:23:1:30 | password | Use of $@. | HardcodedCredentials.rb:20:11:20:76 | "3jOe7sXKX6Tx52qHWUVqh2t9LNsE+..." | hardcoded credentials | +| HardcodedCredentials.rb:21:12:21:37 | "4fQuzXef4f2yow8KWvIJTA==" | HardcodedCredentials.rb:21:12:21:37 | "4fQuzXef4f2yow8KWvIJTA==" : | HardcodedCredentials.rb:1:23:1:30 | password | Use of $@. | HardcodedCredentials.rb:21:12:21:37 | "4fQuzXef4f2yow8KWvIJTA==" | hardcoded credentials | +| HardcodedCredentials.rb:38:40:38:85 | "kdW/xVhiv6y1fQQNevDpUaq+2rfPK..." | HardcodedCredentials.rb:38:40:38:85 | "kdW/xVhiv6y1fQQNevDpUaq+2rfPK..." : | HardcodedCredentials.rb:31:18:31:23 | passwd | Use of $@. | HardcodedCredentials.rb:38:40:38:85 | "kdW/xVhiv6y1fQQNevDpUaq+2rfPK..." | hardcoded credentials | +| HardcodedCredentials.rb:43:29:43:43 | "user@test.com" | HardcodedCredentials.rb:43:29:43:43 | "user@test.com" : | HardcodedCredentials.rb:43:18:43:25 | username | Use of $@. | HardcodedCredentials.rb:43:29:43:43 | "user@test.com" | hardcoded credentials | +| HardcodedCredentials.rb:43:57:43:70 | "abcdef123456" | HardcodedCredentials.rb:43:57:43:70 | "abcdef123456" : | HardcodedCredentials.rb:43:46:43:53 | password | Use of $@. | HardcodedCredentials.rb:43:57:43:70 | "abcdef123456" | hardcoded credentials | diff --git a/ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.qlref b/ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.qlref new file mode 100644 index 00000000000..e65b7754872 --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.qlref @@ -0,0 +1 @@ +queries/security/cwe-798/HardcodedCredentials.ql diff --git a/ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.rb b/ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.rb new file mode 100644 index 00000000000..57f05a25fdf --- /dev/null +++ b/ruby/ql/test/query-tests/security/cwe-798/HardcodedCredentials.rb @@ -0,0 +1,45 @@ +def authenticate(uid, password, cert: nil) + if cert != nil then + # comparison with hardcoded credential + return cert == "xwjVWdfzfRlbcgKkbSfG/xSrUeHYqxPgz9WKN3Yow1o=" + end + + # comparison with hardcoded credential + uid == 123 and password == "X6BLgRWSAtAWG/GaHS+WGGW2K7zZFTAjJ54fGSudHJk=" +end + +# call with hardcoded credential as argument +authenticate(123, "4NQX/CqB5Ae98zFUmwj1DMpF7azshxSvb0Jo4gIFmIQ=") + +# call with hardcoded credential as argument +authenticate(456, nil, cert: "WLC17dLQ9P8YlQvqm77qplOMm5pd1q25Q2onWqu78JI=") + +# concatenation involving literal +authenticate(789, "pw:" + "ogH6qSYWGdbR/2WOGYa7eZ/tObL+GtqDPx6q37BTTRQ=") + +pw_left = "3jOe7sXKX6Tx52qHWUVqh2t9LNsE+ZXFj2qw6asRARTV2deAXFKkMTVOoaFYom1Q" +pw_right = "4fQuzXef4f2yow8KWvIJTA==" +pw = pw_left + pw_right +authenticate(999, pw) + +passwd = gets.chomp +# call with hardcoded credential-like value, but not to a potential credential sink (should not be flagged) +authenticate("gowLsSGfPbh/ZS60k+LQQBhcq1tsh/YgbvNmDauQr5Q=", passwd) + +module Passwords + class KnownPasswords + def include?(passwd) + passwd == "foo" + end + end +end + +# Call to object method +Passwords::KnownPasswords.new.include?("kdW/xVhiv6y1fQQNevDpUaq+2rfPKfh+teE/45zS7bc=") + +# Call to unrelated method with same name (should not be flagged) +"foobar".include?("foo") + +def default_cred(username = "user@test.com", password = "abcdef123456") + username +end \ No newline at end of file diff --git a/ruby/ql/test/query-tests/summary/LinesOfCode.expected b/ruby/ql/test/query-tests/summary/LinesOfCode.expected new file mode 100644 index 00000000000..f17cce5aa70 --- /dev/null +++ b/ruby/ql/test/query-tests/summary/LinesOfCode.expected @@ -0,0 +1 @@ +| 9 | diff --git a/ruby/ql/test/query-tests/summary/LinesOfCode.qlref b/ruby/ql/test/query-tests/summary/LinesOfCode.qlref new file mode 100644 index 00000000000..84278cfc96b --- /dev/null +++ b/ruby/ql/test/query-tests/summary/LinesOfCode.qlref @@ -0,0 +1 @@ +queries/summary/LinesOfCode.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/summary/LinesOfUserCode.expected b/ruby/ql/test/query-tests/summary/LinesOfUserCode.expected new file mode 100644 index 00000000000..69f8503f966 --- /dev/null +++ b/ruby/ql/test/query-tests/summary/LinesOfUserCode.expected @@ -0,0 +1 @@ +| 5 | diff --git a/ruby/ql/test/query-tests/summary/LinesOfUserCode.qlref b/ruby/ql/test/query-tests/summary/LinesOfUserCode.qlref new file mode 100644 index 00000000000..4114db632a2 --- /dev/null +++ b/ruby/ql/test/query-tests/summary/LinesOfUserCode.qlref @@ -0,0 +1 @@ +queries/summary/LinesOfUserCode.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/summary/src/foo.rb b/ruby/ql/test/query-tests/summary/src/foo.rb new file mode 100644 index 00000000000..2f0f2acc09a --- /dev/null +++ b/ruby/ql/test/query-tests/summary/src/foo.rb @@ -0,0 +1,11 @@ +# comment + +def hello + p "hello foo" +end + +# another one + +hello + +p "more code here" diff --git a/ruby/ql/test/query-tests/summary/src/vendor/cache/lib.rb b/ruby/ql/test/query-tests/summary/src/vendor/cache/lib.rb new file mode 100644 index 00000000000..f75db7941db --- /dev/null +++ b/ruby/ql/test/query-tests/summary/src/vendor/cache/lib.rb @@ -0,0 +1,9 @@ +# comment + +def hello + p "hello lib" +end + +# another one + +hello diff --git a/ruby/scripts/create-extractor-pack.ps1 b/ruby/scripts/create-extractor-pack.ps1 new file mode 100644 index 00000000000..43546aa9056 --- /dev/null +++ b/ruby/scripts/create-extractor-pack.ps1 @@ -0,0 +1,12 @@ +cargo build --release + +cargo run --release -p ruby-generator -- --dbscheme ql/lib/ruby.dbscheme --library ql/lib/codeql/ruby/ast/internal/TreeSitter.qll +codeql query format -i ql\lib\codeql/ruby\ast\internal\TreeSitter.qll + +rm -Recurse -Force extractor-pack +mkdir extractor-pack | Out-Null +cp codeql-extractor.yml, ql\lib\ruby.dbscheme, ql\lib\ruby.dbscheme.stats extractor-pack +cp -Recurse tools extractor-pack +mkdir extractor-pack\tools\win64 | Out-Null +cp target\release\ruby-extractor.exe extractor-pack\tools\win64\extractor.exe +cp target\release\ruby-autobuilder.exe extractor-pack\tools\win64\autobuilder.exe diff --git a/ruby/scripts/create-extractor-pack.sh b/ruby/scripts/create-extractor-pack.sh new file mode 100755 index 00000000000..756358902af --- /dev/null +++ b/ruby/scripts/create-extractor-pack.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -eux + +if [[ "$OSTYPE" == "linux-gnu"* ]]; then + platform="linux64" +elif [[ "$OSTYPE" == "darwin"* ]]; then + platform="osx64" +else + echo "Unknown OS" + exit 1 +fi + +cargo build --release + +cargo run --release -p ruby-generator -- --dbscheme ql/lib/ruby.dbscheme --library ql/lib/codeql/ruby/ast/internal/TreeSitter.qll +codeql query format -i ql/lib/codeql/ruby/ast/internal/TreeSitter.qll + +rm -rf extractor-pack +mkdir -p extractor-pack +cp -r codeql-extractor.yml tools ql/lib/ruby.dbscheme ql/lib/ruby.dbscheme.stats extractor-pack/ +mkdir -p extractor-pack/tools/${platform} +cp target/release/ruby-extractor extractor-pack/tools/${platform}/extractor +cp target/release/ruby-autobuilder extractor-pack/tools/${platform}/autobuilder diff --git a/ruby/scripts/merge_stats.py b/ruby/scripts/merge_stats.py new file mode 100644 index 00000000000..d2cbe0a3e64 --- /dev/null +++ b/ruby/scripts/merge_stats.py @@ -0,0 +1,90 @@ +#!/usr/bin/python + +# This script merges a number of stats files to produce a single stats file. + +import sys +from lxml import etree +import argparse + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument('--output', required=True, help="Path of the output file.") + parser.add_argument('--normalise', required=True, help="Name of the relation to normalise the sizes on.") + parser.add_argument('--unscaled-stats', default=[], action='append', help="A stats file which should not be normalised.") + parser.add_argument('inputs', nargs='*', help="The other stats files") + return parser.parse_args() + +def die(msg): + sys.stderr.write('Error: ' + msg + '\n') + sys.exit(1) + +def main(): + args = parse_args() + inputs = args.inputs + output = args.output + normalise = args.normalise + unscaled_stats = args.unscaled_stats + + print("Merging %s into %s normalising on '%s'." % (', '.join(inputs), output, normalise)) + do_xml_files(output, inputs, unscaled_stats, normalise) + +def read_sized_xml(xml_file, name): + # Take the size of the named table as the size of the codebase + xml = etree.parse(xml_file) + ns = xml.xpath("stats/relation[name='%s']/cardinality" % name) + if len(ns) == 0: + die('Sized stats file ' + xml_file + ' does not have a cardinality for normalisation relation ' + name + '.') + n = ns[0] + size = int(n.text) + return (xml, size) + +def scale(xml, size, max_size): + # Scale up the contents of all the and tags + for v in xml.xpath(".//v|.//cardinality"): + v.text = str((int(v.text) * max_size) // size) + +def do_xml_files(output, scaled_xml_files, unscaled_xml_files, name): + # The result starts off empty + result = etree.Element("dbstats") + + # Scale all of the stats so that they might have come code bases of + # the same size + sized_xmls = [read_sized_xml(xml_file, name) + for xml_file in scaled_xml_files] + if sized_xmls != []: + max_size = max([size for (xml, size) in sized_xmls]) + for (xml, size) in sized_xmls: + scale(xml, size, max_size) + unsized_xmls = list(map(etree.parse, unscaled_xml_files)) + xmls = [xml for (xml, size) in sized_xmls] + unsized_xmls + + # Put all the stats in a single XML doc so that we can search them + # more easily + merged_xml = etree.Element("merged") + for xml in xmls: + merged_xml.append(xml.getroot()) + + # For each value of , take the tag with the biggest + typesizes = etree.SubElement(result, "typesizes") + typenames = sorted(set ([ typesize.find("k").text for typesize in merged_xml.xpath("dbstats/typesizes/e")])) + for typename in typenames: + xs = merged_xml.xpath("dbstats/typesizes/e[k='" + typename + "']") + sized_xs = [(int(x.find("v").text), x) for x in xs] + (_, x) = max(sized_xs, key = lambda p: p[0]) + typesizes.append(x) + + # For each value of , take the tag with + # the biggest + stats = etree.SubElement(result, "stats") + + relnames = sorted(set ([relation.find("name").text for relation in merged_xml.xpath("dbstats/stats/relation") ])) + for relname in relnames: + rels = merged_xml.xpath("dbstats/stats/relation[name='" + relname + "']") + sized_rels = [(int(rel.find("cardinality").text), rel) for rel in rels] + (_, rel) = max(sized_rels, key = lambda p: p[0]) + stats.append(rel) + + with open(output, 'wb') as f: + f.write(etree.tostring(result, pretty_print=True)) + +main() diff --git a/ruby/scripts/prepare-db-upgrade.sh b/ruby/scripts/prepare-db-upgrade.sh new file mode 100755 index 00000000000..4cb1957ef85 --- /dev/null +++ b/ruby/scripts/prepare-db-upgrade.sh @@ -0,0 +1,106 @@ +#!/bin/sh +# +# Prepare the upgrade script directory for a Ruby database schema upgrade. + +set -e +set -u + +app_name="$(basename "$0")" + +usage() +{ + exit_code="$1" + shift + + cat >&2 <]" + +--prev-hash + Hash/branch to use to get SHA1 for previous DB scheme. + Default: origin/main + +Must be run within the git repo needing an update. +EOF + exit "${exit_code}" +} + +prev_hash="origin/main" + +while [ $# -gt 0 ]; do + case "$1" in + -x) + set -x + ;; + -h | --help) + usage 0 + ;; + --prev-hash) + if [ $# -eq 1 ]; then + usage 2 "--prev-hash requires Commit/Branch option" + fi + shift + prev_hash="$1" + ;; + --) + shift + break + ;; + -*) + usage 2 "Unrecognised option: $1" + ;; + *) + break + ;; + esac + shift +done + +if [ $# -gt 0 ]; then + usage 2 "Unrecognised operand: $1" +fi + +scheme_file="ql/lib/ruby.dbscheme" +upgrade_root="ql/lib/upgrades" + +check_hash_valid() +{ + if [ ${#2} -ne 40 ]; then + echo "Did not get expected $1 hash: $2" >&2 + exit 2 + fi +} + +# Get the hash of the previous and current DB Schema files +prev_hash="$(git show "${prev_hash}:${scheme_file}" | git hash-object --stdin)" +check_hash_valid previous "${prev_hash}" +current_hash="$(git hash-object "${scheme_file}")" +check_hash_valid current "${current_hash}" +if [ "${current_hash}" = "${prev_hash}" ]; then + echo "No work to be done." + exit +fi + +# Copy current and new dbscheme into the upgrade dir +upgradedir="${upgrade_root}/${prev_hash}" +mkdir -p "${upgradedir}" + +cp "${scheme_file}" "${upgradedir}" +git cat-file blob "${prev_hash}" > "${upgradedir}/old.dbscheme" + +# Create the template upgrade.properties file. +cat < "${upgradedir}/upgrade.properties" +description: +compatibility: full|backwards|partial|breaking +EOF + +# Tell user what we've done +cat <