mirror of
https://github.com/github/codeql.git
synced 2026-05-03 04:39:29 +02:00
Merge remote-tracking branch 'origin/main' into jorgectf/python/insecure-cookie
This commit is contained in:
@@ -1,7 +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" ] }
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -48,3 +48,6 @@
|
||||
*.gif -text
|
||||
*.dll -text
|
||||
*.pdb -text
|
||||
|
||||
java/ql/test/stubs/**/*.java linguist-generated=true
|
||||
java/ql/test/experimental/stubs/**/*.java linguist-generated=true
|
||||
14
.github/actions/fetch-codeql/action.yml
vendored
Normal file
14
.github/actions/fetch-codeql/action.yml
vendored
Normal file
@@ -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 }}
|
||||
18
.github/dependabot.yml
vendored
Normal file
18
.github/dependabot.yml
vendored
Normal file
@@ -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"
|
||||
4
.github/labeler.yml
vendored
4
.github/labeler.yml
vendored
@@ -18,6 +18,10 @@ Python:
|
||||
- python/**/*
|
||||
- change-notes/**/*python*
|
||||
|
||||
Ruby:
|
||||
- ruby/**/*
|
||||
- change-notes/**/*ruby*
|
||||
|
||||
documentation:
|
||||
- "**/*.qhelp"
|
||||
- "**/*.md"
|
||||
|
||||
11
.github/workflows/codeql-analysis.yml
vendored
11
.github/workflows/codeql-analysis.yml
vendored
@@ -11,6 +11,8 @@ on:
|
||||
- 'rc/*'
|
||||
paths:
|
||||
- 'csharp/**'
|
||||
- '.github/codeql/**'
|
||||
- '.github/workflows/codeql-analysis.yml'
|
||||
schedule:
|
||||
- cron: '0 9 * * 1'
|
||||
|
||||
@@ -38,8 +40,8 @@ jobs:
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@main
|
||||
#- name: Autobuild
|
||||
# uses: github/codeql-action/autobuild@main
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -48,9 +50,8 @@ jobs:
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
- run: |
|
||||
dotnet build csharp
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@main
|
||||
|
||||
@@ -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'
|
||||
|
||||
2
.github/workflows/csv-coverage-update.yml
vendored
2
.github/workflows/csv-coverage-update.yml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
jobs:
|
||||
update:
|
||||
name: Update framework coverage report
|
||||
if: github.event.repository.fork == false
|
||||
if: github.repository == 'github/codeql'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
||||
39
.github/workflows/qhelp-pr-preview.yml
vendored
Normal file
39
.github/workflows/qhelp-pr-preview.yml
vendored
Normal file
@@ -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 "<details> <summary>${path}</summary>"
|
||||
echo
|
||||
codeql generate query-help --format=markdown ${path}
|
||||
echo "</details>"
|
||||
done) | gh pr comment "${{ github.event.pull_request.number }}" -F -
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
232
.github/workflows/ruby-build.yml
vendored
Normal file
232
.github/workflows/ruby-build.yml
vendored
Normal file
@@ -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
|
||||
71
.github/workflows/ruby-dataset-measure.yml
vendored
Normal file
71
.github/workflows/ruby-dataset-measure.yml
vendored
Normal file
@@ -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
|
||||
48
.github/workflows/ruby-qltest.yml
vendored
Normal file
48
.github/workflows/ruby-qltest.yml
vendored
Normal file
@@ -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
|
||||
20
.github/workflows/sync-files.yml
vendored
Normal file
20
.github/workflows/sync-files.yml
vendored
Normal file
@@ -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
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -24,3 +24,6 @@
|
||||
/codeql/
|
||||
|
||||
csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json
|
||||
|
||||
# Avoid committing cached package components
|
||||
.codeql
|
||||
|
||||
@@ -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
|
||||
/docs/query-*-style-guide.md @github/codeql-analysis-reviewers
|
||||
|
||||
@@ -4,8 +4,8 @@ This open source repository contains the standard CodeQL libraries and queries t
|
||||
|
||||
## How do I learn CodeQL and run queries?
|
||||
|
||||
There is [extensive documentation](https://help.semmle.com/QL/learn-ql/) on getting started with writing CodeQL.
|
||||
You can use the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com or the [CodeQL for Visual Studio Code](https://help.semmle.com/codeql/codeql-for-vscode.html) extension to try out your queries on any open source project that's currently being analyzed.
|
||||
There is [extensive documentation](https://codeql.github.com/docs/) on getting started with writing CodeQL.
|
||||
You can use the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com or the [CodeQL for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/) extension to try out your queries on any open source project that's currently being analyzed.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
@@ -1,332 +1,339 @@
|
||||
{
|
||||
"DataFlow Java/C++/C#/Python": [
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll",
|
||||
"python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl.qll",
|
||||
"python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl2.qll",
|
||||
"python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl3.qll",
|
||||
"python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl4.qll"
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll",
|
||||
"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",
|
||||
"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/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll"
|
||||
"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",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll"
|
||||
],
|
||||
"TaintTracking::Configuration Java/C++/C#/Python": [
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"python/ql/src/semmle/python/dataflow/new/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"python/ql/src/semmle/python/dataflow/new/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"python/ql/src/semmle/python/dataflow/new/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
"python/ql/src/semmle/python/dataflow/new/internal/tainttracking4/TaintTrackingImpl.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"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",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll"
|
||||
],
|
||||
"DataFlow Java/C++/C#/Python Consistency checks": [
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll"
|
||||
"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",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll"
|
||||
],
|
||||
"DataFlow Java/C# Flow Summaries": [
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll"
|
||||
"java/ql/lib/semmle/code/java/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/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll"
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll"
|
||||
],
|
||||
"Sign Java/C#": [
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll"
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll"
|
||||
],
|
||||
"SignAnalysis Java/C#": [
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll"
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll"
|
||||
],
|
||||
"Bound Java/C#": [
|
||||
"java/ql/src/semmle/code/java/dataflow/Bound.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/Bound.qll"
|
||||
"java/ql/lib/semmle/code/java/dataflow/Bound.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/Bound.qll"
|
||||
],
|
||||
"ModulusAnalysis Java/C#": [
|
||||
"java/ql/src/semmle/code/java/dataflow/ModulusAnalysis.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/ModulusAnalysis.qll"
|
||||
"java/ql/lib/semmle/code/java/dataflow/ModulusAnalysis.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/ModulusAnalysis.qll"
|
||||
],
|
||||
"C++ SubBasicBlocks": [
|
||||
"cpp/ql/src/semmle/code/cpp/controlflow/SubBasicBlocks.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/controlflow/SubBasicBlocks.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll"
|
||||
],
|
||||
"IR Instruction": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll"
|
||||
],
|
||||
"IR IRBlock": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll"
|
||||
],
|
||||
"IR IRVariable": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRVariable.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll"
|
||||
],
|
||||
"IR IRFunction": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRFunction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRFunction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRFunction.qll"
|
||||
],
|
||||
"IR Operand": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Operand.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/Operand.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll"
|
||||
],
|
||||
"IR IRType": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/IRType.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/IRType.qll"
|
||||
],
|
||||
"IR IRConfiguration": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/IRConfiguration.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/IRConfiguration.qll"
|
||||
],
|
||||
"IR UseSoundEscapeAnalysis": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/UseSoundEscapeAnalysis.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/UseSoundEscapeAnalysis.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/UseSoundEscapeAnalysis.qll"
|
||||
],
|
||||
"IR IRFunctionBase": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/IRFunctionBase.qll"
|
||||
],
|
||||
"IR Operand Tag": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll"
|
||||
],
|
||||
"IR TInstruction": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TInstruction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll"
|
||||
],
|
||||
"IR TIRVariable": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/TIRVariable.qll"
|
||||
],
|
||||
"IR IR": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IR.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IR.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll"
|
||||
],
|
||||
"IR IRConsistency": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRConsistency.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRConsistency.qll"
|
||||
],
|
||||
"IR PrintIR": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/PrintIR.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll"
|
||||
],
|
||||
"IR IntegerConstant": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/internal/IntegerConstant.qll",
|
||||
"csharp/ql/src/experimental/ir/internal/IntegerConstant.qll"
|
||||
],
|
||||
"IR IntegerInteval": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerInterval.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/internal/IntegerInterval.qll",
|
||||
"csharp/ql/src/experimental/ir/internal/IntegerInterval.qll"
|
||||
],
|
||||
"IR IntegerPartial": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerPartial.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/internal/IntegerPartial.qll",
|
||||
"csharp/ql/src/experimental/ir/internal/IntegerPartial.qll"
|
||||
],
|
||||
"IR Overlap": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/internal/Overlap.qll",
|
||||
"csharp/ql/src/experimental/ir/internal/Overlap.qll"
|
||||
],
|
||||
"IR EdgeKind": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/EdgeKind.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/EdgeKind.qll"
|
||||
],
|
||||
"IR MemoryAccessKind": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/MemoryAccessKind.qll"
|
||||
],
|
||||
"IR TempVariableTag": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/TempVariableTag.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/TempVariableTag.qll"
|
||||
],
|
||||
"IR Opcode": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/Opcode.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/Opcode.qll"
|
||||
],
|
||||
"IR SSAConsistency": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll"
|
||||
],
|
||||
"C++ IR InstructionImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/InstructionImports.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/InstructionImports.qll"
|
||||
],
|
||||
"C++ IR IRImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRImports.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRImports.qll"
|
||||
],
|
||||
"C++ IR IRBlockImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRBlockImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRBlockImports.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRBlockImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRBlockImports.qll"
|
||||
],
|
||||
"C++ IR IRFunctionImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll"
|
||||
],
|
||||
"C++ IR IRVariableImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRVariableImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRVariableImports.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRVariableImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRVariableImports.qll"
|
||||
],
|
||||
"C++ IR OperandImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/OperandImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/OperandImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/OperandImports.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/OperandImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/OperandImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/OperandImports.qll"
|
||||
],
|
||||
"C++ IR PrintIRImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/PrintIRImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintIRImports.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/PrintIRImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintIRImports.qll"
|
||||
],
|
||||
"C++ SSA SSAConstructionImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll"
|
||||
],
|
||||
"SSA AliasAnalysis": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll"
|
||||
],
|
||||
"SSA PrintAliasAnalysis": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintAliasAnalysis.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasAnalysis.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintAliasAnalysis.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasAnalysis.qll"
|
||||
],
|
||||
"C++ SSA AliasAnalysisImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysisImports.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysisImports.qll"
|
||||
],
|
||||
"C++ IR ValueNumberingImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
||||
],
|
||||
"IR SSA SimpleSSA": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll"
|
||||
],
|
||||
"IR AliasConfiguration (unaliased_ssa)": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll"
|
||||
],
|
||||
"IR SSA SSAConstruction": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll"
|
||||
],
|
||||
"IR SSA PrintSSA": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintSSA.qll"
|
||||
],
|
||||
"IR ValueNumberInternal": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll"
|
||||
],
|
||||
"C++ IR ValueNumber": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll"
|
||||
],
|
||||
"C++ IR PrintValueNumbering": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/PrintValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/PrintValueNumbering.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/PrintValueNumbering.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/PrintValueNumbering.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/PrintValueNumbering.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll"
|
||||
],
|
||||
"C++ IR ConstantAnalysis": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/ConstantAnalysis.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/constant/ConstantAnalysis.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/ConstantAnalysis.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/constant/ConstantAnalysis.qll"
|
||||
],
|
||||
"C++ IR PrintConstantAnalysis": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/PrintConstantAnalysis.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/PrintConstantAnalysis.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/constant/PrintConstantAnalysis.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/constant/PrintConstantAnalysis.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/PrintConstantAnalysis.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/constant/PrintConstantAnalysis.qll"
|
||||
],
|
||||
"C++ IR ReachableBlock": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/ReachableBlock.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlock.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/reachability/ReachableBlock.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlock.qll"
|
||||
],
|
||||
"C++ IR PrintReachableBlock": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintReachableBlock.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintReachableBlock.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintReachableBlock.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintReachableBlock.qll"
|
||||
],
|
||||
"C++ IR Dominance": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/Dominance.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/Dominance.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/reachability/Dominance.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/Dominance.qll"
|
||||
],
|
||||
"C++ IR PrintDominance": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintDominance.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintDominance.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
|
||||
],
|
||||
"C# IR InstructionImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/InstructionImports.qll",
|
||||
@@ -361,13 +368,15 @@
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
||||
],
|
||||
"C# ControlFlowReachability": [
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ControlFlowReachability.qll"
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ControlFlowReachability.qll"
|
||||
],
|
||||
"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",
|
||||
@@ -378,11 +387,11 @@
|
||||
"cpp/ql/src/Security/CWE/CWE-020/ir/SafeExternalAPIFunction.qll"
|
||||
],
|
||||
"XML": [
|
||||
"cpp/ql/src/semmle/code/cpp/XML.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/XML.qll",
|
||||
"java/ql/src/semmle/code/xml/XML.qll",
|
||||
"javascript/ql/src/semmle/javascript/XML.qll",
|
||||
"python/ql/src/semmle/python/xml/XML.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/XML.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/XML.qll",
|
||||
"java/ql/lib/semmle/code/xml/XML.qll",
|
||||
"javascript/ql/lib/semmle/javascript/XML.qll",
|
||||
"python/ql/lib/semmle/python/xml/XML.qll"
|
||||
],
|
||||
"DuplicationProblems.inc.qhelp": [
|
||||
"cpp/ql/src/Metrics/Files/DuplicationProblems.inc.qhelp",
|
||||
@@ -436,29 +445,47 @@
|
||||
"python/ql/src/analysis/IDEContextual.qll"
|
||||
],
|
||||
"SSA C#": [
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll",
|
||||
"csharp/ql/src/semmle/code/cil/internal/SsaImplCommon.qll"
|
||||
"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",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll"
|
||||
],
|
||||
"CryptoAlgorithms Python/JS": [
|
||||
"javascript/ql/src/semmle/javascript/security/CryptoAlgorithms.qll",
|
||||
"python/ql/src/semmle/python/concepts/CryptoAlgorithms.qll"
|
||||
"javascript/ql/lib/semmle/javascript/security/CryptoAlgorithms.qll",
|
||||
"python/ql/lib/semmle/python/concepts/CryptoAlgorithms.qll"
|
||||
],
|
||||
"SensitiveDataHeuristics Python/JS": [
|
||||
"javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll",
|
||||
"python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll"
|
||||
"javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll",
|
||||
"python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll"
|
||||
],
|
||||
"ReDoS Util Python/JS": [
|
||||
"javascript/ql/src/semmle/javascript/security/performance/ReDoSUtil.qll",
|
||||
"python/ql/src/semmle/python/security/performance/ReDoSUtil.qll"
|
||||
"javascript/ql/lib/semmle/javascript/security/performance/ReDoSUtil.qll",
|
||||
"python/ql/lib/semmle/python/security/performance/ReDoSUtil.qll"
|
||||
],
|
||||
"ReDoS Exponential Python/JS": [
|
||||
"javascript/ql/src/semmle/javascript/security/performance/ExponentialBackTracking.qll",
|
||||
"python/ql/src/semmle/python/security/performance/ExponentialBackTracking.qll"
|
||||
"javascript/ql/lib/semmle/javascript/security/performance/ExponentialBackTracking.qll",
|
||||
"python/ql/lib/semmle/python/security/performance/ExponentialBackTracking.qll"
|
||||
],
|
||||
"ReDoS Polynomial Python/JS": [
|
||||
"javascript/ql/src/semmle/javascript/security/performance/SuperlinearBackTracking.qll",
|
||||
"python/ql/src/semmle/python/security/performance/SuperlinearBackTracking.qll"
|
||||
"javascript/ql/lib/semmle/javascript/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"
|
||||
]
|
||||
}
|
||||
|
||||
2
cpp/change-notes/2021-06-10-cleartext-transmission.md
Normal file
2
cpp/change-notes/2021-06-10-cleartext-transmission.md
Normal file
@@ -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.
|
||||
2
cpp/change-notes/2021-06-22-sql-tainted.md
Normal file
2
cpp/change-notes/2021-06-22-sql-tainted.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* The 'Uncontrolled data in SQL query' (cpp/sql-injection) query now supports the `libpqxx` library.
|
||||
3
cpp/change-notes/2021-07-13-cleartext-storage-file.md
Normal file
3
cpp/change-notes/2021-07-13-cleartext-storage-file.md
Normal file
@@ -0,0 +1,3 @@
|
||||
lgtm,codescanning
|
||||
* The "Cleartext storage of sensitive information in file" (cpp/cleartext-storage-file) query now uses dataflow to produce additional results.
|
||||
* Heuristics in the SensitiveExprs.qll library have been improved, making the "Cleartext storage of sensitive information in file" (cpp/cleartext-storage-file), "Cleartext storage of sensitive information in buffer" (cpp/cleartext-storage-buffer) and "Cleartext storage of sensitive information in an SQLite" (cpp/cleartext-storage-database) queries more accurate.
|
||||
2
cpp/change-notes/2021-07-27-uncontrolled-arithmetic.md
Normal file
2
cpp/change-notes/2021-07-27-uncontrolled-arithmetic.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm
|
||||
* Improvements made to the (`cpp/uncontrolled-arithmetic`) query, reducing the frequency of false positive results.
|
||||
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Virtual function specifiers are now accessible via the new predicates on `Function` (`.isDeclaredVirtual`, `.isOverride`, and `.isFinal`).
|
||||
2
cpp/change-notes/2021-08-10-has-trailing-return-type.md
Normal file
2
cpp/change-notes/2021-08-10-has-trailing-return-type.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Added `Function.hasTrailingReturnType` predicate to check whether a function was declared with a trailing return type.
|
||||
2
cpp/change-notes/2021-08-17-has-c-linkage.md
Normal file
2
cpp/change-notes/2021-08-17-has-c-linkage.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Added `RoutineType.hasCLinkage` predicate to check whether a function type has "C" language linkage.
|
||||
2
cpp/change-notes/2021-08-23-ctime-weaken-claims.md
Normal file
2
cpp/change-notes/2021-08-23-ctime-weaken-claims.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Lowered the precision of `cpp/potentially-dangerous-function` so it is run but not displayed on LGTM by default and so it's only run and displayed on Code Scanning if a broader suite like `cpp-security-extended` is opted into.
|
||||
2
cpp/change-notes/2021-08-23-getPrimaryQlClasses.md
Normal file
2
cpp/change-notes/2021-08-23-getPrimaryQlClasses.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Added `Element.getPrimaryQlClasses()` predicate, which gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs.
|
||||
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* The query `cpp/implicit-bitfield-downcast` now accounts for C++ reference types, which leads to more true positive results.
|
||||
@@ -0,0 +1,4 @@
|
||||
lgtm,codescanning
|
||||
* The `SimpleRangeAnalysis` library includes information from the
|
||||
immediate guard for determining the upper bound of a stack
|
||||
variable for improved accuracy.
|
||||
4
cpp/change-notes/2021-09-13-overflow-static.md
Normal file
4
cpp/change-notes/2021-09-13-overflow-static.md
Normal file
@@ -0,0 +1,4 @@
|
||||
lgtm,codescanning
|
||||
* The `memberMayBeVarSize` predicate considers more fields to be variable size.
|
||||
As a result, the "Static buffer overflow" query (cpp/static-buffer-overflow)
|
||||
produces fewer false positives.
|
||||
2
cpp/change-notes/2021-09-27-command-line-injection.md
Normal file
2
cpp/change-notes/2021-09-27-command-line-injection.md
Normal file
@@ -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`
|
||||
3
cpp/change-notes/2021-09-27-overflow-static.md
Normal file
3
cpp/change-notes/2021-09-27-overflow-static.md
Normal file
@@ -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.
|
||||
2
cpp/change-notes/2021-10-01-improper-null-termination.md
Normal file
2
cpp/change-notes/2021-10-01-improper-null-termination.md
Normal file
@@ -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).
|
||||
3
cpp/change-notes/2021-10-07-extraction-errors.md
Normal file
3
cpp/change-notes/2021-10-07-extraction-errors.md
Normal file
@@ -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`.
|
||||
4
cpp/ql/examples/qlpack.lock.yml
Normal file
4
cpp/ql/examples/qlpack.lock.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
dependencies: {}
|
||||
compiled: false
|
||||
lockVersion: 1.0.0
|
||||
@@ -1,3 +1,4 @@
|
||||
name: codeql-cpp-examples
|
||||
version: 0.0.0
|
||||
libraryPathDependencies: codeql-cpp
|
||||
name: codeql/cpp-examples
|
||||
version: 0.0.2
|
||||
dependencies:
|
||||
codeql/cpp-all: "*"
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Provides classes and predicates for identifying private data and functions for security.
|
||||
*
|
||||
* 'Private' data in general is anything that would compromise user privacy if exposed. This
|
||||
* library tries to guess where private data may either be stored in a variable or produced by a
|
||||
* function.
|
||||
*
|
||||
* This library is not concerned with credentials. See `SensitiveActions` for expressions related
|
||||
* to credentials.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/** A string for `match` that identifies strings that look like they represent private data. */
|
||||
private string privateNames() {
|
||||
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. */
|
||||
abstract class PrivateDataExpr extends Expr { }
|
||||
|
||||
/** A functiond call that might produce private data. */
|
||||
class PrivateFunctionCall extends PrivateDataExpr, FunctionCall {
|
||||
PrivateFunctionCall() {
|
||||
exists(string s | this.getTarget().getName().toLowerCase() = s | s.matches(privateNames()))
|
||||
}
|
||||
}
|
||||
|
||||
/** An access to a variable that might contain private data. */
|
||||
class PrivateVariableAccess extends PrivateDataExpr, VariableAccess {
|
||||
PrivateVariableAccess() {
|
||||
exists(string s | this.getTarget().getName().toLowerCase() = s | s.matches(privateNames()))
|
||||
}
|
||||
}
|
||||
60
cpp/ql/lib/external/ExternalArtifact.qll
vendored
Normal file
60
cpp/ql/lib/external/ExternalArtifact.qll
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Provides classes for working with external data.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* An external data item.
|
||||
*/
|
||||
class ExternalData extends @externalDataElement {
|
||||
/** Gets the path of the file this data was loaded from. */
|
||||
string getDataPath() { externalData(this, result, _, _) }
|
||||
|
||||
/**
|
||||
* Gets the path of the file this data was loaded from, with its
|
||||
* extension replaced by `.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) }
|
||||
|
||||
/** Gets the value of the `i`th field of this data item. */
|
||||
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 = this.getField(i).toInt() }
|
||||
|
||||
/** Gets the floating-point value of the `i`th field of this data item. */
|
||||
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 = this.getField(i).toDate() }
|
||||
|
||||
/** Gets a textual representation of this data item. */
|
||||
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 = this.getNumFields() - 1 and result = this.getField(n)
|
||||
or
|
||||
n < this.getNumFields() - 1 and result = this.getField(n) + "," + this.buildTupleString(n + 1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* External data with a location, and a message, as produced by tools that used to produce QLDs.
|
||||
*/
|
||||
class DefectExternalData extends ExternalData {
|
||||
DefectExternalData() {
|
||||
this.getField(0).regexpMatch("\\w+://.*:[0-9]+:[0-9]+:[0-9]+:[0-9]+$") and
|
||||
this.getNumFields() = 2
|
||||
}
|
||||
|
||||
/** Gets the URL associated with this data item. */
|
||||
string getURL() { result = this.getField(0) }
|
||||
|
||||
/** Gets the message associated with this data item. */
|
||||
string getMessage() { result = this.getField(1) }
|
||||
}
|
||||
4
cpp/ql/lib/qlpack.lock.yml
Normal file
4
cpp/ql/lib/qlpack.lock.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
dependencies: {}
|
||||
compiled: false
|
||||
lockVersion: 1.0.0
|
||||
7
cpp/ql/lib/qlpack.yml
Normal file
7
cpp/ql/lib/qlpack.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.0.2
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
library: true
|
||||
dependencies:
|
||||
codeql/cpp-upgrades: 0.0.2
|
||||
@@ -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() {
|
||||
720
cpp/ql/lib/semmle/code/cpp/Declaration.qll
Normal file
720
cpp/ql/lib/semmle/code/cpp/Declaration.qll
Normal file
@@ -0,0 +1,720 @@
|
||||
/**
|
||||
* Provides classes for working with C and C++ declarations.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Element
|
||||
import semmle.code.cpp.Specifier
|
||||
import semmle.code.cpp.Namespace
|
||||
private import semmle.code.cpp.internal.QualifiedName as Q
|
||||
|
||||
/**
|
||||
* A C/C++ declaration: for example, a variable declaration, a type
|
||||
* declaration, or a function declaration.
|
||||
*
|
||||
* This file defines two closely related classes: `Declaration` and
|
||||
* `DeclarationEntry`. Some declarations do not correspond to a unique
|
||||
* location in the source code. For example, a global variable might
|
||||
* be declared in multiple source files:
|
||||
* ```
|
||||
* extern int myglobal;
|
||||
* ```
|
||||
* and defined in one:
|
||||
* ```
|
||||
* int myglobal;
|
||||
* ```
|
||||
* Each of these declarations (including the definition) is given its own
|
||||
* distinct `DeclarationEntry`, but they all share the same `Declaration`.
|
||||
*
|
||||
* Some derived class of `Declaration` do not have a corresponding
|
||||
* `DeclarationEntry`, because they always have a unique source location.
|
||||
* `EnumConstant` and `FriendDecl` are both examples of this.
|
||||
*/
|
||||
class Declaration extends Locatable, @declaration {
|
||||
/**
|
||||
* Gets the innermost namespace which contains this declaration.
|
||||
*
|
||||
* The result will either be `GlobalNamespace`, or the tightest lexically
|
||||
* enclosing namespace block. In particular, note that for declarations
|
||||
* within structures, the namespace of the declaration is the same as the
|
||||
* namespace of the structure.
|
||||
*/
|
||||
Namespace getNamespace() {
|
||||
result = underlyingElement(this).(Q::Declaration).getNamespace()
|
||||
or
|
||||
exists(Parameter p | p = this and result = p.getFunction().getNamespace())
|
||||
or
|
||||
exists(LocalVariable v | v = this and result = v.getFunction().getNamespace())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the declaration, fully qualified with its
|
||||
* namespace and declaring type.
|
||||
*
|
||||
* For performance, prefer the multi-argument `hasQualifiedName` or
|
||||
* `hasGlobalName` predicates since they don't construct so many intermediate
|
||||
* strings. For debugging, the `semmle.code.cpp.Print` module produces more
|
||||
* detailed output but are also more expensive to compute.
|
||||
*
|
||||
* Example: `getQualifiedName() =
|
||||
* "namespace1::namespace2::TemplateClass1<int>::Class2::memberName"`.
|
||||
*/
|
||||
string getQualifiedName() { result = underlyingElement(this).(Q::Declaration).getQualifiedName() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Prefer `hasGlobalName` or the 2-argument or 3-argument
|
||||
* `hasQualifiedName` predicates. To get the exact same results as this
|
||||
* predicate in all edge cases, use `getQualifiedName()`.
|
||||
*
|
||||
* Holds if this declaration has the fully-qualified name `qualifiedName`.
|
||||
* See `getQualifiedName`.
|
||||
*/
|
||||
predicate hasQualifiedName(string qualifiedName) { this.getQualifiedName() = qualifiedName }
|
||||
|
||||
/**
|
||||
* Holds if this declaration has a fully-qualified name with a name-space
|
||||
* component of `namespaceQualifier`, a declaring type of `typeQualifier`,
|
||||
* and a base name of `baseName`. Template parameters and arguments are
|
||||
* stripped from all components. Missing components are `""`.
|
||||
*
|
||||
* Example: `hasQualifiedName("namespace1::namespace2",
|
||||
* "TemplateClass1::Class2", "memberName")`.
|
||||
*
|
||||
* Example (the class `std::vector`): `hasQualifiedName("std", "", "vector")`
|
||||
* or `hasQualifiedName("std", "vector")`.
|
||||
*
|
||||
* Example (the `size` member function of class `std::vector`):
|
||||
* `hasQualifiedName("std", "vector", "size")`.
|
||||
*/
|
||||
predicate hasQualifiedName(string namespaceQualifier, string typeQualifier, string baseName) {
|
||||
underlyingElement(this)
|
||||
.(Q::Declaration)
|
||||
.hasQualifiedName(namespaceQualifier, typeQualifier, baseName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this declaration has a fully-qualified name with a name-space
|
||||
* component of `namespaceQualifier`, no declaring type, and a base name of
|
||||
* `baseName`.
|
||||
*
|
||||
* See the 3-argument `hasQualifiedName` for examples.
|
||||
*/
|
||||
predicate hasQualifiedName(string namespaceQualifier, string baseName) {
|
||||
this.hasQualifiedName(namespaceQualifier, "", baseName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a description of this `Declaration` for display purposes.
|
||||
*/
|
||||
string getDescription() { result = this.getName() }
|
||||
|
||||
final override string toString() { result = this.getDescription() }
|
||||
|
||||
/**
|
||||
* Gets the name of this declaration.
|
||||
*
|
||||
* This name doesn't include a namespace or any argument types, so
|
||||
* for example both functions `::open()` and `::std::ifstream::open(...)`
|
||||
* have the same name. The name of a template _class_ includes a string
|
||||
* representation of its parameters, and the names of its instantiations
|
||||
* include string representations of their arguments. Template _functions_
|
||||
* and their instantiations do not include template parameters or arguments.
|
||||
*
|
||||
* To get the name including the namespace, use `hasQualifiedName`.
|
||||
*
|
||||
* To test whether this declaration has a particular name in the global
|
||||
* namespace, use `hasGlobalName`.
|
||||
*/
|
||||
string getName() { none() } // overridden in subclasses
|
||||
|
||||
/** Holds if this declaration has the given name. */
|
||||
predicate hasName(string name) { name = this.getName() }
|
||||
|
||||
/** Holds if this declaration has the given name in the global namespace. */
|
||||
predicate hasGlobalName(string name) { this.hasQualifiedName("", "", name) }
|
||||
|
||||
/** Holds if this declaration has the given name in the global namespace or the `std` namespace. */
|
||||
predicate hasGlobalOrStdName(string name) {
|
||||
this.hasGlobalName(name)
|
||||
or
|
||||
this.hasQualifiedName("std", "", name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this declaration has the given name in the global namespace,
|
||||
* the `std` namespace or the `bsl` namespace.
|
||||
* We treat `std` and `bsl` as the same in some of our models.
|
||||
*/
|
||||
predicate hasGlobalOrStdOrBslName(string name) {
|
||||
this.hasGlobalName(name)
|
||||
or
|
||||
this.hasQualifiedName("std", "", name)
|
||||
or
|
||||
this.hasQualifiedName("bsl", "", name)
|
||||
}
|
||||
|
||||
/** Gets a specifier of this declaration. */
|
||||
Specifier getASpecifier() { none() } // overridden in subclasses
|
||||
|
||||
/** Holds if this declaration has a specifier with the given name. */
|
||||
predicate hasSpecifier(string name) { this.getASpecifier().hasName(name) }
|
||||
|
||||
/**
|
||||
* Gets a declaration entry corresponding to this declaration. See the
|
||||
* comment above this class for an explanation of the relationship
|
||||
* between `Declaration` and `DeclarationEntry`.
|
||||
*/
|
||||
DeclarationEntry getADeclarationEntry() { none() }
|
||||
|
||||
/**
|
||||
* Gets the location of a declaration entry corresponding to this
|
||||
* declaration.
|
||||
*/
|
||||
Location getADeclarationLocation() { none() } // overridden in subclasses
|
||||
|
||||
/**
|
||||
* Gets the declaration entry corresponding to this declaration that is a
|
||||
* definition, if any.
|
||||
*/
|
||||
DeclarationEntry getDefinition() { none() }
|
||||
|
||||
/** Gets the location of the definition, if any. */
|
||||
Location getDefinitionLocation() { none() } // overridden in subclasses
|
||||
|
||||
/** Holds if the declaration has a definition. */
|
||||
predicate hasDefinition() { exists(this.getDefinition()) }
|
||||
|
||||
/** DEPRECATED: Use `hasDefinition` instead. */
|
||||
predicate isDefined() { this.hasDefinition() }
|
||||
|
||||
/** Gets the preferred location of this declaration, if any. */
|
||||
override Location getLocation() { none() }
|
||||
|
||||
/** Gets a file where this element occurs. */
|
||||
File getAFile() { result = this.getADeclarationLocation().getFile() }
|
||||
|
||||
/** Holds if this declaration is a top-level declaration. */
|
||||
predicate isTopLevel() {
|
||||
not (
|
||||
this.isMember() or
|
||||
this instanceof EnumConstant or
|
||||
this instanceof Parameter or
|
||||
this instanceof ProxyClass or
|
||||
this instanceof LocalVariable or
|
||||
this instanceof TemplateParameter or
|
||||
this.(UserType).isLocal()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this declaration is static. */
|
||||
predicate isStatic() { this.hasSpecifier("static") }
|
||||
|
||||
/** Holds if this declaration is a member of a class/struct/union. */
|
||||
predicate isMember() { this.hasDeclaringType() }
|
||||
|
||||
/** Holds if this declaration is a member of a class/struct/union. */
|
||||
predicate hasDeclaringType() { exists(this.getDeclaringType()) }
|
||||
|
||||
/**
|
||||
* Gets the class where this member is declared, if it is a member.
|
||||
* For templates, both the template itself and all instantiations of
|
||||
* the template are considered to have the same declaring class.
|
||||
*/
|
||||
Class getDeclaringType() { this = result.getAMember() }
|
||||
|
||||
/**
|
||||
* Gets a template argument used to instantiate this declaration from a template.
|
||||
* When called on a template, this will return a template parameter type for
|
||||
* both typed and non-typed parameters.
|
||||
*/
|
||||
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 = this.getTemplateArgumentKind(_) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument used to instantiate this declaration from a
|
||||
* template.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* `template<typename T, T X> class Foo;`
|
||||
*
|
||||
* Will have `getTemplateArgument(0)` return `T`, and
|
||||
* `getTemplateArgument(1)` return `X`.
|
||||
*
|
||||
* `Foo<int, 1> bar;`
|
||||
*
|
||||
* Will have `getTemplateArgument())` return `int`, and
|
||||
* `getTemplateArgument(1)` return `1`.
|
||||
*/
|
||||
final Locatable getTemplateArgument(int index) {
|
||||
if exists(this.getTemplateArgumentValue(index))
|
||||
then result = this.getTemplateArgumentValue(index)
|
||||
else result = this.getTemplateArgumentType(index)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument value used to instantiate this declaration
|
||||
* from a template. When called on a template, this will return the `i`th template
|
||||
* parameter value if it exists.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* `template<typename T, T X> class Foo;`
|
||||
*
|
||||
* Will have `getTemplateArgumentKind(1)` return `T`, and no result for
|
||||
* `getTemplateArgumentKind(0)`.
|
||||
*
|
||||
* `Foo<int, 10> bar;
|
||||
*
|
||||
* Will have `getTemplateArgumentKind(1)` return `int`, and no result for
|
||||
* `getTemplateArgumentKind(0)`.
|
||||
*/
|
||||
final Locatable getTemplateArgumentKind(int index) {
|
||||
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(this.getTemplateArgument(i)))
|
||||
}
|
||||
|
||||
private Type getTemplateArgumentType(int index) {
|
||||
class_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
or
|
||||
function_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
or
|
||||
variable_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
}
|
||||
|
||||
private Expr getTemplateArgumentValue(int index) {
|
||||
class_template_argument_value(underlyingElement(this), index, unresolveElement(result))
|
||||
or
|
||||
function_template_argument_value(underlyingElement(this), index, unresolveElement(result))
|
||||
or
|
||||
variable_template_argument_value(underlyingElement(this), index, unresolveElement(result))
|
||||
}
|
||||
}
|
||||
|
||||
private class TDeclarationEntry = @var_decl or @type_decl or @fun_decl;
|
||||
|
||||
/**
|
||||
* A C/C++ declaration entry. For example the following code contains five
|
||||
* declaration entries:
|
||||
* ```
|
||||
* extern int myGlobal;
|
||||
* int myVariable;
|
||||
* typedef char MyChar;
|
||||
* void myFunction();
|
||||
* void myFunction() {
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
* See the comment above `Declaration` for an explanation of the relationship
|
||||
* between `Declaration` and `DeclarationEntry`.
|
||||
*/
|
||||
class DeclarationEntry extends Locatable, TDeclarationEntry {
|
||||
/** Gets a specifier associated with this declaration entry. */
|
||||
string getASpecifier() { none() } // overridden in subclasses
|
||||
|
||||
/**
|
||||
* Gets the name associated with the corresponding definition (where
|
||||
* available), or the name declared by this entry otherwise.
|
||||
*/
|
||||
string getCanonicalName() {
|
||||
if this.getDeclaration().hasDefinition()
|
||||
then result = this.getDeclaration().getDefinition().getName()
|
||||
else result = this.getName()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the declaration for which this is a declaration entry.
|
||||
*
|
||||
* Note that this is *not* always the inverse of
|
||||
* `Declaration.getADeclarationEntry()`, for example if `C` is a
|
||||
* `TemplateClass`, `I` is an instantiation of `C`, and `D` is a
|
||||
* `Declaration` of `C`, then:
|
||||
* `C.getADeclarationEntry()` returns `D`
|
||||
* `I.getADeclarationEntry()` returns `D`
|
||||
* but `D.getDeclaration()` only returns `C`
|
||||
*/
|
||||
Declaration getDeclaration() { none() } // overridden in subclasses
|
||||
|
||||
/** Gets the name associated with this declaration entry, if any. */
|
||||
string getName() { none() } // overridden in subclasses
|
||||
|
||||
/**
|
||||
* Gets the type associated with this declaration entry.
|
||||
*
|
||||
* For variable declarations, get the type of the variable.
|
||||
* For function declarations, get the return type of the function.
|
||||
* For type declarations, get the type being declared.
|
||||
*/
|
||||
Type getType() { none() } // overridden in subclasses
|
||||
|
||||
/**
|
||||
* Gets the type associated with this declaration entry after specifiers
|
||||
* have been deeply stripped and typedefs have been resolved.
|
||||
*
|
||||
* For variable declarations, get the type of the variable.
|
||||
* For function declarations, get the return type of the function.
|
||||
* For type declarations, get the type being declared.
|
||||
*/
|
||||
Type getUnspecifiedType() { result = this.getType().getUnspecifiedType() }
|
||||
|
||||
/**
|
||||
* Holds if this declaration entry has a specifier with the given name.
|
||||
*/
|
||||
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 this.isDefinition()
|
||||
then result = "definition of " + this.getName()
|
||||
else
|
||||
if this.getName() = this.getCanonicalName()
|
||||
then result = "declaration of " + this.getName()
|
||||
else result = "declaration of " + this.getCanonicalName() + " as " + this.getName()
|
||||
}
|
||||
}
|
||||
|
||||
private class TAccessHolder = @function or @usertype;
|
||||
|
||||
/**
|
||||
* A declaration that can potentially have more C++ access rights than its
|
||||
* enclosing element. This comprises `Class` (they have access to their own
|
||||
* private members) along with other `UserType`s and `Function` (they can be
|
||||
* the target of `friend` declarations). For example `MyClass` and
|
||||
* `myFunction` in the following code:
|
||||
* ```
|
||||
* class MyClass
|
||||
* {
|
||||
* public:
|
||||
* ...
|
||||
* };
|
||||
*
|
||||
* void myFunction() {
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
* In the C++ standard (N4140 11.2), rules for access control revolve around
|
||||
* the informal phrase "_R_ occurs in a member or friend of class C", where
|
||||
* `AccessHolder` corresponds to this _R_.
|
||||
*/
|
||||
class AccessHolder extends Declaration, TAccessHolder {
|
||||
/**
|
||||
* Holds if `this` can access private members of class `c`.
|
||||
*
|
||||
* This predicate encodes the phrase "occurs in a member or friend" that is
|
||||
* repeated many times in the C++14 standard, section 11.2.
|
||||
*/
|
||||
predicate inMemberOrFriendOf(Class c) {
|
||||
this.getEnclosingAccessHolder*() = c
|
||||
or
|
||||
exists(FriendDecl fd | fd.getDeclaringClass() = c |
|
||||
this.getEnclosingAccessHolder*() = fd.getFriend()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the nearest enclosing `AccessHolder`.
|
||||
*/
|
||||
AccessHolder getEnclosingAccessHolder() { none() } // overridden in subclasses
|
||||
|
||||
/**
|
||||
* Holds if a base class `base` of `derived` _is accessible at_ `this` (N4140
|
||||
* 11.2/4). When this holds, and `derived` has only one base subobject of
|
||||
* type `base`, code in `this` can implicitly convert a pointer to `derived`
|
||||
* into a pointer to `base`. Conversely, if such a conversion is possible
|
||||
* then this predicate holds.
|
||||
*
|
||||
* For the sake of generality, this predicate also holds whenever `base` =
|
||||
* `derived`.
|
||||
*
|
||||
* This predicate is `pragma[inline]` because it is infeasible to fully
|
||||
* compute it on large code bases: all classes `derived` can be converted to
|
||||
* their public bases `base` from everywhere (`this`), so this predicate
|
||||
* could yield a number of tuples that is quadratic in the size of the
|
||||
* program. To avoid this combinatorial explosion, only use this predicate in
|
||||
* a context where `this` together with `base` or `derived` are sufficiently
|
||||
* restricted.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate canAccessClass(Class base, Class derived) {
|
||||
// This predicate is marked `inline` and implemented in a very particular
|
||||
// way. If we allowed this predicate to be fully computed, it would relate
|
||||
// all `AccessHolder`s to all classes, which would be too much.
|
||||
// There are four rules in N4140 11.2/4. Only the one named (4.4) is
|
||||
// recursive, and it describes a transitive closure: intuitively, if A can
|
||||
// be converted to B, and B can be converted to C, then A can be converted
|
||||
// to C. To limit the number of tuples in the non-inline helper predicates,
|
||||
// we first separate the derivation of 11.2/4 into two cases:
|
||||
// Derivations using only (4.1) and (4.4). Note that these derivations are
|
||||
// independent of `this`, which is why users of this predicate must take
|
||||
// care to avoid a combinatorial explosion.
|
||||
isDirectPublicBaseOf*(base, derived)
|
||||
or
|
||||
exists(DirectAccessHolder n |
|
||||
this.getEnclosingAccessHolder*() = n and
|
||||
// Derivations using (4.2) or (4.3) at least once.
|
||||
n.thisCanAccessClassTrans(base, derived)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a non-static member `member` _is accessible at_ `this` when named
|
||||
* in a class `derived` that is derived from or equal to the declaring class
|
||||
* of `member` (N4140 11.2/5 and 11.4).
|
||||
*
|
||||
* This predicate determines whether an expression `x.member` would be
|
||||
* allowed in `this` when `x` has type `derived`. The more general syntax
|
||||
* `x.N::member`, where `N` may be a base class of `derived`, is not
|
||||
* supported. This should only affect very rare edge cases of 11.4. This
|
||||
* predicate concerns only _access_ and thus does not determine whether
|
||||
* `member` can be unambiguously named at `this`: multiple overloads may
|
||||
* apply, or `member` may be declared in an ambiguous base class.
|
||||
*
|
||||
* This predicate is `pragma[inline]` because it is infeasible to fully
|
||||
* compute it on large code bases: all public members `member` are accessible
|
||||
* from everywhere (`this`), so this predicate could yield a number of tuples
|
||||
* that is quadratic in the size of the program. To avoid this combinatorial
|
||||
* explosion, only use this predicate in a context where `this` and `member`
|
||||
* are sufficiently restricted when `member` is public.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate canAccessMember(Declaration member, Class derived) {
|
||||
this.couldAccessMember(member.getDeclaringType(), member.getASpecifier().(AccessSpecifier),
|
||||
derived)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a hypothetical non-static member of `memberClass` with access
|
||||
* specifier `memberAccess` _is accessible at_ `this` when named in a class
|
||||
* `derived` that is derived from or equal to `memberClass` (N4140 11.2/5 and
|
||||
* 11.4).
|
||||
*
|
||||
* This predicate determines whether an expression `x.m` would be
|
||||
* allowed in `this` when `x` has type `derived` and `m` has `memberAccess`
|
||||
* in `memberClass`. The more general syntax `x.N::n`, where `N` may be a
|
||||
* base class of `derived`, is not supported. This should only affect very
|
||||
* rare edge cases of 11.4.
|
||||
*
|
||||
* This predicate is `pragma[inline]` because it is infeasible to fully
|
||||
* compute it on large code bases: all classes `memberClass` have their
|
||||
* public members accessible from everywhere (`this`), so this predicate
|
||||
* could yield a number of tuples that is quadratic in the size of the
|
||||
* program. To avoid this combinatorial explosion, only use this predicate in
|
||||
* a context where `this` and `memberClass` are sufficiently restricted when
|
||||
* `memberAccess` is public.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate couldAccessMember(Class memberClass, AccessSpecifier memberAccess, Class derived) {
|
||||
// There are four rules in N4140 11.2/5. To limit the number of tuples in
|
||||
// the non-inline helper predicates, we first separate the derivation of
|
||||
// 11.2/5 into two cases:
|
||||
// Rule (5.1) directly: the member is public, and `derived` uses public
|
||||
// inheritance all the way up to `memberClass`. Note that these derivations
|
||||
// are independent of `this`, which is why users of this predicate must
|
||||
// take care to avoid a combinatorial explosion.
|
||||
everyoneCouldAccessMember(memberClass, memberAccess, derived)
|
||||
or
|
||||
exists(DirectAccessHolder n |
|
||||
this.getEnclosingAccessHolder*() = n and
|
||||
// Any other derivation.
|
||||
n.thisCouldAccessMember(memberClass, memberAccess, derived)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A declaration that very likely has more C++ access rights than its
|
||||
* enclosing element. This comprises `Class` (they have access to their own
|
||||
* private members) along with any target of a `friend` declaration. For
|
||||
* example `MyClass` and `friendFunction` in the following code:
|
||||
* ```
|
||||
* class MyClass
|
||||
* {
|
||||
* public:
|
||||
* friend void friendFunction();
|
||||
* };
|
||||
*
|
||||
* void friendFunction() {
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
* Most access rights are computed for `DirectAccessHolder` instead of
|
||||
* `AccessHolder` -- that's more efficient because there are fewer
|
||||
* `DirectAccessHolder`s. If a `DirectAccessHolder` contains an `AccessHolder`,
|
||||
* then the contained `AccessHolder` inherits its access rights.
|
||||
*/
|
||||
private class DirectAccessHolder extends Element {
|
||||
DirectAccessHolder() {
|
||||
this instanceof Class
|
||||
or
|
||||
exists(FriendDecl fd | fd.getFriend() = this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a base class `base` of `derived` _is accessible at_ `this` when
|
||||
* the derivation of that fact uses rule (4.2) and (4.3) of N4140 11.2/4 at
|
||||
* least once. In other words, the `this` parameter is not ignored. This
|
||||
* restriction makes it feasible to fully enumerate this predicate even on
|
||||
* large code bases.
|
||||
*/
|
||||
predicate thisCanAccessClassTrans(Class base, Class derived) {
|
||||
// This implementation relies on the following property of our predicates:
|
||||
// if `this.thisCanAccessClassStep(b, d)` and
|
||||
// `isDirectPublicBaseOf(b2, b)`, then
|
||||
// `this.thisCanAccessClassStep(b2, d)`. In other words, if a derivation
|
||||
// uses (4.2) or (4.3) somewhere and uses (4.1) directly above that in the
|
||||
// transitive chain, then the use of (4.1) is redundant. This means we only
|
||||
// need to consider derivations that use (4.2) or (4.3) as the "first"
|
||||
// step, that is, towards `base`, so this implementation is essentially a
|
||||
// transitive closure with a restricted base case.
|
||||
this.thisCanAccessClassStep(base, derived)
|
||||
or
|
||||
exists(Class between | this.thisCanAccessClassTrans(base, between) |
|
||||
isDirectPublicBaseOf(between, derived) or
|
||||
this.thisCanAccessClassStep(between, derived)
|
||||
)
|
||||
// It is possible that this predicate could be computed faster for deep
|
||||
// hierarchies if we can prove and utilize that all derivations of 11.2/4
|
||||
// can be broken down into steps where `base` is a _direct_ base of
|
||||
// `derived` in each step.
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a base class `base` of `derived` _is accessible at_ `this` using
|
||||
* only a single application of rule (4.2) and (4.3) of N4140 11.2/4.
|
||||
*/
|
||||
private predicate thisCanAccessClassStep(Class base, Class derived) {
|
||||
exists(AccessSpecifier public | public.hasName("public") |
|
||||
// Rules (4.2) and (4.3) are implemented together as one here with
|
||||
// reflexive-transitive inheritance, where (4.3) is the transitive case,
|
||||
// and (4.2) is the reflexive case.
|
||||
exists(Class p | p = derived.getADerivedClass*() |
|
||||
this.isFriendOfOrEqualTo(p) and
|
||||
// Note: it's crucial that this is `!=` rather than `not =` since
|
||||
// `accessOfBaseMember` does not have a result when the member would be
|
||||
// inaccessible.
|
||||
p.accessOfBaseMember(base, public) != public
|
||||
)
|
||||
) and
|
||||
// This is the only case that doesn't in itself guarantee that
|
||||
// `derived` < `base`, so we add the check here. The standard suggests
|
||||
// computing `canAccessClass` only for derived classes, but that seems
|
||||
// incompatible with the execution model of QL, so we instead construct
|
||||
// every case to guarantee `derived` < `base`.
|
||||
derived = base.getADerivedClass+()
|
||||
}
|
||||
|
||||
/**
|
||||
* Like `couldAccessMember` but only contains derivations in which either
|
||||
* (5.2), (5.3) or (5.4) must be invoked. In other words, the `this`
|
||||
* parameter is not ignored. This restriction makes it feasible to fully
|
||||
* enumerate this predicate even on large code bases. We check for 11.4 as
|
||||
* part of (5.3), since this further limits the number of tuples produced by
|
||||
* this predicate.
|
||||
*/
|
||||
predicate thisCouldAccessMember(Class memberClass, AccessSpecifier memberAccess, Class derived) {
|
||||
// Only (5.4) is recursive, and chains of invocations of (5.4) can always
|
||||
// be collapsed to one invocation by the transitivity of 11.2/4.
|
||||
// Derivations not using (5.4) can always be rewritten to have a (5.4) rule
|
||||
// in front because our encoding of 11.2/4 in `canAccessClass` is
|
||||
// reflexive. Thus, we only need to consider three cases: rule (5.4)
|
||||
// followed by either (5.1), (5.2) or (5.3).
|
||||
// Rule (5.4), using a non-trivial derivation of 11.2/4, followed by (5.1).
|
||||
// If the derivation of 11.2/4 is trivial (only uses (4.1) and (4.4)), this
|
||||
// case can be replaced with purely (5.1) and thus does not need to be in
|
||||
// this predicate.
|
||||
exists(Class between | this.thisCanAccessClassTrans(between, derived) |
|
||||
everyoneCouldAccessMember(memberClass, memberAccess, between)
|
||||
)
|
||||
or
|
||||
// Rule (5.4) followed by Rule (5.2)
|
||||
exists(Class between | this.(AccessHolder).canAccessClass(between, derived) |
|
||||
between.accessOfBaseMember(memberClass, memberAccess).hasName("private") and
|
||||
this.isFriendOfOrEqualTo(between)
|
||||
)
|
||||
or
|
||||
// Rule (5.4) followed by Rule (5.3), integrating 11.4. We integrate 11.4
|
||||
// here because we would otherwise generate too many tuples. This code is
|
||||
// very performance-sensitive, and any changes should be benchmarked on
|
||||
// LibreOffice.
|
||||
// Rule (5.4) requires that `this.canAccessClass(between, derived)`
|
||||
// (implying that `derived <= between` in the class hierarchy) and that
|
||||
// `p <= between`. Rule 11.4 additionally requires `derived <= p`, but
|
||||
// all these rules together result in too much freedom and overlap between
|
||||
// cases. Therefore, for performance, we split into three cases for how
|
||||
// `between` as a base of `derived` is accessible at `this`, where `this`
|
||||
// is the implementation of `p`:
|
||||
// 1. `between` is an accessible base of `derived` by going through `p` as
|
||||
// an intermediate step.
|
||||
// 2. `this` is part of the implementation of `derived` because it's a
|
||||
// member or a friend. In this case, we do not need `p` to perform this
|
||||
// derivation, so we can set `p = derived` and proceed as in case 1.
|
||||
// 3. `derived` has an alternative inheritance path up to `between` that
|
||||
// bypasses `p`. Then that path must be public, or we are in case 2.
|
||||
exists(AccessSpecifier public | public.hasName("public") |
|
||||
exists(Class between, Class p |
|
||||
between.accessOfBaseMember(memberClass, memberAccess).hasName("protected") and
|
||||
this.isFriendOfOrEqualTo(p) and
|
||||
(
|
||||
// This is case 1 from above. If `p` derives privately from `between`
|
||||
// then the member we're trying to access is private or inaccessible
|
||||
// in `derived`, so either rule (5.2) applies instead, or the member
|
||||
// is inaccessible. Therefore, in this case, `p` must derive at least
|
||||
// protected from `between`. Further, since the access of `derived`
|
||||
// to its base `between` must pass through `p` in this case, we know
|
||||
// that `derived` must derived publicly from `p` unless we are in
|
||||
// case 2; there are no other cases of 11.2/4 where the
|
||||
// implementation of a base class can access itself as a base.
|
||||
p.accessOfBaseMember(between, public).getName() >= "protected" and
|
||||
derived.accessOfBaseMember(p, public) = public
|
||||
or
|
||||
// This is case 3 above.
|
||||
derived.accessOfBaseMember(between, public) = public and
|
||||
derived = p.getADerivedClass*() and
|
||||
exists(p.accessOfBaseMember(between, memberAccess))
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isFriendOfOrEqualTo(Class c) {
|
||||
exists(FriendDecl fd | fd.getDeclaringClass() = c | this = fd.getFriend())
|
||||
or
|
||||
this = c
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `base` is a direct public base of `derived`, possibly virtual and
|
||||
* possibly through typedefs. The transitive closure of this predicate encodes
|
||||
* derivations of N4140 11.2/4 that use only (4.1) and (4.4).
|
||||
*/
|
||||
private predicate isDirectPublicBaseOf(Class base, Class derived) {
|
||||
exists(ClassDerivation cd |
|
||||
cd.getBaseClass() = base and
|
||||
cd.getDerivedClass() = derived and
|
||||
cd.getASpecifier().hasName("public")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a hypothetical member of `memberClass` with access specifier
|
||||
* `memberAccess` would be public when named as a member of `derived`.
|
||||
* This encodes N4140 11.2/5 case (5.1).
|
||||
*/
|
||||
private predicate everyoneCouldAccessMember(
|
||||
Class memberClass, AccessSpecifier memberAccess, Class derived
|
||||
) {
|
||||
derived.accessOfBaseMember(memberClass, memberAccess).hasName("public")
|
||||
}
|
||||
297
cpp/ql/lib/semmle/code/cpp/Element.qll
Normal file
297
cpp/ql/lib/semmle/code/cpp/Element.qll
Normal file
@@ -0,0 +1,297 @@
|
||||
/**
|
||||
* Provides the `Element` class, which is the base class for all classes representing C or C++
|
||||
* program elements.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Location
|
||||
private import semmle.code.cpp.Enclosing
|
||||
private import semmle.code.cpp.internal.ResolveClass
|
||||
|
||||
/**
|
||||
* Get the `Element` that represents this `@element`.
|
||||
* Normally this will simply be a cast of `e`, but sometimes it is not.
|
||||
* For example, for an incomplete struct `e` the result may be a
|
||||
* complete struct with the same name.
|
||||
*/
|
||||
pragma[inline]
|
||||
Element mkElement(@element e) { unresolveElement(result) = e }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets an `@element` that resolves to the `Element`. This should
|
||||
* normally only be called from member predicates, where `e` is not
|
||||
* `this` and you need the result for an argument to a database
|
||||
* extensional.
|
||||
* See `underlyingElement` for when `e` is `this`.
|
||||
*/
|
||||
pragma[inline]
|
||||
@element unresolveElement(Element e) {
|
||||
not result instanceof @usertype and
|
||||
result = e
|
||||
or
|
||||
e = resolveClass(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets the `@element` that this `Element` extends. This should normally
|
||||
* only be called from member predicates, where `e` is `this` and you
|
||||
* need the result for an argument to a database extensional.
|
||||
* See `unresolveElement` for when `e` is not `this`.
|
||||
*/
|
||||
@element underlyingElement(Element e) { result = e }
|
||||
|
||||
/**
|
||||
* A C/C++ element with no member predicates other than `toString`. Not for
|
||||
* general use. This class does not define a location, so classes wanting to
|
||||
* change their location without affecting other classes can extend
|
||||
* `ElementBase` instead of `Element` to create a new rootdef for `getURL`,
|
||||
* `getLocation`, or `hasLocationInfo`.
|
||||
*/
|
||||
class ElementBase extends @element {
|
||||
/** Gets a textual representation of this element. */
|
||||
cached
|
||||
string toString() { none() }
|
||||
|
||||
/** DEPRECATED: use `getAPrimaryQlClass` instead. */
|
||||
deprecated string getCanonicalQLClass() { result = this.getAPrimaryQlClass() }
|
||||
|
||||
/**
|
||||
* 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 name of a primary CodeQL class to which this element belongs.
|
||||
*
|
||||
* For most elements, this is simply the most precise syntactic category to
|
||||
* which they belong; for example, `AddExpr` is a primary class, but
|
||||
* `BinaryOperation` is not.
|
||||
*
|
||||
* This predicate can have multiple results if multiple primary classes match.
|
||||
* For some elements, this predicate may not have a result.
|
||||
*/
|
||||
string getAPrimaryQlClass() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ element. This class is the base class for all C/C++
|
||||
* elements, such as functions, classes, expressions, and so on.
|
||||
*/
|
||||
class Element extends ElementBase {
|
||||
/** Gets the primary file where this element occurs. */
|
||||
File getFile() { result = this.getLocation().getFile() }
|
||||
|
||||
/**
|
||||
* Holds if this element may be from source. This predicate holds for all
|
||||
* elements, except for those in the dummy file, whose name is the empty string.
|
||||
* The dummy file contains declarations that are built directly into the compiler.
|
||||
*/
|
||||
predicate fromSource() { this.getFile().fromSource() }
|
||||
|
||||
/**
|
||||
* Holds if this element may be from a library.
|
||||
*
|
||||
* DEPRECATED: always true.
|
||||
*/
|
||||
deprecated predicate fromLibrary() { this.getFile().fromLibrary() }
|
||||
|
||||
/** Gets the primary location of this element. */
|
||||
Location getLocation() { none() }
|
||||
|
||||
/**
|
||||
* Gets the source of this element: either itself or a macro that expanded
|
||||
* to this element.
|
||||
*
|
||||
* If the element is not in a macro expansion, then the "root" is just
|
||||
* the element itself. Otherwise, it is the definition of the innermost
|
||||
* macro whose expansion the element is in.
|
||||
*
|
||||
* This method is useful for filtering macro results in checks: simply
|
||||
* blame `e.findRootCause` rather than `e`. This will report only bugs
|
||||
* that are not in macros, and in addition report macros that (somewhere)
|
||||
* expand to a bug.
|
||||
*/
|
||||
Element findRootCause() {
|
||||
if exists(MacroInvocation mi | this = mi.getAGeneratedElement())
|
||||
then
|
||||
exists(MacroInvocation mi |
|
||||
this = mi.getAGeneratedElement() and
|
||||
not exists(MacroInvocation closer |
|
||||
this = closer.getAGeneratedElement() and
|
||||
mi = closer.getParentInvocation+()
|
||||
) and
|
||||
result = mi.getMacro()
|
||||
)
|
||||
else result = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parent scope of this `Element`, if any.
|
||||
* A scope is a `Type` (`Class` / `Enum`), a `Namespace`, a `BlockStmt`, a `Function`,
|
||||
* or certain kinds of `Statement`.
|
||||
*/
|
||||
Element getParentScope() {
|
||||
// result instanceof class
|
||||
exists(Declaration m |
|
||||
m = this and
|
||||
result = m.getDeclaringType() and
|
||||
not this instanceof EnumConstant
|
||||
)
|
||||
or
|
||||
exists(TemplateClass tc | this = tc.getATemplateArgument() and result = tc)
|
||||
or
|
||||
// result instanceof namespace
|
||||
exists(Namespace n | result = n and n.getADeclaration() = this)
|
||||
or
|
||||
exists(FriendDecl d, Namespace n | this = d and n.getADeclaration() = d and result = n)
|
||||
or
|
||||
exists(Namespace n | this = n and result = n.getParentNamespace())
|
||||
or
|
||||
// result instanceof stmt
|
||||
exists(LocalVariable v |
|
||||
this = v and
|
||||
exists(DeclStmt ds | ds.getADeclaration() = v and result = ds.getParent())
|
||||
)
|
||||
or
|
||||
exists(Parameter p | this = p and result = p.getFunction())
|
||||
or
|
||||
exists(GlobalVariable g, Namespace n | this = g and n.getADeclaration() = g and result = n)
|
||||
or
|
||||
exists(EnumConstant e | this = e and result = e.getDeclaringEnum())
|
||||
or
|
||||
// result instanceof block|function
|
||||
exists(BlockStmt b | this = b and blockscope(unresolveElement(b), unresolveElement(result)))
|
||||
or
|
||||
exists(TemplateFunction tf | this = tf.getATemplateArgument() and result = tf)
|
||||
or
|
||||
// result instanceof stmt
|
||||
exists(ControlStructure s | this = s and result = s.getParent())
|
||||
or
|
||||
using_container(unresolveElement(result), underlyingElement(this))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this element comes from a macro expansion. Only elements that
|
||||
* are entirely generated by a macro are included - for elements that
|
||||
* partially come from a macro, see `isAffectedByMacro`.
|
||||
*/
|
||||
predicate isInMacroExpansion() { inMacroExpansion(this) }
|
||||
|
||||
/**
|
||||
* Holds if this element is affected in any way by a macro. All elements
|
||||
* that are totally or partially generated by a macro are included, so
|
||||
* this is a super-set of `isInMacroExpansion`.
|
||||
*/
|
||||
predicate isAffectedByMacro() { affectedByMacro(this) }
|
||||
|
||||
private Element getEnclosingElementPref() {
|
||||
enclosingfunction(underlyingElement(this), unresolveElement(result)) or
|
||||
result.(Function) = stmtEnclosingElement(this) or
|
||||
this.(LocalScopeVariable).getFunction() = result or
|
||||
enumconstants(underlyingElement(this), unresolveElement(result), _, _, _, _) or
|
||||
derivations(underlyingElement(this), unresolveElement(result), _, _, _) or
|
||||
stmtparents(underlyingElement(this), _, unresolveElement(result)) or
|
||||
exprparents(underlyingElement(this), _, unresolveElement(result)) or
|
||||
namequalifiers(underlyingElement(this), unresolveElement(result), _, _) or
|
||||
initialisers(underlyingElement(this), unresolveElement(result), _, _) or
|
||||
exprconv(unresolveElement(result), underlyingElement(this)) or
|
||||
param_decl_bind(underlyingElement(this), _, unresolveElement(result)) or
|
||||
using_container(unresolveElement(result), underlyingElement(this)) or
|
||||
static_asserts(unresolveElement(this), _, _, _, underlyingElement(result))
|
||||
}
|
||||
|
||||
/** Gets the closest `Element` enclosing this one. */
|
||||
cached
|
||||
Element getEnclosingElement() {
|
||||
result = this.getEnclosingElementPref()
|
||||
or
|
||||
not exists(this.getEnclosingElementPref()) and
|
||||
(
|
||||
this = result.(Class).getAMember()
|
||||
or
|
||||
result = exprEnclosingElement(this)
|
||||
or
|
||||
var_decls(underlyingElement(this), unresolveElement(result), _, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this `Element` is a part of a template instantiation (but not
|
||||
* the template itself).
|
||||
*/
|
||||
predicate isFromTemplateInstantiation(Element instantiation) {
|
||||
exists(Element e | isFromTemplateInstantiationRec(e, instantiation) |
|
||||
this = e or
|
||||
this.(DeclarationEntry).getDeclaration() = e
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this `Element` is part of a template `template` (not if it is
|
||||
* part of an instantiation of `template`). This means it is represented in
|
||||
* the database purely as syntax and without guarantees on the presence or
|
||||
* correctness of type-based operations such as implicit conversions.
|
||||
*
|
||||
* If an element is nested within several templates, this predicate holds with
|
||||
* a value of `template` for each containing template.
|
||||
*/
|
||||
predicate isFromUninstantiatedTemplate(Element template) {
|
||||
exists(Element e | isFromUninstantiatedTemplateRec(e, template) |
|
||||
this = e or
|
||||
this.(DeclarationEntry).getDeclaration() = e
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate isFromTemplateInstantiationRec(Element e, Element instantiation) {
|
||||
instantiation.(Function).isConstructedFrom(_) and
|
||||
e = instantiation
|
||||
or
|
||||
instantiation.(Class).isConstructedFrom(_) and
|
||||
e = instantiation
|
||||
or
|
||||
instantiation.(Variable).isConstructedFrom(_) and
|
||||
e = instantiation
|
||||
or
|
||||
isFromTemplateInstantiationRec(e.getEnclosingElement(), instantiation)
|
||||
}
|
||||
|
||||
private predicate isFromUninstantiatedTemplateRec(Element e, Element template) {
|
||||
is_class_template(unresolveElement(template)) and
|
||||
e = template
|
||||
or
|
||||
is_function_template(unresolveElement(template)) and
|
||||
e = template
|
||||
or
|
||||
is_variable_template(unresolveElement(template)) and
|
||||
e = template
|
||||
or
|
||||
isFromUninstantiatedTemplateRec(e.getEnclosingElement(), template)
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++11 `static_assert` or C11 `_Static_assert` construct. For example each
|
||||
* line in the following example contains a static assert:
|
||||
* ```
|
||||
* static_assert(sizeof(MyStruct) <= 4096);
|
||||
* static_assert(sizeof(MyStruct) <= 4096, "MyStruct is too big!");
|
||||
* ```
|
||||
*/
|
||||
class StaticAssert extends Locatable, @static_assert {
|
||||
override string toString() { result = "static_assert(..., \"" + this.getMessage() + "\")" }
|
||||
|
||||
/**
|
||||
* Gets the expression which this static assertion ensures is true.
|
||||
*/
|
||||
Expr getCondition() { static_asserts(underlyingElement(this), unresolveElement(result), _, _, _) }
|
||||
|
||||
/**
|
||||
* Gets the message which will be reported by the compiler if this static assertion fails.
|
||||
*/
|
||||
string getMessage() { static_asserts(underlyingElement(this), _, result, _, _) }
|
||||
|
||||
override Location getLocation() { static_asserts(underlyingElement(this), _, _, result, _) }
|
||||
}
|
||||
@@ -85,7 +85,7 @@ class Enum extends UserType, IntegralOrEnumType {
|
||||
* ```
|
||||
*/
|
||||
class LocalEnum extends Enum {
|
||||
LocalEnum() { isLocal() }
|
||||
LocalEnum() { this.isLocal() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LocalEnum" }
|
||||
}
|
||||
452
cpp/ql/lib/semmle/code/cpp/File.qll
Normal file
452
cpp/ql/lib/semmle/code/cpp/File.qll
Normal file
@@ -0,0 +1,452 @@
|
||||
/**
|
||||
* Provides classes representing files and folders.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Element
|
||||
import semmle.code.cpp.Declaration
|
||||
import semmle.code.cpp.metrics.MetricFile
|
||||
|
||||
/** A file or folder. */
|
||||
class Container extends Locatable, @container {
|
||||
/**
|
||||
* 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`.
|
||||
*/
|
||||
string getAbsolutePath() { none() } // overridden by subclasses
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getLocation` instead.
|
||||
* Gets a URL representing the location of this container.
|
||||
*
|
||||
* 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
|
||||
|
||||
/**
|
||||
* 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 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.js"</td><td>"tst.js"</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.js"</td><td>"js"</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 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.js"</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 the parent container of this file or folder, if any. */
|
||||
Container getParentContainer() {
|
||||
containerparent(unresolveElement(result), underlyingElement(this))
|
||||
}
|
||||
|
||||
/** 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 the file in this container that has the given `baseName`, if any. */
|
||||
File getFile(string baseName) {
|
||||
result = this.getAFile() and
|
||||
result.getBaseName() = baseName
|
||||
}
|
||||
|
||||
/** Gets a sub-folder in this container. */
|
||||
Folder getAFolder() { result = this.getAChildContainer() }
|
||||
|
||||
/** 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 a textual representation of the path of this container.
|
||||
*
|
||||
* This is the absolute path of the container.
|
||||
*/
|
||||
override string toString() { result = this.getAbsolutePath() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A folder that was observed on disk during the build process.
|
||||
*
|
||||
* For the example folder name of "/usr/home/me", the path decomposes to:
|
||||
*
|
||||
* 1. "/usr/home" - see `getParentContainer`.
|
||||
* 2. "me" - see `getBaseName`.
|
||||
*
|
||||
* To get the full path, use `getAbsolutePath`.
|
||||
*/
|
||||
class Folder extends Container, @folder {
|
||||
override string getAbsolutePath() { folders(underlyingElement(this), result) }
|
||||
|
||||
override Location getLocation() {
|
||||
result.getContainer() = this and
|
||||
result.hasLocationInfo(_, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Folder" }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getLocation` instead.
|
||||
* Gets the URL of this folder.
|
||||
*/
|
||||
deprecated override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getAbsolutePath` instead.
|
||||
* Gets the name of this folder.
|
||||
*/
|
||||
deprecated string getName() { folders(underlyingElement(this), result) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getAbsolutePath` instead.
|
||||
* Holds if this element is named `name`.
|
||||
*/
|
||||
deprecated predicate hasName(string name) { name = this.getName() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getAbsolutePath` instead.
|
||||
* Gets the full name of this folder.
|
||||
*/
|
||||
deprecated string getFullName() { result = this.getName() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getBaseName` instead.
|
||||
* Gets the last part of the folder name.
|
||||
*/
|
||||
deprecated string getShortName() { result = this.getBaseName() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getParentContainer` instead.
|
||||
* Gets the parent folder.
|
||||
*/
|
||||
deprecated Folder getParent() {
|
||||
containerparent(unresolveElement(result), underlyingElement(this))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A file that was observed on disk during the build process.
|
||||
*
|
||||
* For the example filename of "/usr/home/me/myprogram.c", the filename
|
||||
* decomposes to:
|
||||
*
|
||||
* 1. "/usr/home/me" - see `getParentContainer`.
|
||||
* 2. "myprogram.c" - see `getBaseName`.
|
||||
*
|
||||
* The base name further decomposes into the _stem_ and _extension_ -- see
|
||||
* `getStem` and `getExtension`. To get the full path, use `getAbsolutePath`.
|
||||
*/
|
||||
class File extends Container, @file {
|
||||
override string getAbsolutePath() { files(underlyingElement(this), result) }
|
||||
|
||||
override string toString() { result = Container.super.toString() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "File" }
|
||||
|
||||
override Location getLocation() {
|
||||
result.getContainer() = this and
|
||||
result.hasLocationInfo(_, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getLocation` instead.
|
||||
* Gets the URL of this file.
|
||||
*/
|
||||
deprecated override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
|
||||
|
||||
/** Holds if this file was compiled as C (at any point). */
|
||||
predicate compiledAsC() { fileannotations(underlyingElement(this), 1, "compiled as c", "1") }
|
||||
|
||||
/** Holds if this file was compiled as C++ (at any point). */
|
||||
predicate compiledAsCpp() { fileannotations(underlyingElement(this), 1, "compiled as c++", "1") }
|
||||
|
||||
/**
|
||||
* Holds if this file was compiled by a Microsoft compiler (at any point).
|
||||
*
|
||||
* Note: currently unreliable - on some projects only some of the files that
|
||||
* are compiled by a Microsoft compiler are detected by this predicate.
|
||||
*/
|
||||
predicate compiledAsMicrosoft() {
|
||||
exists(File f, Compilation c |
|
||||
c.getAFileCompiled() = f and
|
||||
(
|
||||
c.getAnArgument() = "--microsoft" or
|
||||
c.getAnArgument()
|
||||
.toLowerCase()
|
||||
.replaceAll("\\", "/")
|
||||
.matches(["%/cl.exe", "%/clang-cl.exe"])
|
||||
) and
|
||||
f.getAnIncludedFile*() = this
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a top-level element declared in this file. */
|
||||
Declaration getATopLevelDeclaration() { result.getAFile() = this and result.isTopLevel() }
|
||||
|
||||
/** Gets a declaration in this file. */
|
||||
Declaration getADeclaration() { result.getAFile() = this }
|
||||
|
||||
/** Holds if this file uses the given macro. */
|
||||
predicate usesMacro(Macro m) {
|
||||
exists(MacroInvocation mi |
|
||||
mi.getFile() = this and
|
||||
mi.getMacro() = m
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a file that is directly included from this file (using a
|
||||
* pre-processor directive like `#include`).
|
||||
*/
|
||||
File getAnIncludedFile() {
|
||||
exists(Include i | i.getFile() = this and i.getIncludedFile() = result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this file may be from source. This predicate holds for all files
|
||||
* except the dummy file, whose name is the empty string, which contains
|
||||
* declarations that are built into the compiler.
|
||||
*/
|
||||
override predicate fromSource() { numlines(underlyingElement(this), _, _, _) }
|
||||
|
||||
/**
|
||||
* Holds if this file may be from a library.
|
||||
*
|
||||
* DEPRECATED: For historical reasons this is true for any file.
|
||||
*/
|
||||
deprecated override predicate fromLibrary() { any() }
|
||||
|
||||
/** Gets the metric file. */
|
||||
MetricFile getMetrics() { result = this }
|
||||
|
||||
/**
|
||||
* Gets the remainder of the base name after the first dot character. Note
|
||||
* that the name of this predicate is in plural form, unlike `getExtension`,
|
||||
* which gets the remainder of the base name after the _last_ dot character.
|
||||
*
|
||||
* Predicates `getStem` and `getExtension` should be preferred over
|
||||
* `getShortName` and `getExtensions` since the former pair is compatible
|
||||
* with the file libraries of other languages.
|
||||
* Note the slight difference between this predicate and `getStem`:
|
||||
* for example, for "file.tar.gz", this predicate will have the result
|
||||
* "tar.gz", while `getExtension` will have the result "gz".
|
||||
*/
|
||||
string getExtensions() {
|
||||
exists(string name, int firstDotPos |
|
||||
name = this.getBaseName() and
|
||||
firstDotPos = min([name.indexOf("."), name.length() - 1]) and
|
||||
result = name.suffix(firstDotPos + 1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the short name of this file, that is, the prefix of its base name up
|
||||
* to (but not including) the first dot character if there is one, or the
|
||||
* entire base name if there is not. For example, if the full name is
|
||||
* "/path/to/filename.a.bcd" then the short name is "filename".
|
||||
*
|
||||
* Predicates `getStem` and `getExtension` should be preferred over
|
||||
* `getShortName` and `getExtensions` since the former pair is compatible
|
||||
* with the file libraries of other languages.
|
||||
* Note the slight difference between this predicate and `getStem`:
|
||||
* for example, for "file.tar.gz", this predicate will have the result
|
||||
* "file", while `getStem` will have the result "file.tar".
|
||||
*/
|
||||
string getShortName() {
|
||||
exists(string name, int firstDotPos |
|
||||
name = this.getBaseName() and
|
||||
firstDotPos = min([name.indexOf("."), name.length()]) and
|
||||
result = name.prefix(firstDotPos)
|
||||
)
|
||||
or
|
||||
this.getAbsolutePath() = "" and
|
||||
result = ""
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if any file was compiled by a Microsoft compiler.
|
||||
*/
|
||||
predicate anyFileCompiledAsMicrosoft() { any(File f).compiledAsMicrosoft() }
|
||||
|
||||
/**
|
||||
* A C/C++ header file, as determined (mainly) by file extension.
|
||||
*
|
||||
* For the related notion of whether a file is included anywhere (using a
|
||||
* pre-processor directive like `#include`), use `Include.getIncludedFile`.
|
||||
*/
|
||||
class HeaderFile extends File {
|
||||
HeaderFile() {
|
||||
this.getExtension().toLowerCase() =
|
||||
["h", "r", "hpp", "hxx", "h++", "hh", "hp", "tcc", "tpp", "txx", "t++"]
|
||||
or
|
||||
not exists(this.getExtension()) and
|
||||
exists(Include i | i.getIncludedFile() = this)
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "HeaderFile" }
|
||||
|
||||
/**
|
||||
* Holds if this header file does not contain any declaration entries or top level
|
||||
* declarations. For example it might be:
|
||||
* - a file containing only preprocessor directives and/or comments
|
||||
* - an empty file
|
||||
* - a file that contains non-top level code or data that's included in an
|
||||
* unusual way
|
||||
*/
|
||||
predicate noTopLevelCode() {
|
||||
not exists(DeclarationEntry de | de.getFile() = this) and
|
||||
not exists(Declaration d | d.getFile() = this and d.isTopLevel()) and
|
||||
not exists(UsingEntry ue | ue.getFile() = this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C source file, as determined by file extension.
|
||||
*
|
||||
* For the related notion of whether a file is compiled as C code, use
|
||||
* `File.compiledAsC`.
|
||||
*/
|
||||
class CFile extends File {
|
||||
CFile() { this.getExtension().toLowerCase() = ["c", "i"] }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "CFile" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ source file, as determined by file extension.
|
||||
*
|
||||
* For the related notion of whether a file is compiled as C++ code, use
|
||||
* `File.compiledAsCpp`.
|
||||
*/
|
||||
class CppFile extends File {
|
||||
CppFile() {
|
||||
this.getExtension().toLowerCase() =
|
||||
["cpp", "cxx", "c++", "cc", "cp", "icc", "ipp", "ixx", "i++", "ii"]
|
||||
// Note: .C files are indistinguishable from .c files on some
|
||||
// file systems, so we just treat them as CFile's.
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "CppFile" }
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Objective-C is no longer supported.
|
||||
* An Objective C source file, as determined by file extension.
|
||||
*
|
||||
* For the related notion of whether a file is compiled as Objective C
|
||||
* code, use `File.compiledAsObjC`.
|
||||
*/
|
||||
deprecated class ObjCFile extends File {
|
||||
ObjCFile() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Objective-C is no longer supported.
|
||||
* An Objective C++ source file, as determined by file extension.
|
||||
*
|
||||
* For the related notion of whether a file is compiled as Objective C++
|
||||
* code, use `File.compiledAsObjCpp`.
|
||||
*/
|
||||
deprecated class ObjCppFile extends File {
|
||||
ObjCppFile() { none() }
|
||||
}
|
||||
877
cpp/ql/lib/semmle/code/cpp/Function.qll
Normal file
877
cpp/ql/lib/semmle/code/cpp/Function.qll
Normal file
@@ -0,0 +1,877 @@
|
||||
/**
|
||||
* Provides classes for working with functions, including template functions.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Location
|
||||
import semmle.code.cpp.Class
|
||||
import semmle.code.cpp.Parameter
|
||||
import semmle.code.cpp.exprs.Call
|
||||
import semmle.code.cpp.metrics.MetricFunction
|
||||
import semmle.code.cpp.Linkage
|
||||
private import semmle.code.cpp.internal.ResolveClass
|
||||
|
||||
/**
|
||||
* A C/C++ function [N4140 8.3.5]. Both member functions and non-member
|
||||
* functions are included. For example the function `MyFunction` in:
|
||||
* ```
|
||||
* void MyFunction() {
|
||||
* DoSomething();
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Function has a one-to-many relationship with FunctionDeclarationEntry,
|
||||
* because the same function can be declared in multiple locations. This
|
||||
* relationship between `Declaration` and `DeclarationEntry` is explained
|
||||
* in more detail in `Declaration.qll`.
|
||||
*/
|
||||
class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
override string getName() { functions(underlyingElement(this), result, _) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getIdentityString(Declaration)` from `semmle.code.cpp.Print` instead.
|
||||
* Gets the full signature of this function, including return type, parameter
|
||||
* types, and template arguments.
|
||||
*
|
||||
* For example, in the following code:
|
||||
* ```
|
||||
* template<typename T> T min(T x, T y);
|
||||
* int z = min(5, 7);
|
||||
* ```
|
||||
* The full signature of the function called on the last line would be
|
||||
* "min<int>(int, int) -> int", and the full signature of the uninstantiated
|
||||
* template on the first line would be "min<T>(T, T) -> T".
|
||||
*/
|
||||
string getFullSignature() {
|
||||
exists(string name, string templateArgs, string args |
|
||||
result = name + templateArgs + args + " -> " + this.getType().toString() and
|
||||
name = this.getQualifiedName() and
|
||||
(
|
||||
if exists(this.getATemplateArgument())
|
||||
then
|
||||
templateArgs =
|
||||
"<" +
|
||||
concat(int i |
|
||||
exists(this.getTemplateArgument(i))
|
||||
|
|
||||
this.getTemplateArgument(i).toString(), ", " order by i
|
||||
) + ">"
|
||||
else templateArgs = ""
|
||||
) and
|
||||
args =
|
||||
"(" +
|
||||
concat(int i |
|
||||
exists(this.getParameter(i))
|
||||
|
|
||||
this.getParameter(i).getType().toString(), ", " order by i
|
||||
) + ")"
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a specifier of this function. */
|
||||
override Specifier getASpecifier() {
|
||||
funspecifiers(underlyingElement(this), unresolveElement(result)) or
|
||||
result.hasName(this.getADeclarationEntry().getASpecifier())
|
||||
}
|
||||
|
||||
/** Gets an attribute of this function. */
|
||||
Attribute getAnAttribute() { funcattributes(underlyingElement(this), unresolveElement(result)) }
|
||||
|
||||
/** Holds if this function is generated by the compiler. */
|
||||
predicate isCompilerGenerated() { compgenerated(underlyingElement(this)) }
|
||||
|
||||
/** Holds if this function is inline. */
|
||||
predicate isInline() { this.hasSpecifier("inline") }
|
||||
|
||||
/**
|
||||
* Holds if this function is virtual.
|
||||
*
|
||||
* Unlike `isDeclaredVirtual()`, `isVirtual()` holds even if the function
|
||||
* is not explicitly declared with the `virtual` specifier.
|
||||
*/
|
||||
predicate isVirtual() { this.hasSpecifier("virtual") }
|
||||
|
||||
/** Holds if this function is declared with the `virtual` specifier. */
|
||||
predicate isDeclaredVirtual() { this.hasSpecifier("declared_virtual") }
|
||||
|
||||
/** Holds if this function is declared with the `override` specifier. */
|
||||
predicate isOverride() { this.hasSpecifier("override") }
|
||||
|
||||
/** Holds if this function is declared with the `final` specifier. */
|
||||
predicate isFinal() { this.hasSpecifier("final") }
|
||||
|
||||
/**
|
||||
* Holds if this function is deleted.
|
||||
* This may be because it was explicitly deleted with an `= delete`
|
||||
* definition, or because the compiler was unable to auto-generate a
|
||||
* definition for it.
|
||||
*
|
||||
* Most implicitly deleted functions are omitted from the database.
|
||||
* `Class.implicitCopyConstructorDeleted` and
|
||||
* `Class.implicitCopyAssignmentOperatorDeleted` can be used to find
|
||||
* whether a class would have had those members implicitly deleted.
|
||||
*/
|
||||
predicate isDeleted() { function_deleted(underlyingElement(this)) }
|
||||
|
||||
/**
|
||||
* Holds if this function is explicitly defaulted with the `= default`
|
||||
* specifier.
|
||||
*/
|
||||
predicate isDefaulted() { function_defaulted(underlyingElement(this)) }
|
||||
|
||||
/**
|
||||
* Holds if this function is declared to be `constexpr`.
|
||||
*
|
||||
* Note that this does not hold if the function has been declared
|
||||
* `consteval`.
|
||||
*/
|
||||
predicate isDeclaredConstexpr() { this.hasSpecifier("declared_constexpr") }
|
||||
|
||||
/**
|
||||
* Holds if this function is `constexpr`. Normally, this holds if and
|
||||
* only if `isDeclaredConstexpr()` holds, but in some circumstances
|
||||
* they differ. For example, with
|
||||
* ```
|
||||
* int f(int i) { return 6; }
|
||||
* template <typename T> constexpr int g(T x) { return f(x); }
|
||||
* ```
|
||||
* `g<int>` is declared constexpr, but is not constexpr.
|
||||
*
|
||||
* Will also hold if this function is `consteval`.
|
||||
*/
|
||||
predicate isConstexpr() { this.hasSpecifier("is_constexpr") }
|
||||
|
||||
/**
|
||||
* Holds if this function is declared to be `consteval`.
|
||||
*/
|
||||
predicate isConsteval() { this.hasSpecifier("is_consteval") }
|
||||
|
||||
/**
|
||||
* Holds if this function is declared with `__attribute__((naked))` or
|
||||
* `__declspec(naked)`.
|
||||
*/
|
||||
predicate isNaked() { this.getAnAttribute().hasName("naked") }
|
||||
|
||||
/**
|
||||
* Holds if this function has a trailing return type.
|
||||
*
|
||||
* Note that this is true whether or not deduction took place. For example,
|
||||
* this holds for both `e` and `f`, but not `g` or `h`:
|
||||
* ```
|
||||
* auto e() -> int { return 0; }
|
||||
* auto f() -> auto { return 0; }
|
||||
* auto g() { return 0; }
|
||||
* int h() { return 0; }
|
||||
* ```
|
||||
*/
|
||||
predicate hasTrailingReturnType() { this.hasSpecifier("has_trailing_return_type") }
|
||||
|
||||
/** Gets the return type of this function. */
|
||||
Type getType() { function_return_type(underlyingElement(this), unresolveElement(result)) }
|
||||
|
||||
/**
|
||||
* Gets the return type of this function after specifiers have been deeply
|
||||
* stripped and typedefs have been resolved.
|
||||
*/
|
||||
Type getUnspecifiedType() { result = this.getType().getUnspecifiedType() }
|
||||
|
||||
/**
|
||||
* Gets the nth parameter of this function. There is no result for the
|
||||
* implicit `this` parameter, and there is no `...` varargs pseudo-parameter.
|
||||
*/
|
||||
Parameter getParameter(int n) { params(unresolveElement(result), underlyingElement(this), n, _) }
|
||||
|
||||
/**
|
||||
* Gets a parameter of this function. There is no result for the implicit
|
||||
* `this` parameter, and there is no `...` varargs pseudo-parameter.
|
||||
*/
|
||||
Parameter getAParameter() { params(unresolveElement(result), underlyingElement(this), _, _) }
|
||||
|
||||
/**
|
||||
* Gets an access of this function.
|
||||
*
|
||||
* To get calls to this function, use `getACallToThisFunction` instead.
|
||||
*/
|
||||
FunctionAccess getAnAccess() { result.getTarget() = this }
|
||||
|
||||
/**
|
||||
* Gets the number of parameters of this function, _not_ including any
|
||||
* implicit `this` parameter or any `...` varargs pseudo-parameter.
|
||||
*/
|
||||
int getNumberOfParameters() { result = count(this.getAParameter()) }
|
||||
|
||||
/**
|
||||
* Gets the number of parameters of this function, _including_ any implicit
|
||||
* `this` parameter but _not_ including any `...` varargs pseudo-parameter.
|
||||
*/
|
||||
int getEffectiveNumberOfParameters() {
|
||||
// This method is overridden in `MemberFunction`, where the result is
|
||||
// adjusted to account for the implicit `this` parameter.
|
||||
result = this.getNumberOfParameters()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string representing the parameters of this function.
|
||||
*
|
||||
* For example: for a function `int Foo(int p1, int p2)` this would
|
||||
* return `int p1, int p2`.
|
||||
*/
|
||||
string getParameterString() {
|
||||
result = concat(int i | | min(this.getParameter(i).getTypedName()), ", " order by i)
|
||||
}
|
||||
|
||||
/** Gets a call to this function. */
|
||||
FunctionCall getACallToThisFunction() { result.getTarget() = this }
|
||||
|
||||
/**
|
||||
* Gets a declaration entry corresponding to this declaration. The
|
||||
* relationship between `Declaration` and `DeclarationEntry` is explained
|
||||
* in `Declaration.qll`.
|
||||
*/
|
||||
override FunctionDeclarationEntry getADeclarationEntry() {
|
||||
if fun_decls(_, underlyingElement(this), _, _, _)
|
||||
then this.declEntry(result)
|
||||
else
|
||||
exists(Function f |
|
||||
this.isConstructedFrom(f) and
|
||||
fun_decls(unresolveElement(result), unresolveElement(f), _, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate declEntry(FunctionDeclarationEntry fde) {
|
||||
fun_decls(unresolveElement(fde), underlyingElement(this), _, _, _) and
|
||||
// If one .cpp file specializes a function, and another calls the
|
||||
// specialized function, then when extracting the second we only see an
|
||||
// instantiation, not the specialization. We Therefore need to ignore
|
||||
// any non-specialized declarations if there are any specialized ones.
|
||||
(this.isSpecialization() implies fde.isSpecialization())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the location of a `FunctionDeclarationEntry` corresponding to this
|
||||
* declaration.
|
||||
*/
|
||||
override Location getADeclarationLocation() { result = this.getADeclarationEntry().getLocation() }
|
||||
|
||||
/** Holds if this Function is a Template specialization. */
|
||||
predicate isSpecialization() {
|
||||
exists(FunctionDeclarationEntry fde |
|
||||
fun_decls(unresolveElement(fde), underlyingElement(this), _, _, _) and
|
||||
fde.isSpecialization()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the declaration entry corresponding to this declaration that is a
|
||||
* definition, if any.
|
||||
*/
|
||||
override FunctionDeclarationEntry getDefinition() {
|
||||
result = this.getADeclarationEntry() and
|
||||
result.isDefinition()
|
||||
}
|
||||
|
||||
/** Gets the location of the definition, if any. */
|
||||
override Location getDefinitionLocation() {
|
||||
if exists(this.getDefinition())
|
||||
then result = this.getDefinition().getLocation()
|
||||
else exists(Function f | this.isConstructedFrom(f) and result = f.getDefinition().getLocation())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the preferred location of this declaration. (The location of the
|
||||
* definition, if possible.)
|
||||
*/
|
||||
override Location getLocation() {
|
||||
if exists(this.getDefinition())
|
||||
then result = this.getDefinitionLocation()
|
||||
else result = this.getADeclarationLocation()
|
||||
}
|
||||
|
||||
/** Gets a child declaration of this function. */
|
||||
Declaration getADeclaration() { result = this.getAParameter() }
|
||||
|
||||
/**
|
||||
* Gets the block that is the function body.
|
||||
*
|
||||
* For C++ functions whose body is a function try statement rather than a
|
||||
* block, this gives the block guarded by the try statement. See
|
||||
* `FunctionTryStmt` for further information.
|
||||
*/
|
||||
BlockStmt getBlock() { result.getParentScope() = this }
|
||||
|
||||
/** Holds if this function has an entry point. */
|
||||
predicate hasEntryPoint() { exists(this.getEntryPoint()) }
|
||||
|
||||
/**
|
||||
* Gets the first node in this function's control flow graph.
|
||||
*
|
||||
* For most functions, this first node will be the `BlockStmt` returned by
|
||||
* `getBlock`. However in C++, the first node can also be a
|
||||
* `FunctionTryStmt`.
|
||||
*/
|
||||
Stmt getEntryPoint() { function_entry_point(underlyingElement(this), unresolveElement(result)) }
|
||||
|
||||
/**
|
||||
* Gets the metric class. `MetricFunction` has methods for computing
|
||||
* various metrics, such as "number of lines of code" and "number of
|
||||
* function calls".
|
||||
*/
|
||||
MetricFunction getMetrics() { result = this }
|
||||
|
||||
/** Holds if this function calls the function `f`. */
|
||||
predicate calls(Function f) { exists(Locatable l | this.calls(f, l)) }
|
||||
|
||||
/**
|
||||
* Holds if this function calls the function `f` in the `FunctionCall`
|
||||
* expression `l`.
|
||||
*/
|
||||
predicate calls(Function f, Locatable l) {
|
||||
exists(FunctionCall call |
|
||||
call.getEnclosingFunction() = this and call.getTarget() = f and call = l
|
||||
)
|
||||
or
|
||||
exists(DestructorCall call |
|
||||
call.getEnclosingFunction() = this and call.getTarget() = f and call = l
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this function accesses a function or variable or enumerator `a`. */
|
||||
predicate accesses(Declaration a) { exists(Locatable l | this.accesses(a, l)) }
|
||||
|
||||
/**
|
||||
* Holds if this function accesses a function or variable or enumerator `a`
|
||||
* in the `Access` expression `l`.
|
||||
*/
|
||||
predicate accesses(Declaration a, Locatable l) {
|
||||
exists(Access access |
|
||||
access.getEnclosingFunction() = this and
|
||||
a = access.getTarget() and
|
||||
access = l
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a variable that is written-to in this function. */
|
||||
Variable getAWrittenVariable() {
|
||||
exists(ConstructorFieldInit cfi |
|
||||
cfi.getEnclosingFunction() = this and result = cfi.getTarget()
|
||||
)
|
||||
or
|
||||
exists(VariableAccess va |
|
||||
va = result.getAnAccess() and
|
||||
va.isUsedAsLValue() and
|
||||
va.getEnclosingFunction() = this
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class of which this function, called `memberName`, is a member.
|
||||
*
|
||||
* Prefer to use `getDeclaringType()` or `getName()` directly if you do not
|
||||
* need to reason about both.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
Class getClassAndName(string memberName) {
|
||||
this.hasName(memberName) and
|
||||
this.getDeclaringType() = result
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements `ControlFlowNode.getControlFlowScope`. The `Function` is
|
||||
* used to represent the exit node of the control flow graph, so it is
|
||||
* its own scope.
|
||||
*/
|
||||
override Function getControlFlowScope() { result = this }
|
||||
|
||||
/**
|
||||
* Implements `ControlFlowNode.getEnclosingStmt`. The `Function` is
|
||||
* used to represent the exit node of the control flow graph, so it
|
||||
* has no enclosing statement.
|
||||
*/
|
||||
override Stmt getEnclosingStmt() { none() }
|
||||
|
||||
/**
|
||||
* Holds if this function has C linkage, as specified by one of its
|
||||
* declaration entries. For example: `extern "C" void foo();`.
|
||||
*/
|
||||
predicate hasCLinkage() { this.getADeclarationEntry().hasCLinkage() }
|
||||
|
||||
/**
|
||||
* Holds if this function is constructed from `f` as a result
|
||||
* of template instantiation. If so, it originates either from a template
|
||||
* function or from a function nested in a template class.
|
||||
*/
|
||||
predicate isConstructedFrom(Function f) {
|
||||
function_instantiation(underlyingElement(this), unresolveElement(f))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function is defined in several files. This is illegal in
|
||||
* C (though possible in some C++ compilers), and likely indicates that
|
||||
* several functions that are not linked together have been compiled. An
|
||||
* example would be a project with many 'main' functions.
|
||||
*/
|
||||
predicate isMultiplyDefined() { strictcount(this.getFile()) > 1 }
|
||||
|
||||
/** Holds if this function is a varargs function. */
|
||||
predicate isVarargs() { this.hasSpecifier("varargs") }
|
||||
|
||||
/** Gets a type that is specified to be thrown by the function. */
|
||||
Type getAThrownType() { result = this.getADeclarationEntry().getAThrownType() }
|
||||
|
||||
/**
|
||||
* Gets the `i`th type specified to be thrown by the function.
|
||||
*/
|
||||
Type getThrownType(int i) { result = this.getADeclarationEntry().getThrownType(i) }
|
||||
|
||||
/** Holds if the function has an exception specification. */
|
||||
predicate hasExceptionSpecification() { this.getADeclarationEntry().hasExceptionSpecification() }
|
||||
|
||||
/** Holds if this function has a `throw()` exception specification. */
|
||||
predicate isNoThrow() { this.getADeclarationEntry().isNoThrow() }
|
||||
|
||||
/** Holds if this function has a `noexcept` exception specification. */
|
||||
predicate isNoExcept() { this.getADeclarationEntry().isNoExcept() }
|
||||
|
||||
/**
|
||||
* Gets a function that overloads this one.
|
||||
*
|
||||
* Note: if _overrides_ are wanted rather than _overloads_ then
|
||||
* `MemberFunction::getAnOverridingFunction` should be used instead.
|
||||
*/
|
||||
Function getAnOverload() {
|
||||
(
|
||||
// If this function is declared in a class, only consider other
|
||||
// functions from the same class.
|
||||
exists(string name, Class declaringType |
|
||||
candGetAnOverloadMember(name, declaringType, this) and
|
||||
candGetAnOverloadMember(name, declaringType, result)
|
||||
)
|
||||
or
|
||||
// Conversely, if this function is not
|
||||
// declared in a class, only consider other functions not declared in a
|
||||
// class.
|
||||
exists(string name, Namespace namespace |
|
||||
candGetAnOverloadNonMember(name, namespace, this) and
|
||||
candGetAnOverloadNonMember(name, namespace, result)
|
||||
)
|
||||
) and
|
||||
result != this and
|
||||
// Instantiations and specializations don't participate in overload
|
||||
// resolution.
|
||||
not (
|
||||
this instanceof FunctionTemplateInstantiation or
|
||||
result instanceof FunctionTemplateInstantiation
|
||||
) and
|
||||
not (
|
||||
this instanceof FunctionTemplateSpecialization or
|
||||
result instanceof FunctionTemplateSpecialization
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a link target which compiled or referenced this function. */
|
||||
LinkTarget getALinkTarget() { this = result.getAFunction() }
|
||||
|
||||
/**
|
||||
* Holds if this function is side-effect free (conservative
|
||||
* approximation).
|
||||
*/
|
||||
predicate isSideEffectFree() { not this.mayHaveSideEffects() }
|
||||
|
||||
/**
|
||||
* Holds if this function may have side-effects; if in doubt, we assume it
|
||||
* may.
|
||||
*/
|
||||
predicate mayHaveSideEffects() {
|
||||
// If we cannot see the definition then we assume that it may have
|
||||
// side-effects.
|
||||
if exists(this.getEntryPoint())
|
||||
then
|
||||
// If it might be globally impure (we don't care about it modifying
|
||||
// temporaries) then it may have side-effects.
|
||||
this.getEntryPoint().mayBeGloballyImpure()
|
||||
or
|
||||
// Constructor initializers are separate from the entry point ...
|
||||
this.(Constructor).getAnInitializer().mayBeGloballyImpure()
|
||||
or
|
||||
// ... and likewise for destructors.
|
||||
this.(Destructor).getADestruction().mayBeGloballyImpure()
|
||||
else
|
||||
// Unless it's a function that we know is side-effect free, it may
|
||||
// have side-effects.
|
||||
not this.hasGlobalOrStdName([
|
||||
"strcmp", "wcscmp", "_mbscmp", "strlen", "wcslen", "_mbslen", "_mbslen_l", "_mbstrlen",
|
||||
"_mbstrlen_l", "strnlen", "strnlen_s", "wcsnlen", "wcsnlen_s", "_mbsnlen", "_mbsnlen_l",
|
||||
"_mbstrnlen", "_mbstrnlen_l", "strncmp", "wcsncmp", "_mbsncmp", "_mbsncmp_l", "strchr",
|
||||
"memchr", "wmemchr", "memcmp", "wmemcmp", "_memicmp", "_memicmp_l", "feof", "isdigit",
|
||||
"isxdigit", "abs", "fabs", "labs", "floor", "ceil", "atoi", "atol", "atoll", "atof"
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the nearest enclosing AccessHolder.
|
||||
*/
|
||||
override AccessHolder getEnclosingAccessHolder() { result = this.getDeclaringType() }
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate candGetAnOverloadMember(string name, Class declaringType, Function f) {
|
||||
f.getName() = name and
|
||||
f.getDeclaringType() = declaringType
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate candGetAnOverloadNonMember(string name, Namespace namespace, Function f) {
|
||||
f.getName() = name and
|
||||
f.getNamespace() = namespace and
|
||||
not exists(f.getDeclaringType())
|
||||
}
|
||||
|
||||
/**
|
||||
* A particular declaration or definition of a C/C++ function. For example the
|
||||
* declaration and definition of `MyFunction` in the following code are each a
|
||||
* `FunctionDeclarationEntry`:
|
||||
* ```
|
||||
* void MyFunction();
|
||||
*
|
||||
* void MyFunction() {
|
||||
* DoSomething();
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
|
||||
/** Gets the function which is being declared or defined. */
|
||||
override Function getDeclaration() { result = this.getFunction() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "FunctionDeclarationEntry" }
|
||||
|
||||
/** Gets the function which is being declared or defined. */
|
||||
Function getFunction() { fun_decls(underlyingElement(this), unresolveElement(result), _, _, _) }
|
||||
|
||||
/** Gets the name of the function. */
|
||||
override string getName() { fun_decls(underlyingElement(this), _, _, result, _) }
|
||||
|
||||
/**
|
||||
* Gets the return type of the function which is being declared or
|
||||
* defined.
|
||||
*/
|
||||
override Type getType() { fun_decls(underlyingElement(this), _, unresolveElement(result), _, _) }
|
||||
|
||||
/** Gets the location of this declaration entry. */
|
||||
override Location getLocation() { fun_decls(underlyingElement(this), _, _, _, result) }
|
||||
|
||||
/** Gets a specifier associated with this declaration entry. */
|
||||
override string getASpecifier() { fun_decl_specifiers(underlyingElement(this), result) }
|
||||
|
||||
/**
|
||||
* Implements `Element.getEnclosingElement`. A function declaration does
|
||||
* not have an enclosing element.
|
||||
*/
|
||||
override Element getEnclosingElement() { none() }
|
||||
|
||||
/**
|
||||
* Gets the typedef type (if any) used for this function declaration. As
|
||||
* an example, the typedef type in the declaration of function foo in the
|
||||
* following is Foo:
|
||||
*
|
||||
* typedef int Foo();
|
||||
* static Foo foo;
|
||||
*/
|
||||
TypedefType getTypedefType() {
|
||||
fun_decl_typedef_type(underlyingElement(this), unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cyclomatic complexity of this function:
|
||||
*
|
||||
* The number of branching statements (if, while, do, for, switch,
|
||||
* case, catch) plus the number of branching expressions (`?`, `&&`,
|
||||
* `||`) plus one.
|
||||
*/
|
||||
int getCyclomaticComplexity() { result = 1 + cyclomaticComplexityBranches(this.getBlock()) }
|
||||
|
||||
/**
|
||||
* If this is a function definition, get the block containing the
|
||||
* function body.
|
||||
*/
|
||||
BlockStmt getBlock() {
|
||||
this.isDefinition() and
|
||||
result = this.getFunction().getBlock() and
|
||||
result.getFile() = this.getFile()
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is a function definition, get the number of lines of code
|
||||
* associated with it.
|
||||
*/
|
||||
pragma[noopt]
|
||||
int getNumberOfLines() {
|
||||
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
|
||||
diff = end - start and
|
||||
result = diff + 1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the declaration entry for a parameter of this function
|
||||
* declaration.
|
||||
*/
|
||||
ParameterDeclarationEntry getAParameterDeclarationEntry() {
|
||||
result = this.getParameterDeclarationEntry(_)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the declaration entry for the nth parameter of this function
|
||||
* declaration.
|
||||
*/
|
||||
ParameterDeclarationEntry getParameterDeclarationEntry(int n) {
|
||||
param_decl_bind(unresolveElement(result), n, underlyingElement(this))
|
||||
}
|
||||
|
||||
/** Gets the number of parameters of this function declaration. */
|
||||
int getNumberOfParameters() { result = count(this.getAParameterDeclarationEntry()) }
|
||||
|
||||
/**
|
||||
* Gets a string representing the parameters of this function declaration.
|
||||
*
|
||||
* For example: for a function 'int Foo(int p1, int p2)' this would
|
||||
* return 'int p1, int p2'.
|
||||
*/
|
||||
string getParameterString() {
|
||||
result =
|
||||
concat(int i | | min(this.getParameterDeclarationEntry(i).getTypedName()), ", " order by i)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this declaration entry specifies C linkage:
|
||||
*
|
||||
* `extern "C" void foo();`
|
||||
*/
|
||||
predicate hasCLinkage() { this.getASpecifier() = "c_linkage" }
|
||||
|
||||
/** Holds if this declaration entry has a void parameter 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)) }
|
||||
|
||||
/** Holds if this declaration is a Template specialization. */
|
||||
predicate isSpecialization() { fun_specialized(underlyingElement(this)) }
|
||||
|
||||
/**
|
||||
* Holds if this declaration is an implicit function declaration, that is,
|
||||
* where a function is used before it is declared (under older C standards).
|
||||
*/
|
||||
predicate isImplicit() { fun_implicit(underlyingElement(this)) }
|
||||
|
||||
/** Gets a type that is specified to be thrown by the declared function. */
|
||||
Type getAThrownType() { result = this.getThrownType(_) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th type specified to be thrown by the declared function
|
||||
* (where `i` is indexed from 0). For example, if a function is declared
|
||||
* to `throw(int,float)`, then the thrown type with index 0 would be
|
||||
* `int`, and that with index 1 would be `float`.
|
||||
*/
|
||||
Type getThrownType(int i) {
|
||||
fun_decl_throws(underlyingElement(this), i, unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* If this declaration has a noexcept-specification [N4140 15.4], then
|
||||
* this predicate returns the argument to `noexcept` if one was given.
|
||||
*/
|
||||
Expr getNoExceptExpr() { fun_decl_noexcept(underlyingElement(this), unresolveElement(result)) }
|
||||
|
||||
/**
|
||||
* Holds if the declared function has an exception specification [N4140
|
||||
* 15.4].
|
||||
*/
|
||||
predicate hasExceptionSpecification() {
|
||||
fun_decl_throws(underlyingElement(this), _, _) or
|
||||
fun_decl_noexcept(underlyingElement(this), _) or
|
||||
this.isNoThrow() or
|
||||
this.isNoExcept()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the declared function has a `throw()` exception specification.
|
||||
*/
|
||||
predicate isNoThrow() { fun_decl_empty_throws(underlyingElement(this)) }
|
||||
|
||||
/**
|
||||
* Holds if the declared function has an empty `noexcept` exception
|
||||
* specification.
|
||||
*/
|
||||
predicate isNoExcept() { fun_decl_empty_noexcept(underlyingElement(this)) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ non-member function (a function that is not a member of any
|
||||
* class). For example, in the following code, `MyFunction` is a
|
||||
* `TopLevelFunction` but `MyMemberFunction` is not:
|
||||
* ```
|
||||
* void MyFunction() {
|
||||
* DoSomething();
|
||||
* }
|
||||
*
|
||||
* class MyClass {
|
||||
* public:
|
||||
* void MyMemberFunction() {
|
||||
* DoSomething();
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class TopLevelFunction extends Function {
|
||||
TopLevelFunction() { not this.isMember() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TopLevelFunction" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ user-defined operator [N4140 13.5].
|
||||
*/
|
||||
class Operator extends Function {
|
||||
Operator() { functions(underlyingElement(this), _, 5) }
|
||||
|
||||
override string getAPrimaryQlClass() {
|
||||
not this instanceof MemberFunction and result = "Operator"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ function which has a non-empty template argument list. For example
|
||||
* the function `myTemplateFunction` in the following code:
|
||||
* ```
|
||||
* template<class T>
|
||||
* void myTemplateFunction(T t) {
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* This comprises function declarations which are immediately preceded by
|
||||
* `template <...>`, where the "..." part is not empty, and therefore it does
|
||||
* not include:
|
||||
*
|
||||
* 1. Full specializations of template functions, as they have an empty
|
||||
* template argument list.
|
||||
* 2. Instantiations of template functions, as they don't have an
|
||||
* explicit template argument list.
|
||||
* 3. Member functions of template classes - unless they have their own
|
||||
* (non-empty) template argument list.
|
||||
*/
|
||||
class TemplateFunction extends Function {
|
||||
TemplateFunction() {
|
||||
is_function_template(underlyingElement(this)) and exists(this.getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TemplateFunction" }
|
||||
|
||||
/**
|
||||
* Gets a compiler-generated instantiation of this function template.
|
||||
*/
|
||||
Function getAnInstantiation() {
|
||||
result.isConstructedFrom(this) and
|
||||
not result.isSpecialization()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a full specialization of this function template.
|
||||
*
|
||||
* Note that unlike classes, functions overload rather than specialize
|
||||
* partially. Therefore this does not include things which "look like"
|
||||
* partial specializations, nor does it include full specializations of
|
||||
* such things -- see FunctionTemplateSpecialization for further details.
|
||||
*/
|
||||
FunctionTemplateSpecialization getASpecialization() { result.getPrimaryTemplate() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that is an instantiation of a template. For example
|
||||
* the instantiation `myTemplateFunction<int>` in the following code:
|
||||
* ```
|
||||
* template<class T>
|
||||
* void myTemplateFunction(T t) {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* void caller(int i) {
|
||||
* myTemplateFunction<int>(i);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class FunctionTemplateInstantiation extends Function {
|
||||
TemplateFunction tf;
|
||||
|
||||
FunctionTemplateInstantiation() { tf.getAnInstantiation() = this }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "FunctionTemplateInstantiation" }
|
||||
|
||||
/**
|
||||
* Gets the function template from which this instantiation was instantiated.
|
||||
*
|
||||
* Example: For `int const& std::min<int>(int const&, int const&)`, returns `T const& min<T>(T const&, T const&)`.
|
||||
*/
|
||||
TemplateFunction getTemplate() { result = tf }
|
||||
}
|
||||
|
||||
/**
|
||||
* An explicit specialization of a C++ function template. For example the
|
||||
* function `myTemplateFunction<int>` in the following code:
|
||||
* ```
|
||||
* template<class T>
|
||||
* void myTemplateFunction(T t) {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* template<>
|
||||
* void myTemplateFunction<int>(int i) {
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Note that unlike classes, functions overload rather than specialize
|
||||
* partially. Therefore this only includes the last two of the following
|
||||
* four definitions, and in particular does not include the second one:
|
||||
*
|
||||
* ```
|
||||
* template <typename T> void f(T) {...}
|
||||
* template <typename T> void f(T*) {...}
|
||||
* template <> void f<int>(int *) {...}
|
||||
* template <> void f<int*>(int *) {...}
|
||||
* ```
|
||||
*
|
||||
* Furthermore, this does not include compiler-generated instantiations of
|
||||
* function templates.
|
||||
*
|
||||
* For further reference on function template specializations, see:
|
||||
* http://www.gotw.ca/publications/mill17.htm
|
||||
*/
|
||||
class FunctionTemplateSpecialization extends Function {
|
||||
FunctionTemplateSpecialization() { this.isSpecialization() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "FunctionTemplateSpecialization" }
|
||||
|
||||
/**
|
||||
* Gets the primary template for the specialization (the function template
|
||||
* this specializes).
|
||||
*/
|
||||
TemplateFunction getPrimaryTemplate() { this.isConstructedFrom(result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A GCC built-in function. For example: `__builtin___memcpy_chk`.
|
||||
*/
|
||||
class BuiltInFunction extends Function {
|
||||
BuiltInFunction() { functions(underlyingElement(this), _, 6) }
|
||||
|
||||
/** Gets a dummy location for the built-in function. */
|
||||
override Location getLocation() {
|
||||
suppressUnusedThis(this) and
|
||||
result instanceof UnknownDefaultLocation
|
||||
}
|
||||
}
|
||||
|
||||
private predicate suppressUnusedThis(Function f) { any() }
|
||||
@@ -23,7 +23,7 @@ class Include extends PreprocessorDirective, @ppd_include {
|
||||
* Gets the token which occurs after `#include`, for example `"filename"`
|
||||
* or `<filename>`.
|
||||
*/
|
||||
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() }
|
||||
}
|
||||
@@ -18,6 +18,12 @@ import semmle.code.cpp.controlflow.ControlFlowGraph
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
* But _not_ `4` in the following code:
|
||||
* ```
|
||||
* int myUninitializedVariable;
|
||||
* myUninitializedVariable = 4;
|
||||
* ```
|
||||
* Instead, this is an `Assignment`.
|
||||
*/
|
||||
class Initializer extends ControlFlowNode, @initialiser {
|
||||
override Location getLocation() { initialisers(underlyingElement(this), _, _, result) }
|
||||
@@ -28,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"
|
||||
}
|
||||
|
||||
173
cpp/ql/lib/semmle/code/cpp/Location.qll
Normal file
173
cpp/ql/lib/semmle/code/cpp/Location.qll
Normal file
@@ -0,0 +1,173 @@
|
||||
/**
|
||||
* Provides classes and predicates for locations in the source code.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Element
|
||||
import semmle.code.cpp.File
|
||||
|
||||
/**
|
||||
* A location of a C/C++ artifact.
|
||||
*/
|
||||
class Location extends @location {
|
||||
/** Gets the container corresponding to this location. */
|
||||
Container getContainer() { this.fullLocationInfo(result, _, _, _, _) }
|
||||
|
||||
/** Gets the file corresponding to this location, if any. */
|
||||
File getFile() { result = this.getContainer() }
|
||||
|
||||
/** Gets the 1-based line number (inclusive) where this location starts. */
|
||||
int getStartLine() { this.fullLocationInfo(_, result, _, _, _) }
|
||||
|
||||
/** Gets the 1-based column number (inclusive) where this location starts. */
|
||||
int getStartColumn() { this.fullLocationInfo(_, _, result, _, _) }
|
||||
|
||||
/** Gets the 1-based line number (inclusive) where this location ends. */
|
||||
int getEndLine() { this.fullLocationInfo(_, _, _, result, _) }
|
||||
|
||||
/** Gets the 1-based column number (inclusive) where this location ends. */
|
||||
int getEndColumn() { this.fullLocationInfo(_, _, _, _, result) }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element.
|
||||
*
|
||||
* The format is "file://filePath:startLine:startColumn:endLine:endColumn".
|
||||
*/
|
||||
string toString() {
|
||||
exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
|
||||
this.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
|
|
||||
toUrl(filepath, startline, startcolumn, endline, endcolumn, result)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this element is in the specified container.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline`.
|
||||
*
|
||||
* This predicate is similar to `hasLocationInfo`, but exposes the `Container`
|
||||
* entity, rather than merely its path.
|
||||
*/
|
||||
predicate fullLocationInfo(
|
||||
Container container, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
locations_default(this, unresolveElement(container), startline, startcolumn, endline, endcolumn) or
|
||||
locations_expr(this, unresolveElement(container), startline, startcolumn, endline, endcolumn) or
|
||||
locations_stmt(this, unresolveElement(container), 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
|
||||
* [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(Container f | this.fullLocationInfo(f, startline, startcolumn, endline, endcolumn) |
|
||||
filepath = f.getAbsolutePath()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `this` comes on a line strictly before `l`. */
|
||||
pragma[inline]
|
||||
predicate isBefore(Location l) {
|
||||
this.getFile() = l.getFile() and this.getEndLine() < l.getStartLine()
|
||||
}
|
||||
|
||||
/** Holds if location `l` is completely contained within this one. */
|
||||
predicate subsumes(Location l) {
|
||||
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
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this location corresponds to file `f` and character "offsets"
|
||||
* `start..end`. Note that these are not real character offsets, because
|
||||
* we use `maxCols` to find the length of the longest line and then pretend
|
||||
* that all the lines are the same length. However, these offsets are
|
||||
* convenient for comparing or sorting locations in a file. For an example,
|
||||
* see `subsumes`.
|
||||
*/
|
||||
predicate charLoc(File f, int start, int end) {
|
||||
f = this.getFile() and
|
||||
exists(int maxCols | maxCols = maxCols(f) |
|
||||
start = this.getStartLine() * maxCols + this.getStartColumn() and
|
||||
end = this.getEndLine() * maxCols + this.getEndColumn()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Location` instead.
|
||||
* A location of an element. Not used for expressions or statements, which
|
||||
* instead use LocationExpr and LocationStmt respectively.
|
||||
*/
|
||||
deprecated library class LocationDefault extends Location, @location_default { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Location` instead.
|
||||
* A location of a statement.
|
||||
*/
|
||||
deprecated library class LocationStmt extends Location, @location_stmt { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Location` instead.
|
||||
* A location of an expression.
|
||||
*/
|
||||
deprecated library class LocationExpr extends Location, @location_expr { }
|
||||
|
||||
/**
|
||||
* Gets the length of the longest line in file `f`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int maxCols(File f) {
|
||||
result = max(Location l | l.getFile() = f | l.getStartColumn().maximum(l.getEndColumn()))
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ element that has a location in a file
|
||||
*/
|
||||
class Locatable extends Element { }
|
||||
|
||||
/**
|
||||
* A dummy location which is used when something doesn't have a location in
|
||||
* the source code but needs to have a `Location` associated with it. There
|
||||
* may be several distinct kinds of unknown locations. For example: one for
|
||||
* expressions, one for statements and one for other program elements.
|
||||
*/
|
||||
class UnknownLocation extends Location {
|
||||
UnknownLocation() { this.getFile().getAbsolutePath() = "" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A dummy location which is used when something doesn't have a location in
|
||||
* the source code but needs to have a `Location` associated with it.
|
||||
*/
|
||||
class UnknownDefaultLocation extends UnknownLocation {
|
||||
UnknownDefaultLocation() { locations_default(this, _, 0, 0, 0, 0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A dummy location which is used when an expression doesn't have a
|
||||
* location in the source code but needs to have a `Location` associated
|
||||
* with it.
|
||||
*/
|
||||
class UnknownExprLocation extends UnknownLocation {
|
||||
UnknownExprLocation() { locations_expr(this, _, 0, 0, 0, 0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A dummy location which is used when a statement doesn't have a location
|
||||
* in the source code but needs to have a `Location` associated with it.
|
||||
*/
|
||||
class UnknownStmtLocation extends UnknownLocation {
|
||||
UnknownStmtLocation() { locations_stmt(this, _, 0, 0, 0, 0) }
|
||||
}
|
||||
@@ -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. */
|
||||
@@ -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" }
|
||||
249
cpp/ql/lib/semmle/code/cpp/Namespace.qll
Normal file
249
cpp/ql/lib/semmle/code/cpp/Namespace.qll
Normal file
@@ -0,0 +1,249 @@
|
||||
/**
|
||||
* Provides classes for modeling namespaces, `using` directives and `using` declarations.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Element
|
||||
import semmle.code.cpp.Type
|
||||
import semmle.code.cpp.metrics.MetricNamespace
|
||||
|
||||
/**
|
||||
* A C++ namespace. For example the (single) namespace `A` in the following
|
||||
* code:
|
||||
* ```
|
||||
* namespace A
|
||||
* {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* namespace A
|
||||
* {
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
* Note that namespaces are somewhat nebulous entities, as they do not in
|
||||
* general have a single well-defined location in the source code. The
|
||||
* related notion of a `NamespaceDeclarationEntry` is rather more concrete,
|
||||
* and should be used when a location is required. For example, the `std::`
|
||||
* namespace is particularly nebulous, as parts of it are defined across a
|
||||
* wide range of headers. As a more extreme example, the global namespace
|
||||
* is never explicitly declared, but might correspond to a large proportion
|
||||
* of the source code.
|
||||
*/
|
||||
class Namespace extends NameQualifyingElement, @namespace {
|
||||
/**
|
||||
* Gets the location of the namespace. Most namespaces do not have a
|
||||
* single well-defined source location, so a dummy location is returned,
|
||||
* unless the namespace has exactly one declaration entry.
|
||||
*/
|
||||
override Location getLocation() {
|
||||
if strictcount(this.getADeclarationEntry()) = 1
|
||||
then result = this.getADeclarationEntry().getLocation()
|
||||
else result instanceof UnknownDefaultLocation
|
||||
}
|
||||
|
||||
/** Gets the simple name of this namespace. */
|
||||
override string getName() { namespaces(underlyingElement(this), result) }
|
||||
|
||||
/** Holds if this element is named `name`. */
|
||||
predicate hasName(string name) { name = this.getName() }
|
||||
|
||||
/** Holds if this namespace is anonymous. */
|
||||
predicate isAnonymous() { this.hasName("(unnamed namespace)") }
|
||||
|
||||
/** Gets the name of the parent namespace, if it exists. */
|
||||
private string getParentName() {
|
||||
result = this.getParentNamespace().getName() and
|
||||
result != ""
|
||||
}
|
||||
|
||||
/** Gets the qualified name of this namespace. For example: `a::b`. */
|
||||
string getQualifiedName() {
|
||||
if exists(this.getParentName())
|
||||
then result = this.getParentNamespace().getQualifiedName() + "::" + this.getName()
|
||||
else result = this.getName()
|
||||
}
|
||||
|
||||
/** Gets the parent namespace, if any. */
|
||||
Namespace getParentNamespace() {
|
||||
namespacembrs(unresolveElement(result), underlyingElement(this))
|
||||
or
|
||||
not namespacembrs(_, underlyingElement(this)) and result instanceof GlobalNamespace
|
||||
}
|
||||
|
||||
/** Gets a child declaration of this namespace. */
|
||||
Declaration getADeclaration() { namespacembrs(underlyingElement(this), unresolveElement(result)) }
|
||||
|
||||
/** Gets a child namespace of this namespace. */
|
||||
Namespace getAChildNamespace() {
|
||||
namespacembrs(underlyingElement(this), unresolveElement(result))
|
||||
}
|
||||
|
||||
/** Holds if the namespace is inline. */
|
||||
predicate isInline() { namespace_inline(underlyingElement(this)) }
|
||||
|
||||
/** Holds if this namespace may be from source. */
|
||||
override predicate fromSource() { this.getADeclaration().fromSource() }
|
||||
|
||||
/**
|
||||
* Holds if this namespace is in a library.
|
||||
*
|
||||
* DEPRECATED: never holds.
|
||||
*/
|
||||
deprecated override predicate fromLibrary() { not this.fromSource() }
|
||||
|
||||
/** Gets the metric namespace. */
|
||||
MetricNamespace getMetrics() { result = this }
|
||||
|
||||
/** Gets a version of the `QualifiedName` that is more suitable for display purposes. */
|
||||
string getFriendlyName() { result = this.getQualifiedName() }
|
||||
|
||||
final override string toString() { result = this.getFriendlyName() }
|
||||
|
||||
/** Gets a declaration of (part of) this namespace. */
|
||||
NamespaceDeclarationEntry getADeclarationEntry() { result.getNamespace() = this }
|
||||
|
||||
/** Gets a file which declares (part of) this namespace. */
|
||||
File getAFile() { result = this.getADeclarationEntry().getLocation().getFile() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A declaration of (part of) a C++ namespace. This corresponds to a single
|
||||
* `namespace N { ... }` occurrence in the source code. For example the two
|
||||
* mentions of `A` in the following code:
|
||||
* ```
|
||||
* namespace A
|
||||
* {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* namespace A
|
||||
* {
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class NamespaceDeclarationEntry extends Locatable, @namespace_decl {
|
||||
/**
|
||||
* Get the namespace that this declaration entry corresponds to. There
|
||||
* is a one-to-many relationship between `Namespace` and
|
||||
* `NamespaceDeclarationEntry`.
|
||||
*/
|
||||
Namespace getNamespace() {
|
||||
namespace_decls(underlyingElement(this), unresolveElement(result), _, _)
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNamespace().getFriendlyName() }
|
||||
|
||||
/**
|
||||
* Gets the location of the token preceding the namespace declaration
|
||||
* entry's body.
|
||||
*
|
||||
* For named declarations, such as "namespace MyStuff { ... }", this will
|
||||
* give the "MyStuff" token.
|
||||
*
|
||||
* For anonymous declarations, such as "namespace { ... }", this will
|
||||
* give the "namespace" token.
|
||||
*/
|
||||
override Location getLocation() { namespace_decls(underlyingElement(this), _, result, _) }
|
||||
|
||||
/**
|
||||
* Gets the location of the namespace declaration entry's body. For
|
||||
* example: the "{ ... }" in "namespace N { ... }".
|
||||
*/
|
||||
Location getBodyLocation() { namespace_decls(underlyingElement(this), _, _, result) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "NamespaceDeclarationEntry" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `using` directive or `using` declaration.
|
||||
*/
|
||||
class UsingEntry extends Locatable, @using {
|
||||
override Location getLocation() { usings(underlyingElement(this), _, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `using` declaration. For example:
|
||||
* ```
|
||||
* using std::string;
|
||||
* ```
|
||||
*/
|
||||
class UsingDeclarationEntry extends UsingEntry {
|
||||
UsingDeclarationEntry() {
|
||||
not exists(Namespace n | usings(underlyingElement(this), unresolveElement(n), _))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the declaration that is referenced by this using declaration. For
|
||||
* example, `std::string` in `using std::string`.
|
||||
*/
|
||||
Declaration getDeclaration() { usings(underlyingElement(this), unresolveElement(result), _) }
|
||||
|
||||
override string toString() { result = "using " + this.getDeclaration().getDescription() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `using` directive. For example:
|
||||
* ```
|
||||
* using namespace std;
|
||||
* ```
|
||||
*/
|
||||
class UsingDirectiveEntry extends UsingEntry {
|
||||
UsingDirectiveEntry() {
|
||||
exists(Namespace n | usings(underlyingElement(this), unresolveElement(n), _))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the namespace that is referenced by this using directive. For
|
||||
* example, `std` in `using namespace std`.
|
||||
*/
|
||||
Namespace getNamespace() { usings(underlyingElement(this), unresolveElement(result), _) }
|
||||
|
||||
override string toString() { result = "using namespace " + this.getNamespace().getFriendlyName() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `g` is an instance of `GlobalNamespace`. This predicate
|
||||
* is used suppress a warning in `GlobalNamespace.getADeclaration()`
|
||||
* by providing a fake use of `this`.
|
||||
*/
|
||||
private predicate suppressWarningForUnused(GlobalNamespace g) { any() }
|
||||
|
||||
/**
|
||||
* The C/C++ global namespace.
|
||||
*/
|
||||
class GlobalNamespace extends Namespace {
|
||||
GlobalNamespace() { this.hasName("") }
|
||||
|
||||
override Declaration getADeclaration() {
|
||||
suppressWarningForUnused(this) and
|
||||
result.isTopLevel() and
|
||||
not namespacembrs(_, unresolveElement(result))
|
||||
}
|
||||
|
||||
/** Gets a child namespace of the global namespace. */
|
||||
override Namespace getAChildNamespace() {
|
||||
suppressWarningForUnused(this) and
|
||||
not namespacembrs(unresolveElement(result), _)
|
||||
}
|
||||
|
||||
override Namespace getParentNamespace() { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getName()`.
|
||||
*/
|
||||
deprecated string getFullName() { result = this.getName() }
|
||||
|
||||
override string getFriendlyName() { result = "(global namespace)" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The C++ `std::` namespace.
|
||||
*/
|
||||
class StdNamespace extends Namespace {
|
||||
StdNamespace() { this.hasName("std") and this.getParentNamespace() instanceof GlobalNamespace }
|
||||
}
|
||||
180
cpp/ql/lib/semmle/code/cpp/Parameter.qll
Normal file
180
cpp/ql/lib/semmle/code/cpp/Parameter.qll
Normal file
@@ -0,0 +1,180 @@
|
||||
/**
|
||||
* Provides a class that models parameters to functions.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Location
|
||||
import semmle.code.cpp.Declaration
|
||||
private import semmle.code.cpp.internal.ResolveClass
|
||||
|
||||
/**
|
||||
* A C/C++ function parameter or catch block parameter. For example the
|
||||
* function parameter `p` and the catch block parameter `e` in the following
|
||||
* code:
|
||||
* ```
|
||||
* void myFunction(int p) {
|
||||
* try {
|
||||
* ...
|
||||
* } catch (const std::exception &e) {
|
||||
* ...
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* For catch block parameters, there is a one-to-one correspondence between
|
||||
* the `Parameter` and its `ParameterDeclarationEntry`.
|
||||
*
|
||||
* For function parameters, there is a one-to-many relationship between
|
||||
* `Parameter` and `ParameterDeclarationEntry`, because one function can
|
||||
* have multiple declarations.
|
||||
*/
|
||||
class Parameter extends LocalScopeVariable, @parameter {
|
||||
/**
|
||||
* Gets the canonical name, or names, of this parameter.
|
||||
*
|
||||
* The canonical names are the first non-empty category from the
|
||||
* following list:
|
||||
* 1. The name given to the parameter at the function's definition or
|
||||
* (for catch block parameters) at the catch block.
|
||||
* 2. A name given to the parameter at a function declaration.
|
||||
* 3. The name "(unnamed parameter i)" where i is the index of the parameter.
|
||||
*/
|
||||
override string getName() {
|
||||
exists(VariableDeclarationEntry vde |
|
||||
vde = this.getANamedDeclarationEntry() and result = vde.getName()
|
||||
|
|
||||
vde.isDefinition() or not this.getANamedDeclarationEntry().isDefinition()
|
||||
)
|
||||
or
|
||||
not exists(this.getANamedDeclarationEntry()) and
|
||||
result = "(unnamed parameter " + this.getIndex().toString() + ")"
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Parameter" }
|
||||
|
||||
/**
|
||||
* Gets the name of this parameter, including it's type.
|
||||
*
|
||||
* For example: `int p`.
|
||||
*/
|
||||
string getTypedName() {
|
||||
exists(string typeString, string nameString |
|
||||
(
|
||||
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
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private VariableDeclarationEntry getANamedDeclarationEntry() {
|
||||
result = this.getAnEffectiveDeclarationEntry() and result.getName() != ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a declaration entry corresponding to this declaration.
|
||||
*
|
||||
* This predicate is the same as getADeclarationEntry(), except that for
|
||||
* parameters of instantiated function templates, gives the declaration
|
||||
* entry of the prototype instantiation of the parameter (as
|
||||
* non-prototype instantiations don't have declaration entries of their
|
||||
* own).
|
||||
*/
|
||||
private VariableDeclarationEntry getAnEffectiveDeclarationEntry() {
|
||||
if this.getFunction().isConstructedFrom(_)
|
||||
then
|
||||
exists(Function prototypeInstantiation |
|
||||
prototypeInstantiation.getParameter(this.getIndex()) = result.getVariable() and
|
||||
this.getFunction().isConstructedFrom(prototypeInstantiation)
|
||||
)
|
||||
else result = this.getADeclarationEntry()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of this parameter in the given block (which should be
|
||||
* the body of a function with which the parameter is associated).
|
||||
*
|
||||
* DEPRECATED: this method was used in a previous implementation of
|
||||
* getName, but is no longer in use.
|
||||
*/
|
||||
deprecated string getNameInBlock(BlockStmt b) {
|
||||
exists(ParameterDeclarationEntry pde |
|
||||
pde.getFunctionDeclarationEntry().getBlock() = b and
|
||||
this.getFunction().getBlock() = b and
|
||||
pde.getVariable() = this and
|
||||
result = pde.getName()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this parameter has a name.
|
||||
*
|
||||
* In other words, this predicate holds precisely when the result of
|
||||
* `getName()` is not "(unnamed parameter i)" (where `i` is the index
|
||||
* of the parameter).
|
||||
*/
|
||||
predicate isNamed() { exists(this.getANamedDeclarationEntry()) }
|
||||
|
||||
/**
|
||||
* Gets the function to which this parameter belongs, if it is a function
|
||||
* parameter.
|
||||
*/
|
||||
override Function getFunction() {
|
||||
params(underlyingElement(this), unresolveElement(result), _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the catch block to which this parameter belongs, if it is a catch
|
||||
* block parameter.
|
||||
*/
|
||||
BlockStmt getCatchBlock() { params(underlyingElement(this), unresolveElement(result), _, _) }
|
||||
|
||||
/**
|
||||
* Gets the zero-based index of this parameter.
|
||||
*
|
||||
* For catch block parameters, this is always zero.
|
||||
*/
|
||||
int getIndex() { params(underlyingElement(this), _, result, _) }
|
||||
|
||||
/**
|
||||
* Gets the type of this parameter.
|
||||
*
|
||||
* Function parameters of array type are a special case in C/C++,
|
||||
* as they are syntactic sugar for parameters of pointer type. The
|
||||
* result is an array type for such parameters.
|
||||
*/
|
||||
override Type getType() { params(underlyingElement(this), _, _, unresolveElement(result)) }
|
||||
|
||||
/**
|
||||
* Gets the canonical location, or locations, of this parameter.
|
||||
*
|
||||
* 1. For catch block parameters, gets the obvious location.
|
||||
* 2. For parameters of functions which have a definition, gets the
|
||||
* location within the function definition.
|
||||
* 3. For parameters of functions which don't have a definition, gets all
|
||||
* of the declaration locations.
|
||||
*/
|
||||
override Location getLocation() {
|
||||
exists(VariableDeclarationEntry vde |
|
||||
vde = this.getAnEffectiveDeclarationEntry() and result = vde.getLocation()
|
||||
|
|
||||
vde.isDefinition() or not this.getAnEffectiveDeclarationEntry().isDefinition()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `int` that is a parameter index for some function. This is needed for binding in certain cases.
|
||||
*/
|
||||
class ParameterIndex extends int {
|
||||
ParameterIndex() {
|
||||
exists(Parameter p | this = p.getIndex()) or
|
||||
exists(Call c | exists(c.getArgument(this))) or // permit indexing varargs
|
||||
this = -1 // used for `this`
|
||||
}
|
||||
}
|
||||
296
cpp/ql/lib/semmle/code/cpp/Preprocessor.qll
Normal file
296
cpp/ql/lib/semmle/code/cpp/Preprocessor.qll
Normal file
@@ -0,0 +1,296 @@
|
||||
import semmle.code.cpp.Location
|
||||
import semmle.code.cpp.Element
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor directive. For example each of the following lines of
|
||||
* code contains a `PreprocessorDirective`:
|
||||
* ```
|
||||
* #pragma once
|
||||
* #ifdef MYDEFINE
|
||||
* #include "myfile.h"
|
||||
* #line 1 "source.c"
|
||||
* ```
|
||||
*/
|
||||
class PreprocessorDirective extends Locatable, @preprocdirect {
|
||||
override string toString() { result = "Preprocessor directive" }
|
||||
|
||||
override Location getLocation() { preprocdirects(underlyingElement(this), _, result) }
|
||||
|
||||
string getHead() { preproctext(underlyingElement(this), result, _) }
|
||||
|
||||
/**
|
||||
* Gets a preprocessor branching directive whose condition affects
|
||||
* whether this directive is performed.
|
||||
*
|
||||
* From a lexical point of view, this returns all `#if`, `#ifdef`,
|
||||
* `#ifndef`, or `#elif` directives which occur before this directive and
|
||||
* have a matching `#endif` which occurs after this directive.
|
||||
*/
|
||||
PreprocessorBranch getAGuard() {
|
||||
exists(PreprocessorEndif e, int line |
|
||||
result.getEndIf() = e 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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class TPreprocessorBranchDirective = @ppd_branch or @ppd_else or @ppd_endif;
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor branch related directive: `#if`, `#ifdef`,
|
||||
* `#ifndef`, `#elif`, `#else` or `#endif`.
|
||||
*/
|
||||
class PreprocessorBranchDirective extends PreprocessorDirective, TPreprocessorBranchDirective {
|
||||
/**
|
||||
* Gets the `#if`, `#ifdef` or `#ifndef` directive which matches this
|
||||
* branching directive.
|
||||
*
|
||||
* If this branch directive was unbalanced, then there will be no
|
||||
* result. Conversely, if the branch matches different `#if` directives
|
||||
* in different translation units, then there can be more than one
|
||||
* result.
|
||||
*/
|
||||
PreprocessorBranch getIf() {
|
||||
result = this.(PreprocessorIf) or
|
||||
result = this.(PreprocessorIfdef) or
|
||||
result = this.(PreprocessorIfndef) or
|
||||
preprocpair(unresolveElement(result), underlyingElement(this))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `#endif` directive which matches this branching directive.
|
||||
*
|
||||
* If this branch directive was unbalanced, then there will be no
|
||||
* result. Conversely, if the branch matched different `#endif`
|
||||
* directives in different translation units, then there can be more than
|
||||
* one result.
|
||||
*/
|
||||
PreprocessorEndif getEndIf() {
|
||||
preprocpair(unresolveElement(this.getIf()), unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next `#elif`, `#else` or `#endif` matching this branching
|
||||
* directive.
|
||||
*
|
||||
* For example `somePreprocessorBranchDirective.getIf().getNext()` gets
|
||||
* the second directive in the same construct as
|
||||
* `somePreprocessorBranchDirective`.
|
||||
*/
|
||||
PreprocessorBranchDirective getNext() {
|
||||
exists(PreprocessorBranch branch |
|
||||
this.getIndexInBranch(branch) + 1 = result.getIndexInBranch(branch)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of this branching directive within the matching #if,
|
||||
* #ifdef or #ifndef.
|
||||
*/
|
||||
private int getIndexInBranch(PreprocessorBranch branch) {
|
||||
this =
|
||||
rank[result](PreprocessorBranchDirective other |
|
||||
other.getIf() = branch
|
||||
|
|
||||
other order by other.getLocation().getStartLine()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor branching directive: `#if`, `#ifdef`, `#ifndef`, or
|
||||
* `#elif`.
|
||||
*
|
||||
* A branching directive has a condition and that condition may be evaluated
|
||||
* at compile-time. As a result, the preprocessor will either take the
|
||||
* branch, or not take the branch.
|
||||
*
|
||||
* However, there are also situations in which a branch's condition isn't
|
||||
* evaluated. The obvious case of this is when the directive is contained
|
||||
* within a branch which wasn't taken. There is also a much more subtle
|
||||
* case involving header guard branches: suitably clever compilers can
|
||||
* notice that a branch is a header guard, and can then subsequently ignore
|
||||
* a `#include` for the file being guarded. It is for this reason that
|
||||
* `wasTaken()` always holds on header guard branches, but `wasNotToken()`
|
||||
* rarely holds on header guard branches.
|
||||
*/
|
||||
class PreprocessorBranch extends PreprocessorBranchDirective, @ppd_branch {
|
||||
/**
|
||||
* Holds if at least one translation unit evaluated this directive's
|
||||
* condition and subsequently took the branch.
|
||||
*/
|
||||
predicate wasTaken() { preproctrue(underlyingElement(this)) }
|
||||
|
||||
/**
|
||||
* Holds if at least one translation unit evaluated this directive's
|
||||
* condition but then didn't take the branch.
|
||||
*
|
||||
* If `#else` is the next matching directive, then this means that the
|
||||
* `#else` was taken instead.
|
||||
*/
|
||||
predicate wasNotTaken() { preprocfalse(underlyingElement(this)) }
|
||||
|
||||
/**
|
||||
* Holds if this directive was either taken by all translation units
|
||||
* which evaluated it, or was not taken by any translation unit which
|
||||
* evaluated it.
|
||||
*/
|
||||
predicate wasPredictable() { not (this.wasTaken() and this.wasNotTaken()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#if` directive. For example there is a
|
||||
* `PreprocessorIf` on the first line of the following code:
|
||||
* ```
|
||||
* #if defined(MYDEFINE)
|
||||
* // ...
|
||||
* #endif
|
||||
* ```
|
||||
* For the related notion of a directive which causes branching (which
|
||||
* includes `#if`, plus also `#ifdef`, `#ifndef`, and `#elif`), see
|
||||
* `PreprocessorBranch`.
|
||||
*/
|
||||
class PreprocessorIf extends PreprocessorBranch, @ppd_if {
|
||||
override string toString() { result = "#if " + this.getHead() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#ifdef` directive. For example there is a
|
||||
* `PreprocessorIfdef` on the first line of the following code:
|
||||
* ```
|
||||
* #ifdef MYDEFINE
|
||||
* // ...
|
||||
* #endif
|
||||
* ```
|
||||
* The syntax `#ifdef X` is shorthand for `#if defined(X)`.
|
||||
*/
|
||||
class PreprocessorIfdef extends PreprocessorBranch, @ppd_ifdef {
|
||||
override string toString() { result = "#ifdef " + this.getHead() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "PreprocessorIfdef" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#ifndef` directive. For example there is a
|
||||
* `PreprocessorIfndef` on the first line of the following code:
|
||||
* ```
|
||||
* #ifndef MYDEFINE
|
||||
* // ...
|
||||
* #endif
|
||||
* ```
|
||||
* The syntax `#ifndef X` is shorthand for `#if !defined(X)`.
|
||||
*/
|
||||
class PreprocessorIfndef extends PreprocessorBranch, @ppd_ifndef {
|
||||
override string toString() { result = "#ifndef " + this.getHead() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#else` directive. For example there is a
|
||||
* `PreprocessorElse` on the fifth line of the following code:
|
||||
* ```
|
||||
* #ifdef MYDEFINE1
|
||||
* // ...
|
||||
* #elif MYDEFINE2
|
||||
* // ...
|
||||
* #else
|
||||
* // ...
|
||||
* #endif
|
||||
* ```
|
||||
*/
|
||||
class PreprocessorElse extends PreprocessorBranchDirective, @ppd_else {
|
||||
override string toString() { result = "#else" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#elif` directive. For example there is a
|
||||
* `PreprocessorElif` on the third line of the following code:
|
||||
* ```
|
||||
* #ifdef MYDEFINE1
|
||||
* // ...
|
||||
* #elif MYDEFINE2
|
||||
* // ...
|
||||
* #else
|
||||
* // ...
|
||||
* #endif
|
||||
* ```
|
||||
*/
|
||||
class PreprocessorElif extends PreprocessorBranch, @ppd_elif {
|
||||
override string toString() { result = "#elif " + this.getHead() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#endif` directive. For example there is a
|
||||
* `PreprocessorEndif` on the third line of the following code:
|
||||
* ```
|
||||
* #ifdef MYDEFINE
|
||||
* // ...
|
||||
* #endif
|
||||
* ```
|
||||
*/
|
||||
class PreprocessorEndif extends PreprocessorBranchDirective, @ppd_endif {
|
||||
override string toString() { result = "#endif" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#warning` directive. For example:
|
||||
* ```
|
||||
* #warning "This configuration is not supported."
|
||||
* ```
|
||||
*/
|
||||
class PreprocessorWarning extends PreprocessorDirective, @ppd_warning {
|
||||
override string toString() { result = "#warning " + this.getHead() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#error` directive. For example:
|
||||
* ```
|
||||
* #error "This configuration is not implemented."
|
||||
* ```
|
||||
*/
|
||||
class PreprocessorError extends PreprocessorDirective, @ppd_error {
|
||||
override string toString() { result = "#error " + this.getHead() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#undef` directive. For example there is a
|
||||
* `PreprocessorUndef` on the second line of the following code:
|
||||
* ```
|
||||
* #ifdef MYMACRO
|
||||
* #undef MYMACRO
|
||||
* #endif
|
||||
* ```
|
||||
*/
|
||||
class PreprocessorUndef extends PreprocessorDirective, @ppd_undef {
|
||||
override string toString() { result = "#undef " + this.getHead() }
|
||||
|
||||
/**
|
||||
* Gets the name of the macro that is undefined.
|
||||
*/
|
||||
string getName() { result = this.getHead() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#pragma` directive. For example:
|
||||
* ```
|
||||
* #pragma once
|
||||
* ```
|
||||
*/
|
||||
class PreprocessorPragma extends PreprocessorDirective, @ppd_pragma {
|
||||
override string toString() {
|
||||
if exists(this.getHead()) then result = "#pragma " + this.getHead() else result = "#pragma"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#line` directive. For example:
|
||||
* ```
|
||||
* #line 1 "source.c"
|
||||
* ```
|
||||
*/
|
||||
class PreprocessorLine extends PreprocessorDirective, @ppd_line {
|
||||
override string toString() { result = "#line " + this.getHead() }
|
||||
}
|
||||
428
cpp/ql/lib/semmle/code/cpp/Print.qll
Normal file
428
cpp/ql/lib/semmle/code/cpp/Print.qll
Normal file
@@ -0,0 +1,428 @@
|
||||
import cpp
|
||||
private import PrintAST
|
||||
|
||||
/**
|
||||
* Print function declarations only if there is a `PrintASTConfiguration`
|
||||
* that requests that function, or no `PrintASTConfiguration` exists.
|
||||
*/
|
||||
private predicate shouldPrintDeclaration(Declaration decl) {
|
||||
not decl instanceof Function
|
||||
or
|
||||
not exists(PrintASTConfiguration c)
|
||||
or
|
||||
exists(PrintASTConfiguration config | config.shouldPrintFunction(decl))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string containing the scope in which this declaration is declared.
|
||||
*/
|
||||
private string getScopePrefix(Declaration decl) {
|
||||
decl.isMember() and result = decl.getDeclaringType().(UserDumpType).getIdentityString() + "::"
|
||||
or
|
||||
decl.isTopLevel() and
|
||||
exists(string parentName |
|
||||
parentName = decl.getNamespace().getQualifiedName() and
|
||||
(
|
||||
parentName != "" and result = parentName + "::"
|
||||
or
|
||||
parentName = "" and result = ""
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(UserType type |
|
||||
type = decl and
|
||||
type.isLocal() and
|
||||
result = "(" + type.getEnclosingFunction().(DumpFunction).getIdentityString() + ")::"
|
||||
)
|
||||
or
|
||||
decl instanceof TemplateParameter and result = ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the identity string of a type used as a parameter. Identical to `Type.getTypeIdentityString()`, except that
|
||||
* it returns `...` for `UnknownType`, which is used to represent variable arguments.
|
||||
*/
|
||||
private string getParameterTypeString(Type parameterType) {
|
||||
if parameterType instanceof UnknownType
|
||||
then result = "..."
|
||||
else result = parameterType.(DumpType).getTypeIdentityString()
|
||||
}
|
||||
|
||||
private string getTemplateArgumentString(Declaration d, int i) {
|
||||
if exists(d.getTemplateArgumentKind(i))
|
||||
then
|
||||
result =
|
||||
d.getTemplateArgumentKind(i).(DumpType).getTypeIdentityString() + " " +
|
||||
d.getTemplateArgument(i)
|
||||
else result = d.getTemplateArgument(i).(DumpType).getTypeIdentityString()
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Declaration` extended to add methods for generating strings useful only for dumps and debugging.
|
||||
*/
|
||||
private class DumpDeclaration extends Declaration {
|
||||
DumpDeclaration() { shouldPrintDeclaration(this) }
|
||||
|
||||
/**
|
||||
* Gets a string that uniquely identifies this declaration, suitable for use when debugging queries. Only holds for
|
||||
* functions, user-defined types, global and namespace-scope variables, and member variables.
|
||||
*
|
||||
* This operation is very expensive, and should not be used in production queries. Consider using
|
||||
* `hasQualifiedName()` for identifying known declarations in production queries.
|
||||
*/
|
||||
string getIdentityString() { none() }
|
||||
|
||||
language[monotonicAggregates]
|
||||
final string getTemplateArgumentsString() {
|
||||
if exists(this.getATemplateArgument())
|
||||
then
|
||||
result =
|
||||
"<" +
|
||||
strictconcat(int i |
|
||||
exists(this.getTemplateArgument(i))
|
||||
|
|
||||
getTemplateArgumentString(this, i), ", " order by i
|
||||
) + ">"
|
||||
else result = ""
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Type` extended to add methods for generating strings useful only for dumps and debugging.
|
||||
*/
|
||||
private class DumpType extends Type {
|
||||
/**
|
||||
* Gets a string that uniquely identifies this type, suitable for use when debugging queries. All typedefs and
|
||||
* decltypes are expanded, and all symbol names are fully qualified.
|
||||
*
|
||||
* This operation is very expensive, and should not be used in production queries.
|
||||
*/
|
||||
final string getTypeIdentityString() {
|
||||
// The identity string of a type is just the concatenation of the four
|
||||
// components below. To create the type identity for a derived type, insert
|
||||
// the declarator of the derived type between the `getDeclaratorPrefix()`
|
||||
// and `getDeclaratorSuffixBeforeQualifiers()`. To create the type identity
|
||||
// for a `SpecifiedType`, insert the qualifiers after
|
||||
// `getDeclaratorSuffixBeforeQualifiers()`.
|
||||
result =
|
||||
this.getTypeSpecifier() + this.getDeclaratorPrefix() +
|
||||
this.getDeclaratorSuffixBeforeQualifiers() + this.getDeclaratorSuffix()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the "type specifier" part of this type's name. This is generally the "leaf" type from which the type was
|
||||
* constructed.
|
||||
*
|
||||
* Examples:
|
||||
* - `int` -> `int`
|
||||
* - `int*` -> `int`
|
||||
* - `int (*&)(float, double) const` -> `int`
|
||||
*
|
||||
* This predicate is intended to be used only by the implementation of `getTypeIdentityString`.
|
||||
*/
|
||||
string getTypeSpecifier() { result = "" }
|
||||
|
||||
/**
|
||||
* Gets the portion of this type's declarator that comes before the declarator for any derived type.
|
||||
*
|
||||
* This predicate is intended to be used only by the implementation of `getTypeIdentityString`.
|
||||
*/
|
||||
string getDeclaratorPrefix() { result = "" }
|
||||
|
||||
/**
|
||||
* Gets the portion of this type's declarator that comes after the declarator for any derived type, but before any
|
||||
* qualifiers on the current type.
|
||||
*
|
||||
* This predicate is intended to be used only by the implementation of `getTypeIdentityString`.
|
||||
*/
|
||||
string getDeclaratorSuffixBeforeQualifiers() { result = "" }
|
||||
|
||||
/**
|
||||
* Gets the portion of this type's declarator that comes after the declarator for any derived type and after any
|
||||
* qualifiers on the current type.
|
||||
*
|
||||
* This predicate is intended to be used only by the implementation of `getTypeIdentityString`.
|
||||
*/
|
||||
string getDeclaratorSuffix() { result = "" }
|
||||
}
|
||||
|
||||
private class BuiltInDumpType extends DumpType, BuiltInType {
|
||||
override string getTypeSpecifier() { result = this.toString() }
|
||||
}
|
||||
|
||||
private class IntegralDumpType extends BuiltInDumpType, IntegralType {
|
||||
override string getTypeSpecifier() { result = this.getCanonicalArithmeticType().toString() }
|
||||
}
|
||||
|
||||
private class DerivedDumpType extends DumpType, DerivedType {
|
||||
override string getTypeSpecifier() { result = this.getBaseType().(DumpType).getTypeSpecifier() }
|
||||
|
||||
override string getDeclaratorSuffixBeforeQualifiers() {
|
||||
result = this.getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
|
||||
}
|
||||
|
||||
override string getDeclaratorSuffix() {
|
||||
result = this.getBaseType().(DumpType).getDeclaratorSuffix()
|
||||
}
|
||||
}
|
||||
|
||||
private class DecltypeDumpType extends DumpType, Decltype {
|
||||
override string getTypeSpecifier() { result = this.getBaseType().(DumpType).getTypeSpecifier() }
|
||||
|
||||
override string getDeclaratorPrefix() {
|
||||
result = this.getBaseType().(DumpType).getDeclaratorPrefix()
|
||||
}
|
||||
|
||||
override string getDeclaratorSuffix() {
|
||||
result = this.getBaseType().(DumpType).getDeclaratorSuffix()
|
||||
}
|
||||
}
|
||||
|
||||
private class PointerIshDumpType extends DerivedDumpType {
|
||||
PointerIshDumpType() {
|
||||
this instanceof PointerType or
|
||||
this instanceof ReferenceType
|
||||
}
|
||||
|
||||
override string getDeclaratorPrefix() {
|
||||
exists(string declarator |
|
||||
result = this.getBaseType().(DumpType).getDeclaratorPrefix() + declarator and
|
||||
if this.getBaseType().getUnspecifiedType() instanceof ArrayType
|
||||
then declarator = "(" + this.getDeclaratorToken() + ")"
|
||||
else declarator = this.getDeclaratorToken()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the token used when declaring this kind of type (e.g. `*`, `&`, `&&`)/
|
||||
*/
|
||||
string getDeclaratorToken() { result = "" }
|
||||
}
|
||||
|
||||
private class PointerDumpType extends PointerIshDumpType, PointerType {
|
||||
override string getDeclaratorToken() { result = "*" }
|
||||
}
|
||||
|
||||
private class LValueReferenceDumpType extends PointerIshDumpType, LValueReferenceType {
|
||||
override string getDeclaratorToken() { result = "&" }
|
||||
}
|
||||
|
||||
private class RValueReferenceDumpType extends PointerIshDumpType, RValueReferenceType {
|
||||
override string getDeclaratorToken() { result = "&&" }
|
||||
}
|
||||
|
||||
private class PointerToMemberDumpType extends DumpType, PointerToMemberType {
|
||||
override string getTypeSpecifier() { result = this.getBaseType().(DumpType).getTypeSpecifier() }
|
||||
|
||||
override string getDeclaratorPrefix() {
|
||||
exists(string declarator, string parenDeclarator, Type baseType |
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
override string getDeclaratorSuffixBeforeQualifiers() {
|
||||
exists(Type baseType |
|
||||
baseType = this.getBaseType().getUnspecifiedType() and
|
||||
if baseType instanceof ArrayType or baseType instanceof RoutineType
|
||||
then result = ")" + this.getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
|
||||
else result = this.getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
|
||||
)
|
||||
}
|
||||
|
||||
override string getDeclaratorSuffix() {
|
||||
result = this.getBaseType().(DumpType).getDeclaratorSuffix()
|
||||
}
|
||||
}
|
||||
|
||||
private class ArrayDumpType extends DerivedDumpType, ArrayType {
|
||||
override string getDeclaratorPrefix() {
|
||||
result = this.getBaseType().(DumpType).getDeclaratorPrefix()
|
||||
}
|
||||
|
||||
override string getDeclaratorSuffixBeforeQualifiers() {
|
||||
if exists(this.getArraySize())
|
||||
then
|
||||
result =
|
||||
"[" + this.getArraySize().toString() + "]" +
|
||||
this.getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
|
||||
else result = "[]" + this.getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
|
||||
}
|
||||
}
|
||||
|
||||
private class FunctionPointerIshDumpType extends DerivedDumpType, FunctionPointerIshType {
|
||||
override string getDeclaratorSuffixBeforeQualifiers() {
|
||||
result = ")" + this.getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
|
||||
}
|
||||
|
||||
override string getDeclaratorSuffix() {
|
||||
result = this.getBaseType().(DumpType).getDeclaratorSuffix()
|
||||
}
|
||||
|
||||
override string getDeclaratorPrefix() {
|
||||
result = this.getBaseType().(DumpType).getDeclaratorPrefix() + "(" + this.getDeclaratorToken()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the token used when declaring this kind of type (e.g. `*`, `&`, `^`)/
|
||||
*/
|
||||
string getDeclaratorToken() { result = "" }
|
||||
}
|
||||
|
||||
private class FunctionPointerDumpType extends FunctionPointerIshDumpType, FunctionPointerType {
|
||||
override string getDeclaratorToken() { result = "*" }
|
||||
}
|
||||
|
||||
private class FunctionReferenceDumpType extends FunctionPointerIshDumpType, FunctionReferenceType {
|
||||
override string getDeclaratorToken() { result = "&" }
|
||||
}
|
||||
|
||||
private class BlockDumpType extends FunctionPointerIshDumpType, BlockType {
|
||||
override string getDeclaratorToken() { result = "^" }
|
||||
}
|
||||
|
||||
private class RoutineDumpType extends DumpType, RoutineType {
|
||||
override string getTypeSpecifier() { result = this.getReturnType().(DumpType).getTypeSpecifier() }
|
||||
|
||||
override string getDeclaratorPrefix() {
|
||||
result = this.getReturnType().(DumpType).getDeclaratorPrefix()
|
||||
}
|
||||
|
||||
language[monotonicAggregates]
|
||||
override string getDeclaratorSuffixBeforeQualifiers() {
|
||||
result =
|
||||
"(" +
|
||||
concat(int i |
|
||||
exists(this.getParameterType(i))
|
||||
|
|
||||
getParameterTypeString(this.getParameterType(i)), ", " order by i
|
||||
) + ")"
|
||||
}
|
||||
|
||||
override string getDeclaratorSuffix() {
|
||||
result =
|
||||
this.getReturnType().(DumpType).getDeclaratorSuffixBeforeQualifiers() +
|
||||
this.getReturnType().(DumpType).getDeclaratorSuffix()
|
||||
}
|
||||
}
|
||||
|
||||
private class SpecifiedDumpType extends DerivedDumpType, SpecifiedType {
|
||||
override string getDeclaratorPrefix() {
|
||||
exists(string basePrefix |
|
||||
basePrefix = this.getBaseType().(DumpType).getDeclaratorPrefix() and
|
||||
if this.getBaseType().getUnspecifiedType() instanceof RoutineType
|
||||
then result = basePrefix
|
||||
else result = basePrefix + " " + this.getSpecifierString()
|
||||
)
|
||||
}
|
||||
|
||||
override string getDeclaratorSuffixBeforeQualifiers() {
|
||||
exists(string baseSuffix |
|
||||
baseSuffix = this.getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() and
|
||||
if this.getBaseType().getUnspecifiedType() instanceof RoutineType
|
||||
then result = baseSuffix + " " + this.getSpecifierString()
|
||||
else result = baseSuffix
|
||||
)
|
||||
}
|
||||
|
||||
override string getDeclaratorSuffix() {
|
||||
result = this.getBaseType().(DumpType).getDeclaratorSuffix()
|
||||
}
|
||||
}
|
||||
|
||||
private class UserDumpType extends DumpType, DumpDeclaration, UserType {
|
||||
override string getIdentityString() {
|
||||
exists(string simpleName |
|
||||
(
|
||||
if this instanceof Closure
|
||||
then
|
||||
// Parenthesize the name of the lambda because it's freeform text similar to
|
||||
// "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(this.getSimpleName()) + ")"
|
||||
else simpleName = this.getSimpleName()
|
||||
) and
|
||||
result = getScopePrefix(this) + simpleName + this.getTemplateArgumentsString()
|
||||
)
|
||||
}
|
||||
|
||||
override string getTypeSpecifier() { result = this.getIdentityString() }
|
||||
}
|
||||
|
||||
private class DumpProxyClass extends UserDumpType, ProxyClass {
|
||||
override string getIdentityString() { result = this.getName() }
|
||||
}
|
||||
|
||||
private class DumpVariable extends DumpDeclaration, Variable {
|
||||
override string getIdentityString() {
|
||||
exists(DumpType type |
|
||||
(this instanceof MemberVariable or this instanceof GlobalOrNamespaceVariable) and
|
||||
type = this.getType() and
|
||||
result =
|
||||
type.getTypeSpecifier() + type.getDeclaratorPrefix() + " " + getScopePrefix(this) +
|
||||
this.getName() + this.getTemplateArgumentsString() +
|
||||
type.getDeclaratorSuffixBeforeQualifiers() + type.getDeclaratorSuffix()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class DumpFunction extends DumpDeclaration, Function {
|
||||
override string getIdentityString() {
|
||||
result =
|
||||
this.getType().(DumpType).getTypeSpecifier() + this.getType().(DumpType).getDeclaratorPrefix()
|
||||
+ " " + getScopePrefix(this) + this.getName() + this.getTemplateArgumentsString() +
|
||||
this.getDeclaratorSuffixBeforeQualifiers() + this.getDeclaratorSuffix()
|
||||
}
|
||||
|
||||
language[monotonicAggregates]
|
||||
private string getDeclaratorSuffixBeforeQualifiers() {
|
||||
result =
|
||||
"(" +
|
||||
concat(int i |
|
||||
exists(this.getParameter(i).getType())
|
||||
|
|
||||
getParameterTypeString(this.getParameter(i).getType()), ", " order by i
|
||||
) + ")" + this.getQualifierString()
|
||||
}
|
||||
|
||||
private string getQualifierString() {
|
||||
if exists(this.getACVQualifier())
|
||||
then
|
||||
result =
|
||||
" " + strictconcat(string qualifier | qualifier = this.getACVQualifier() | qualifier, " ")
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string getACVQualifier() {
|
||||
result = this.getASpecifier().getName() and
|
||||
result = ["const", "volatile"]
|
||||
}
|
||||
|
||||
private string getDeclaratorSuffix() {
|
||||
result =
|
||||
this.getType().(DumpType).getDeclaratorSuffixBeforeQualifiers() +
|
||||
this.getType().(DumpType).getDeclaratorSuffix()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string that uniquely identifies this declaration, suitable for use when debugging queries. Only holds for
|
||||
* functions, user-defined types, global and namespace-scope variables, and member variables.
|
||||
*
|
||||
* This operation is very expensive, and should not be used in production queries. Consider using `hasName()` or
|
||||
* `hasQualifiedName()` for identifying known declarations in production queries.
|
||||
*/
|
||||
string getIdentityString(Declaration decl) { result = decl.(DumpDeclaration).getIdentityString() }
|
||||
|
||||
/**
|
||||
* Gets a string that uniquely identifies this type, suitable for use when debugging queries. All typedefs and
|
||||
* decltypes are expanded, and all symbol names are fully qualified.
|
||||
*
|
||||
* This operation is very expensive, and should not be used in production queries.
|
||||
*/
|
||||
string getTypeIdentityString(Type type) { result = type.(DumpType).getTypeIdentityString() }
|
||||
@@ -46,7 +46,7 @@ private string escapeString(string s) {
|
||||
* string representation comes first in lexicographical order.
|
||||
*/
|
||||
private Location getRepresentativeLocation(Locatable ast) {
|
||||
result = rank[1](Location loc | loc = ast.getLocation() | loc order by loc.toString())
|
||||
result = min(Location loc | loc = ast.getLocation() | loc order by loc.toString())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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
|
||||
)
|
||||
@@ -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" }
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
1751
cpp/ql/lib/semmle/code/cpp/Type.qll
Normal file
1751
cpp/ql/lib/semmle/code/cpp/Type.qll
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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" }
|
||||
}
|
||||
@@ -37,7 +37,7 @@ class Union extends Struct {
|
||||
* ```
|
||||
*/
|
||||
class LocalUnion extends Union {
|
||||
LocalUnion() { isLocal() }
|
||||
LocalUnion() { this.isLocal() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LocalUnion" }
|
||||
}
|
||||
@@ -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<int>"`, 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" }
|
||||
|
||||
613
cpp/ql/lib/semmle/code/cpp/Variable.qll
Normal file
613
cpp/ql/lib/semmle/code/cpp/Variable.qll
Normal file
@@ -0,0 +1,613 @@
|
||||
/**
|
||||
* Provides classes for modeling variables and their declarations.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Element
|
||||
import semmle.code.cpp.exprs.Access
|
||||
import semmle.code.cpp.Initializer
|
||||
private import semmle.code.cpp.internal.ResolveClass
|
||||
|
||||
/**
|
||||
* A C/C++ variable. For example, in the following code there are four
|
||||
* variables, `a`, `b`, `c` and `d`:
|
||||
* ```
|
||||
* extern int a;
|
||||
* int a;
|
||||
*
|
||||
* void myFunction(int b) {
|
||||
* int c;
|
||||
* }
|
||||
*
|
||||
* namespace N {
|
||||
* extern int d;
|
||||
* int d = 1;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* For local variables, there is a one-to-one correspondence between
|
||||
* `Variable` and `VariableDeclarationEntry`.
|
||||
*
|
||||
* For other types of variable, there is a one-to-many relationship between
|
||||
* `Variable` and `VariableDeclarationEntry`. For example, a `Parameter`
|
||||
* can have multiple declarations.
|
||||
*/
|
||||
class Variable extends Declaration, @variable {
|
||||
override string getAPrimaryQlClass() { result = "Variable" }
|
||||
|
||||
/** Gets the initializer of this variable, if any. */
|
||||
Initializer getInitializer() { result.getDeclaration() = this }
|
||||
|
||||
/** Holds if this variable has an initializer. */
|
||||
predicate hasInitializer() { exists(this.getInitializer()) }
|
||||
|
||||
/** Gets an access to this variable. */
|
||||
VariableAccess getAnAccess() { result.getTarget() = this }
|
||||
|
||||
/**
|
||||
* Gets a specifier of this variable. This includes `extern`, `static`,
|
||||
* `auto`, `private`, `protected`, `public`. Specifiers of the *type* of
|
||||
* this variable, such as `const` and `volatile`, are instead accessed
|
||||
* through `this.getType().getASpecifier()`.
|
||||
*/
|
||||
override Specifier getASpecifier() {
|
||||
varspecifiers(underlyingElement(this), unresolveElement(result))
|
||||
}
|
||||
|
||||
/** Gets an attribute of this variable. */
|
||||
Attribute getAnAttribute() { varattributes(underlyingElement(this), unresolveElement(result)) }
|
||||
|
||||
/** Holds if this variable is `const`. */
|
||||
predicate isConst() { this.getType().isConst() }
|
||||
|
||||
/** Holds if this variable is `volatile`. */
|
||||
predicate isVolatile() { this.getType().isVolatile() }
|
||||
|
||||
/** Gets the name of this variable. */
|
||||
override string getName() { none() }
|
||||
|
||||
/** Gets the type of this variable. */
|
||||
Type getType() { none() }
|
||||
|
||||
/** Gets the type of this variable, after typedefs have been resolved. */
|
||||
Type getUnderlyingType() { result = this.getType().getUnderlyingType() }
|
||||
|
||||
/**
|
||||
* Gets the type of this variable, after specifiers have been deeply
|
||||
* stripped and typedefs have been resolved.
|
||||
*/
|
||||
Type getUnspecifiedType() { result = this.getType().getUnspecifiedType() }
|
||||
|
||||
/**
|
||||
* Gets the type of this variable prior to deduction caused by the C++11
|
||||
* `auto` keyword.
|
||||
*
|
||||
* If the type of this variable was not declared with the C++11 `auto`
|
||||
* keyword, then this predicate does not hold.
|
||||
*
|
||||
* If the type of this variable is completely `auto`, then `result` is an
|
||||
* instance of `AutoType`. For example:
|
||||
*
|
||||
* `auto four = 4;`
|
||||
*
|
||||
* If the type of this variable is partially `auto`, then a descendant of
|
||||
* `result` is an instance of `AutoType`. For example:
|
||||
*
|
||||
* `const auto& c = container;`
|
||||
*/
|
||||
Type getTypeWithAuto() { autoderivation(underlyingElement(this), unresolveElement(result)) }
|
||||
|
||||
/**
|
||||
* Holds if the type of this variable is declared using the C++ `auto`
|
||||
* keyword.
|
||||
*/
|
||||
predicate declaredUsingAutoType() { autoderivation(underlyingElement(this), _) }
|
||||
|
||||
override VariableDeclarationEntry getADeclarationEntry() { result.getDeclaration() = this }
|
||||
|
||||
override Location getADeclarationLocation() { result = this.getADeclarationEntry().getLocation() }
|
||||
|
||||
override VariableDeclarationEntry getDefinition() {
|
||||
result = this.getADeclarationEntry() and
|
||||
result.isDefinition()
|
||||
}
|
||||
|
||||
override Location getDefinitionLocation() { result = this.getDefinition().getLocation() }
|
||||
|
||||
override Location getLocation() {
|
||||
if exists(this.getDefinition())
|
||||
then result = this.getDefinitionLocation()
|
||||
else result = this.getADeclarationLocation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an expression that is assigned to this variable somewhere in the
|
||||
* program.
|
||||
*/
|
||||
Expr getAnAssignedValue() {
|
||||
result = this.getInitializer().getExpr()
|
||||
or
|
||||
exists(ConstructorFieldInit cfi | cfi.getTarget() = this and result = cfi.getExpr())
|
||||
or
|
||||
exists(AssignExpr ae | ae.getLValue().(Access).getTarget() = this and result = ae.getRValue())
|
||||
or
|
||||
exists(ClassAggregateLiteral l | result = l.getFieldExpr(this))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an assignment expression that assigns to this variable.
|
||||
* For example: `x=...` or `x+=...`.
|
||||
*
|
||||
* This does _not_ include the initialization of the variable. Use
|
||||
* `Variable.getInitializer()` to get the variable's initializer,
|
||||
* or use `Variable.getAnAssignedValue()` to get an expression that
|
||||
* is the right-hand side of an assignment or an initialization of
|
||||
* the varible.
|
||||
*/
|
||||
Assignment getAnAssignment() { result.getLValue() = this.getAnAccess() }
|
||||
|
||||
/**
|
||||
* Holds if this variable is `constexpr`.
|
||||
*/
|
||||
predicate isConstexpr() { this.hasSpecifier("is_constexpr") }
|
||||
|
||||
/**
|
||||
* Holds if this variable is declared `constinit`.
|
||||
*/
|
||||
predicate isConstinit() { this.hasSpecifier("declared_constinit") }
|
||||
|
||||
/**
|
||||
* Holds if this variable is `thread_local`.
|
||||
*/
|
||||
predicate isThreadLocal() { this.hasSpecifier("is_thread_local") }
|
||||
|
||||
/**
|
||||
* Holds if this variable is constructed from `v` as a result
|
||||
* of template instantiation. If so, it originates either from a template
|
||||
* variable or from a variable nested in a template class.
|
||||
*/
|
||||
predicate isConstructedFrom(Variable v) {
|
||||
variable_instantiation(underlyingElement(this), unresolveElement(v))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this is a compiler-generated variable. For example, a
|
||||
* [range-based for loop](http://en.cppreference.com/w/cpp/language/range-for)
|
||||
* typically has three compiler-generated variables, named `__range`,
|
||||
* `__begin`, and `__end`:
|
||||
*
|
||||
* `for (char c : str) { ... }`
|
||||
*/
|
||||
predicate isCompilerGenerated() { compgenerated(underlyingElement(this)) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A particular declaration or definition of a C/C++ variable. For example, in
|
||||
* the following code there are six variable declaration entries - two each for
|
||||
* `a` and `d`, and one each for `b` and `c`:
|
||||
* ```
|
||||
* extern int a;
|
||||
* int a;
|
||||
*
|
||||
* void myFunction(int b) {
|
||||
* int c;
|
||||
* }
|
||||
*
|
||||
* namespace N {
|
||||
* extern int d;
|
||||
* int d = 1;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class VariableDeclarationEntry extends DeclarationEntry, @var_decl {
|
||||
override Variable getDeclaration() { result = this.getVariable() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "VariableDeclarationEntry" }
|
||||
|
||||
/**
|
||||
* Gets the variable which is being declared or defined.
|
||||
*/
|
||||
Variable getVariable() { var_decls(underlyingElement(this), unresolveElement(result), _, _, _) }
|
||||
|
||||
/**
|
||||
* Gets the name, if any, used for the variable at this declaration or
|
||||
* definition.
|
||||
*
|
||||
* In most cases, this will be the name of the variable itself. The only
|
||||
* case in which it can differ is in a parameter declaration entry,
|
||||
* because the parameter may have a different name in the declaration
|
||||
* than in the definition. For example:
|
||||
*
|
||||
* ```
|
||||
* // Declaration. Parameter is named "x".
|
||||
* int f(int x);
|
||||
*
|
||||
* // Definition. Parameter is named "y".
|
||||
* int f(int y) { return y; }
|
||||
* ```
|
||||
*/
|
||||
override string getName() { var_decls(underlyingElement(this), _, _, result, _) and result != "" }
|
||||
|
||||
/**
|
||||
* Gets the type of the variable which is being declared or defined.
|
||||
*/
|
||||
override Type getType() { var_decls(underlyingElement(this), _, unresolveElement(result), _, _) }
|
||||
|
||||
override Location getLocation() { var_decls(underlyingElement(this), _, _, _, result) }
|
||||
|
||||
/**
|
||||
* Holds if this is a definition of a variable.
|
||||
*
|
||||
* This always holds for local variables and member variables, but need
|
||||
* not hold for global variables. In the case of function parameters,
|
||||
* this holds precisely when the enclosing `FunctionDeclarationEntry` is
|
||||
* a definition.
|
||||
*/
|
||||
override predicate isDefinition() { var_def(underlyingElement(this)) }
|
||||
|
||||
override string getASpecifier() { var_decl_specifiers(underlyingElement(this), result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter as described within a particular declaration or definition
|
||||
* of a C/C++ function. For example the declaration of `a` in the following
|
||||
* code:
|
||||
* ```
|
||||
* void myFunction(int a) {
|
||||
* int b;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class ParameterDeclarationEntry extends VariableDeclarationEntry {
|
||||
ParameterDeclarationEntry() { param_decl_bind(underlyingElement(this), _, _) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ParameterDeclarationEntry" }
|
||||
|
||||
/**
|
||||
* Gets the function declaration or definition which this parameter
|
||||
* description is part of.
|
||||
*/
|
||||
FunctionDeclarationEntry getFunctionDeclarationEntry() {
|
||||
param_decl_bind(underlyingElement(this), _, unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the zero-based index of this parameter.
|
||||
*/
|
||||
int getIndex() { param_decl_bind(underlyingElement(this), result, _) }
|
||||
|
||||
private string getAnonymousParameterDescription() {
|
||||
not exists(this.getName()) and
|
||||
exists(string idx |
|
||||
idx =
|
||||
((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(this.getCanonicalName())
|
||||
then
|
||||
result = "declaration of " + this.getCanonicalName() + " as anonymous " + idx + " parameter"
|
||||
else result = "declaration of " + idx + " parameter"
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
this.isDefinition() and
|
||||
result = "definition of " + this.getName()
|
||||
or
|
||||
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 = this.getAnonymousParameterDescription()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of this `ParameterDeclarationEntry` including it's type.
|
||||
*
|
||||
* For example: "int p".
|
||||
*/
|
||||
string getTypedName() {
|
||||
exists(string typeString, string nameString |
|
||||
(
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ variable with block scope [N4140 3.3.3]. In other words, a local
|
||||
* variable or a function parameter. For example, the variables `a`, `b` and
|
||||
* `c` in the following code:
|
||||
* ```
|
||||
* void myFunction(int a) {
|
||||
* int b;
|
||||
* static int c;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* See also `StackVariable`, which is the class of local-scope variables
|
||||
* without statics and thread-locals.
|
||||
*/
|
||||
class LocalScopeVariable extends Variable, @localscopevariable {
|
||||
/** Gets the function to which this variable belongs. */
|
||||
Function getFunction() { none() } // overridden in subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ variable with _automatic storage duration_. In other words, a
|
||||
* function parameter or a local variable that is not static or thread-local.
|
||||
* For example, the variables `a` and `b` in the following code.
|
||||
* ```
|
||||
* void myFunction(int a) {
|
||||
* int b;
|
||||
* static int c;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class StackVariable extends LocalScopeVariable {
|
||||
StackVariable() {
|
||||
not this.isStatic() and
|
||||
not this.isThreadLocal()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ local variable. In other words, any variable that has block
|
||||
* scope [N4140 3.3.3], but is not a parameter of a `Function` or `CatchBlock`.
|
||||
* For example the variables `b` and `c` in the following code:
|
||||
* ```
|
||||
* void myFunction(int a) {
|
||||
* int b;
|
||||
* static int c;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Local variables can be static; use the `isStatic` member predicate to detect
|
||||
* those.
|
||||
*
|
||||
* A local variable can be declared by a `DeclStmt` or a `ConditionDeclExpr`.
|
||||
*/
|
||||
class LocalVariable extends LocalScopeVariable, @localvariable {
|
||||
override string getAPrimaryQlClass() { result = "LocalVariable" }
|
||||
|
||||
override string getName() { localvariables(underlyingElement(this), _, result) }
|
||||
|
||||
override Type getType() { localvariables(underlyingElement(this), unresolveElement(result), _) }
|
||||
|
||||
override Function getFunction() {
|
||||
exists(DeclStmt s | s.getADeclaration() = this and s.getEnclosingFunction() = result)
|
||||
or
|
||||
exists(ConditionDeclExpr e | e.getVariable() = this and e.getEnclosingFunction() = result)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable whose contents always have static storage duration. This can be a
|
||||
* global variable, a namespace variable, a static local variable, or a static
|
||||
* member variable.
|
||||
*/
|
||||
class StaticStorageDurationVariable extends Variable {
|
||||
StaticStorageDurationVariable() {
|
||||
this instanceof GlobalOrNamespaceVariable
|
||||
or
|
||||
this.(LocalVariable).isStatic()
|
||||
or
|
||||
this.(MemberVariable).isStatic()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the initializer for this variable is evaluated at runtime.
|
||||
*/
|
||||
predicate hasDynamicInitialization() {
|
||||
runtimeExprInStaticInitializer(this.getInitializer().getExpr())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is an expression in a static initializer that must be evaluated
|
||||
* at run time. This predicate computes "is non-const" instead of "is const"
|
||||
* since computing "is const" for an aggregate literal with many children would
|
||||
* either involve recursion through `forall` on those children or an iteration
|
||||
* through the rank numbers of the children, both of which can be slow.
|
||||
*/
|
||||
private predicate runtimeExprInStaticInitializer(Expr e) {
|
||||
inStaticInitializer(e) and
|
||||
if e instanceof AggregateLiteral // in sync with the cast in `inStaticInitializer`
|
||||
then runtimeExprInStaticInitializer(e.getAChild())
|
||||
else not e.getFullyConverted().isConstant()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is the initializer of a `StaticStorageDurationVariable`, either
|
||||
* directly or below some top-level `AggregateLiteral`s.
|
||||
*/
|
||||
private predicate inStaticInitializer(Expr e) {
|
||||
exists(StaticStorageDurationVariable var | e = var.getInitializer().getExpr())
|
||||
or
|
||||
// The cast to `AggregateLiteral` ensures we only compute what'll later be
|
||||
// needed by `runtimeExprInStaticInitializer`.
|
||||
inStaticInitializer(e.getParent().(AggregateLiteral))
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ local variable declared as `static`.
|
||||
*/
|
||||
class StaticLocalVariable extends LocalVariable, StaticStorageDurationVariable { }
|
||||
|
||||
/**
|
||||
* A C/C++ variable which has global scope or namespace scope. For example the
|
||||
* variables `a` and `b` in the following code:
|
||||
* ```
|
||||
* int a;
|
||||
*
|
||||
* namespace N {
|
||||
* int b;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class GlobalOrNamespaceVariable extends Variable, @globalvariable {
|
||||
override string getName() { globalvariables(underlyingElement(this), _, result) }
|
||||
|
||||
override Type getType() { globalvariables(underlyingElement(this), unresolveElement(result), _) }
|
||||
|
||||
override Element getEnclosingElement() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ variable which has namespace scope. For example the variable `b`
|
||||
* in the following code:
|
||||
* ```
|
||||
* int a;
|
||||
*
|
||||
* namespace N {
|
||||
* int b;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class NamespaceVariable extends GlobalOrNamespaceVariable {
|
||||
NamespaceVariable() {
|
||||
exists(Namespace n | namespacembrs(unresolveElement(n), underlyingElement(this)))
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "NamespaceVariable" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ variable which has global scope. For example the variable `a`
|
||||
* in the following code:
|
||||
* ```
|
||||
* int a;
|
||||
*
|
||||
* namespace N {
|
||||
* int b;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Note that variables declared in anonymous namespaces have namespace scope,
|
||||
* even though they are accessed in the same manner as variables declared in
|
||||
* the enclosing scope of said namespace (which may be the global scope).
|
||||
*/
|
||||
class GlobalVariable extends GlobalOrNamespaceVariable {
|
||||
GlobalVariable() { not this instanceof NamespaceVariable }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "GlobalVariable" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C structure member or C++ member variable. For example the member
|
||||
* variables `m` and `s` in the following code:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* int m;
|
||||
* static int s;
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* This includes static member variables in C++. To exclude static member
|
||||
* variables, use `Field` instead of `MemberVariable`.
|
||||
*/
|
||||
class MemberVariable extends Variable, @membervariable {
|
||||
MemberVariable() { this.isMember() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "MemberVariable" }
|
||||
|
||||
/** Holds if this member is private. */
|
||||
predicate isPrivate() { this.hasSpecifier("private") }
|
||||
|
||||
/** Holds if this member is protected. */
|
||||
predicate isProtected() { this.hasSpecifier("protected") }
|
||||
|
||||
/** Holds if this member is public. */
|
||||
predicate isPublic() { this.hasSpecifier("public") }
|
||||
|
||||
override string getName() { membervariables(underlyingElement(this), _, result) }
|
||||
|
||||
override Type getType() {
|
||||
if strictcount(this.getAType()) = 1
|
||||
then result = this.getAType()
|
||||
else
|
||||
// In rare situations a member variable may have multiple types in
|
||||
// different translation units. In that case, we return the unspecified
|
||||
// type.
|
||||
result = this.getAType().getUnspecifiedType()
|
||||
}
|
||||
|
||||
/** Holds if this member is mutable. */
|
||||
predicate isMutable() { this.getADeclarationEntry().hasSpecifier("mutable") }
|
||||
|
||||
private Type getAType() { membervariables(underlyingElement(this), unresolveElement(result), _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ function pointer variable.
|
||||
*
|
||||
* DEPRECATED: use `Variable.getType() instanceof FunctionPointerType` instead.
|
||||
*/
|
||||
deprecated class FunctionPointerVariable extends Variable {
|
||||
FunctionPointerVariable() { this.getType() instanceof FunctionPointerType }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ function pointer member variable.
|
||||
*
|
||||
* DEPRECATED: use `MemberVariable.getType() instanceof FunctionPointerType` instead.
|
||||
*/
|
||||
deprecated class FunctionPointerMemberVariable extends MemberVariable {
|
||||
FunctionPointerMemberVariable() { this instanceof FunctionPointerVariable }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++14 variable template. For example, in the following code the variable
|
||||
* template `v` defines a family of variables:
|
||||
* ```
|
||||
* template<class T>
|
||||
* T v;
|
||||
* ```
|
||||
*/
|
||||
class TemplateVariable extends Variable {
|
||||
TemplateVariable() { is_variable_template(underlyingElement(this)) }
|
||||
|
||||
/**
|
||||
* Gets an instantiation of this variable template.
|
||||
*/
|
||||
Variable getAnInstantiation() { result.isConstructedFrom(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-static local variable or parameter that is not part of an
|
||||
* uninstantiated template. Uninstantiated templates are purely syntax, and
|
||||
* only on instantiation will they be complete with information about types,
|
||||
* conversions, call targets, etc. For example in the following code, the
|
||||
* variables `a` in `myFunction` and `b` in the instantiation
|
||||
* `myTemplateFunction<int>`, but not `b` in the template
|
||||
* `myTemplateFunction<T>`:
|
||||
* ```
|
||||
* void myFunction() {
|
||||
* float a;
|
||||
* }
|
||||
*
|
||||
* template<typename T>
|
||||
* void myTemplateFunction() {
|
||||
* T b;
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* myTemplateFunction<int>();
|
||||
* ```
|
||||
*/
|
||||
class SemanticStackVariable extends StackVariable {
|
||||
SemanticStackVariable() { not this.isFromUninstantiatedTemplate(_) }
|
||||
}
|
||||
343
cpp/ql/lib/semmle/code/cpp/XML.qll
Executable file
343
cpp/ql/lib/semmle/code/cpp/XML.qll
Executable file
@@ -0,0 +1,343 @@
|
||||
/**
|
||||
* Provides classes and predicates for working with XML files and their content.
|
||||
*/
|
||||
|
||||
import semmle.files.FileSystem
|
||||
|
||||
private class TXMLLocatable =
|
||||
@xmldtd or @xmlelement or @xmlattribute or @xmlnamespace or @xmlcomment or @xmlcharacters;
|
||||
|
||||
/** An XML element that has a location. */
|
||||
class XMLLocatable extends @xmllocatable, TXMLLocatable {
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { xmllocations(this, result) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getLocation()` instead.
|
||||
*
|
||||
* Gets the source location for this element.
|
||||
*/
|
||||
deprecated Location getALocation() { result = this.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/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
exists(File f, Location l | l = this.getLocation() |
|
||||
locations_default(l, f, startline, startcolumn, endline, endcolumn) and
|
||||
filepath = f.getAbsolutePath()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() } // overridden in subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
* An `XMLParent` is either an `XMLElement` or an `XMLFile`,
|
||||
* both of which can contain other elements.
|
||||
*/
|
||||
class XMLParent extends @xmlparent {
|
||||
XMLParent() {
|
||||
// explicitly restrict `this` to be either an `XMLElement` or an `XMLFile`;
|
||||
// the type `@xmlparent` currently also includes non-XML files
|
||||
this instanceof @xmlelement or xmlEncoding(this, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a printable representation of this XML parent.
|
||||
* (Intended to be overridden in subclasses.)
|
||||
*/
|
||||
string getName() { none() } // overridden in subclasses
|
||||
|
||||
/** Gets the file to which this XML parent belongs. */
|
||||
XMLFile getFile() { result = this or xmlElements(this, _, _, _, result) }
|
||||
|
||||
/** Gets the child element at a specified index of this XML parent. */
|
||||
XMLElement getChild(int index) { xmlElements(result, _, this, index, _) }
|
||||
|
||||
/** Gets a child element of this XML parent. */
|
||||
XMLElement getAChild() { xmlElements(result, _, this, _, _) }
|
||||
|
||||
/** Gets a child element of this XML parent with the given `name`. */
|
||||
XMLElement getAChild(string name) { xmlElements(result, _, this, _, _) and result.hasName(name) }
|
||||
|
||||
/** Gets a comment that is a child of this XML parent. */
|
||||
XMLComment getAComment() { xmlComments(result, _, this, _) }
|
||||
|
||||
/** Gets a character sequence that is a child of this XML parent. */
|
||||
XMLCharacters getACharactersSet() { xmlChars(result, _, this, _, _, _) }
|
||||
|
||||
/** Gets the depth in the tree. (Overridden in XMLElement.) */
|
||||
int getDepth() { result = 0 }
|
||||
|
||||
/** Gets the number of child XML elements of this XML parent. */
|
||||
int getNumberOfChildren() { result = count(XMLElement e | xmlElements(e, _, this, _, _)) }
|
||||
|
||||
/** Gets the number of places in the body of this XML parent where text occurs. */
|
||||
int getNumberOfCharacterSets() { result = count(int pos | xmlChars(_, _, this, pos, _, _)) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Internal.
|
||||
*
|
||||
* Append the character sequences of this XML parent from left to right, separated by a space,
|
||||
* up to a specified (zero-based) index.
|
||||
*/
|
||||
deprecated string charsSetUpTo(int n) {
|
||||
n = 0 and xmlChars(_, result, this, 0, _, _)
|
||||
or
|
||||
n > 0 and
|
||||
exists(string chars | xmlChars(_, chars, this, n, _, _) |
|
||||
result = this.charsSetUpTo(n - 1) + " " + chars
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the result of appending all the character sequences of this XML parent from
|
||||
* left to right, separated by a space.
|
||||
*/
|
||||
string allCharactersString() {
|
||||
result =
|
||||
concat(string chars, int pos | xmlChars(_, chars, this, pos, _, _) | chars, " " order by pos)
|
||||
}
|
||||
|
||||
/** Gets the text value contained in this XML parent. */
|
||||
string getTextValue() { result = this.allCharactersString() }
|
||||
|
||||
/** Gets a printable representation of this XML parent. */
|
||||
string toString() { result = this.getName() }
|
||||
}
|
||||
|
||||
/** An XML file. */
|
||||
class XMLFile extends XMLParent, File {
|
||||
XMLFile() { xmlEncoding(this, _) }
|
||||
|
||||
/** Gets a printable representation of this XML file. */
|
||||
override string toString() { result = this.getName() }
|
||||
|
||||
/** Gets the name of this XML file. */
|
||||
override string getName() { result = File.super.getAbsolutePath() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getAbsolutePath()` instead.
|
||||
*
|
||||
* Gets the path of this XML file.
|
||||
*/
|
||||
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 = this.getParentContainer().getAbsolutePath() }
|
||||
|
||||
/** Gets the encoding of this XML file. */
|
||||
string getEncoding() { xmlEncoding(this, result) }
|
||||
|
||||
/** Gets the XML file itself. */
|
||||
override XMLFile getFile() { result = this }
|
||||
|
||||
/** Gets a top-most element in an XML file. */
|
||||
XMLElement getARootElement() { result = this.getAChild() }
|
||||
|
||||
/** Gets a DTD associated with this XML file. */
|
||||
XMLDTD getADTD() { xmlDTDs(result, _, _, _, this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An XML document type definition (DTD).
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* <!ELEMENT person (firstName, lastName?)>
|
||||
* <!ELEMENT firstName (#PCDATA)>
|
||||
* <!ELEMENT lastName (#PCDATA)>
|
||||
* ```
|
||||
*/
|
||||
class XMLDTD extends XMLLocatable, @xmldtd {
|
||||
/** Gets the name of the root element of this DTD. */
|
||||
string getRoot() { xmlDTDs(this, result, _, _, _) }
|
||||
|
||||
/** Gets the public ID of this DTD. */
|
||||
string getPublicId() { xmlDTDs(this, _, result, _, _) }
|
||||
|
||||
/** Gets the system ID of this DTD. */
|
||||
string getSystemId() { xmlDTDs(this, _, _, result, _) }
|
||||
|
||||
/** Holds if this DTD is public. */
|
||||
predicate isPublic() { not xmlDTDs(this, _, "", _, _) }
|
||||
|
||||
/** Gets the parent of this DTD. */
|
||||
XMLParent getParent() { xmlDTDs(this, _, _, _, result) }
|
||||
|
||||
override string toString() {
|
||||
this.isPublic() and
|
||||
result = this.getRoot() + " PUBLIC '" + this.getPublicId() + "' '" + this.getSystemId() + "'"
|
||||
or
|
||||
not this.isPublic() and
|
||||
result = this.getRoot() + " SYSTEM '" + this.getSystemId() + "'"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An XML element in an XML file.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* <manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
* package="com.example.exampleapp" android:versionCode="1">
|
||||
* </manifest>
|
||||
* ```
|
||||
*/
|
||||
class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
|
||||
/** Holds if this XML element has the given `name`. */
|
||||
predicate hasName(string name) { name = this.getName() }
|
||||
|
||||
/** Gets the name of this XML element. */
|
||||
override string getName() { xmlElements(this, result, _, _, _) }
|
||||
|
||||
/** Gets the XML file in which this XML element occurs. */
|
||||
override XMLFile getFile() { xmlElements(this, _, _, _, result) }
|
||||
|
||||
/** Gets the parent of this XML element. */
|
||||
XMLParent getParent() { xmlElements(this, _, result, _, _) }
|
||||
|
||||
/** Gets the index of this XML element among its parent's children. */
|
||||
int getIndex() { xmlElements(this, _, _, result, _) }
|
||||
|
||||
/** Holds if this XML element has a namespace. */
|
||||
predicate hasNamespace() { xmlHasNs(this, _, _) }
|
||||
|
||||
/** Gets the namespace of this XML element, if any. */
|
||||
XMLNamespace getNamespace() { xmlHasNs(this, result, _) }
|
||||
|
||||
/** Gets the index of this XML element among its parent's children. */
|
||||
int getElementPositionIndex() { xmlElements(this, _, _, result, _) }
|
||||
|
||||
/** Gets the depth of this element within the XML file tree structure. */
|
||||
override int getDepth() { result = this.getParent().getDepth() + 1 }
|
||||
|
||||
/** Gets an XML attribute of this XML element. */
|
||||
XMLAttribute getAnAttribute() { result.getElement() = this }
|
||||
|
||||
/** Gets the attribute with the specified `name`, if any. */
|
||||
XMLAttribute getAttribute(string name) { result.getElement() = this and result.getName() = name }
|
||||
|
||||
/** Holds if this XML element has an attribute with the specified `name`. */
|
||||
predicate hasAttribute(string name) { exists(XMLAttribute a | a = this.getAttribute(name)) }
|
||||
|
||||
/** Gets the value of the attribute with the specified `name`, if any. */
|
||||
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }
|
||||
|
||||
/** Gets a printable representation of this XML element. */
|
||||
override string toString() { result = this.getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An attribute that occurs inside an XML element.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* ```
|
||||
* package="com.example.exampleapp"
|
||||
* android:versionCode="1"
|
||||
* ```
|
||||
*/
|
||||
class XMLAttribute extends @xmlattribute, XMLLocatable {
|
||||
/** Gets the name of this attribute. */
|
||||
string getName() { xmlAttrs(this, _, result, _, _, _) }
|
||||
|
||||
/** Gets the XML element to which this attribute belongs. */
|
||||
XMLElement getElement() { xmlAttrs(this, result, _, _, _, _) }
|
||||
|
||||
/** Holds if this attribute has a namespace. */
|
||||
predicate hasNamespace() { xmlHasNs(this, _, _) }
|
||||
|
||||
/** Gets the namespace of this attribute, if any. */
|
||||
XMLNamespace getNamespace() { xmlHasNs(this, result, _) }
|
||||
|
||||
/** Gets the value of this attribute. */
|
||||
string getValue() { xmlAttrs(this, _, _, result, _, _) }
|
||||
|
||||
/** Gets a printable representation of this XML attribute. */
|
||||
override string toString() { result = this.getName() + "=" + this.getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A namespace used in an XML file.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
* ```
|
||||
*/
|
||||
class XMLNamespace extends XMLLocatable, @xmlnamespace {
|
||||
/** Gets the prefix of this namespace. */
|
||||
string getPrefix() { xmlNs(this, result, _, _) }
|
||||
|
||||
/** Gets the URI of this namespace. */
|
||||
string getURI() { xmlNs(this, _, result, _) }
|
||||
|
||||
/** Holds if this namespace has no prefix. */
|
||||
predicate isDefault() { this.getPrefix() = "" }
|
||||
|
||||
override string toString() {
|
||||
this.isDefault() and result = this.getURI()
|
||||
or
|
||||
not this.isDefault() and result = this.getPrefix() + ":" + this.getURI()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A comment in an XML file.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* <!-- This is a comment. -->
|
||||
* ```
|
||||
*/
|
||||
class XMLComment extends @xmlcomment, XMLLocatable {
|
||||
/** Gets the text content of this XML comment. */
|
||||
string getText() { xmlComments(this, result, _, _) }
|
||||
|
||||
/** Gets the parent of this XML comment. */
|
||||
XMLParent getParent() { xmlComments(this, _, result, _) }
|
||||
|
||||
/** Gets a printable representation of this XML comment. */
|
||||
override string toString() { result = this.getText() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A sequence of characters that occurs between opening and
|
||||
* closing tags of an XML element, excluding other elements.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* <content>This is a sequence of characters.</content>
|
||||
* ```
|
||||
*/
|
||||
class XMLCharacters extends @xmlcharacters, XMLLocatable {
|
||||
/** Gets the content of this character sequence. */
|
||||
string getCharacters() { xmlChars(this, result, _, _, _, _) }
|
||||
|
||||
/** Gets the parent of this character sequence. */
|
||||
XMLParent getParent() { xmlChars(this, _, result, _, _, _) }
|
||||
|
||||
/** Holds if this character sequence is CDATA. */
|
||||
predicate isCDATA() { xmlChars(this, _, _, _, 1, _) }
|
||||
|
||||
/** Gets a printable representation of this XML character sequence. */
|
||||
override string toString() { result = this.getCharacters() }
|
||||
}
|
||||
90
cpp/ql/lib/semmle/code/cpp/commons/Buffer.qll
Normal file
90
cpp/ql/lib/semmle/code/cpp/commons/Buffer.qll
Normal file
@@ -0,0 +1,90 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
|
||||
/**
|
||||
* Holds if `v` is a member variable of `c` that looks like it might be variable sized
|
||||
* in practice. For example:
|
||||
* ```
|
||||
* struct myStruct { // c
|
||||
* int amount;
|
||||
* char data[1]; // v
|
||||
* };
|
||||
* ```
|
||||
* This requires that `v` is an array of size 0 or 1.
|
||||
*/
|
||||
predicate memberMayBeVarSize(Class c, MemberVariable v) {
|
||||
c = v.getDeclaringType() and
|
||||
v.getUnspecifiedType().(ArrayType).getArraySize() <= 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size in bytes of the buffer pointed to by an expression (if this can be determined).
|
||||
*/
|
||||
language[monotonicAggregates]
|
||||
int getBufferSize(Expr bufferExpr, Element why) {
|
||||
exists(Variable bufferVar | bufferVar = bufferExpr.(VariableAccess).getTarget() |
|
||||
// buffer is a fixed size array
|
||||
result = bufferVar.getUnspecifiedType().(ArrayType).getSize() and
|
||||
why = bufferVar and
|
||||
not memberMayBeVarSize(_, bufferVar) 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.
|
||||
// buffer is an initialized array
|
||||
// e.g. int buffer[] = {1, 2, 3};
|
||||
why = bufferVar.getInitializer().getExpr() and
|
||||
(
|
||||
why instanceof AggregateLiteral or
|
||||
why instanceof StringLiteral
|
||||
) and
|
||||
result = why.(Expr).getType().(ArrayType).getSize() and
|
||||
not exists(bufferVar.getUnspecifiedType().(ArrayType).getSize())
|
||||
or
|
||||
exists(Class parentClass, VariableAccess parentPtr |
|
||||
// buffer is the parentPtr->bufferVar of a 'variable size struct'
|
||||
memberMayBeVarSize(parentClass, bufferVar) and
|
||||
why = bufferVar and
|
||||
parentPtr = bufferExpr.(VariableAccess).getQualifier() and
|
||||
parentPtr.getTarget().getUnspecifiedType().(PointerType).getBaseType() = parentClass and
|
||||
result = getBufferSize(parentPtr, _) + bufferVar.getType().getSize() - parentClass.getSize()
|
||||
)
|
||||
)
|
||||
or
|
||||
// buffer is a fixed size dynamic allocation
|
||||
result = bufferExpr.(AllocationExpr).getSizeBytes() and
|
||||
why = bufferExpr
|
||||
or
|
||||
exists(DataFlow::ExprNode bufferExprNode |
|
||||
// dataflow (all sources must be the same size)
|
||||
bufferExprNode = DataFlow::exprNode(bufferExpr) and
|
||||
result =
|
||||
unique(Expr def |
|
||||
DataFlow::localFlowStep(DataFlow::exprNode(def), bufferExprNode)
|
||||
|
|
||||
getBufferSize(def, _)
|
||||
) and
|
||||
// find reason
|
||||
exists(Expr def | DataFlow::localFlowStep(DataFlow::exprNode(def), bufferExprNode) |
|
||||
why = def or
|
||||
exists(getBufferSize(def, why))
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(Type bufferType |
|
||||
// buffer is the address of a variable
|
||||
why = bufferExpr.(AddressOfExpr).getAddressable() and
|
||||
bufferType = why.(Variable).getType() and
|
||||
result = bufferType.getSize() and
|
||||
not bufferType instanceof ReferenceType and
|
||||
not any(Union u).getAMemberVariable() = why
|
||||
)
|
||||
or
|
||||
exists(Union bufferType |
|
||||
// buffer is the address of a union member; in this case, we
|
||||
// take the size of the union itself rather the union member, since
|
||||
// it's usually OK to access that amount (e.g. clearing with memset).
|
||||
why = bufferExpr.(AddressOfExpr).getAddressable() and
|
||||
bufferType.getAMemberVariable() = why and
|
||||
result = bufferType.getSize()
|
||||
)
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user